diff options
Diffstat (limited to 'activerecord')
78 files changed, 607 insertions, 318 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index db8fa303be..f1cca0ad76 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,14 +1,44 @@ ## Rails 4.0.0 (unreleased) ## -* Rename `update_attributes` to `update`, keep `update_attributes` as an alias for `update` method. +* Added a state instance variable to each transaction. Will allow other objects + to know whether a transaction has been committed or rolled back. + + *John Wang* + +* Collection associations `#empty?` always respects builded records. + Fix #8879. + + Example: + + widget = Widget.new + widget.things.build + widget.things.empty? # => false + + *Yves Senn* + +* Remove support for parsing YAML parameters from request. + + *Aaron Patterson* + +* Support for PostgreSQL's `ltree` data type. + + *Rob Worley* + +* Fix undefined method `to_i` when calling `new` on a scope that uses an + Array; Fix FloatDomainError when setting integer column to NaN. + Fixes #8718, #8734, #8757. + + *Jason Stirk + Tristan Harward* + +* Rename `update_attributes` to `update`, keep `update_attributes` as an alias for `update` method. This is a soft-deprecation for `update_attributes`, although it will still work without any deprecation message in 4.0 is recommended to start using `update` since `update_attributes` will be deprecated and removed in future versions of Rails. - + *Amparo Luna + Guillermo Iguaran* * `after_commit` and `after_rollback` now validate the `:on` option and raise an `ArgumentError` - if it is not one of `:create`, `:destroy` or ``:update` + if it is not one of `:create`, `:destroy` or `:update` *Pascal Friederich* @@ -960,7 +990,6 @@ * `:conditions` becomes `:where`. * `:include` becomes `:includes`. - * `:extend` becomes `:extending`. The code to implement the deprecated features has been moved out to the `activerecord-deprecated_finders` gem. This gem is a dependency diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 53ddff420e..0523314128 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -39,6 +39,11 @@ namespace :test do end end +namespace :db do + task :create => ['mysql:build_databases', 'postgresql:build_databases'] + task :drop => ['mysql:drop_databases', 'postgresql:drop_databases'] +end + %w( mysql mysql2 postgresql sqlite3 sqlite3_mem firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ).each do |adapter| Rake::TestTask.new("test_#{adapter}") { |t| adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z0-9]+/] diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 31ddb01123..bfc2e54aba 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -25,5 +25,5 @@ Gem::Specification.new do |s| s.add_dependency 'activemodel', version s.add_dependency 'arel', '~> 3.0.2' - s.add_dependency 'activerecord-deprecated_finders', '0.0.1' + s.add_dependency 'activerecord-deprecated_finders', '0.0.2' end diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 6acfec02c4..9d1c12ec62 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -165,7 +165,7 @@ module ActiveRecord # by specifying an instance of the value object in the conditions hash. The following example # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD": # - # Customer.where(balance: Money.new(20, "USD")).all + # Customer.where(balance: Money.new(20, "USD")) # module ClassMethods # Adds reader and writer methods for manipulating a value object: diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d8b6d7a86b..16a46a59d1 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1,8 +1,6 @@ require 'active_support/core_ext/enumerable' require 'active_support/core_ext/string/conversions' require 'active_support/core_ext/module/remove_method' -require 'active_support/dependencies/autoload' -require 'active_support/concern' require 'active_record/errors' module ActiveRecord @@ -194,7 +192,7 @@ module ActiveRecord # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt> # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt> # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(mileston), Project#milestones.find(milestone_id),</tt> - # <tt>Project#milestones.all(options), Project#milestones.build, Project#milestones.create</tt> + # <tt>Project#milestones.build, Project#milestones.create</tt> # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt> # <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt> # @@ -459,7 +457,7 @@ module ActiveRecord # has_many :people do # def find_or_create_by_name(name) # first_name, last_name = name.split(" ", 2) - # find_or_create_by_first_name_and_last_name(first_name, last_name) + # find_or_create_by(first_name: first_name, last_name: last_name) # end # end # end @@ -474,7 +472,7 @@ module ActiveRecord # module FindOrCreateByNameExtension # def find_or_create_by_name(name) # first_name, last_name = name.split(" ", 2) - # find_or_create_by_first_name_and_last_name(first_name, last_name) + # find_or_create_by(first_name: first_name, last_name: last_name) # end # end # @@ -743,7 +741,7 @@ module ActiveRecord # other than the main one. If this is the case Active Record falls back to the previously # used LEFT OUTER JOIN based strategy. For example # - # Post.includes([:author, :comments]).where(['comments.approved = ?', true]).all + # Post.includes([:author, :comments]).where(['comments.approved = ?', true]) # # This will result in a single SQL query with joins along the lines of: # <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and @@ -951,7 +949,7 @@ module ActiveRecord # # The <tt>:dependent</tt> option can have different values which specify how the deletion # is done. For more information, see the documentation for this option on the different - # specific association types. When no option is given, the behaviour is to do nothing + # specific association types. When no option is given, the behavior is to do nothing # with the associated records when destroying a record. # # Note that <tt>:dependent</tt> is implemented using Rails' callback @@ -1081,7 +1079,7 @@ module ActiveRecord # === Example # # Example: A Firm class declares <tt>has_many :clients</tt>, which will add: - # * <tt>Firm#clients</tt> (similar to <tt>Clients.all conditions: ["firm_id = ?", id]</tt>) + # * <tt>Firm#clients</tt> (similar to <tt>Client.where(firm_id: id)</tt>) # * <tt>Firm#clients<<</tt> # * <tt>Firm#clients.delete</tt> # * <tt>Firm#clients.destroy</tt> @@ -1091,7 +1089,7 @@ module ActiveRecord # * <tt>Firm#clients.clear</tt> # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>) # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>) - # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, conditions: "firm_id = #{id}")</tt>) + # * <tt>Firm#clients.find</tt> (similar to <tt>Client.where(firm_id: id).find(id)</tt>) # * <tt>Firm#clients.exists?(name: 'ACME')</tt> (similar to <tt>Client.exists?(name: 'ACME', firm_id: firm.id)</tt>) # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>) # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>) @@ -1213,7 +1211,7 @@ module ActiveRecord # === Example # # An Account class declares <tt>has_one :beneficiary</tt>, which will add: - # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.first(conditions: "account_id = #{id}")</tt>) + # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.where(account_id: id).first</tt>) # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>) # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>) # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>) diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index 1303822868..300f67959d 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -16,6 +16,7 @@ module ActiveRecord def scope scope = klass.unscoped scope.merge! eval_scope(klass, reflection.scope) if reflection.scope + scope.extending! Array(options[:extend]) add_constraints(scope) end diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 75f72c1a46..532af3e83e 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -50,8 +50,11 @@ module ActiveRecord # Checks whether record is different to the current target, without loading it def different_target?(record) - record.nil? && owner[reflection.foreign_key] || - record && record.id != owner[reflection.foreign_key] + if record.nil? + owner[reflection.foreign_key] + else + record.id != owner[reflection.foreign_key] + end end def replace_keys(record) diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb index fcdfc1e150..fdead16761 100644 --- a/activerecord/lib/active_record/associations/builder/collection_association.rb +++ b/activerecord/lib/active_record/associations/builder/collection_association.rb @@ -6,7 +6,8 @@ module ActiveRecord::Associations::Builder CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove] def valid_options - super + [:table_name, :finder_sql, :counter_sql, :before_add, :after_add, :before_remove, :after_remove] + super + [:table_name, :finder_sql, :counter_sql, :before_add, + :after_add, :before_remove, :after_remove, :extend] end attr_reader :block_extension, :extension_module diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 832b963052..5feb149946 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -273,7 +273,7 @@ module ActiveRecord if loaded? || options[:counter_sql] size.zero? else - !scope.exists? + @target.blank? && !scope.exists? end end diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 7c43e37cf2..e93e700c93 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -33,6 +33,7 @@ module ActiveRecord def initialize(klass, association) #:nodoc: @association = association super klass, klass.arel_table + self.default_scoped = true merge! association.scope(nullify: false) end @@ -758,7 +759,7 @@ module ActiveRecord # person.pets.count # => 0 # person.pets.any? # => true # - # You can also pass a block to define criteria. The behaviour + # You can also pass a block to define criteria. The behavior # is the same, it returns true if the collection based on the # criteria is not empty. # @@ -793,7 +794,7 @@ module ActiveRecord # person.pets.many? #=> true # # You can also pass a block to define criteria. The - # behaviour is the same, it returns true if the collection + # behavior is the same, it returns true if the collection # based on the criteria has more than one record. # # person.pets diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index c3266f2bb4..d1458f30ba 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -114,11 +114,7 @@ module ActiveRecord end def target_reflection_has_associated_record? - if through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank? - false - else - true - end + !(through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?) end def update_through_counter?(method) diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index cbf5e734ea..82588905c6 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -76,7 +76,7 @@ module ActiveRecord else # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000) # Make several smaller queries if necessary or make one query if the adapter supports it - sliced = owner_keys.each_slice(model.connection.in_clause_length || owner_keys.size) + sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size) records = sliced.map { |slice| records_for(slice).to_a }.flatten end diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 7e357aa2f4..616ae1631f 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -1,5 +1,4 @@ require 'active_support/core_ext/module/attribute_accessors' -require 'active_support/deprecation' module ActiveRecord module AttributeMethods diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index 0857b02c8e..310f1b6e75 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -76,7 +76,7 @@ module ActiveRecord end def get_primary_key(base_name) #:nodoc: - return 'id' unless base_name && !base_name.blank? + return 'id' if base_name.blank? case primary_key_prefix_type when :table_name diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index aab832c2f7..bf5793d454 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -162,12 +162,9 @@ module ActiveRecord #:nodoc: # # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects # by simple queries without turning to SQL. They work by appending the name of an attribute - # to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt> and thus produces finders - # like <tt>Person.find_by_user_name</tt>, <tt>Person.find_all_by_last_name</tt>, and - # <tt>Payment.find_by_transaction_id</tt>. Instead of writing - # <tt>Person.where(user_name: user_name).first</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>. - # And instead of writing <tt>Person.where(last_name: last_name).all</tt>, you just do - # <tt>Person.find_all_by_last_name(last_name)</tt>. + # to <tt>find_by_</tt> # like <tt>Person.find_by_user_name</tt>. + # Instead of writing # <tt>Person.where(user_name: user_name).first</tt>, you just do + # <tt>Person.find_by_user_name(user_name)</tt>. # # It's possible to add an exclamation point (!) on the end of the dynamic finders to get them to raise an # <tt>ActiveRecord::RecordNotFound</tt> error if they do not return any records, @@ -180,46 +177,7 @@ module ActiveRecord #:nodoc: # # It's even possible to call these dynamic finder methods on relations and named scopes. # - # Payment.order("created_on").find_all_by_amount(50) - # Payment.pending.find_last_by_amount(100) - # - # The same dynamic finder style can be used to create the object if it doesn't already exist. - # This dynamic finder is called with <tt>find_or_create_by_</tt> and will return the object if - # it already exists and otherwise creates it, then returns it. Protected attributes won't be set - # unless they are given in a block. - # - # # No 'Summer' tag exists - # Tag.find_or_create_by_name("Summer") # equal to Tag.create(name: "Summer") - # - # # Now the 'Summer' tag does exist - # Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer") - # - # # Now 'Bob' exist and is an 'admin' - # User.find_or_create_by_name('Bob', age: 40) { |u| u.admin = true } - # - # Adding an exclamation point (!) on to the end of <tt>find_or_create_by_</tt> will - # raise an <tt>ActiveRecord::RecordInvalid</tt> error if the new record is invalid. - # - # Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without - # saving it first. Protected attributes won't be set unless they are given in a block. - # - # # No 'Winter' tag exists - # winter = Tag.find_or_initialize_by_name("Winter") - # winter.persisted? # false - # - # To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of - # a list of parameters. - # - # Tag.find_or_create_by_name(name: "rails", creator: current_user) - # - # That will either find an existing tag named "rails", or create a new one while setting the - # user that created it. - # - # Just like <tt>find_by_*</tt>, you can also use <tt>scoped_by_*</tt> to retrieve data. The good thing about - # using this feature is that the very first time result is returned using <tt>method_missing</tt> technique - # but after that the method is declared on the class. Henceforth <tt>method_missing</tt> will not be hit. - # - # User.scoped_by_user_name('David') + # Payment.order("created_on").find_by_amount(50) # # == Saving arrays, hashes, and other non-mappable objects in text columns # diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 82d0cf7e2e..3675184193 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -2,7 +2,6 @@ require 'thread' require 'thread_safe' require 'monitor' require 'set' -require 'active_support/deprecation' module ActiveRecord # Raised when a connection could not be obtained within the connection @@ -518,6 +517,7 @@ module ActiveRecord def establish_connection(owner, spec) @class_to_pool.clear + raise RuntimeError, "Anonymous class is not allowed." unless owner.name owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 4cca94e40b..3ecef96b10 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -5,6 +5,35 @@ module ActiveRecord def initialize(connection) @connection = connection + @state = TransactionState.new + end + + def state + @state + end + end + + class TransactionState + + VALID_STATES = Set.new([:committed, :rolledback, nil]) + + def initialize(state = nil) + @state = state + end + + def committed? + @state == :committed + end + + def rolledback? + @state == :rolledback + end + + def set_state(state) + if !VALID_STATES.include?(state) + raise ArgumentError, "Invalid transaction state: #{state}" + end + @state = state end end @@ -91,6 +120,7 @@ module ActiveRecord end def rollback_records + @state.set_state(:rolledback) records.uniq.each do |record| begin record.rolledback!(parent.closed?) @@ -101,6 +131,7 @@ module ActiveRecord end def commit_records + @state.set_state(:committed) records.uniq.each do |record| begin record.committed! diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 8517ce5fc5..cbb6869e66 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -5,7 +5,6 @@ require 'active_support/core_ext/benchmark' require 'active_record/connection_adapters/schema_cache' require 'active_record/connection_adapters/abstract/schema_dumper' require 'monitor' -require 'active_support/deprecation' module ActiveRecord module ConnectionAdapters # :nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index fd36a5b075..fb28ecb6cf 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -206,7 +206,7 @@ module ActiveRecord when TrueClass, FalseClass value ? 1 : 0 else - value.to_i + value.to_i rescue nil end end diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index a6013f754a..20a5ca2baa 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -7,13 +7,15 @@ module ActiveRecord module ConnectionHandling # Establishes a connection to the database that's used by all Active Record objects. def mysql2_connection(config) + config = config.symbolize_keys + config[:username] = 'root' if config[:username].nil? if Mysql2::Client.const_defined? :FOUND_ROWS config[:flags] = Mysql2::Client::FOUND_ROWS end - client = Mysql2::Client.new(config.symbolize_keys) + client = Mysql2::Client.new(config) options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0] ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config) end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index 34d7a246b2..9b5170f657 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -1,5 +1,3 @@ -require 'active_support/deprecation' - module ActiveRecord module ConnectionAdapters class PostgreSQLAdapter < AbstractAdapter diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index 18ea83ce42..02c295983f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -82,7 +82,7 @@ module ActiveRecord def type_cast(value) return if value.nil? - value.to_i rescue value ? 1 : 0 + ConnectionAdapters::Column.value_to_integer value end end @@ -276,6 +276,7 @@ module ActiveRecord register_type 'circle', OID::Identity.new register_type 'hstore', OID::Hstore.new register_type 'json', OID::Json.new + register_type 'ltree', OID::Identity.new register_type 'int4range', OID::IntRange.new alias_type 'int8range', 'int4range' diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index e10b562fa4..8c68576bdc 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -18,9 +18,9 @@ module ActiveRecord # create_database config[:database], config # create_database 'foo_development', encoding: 'unicode' def create_database(name, options = {}) - options = options.reverse_merge(:encoding => "utf8") + options = { encoding: 'utf8' }.merge!(options.symbolize_keys) - option_string = options.symbolize_keys.sum do |key, value| + option_string = options.sum do |key, value| case key when :owner " OWNER = \"#{value}\"" diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index d62a375470..b1b0467379 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -17,22 +17,25 @@ require 'ipaddr' module ActiveRecord module ConnectionHandling + VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout, + :client_encoding, :options, :application_name, :fallback_application_name, + :keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count, + :tty, :sslmode, :requiressl, :sslcert, :sslkey, :sslrootcert, :sslcrl, + :requirepeer, :krbsrvname, :gsslib, :service] + # Establishes a connection to the database that's used by all Active Record objects def postgresql_connection(config) # :nodoc: conn_params = config.symbolize_keys - # Forward any unused config params to PGconn.connect. - [:statement_limit, :encoding, :min_messages, :schema_search_path, - :schema_order, :adapter, :pool, :checkout_timeout, :template, - :reaping_frequency, :insert_returning, :variables].each do |key| - conn_params.delete key - end - conn_params.delete_if { |k,v| v.nil? } + conn_params.delete_if { |_, v| v.nil? } # Map ActiveRecords param names to PGs. conn_params[:user] = conn_params.delete(:username) if conn_params[:username] conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database] + # Forward only valid config params to PGconn.connect. + conn_params.keep_if { |k, _| VALID_CONN_PARAMS.include?(k) } + # The postgres drivers don't allow the creation of an unconnected PGconn object, # so just pass a nil connection object for the time being. ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config) @@ -173,6 +176,8 @@ module ActiveRecord :decimal when 'hstore' :hstore + when 'ltree' + :ltree # Network address types when 'inet' :inet @@ -275,6 +280,10 @@ module ActiveRecord column(name, 'hstore', options) end + def ltree(name, options = {}) + column(name, 'ltree', options) + end + def inet(name, options = {}) column(name, 'inet', options) end @@ -340,7 +349,8 @@ module ActiveRecord macaddr: { name: "macaddr" }, uuid: { name: "uuid" }, json: { name: "json" }, - intrange: { name: "int4range" } + intrange: { name: "int4range" }, + ltree: { name: "ltree" } } include Quoting @@ -451,8 +461,6 @@ module ActiveRecord @visitor = BindSubstitution.new self end - connection_parameters.delete :prepared_statements - @connection_parameters, @config = connection_parameters, config # @local_tz is initialized as nil to avoid warnings when connect tries to use it @@ -640,32 +648,30 @@ module ActiveRecord end def exec_cache(sql, binds) + stmt_key = prepare_statement sql + + # Clear the queue + @connection.get_last_result + @connection.send_query_prepared(stmt_key, binds.map { |col, val| + type_cast(val, col) + }) + @connection.block + @connection.get_last_result + rescue PGError => e + # Get the PG code for the failure. Annoyingly, the code for + # prepared statements whose return value may have changed is + # FEATURE_NOT_SUPPORTED. Check here for more details: + # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573 begin - stmt_key = prepare_statement sql - - # Clear the queue - @connection.get_last_result - @connection.send_query_prepared(stmt_key, binds.map { |col, val| - type_cast(val, col) - }) - @connection.block - @connection.get_last_result - rescue PGError => e - # Get the PG code for the failure. Annoyingly, the code for - # prepared statements whose return value may have changed is - # FEATURE_NOT_SUPPORTED. Check here for more details: - # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573 - begin - code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE) - rescue - raise e - end - if FEATURE_NOT_SUPPORTED == code - @statements.delete sql_key(sql) - retry - else - raise e - end + code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE) + rescue + raise e + end + if FEATURE_NOT_SUPPORTED == code + @statements.delete sql_key(sql) + retry + else + raise e end end diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 94c6684700..812f1ce5c5 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -365,17 +365,18 @@ module ActiveRecord pk = self.class.primary_key @attributes[pk] = nil unless @attributes.key?(pk) - @aggregation_cache = {} - @association_cache = {} - @attributes_cache = {} - @previously_changed = {} - @changed_attributes = {} - @readonly = false - @destroyed = false - @marked_for_destruction = false - @new_record = true - @txn = nil - @_start_transaction_state = {} + @aggregation_cache = {} + @association_cache = {} + @attributes_cache = {} + @previously_changed = {} + @changed_attributes = {} + @readonly = false + @destroyed = false + @marked_for_destruction = false + @new_record = true + @txn = nil + @_start_transaction_state = {} + @transaction = nil end end end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index ea3bb8f33f..5a4ef5991d 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -5,8 +5,6 @@ require 'active_support/dependencies' require 'active_record/fixture_set/file' require 'active_record/errors' -require 'active_support/deprecation' # temporary - module ActiveRecord class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc: end diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index 6ab67fdece..e630897a4b 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/hash/indifferent_access' + module ActiveRecord module Inheritance extend ActiveSupport::Concern diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb index b4bb95a392..8e4ddcac82 100644 --- a/activerecord/lib/active_record/locking/pessimistic.rb +++ b/activerecord/lib/active_record/locking/pessimistic.rb @@ -26,7 +26,7 @@ module ActiveRecord # # Account.transaction do # # select * from accounts where ... - # accounts = Account.where(...).all + # accounts = Account.where(...) # account1 = accounts.detect { |account| ... } # account2 = accounts.detect { |account| ... } # # select * from accounts where id=? for update diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 2366a91bb5..c1ba524c84 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -21,13 +21,15 @@ module ActiveRecord end def render_bind(column, value) - if column.type == :binary - rendered_value = "<#{value.bytesize} bytes of binary data>" + if column + if column.binary? + value = "<#{value.bytesize} bytes of binary data>" + end + + [column.name, value] else - rendered_value = value + [nil, value] end - - [column.name, rendered_value] end def sql(event) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 3011f959a5..1b2aa9349e 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -236,26 +236,26 @@ module ActiveRecord alias update_attributes! update! - # Updates a single attribute of an object, without having to explicitly call save on that object. - # - # * Validation is skipped. - # * Callbacks are skipped. - # * updated_at/updated_on column is not updated if that column is available. - # - # Raises an +ActiveRecordError+ when called on new objects, or when the +name+ - # attribute is marked as readonly. + # Equivalent to <code>update_columns(name => value)</code>. def update_column(name, value) update_columns(name => value) end - # Updates the attributes from the passed-in hash, without having to explicitly call save on that object. + # Updates the attributes directly in the database issuing an UPDATE SQL + # statement and sets them in the receiver: # - # * Validation is skipped. + # user.update_columns(last_request_at: Time.current) + # + # This is the fastest way to update attributes because it goes straight to + # the database, but take into account that in consequence the regular update + # procedures are totally bypassed. In particular: + # + # * Validations are skipped. # * Callbacks are skipped. - # * updated_at/updated_on column is not updated if that column is available. + # * +updated_at+/+updated_on+ are not updated. # - # Raises an +ActiveRecordError+ when called on new objects, or when at least - # one of the attributes is marked as readonly. + # This method raises an +ActiveRecord::ActiveRecordError+ when called on new + # objects, or when at least one of the attributes is marked as readonly. def update_columns(attributes) raise ActiveRecordError, "can not update on a new record object" unless persisted? diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 6ec5cf3e18..0053530f73 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -276,7 +276,7 @@ module ActiveRecord stmt.table(table) stmt.key = table[primary_key] - if joins_values.any? + if with_default_scope.joins_values.any? @klass.connection.join_to_update(stmt, arel) else stmt.take(arel.limit) @@ -401,7 +401,7 @@ module ActiveRecord stmt = Arel::DeleteManager.new(arel.engine) stmt.from(table) - if joins_values.any? + if with_default_scope.joins_values.any? @klass.connection.join_to_delete(stmt, arel, table[primary_key]) else stmt.wheres = arel.constraints @@ -474,16 +474,16 @@ module ActiveRecord # Returns sql statement for the relation. # - # Users.where(name: 'Oscar').to_sql + # User.where(name: 'Oscar').to_sql # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar' def to_sql @to_sql ||= klass.connection.to_sql(arel, bind_values.dup) end - # Returns a hash of where conditions + # Returns a hash of where conditions. # - # Users.where(name: 'Oscar').where_values_hash - # # => {name: "oscar"} + # User.where(name: 'Oscar').where_values_hash + # # => {name: "Oscar"} def where_values_hash equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node| node.left.relation.name == table_name diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 0b27ea730b..3f154bd1cc 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -1,5 +1,3 @@ -require 'active_support/deprecation' - module ActiveRecord module Calculations # Count the records. diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 431d083f21..615309964c 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -1,4 +1,3 @@ -require 'active_support/concern' require 'thread' require 'thread_safe' diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 83074e72c1..883d25d80b 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -7,12 +7,12 @@ module ActiveRecord table = default_table if value.is_a?(Hash) - table = Arel::Table.new(column, default_table.engine) - association = klass.reflect_on_association(column.to_sym) - if value.empty? - queries.concat ['1 = 2'] + queries << '1 = 2' else + table = Arel::Table.new(column, default_table.engine) + association = klass.reflect_on_association(column.to_sym) + value.each do |k, v| queries.concat expand(association && association.klass, table, k, v) end diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 8b7eda6eee..01fbb96b8e 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -134,16 +134,14 @@ module ActiveRecord # end # # def self.titles - # map(&:title) + # pluck(:title) # end - # # end # # We are able to call the methods like this: # # Article.published.featured.latest_article # Article.featured.titles - def scope(name, body, &block) extension = Module.new(&block) if block diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index df7f58c81f..cf4cf9e602 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -107,7 +107,7 @@ module ActiveRecord private def initialize_store_attribute(store_attribute) attribute = send(store_attribute) - unless attribute.is_a?(HashWithIndifferentAccess) + unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess) attribute = IndifferentCoder.as_indifferent_hash(attribute) send :"#{store_attribute}=", attribute end @@ -134,12 +134,12 @@ module ActiveRecord def self.as_indifferent_hash(obj) case obj - when HashWithIndifferentAccess + when ActiveSupport::HashWithIndifferentAccess obj when Hash obj.with_indifferent_access else - HashWithIndifferentAccess.new + ActiveSupport::HashWithIndifferentAccess.new end end end diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index fda51b3d76..67c7e714e6 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -1,5 +1,7 @@ module ActiveRecord module Tasks # :nodoc: + class DatabaseAlreadyExists < StandardError; end # :nodoc: + module DatabaseTasks # :nodoc: extend self @@ -32,6 +34,8 @@ module ActiveRecord def create(*arguments) configuration = arguments.first class_for_adapter(configuration['adapter']).new(*arguments).create + rescue DatabaseAlreadyExists + $stderr.puts "#{configuration['database']} already exists" rescue Exception => error $stderr.puts error, *(error.backtrace) $stderr.puts "Couldn't create database for #{configuration.inspect}" diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb index 3d27c97254..17378969a5 100644 --- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb @@ -1,7 +1,6 @@ module ActiveRecord module Tasks # :nodoc: class MySQLDatabaseTasks # :nodoc: - DEFAULT_CHARSET = ENV['CHARSET'] || 'utf8' DEFAULT_COLLATION = ENV['COLLATION'] || 'utf8_unicode_ci' ACCESS_DENIED_ERROR = 1045 @@ -16,18 +15,23 @@ module ActiveRecord establish_connection configuration_without_database connection.create_database configuration['database'], creation_options establish_connection configuration + rescue ActiveRecord::StatementInvalid => error + if /database exists/ === error.message + raise DatabaseAlreadyExists + else + raise + end rescue error_class => error - raise error unless error.errno == ACCESS_DENIED_ERROR - - $stdout.print error.error - establish_connection root_configuration_without_database - connection.create_database configuration['database'], creation_options - connection.execute grant_statement.gsub(/\s+/, ' ').strip - establish_connection configuration - rescue error_class => error - $stderr.puts error.error - $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}" - $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding'] + if error.respond_to?(:errno) && error.errno == ACCESS_DENIED_ERROR + $stdout.print error.error + establish_connection root_configuration_without_database + connection.create_database configuration['database'], creation_options + connection.execute grant_statement.gsub(/\s+/, ' ').strip + establish_connection configuration + else + $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}" + $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding'] + end end def drop @@ -87,14 +91,15 @@ module ActiveRecord end def error_class - case configuration['adapter'] - when /jdbc/ + if configuration['adapter'] =~ /jdbc/ require 'active_record/railties/jdbcmysql_error' ArJdbcMySQL::Error - when /mysql2/ + elsif defined?(Mysql2) Mysql2::Error - else + elsif defined?(Mysql) Mysql::Error + else + StandardError end end @@ -128,7 +133,6 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION; end args end - end end end diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb index ea5cb888fb..0b1b030516 100644 --- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb @@ -3,7 +3,6 @@ require 'shellwords' module ActiveRecord module Tasks # :nodoc: class PostgreSQLDatabaseTasks # :nodoc: - DEFAULT_ENCODING = ENV['CHARSET'] || 'utf8' delegate :connection, :establish_connection, :clear_active_connections!, @@ -18,6 +17,12 @@ module ActiveRecord connection.create_database configuration['database'], configuration.merge('encoding' => encoding) establish_connection configuration + rescue ActiveRecord::StatementInvalid => error + if /database .* already exists/ === error.message + raise DatabaseAlreadyExists + else + raise + end end def drop diff --git a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb index da01058a82..de8b16627e 100644 --- a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb @@ -1,7 +1,6 @@ module ActiveRecord module Tasks # :nodoc: class SQLiteDatabaseTasks # :nodoc: - delegate :connection, :establish_connection, to: ActiveRecord::Base def initialize(configuration, root = Rails.root) @@ -9,10 +8,7 @@ module ActiveRecord end def create - if File.exist?(configuration['database']) - $stderr.puts "#{configuration['database']} already exists" - return - end + raise DatabaseAlreadyExists if File.exist?(configuration['database']) establish_connection configuration connection diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb index c035ad43a2..e9142481a3 100644 --- a/activerecord/lib/active_record/test_case.rb +++ b/activerecord/lib/active_record/test_case.rb @@ -60,16 +60,17 @@ module ActiveRecord self.clear_log - self.ignored_sql = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/] + self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/] # FIXME: this needs to be refactored so specific database can add their own # ignored SQL, or better yet, use a different notification for the queries # instead examining the SQL content. oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im] mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/] - postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im] + postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i] + sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im] - [oracle_ignored, mysql_ignored, postgresql_ignored].each do |db_ignored_sql| + [oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql| ignored_sql.concat db_ignored_sql end diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 404b492288..f9149c1819 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -164,14 +164,16 @@ module ActiveRecord class AdapterTestWithoutTransaction < ActiveRecord::TestCase self.use_transactional_fixtures = false + class Klass < ActiveRecord::Base + end + def setup - @klass = Class.new(ActiveRecord::Base) - @klass.establish_connection 'arunit' - @connection = @klass.connection + Klass.establish_connection 'arunit' + @connection = Klass.connection end def teardown - @klass.remove_connection + Klass.remove_connection end test "transaction state is reset after a reconnect" do diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index ffd6904aec..b67d70ede7 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -1,6 +1,9 @@ require "cases/helper" class MysqlConnectionTest < ActiveRecord::TestCase + class Klass < ActiveRecord::Base + end + def setup super @connection = ActiveRecord::Base.connection @@ -17,9 +20,8 @@ class MysqlConnectionTest < ActiveRecord::TestCase run_without_connection do |orig| ar_config = ARTest.connection_config['arunit'] url = "mysql://#{ar_config["username"]}@localhost/#{ar_config["database"]}" - klass = Class.new(ActiveRecord::Base) - klass.establish_connection(url) - assert_equal ar_config['database'], klass.connection.current_database + Klass.establish_connection(url) + assert_equal ar_config['database'], Klass.connection.current_database end end diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb index ddfe42b375..0eb1cc511e 100644 --- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb +++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb @@ -49,13 +49,11 @@ module ActiveRecord end def test_tables_quoting - begin - @conn.tables(nil, "foo-bar", nil) - flunk - rescue => e - # assertion for *quoted* database properly - assert_match(/database 'foo-bar'/, e.inspect) - end + @conn.tables(nil, "foo-bar", nil) + flunk + rescue => e + # assertion for *quoted* database properly + assert_match(/database 'foo-bar'/, e.inspect) end def test_pk_and_sequence_for diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb index 2c0ed73c92..94429e772f 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb @@ -37,13 +37,11 @@ module ActiveRecord end def test_tables_quoting - begin - @connection.tables(nil, "foo-bar", nil) - flunk - rescue => e - # assertion for *quoted* database properly - assert_match(/database 'foo-bar'/, e.inspect) - end + @connection.tables(nil, "foo-bar", nil) + flunk + rescue => e + # assertion for *quoted* database properly + assert_match(/database 'foo-bar'/, e.inspect) end end diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb index 1b4f4a5fc9..01c3e6b49b 100644 --- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb @@ -16,6 +16,7 @@ class PostgresqlActiveSchemaTest < ActiveRecord::TestCase def test_create_database_with_encoding assert_equal %(CREATE DATABASE "matt" ENCODING = 'utf8'), create_database(:matt) assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1) + assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, 'encoding' => :latin1) end def test_create_database_with_collation_and_ctype diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb index c7ce43d71e..2254be8612 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -30,6 +30,9 @@ end class PostgresqlUUID < ActiveRecord::Base end +class PostgresqlLtree < ActiveRecord::Base +end + class PostgresqlDataTypeTest < ActiveRecord::TestCase self.use_transactional_fixtures = false @@ -37,38 +40,43 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase @connection = ActiveRecord::Base.connection @connection.execute("set lc_monetary = 'C'") - @connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )") + @connection.execute("INSERT INTO postgresql_arrays (id, commission_by_quarter, nicknames) VALUES (1, '{35000,21000,18000,17000}', '{foo,bar,baz}')") @first_array = PostgresqlArray.find(1) - @connection.execute("INSERT INTO postgresql_tsvectors (text_vector) VALUES (' ''text'' ''vector'' ')") + @connection.execute("INSERT INTO postgresql_tsvectors (id, text_vector) VALUES (1, ' ''text'' ''vector'' ')") @first_tsvector = PostgresqlTsvector.find(1) - @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('567.89'::money)") - @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('-567.89'::money)") + @connection.execute("INSERT INTO postgresql_moneys (id, wealth) VALUES (1, '567.89'::money)") + @connection.execute("INSERT INTO postgresql_moneys (id, wealth) VALUES (2, '-567.89'::money)") @first_money = PostgresqlMoney.find(1) @second_money = PostgresqlMoney.find(2) - @connection.execute("INSERT INTO postgresql_numbers (single, double) VALUES (123.456, 123456.789)") + @connection.execute("INSERT INTO postgresql_numbers (id, single, double) VALUES (1, 123.456, 123456.789)") @first_number = PostgresqlNumber.find(1) - @connection.execute("INSERT INTO postgresql_times (time_interval, scaled_time_interval) VALUES ('1 year 2 days ago', '3 weeks ago')") + @connection.execute("INSERT INTO postgresql_times (id, time_interval, scaled_time_interval) VALUES (1, '1 year 2 days ago', '3 weeks ago')") @first_time = PostgresqlTime.find(1) - @connection.execute("INSERT INTO postgresql_network_addresses (cidr_address, inet_address, mac_address) VALUES('192.168.0/24', '172.16.1.254/32', '01:23:45:67:89:0a')") + @connection.execute("INSERT INTO postgresql_network_addresses (id, cidr_address, inet_address, mac_address) VALUES(1, '192.168.0/24', '172.16.1.254/32', '01:23:45:67:89:0a')") @first_network_address = PostgresqlNetworkAddress.find(1) - @connection.execute("INSERT INTO postgresql_bit_strings (bit_string, bit_string_varying) VALUES (B'00010101', X'15')") + @connection.execute("INSERT INTO postgresql_bit_strings (id, bit_string, bit_string_varying) VALUES (1, B'00010101', X'15')") @first_bit_string = PostgresqlBitString.find(1) - @connection.execute("INSERT INTO postgresql_oids (obj_id) VALUES (1234)") + @connection.execute("INSERT INTO postgresql_oids (id, obj_id) VALUES (1, 1234)") @first_oid = PostgresqlOid.find(1) - @connection.execute("INSERT INTO postgresql_timestamp_with_zones (time) VALUES ('2010-01-01 10:00:00-1')") + @connection.execute("INSERT INTO postgresql_timestamp_with_zones (id, time) VALUES (1, '2010-01-01 10:00:00-1')") - @connection.execute("INSERT INTO postgresql_uuids (guid, compact_guid) VALUES('d96c3da0-96c1-012f-1316-64ce8f32c6d8', 'f06c715096c1012f131764ce8f32c6d8')") + @connection.execute("INSERT INTO postgresql_uuids (id, guid, compact_guid) VALUES(1, 'd96c3da0-96c1-012f-1316-64ce8f32c6d8', 'f06c715096c1012f131764ce8f32c6d8')") @first_uuid = PostgresqlUUID.find(1) end + def teardown + [PostgresqlArray, PostgresqlTsvector, PostgresqlMoney, PostgresqlNumber, PostgresqlTime, PostgresqlNetworkAddress, + PostgresqlBitString, PostgresqlOid, PostgresqlTimestampWithZone, PostgresqlUUID].each(&:delete_all) + end + def test_data_type_of_array_types assert_equal :integer, @first_array.column_for_attribute(:commission_by_quarter).type assert_equal :text, @first_array.column_for_attribute(:nicknames).type diff --git a/activerecord/test/cases/adapters/postgresql/ltree_test.rb b/activerecord/test/cases/adapters/postgresql/ltree_test.rb new file mode 100644 index 0000000000..5d12ca75ca --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/ltree_test.rb @@ -0,0 +1,41 @@ +# encoding: utf-8 +require "cases/helper" +require 'active_record/base' +require 'active_record/connection_adapters/postgresql_adapter' + +class PostgresqlLtreeTest < ActiveRecord::TestCase + class Ltree < ActiveRecord::Base + self.table_name = 'ltrees' + end + + def setup + @connection = ActiveRecord::Base.connection + @connection.transaction do + @connection.create_table('ltrees') do |t| + t.ltree 'path' + end + end + rescue ActiveRecord::StatementInvalid + skip "do not test on PG without ltree" + end + + def teardown + @connection.execute 'drop table if exists ltrees' + end + + def test_column + column = Ltree.columns_hash['path'] + assert_equal :ltree, column.type + end + + def test_write + ltree = Ltree.new(path: '1.2.3.4') + assert ltree.save! + end + + def test_select + @connection.execute "insert into ltrees (path) VALUES ('1.2.3')" + ltree = Ltree.first + assert_equal '1.2.3', ltree.path + end +end diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index b2eac0349b..244e0b7179 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -7,10 +7,12 @@ if ActiveRecord::Base.connection.supports_migrations? def setup @connection = ActiveRecord::Base.connection + ActiveRecord::SchemaMigration.drop_table end def teardown @connection.drop_table :fruits rescue nil + ActiveRecord::SchemaMigration.delete_all rescue nil end def test_schema_define @@ -45,5 +47,4 @@ if ActiveRecord::Base.connection.supports_migrations? assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" } end end - end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 97a3d57fe0..3a6da0e59f 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -63,6 +63,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal apple.id, citibank.firm_id end + def test_id_assignment + apple = Firm.create("name" => "Apple") + citibank = Account.create("credit_limit" => 10) + citibank.firm_id = apple + assert_nil citibank.firm_id + end + def test_natural_assignment_with_primary_key apple = Firm.create("name" => "Apple") citibank = Client.create("name" => "Primary key client") @@ -567,6 +574,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal new_firm.name, "Apple" end + def test_attributes_are_set_without_error_when_initialized_from_belongs_to_association_with_array_in_where_clause + new_account = Account.where(:credit_limit => [ 50, 60 ]).new + assert_nil new_account.credit_limit + end + def test_reassigning_the_parent_id_updates_the_object client = companies(:second_client) diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index ce1da53859..be2d30641e 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -93,31 +93,31 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_preloading_has_many_in_multiple_queries_with_more_ids_than_database_can_handle - Post.connection.expects(:in_clause_length).at_least_once.returns(5) + Comment.connection.expects(:in_clause_length).at_least_once.returns(5) posts = Post.all.merge!(:includes=>:comments).to_a assert_equal 11, posts.size end def test_preloading_has_many_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle - Post.connection.expects(:in_clause_length).at_least_once.returns(nil) + Comment.connection.expects(:in_clause_length).at_least_once.returns(nil) posts = Post.all.merge!(:includes=>:comments).to_a assert_equal 11, posts.size end def test_preloading_habtm_in_multiple_queries_with_more_ids_than_database_can_handle - Post.connection.expects(:in_clause_length).at_least_once.returns(5) + Comment.connection.expects(:in_clause_length).at_least_once.returns(5) posts = Post.all.merge!(:includes=>:categories).to_a assert_equal 11, posts.size end def test_preloading_habtm_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle - Post.connection.expects(:in_clause_length).at_least_once.returns(nil) + Comment.connection.expects(:in_clause_length).at_least_once.returns(nil) posts = Post.all.merge!(:includes=>:categories).to_a assert_equal 11, posts.size end def test_load_associated_records_in_one_query_when_adapter_has_no_limit - Post.connection.expects(:in_clause_length).at_least_once.returns(nil) + Comment.connection.expects(:in_clause_length).at_least_once.returns(nil) post = posts(:welcome) assert_queries(2) do @@ -126,7 +126,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_load_associated_records_in_several_queries_when_many_ids_passed - Post.connection.expects(:in_clause_length).at_least_once.returns(1) + Comment.connection.expects(:in_clause_length).at_least_once.returns(1) post1, post2 = posts(:welcome), posts(:thinking) assert_queries(3) do @@ -135,7 +135,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_load_associated_records_in_one_query_when_a_few_ids_passed - Post.connection.expects(:in_clause_length).at_least_once.returns(3) + Comment.connection.expects(:in_clause_length).at_least_once.returns(3) post = posts(:welcome) assert_queries(2) do diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 7e6c7d5862..d42630e1b7 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -625,6 +625,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 3, company.clients_of_firm.size end + def test_collection_not_empty_after_building + company = companies(:first_firm) + assert_predicate company.contracts, :empty? + company.contracts.build + assert_not_predicate company.contracts, :empty? + end + def test_collection_size_twice_for_regressions post = posts(:thinking) assert_equal 0, post.readers.size @@ -1705,4 +1712,21 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 0, post.comments.count end end + + test "collection proxy respects default scope" do + author = authors(:mary) + assert !author.first_posts.exists? + end + + test "association with extend option" do + post = posts(:welcome) + assert_equal "lifo", post.comments_with_extend.author + assert_equal "hello", post.comments_with_extend.greeting + end + + test "association with extend option with multiple extensions" do + post = posts(:welcome) + assert_equal "lifo", post.comments_with_extend_2.author + assert_equal "hello", post.comments_with_extend_2.greeting + end end diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 9b00c21b52..10ec33be75 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -443,8 +443,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_has_many_through_uses_conditions_specified_on_the_has_many_association author = Author.first - assert_present author.comments - assert_blank author.nonexistant_comments + assert author.comments.present? + assert author.nonexistant_comments.blank? end def test_has_many_through_uses_correct_attributes diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index 03d99d19f6..e355ed3495 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -221,6 +221,9 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase end def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection_preload_via_joins + # preload table schemas + Author.joins(:post_categories).first + assert_includes_and_joins_equal( Author.where('categories.id' => categories(:cooking).id), [authors(:bob)], :post_categories @@ -246,6 +249,9 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase end def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload_via_joins + # preload table schemas + Category.joins(:post_comments).first + assert_includes_and_joins_equal( Category.where('comments.id' => comments(:more_greetings).id).order('categories.id'), [categories(:general), categories(:technology)], :post_comments @@ -271,6 +277,9 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase end def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload_via_joins + # preload table schemas + Author.joins(:category_post_comments).first + assert_includes_and_joins_equal( Author.where('comments.id' => comments(:does_it_hurt).id).order('authors.id'), [authors(:david), authors(:mary)], :category_post_comments diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 5cf597b9f2..4f46459ab3 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -300,13 +300,11 @@ class BasicsTest < ActiveRecord::TestCase end def test_initialize_with_invalid_attribute - begin - Topic.new({ "title" => "test", - "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"}) - rescue ActiveRecord::MultiparameterAssignmentErrors => ex - assert_equal(1, ex.errors.size) - assert_equal("last_read", ex.errors[0].attribute) - end + Topic.new({ "title" => "test", + "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"}) + rescue ActiveRecord::MultiparameterAssignmentErrors => ex + assert_equal(1, ex.errors.size) + assert_equal("last_read", ex.errors[0].attribute) end def test_create_after_initialize_without_block diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb index 2124c256fa..adbe51f430 100644 --- a/activerecord/test/cases/column_test.rb +++ b/activerecord/test/cases/column_test.rb @@ -1,4 +1,5 @@ require "cases/helper" +require 'models/company' module ActiveRecord module ConnectionAdapters @@ -40,13 +41,26 @@ module ActiveRecord def test_type_cast_non_integer_to_integer column = Column.new("field", nil, "integer") - assert_raises(NoMethodError) do - column.type_cast([]) - end + assert_nil column.type_cast([1,2]) + assert_nil column.type_cast({1 => 2}) + assert_nil column.type_cast((1..2)) + end - assert_raises(NoMethodError) do - column.type_cast(Object.new) - end + def test_type_cast_activerecord_to_integer + column = Column.new("field", nil, "integer") + firm = Firm.create(:name => 'Apple') + assert_nil column.type_cast(firm) + end + + def test_type_cast_object_without_to_i_to_integer + column = Column.new("field", nil, "integer") + assert_nil column.type_cast(Object.new) + end + + def test_type_cast_nan_and_infinity_to_integer + column = Column.new("field", nil, "integer") + assert_nil column.type_cast(Float::NAN) + assert_nil column.type_cast(1.0/0.0) end def test_type_cast_time diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 0718d0886f..ea344e992b 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -327,6 +327,20 @@ module ActiveRecord def test_pool_sets_connection_visitor assert @pool.connection.visitor.is_a?(Arel::Visitors::ToSql) end + + + #make sure exceptions are thrown when establish_connection + #is called with a anonymous class + def test_anonymous_class_exception + anonymous = Class.new(ActiveRecord::Base) + handler = ActiveRecord::Base.connection_handler + + assert_raises(RuntimeError){ + handler.establish_connection anonymous, nil + } + end + + end end end diff --git a/activerecord/test/cases/deprecated_dynamic_methods_test.rb b/activerecord/test/cases/deprecated_dynamic_methods_test.rb index dde36e7f72..8e842d8758 100644 --- a/activerecord/test/cases/deprecated_dynamic_methods_test.rb +++ b/activerecord/test/cases/deprecated_dynamic_methods_test.rb @@ -1,7 +1,7 @@ # This file should be deleted when activerecord-deprecated_finders is removed as # a dependency. # -# It is kept for now as there is some fairly nuanced behaviour in the dynamic +# It is kept for now as there is some fairly nuanced behavior in the dynamic # finders so it is useful to keep this around to guard against regressions if # we need to change the code. @@ -568,9 +568,9 @@ class DynamicScopeTest < ActiveRecord::TestCase end def test_dynamic_scope_should_create_methods_after_hitting_method_missing - assert_blank @test_klass.methods.grep(/scoped_by_type/) + assert @test_klass.methods.grep(/scoped_by_type/).blank? @test_klass.scoped_by_type(nil) - assert_present @test_klass.methods.grep(/scoped_by_type/) + assert @test_klass.methods.grep(/scoped_by_type/).present? end def test_dynamic_scope_with_less_number_of_arguments diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb index 4e2adff344..eca500f7e4 100644 --- a/activerecord/test/cases/dup_test.rb +++ b/activerecord/test/cases/dup_test.rb @@ -108,18 +108,20 @@ module ActiveRecord end def test_dup_validity_is_independent - Topic.validates_presence_of :title - topic = Topic.new("title" => "Litterature") - topic.valid? - - duped = topic.dup - duped.title = nil - assert duped.invalid? - - topic.title = nil - duped.title = 'Mathematics' - assert topic.invalid? - assert duped.valid? + repair_validations(Topic) do + Topic.validates_presence_of :title + topic = Topic.new("title" => "Litterature") + topic.valid? + + duped = topic.dup + duped.title = nil + assert duped.invalid? + + topic.title = nil + duped.title = 'Mathematics' + assert topic.invalid? + assert duped.valid? + end end end end diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb index a7e5fdf709..aa2a6d7509 100644 --- a/activerecord/test/cases/explain_test.rb +++ b/activerecord/test/cases/explain_test.rb @@ -122,7 +122,7 @@ if ActiveRecord::Base.connection.supports_explain? base.expects(:collecting_sqls_for_explain).never base.logger.expects(:warn).never base.silence_auto_explain do - with_threshold(0) { Car.all } + with_threshold(0) { Car.all.to_a } end end diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 345e83a102..57eac0c175 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -8,6 +8,19 @@ class LogSubscriberTest < ActiveRecord::TestCase include ActiveSupport::LogSubscriber::TestHelper include ActiveSupport::Logger::Severity + class TestDebugLogSubscriber < ActiveRecord::LogSubscriber + attr_reader :debugs + + def initialize + @debugs = [] + super + end + + def debug message + @debugs << message + end + end + fixtures :posts def setup @@ -30,30 +43,27 @@ class LogSubscriberTest < ActiveRecord::TestCase def test_schema_statements_are_ignored event = Struct.new(:duration, :payload) - logger = Class.new(ActiveRecord::LogSubscriber) { - attr_accessor :debugs - - def initialize - @debugs = [] - super - end - - def debug message - @debugs << message - end - }.new + logger = TestDebugLogSubscriber.new assert_equal 0, logger.debugs.length - logger.sql(event.new(0, { :sql => 'hi mom!' })) + logger.sql(event.new(0, sql: 'hi mom!')) assert_equal 1, logger.debugs.length - logger.sql(event.new(0, { :sql => 'hi mom!', :name => 'foo' })) + logger.sql(event.new(0, sql: 'hi mom!', name: 'foo')) assert_equal 2, logger.debugs.length - logger.sql(event.new(0, { :sql => 'hi mom!', :name => 'SCHEMA' })) + logger.sql(event.new(0, sql: 'hi mom!', name: 'SCHEMA')) assert_equal 2, logger.debugs.length end + def test_ignore_binds_payload_with_nil_column + event = Struct.new(:duration, :payload) + + logger = TestDebugLogSubscriber.new + logger.sql(event.new(0, sql: 'hi mom!', binds: [[nil, 1]])) + assert_equal 1, logger.debugs.length + end + def test_basic_query_logging Developer.all.load wait @@ -105,7 +115,7 @@ class LogSubscriberTest < ActiveRecord::TestCase def test_binary_data_is_not_logged skip if current_adapter?(:Mysql2Adapter) - Binary.create(:data => 'some binary data') + Binary.create(data: 'some binary data') wait assert_match(/<16 bytes of binary data>/, @logger.logged(:debug).join) end diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index 5ac4a16f33..cad759bba9 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -35,7 +35,7 @@ module ActiveRecord t.column :foo, :string end - assert_equal %w(foo id), connection.columns(:testings).map(&:name).sort + assert_equal %w(id foo), connection.columns(:testings).map(&:name) end def test_create_table_with_not_null_column @@ -119,7 +119,7 @@ module ActiveRecord t.column :foo, :string end - assert_equal %w(foo testing_id), connection.columns(:testings).map(&:name).sort + assert_equal %w(testing_id foo), connection.columns(:testings).map(&:name) end def test_create_table_with_primary_key_prefix_as_table_name @@ -129,7 +129,7 @@ module ActiveRecord t.column :foo, :string end - assert_equal %w(foo testingid), connection.columns(:testings).map(&:name).sort + assert_equal %w(testingid foo), connection.columns(:testings).map(&:name) end def test_create_table_raises_when_redefining_primary_key_column diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 9cb64a6a71..fa8dec0e15 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -24,15 +24,19 @@ class MigrationTest < ActiveRecord::TestCase def setup super - %w(reminders people_reminders prefix_reminders_suffix).each do |table| + %w(reminders people_reminders prefix_reminders_suffix p_things_s).each do |table| Reminder.connection.drop_table(table) rescue nil end Reminder.reset_column_information ActiveRecord::Migration.verbose = true ActiveRecord::Migration.message_count = 0 + ActiveRecord::Base.connection.schema_cache.clear! end def teardown + ActiveRecord::Base.table_name_prefix = "" + ActiveRecord::Base.table_name_suffix = "" + ActiveRecord::Base.connection.initialize_schema_migrations_table ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}" @@ -44,6 +48,7 @@ class MigrationTest < ActiveRecord::TestCase %w(reminders people_reminders prefix_reminders_suffix).each do |table| Reminder.connection.drop_table(table) rescue nil end + Reminder.reset_table_name Reminder.reset_column_information %w(last_name key bio age height wealth birthday favorite_day @@ -257,9 +262,6 @@ class MigrationTest < ActiveRecord::TestCase ActiveRecord::Base.table_name_suffix = "" Reminder.reset_table_name assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name - ensure - ActiveRecord::Base.table_name_prefix = "" - ActiveRecord::Base.table_name_suffix = "" end def test_proper_table_name @@ -286,9 +288,6 @@ class MigrationTest < ActiveRecord::TestCase Reminder.reset_table_name assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table') assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table) - ActiveRecord::Base.table_name_prefix = "" - ActiveRecord::Base.table_name_suffix = "" - Reminder.reset_table_name end def test_rename_table_with_prefix_and_suffix @@ -307,8 +306,6 @@ class MigrationTest < ActiveRecord::TestCase assert_equal "hello world", Thing.first.content ensure - ActiveRecord::Base.table_name_prefix = '' - ActiveRecord::Base.table_name_suffix = '' Thing.reset_table_name Thing.reset_sequence_name end @@ -326,9 +323,6 @@ class MigrationTest < ActiveRecord::TestCase WeNeedReminders.down assert_raise(ActiveRecord::StatementInvalid) { Reminder.first } ensure - ActiveRecord::Base.table_name_prefix = '' - ActiveRecord::Base.table_name_suffix = '' - Reminder.reset_table_name Reminder.reset_sequence_name end @@ -437,6 +431,8 @@ if ActiveRecord::Base.connection.supports_bulk_alter? def setup @connection = Person.connection @connection.create_table(:delete_me, :force => true) {|t| } + Person.reset_column_information + Person.reset_sequence_name end def teardown diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb index e905006570..b5a69c4a92 100644 --- a/activerecord/test/cases/migrator_test.rb +++ b/activerecord/test/cases/migrator_test.rb @@ -29,6 +29,7 @@ module ActiveRecord def teardown super ActiveRecord::SchemaMigration.delete_all rescue nil + ActiveRecord::Migration.verbose = true end def test_migrator_with_duplicate_names diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 9574678e38..94837341fc 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -311,7 +311,7 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase end def test_should_also_work_with_a_HashWithIndifferentAccess - @pirate.ship_attributes = HashWithIndifferentAccess.new(:id => @ship.id, :name => 'Davy Jones Gold Dagger') + @pirate.ship_attributes = ActiveSupport::HashWithIndifferentAccess.new(:id => @ship.id, :name => 'Davy Jones Gold Dagger') assert @pirate.ship.persisted? assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name @@ -593,7 +593,7 @@ module NestedAttributesOnACollectionAssociationTests end def test_should_also_work_with_a_HashWithIndifferentAccess - @pirate.send(association_setter, HashWithIndifferentAccess.new('foo' => HashWithIndifferentAccess.new(:id => @child_1.id, :name => 'Grace OMalley'))) + @pirate.send(association_setter, ActiveSupport::HashWithIndifferentAccess.new('foo' => ActiveSupport::HashWithIndifferentAccess.new(:id => @child_1.id, :name => 'Grace OMalley'))) @pirate.save assert_equal 'Grace OMalley', @child_1.reload.name end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 85b069b593..b936cca875 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -242,7 +242,7 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal "David", topic2.author_name end - def test_update + def test_update_object topic = Topic.new topic.title = "Another New Topic" topic.written_on = "2003-12-12 23:23:00" @@ -646,7 +646,7 @@ class PersistencesTest < ActiveRecord::TestCase assert !topic.approved? assert_equal "The First Topic", topic.title end - + def test_update_attributes topic = Topic.find(1) assert !topic.approved? diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index 297e865308..f69a248491 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -82,6 +82,10 @@ module ActiveRecord assert_equal 0, Post.where(:posts => {}).count end + def test_where_with_table_name_and_empty_array + assert_equal 0, Post.where(:id => []).count + end + def test_where_with_empty_hash_and_no_foreign_key assert_equal 0, Edge.where(:sink => {}).count end diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index d318dab1e1..7388324a0d 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -161,6 +161,28 @@ class RelationScopingTest < ActiveRecord::TestCase assert !Developer.all.where_values.include?("name = 'Jamis'") end + + def test_default_scope_filters_on_joins + assert_equal 1, DeveloperFilteredOnJoins.all.count + assert_equal DeveloperFilteredOnJoins.all.first, developers(:david).becomes(DeveloperFilteredOnJoins) + end + + def test_update_all_default_scope_filters_on_joins + DeveloperFilteredOnJoins.update_all(:salary => 65000) + assert_equal 65000, Developer.find(developers(:david).id).salary + + # has not changed jamis + assert_not_equal 65000, Developer.find(developers(:jamis).id).salary + end + + def test_delete_all_default_scope_filters_on_joins + assert_not_equal [], DeveloperFilteredOnJoins.all + + DeveloperFilteredOnJoins.delete_all() + + assert_equal [], DeveloperFilteredOnJoins.all + assert_not_equal [], Developer.all + end end class NestedRelationScopingTest < ActiveRecord::TestCase @@ -227,7 +249,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase def test_nested_exclusive_scope_for_create comment = Comment.create_with(:body => "Hey guys, nested scopes are broken. Please fix!").scoping do Comment.unscoped.create_with(:post_id => 1).scoping do - assert_blank Comment.new.body + assert Comment.new.body.blank? Comment.create :body => "Hey guys" end end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 0cd838c0b0..64f1c9f6cc 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -404,6 +404,13 @@ class RelationTest < ActiveRecord::TestCase end end + def test_preload_applies_to_all_chained_preloaded_scopes + assert_queries(3) do + post = Post.with_comments.with_tags.first + assert post + end + end + def test_find_with_included_associations assert_queries(2) do posts = Post.includes(:comments).order('posts.id') @@ -509,7 +516,7 @@ class RelationTest < ActiveRecord::TestCase def test_find_in_empty_array authors = Author.all.where(:id => []) - assert_blank authors.to_a + assert authors.to_a.blank? end def test_where_with_ar_object @@ -723,7 +730,7 @@ class RelationTest < ActiveRecord::TestCase def test_relation_merging_with_locks devs = Developer.lock.where("salary >= 80000").order("id DESC").merge(Developer.limit(2)) - assert_present devs.locked + assert devs.locked.present? end def test_relation_merging_with_preload diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 907b7bec30..cae12e0e3a 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -280,6 +280,13 @@ class SchemaDumperTest < ActiveRecord::TestCase end end + def test_schema_dump_includes_ltrees_shorthand_definition + output = standard_dump + if %r{create_table "postgresql_ltrees"} =~ output + assert_match %r[t.ltree "path"], output + end + end + def test_schema_dump_includes_arrays_shorthand_definition output = standard_dump if %r{create_table "postgresql_arrays"} =~ output diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb index 562ca8d9ff..43bf285ba9 100644 --- a/activerecord/test/cases/store_test.rb +++ b/activerecord/test/cases/store_test.rb @@ -67,9 +67,9 @@ class StoreTest < ActiveRecord::TestCase end test "preserve store attributes data in HashWithIndifferentAccess format without any conversion" do - @john.json_data = HashWithIndifferentAccess.new(:height => 'tall', 'weight' => 'heavy') + @john.json_data = ActiveSupport::HashWithIndifferentAccess.new(:height => 'tall', 'weight' => 'heavy') @john.height = 'low' - assert_equal true, @john.json_data.instance_of?(HashWithIndifferentAccess) + assert_equal true, @john.json_data.instance_of?(ActiveSupport::HashWithIndifferentAccess) assert_equal 'low', @john.json_data[:height] assert_equal 'low', @john.json_data['height'] assert_equal 'heavy', @john.json_data[:weight] @@ -95,7 +95,7 @@ class StoreTest < ActiveRecord::TestCase test "convert store attributes from any format other than Hash or HashWithIndifferent access losing the data" do @john.json_data = "somedata" @john.height = 'low' - assert_equal true, @john.json_data.instance_of?(HashWithIndifferentAccess) + assert_equal true, @john.json_data.instance_of?(ActiveSupport::HashWithIndifferentAccess) assert_equal 'low', @john.json_data[:height] assert_equal 'low', @john.json_data['height'] assert_equal false, @john.json_data.delete_if { |k, v| k == 'height' }.any? @@ -143,5 +143,4 @@ class StoreTest < ActiveRecord::TestCase assert_raise(NoMethodError) { @john.stored_attributes = Hash.new } assert_raise(NoMethodError) { @john.stored_attributes } end - end diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb index 4f3489b7a5..659d5eae72 100644 --- a/activerecord/test/cases/tasks/database_tasks_test.rb +++ b/activerecord/test/cases/tasks/database_tasks_test.rb @@ -11,10 +11,10 @@ module ActiveRecord end ADAPTERS_TASKS = { - :mysql => :mysql_tasks, - :mysql2 => :mysql_tasks, - :postgresql => :postgresql_tasks, - :sqlite3 => :sqlite_tasks + mysql: :mysql_tasks, + mysql2: :mysql_tasks, + postgresql: :postgresql_tasks, + sqlite3: :sqlite_tasks } class DatabaseTasksRegisterTask < ActiveRecord::TestCase @@ -32,7 +32,7 @@ module ActiveRecord ActiveRecord::Tasks::DatabaseTasks.structure_dump({'adapter' => :foo}, "awesome-file.sql") end end - + class DatabaseTasksCreateTest < ActiveRecord::TestCase include DatabaseTasksSetupper @@ -258,7 +258,7 @@ module ActiveRecord class DatabaseTasksCharsetTest < ActiveRecord::TestCase include DatabaseTasksSetupper - + ADAPTERS_TASKS.each do |k, v| define_method("test_#{k}_charset") do eval("@#{v}").expects(:charset) @@ -269,7 +269,7 @@ module ActiveRecord class DatabaseTasksCollationTest < ActiveRecord::TestCase include DatabaseTasksSetupper - + ADAPTERS_TASKS.each do |k, v| define_method("test_#{k}_collation") do eval("@#{v}").expects(:collation) diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb index 69a049fcfa..38b9dd02f0 100644 --- a/activerecord/test/cases/tasks/mysql_rake_test.rb +++ b/activerecord/test/cases/tasks/mysql_rake_test.rb @@ -53,6 +53,16 @@ module ActiveRecord ActiveRecord::Tasks::DatabaseTasks.create @configuration end + + def test_create_when_database_exists_outputs_info_to_stderr + $stderr.expects(:puts).with("my-app-db already exists").once + + ActiveRecord::Base.connection.stubs(:create_database).raises( + ActiveRecord::StatementInvalid.new("Can't create database 'dev'; database exists:") + ) + + ActiveRecord::Tasks::DatabaseTasks.create @configuration + end end class MysqlDBCreateAsRootTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb index 62acd53003..3006a87589 100644 --- a/activerecord/test/cases/tasks/postgresql_rake_test.rb +++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb @@ -61,6 +61,16 @@ module ActiveRecord ActiveRecord::Tasks::DatabaseTasks.create @configuration end + + def test_create_when_database_exists_outputs_info_to_stderr + $stderr.expects(:puts).with("my-app-db already exists").once + + ActiveRecord::Base.connection.stubs(:create_database).raises( + ActiveRecord::StatementInvalid.new('database "my-app-db" already exists') + ) + + ActiveRecord::Tasks::DatabaseTasks.create @configuration + end end class PostgreSQLDBDropTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index bcbc48b38a..546737b398 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -451,6 +451,34 @@ class TransactionTest < ActiveRecord::TestCase end end + def test_transactions_state_from_rollback + connection = Topic.connection + transaction = ActiveRecord::ConnectionAdapters::ClosedTransaction.new(connection).begin + + assert transaction.open? + assert !transaction.state.rolledback? + assert !transaction.state.committed? + + transaction.perform_rollback + + assert transaction.state.rolledback? + assert !transaction.state.committed? + end + + def test_transactions_state_from_commit + connection = Topic.connection + transaction = ActiveRecord::ConnectionAdapters::ClosedTransaction.new(connection).begin + + assert transaction.open? + assert !transaction.state.rolledback? + assert !transaction.state.committed? + + transaction.perform_commit + + assert !transaction.state.rolledback? + assert transaction.state.committed? + end + private %w(validation save destroy).each do |filter| diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 683cb54a10..81bc87bd42 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -101,6 +101,15 @@ class DeveloperWithIncludes < ActiveRecord::Base default_scope { includes(:audit_logs) } end +class DeveloperFilteredOnJoins < ActiveRecord::Base + self.table_name = 'developers' + has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects' + + def self.default_scope + joins(:projects).where(:projects => { :name => 'Active Controller' }) + end +end + class DeveloperOrderedBySalary < ActiveRecord::Base self.table_name = 'developers' default_scope { order('salary DESC') } diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index c995f59a15..603f1f2555 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -5,6 +5,12 @@ class Post < ActiveRecord::Base end end + module NamedExtension2 + def greeting + "hello" + end + end + scope :containing_the_letter_a, -> { where("body LIKE '%a%'") } scope :ranked_by_comments, -> { order("comments_count DESC") } @@ -29,6 +35,9 @@ class Post < ActiveRecord::Base scope :with_very_special_comments, -> { joins(:comments).where(:comments => {:type => 'VerySpecialComment'}) } scope :with_post, ->(post_id) { joins(:comments).where(:comments => { :post_id => post_id }) } + scope :with_comments, -> { preload(:comments) } + scope :with_tags, -> { preload(:taggings) } + has_many :comments do def find_most_recent order("id DESC").first @@ -43,6 +52,14 @@ class Post < ActiveRecord::Base end end + has_many :comments_with_extend, extend: NamedExtension, class_name: "Comment", foreign_key: "post_id" do + def greeting + "hello" + end + end + + has_many :comments_with_extend_2, extend: [NamedExtension, NamedExtension2], class_name: "Comment", foreign_key: "post_id" + has_many :author_favorites, :through => :author has_many :author_categorizations, :through => :author, :source => :categorizations has_many :author_addresses, :through => :author diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb index 96fef3a831..ae13f2cd8a 100644 --- a/activerecord/test/schema/postgresql_specific_schema.rb +++ b/activerecord/test/schema/postgresql_specific_schema.rb @@ -1,6 +1,6 @@ ActiveRecord::Schema.define do - %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_bit_strings postgresql_uuids + %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_bit_strings postgresql_uuids postgresql_ltrees postgresql_oids postgresql_xml_data_type defaults geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent postgresql_json_data_type postgresql_intrange_data_type).each do |table_name| execute "DROP TABLE IF EXISTS #{quote_table_name table_name}" end @@ -89,6 +89,15 @@ _SQL _SQL end + if 't' == select_value("select 'ltree'=ANY(select typname from pg_type)") + execute <<_SQL + CREATE TABLE postgresql_ltrees ( + id SERIAL PRIMARY KEY, + path ltree + ); +_SQL + end + if 't' == select_value("select 'json'=ANY(select typname from pg_type)") execute <<_SQL CREATE TABLE postgresql_json_data_type ( |