From 5662ea511225b34d3b533d03614c496c007215b0 Mon Sep 17 00:00:00 2001 From: "Juan M. Cuello" Date: Wed, 21 Dec 2011 17:00:25 -0300 Subject: Reset postgreSQL search path in db:test:clone_structure. This patch resets the postgres search path in the structure.sql after the structure is dumped in order to find schema_migrations table when multiples schemas are used. Fixes #945 --- activerecord/lib/active_record/railties/databases.rake | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 199eee4359..482901f23f 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -387,6 +387,7 @@ db_namespace = namespace :db do end `pg_dump -i -s -x -O -f #{filename} #{search_path} #{abcs[Rails.env]['database']}` raise 'Error dumping database' if $?.exitstatus == 1 + File.open(filename, "a") { |f| f << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" } when /sqlite/ dbfile = abcs[Rails.env]['database'] `sqlite3 #{dbfile} .schema > #{filename}` -- cgit v1.2.3 From a2bf014330310da718dd92ffdfd0435463ed3a4f Mon Sep 17 00:00:00 2001 From: Fotos Georgiadis Date: Fri, 2 Mar 2012 19:12:11 +0200 Subject: Maximum wait_timeout on Windows is 2147483 --- activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 3f45f23de8..b343898854 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -260,7 +260,7 @@ module ActiveRecord # increase timeout so mysql server doesn't disconnect us wait_timeout = @config[:wait_timeout] - wait_timeout = 2592000 unless wait_timeout.is_a?(Fixnum) + wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum) variable_assignments << "@@wait_timeout = #{wait_timeout}" execute("SET #{variable_assignments.join(', ')}", :skip_logging) -- cgit v1.2.3 From fa5f037551dfb860bf6359acf901915856646fea Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Mon, 24 Oct 2011 14:47:01 +0200 Subject: allow the :converter Proc form composed_of to return nil This makes it possible to filter invalid input values before they are passed into the value-object (like empty strings). This behaviour is only relevant if the :allow_nil options is set to true. Otherwise you will get the resulting NoMethodError. --- activerecord/lib/active_record/aggregations.rb | 15 ++++++++------- activerecord/test/cases/aggregations_test.rb | 18 ++++++++++++++++++ activerecord/test/models/customer.rb | 5 +++++ 3 files changed, 31 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 90d3b58c78..f7dcc019c8 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -187,7 +187,8 @@ module ActiveRecord # * :converter - A symbol specifying the name of a class method of :class_name # or a Proc that is called when a new value is assigned to the value object. The converter is # passed the single value that is used in the assignment and is only called if the new value is - # not an instance of :class_name. + # not an instance of :class_name. If :allow_nil is set to true, the converter + # can return nil to skip the assignment. # # Option examples: # composed_of :temperature, :mapping => %w(reading celsius) @@ -234,16 +235,16 @@ module ActiveRecord def writer_method(name, class_name, mapping, allow_nil, converter) define_method("#{name}=") do |part| + unless part.is_a?(class_name.constantize) || converter.nil? || part.nil? + part = converter.respond_to?(:call) ? + converter.call(part) : + class_name.constantize.send(converter, part) + end + if part.nil? && allow_nil mapping.each { |pair| self[pair.first] = nil } @aggregation_cache[name] = nil else - unless part.is_a?(class_name.constantize) || converter.nil? - part = converter.respond_to?(:call) ? - converter.call(part) : - class_name.constantize.send(converter, part) - end - mapping.each { |pair| self[pair.first] = part.send(pair.last) } @aggregation_cache[name] = part.freeze end diff --git a/activerecord/test/cases/aggregations_test.rb b/activerecord/test/cases/aggregations_test.rb index 3e0e6dce2c..5bd8f76ba2 100644 --- a/activerecord/test/cases/aggregations_test.rb +++ b/activerecord/test/cases/aggregations_test.rb @@ -109,6 +109,24 @@ class AggregationsTest < ActiveRecord::TestCase assert_nil customers(:david).gps_location end + def test_nil_return_from_converter_is_respected_when_allow_nil_is_true + customers(:david).non_blank_gps_location = "" + customers(:david).save + customers(:david).reload + assert_nil customers(:david).non_blank_gps_location + end + + def test_nil_return_from_converter_results_in_failure_when_allow_nil_is_false + assert_raises(NoMethodError) do + customers(:barney).gps_location = "" + end + end + + def test_do_not_run_the_converter_when_nil_was_set + customers(:david).non_blank_gps_location = nil + assert_nil Customer.gps_conversion_was_run + end + def test_custom_constructor assert_equal 'Barney GUMBLE', customers(:barney).fullname.to_s assert_kind_of Fullname, customers(:barney).fullname diff --git a/activerecord/test/models/customer.rb b/activerecord/test/models/customer.rb index 777f6b5ba0..807f25b687 100644 --- a/activerecord/test/models/customer.rb +++ b/activerecord/test/models/customer.rb @@ -1,7 +1,12 @@ class Customer < ActiveRecord::Base + + cattr_accessor :gps_conversion_was_run + composed_of :address, :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ], :allow_nil => true composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| balance.to_money } composed_of :gps_location, :allow_nil => true + composed_of :non_blank_gps_location, :class_name => "GpsLocation", :allow_nil => true, :mapping => %w(gps_location gps_location), + :converter => lambda { |gps| self.gps_conversion_was_run = true; gps.blank? ? nil : GpsLocation.new(gps)} composed_of :fullname, :mapping => %w(name to_s), :constructor => Proc.new { |name| Fullname.parse(name) }, :converter => :parse end -- cgit v1.2.3 From 6f3489c52a407040888605b5a984e010236aec01 Mon Sep 17 00:00:00 2001 From: Ivan Evtukhovich Date: Tue, 17 Apr 2012 12:26:23 +0400 Subject: Remove prepared statement from system query in postgresql adapter --- .../connection_adapters/postgresql_adapter.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 4d5459939b..871393b37b 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -916,22 +916,22 @@ module ActiveRecord binds = [[nil, table]] binds << [nil, schema] if schema - exec_query(<<-SQL, 'SCHEMA', binds).rows.first[0].to_i > 0 + exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0 SELECT COUNT(*) FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind in ('v','r') - AND c.relname = $1 - AND n.nspname = #{schema ? '$2' : 'ANY (current_schemas(false))'} + AND c.relname = '#{table.gsub(/(^"|"$)/,'')}' + AND n.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'} SQL end # Returns true if schema exists. def schema_exists?(name) - exec_query(<<-SQL, 'SCHEMA', [[nil, name]]).rows.first[0].to_i > 0 + exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0 SELECT COUNT(*) FROM pg_namespace - WHERE nspname = $1 + WHERE nspname = '#{name}' SQL end @@ -1062,8 +1062,8 @@ module ActiveRecord end def serial_sequence(table, column) - result = exec_query(<<-eosql, 'SCHEMA', [[nil, table], [nil, column]]) - SELECT pg_get_serial_sequence($1, $2) + result = exec_query(<<-eosql, 'SCHEMA') + SELECT pg_get_serial_sequence('#{table}', '#{column}') eosql result.rows.first.first end @@ -1140,13 +1140,13 @@ module ActiveRecord # Returns just a table's primary key def primary_key(table) - row = exec_query(<<-end_sql, 'SCHEMA', [[nil, table]]).rows.first + row = exec_query(<<-end_sql, 'SCHEMA').rows.first SELECT DISTINCT(attr.attname) FROM pg_attribute attr INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1] WHERE cons.contype = 'p' - AND dep.refobjid = $1::regclass + AND dep.refobjid = '#{table}'::regclass end_sql row && row.first @@ -1408,7 +1408,7 @@ module ActiveRecord end def last_insert_id_result(sequence_name) #:nodoc: - exec_query("SELECT currval($1)", 'SQL', [[nil, sequence_name]]) + exec_query("SELECT currval('#{sequence_name}')", 'SQL') end # Executes a SELECT query and returns the results, performing any data type -- cgit v1.2.3 From 2fe281323c8ccebc592d423b69b5b03ac9254b29 Mon Sep 17 00:00:00 2001 From: kennyj Date: Sun, 20 May 2012 01:38:14 +0900 Subject: Fix a problem of translate_exception method in Japanese. --- .../active_record/connection_adapters/postgresql_adapter.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 14bc95abfe..15c3d7be36 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -1336,11 +1336,15 @@ module ActiveRecord @connection.server_version end + # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html + FOREIGN_KEY_VIOLATION = "23503" + UNIQUE_VIOLATION = "23505" + def translate_exception(exception, message) - case exception.message - when /duplicate key value violates unique constraint/ + case exception.result.error_field(PGresult::PG_DIAG_SQLSTATE) + when UNIQUE_VIOLATION RecordNotUnique.new(message, exception) - when /violates foreign key constraint/ + when FOREIGN_KEY_VIOLATION InvalidForeignKey.new(message, exception) else super -- cgit v1.2.3 From c2d416fe7712cbaf69036f9e638c825e08415e34 Mon Sep 17 00:00:00 2001 From: Patrick Mahoney Date: Sat, 19 May 2012 11:45:55 -0500 Subject: Synchronize read and modification of @reserved_connections hash to avoid concurrency error. --- .../connection_adapters/abstract/connection_pool.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'activerecord') 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 46c7fc71ac..c6699737b4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -139,14 +139,18 @@ module ActiveRecord # #connection can be called any number of times; the connection is # held in a hash keyed by the thread id. def connection - @reserved_connections[current_connection_id] ||= checkout + synchronize do + @reserved_connections[current_connection_id] ||= checkout + end end # Is there an open connection that is being used for the current thread? def active_connection? - @reserved_connections.fetch(current_connection_id) { - return false - }.in_use? + synchronize do + @reserved_connections.fetch(current_connection_id) { + return false + }.in_use? + end end # Signal that the thread is finished with the current connection. -- cgit v1.2.3 From 45d059b401f4fdd926d263ad8a3195b5b2223a1c Mon Sep 17 00:00:00 2001 From: Ivan Kukobko Date: Sun, 20 May 2012 22:11:32 +0300 Subject: fixed typo in word finiding --- activerecord/test/cases/relations_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 4a56ae0d23..2dc8f0053b 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -133,7 +133,7 @@ class RelationTest < ActiveRecord::TestCase assert topics.loaded? end - def test_finiding_with_subquery + def test_finding_with_subquery relation = Topic.where(:approved => true) assert_equal relation.to_a, Topic.select('*').from(relation).to_a assert_equal relation.to_a, Topic.select('subquery.*').from(relation).to_a -- cgit v1.2.3 From 03402d206ae1d1977e5283173ecb43a88aacead0 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Sun, 20 May 2012 16:11:52 -0500 Subject: update CollectionProxy#destroy_all documentation --- .../lib/active_record/associations/collection_proxy.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 647d495d56..21c5e3fc5a 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -221,18 +221,26 @@ module ActiveRecord ## # :method: destroy_all - # Destroy all the records from this association. + # Deletes the records of the collection directly from the database. # # class Person < ActiveRecord::Base # has_many :pets # end # # person.pets.size # => 3 + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] # # person.pets.destroy_all # # person.pets.size # => 0 # person.pets # => [] + # + # Pet.find(1) # => Couldn't find Pet with id=1 ## # :method: empty? -- cgit v1.2.3 From d06674d5dfcecb6355890020ee2245b6e87b07e4 Mon Sep 17 00:00:00 2001 From: Patrick Mahoney Date: Sun, 20 May 2012 21:46:08 -0500 Subject: Make connection pool fair with respect to waiting threads. --- .../abstract/connection_pool.rb | 120 +++++++++++++++------ .../connection_adapters/abstract_adapter_test.rb | 2 +- activerecord/test/cases/connection_pool_test.rb | 115 ++++++++++++++++++++ 3 files changed, 202 insertions(+), 35 deletions(-) (limited to 'activerecord') 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 c6699737b4..450ef69744 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 'monitor' require 'set' require 'active_support/core_ext/module/deprecation' -require 'timeout' module ActiveRecord # Raised when a connection could not be obtained within the connection @@ -92,21 +91,6 @@ module ActiveRecord attr_accessor :automatic_reconnect, :timeout attr_reader :spec, :connections, :size, :reaper - class Latch # :nodoc: - def initialize - @mutex = Mutex.new - @cond = ConditionVariable.new - end - - def release - @mutex.synchronize { @cond.broadcast } - end - - def await - @mutex.synchronize { @cond.wait @mutex } - end - end - # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification # object which describes database connection information (e.g. adapter, # host name, username, password, etc), as well as the maximum size for @@ -128,9 +112,25 @@ module ActiveRecord # default max pool size to 5 @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5 - @latch = Latch.new @connections = [] @automatic_reconnect = true + + # connections available to be checked out + @available = [] + + # number of threads waiting to check out a connection + @num_waiting = 0 + + # signal threads waiting + @cond = new_cond + end + + # Hack for tests to be able to add connections. Do not call outside of tests + def insert_connection_for_test!(c) + synchronize do + @connections << c + @available << c + end end # Retrieve the connection associated with the current thread, or call @@ -188,6 +188,7 @@ module ActiveRecord conn.disconnect! end @connections = [] + @available = [] end end @@ -202,6 +203,9 @@ module ActiveRecord @connections.delete_if do |conn| conn.requires_reloading? end + @available.delete_if do |conn| + conn.requires_reloading? + end end end @@ -225,23 +229,19 @@ module ActiveRecord # Raises: # - PoolFullError: no connection can be obtained from the pool. def checkout - loop do - # Checkout an available connection - synchronize do - # Try to find a connection that hasn't been leased, and lease it - conn = connections.find { |c| c.lease } - - # If all connections were leased, and we have room to expand, - # create a new connection and lease it. - if !conn && connections.size < size - conn = checkout_new_connection - conn.lease - end + synchronize do + conn = nil + + if @num_waiting == 0 + conn = acquire_connection + end - return checkout_and_verify(conn) if conn + unless conn + conn = wait_until(@timeout) { acquire_connection } end - Timeout.timeout(@timeout, PoolFullError) { @latch.await } + conn.lease + checkout_and_verify(conn) end end @@ -257,8 +257,10 @@ module ActiveRecord end release conn + + @available.unshift conn + @cond.signal end - @latch.release end # Remove a connection from the connection pool. The connection will @@ -266,12 +268,14 @@ module ActiveRecord def remove(conn) synchronize do @connections.delete conn + @available.delete conn # FIXME: we might want to store the key on the connection so that removing # from the reserved hash will be a little easier. release conn + + @cond.signal # can make a new connection now end - @latch.release end # Removes dead connections from the pool. A dead connection can occur @@ -283,12 +287,60 @@ module ActiveRecord connections.dup.each do |conn| remove conn if conn.in_use? && stale > conn.last_use && !conn.active? end + @cond.broadcast # may violate fairness end - @latch.release end private + # Take an available connection or, if possible, create a new + # one, or nil. + # + # Monitor must be held while calling this method. + # + # Returns: a newly acquired connection. + def acquire_connection + if @available.any? + @available.pop + elsif connections.size < size + checkout_new_connection + end + end + + # Wait on +@cond+ until the block returns non-nil. Note that + # unlike MonitorMixin::ConditionVariable#wait_until, this method + # does not test the block before the first wait period. + # + # Monitor must be held when calling this method. + # + # +timeout+: Integer timeout in seconds + # + # Returns: the result of the block + # + # Raises: + # - PoolFullError: timeout elapsed before +&block+ returned a connection + def wait_until(timeout, &block) + @num_waiting += 1 + begin + t0 = Time.now + loop do + elapsed = Time.now - t0 + if elapsed >= timeout + msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' % + [timeout, elapsed] + raise PoolFullError, msg + end + + @cond.wait(timeout - elapsed) + + conn = yield + return conn if conn + end + ensure + @num_waiting -= 1 + end + end + def release(conn) thread_id = if @reserved_connections[current_connection_id] == conn current_connection_id diff --git a/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb b/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb index 7dc6e8afcb..3e3d6e2769 100644 --- a/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb +++ b/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb @@ -36,7 +36,7 @@ module ActiveRecord def test_close pool = ConnectionPool.new(ConnectionSpecification.new({}, nil)) - pool.connections << adapter + pool.insert_connection_for_test! adapter adapter.pool = pool # Make sure the pool marks the connection in use diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 8dc9f761c2..6871e628aa 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -200,6 +200,121 @@ module ActiveRecord end.join end + # The connection pool is "fair" if threads waiting for + # connections receive them the order in which they began + # waiting. This ensures that we don't timeout one HTTP request + # even while well under capacity in a multi-threaded environment + # such as a Java servlet container. + # + # We don't need strict fairness: if two connections become + # available at the same time, it's fine of two threads that were + # waiting acquire the connections out of order. + # + # Thus this test prepares waiting threads and then trickles in + # available connections slowly, ensuring the wakeup order is + # correct in this case. + # + # Try a few times since it might work out just by chance. + def test_checkout_fairness + 4.times { setup; do_checkout_fairness } + end + + def do_checkout_fairness + expected = (1..@pool.size).to_a.freeze + # check out all connections so our threads start out waiting + conns = expected.map { @pool.checkout } + mutex = Mutex.new + order = [] + errors = [] + + threads = expected.map do |i| + t = Thread.new { + begin + conn = @pool.checkout # never checked back in + mutex.synchronize { order << i } + rescue => e + mutex.synchronize { errors << e } + end + } + Thread.pass until t.status == "sleep" + t + end + + # this should wake up the waiting threads one by one in order + conns.each { |conn| @pool.checkin(conn); sleep 0.1 } + + threads.each(&:join) + + raise errors.first if errors.any? + + assert_equal(expected, order) + end + + # As mentioned in #test_checkout_fairness, we don't care about + # strict fairness. This test creates two groups of threads: + # group1 whose members all start waiting before any thread in + # group2. Enough connections are checked in to wakeup all + # group1 threads, and the fact that only group1 and no group2 + # threads acquired a connection is enforced. + # + # Try a few times since it might work out just by chance. + def test_checkout_fairness_by_group + 4.times { setup; do_checkout_fairness_by_group } + end + + def do_checkout_fairness_by_group + @pool.instance_variable_set(:@size, 10) + # take all the connections + conns = (1..10).map { @pool.checkout } + mutex = Mutex.new + successes = [] # threads that successfully got a connection + errors = [] + + make_thread = proc do |i| + t = Thread.new { + begin + conn = @pool.checkout # never checked back in + mutex.synchronize { successes << i } + rescue => e + mutex.synchronize { errors << e } + end + } + Thread.pass until t.status == "sleep" + t + end + + # all group1 threads start waiting before any in group2 + group1 = (1..5).map(&make_thread) + group2 = (6..10).map(&make_thread) + + # checkin n connections back to the pool + checkin = proc do |n| + n.times do + c = conns.pop + @pool.checkin(c) + end + end + + checkin.call(group1.size) # should wake up all group1 + + loop do + sleep 0.1 + break if mutex.synchronize { (successes.size + errors.size) == group1.size } + end + + winners = mutex.synchronize { successes.dup } + checkin.call(group2.size) # should wake up everyone remaining + + group1.each(&:join) + group2.each(&:join) + + assert_equal((1..group1.size).to_a, winners.sort) + + if errors.any? + raise errors.first + end + end + def test_automatic_reconnect= pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec assert pool.automatic_reconnect -- cgit v1.2.3 From d83173483d397df8cdff913d9a4929684c7bd4ce Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Mon, 21 May 2012 10:16:43 -0500 Subject: add CollectionProxy#destroy documentation --- .../active_record/associations/collection_proxy.rb | 43 +++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 21c5e3fc5a..a3797e9d59 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -222,6 +222,8 @@ module ActiveRecord ## # :method: destroy_all # Deletes the records of the collection directly from the database. + # This will _always_ remove the records ignoring the +:dependent+ + # option. # # class Person < ActiveRecord::Base # has_many :pets @@ -242,6 +244,45 @@ module ActiveRecord # # Pet.find(1) # => Couldn't find Pet with id=1 + ## + # :method: destroy + # Destroy the +records+ supplied and remove them from the collection. + # This method will _always_ remove record from the database ignoring + # the +:dependent+ option. Returns an array with the deleted records. + # + # class Person < ActiveRecord::Base + # has_many :pets + # end + # + # person.pets.size # => 3 + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] + # + # person.pets.destroy(Pet.find(1)) + # # => [#] + # + # person.pets.size # => 2 + # person.pets + # # => [ + # # #, + # # # + # # ] + # + # person.pets.destroy(Pet.find(2), Pet.find(3)) + # # => [ + # # #, + # # # + # # ] + # + # person.pets.size # => 0 + # person.pets # => [] + # + # Pet.find([1, 2, 3]) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3) + ## # :method: empty? # Returns true if the collection is empty. @@ -257,7 +298,7 @@ module ActiveRecord # # person.pets.count # => 0 # person.pets.empty? # => true - + ## # :method: any? # Returns true if the collection is not empty. -- cgit v1.2.3 From 1f5c1a14236b2df638d20bcf1617110c47a4bfe8 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Mon, 21 May 2012 11:56:00 -0500 Subject: improve CollectionProxy#destroy documentation --- .../active_record/associations/collection_proxy.rb | 42 +++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index a3797e9d59..e3ebe03121 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -248,7 +248,7 @@ module ActiveRecord # :method: destroy # Destroy the +records+ supplied and remove them from the collection. # This method will _always_ remove record from the database ignoring - # the +:dependent+ option. Returns an array with the deleted records. + # the +:dependent+ option. Returns an array with the removed records. # # class Person < ActiveRecord::Base # has_many :pets @@ -278,10 +278,42 @@ module ActiveRecord # # # # # ] # - # person.pets.size # => 0 - # person.pets # => [] + # person.pets.size # => 0 + # person.pets # => [] # - # Pet.find([1, 2, 3]) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3) + # Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3) + # + # You can pass +Fixnum+ or +String+ values, it finds the records + # responding to the +id+ and then deletes them from the database. + # + # person.pets.size # => 3 + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] + # + # person.pets.destroy("4") + # # => # + # + # person.pets.size # => 2 + # person.pets + # # => [ + # # #, + # # # + # # ] + # + # person.pets.destroy(5, 6) + # # => [ + # # #, + # # # + # # ] + # + # person.pets.size # => 0 + # person.pets # => [] + # + # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6) ## # :method: empty? @@ -298,7 +330,7 @@ module ActiveRecord # # person.pets.count # => 0 # person.pets.empty? # => true - + ## # :method: any? # Returns true if the collection is not empty. -- cgit v1.2.3 From 158a71b2cbd6be9ef9c88282b48a3e39e47908ed Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Mon, 21 May 2012 12:19:34 -0500 Subject: add CollectionProxy#reload documentation --- .../lib/active_record/associations/collection_proxy.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index e3ebe03121..80a1715ee2 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -516,6 +516,24 @@ module ActiveRecord self end + # Reloads the collection from the database. Returns +self+. + # Equivalent to +collection(true)+. + # + # class Person < ActiveRecord::Base + # has_many :pets + # end + # + # person.pets # fetches pets from the database + # # => [#] + # + # person.pets # uses the pets cache + # # => [#] + # + # person.pets.reload # fetches pets from the database + # # => [#] + # + # person.pets(true)  # fetches pets from the database + # # => [#] def reload proxy_association.reload self -- cgit v1.2.3 From ab2e2a9d2b772cf4aef4d9f4f2d506a03c205035 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Mon, 21 May 2012 12:26:04 -0500 Subject: fix CollectionProxy documentation markup --- .../lib/active_record/associations/collection_proxy.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 80a1715ee2..b419b9a749 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -112,7 +112,7 @@ module ActiveRecord ## # :method: first # Returns the first record, or the first +n+ records, from the collection. - # If the collection is empty, the first form returns nil, and the second + # If the collection is empty, the first form returns +nil+, and the second # form returns an empty array. # # class Person < ActiveRecord::Base @@ -141,7 +141,7 @@ module ActiveRecord ## # :method: last # Returns the last record, or the last +n+ records, from the collection. - # If the collection is empty, the first form returns nil, and the second + # If the collection is empty, the first form returns +nil+, and the second # form returns an empty array. # # class Person < ActiveRecord::Base @@ -317,7 +317,7 @@ module ActiveRecord ## # :method: empty? - # Returns true if the collection is empty. + # Returns +true+ if the collection is empty. # # class Person < ActiveRecord::Base # has_many :pets @@ -333,7 +333,7 @@ module ActiveRecord ## # :method: any? - # Returns true if the collection is not empty. + # Returns +true+ if the collection is not empty. # # class Person < ActiveRecord::Base # has_many :pets @@ -366,7 +366,7 @@ module ActiveRecord ## # :method: many? # Returns true if the collection has more than one record. - # Equivalent to +collection.size > 1+. + # Equivalent to collection.size > 1. # # class Person < ActiveRecord::Base # has_many :pets @@ -402,7 +402,7 @@ module ActiveRecord ## # :method: include? - # Returns true if the given object is present in the collection. + # Returns +true+ if the given object is present in the collection. # # class Person < ActiveRecord::Base # has_many :pets @@ -499,7 +499,7 @@ module ActiveRecord # # Pet.find(1) # => # # - # If they are associated with +dependent: :destroy+ option, it deletes + # If they are associated with dependent: :destroy option, it deletes # them directly from the database. # # class Person < ActiveRecord::Base @@ -517,7 +517,7 @@ module ActiveRecord end # Reloads the collection from the database. Returns +self+. - # Equivalent to +collection(true)+. + # Equivalent to collection(true). # # class Person < ActiveRecord::Base # has_many :pets -- cgit v1.2.3 From d5d9a281aaf9bc0fd7f5e92e1609a7677f25887c Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Mon, 21 May 2012 18:34:31 -0300 Subject: Refactor aggregation writer method Only constantize class_name once. --- activerecord/lib/active_record/aggregations.rb | 7 +++---- activerecord/test/models/customer.rb | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 5de10a8dd6..f0b549e5ad 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -242,10 +242,9 @@ module ActiveRecord def writer_method(name, class_name, mapping, allow_nil, converter) define_method("#{name}=") do |part| - unless part.is_a?(class_name.constantize) || converter.nil? || part.nil? - part = converter.respond_to?(:call) ? - converter.call(part) : - class_name.constantize.send(converter, part) + klass = class_name.constantize + unless part.is_a?(klass) || converter.nil? || part.nil? + part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part) end if part.nil? && allow_nil diff --git a/activerecord/test/models/customer.rb b/activerecord/test/models/customer.rb index 807f25b687..7e8e82542f 100644 --- a/activerecord/test/models/customer.rb +++ b/activerecord/test/models/customer.rb @@ -1,5 +1,4 @@ class Customer < ActiveRecord::Base - cattr_accessor :gps_conversion_was_run composed_of :address, :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ], :allow_nil => true -- cgit v1.2.3 From 03886d817307e091024ff3bd26844447abd8c408 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Mon, 21 May 2012 18:35:44 -0300 Subject: Remove not used variables from connection poll test These variables were issuing some "not used" warnings. --- activerecord/test/cases/connection_pool_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 6871e628aa..b1905515d3 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -230,7 +230,7 @@ module ActiveRecord threads = expected.map do |i| t = Thread.new { begin - conn = @pool.checkout # never checked back in + @pool.checkout # never checked back in mutex.synchronize { order << i } rescue => e mutex.synchronize { errors << e } @@ -273,7 +273,7 @@ module ActiveRecord make_thread = proc do |i| t = Thread.new { begin - conn = @pool.checkout # never checked back in + @pool.checkout # never checked back in mutex.synchronize { successes << i } rescue => e mutex.synchronize { errors << e } -- cgit v1.2.3 From 5b7b705d36c58508e7da0a87dd6232f699653969 Mon Sep 17 00:00:00 2001 From: Alexey Vakhov Date: Tue, 22 May 2012 01:37:03 +0400 Subject: Fix AR preloader example --- activerecord/lib/active_record/associations/preloader.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb index fafed94ff2..54705e4950 100644 --- a/activerecord/lib/active_record/associations/preloader.rb +++ b/activerecord/lib/active_record/associations/preloader.rb @@ -12,7 +12,7 @@ module ActiveRecord # and all of its books via a single query: # # SELECT * FROM authors - # LEFT OUTER JOIN books ON authors.id = books.id + # LEFT OUTER JOIN books ON authors.id = books.author_id # WHERE authors.name = 'Ken Akamatsu' # # However, this could result in many rows that contain redundant data. After -- cgit v1.2.3 From cb847b9f2e56eeff737323d9a42a2a0a6c23804d Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Mon, 21 May 2012 14:57:04 -0700 Subject: Restore the frozen state on rollback. Fixes #6417. Currently, when saving a frozen record, an exception would be thrown which causes a rollback. However, there is a bug in active record that "defrost" the record as a side effect: >> t = Topic.new => # >> t.freeze => # >> t.save RuntimeError: can't modify a frozen Hash >> t.frozen? => false >> t.save => true This patch fixes the bug by explictly restoring the frozen state on the attributes Hash after every rollback. --- activerecord/lib/active_record/transactions.rb | 4 +++- activerecord/test/cases/transactions_test.rb | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 30e1035300..9cb9b4627b 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -329,7 +329,8 @@ module ActiveRecord @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 if @_start_transaction_state[:level] < 1 restore_state = remove_instance_variable(:@_start_transaction_state) - @attributes = @attributes.dup if @attributes.frozen? + was_frozen = @attributes.frozen? + @attributes = @attributes.dup if was_frozen @new_record = restore_state[:new_record] @destroyed = restore_state[:destroyed] if restore_state.has_key?(:id) @@ -338,6 +339,7 @@ module ActiveRecord @attributes.delete(self.class.primary_key) @attributes_cache.delete(self.class.primary_key) end + @attributes.freeze if was_frozen end end end diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 203dd054f1..a9ccd00fac 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -362,6 +362,16 @@ class TransactionTest < ActiveRecord::TestCase end end + def test_rollback_when_saving_a_frozen_record + topic = Topic.new(:title => 'test') + topic.freeze + e = assert_raise(RuntimeError) { topic.save } + assert_equal "can't modify frozen Hash", e.message + assert !topic.persisted?, 'not persisted' + assert_nil topic.id + assert topic.frozen?, 'not frozen' + end + def test_restore_active_record_state_for_all_records_in_a_transaction topic_1 = Topic.new(:title => 'test_1') topic_2 = Topic.new(:title => 'test_2') -- cgit v1.2.3 From f9a718eb5e3fe969c3a01cf084c6686cc2ce7aff Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Mon, 21 May 2012 23:30:13 -0500 Subject: add CollectionProxy#delete_all documentation --- .../active_record/associations/collection_proxy.rb | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index b419b9a749..650d68a64a 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -218,6 +218,71 @@ module ActiveRecord # # person.pets.replace(["doo", "ggie", "gaga"]) # # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String + + + ## + # :method: delete_all + # Deletes all the records from the collection. For +has_many+ it will do the + # deletion according to the strategy specified by the :dependent + # option. Returns an array with the deleted records. + # + # If no :dependent option is given, then it will follow the + # default strategy. The default strategy is :nullify. This + # sets the foreign keys to NULL. + # + # class Person < ActiveRecord::Base + # has_many :pets # dependent: :nullify option by default + # end + # + # person.pets.size # => 3 + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] + # + # person.pets.delete_all + # # => [ + # # #, + # # #, + # # # + # # ] + # + # person.pets.size # => 0 + # person.pets # => [] + # + # Pet.find(1, 2, 3) + # # => [ + # # #, + # # #, + # # # + # # ] + # + # If it is set to :destroy all the objects from the collection + # are destroyed by calling their +destroy+ method. + # + # class Person < ActiveRecord::Base + # has_many :pets, dependent: :destroy + # end + # + # person.pets.size # => 3 + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] + # + # person.pets.delete_all + # # => [ + # # #, + # # #, + # # # + # # ] + # + # Pet.find(1, 2, 3) + # # => ActiveRecord::RecordNotFound ## # :method: destroy_all -- cgit v1.2.3 From 1bcd5e42ed6c2efed4d1a53e74b4f8d6631b119d Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Mon, 21 May 2012 23:49:41 -0500 Subject: update CollectionProxy#delete_all documentation --- .../active_record/associations/collection_proxy.rb | 31 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 650d68a64a..f0c4688ee3 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -228,7 +228,8 @@ module ActiveRecord # # If no :dependent option is given, then it will follow the # default strategy. The default strategy is :nullify. This - # sets the foreign keys to NULL. + # sets the foreign keys to NULL. For, +has_many+ :through, + # the default strategy is +delete_all+. # # class Person < ActiveRecord::Base # has_many :pets # dependent: :nullify option by default @@ -260,7 +261,8 @@ module ActiveRecord # # ] # # If it is set to :destroy all the objects from the collection - # are destroyed by calling their +destroy+ method. + # are removed by calling their +destroy+ method. See +destroy+ for more + # information. # # class Person < ActiveRecord::Base # has_many :pets, dependent: :destroy @@ -283,6 +285,31 @@ module ActiveRecord # # Pet.find(1, 2, 3) # # => ActiveRecord::RecordNotFound + # + # If it is set to :delete_all, all the objects are deleted + # *without* calling their +destroy+ method. + # + # class Person < ActiveRecord::Base + # has_many :pets, dependent: :delete_all + # end + # + # person.pets.size # => 3 + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] + # + # person.pets.delete_all + # # => [ + # # #, + # # #, + # # # + # # ] + # + # Pet.find(1, 2, 3) + # # => ActiveRecord::RecordNotFound ## # :method: destroy_all -- cgit v1.2.3 From 8281194fee1a049826c50f9e0a095ea1abb91277 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Mon, 21 May 2012 23:51:31 -0500 Subject: update CollectionProxy#clear documentation --- activerecord/lib/active_record/associations/collection_proxy.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index f0c4688ee3..468bf5c2f8 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -577,9 +577,8 @@ module ActiveRecord end alias_method :push, :<< - # Removes every object from the collection. This does not destroy - # the objects, it sets their foreign keys to +NULL+. Returns +self+ - # so methods can be chained. + # Equivalent to +delete_all+. The difference is that returns +self+, instead + # of an array with the deleted objects, so methods can be chained. # # class Person < ActiveRecord::Base # has_many :pets -- cgit v1.2.3 From 940c135175cba6e6611cb67a93b9370da4e81fd9 Mon Sep 17 00:00:00 2001 From: Andrey Voronkov Date: Tue, 22 May 2012 18:08:36 +0400 Subject: Convert Hash to HashWithIndifferentAccess in ActiveRecord::Store. In order to make migration from 3.x apps easier, we should try to convert Hash instances to HashWithIndifferentAccess, to allow accessing values with both symbol and a string. This is follow up to changes in 3c0bf043. --- activerecord/lib/active_record/store.rb | 37 ++++++++++++++++++++++++--------- activerecord/test/cases/store_test.rb | 34 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index ce2ea85ef9..fdd82b489a 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/hash/indifferent_access' + module ActiveRecord # Store gives you a thin wrapper around serialize for the purpose of storing hashes in a single column. # It's like a simple key/value store backed into your record when you don't care about being able to @@ -13,9 +15,6 @@ module ActiveRecord # You can set custom coder to encode/decode your serialized attributes to/from different formats. # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+. # - # String keys should be used for direct access to virtual attributes because of most of the coders do not - # distinguish symbols and strings as keys. - # # Examples: # # class User < ActiveRecord::Base @@ -23,8 +22,12 @@ module ActiveRecord # end # # u = User.new(color: 'black', homepage: '37signals.com') - # u.color # Accessor stored attribute - # u.settings['country'] = 'Denmark' # Any attribute, even if not specified with an accessor + # u.color # Accessor stored attribute + # u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor + # + # # There is no difference between strings and symbols for accessing custom attributes + # u.settings[:country] # => 'Denmark' + # u.settings['country'] # => 'Denmark' # # # Add additional accessors to an existing store through store_accessor # class SuperUser < User @@ -35,24 +38,38 @@ module ActiveRecord module ClassMethods def store(store_attribute, options = {}) - serialize store_attribute, options.fetch(:coder, Hash) + serialize store_attribute, options.fetch(:coder, ActiveSupport::HashWithIndifferentAccess) store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors end def store_accessor(store_attribute, *keys) keys.flatten.each do |key| define_method("#{key}=") do |value| - send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash) - send(store_attribute)[key.to_s] = value + initialize_store_attribute(store_attribute) + send(store_attribute)[key] = value send("#{store_attribute}_will_change!") end define_method(key) do - send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash) - send(store_attribute)[key.to_s] + initialize_store_attribute(store_attribute) + send(store_attribute)[key] end end end end + + private + def initialize_store_attribute(store_attribute) + case attribute = send(store_attribute) + when ActiveSupport::HashWithIndifferentAccess + # Already initialized. Do nothing. + when Hash + # Initialized as a Hash. Convert to indifferent access. + send :"#{store_attribute}=", attribute.with_indifferent_access + else + # Uninitialized. Set to an indifferent hash. + send :"#{store_attribute}=", ActiveSupport::HashWithIndifferentAccess.new + end + end end end diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb index e1d0f1f799..3a5d84df9f 100644 --- a/activerecord/test/cases/store_test.rb +++ b/activerecord/test/cases/store_test.rb @@ -41,6 +41,40 @@ class StoreTest < ActiveRecord::TestCase assert_equal false, @john.remember_login end + test "preserve store attributes data in HashWithIndifferentAccess format without any conversion" do + @john.json_data = HashWithIndifferentAccess.new(:height => 'tall', 'weight' => 'heavy') + @john.height = 'low' + assert_equal true, @john.json_data.instance_of?(HashWithIndifferentAccess) + assert_equal 'low', @john.json_data[:height] + assert_equal 'low', @john.json_data['height'] + assert_equal 'heavy', @john.json_data[:weight] + assert_equal 'heavy', @john.json_data['weight'] + end + + test "convert store attributes from Hash to HashWithIndifferentAccess saving the data and access attributes indifferently" do + @john.json_data = { :height => 'tall', 'weight' => 'heavy' } + assert_equal true, @john.json_data.instance_of?(Hash) + assert_equal 'tall', @john.json_data[:height] + assert_equal nil, @john.json_data['height'] + assert_equal nil, @john.json_data[:weight] + assert_equal 'heavy', @john.json_data['weight'] + @john.height = 'low' + assert_equal true, @john.json_data.instance_of?(HashWithIndifferentAccess) + assert_equal 'low', @john.json_data[:height] + assert_equal 'low', @john.json_data['height'] + assert_equal 'heavy', @john.json_data[:weight] + assert_equal 'heavy', @john.json_data['weight'] + end + + 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 '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? + end + test "reading store attributes through accessors encoded with JSON" do assert_equal 'tall', @john.height assert_nil @john.weight -- cgit v1.2.3 From 38293ea828ab52043fceecbdc96946b5bc9bb0e5 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 22 May 2012 11:01:16 -0300 Subject: require active_support/lazy_load_hooks where is needed --- activerecord/lib/active_record.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 210820062b..2cbe04ee44 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -22,6 +22,7 @@ #++ require 'active_support' +require 'active_support/lazy_load_hooks' require 'active_model' require 'arel' require 'active_record_deprecated_finders' -- cgit v1.2.3 From d1cab8e5e0aba87e307a387f1b8267e5c41419c6 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Tue, 22 May 2012 09:55:13 -0500 Subject: remove repeated documentation in CollectionProxy#clear --- .../active_record/associations/collection_proxy.rb | 26 ++-------------------- 1 file changed, 2 insertions(+), 24 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 468bf5c2f8..9ee69157b3 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -578,30 +578,8 @@ module ActiveRecord alias_method :push, :<< # Equivalent to +delete_all+. The difference is that returns +self+, instead - # of an array with the deleted objects, so methods can be chained. - # - # class Person < ActiveRecord::Base - # has_many :pets - # end - # - # person.pets # => [#] - # person.pets.clear # => [] - # person.pets.size # => 0 - # - # Pet.find(1) # => # - # - # If they are associated with dependent: :destroy option, it deletes - # them directly from the database. - # - # class Person < ActiveRecord::Base - # has_many :pets, dependent: :destroy - # end - # - # person.pets # => [#] - # person.pets.clear # => [] - # person.pets.size # => 0 - # - # Pet.find(2) # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=2 + # of an array with the deleted objects, so methods can be chained. See + # +delete_all+ for more information. def clear delete_all self -- cgit v1.2.3 From 344ea048659f2ba47012f0330183ea4a96752732 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 22 May 2012 12:10:35 -0300 Subject: Fix the build --- activerecord/lib/active_record.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 2cbe04ee44..210820062b 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -22,7 +22,6 @@ #++ require 'active_support' -require 'active_support/lazy_load_hooks' require 'active_model' require 'arel' require 'active_record_deprecated_finders' -- cgit v1.2.3 From 2c62dd64dbaf1b3c758e7259fa8fefbafbbdcf81 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Tue, 22 May 2012 11:02:43 -0500 Subject: add CollectionProxy#build documentation --- .../active_record/associations/collection_proxy.rb | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 9ee69157b3..6c8fcec0c4 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -167,6 +167,37 @@ module ActiveRecord # another_person_without.pets.last # => nil # another_person_without.pets.last(3) # => [] + ## + # :method: build + # + # :call-seq: + # build(attributes = {}, options = {}, &block) + # + # Returns a new object of the collection type that has been instantiated + # with +attributes+ and linked to this object, but have not yet been saved. + # You can pass an array of attributes hashes, this will return an array + # with the new objects. + # + # class Person + # has_many :pets + # end + # + # person.pets.build + # # => # + # + # person.pets.build(name: 'Fancy-Fancy') + # # => # + # + # person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}]) + # # => [ + # # #, + # # #, + # # # + # # ] + # + # person.pets.size # => 5 # size of the collection + # person.pets.count # => 0 # count from database + ## # :method: concat # Add one or more records to the collection by setting their foreign keys -- cgit v1.2.3 From 21f4c2eb593f62658bbf5691c60ec3da3b0ab76d Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Tue, 22 May 2012 11:13:31 -0500 Subject: adding :call-seq: to CollectionProxy methods --- .../active_record/associations/collection_proxy.rb | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 6c8fcec0c4..6d1ba188c3 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -39,6 +39,10 @@ module ActiveRecord ## # :method: select # + # :call-seq: + # select(select = nil) + # select(&block) + # # Works in two ways. # # *First:* Specify a subset of fields to be selected from the result set. @@ -91,6 +95,10 @@ module ActiveRecord ## # :method: find + # + # :call-seq: + # find(*args, &block) + # # Finds an object in the collection responding to the +id+. Uses the same # rules as +ActiveRecord::Base.find+. Returns +ActiveRecord::RecordNotFound++ # error if the object can not be found. @@ -111,6 +119,7 @@ module ActiveRecord ## # :method: first + # # Returns the first record, or the first +n+ records, from the collection. # If the collection is empty, the first form returns +nil+, and the second # form returns an empty array. @@ -140,6 +149,7 @@ module ActiveRecord ## # :method: last + # # Returns the last record, or the last +n+ records, from the collection. # If the collection is empty, the first form returns +nil+, and the second # form returns an empty array. @@ -200,6 +210,10 @@ module ActiveRecord ## # :method: concat + # + # :call-seq: + # concat(*records) + # # Add one or more records to the collection by setting their foreign keys # to the association's primary key. Since << flattens its argument list and # inserts each record, +push+ and +concat+ behave identically. Returns +self+ @@ -227,6 +241,10 @@ module ActiveRecord ## # :method: replace + # + # :call-seq: + # replace(other_array) + # # Replace this collection with +other_array+. This will perform a diff # and delete/add only records that have changed. # @@ -253,6 +271,7 @@ module ActiveRecord ## # :method: delete_all + # # Deletes all the records from the collection. For +has_many+ it will do the # deletion according to the strategy specified by the :dependent # option. Returns an array with the deleted records. @@ -344,6 +363,7 @@ module ActiveRecord ## # :method: destroy_all + # # Deletes the records of the collection directly from the database. # This will _always_ remove the records ignoring the +:dependent+ # option. @@ -369,6 +389,10 @@ module ActiveRecord ## # :method: destroy + # + # :call-seq: + # destroy(*records) + # # Destroy the +records+ supplied and remove them from the collection. # This method will _always_ remove record from the database ignoring # the +:dependent+ option. Returns an array with the removed records. @@ -440,6 +464,7 @@ module ActiveRecord ## # :method: empty? + # # Returns +true+ if the collection is empty. # # class Person < ActiveRecord::Base @@ -456,6 +481,11 @@ module ActiveRecord ## # :method: any? + # + # :call-seq: + # any? + # any?{|item| block} + # # Returns +true+ if the collection is not empty. # # class Person < ActiveRecord::Base @@ -488,6 +518,11 @@ module ActiveRecord ## # :method: many? + # + # :call-seq: + # many? + # many?{|item| block} + # # Returns true if the collection has more than one record. # Equivalent to collection.size > 1. # @@ -525,6 +560,10 @@ module ActiveRecord ## # :method: include? + # + # :call-seq: + # include?(record) + # # Returns +true+ if the given object is present in the collection. # # class Person < ActiveRecord::Base -- cgit v1.2.3 From e0859e569c007cd108797883eec402c876b9e8a0 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Tue, 22 May 2012 11:27:04 -0500 Subject: add more examples to CollectionProxy#find --- activerecord/lib/active_record/associations/collection_proxy.rb | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 6d1ba188c3..33779a9ad7 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -116,6 +116,15 @@ module ActiveRecord # # person.pets.find(1) # => # # person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=4 + # + # person.pets.find(2) { |pet| pet.name.downcase! } + # # => # + # + # person.pets.find(2, 3) + # # => [ + # # #, + # # # + # # ] ## # :method: first -- cgit v1.2.3 From 9c38cfc44afc2c8a9ab79801ff23c53f833d1085 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Tue, 22 May 2012 11:39:13 -0500 Subject: add CollectionProxy#create documentation --- .../active_record/associations/collection_proxy.rb | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 33779a9ad7..6e73ac79e2 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -217,6 +217,39 @@ module ActiveRecord # person.pets.size # => 5 # size of the collection # person.pets.count # => 0 # count from database + ## + # :method: create + # + # :call-seq: + # create(attributes = {}, options = {}, &block) + # + # Returns a new object of the collection type that has been instantiated with + # attributes, linked to this object and that has already been saved (if it + # passed the validations). + # + # class Person + # has_many :pets + # end + # + # person.pets.create(name: 'Fancy-Fancy') + # # => # + # + # person.pets.create([{name: 'Spook'}, {name: 'Choo-Choo'}]) + # # => [ + # # #, + # # # + # # ] + # + # person.pets.size # => 3 + # person.pets.count # => 3 + # + # person.pets.find(1, 2, 3) + # # => [ + # # #, + # # #, + # # # + # # ] + ## # :method: concat # -- cgit v1.2.3 From 7e5beca38fe64344eb8b97c9fba93096ff533543 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Tue, 22 May 2012 11:52:14 -0500 Subject: add CollectionProxy#create! documentation --- .../active_record/associations/collection_proxy.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 6e73ac79e2..6d001e8d10 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -249,6 +249,28 @@ module ActiveRecord # # #, # # # # # ] + + ## + # :method: create! + # + # :call-seq: + # create!(attributes = {}, options = {}, &block) + # + # Like +create+, except that if the record is invalid will + # raise an exception. + # + # class Person + # has_many :pets + # end + # + # class Pet + # attr_accessible :name + # validates :name, presence: true + # end + # + # person.pets.create!(name: nil) + # # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank + ## # :method: concat -- cgit v1.2.3 From 3e847afbdd88d53bbf85eb2436ead2dab88f6e5b Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Tue, 22 May 2012 11:55:30 -0500 Subject: add :call-seq: to +first+ and +last+ CollectionProxy methods --- activerecord/lib/active_record/associations/collection_proxy.rb | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 6d001e8d10..776d65294e 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -129,6 +129,9 @@ module ActiveRecord ## # :method: first # + # :call-seq: + # first(limit = nil) + # # Returns the first record, or the first +n+ records, from the collection. # If the collection is empty, the first form returns +nil+, and the second # form returns an empty array. @@ -159,6 +162,9 @@ module ActiveRecord ## # :method: last # + # :call-seq: + # last(limit = nil) + # # Returns the last record, or the last +n+ records, from the collection. # If the collection is empty, the first form returns +nil+, and the second # form returns an empty array. -- cgit v1.2.3 From f539d98cc8838b0f7c3c31465cdc070ab9c1e9f3 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Tue, 22 May 2012 12:17:00 -0500 Subject: add CollectionProxy#size documentation --- .../active_record/associations/collection_proxy.rb | 27 ++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 776d65294e..bc310cd13e 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -277,7 +277,6 @@ module ActiveRecord # person.pets.create!(name: nil) # # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank - ## # :method: concat # @@ -337,7 +336,6 @@ module ActiveRecord # # person.pets.replace(["doo", "ggie", "gaga"]) # # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String - ## # :method: delete_all @@ -531,6 +529,31 @@ module ActiveRecord # person.pets # => [] # # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6) + + ## + # :method: size + # + # Returns the size of the collection. If the collection hasn't been loaded, + # it executes a SELECT COUNT(*) query. + # + # class Person < ActiveRecord::Base + # has_many :pets + # end + # + # # This will execute: + # # SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = ? [["person_id", 1]] + # person.pets.size # => 3 + # + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] + # + # # Because the collection is already loaded, this will behave like + # collection.size and no SQL count query is executed. + # person.pets.size # => 3 ## # :method: empty? -- cgit v1.2.3 From eb2c0a4f712b015bf6886286efd6e5b2eeaf54aa Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Tue, 22 May 2012 12:34:12 -0500 Subject: add CollectionProxy#length documentation --- .../active_record/associations/collection_proxy.rb | 42 +++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index bc310cd13e..6fd5c466f6 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -540,20 +540,52 @@ module ActiveRecord # has_many :pets # end # - # # This will execute: - # # SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = ? [["person_id", 1]] # person.pets.size # => 3 + # # Executes: + # # + # # SELECT COUNT(*) + # # FROM "pets" + # # WHERE "pets"."person_id" = 1 # - # person.pets + # person.pets # This will execute a SELECT * FROM query # # => [ # # #, # # #, # # # # # ] # - # # Because the collection is already loaded, this will behave like - # collection.size and no SQL count query is executed. # person.pets.size # => 3 + # # Because the collection is already loaded, this will behave like + # # collection.size and no SQL count query is executed. + + ## + # :method: length + # + # Returns the size of the collection calling +size+ on the target. + # If the collection has been already loaded +length+ and +size+ are + # equivalent. If not and you are going to need the records anyway this + # method will take one less query because loads the collection. Otherwise + # +size+ is more efficient. + # + # class Person < ActiveRecord::Base + # has_many :pets + # end + # + # person.pets.length # => 3 + # # Executes: + # # + # # SELECT "pets".* + # #  FROM "pets" + # # WHERE "pets"."person_id" = 1 + # + # # Because the collection is loaded, you can + # # call the collection without execute a query: + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] ## # :method: empty? -- cgit v1.2.3 From 7c69e2db36d3892b050ddc248d3ea4d0f4f4f8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 22 May 2012 15:01:36 -0300 Subject: Revert "Merge pull request #6416 from pmahoney/threadsafe-connection-pool" This reverts commit d2901f0fc4270a765717ad572d559dc49a56b3a8, reversing changes made to 525839fdd8cc34d6d524f204528d5b6f36fe410c. Conflicts: activerecord/test/cases/connection_pool_test.rb Reason: This change broke the build (http://travis-ci.org/#!/rails/rails/builds/1391490) and we don't have any solution until now. I asked the author to try to fix it and open a new pull request. --- .../abstract/connection_pool.rb | 120 ++++++--------------- .../connection_adapters/abstract_adapter_test.rb | 2 +- activerecord/test/cases/connection_pool_test.rb | 115 -------------------- 3 files changed, 35 insertions(+), 202 deletions(-) (limited to 'activerecord') 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 450ef69744..c6699737b4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -2,6 +2,7 @@ require 'thread' require 'monitor' require 'set' require 'active_support/core_ext/module/deprecation' +require 'timeout' module ActiveRecord # Raised when a connection could not be obtained within the connection @@ -91,6 +92,21 @@ module ActiveRecord attr_accessor :automatic_reconnect, :timeout attr_reader :spec, :connections, :size, :reaper + class Latch # :nodoc: + def initialize + @mutex = Mutex.new + @cond = ConditionVariable.new + end + + def release + @mutex.synchronize { @cond.broadcast } + end + + def await + @mutex.synchronize { @cond.wait @mutex } + end + end + # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification # object which describes database connection information (e.g. adapter, # host name, username, password, etc), as well as the maximum size for @@ -112,25 +128,9 @@ module ActiveRecord # default max pool size to 5 @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5 + @latch = Latch.new @connections = [] @automatic_reconnect = true - - # connections available to be checked out - @available = [] - - # number of threads waiting to check out a connection - @num_waiting = 0 - - # signal threads waiting - @cond = new_cond - end - - # Hack for tests to be able to add connections. Do not call outside of tests - def insert_connection_for_test!(c) - synchronize do - @connections << c - @available << c - end end # Retrieve the connection associated with the current thread, or call @@ -188,7 +188,6 @@ module ActiveRecord conn.disconnect! end @connections = [] - @available = [] end end @@ -203,9 +202,6 @@ module ActiveRecord @connections.delete_if do |conn| conn.requires_reloading? end - @available.delete_if do |conn| - conn.requires_reloading? - end end end @@ -229,19 +225,23 @@ module ActiveRecord # Raises: # - PoolFullError: no connection can be obtained from the pool. def checkout - synchronize do - conn = nil - - if @num_waiting == 0 - conn = acquire_connection - end + loop do + # Checkout an available connection + synchronize do + # Try to find a connection that hasn't been leased, and lease it + conn = connections.find { |c| c.lease } + + # If all connections were leased, and we have room to expand, + # create a new connection and lease it. + if !conn && connections.size < size + conn = checkout_new_connection + conn.lease + end - unless conn - conn = wait_until(@timeout) { acquire_connection } + return checkout_and_verify(conn) if conn end - conn.lease - checkout_and_verify(conn) + Timeout.timeout(@timeout, PoolFullError) { @latch.await } end end @@ -257,10 +257,8 @@ module ActiveRecord end release conn - - @available.unshift conn - @cond.signal end + @latch.release end # Remove a connection from the connection pool. The connection will @@ -268,14 +266,12 @@ module ActiveRecord def remove(conn) synchronize do @connections.delete conn - @available.delete conn # FIXME: we might want to store the key on the connection so that removing # from the reserved hash will be a little easier. release conn - - @cond.signal # can make a new connection now end + @latch.release end # Removes dead connections from the pool. A dead connection can occur @@ -287,60 +283,12 @@ module ActiveRecord connections.dup.each do |conn| remove conn if conn.in_use? && stale > conn.last_use && !conn.active? end - @cond.broadcast # may violate fairness end + @latch.release end private - # Take an available connection or, if possible, create a new - # one, or nil. - # - # Monitor must be held while calling this method. - # - # Returns: a newly acquired connection. - def acquire_connection - if @available.any? - @available.pop - elsif connections.size < size - checkout_new_connection - end - end - - # Wait on +@cond+ until the block returns non-nil. Note that - # unlike MonitorMixin::ConditionVariable#wait_until, this method - # does not test the block before the first wait period. - # - # Monitor must be held when calling this method. - # - # +timeout+: Integer timeout in seconds - # - # Returns: the result of the block - # - # Raises: - # - PoolFullError: timeout elapsed before +&block+ returned a connection - def wait_until(timeout, &block) - @num_waiting += 1 - begin - t0 = Time.now - loop do - elapsed = Time.now - t0 - if elapsed >= timeout - msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' % - [timeout, elapsed] - raise PoolFullError, msg - end - - @cond.wait(timeout - elapsed) - - conn = yield - return conn if conn - end - ensure - @num_waiting -= 1 - end - end - def release(conn) thread_id = if @reserved_connections[current_connection_id] == conn current_connection_id diff --git a/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb b/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb index 3e3d6e2769..7dc6e8afcb 100644 --- a/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb +++ b/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb @@ -36,7 +36,7 @@ module ActiveRecord def test_close pool = ConnectionPool.new(ConnectionSpecification.new({}, nil)) - pool.insert_connection_for_test! adapter + pool.connections << adapter adapter.pool = pool # Make sure the pool marks the connection in use diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index b1905515d3..8dc9f761c2 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -200,121 +200,6 @@ module ActiveRecord end.join end - # The connection pool is "fair" if threads waiting for - # connections receive them the order in which they began - # waiting. This ensures that we don't timeout one HTTP request - # even while well under capacity in a multi-threaded environment - # such as a Java servlet container. - # - # We don't need strict fairness: if two connections become - # available at the same time, it's fine of two threads that were - # waiting acquire the connections out of order. - # - # Thus this test prepares waiting threads and then trickles in - # available connections slowly, ensuring the wakeup order is - # correct in this case. - # - # Try a few times since it might work out just by chance. - def test_checkout_fairness - 4.times { setup; do_checkout_fairness } - end - - def do_checkout_fairness - expected = (1..@pool.size).to_a.freeze - # check out all connections so our threads start out waiting - conns = expected.map { @pool.checkout } - mutex = Mutex.new - order = [] - errors = [] - - threads = expected.map do |i| - t = Thread.new { - begin - @pool.checkout # never checked back in - mutex.synchronize { order << i } - rescue => e - mutex.synchronize { errors << e } - end - } - Thread.pass until t.status == "sleep" - t - end - - # this should wake up the waiting threads one by one in order - conns.each { |conn| @pool.checkin(conn); sleep 0.1 } - - threads.each(&:join) - - raise errors.first if errors.any? - - assert_equal(expected, order) - end - - # As mentioned in #test_checkout_fairness, we don't care about - # strict fairness. This test creates two groups of threads: - # group1 whose members all start waiting before any thread in - # group2. Enough connections are checked in to wakeup all - # group1 threads, and the fact that only group1 and no group2 - # threads acquired a connection is enforced. - # - # Try a few times since it might work out just by chance. - def test_checkout_fairness_by_group - 4.times { setup; do_checkout_fairness_by_group } - end - - def do_checkout_fairness_by_group - @pool.instance_variable_set(:@size, 10) - # take all the connections - conns = (1..10).map { @pool.checkout } - mutex = Mutex.new - successes = [] # threads that successfully got a connection - errors = [] - - make_thread = proc do |i| - t = Thread.new { - begin - @pool.checkout # never checked back in - mutex.synchronize { successes << i } - rescue => e - mutex.synchronize { errors << e } - end - } - Thread.pass until t.status == "sleep" - t - end - - # all group1 threads start waiting before any in group2 - group1 = (1..5).map(&make_thread) - group2 = (6..10).map(&make_thread) - - # checkin n connections back to the pool - checkin = proc do |n| - n.times do - c = conns.pop - @pool.checkin(c) - end - end - - checkin.call(group1.size) # should wake up all group1 - - loop do - sleep 0.1 - break if mutex.synchronize { (successes.size + errors.size) == group1.size } - end - - winners = mutex.synchronize { successes.dup } - checkin.call(group2.size) # should wake up everyone remaining - - group1.each(&:join) - group2.each(&:join) - - assert_equal((1..group1.size).to_a, winners.sort) - - if errors.any? - raise errors.first - end - end - def test_automatic_reconnect= pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec assert pool.automatic_reconnect -- cgit v1.2.3 From 5646d65d01845adf669b9e6c4899b6ac15849bb9 Mon Sep 17 00:00:00 2001 From: Angelo capilleri Date: Wed, 23 May 2012 09:59:13 +0200 Subject: changed xml type datetime to dateTime, fixes #6328 XmlMini define the xml 'datatime', but according to http://www.w3.org/TR/xmlschema-2/#dateTime could be better change this to 'dateTime' with upper case letter 'T. So 'DateTime' and 'Time' are redefined from 'datetime' to 'dateTime' add the changing to the changelog --- .../lib/active_record/serializers/xml_serializer.rb | 4 ++-- activerecord/test/cases/xml_serialization_test.rb | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index 2e60521638..b833af64fe 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -18,8 +18,8 @@ module ActiveRecord #:nodoc: # 1 # false # 0 - # 2000-01-01T08:28:00+12:00 - # 2003-07-16T09:28:00+1200 + # 2000-01-01T08:28:00+12:00 + # 2003-07-16T09:28:00+1200 # Have a nice day # david@loudthinking.com # diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb index 88751a72f9..12373333b0 100644 --- a/activerecord/test/cases/xml_serialization_test.rb +++ b/activerecord/test/cases/xml_serialization_test.rb @@ -92,7 +92,7 @@ class DefaultXmlSerializationTest < ActiveRecord::TestCase end def test_should_serialize_datetime - assert_match %r{2006-08-01T00:00:00Z}, @xml + assert_match %r{2006-08-01T00:00:00Z}, @xml end def test_should_serialize_boolean @@ -109,7 +109,7 @@ class DefaultXmlSerializationTimezoneTest < ActiveRecord::TestCase timezone, Time.zone = Time.zone, "Pacific Time (US & Canada)" toy = Toy.create(:name => 'Mickey', :updated_at => Time.utc(2006, 8, 1)) - assert_match %r{2006-07-31T17:00:00-07:00}, toy.to_xml + assert_match %r{2006-07-31T17:00:00-07:00}, toy.to_xml ensure Time.zone = timezone end @@ -118,7 +118,7 @@ class DefaultXmlSerializationTimezoneTest < ActiveRecord::TestCase timezone, Time.zone = Time.zone, "Pacific Time (US & Canada)" toy = Toy.create(:name => 'Minnie', :updated_at => Time.utc(2006, 8, 1)).reload - assert_match %r{2006-07-31T17:00:00-07:00}, toy.to_xml + assert_match %r{2006-07-31T17:00:00-07:00}, toy.to_xml ensure Time.zone = timezone end @@ -152,7 +152,7 @@ class NilXmlSerializationTest < ActiveRecord::TestCase assert %r{}.match(@xml) attributes = $1 assert_match %r{nil="true"}, attributes - assert_match %r{type="datetime"}, attributes + assert_match %r{type="dateTime"}, attributes end def test_should_serialize_boolean @@ -188,7 +188,7 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase assert_equal "integer" , xml.elements["//replies-count"].attributes['type'] assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text - assert_equal "datetime" , xml.elements["//written-on"].attributes['type'] + assert_equal "dateTime" , xml.elements["//written-on"].attributes['type'] assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text @@ -198,7 +198,7 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase if current_adapter?(:SybaseAdapter) assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text - assert_equal "datetime" , xml.elements["//last-read"].attributes['type'] + assert_equal "dateTime" , xml.elements["//last-read"].attributes['type'] else # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb) assert_equal "2004-04-15", xml.elements["//last-read"].text @@ -211,7 +211,7 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase assert_equal "boolean" , xml.elements["//approved"].attributes['type'] assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text - assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type'] + assert_equal "dateTime" , xml.elements["//bonus-time"].attributes['type'] end end -- cgit v1.2.3 From cb6f839359bd894feb0a1105b79af72b336aa41e Mon Sep 17 00:00:00 2001 From: Jonathan Rochkind Date: Wed, 23 May 2012 12:08:11 -0400 Subject: ConnectionPool wait_timeout no longer used for different types of timeouts. #6441 An AR ConnectionSpec `wait_timeout` is pre-patch used for three different things: * mysql2 uses it for MySQL's own wait_timeout (how long MySQL should allow an idle connection before closing it), and defaults to 2592000 seconds. * ConnectionPool uses it for "number of seconds to block and wait for a connection before giving up and raising a timeout error", default 5 seconds. * ConnectionPool uses it for the Reaper, for deciding if a 'dead' connection can be reaped. Default 5 seconds. Previously, if you want to change these from defaults, you need to change them all together. This is problematic _especially_ for the mysql2/ConnectionPool conflict, you will generally _not_ want them to be the same, as evidenced by their wildly different defaults. This has caused real problems for people #6441 #2894 But as long as we're changing this, forcing renaming the ConnectionPool key to be more specific, it made sense to seperate the two ConnectionPool uses too -- these two types of ConnectionPool timeouts ought to be able to be changed independently, you won't neccesarily want them to be the same, even though the defaults are (currently) the same. --- .../abstract/connection_pool.rb | 23 +++++++++++++++------- activerecord/test/cases/connection_pool_test.rb | 4 ++-- activerecord/test/cases/pooled_connections_test.rb | 4 ++-- activerecord/test/cases/reaper_test.rb | 2 +- 4 files changed, 21 insertions(+), 12 deletions(-) (limited to 'activerecord') 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 c6699737b4..be36c9695f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -55,19 +55,27 @@ module ActiveRecord # # == Options # - # There are two connection-pooling-related options that you can add to + # There are several connection-pooling-related options that you can add to # your database connection configuration: # # * +pool+: number indicating size of connection pool (default 5) - # * +wait_timeout+: number of seconds to block and wait for a connection + # * +checkout_timeout+: number of seconds to block and wait for a connection # before giving up and raising a timeout error (default 5 seconds). + # * +reaping_frequency+: frequency in seconds to periodically run the + # Reaper, which attempts to find and close dead connections, which can + # occur if a programmer forgets to close a connection at the end of a + # thread or a thread dies unexpectedly. (Default nil, which means don't + # run the Reaper). + # * +dead_connection_timeout+: number of seconds from last checkout + # after which the Reaper will consider a connection reapable. (default + # 5 seconds). class ConnectionPool # Every +frequency+ seconds, the reaper will call +reap+ on +pool+. # A reaper instantiated with a nil frequency will never reap the # connection pool. # # Configure the frequency by setting "reaping_frequency" in your - # database yaml file. + # database yaml file. class Reaper attr_reader :pool, :frequency @@ -89,7 +97,7 @@ module ActiveRecord include MonitorMixin - attr_accessor :automatic_reconnect, :timeout + attr_accessor :automatic_reconnect, :checkout_timeout, :dead_connection_timeout attr_reader :spec, :connections, :size, :reaper class Latch # :nodoc: @@ -121,7 +129,8 @@ module ActiveRecord # The cache of reserved connections mapped to threads @reserved_connections = {} - @timeout = spec.config[:wait_timeout] || 5 + @checkout_timeout = spec.config[:checkout_timeout] || 5 + @dead_connection_timeout = spec.config[:dead_connection_timeout] @reaper = Reaper.new self, spec.config[:reaping_frequency] @reaper.run @@ -241,7 +250,7 @@ module ActiveRecord return checkout_and_verify(conn) if conn end - Timeout.timeout(@timeout, PoolFullError) { @latch.await } + Timeout.timeout(@checkout_timeout, PoolFullError) { @latch.await } end end @@ -279,7 +288,7 @@ module ActiveRecord # or a thread dies unexpectedly. def reap synchronize do - stale = Time.now - @timeout + stale = Time.now - @dead_connection_timeout connections.dup.each do |conn| remove conn if conn.in_use? && stale > conn.last_use && !conn.active? end diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 8dc9f761c2..bba7815d73 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -124,7 +124,7 @@ module ActiveRecord @pool.checkout @pool.checkout @pool.checkout - @pool.timeout = 0 + @pool.dead_connection_timeout = 0 connections = @pool.connections.dup @@ -137,7 +137,7 @@ module ActiveRecord @pool.checkout @pool.checkout @pool.checkout - @pool.timeout = 0 + @pool.dead_connection_timeout = 0 connections = @pool.connections.dup connections.each do |conn| diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb index fba3006ebe..0a6354f5cc 100644 --- a/activerecord/test/cases/pooled_connections_test.rb +++ b/activerecord/test/cases/pooled_connections_test.rb @@ -17,7 +17,7 @@ class PooledConnectionsTest < ActiveRecord::TestCase end def checkout_connections - ActiveRecord::Model.establish_connection(@connection.merge({:pool => 2, :wait_timeout => 0.3})) + ActiveRecord::Model.establish_connection(@connection.merge({:pool => 2, :checkout_timeout => 0.3})) @connections = [] @timed_out = 0 @@ -34,7 +34,7 @@ class PooledConnectionsTest < ActiveRecord::TestCase # Will deadlock due to lack of Monitor timeouts in 1.9 def checkout_checkin_connections(pool_size, threads) - ActiveRecord::Model.establish_connection(@connection.merge({:pool => pool_size, :wait_timeout => 0.5})) + ActiveRecord::Model.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5})) @connection_count = 0 @timed_out = 0 threads.times do diff --git a/activerecord/test/cases/reaper_test.rb b/activerecord/test/cases/reaper_test.rb index 576ab60090..e53a27d5dd 100644 --- a/activerecord/test/cases/reaper_test.rb +++ b/activerecord/test/cases/reaper_test.rb @@ -64,7 +64,7 @@ module ActiveRecord spec.config[:reaping_frequency] = 0.0001 pool = ConnectionPool.new spec - pool.timeout = 0 + pool.dead_connection_timeout = 0 conn = pool.checkout count = pool.connections.length -- cgit v1.2.3 From 6c7b250ac0292001e293ee19157f3d58b7553f41 Mon Sep 17 00:00:00 2001 From: Matt Griffin Date: Fri, 21 Oct 2011 14:24:33 -0400 Subject: Add license field to gemspecs, by Matt Griffin --- activerecord/activerecord.gemspec | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index e8e5f4adfe..dca7f13fd2 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -8,6 +8,7 @@ Gem::Specification.new do |s| s.description = 'Databases on Rails. Build a persistent domain model by mapping database tables to Ruby classes. Strong conventions for associations, validations, aggregations, migrations, and testing come baked-in.' s.required_ruby_version = '>= 1.9.3' + s.license = 'MIT' s.author = 'David Heinemeier Hansson' s.email = 'david@loudthinking.com' -- cgit v1.2.3 From 1ad0b378cc081937c117577ab628f2160fcc448d Mon Sep 17 00:00:00 2001 From: Vijay Dev Date: Wed, 23 May 2012 22:43:08 +0530 Subject: Revert "Remove blank trailing comments" This reverts commit fa6d921e11363e9b8c4bc10f7aed0b9faffdc33a. Reason: Not a fan of such massive changes. We usually close such changes if made to Rails master as a pull request. Following the same principle here and reverting. [ci skip] --- activerecord/lib/active_record/aggregations.rb | 2 ++ activerecord/lib/active_record/associations/association.rb | 1 + activerecord/lib/active_record/callbacks.rb | 1 + .../connection_adapters/abstract/schema_definitions.rb | 1 + .../active_record/connection_adapters/abstract/schema_statements.rb | 1 + activerecord/lib/active_record/connection_adapters/mysql_adapter.rb | 1 + .../lib/active_record/connection_adapters/postgresql_adapter.rb | 1 + .../lib/active_record/connection_adapters/sqlite3_adapter.rb | 1 + activerecord/lib/active_record/locking/optimistic.rb | 1 + activerecord/lib/active_record/model.rb | 1 + activerecord/lib/active_record/observer.rb | 1 + activerecord/lib/active_record/persistence.rb | 1 + activerecord/lib/active_record/reflection.rb | 6 ++++++ activerecord/lib/active_record/relation/calculations.rb | 1 + activerecord/lib/active_record/relation/finder_methods.rb | 1 + activerecord/lib/active_record/relation/query_methods.rb | 3 +++ activerecord/lib/active_record/relation/spawn_methods.rb | 3 +++ activerecord/lib/active_record/validations/uniqueness.rb | 1 + activerecord/test/cases/attribute_methods_test.rb | 1 + activerecord/test/cases/validations/i18n_validation_test.rb | 1 + activerecord/test/models/subject.rb | 1 + 21 files changed, 31 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index e0392c5a48..6ad16bee2b 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -166,6 +166,7 @@ module ActiveRecord # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD": # # Customer.where(:balance => Money.new(20, "USD")).all + # module ClassMethods # Adds reader and writer methods for manipulating a value object: # composed_of :address adds address and address=(new_address) methods. @@ -206,6 +207,7 @@ module ActiveRecord # :mapping => %w(ip to_i), # :constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) }, # :converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) } + # def composed_of(part_id, options = {}) options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter) diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index 57d602fd83..e75003f261 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -34,6 +34,7 @@ module ActiveRecord # Returns the name of the table of the related class: # # post.comments.aliased_table_name # => "comments" + # def aliased_table_name klass.table_name end diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index fe18a5d5aa..a050fabf35 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -229,6 +229,7 @@ module ActiveRecord # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead) # # Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model. + # module Callbacks # We can't define callbacks directly on ActiveRecord::Model because # it is a module. So we queue up the definitions and execute them diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 95ab375951..df78ba6c5a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -339,6 +339,7 @@ module ActiveRecord # t.remove_index # t.remove_timestamps # end + # class Table def initialize(table_name, base) @table_name = table_name diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index e2eb72fdd6..62b0f51bb2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -380,6 +380,7 @@ module ActiveRecord # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active # # Note: only supported by PostgreSQL + # def add_index(table_name, column_name, options = {}) index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options) execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}" diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 3d6ab72e07..0b6734b010 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -59,6 +59,7 @@ module ActiveRecord # * :sslcert - Necessary to use MySQL with an SSL connection. # * :sslcapath - Necessary to use MySQL with an SSL connection. # * :sslcipher - Necessary to use MySQL with an SSL connection. + # class MysqlAdapter < AbstractMysqlAdapter class Column < AbstractMysqlAdapter::Column #:nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index ad7c4025cf..14bc95abfe 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -684,6 +684,7 @@ module ActiveRecord # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4) # Filter: (posts.user_id = 1) # (6 rows) + # def pp(result) header = result.columns.first lines = result.rows.map(&:first) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 25ec88c9c5..d4ffa82b17 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -265,6 +265,7 @@ module ActiveRecord # # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows) # 0|1|1|SCAN TABLE posts (~100000 rows) + # def pp(result) # :nodoc: result.rows.map do |row| row.join('|') diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 604da5a4fd..a3412582fa 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -46,6 +46,7 @@ module ActiveRecord # class Person < ActiveRecord::Base # self.locking_column = :lock_person # end + # module Optimistic extend ActiveSupport::Concern diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb index 5906a57828..105d1e0e2b 100644 --- a/activerecord/lib/active_record/model.rb +++ b/activerecord/lib/active_record/model.rb @@ -7,6 +7,7 @@ module ActiveRecord # class Post # include ActiveRecord::Model # end + # module Model module ClassMethods #:nodoc: include ActiveSupport::Callbacks::ClassMethods diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index 2467edcc53..fdf17c003c 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -87,6 +87,7 @@ module ActiveRecord # If by any chance you are using observed models in the initialization you can still # load their observers by calling ModelObserver.instance before. Observers are # singletons and that call instantiates and registers them. + # class Observer < ActiveModel::Observer protected diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index daf5936a2d..a1bc39a32d 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -158,6 +158,7 @@ module ActiveRecord # * Callbacks are invoked. # * updated_at/updated_on column is updated if that column is available. # * Updates all the attributes that are dirty in this object. + # def update_attribute(name, value) name = name.to_s verify_readonly_attribute(name) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 17e388a1d4..c380b5c029 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -42,6 +42,7 @@ module ActiveRecord # Returns the AggregateReflection object for the named +aggregation+ (use the symbol). # # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection + # def reflect_on_aggregation(aggregation) reflection = reflections[aggregation] reflection if reflection.is_a?(AggregateReflection) @@ -56,6 +57,7 @@ module ActiveRecord # # Account.reflect_on_all_associations # returns an array of all associations # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations + # def reflect_on_all_associations(macro = nil) association_reflections = reflections.values.grep(AssociationReflection) macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections @@ -65,6 +67,7 @@ module ActiveRecord # # Account.reflect_on_association(:owner) # returns the owner AssociationReflection # Invoice.reflect_on_association(:line_items).macro # returns :has_many + # def reflect_on_association(association) reflection = reflections[association] reflection if reflection.is_a?(AssociationReflection) @@ -383,6 +386,7 @@ module ActiveRecord # has_many :taggings # has_many :tags, :through => :taggings # end + # def source_reflection @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first end @@ -397,6 +401,7 @@ module ActiveRecord # # tags_reflection = Post.reflect_on_association(:tags) # taggings_reflection = tags_reflection.through_reflection + # def through_reflection @through_reflection ||= active_record.reflect_on_association(options[:through]) end @@ -483,6 +488,7 @@ module ActiveRecord # Gets an array of possible :through source reflection names: # # [:singularized, :pluralized] + # def source_reflection_names @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym } end diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 75d983d3b8..31d99f0192 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -138,6 +138,7 @@ module ActiveRecord # Person.pluck('DATEDIFF(updated_at, created_at)') # # SELECT DATEDIFF(updated_at, created_at) FROM people # # => ['0', '27761', '173'] + # def pluck(column_name) if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s) column_name = "#{table_name}.#{column_name}" diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 9d62e726d4..4fedd33d64 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -49,6 +49,7 @@ module ActiveRecord # # Post.find_by name: 'Spartacus', rating: 4 # Post.find_by "published_at < ?", 2.weeks.ago + # def find_by(*args) where(*args).take end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index c086386da6..19fe8155d9 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -163,6 +163,7 @@ module ActiveRecord # User.order('email DESC').reorder('id ASC').order('name ASC') # # generates a query with 'ORDER BY id ASC, name ASC'. + # def reorder(*args) args.blank? ? self : spawn.reorder!(*args) end @@ -276,6 +277,7 @@ module ActiveRecord # Post.none # => returning [] instead breaks the previous code # end # end + # def none NullRelation.new(@klass, @table) end @@ -310,6 +312,7 @@ module ActiveRecord # # Topics.select('a.title').from(Topics.approved, :a) # # => SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a + # def from(value, subquery_name = nil) spawn.from!(value, subquery_name) end diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 4fb1cb6726..80d087a9ea 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -23,6 +23,7 @@ module ActiveRecord # Post.where(:published => true).merge(recent_posts) # # Returns the intersection of all published posts with the 5 most recently created posts. # # (This is just an example. You'd probably want to do this with a single query!) + # def merge(other) if other.is_a?(Array) to_a & other @@ -44,6 +45,7 @@ module ActiveRecord # # Post.order('id asc').except(:order) # discards the order condition # Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order + # def except(*skips) result = Relation.new(klass, table, values.except(*skips)) result.default_scoped = default_scoped @@ -57,6 +59,7 @@ module ActiveRecord # # Post.order('id asc').only(:where) # discards the order condition # Post.order('id asc').only(:where, :order) # uses the specified order + # def only(*onlies) result = Relation.new(klass, table, values.slice(*onlies)) result.default_scoped = default_scoped diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index c1a72ea8b3..9e4b588ac2 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -198,6 +198,7 @@ module ActiveRecord # * ActiveRecord::ConnectionAdapters::Mysql2Adapter # * ActiveRecord::ConnectionAdapters::SQLite3Adapter # * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter + # def validates_uniqueness_of(*attr_names) validates_with UniquenessValidator, _merge_attributes(attr_names) end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index eef1366a6d..1093fedea1 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -719,6 +719,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase def test_read_attribute_overwrites_private_method_not_considered_implemented # simulate a model with a db column that shares its name an inherited # private method (e.g. Object#system) + # Object.class_eval do private def title; "private!"; end diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb index 89b92e0b13..15b97c02c8 100644 --- a/activerecord/test/cases/validations/i18n_validation_test.rb +++ b/activerecord/test/cases/validations/i18n_validation_test.rb @@ -33,6 +33,7 @@ class I18nValidationTest < ActiveRecord::TestCase # A set of common cases for ActiveModel::Validations message generation that # are used to generate tests to keep things DRY + # COMMON_CASES = [ # [ case, validation_options, generate_message_options] [ "given no options", {}, {}], diff --git a/activerecord/test/models/subject.rb b/activerecord/test/models/subject.rb index 6bf45ba5b2..8e28f8b86b 100644 --- a/activerecord/test/models/subject.rb +++ b/activerecord/test/models/subject.rb @@ -1,4 +1,5 @@ # used for OracleSynonymTest, see test/synonym_test_oracle.rb +# class Subject < ActiveRecord::Base # added initialization of author_email_address in the same way as in Topic class -- cgit v1.2.3 From 1c94868033c631dcb44d705f70433a64d48e0938 Mon Sep 17 00:00:00 2001 From: Vijay Dev Date: Wed, 23 May 2012 23:12:35 +0530 Subject: copy edits in collection proxy docs [ci skip] --- .../active_record/associations/collection_proxy.rb | 43 +++++++++------------- 1 file changed, 18 insertions(+), 25 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 6fd5c466f6..100fb38dec 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -92,6 +92,10 @@ module ActiveRecord # # ] # # person.pets.select(:name) { |pet| pet.name =~ /oo/ } + # # => [ + # # #, + # # # + # # ] ## # :method: find @@ -113,7 +117,7 @@ module ActiveRecord # # #, # # # # # ] - # + # # person.pets.find(1) # => # # person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=4 # @@ -231,7 +235,7 @@ module ActiveRecord # # Returns a new object of the collection type that has been instantiated with # attributes, linked to this object and that has already been saved (if it - # passed the validations). + # passes the validations). # # class Person # has_many :pets @@ -255,15 +259,14 @@ module ActiveRecord # # #, # # # # # ] - + ## # :method: create! # # :call-seq: # create!(attributes = {}, options = {}, &block) # - # Like +create+, except that if the record is invalid will - # raise an exception. + # Like +create+, except that if the record is invalid, raises an exception. # # class Person # has_many :pets @@ -340,8 +343,8 @@ module ActiveRecord ## # :method: delete_all # - # Deletes all the records from the collection. For +has_many+ it will do the - # deletion according to the strategy specified by the :dependent + # Deletes all the records from the collection. For +has_many+ asssociations, + # the deletion is done according to the strategy specified by the :dependent # option. Returns an array with the deleted records. # # If no :dependent option is given, then it will follow the @@ -501,7 +504,7 @@ module ActiveRecord # You can pass +Fixnum+ or +String+ values, it finds the records # responding to the +id+ and then deletes them from the database. # - # person.pets.size # => 3 + # person.pets.size # => 3 # person.pets # # => [ # # #, @@ -529,7 +532,7 @@ module ActiveRecord # person.pets # => [] # # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6) - + ## # :method: size # @@ -541,11 +544,7 @@ module ActiveRecord # end # # person.pets.size # => 3 - # # Executes: - # # - # # SELECT COUNT(*) - # # FROM "pets" - # # WHERE "pets"."person_id" = 1 + # # executes something like SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = 1 # # person.pets # This will execute a SELECT * FROM query # # => [ @@ -562,24 +561,18 @@ module ActiveRecord # :method: length # # Returns the size of the collection calling +size+ on the target. - # If the collection has been already loaded +length+ and +size+ are - # equivalent. If not and you are going to need the records anyway this - # method will take one less query because loads the collection. Otherwise - # +size+ is more efficient. + # If the collection has been already loaded, +length+ and +size+ are + # equivalent. # # class Person < ActiveRecord::Base # has_many :pets # end # # person.pets.length # => 3 - # # Executes: - # # - # # SELECT "pets".* - # #  FROM "pets" - # # WHERE "pets"."person_id" = 1 + # # executes something like SELECT "pets".* FROM "pets" WHERE "pets"."person_id" = 1 # # # Because the collection is loaded, you can - # # call the collection without execute a query: + # # call the collection with no additional queries: # person.pets # # => [ # # #, @@ -705,7 +698,7 @@ module ActiveRecord :sum, :count, :size, :length, :empty?, :any?, :many?, :include?, :to => :@association - + def initialize(association) @association = association super association.klass, association.klass.arel_table -- cgit v1.2.3 From 925f6bd546bd719c85b0566157ea5c17cfcd612d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 23 May 2012 15:30:07 -0300 Subject: PGconn doesn't accepts :checkout_timeout option. This option was added on cb6f839359bd894feb0a1105b79af72b336aa41e renaming the :wait_timeout option. Fix build http://travis-ci.org/#!/rails/rails/jobs/1413405 --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 15c3d7be36..8e318a9e94 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -18,7 +18,7 @@ module ActiveRecord # Forward any unused config params to PGconn.connect. [:statement_limit, :encoding, :min_messages, :schema_search_path, - :schema_order, :adapter, :pool, :wait_timeout, :template, + :schema_order, :adapter, :pool, :checkout_timeout, :template, :reaping_frequency, :insert_returning].each do |key| conn_params.delete key end -- cgit v1.2.3 From 82b05fbcf4869517c08fb62398b03f3f133c0a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 23 May 2012 15:32:05 -0300 Subject: Whitespaces :scissors: --- .../connection_adapters/abstract/connection_pool.rb | 12 ++++++------ .../active_record/connection_adapters/postgresql_adapter.rb | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord') 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 be36c9695f..c259e46073 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -61,21 +61,21 @@ module ActiveRecord # * +pool+: number indicating size of connection pool (default 5) # * +checkout_timeout+: number of seconds to block and wait for a connection # before giving up and raising a timeout error (default 5 seconds). - # * +reaping_frequency+: frequency in seconds to periodically run the - # Reaper, which attempts to find and close dead connections, which can - # occur if a programmer forgets to close a connection at the end of a + # * +reaping_frequency+: frequency in seconds to periodically run the + # Reaper, which attempts to find and close dead connections, which can + # occur if a programmer forgets to close a connection at the end of a # thread or a thread dies unexpectedly. (Default nil, which means don't - # run the Reaper). + # run the Reaper). # * +dead_connection_timeout+: number of seconds from last checkout # after which the Reaper will consider a connection reapable. (default - # 5 seconds). + # 5 seconds). class ConnectionPool # Every +frequency+ seconds, the reaper will call +reap+ on +pool+. # A reaper instantiated with a nil frequency will never reap the # connection pool. # # Configure the frequency by setting "reaping_frequency" in your - # database yaml file. + # database yaml file. class Reaper attr_reader :pool, :frequency diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 8e318a9e94..d62cf529a4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -1274,7 +1274,7 @@ module ActiveRecord end when 'integer' return 'integer' unless limit - + case limit when 1, 2; 'smallint' when 3, 4; 'integer' -- cgit v1.2.3 From 6775ad8c61b18e0df2df7c7103478d4f315c0cb3 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Thu, 24 May 2012 15:12:11 -0700 Subject: Merge pull request #5925 from Juanmcuello/pg_structure_dump Quote arguments in db:structure:dump for PostgreSQL. --- activerecord/lib/active_record/railties/databases.rake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index f26e18b1e0..791a22958c 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -406,9 +406,9 @@ db_namespace = namespace :db do set_psql_env(abcs[Rails.env]) search_path = abcs[Rails.env]['schema_search_path'] unless search_path.blank? - search_path = search_path.split(",").map{|search_path_part| "--schema=#{search_path_part.strip}" }.join(" ") + search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ") end - `pg_dump -i -s -x -O -f #{filename} #{search_path} #{abcs[Rails.env]['database']}` + `pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(abcs[Rails.env]['database'])}` raise 'Error dumping database' if $?.exitstatus == 1 when /sqlite/ dbfile = abcs[Rails.env]['database'] -- cgit v1.2.3 From 29463aa15dd670621ad0de861b5aa66feeb09ffa Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Fri, 25 May 2012 22:08:59 -0500 Subject: add CollectionProxy#delete documentation --- .../active_record/associations/collection_proxy.rb | 105 ++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 100fb38dec..2b59b07eb2 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -343,6 +343,9 @@ module ActiveRecord ## # :method: delete_all # + # :call-seq: + # delete_all() + # # Deletes all the records from the collection. For +has_many+ asssociations, # the deletion is done according to the strategy specified by the :dependent # option. Returns an array with the deleted records. @@ -435,6 +438,9 @@ module ActiveRecord ## # :method: destroy_all # + # :call-seq: + # destroy_all() + # # Deletes the records of the collection directly from the database. # This will _always_ remove the records ignoring the +:dependent+ # option. @@ -458,13 +464,110 @@ module ActiveRecord # # Pet.find(1) # => Couldn't find Pet with id=1 + ## + # :method: delete + # + # :call-seq: + # delete(*records) + # + # Deletes the +records+ supplied and remove them from the collection. For + # +has_many+ associations, the deletion is done according to the strategy + # specified by the :dependent option. Returns an array with the + # deleted records. + # + # If no :dependent option is given, then it will follow the default + # strategy. The default strategy is :nullify. This sets the foreign + # keys to NULL. For, +has_many+ :through, the default + # strategy is +delete_all+. + # + # class Person < ActiveRecord::Base + # has_many :pets # dependent: :nullify option by default + # end + # + # person.pets.size # => 3 + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] + # + # person.pets.delete(Pet.find(1)) + # # => [#] + # + # person.pets.size # => 2 + # person.pets + # # => [ + # # #, + # # # + # # ] + # + # Pet.find(1) + # # => # + # + # If it is set to :destroy all the +records+ are removed by calling + # their +destroy+ method. See +destroy+ for more information. + # + # class Person < ActiveRecord::Base + # has_many :pets, dependent: :destroy + # end + # + # person.pets.size # => 3 + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] + # + # person.pets.delete([Pet.find(1), Pet.find(3)]) + # # => [ + # # #, + # # # + # # ] + # + # person.pets.size # => 1 + # person.pets + # # => [#] + # + # Pet.find(1, 3) + # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 3) + # + # If it is set to :delete_all, all the +records+ are deleted + # *without* calling their +destroy+ method. + # + # class Person < ActiveRecord::Base + # has_many :pets, dependent: :delete_all + # end + # + # person.pets.size # => 3 + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] + # + # person.pets.delete(Pet.find(1)) + # # => [#] + # + # person.pets.size # => 2 + # person.pets + # # => [ + # # #, + # # # + # # ] + # + # Pet.find(1) + # # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1 + ## # :method: destroy # # :call-seq: # destroy(*records) # - # Destroy the +records+ supplied and remove them from the collection. + # Destroys the +records+ supplied and remove them from the collection. # This method will _always_ remove record from the database ignoring # the +:dependent+ option. Returns an array with the removed records. # -- cgit v1.2.3 From 390d86ae899f6aba735d8efa0f7f23e05c6957a5 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Fri, 25 May 2012 22:18:56 -0500 Subject: add CollectionProxy#to_ary documentation --- .../active_record/associations/collection_proxy.rb | 37 ++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 2b59b07eb2..5b5aef070d 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -470,7 +470,7 @@ module ActiveRecord # :call-seq: # delete(*records) # - # Deletes the +records+ supplied and remove them from the collection. For + # Deletes the +records+ supplied and removes them from the collection. For # +has_many+ associations, the deletion is done according to the strategy # specified by the :dependent option. Returns an array with the # deleted records. @@ -567,7 +567,7 @@ module ActiveRecord # :call-seq: # destroy(*records) # - # Destroys the +records+ supplied and remove them from the collection. + # Destroys the +records+ supplied and removes them from the collection. # This method will _always_ remove record from the database ignoring # the +:dependent+ option. Returns an array with the removed records. # @@ -838,6 +838,39 @@ module ActiveRecord load_target == other end + # Returns a new array of objects from the collection. If the collection + # hasn't been loaded, it fetches the records from the database. + # + # class Person < ActiveRecord::Base + # has_many :pets + # end + # + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] + # + # other_pets = person.pets.to_ary + # # => [ + # # #, + # # #, + # # # + # # ] + # + # other_pets.replace([Pet.new(name: 'BooGoo')]) + # + # other_pets + # # => [#] + # + # person.pets + # # This is not affected by replace + # # => [ + # # #, + # # #, + # # # + # # ] def to_ary load_target.dup end -- cgit v1.2.3 From f81798d06415c538f5b1c5acfd4889b45473e62d Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Fri, 25 May 2012 22:45:22 -0500 Subject: add CollectionProxy#count documentation --- .../active_record/associations/collection_proxy.rb | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 5b5aef070d..93ed2e5a56 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -636,9 +636,32 @@ module ActiveRecord # # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6) + ## + # :method: count + # + # :call-seq: + # count() + # + # Count all records using SQL. + # + # class Person < ActiveRecord::Base + # has_many :pets + # end + # + # person.pets.count # => 3 + # person.pets + # # => [ + # # #, + # # #, + # # # + # # ] + ## # :method: size # + # :call-seq: + # size() + # # Returns the size of the collection. If the collection hasn't been loaded, # it executes a SELECT COUNT(*) query. # @@ -663,6 +686,9 @@ module ActiveRecord ## # :method: length # + # :call-seq: + # length() + # # Returns the size of the collection calling +size+ on the target. # If the collection has been already loaded, +length+ and +size+ are # equivalent. -- cgit v1.2.3 From bb55f52ee8ae0eb932eec4eace8adb81b05d13ad Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Fri, 25 May 2012 23:01:38 -0500 Subject: add CollectionProxy#== documentation --- .../active_record/associations/collection_proxy.rb | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 93ed2e5a56..44f5041bd8 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -860,6 +860,30 @@ module ActiveRecord end end + # Equivalent to Array#==. Returns +true+ if the two arrays + # contain the same number of elements and if each element is equal + # to the corresponding element in the other array, otherwise returns + # +false+. + # + # class Person < ActiveRecord::Base + # has_many :pets + # end + # + # person.pets + # # => [ + # # #, + # # # + # # ] + # + # other = person.pets.to_ary + # + # person.pets == other + # # => true + # + # other = [Pet.new(id: 1), Pet.new(id: 2)] + # + # person.pets == other + # # => false def ==(other) load_target == other end -- cgit v1.2.3 From 7a323e2ed15e06f468d783340c4680c5ad235e87 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Fri, 25 May 2012 23:09:47 -0500 Subject: add :nodoc: to CollectionProxy#initialize --- activerecord/lib/active_record/associations/collection_proxy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 44f5041bd8..7408428493 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -828,7 +828,7 @@ module ActiveRecord :any?, :many?, :include?, :to => :@association - def initialize(association) + def initialize(association) #:nodoc: @association = association super association.klass, association.klass.arel_table merge! association.scoped -- cgit v1.2.3 From 55c05276c71548f158cb9ae28fe2a154de9f8349 Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Sat, 26 May 2012 22:43:24 -0500 Subject: add CollectionProxy#uniq documentation --- .../active_record/associations/collection_proxy.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 7408428493..294aa63f75 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -636,6 +636,27 @@ module ActiveRecord # # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6) + ## + # :method: uniq + # + # :call-seq: + # uniq() + # + # Specifies whether the records should be unique or not. + # + # class Person < ActiveRecord::Base + # has_many :pets + # end + # + # person.pets.select(:name) + # # => [ + # # #, + # # # + # # ] + # + # person.pets.select(:name).uniq + # # => [#] + ## # :method: count # -- cgit v1.2.3 From 4df16f3f3d2e2f806532ef2f698ae29f26df44db Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Sat, 26 May 2012 22:51:26 -0500 Subject: remove unnecessary ruby 1.8 reference from active_record/core [ci skip] --- activerecord/lib/active_record/core.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index f2833fbf3c..80c6f20b1a 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -10,9 +10,10 @@ module ActiveRecord included do ## # :singleton-method: - # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, - # which is then passed on to any new database connections made and which can be retrieved on both - # a class and instance level by calling +logger+. + # + # Accepts a logger conforming to the interface of Log4r which is then + # passed on to any new database connections made and which can be + # retrieved on both a class and instance level by calling +logger+. config_attribute :logger, :global => true ## -- cgit v1.2.3 From d138921cece0c4923849ff61100fcce56e98e967 Mon Sep 17 00:00:00 2001 From: Mark Rushakoff Date: Sun, 27 May 2012 08:25:00 -0700 Subject: "a sql" -> "an SQL" per API documentation guidelines --- activerecord/lib/active_record/relation/calculations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 31d99f0192..ab32bfe70a 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -118,7 +118,7 @@ module ActiveRecord # Person.all.map(&:name) # # Pluck returns an Array of attribute values type-casted to match - # the plucked column name, if it can be deduced. Plucking a SQL fragment + # the plucked column name, if it can be deduced. Plucking an SQL fragment # returns String values by default. # # Examples: -- cgit v1.2.3