From 8b2f801ed8690dcbc61d62e6b3518efaac70a4a4 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 12:53:38 -0800 Subject: converted migrations to support instance methods --- activerecord/lib/active_record/migration.rb | 40 ++++++++++++++++++++++++----- activerecord/lib/active_record/schema.rb | 6 ++--- 2 files changed, 36 insertions(+), 10 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 2c306d233a..4a1a84b74b 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -287,10 +287,30 @@ module ActiveRecord # In application.rb. # class Migration - @@verbose = true - cattr_accessor :verbose - class << self + attr_accessor :verbose + attr_accessor :delegate # :nodoc: + def method_missing(name, *args, &block) # :nodoc: + (delegate || superclass.delegate).send(name, *args, &block) + end + end + self.delegate = new + self.verbose = true + + def name + self.class.name + end + + def up + self.class.delegate = self + self.class.up + end + + def down + self.class.delegate = self + self.class.down + end + def up_with_benchmarks #:nodoc: migrate(:up) end @@ -309,7 +329,7 @@ module ActiveRecord end result = nil - time = Benchmark.measure { result = send("#{direction}_without_benchmarks") } + time = Benchmark.measure { result = send("#{direction}") } case direction when :up then announce "migrated (%.4fs)" % time.real; write @@ -380,10 +400,19 @@ module ActiveRecord unless arguments.empty? || method == :execute arguments[0] = Migrator.proper_table_name(arguments.first) end + return super unless connection.respond_to?(method) connection.send(method, *arguments, &block) end end + def verbose + self.class.verbose + end + + def verbose= verbosity + self.class.verbose = verbosity + end + def copy(destination, sources, options = {}) copied = [] @@ -425,7 +454,6 @@ module ActiveRecord "%.3d" % number end end - end end # MigrationProxy is used to defer loading of the actual migration classes @@ -451,7 +479,7 @@ module ActiveRecord def load_migration require(File.expand_path(filename)) - name.constantize + name.constantize.new end end diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index c1bc3214ea..a621248cc3 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -30,9 +30,7 @@ module ActiveRecord # ActiveRecord::Schema is only supported by database adapters that also # support migrations, the two features being very similar. class Schema < Migration - private_class_method :new - - def self.migrations_path + def migrations_path ActiveRecord::Migrator.migrations_path end @@ -48,7 +46,7 @@ module ActiveRecord # ... # end def self.define(info={}, &block) - instance_eval(&block) + new.instance_eval(&block) unless info[:version].blank? initialize_schema_migrations_table -- cgit v1.2.3 From 0bea9fd6be1c82154d7b2d4adbfc690a2c1df297 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 13:31:43 -0800 Subject: schema migrations work as instances --- activerecord/lib/active_record/migration.rb | 11 +++++++---- activerecord/lib/active_record/schema.rb | 5 +++-- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 4a1a84b74b..390c24c2f2 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -288,12 +288,15 @@ module ActiveRecord # class Migration class << self - attr_accessor :verbose attr_accessor :delegate # :nodoc: - def method_missing(name, *args, &block) # :nodoc: - (delegate || superclass.delegate).send(name, *args, &block) - end end + + def self.method_missing(name, *args, &block) # :nodoc: + (delegate || superclass.delegate).send(name, *args, &block) + end + + cattr_accessor :verbose + self.delegate = new self.verbose = true diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index a621248cc3..c6bb5c1961 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -46,11 +46,12 @@ module ActiveRecord # ... # end def self.define(info={}, &block) - new.instance_eval(&block) + schema = new + schema.instance_eval(&block) unless info[:version].blank? initialize_schema_migrations_table - assume_migrated_upto_version(info[:version], migrations_path) + assume_migrated_upto_version(info[:version], schema.migrations_path) end end end -- cgit v1.2.3 From 974ff0dd43826aa375417852356ceede1bd24cf2 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 13:44:26 -0800 Subject: singleton method added is no longer needed --- activerecord/lib/active_record/migration.rb | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 390c24c2f2..936e9a19a2 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -332,7 +332,7 @@ module ActiveRecord end result = nil - time = Benchmark.measure { result = send("#{direction}") } + time = Benchmark.measure { result = send(direction) } case direction when :up then announce "migrated (%.4fs)" % time.real; write @@ -342,24 +342,6 @@ module ActiveRecord result end - # Because the method added may do an alias_method, it can be invoked - # recursively. We use @ignore_new_methods as a guard to indicate whether - # it is safe for the call to proceed. - def singleton_method_added(sym) #:nodoc: - return if defined?(@ignore_new_methods) && @ignore_new_methods - - begin - @ignore_new_methods = true - - case sym - when :up, :down - singleton_class.send(:alias_method_chain, sym, "benchmarks") - end - ensure - @ignore_new_methods = false - end - end - def write(text="") puts(text) if verbose end -- cgit v1.2.3 From 68b66ef3082b5d6ca47f621ea51cad9321847caf Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 13:55:03 -0800 Subject: testing instance based migrations --- activerecord/lib/active_record/migration.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 936e9a19a2..470f63e8f7 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -306,11 +306,13 @@ module ActiveRecord def up self.class.delegate = self + return unless self.class.respond_to?(:up) self.class.up end def down self.class.delegate = self + return unless self.class.respond_to?(:down) self.class.down end -- cgit v1.2.3 From b0a6f58068394d11841b57d94b3a6ecb42c3b8b0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 14:10:51 -0800 Subject: do not need these accessors --- activerecord/lib/active_record/migration.rb | 8 -------- 1 file changed, 8 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 470f63e8f7..6acb8382e7 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -392,14 +392,6 @@ module ActiveRecord end end - def verbose - self.class.verbose - end - - def verbose= verbosity - self.class.verbose = verbosity - end - def copy(destination, sources, options = {}) copied = [] -- cgit v1.2.3 From 606e41a4dd67fd0af2166b9b74836c2fd11c189e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 14:48:12 -0800 Subject: these methods are no longer needed --- activerecord/lib/active_record/migration.rb | 8 -------- 1 file changed, 8 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 6acb8382e7..222fb4d83c 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -316,14 +316,6 @@ module ActiveRecord self.class.down end - def up_with_benchmarks #:nodoc: - migrate(:up) - end - - def down_with_benchmarks #:nodoc: - migrate(:down) - end - # Execute this migration in the named direction def migrate(direction) return unless respond_to?(direction) -- cgit v1.2.3 From 7906e08bbacc2f7d96227e8a739c6762cd45a6ca Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 14:49:47 -0800 Subject: fixing indentation since these methods are not class methods --- activerecord/lib/active_record/migration.rb | 166 ++++++++++++++-------------- 1 file changed, 83 insertions(+), 83 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 222fb4d83c..ca3ca54fa0 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -316,115 +316,115 @@ module ActiveRecord self.class.down end - # Execute this migration in the named direction - def migrate(direction) - return unless respond_to?(direction) + # Execute this migration in the named direction + def migrate(direction) + return unless respond_to?(direction) - case direction - when :up then announce "migrating" - when :down then announce "reverting" - end - - result = nil - time = Benchmark.measure { result = send(direction) } + case direction + when :up then announce "migrating" + when :down then announce "reverting" + end - case direction - when :up then announce "migrated (%.4fs)" % time.real; write - when :down then announce "reverted (%.4fs)" % time.real; write - end + result = nil + time = Benchmark.measure { result = send(direction) } - result + case direction + when :up then announce "migrated (%.4fs)" % time.real; write + when :down then announce "reverted (%.4fs)" % time.real; write end - def write(text="") - puts(text) if verbose - end + result + end - def announce(message) - version = defined?(@version) ? @version : nil + def write(text="") + puts(text) if verbose + end - text = "#{version} #{name}: #{message}" - length = [0, 75 - text.length].max - write "== %s %s" % [text, "=" * length] - end + def announce(message) + version = defined?(@version) ? @version : nil - def say(message, subitem=false) - write "#{subitem ? " ->" : "--"} #{message}" - end + text = "#{version} #{name}: #{message}" + length = [0, 75 - text.length].max + write "== %s %s" % [text, "=" * length] + end - def say_with_time(message) - say(message) - result = nil - time = Benchmark.measure { result = yield } - say "%.4fs" % time.real, :subitem - say("#{result} rows", :subitem) if result.is_a?(Integer) - result - end + def say(message, subitem=false) + write "#{subitem ? " ->" : "--"} #{message}" + end - def suppress_messages - save, self.verbose = verbose, false - yield - ensure - self.verbose = save - end + def say_with_time(message) + say(message) + result = nil + time = Benchmark.measure { result = yield } + say "%.4fs" % time.real, :subitem + say("#{result} rows", :subitem) if result.is_a?(Integer) + result + end - def connection - ActiveRecord::Base.connection - end + def suppress_messages + save, self.verbose = verbose, false + yield + ensure + self.verbose = save + end - def method_missing(method, *arguments, &block) - arg_list = arguments.map{ |a| a.inspect } * ', ' + def connection + ActiveRecord::Base.connection + end - say_with_time "#{method}(#{arg_list})" do - unless arguments.empty? || method == :execute - arguments[0] = Migrator.proper_table_name(arguments.first) - end - return super unless connection.respond_to?(method) - connection.send(method, *arguments, &block) + def method_missing(method, *arguments, &block) + arg_list = arguments.map{ |a| a.inspect } * ', ' + + say_with_time "#{method}(#{arg_list})" do + unless arguments.empty? || method == :execute + arguments[0] = Migrator.proper_table_name(arguments.first) end + return super unless connection.respond_to?(method) + connection.send(method, *arguments, &block) end + end - def copy(destination, sources, options = {}) - copied = [] + def copy(destination, sources, options = {}) + copied = [] - FileUtils.mkdir_p(destination) unless File.exists?(destination) + FileUtils.mkdir_p(destination) unless File.exists?(destination) - destination_migrations = ActiveRecord::Migrator.migrations(destination) - last = destination_migrations.last - sources.each do |name, path| - source_migrations = ActiveRecord::Migrator.migrations(path) + destination_migrations = ActiveRecord::Migrator.migrations(destination) + last = destination_migrations.last + sources.each do |name, path| + source_migrations = ActiveRecord::Migrator.migrations(path) - source_migrations.each do |migration| - source = File.read(migration.filename) - source = "# This migration comes from #{name} (originally #{migration.version})\n#{source}" + source_migrations.each do |migration| + source = File.read(migration.filename) + source = "# This migration comes from #{name} (originally #{migration.version})\n#{source}" - if duplicate = destination_migrations.detect { |m| m.name == migration.name } - options[:on_skip].call(name, migration) if File.read(duplicate.filename) != source && options[:on_skip] - next - end + if duplicate = destination_migrations.detect { |m| m.name == migration.name } + options[:on_skip].call(name, migration) if File.read(duplicate.filename) != source && options[:on_skip] + next + end - migration.version = next_migration_number(last ? last.version + 1 : 0).to_i - new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.rb") - old_path, migration.filename = migration.filename, new_path - last = migration + migration.version = next_migration_number(last ? last.version + 1 : 0).to_i + new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.rb") + old_path, migration.filename = migration.filename, new_path + last = migration - FileUtils.cp(old_path, migration.filename) - copied << migration - options[:on_copy].call(name, migration, old_path) if options[:on_copy] - destination_migrations << migration - end + FileUtils.cp(old_path, migration.filename) + copied << migration + options[:on_copy].call(name, migration, old_path) if options[:on_copy] + destination_migrations << migration end - - copied end - def next_migration_number(number) - if ActiveRecord::Base.timestamped_migrations - [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max - else - "%.3d" % number - end + copied + end + + def next_migration_number(number) + if ActiveRecord::Base.timestamped_migrations + [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max + else + "%.3d" % number end + end end # MigrationProxy is used to defer loading of the actual migration classes -- cgit v1.2.3 From d1fcba81188fafae2a65cc4c2ebca67df3f36f75 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 14:52:52 -0800 Subject: fixing documentation, removing unused AS files --- activerecord/lib/active_record/migration.rb | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index ca3ca54fa0..80aa123fdd 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1,6 +1,3 @@ -require 'active_support/core_ext/kernel/singleton_class' -require 'active_support/core_ext/module/aliasing' - module ActiveRecord # Exception that can be raised to stop migrations from going backwards. class IrreversibleMigration < ActiveRecordError @@ -43,11 +40,11 @@ module ActiveRecord # Example of a simple migration: # # class AddSsl < ActiveRecord::Migration - # def self.up + # def up # add_column :accounts, :ssl_enabled, :boolean, :default => 1 # end # - # def self.down + # def down # remove_column :accounts, :ssl_enabled # end # end @@ -63,7 +60,7 @@ module ActiveRecord # Example of a more complex migration that also needs to initialize data: # # class AddSystemSettings < ActiveRecord::Migration - # def self.up + # def up # create_table :system_settings do |t| # t.string :name # t.string :label @@ -77,7 +74,7 @@ module ActiveRecord # :value => 1 # end # - # def self.down + # def down # drop_table :system_settings # end # end @@ -147,11 +144,11 @@ module ActiveRecord # # This will generate the file timestamp_add_fieldname_to_tablename, which will look like this: # class AddFieldnameToTablename < ActiveRecord::Migration - # def self.up + # def up # add_column :tablenames, :fieldname, :string # end # - # def self.down + # def down # remove_column :tablenames, :fieldname # end # end @@ -179,11 +176,11 @@ module ActiveRecord # Not all migrations change the schema. Some just fix the data: # # class RemoveEmptyTags < ActiveRecord::Migration - # def self.up + # def up # Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? } # end # - # def self.down + # def down # # not much we can do to restore deleted data # raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags" # end @@ -192,12 +189,12 @@ module ActiveRecord # Others remove columns when they migrate up instead of down: # # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration - # def self.up + # def up # remove_column :items, :incomplete_items_count # remove_column :items, :completed_items_count # end # - # def self.down + # def down # add_column :items, :incomplete_items_count # add_column :items, :completed_items_count # end @@ -206,11 +203,11 @@ module ActiveRecord # And sometimes you need to do something in SQL not abstracted directly by migrations: # # class MakeJoinUnique < ActiveRecord::Migration - # def self.up + # def up # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)" # end # - # def self.down + # def down # execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`" # end # end -- cgit v1.2.3 From c1a63c8dba00b9f6ed81b3a46ecf595273cd876d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 14:54:08 -0800 Subject: fixing more documentation --- activerecord/lib/active_record/migration.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 80aa123fdd..65befa7473 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -135,7 +135,7 @@ module ActiveRecord # in the db/migrate/ directory where timestamp is the # UTC formatted date and time that the migration was generated. # - # You may then edit the self.up and self.down methods of + # You may then edit the up and down methods of # MyNewMigration. # # There is a special syntactic shortcut to generate migrations that add fields to a table. @@ -220,7 +220,7 @@ module ActiveRecord # latest column data from after the new column was added. Example: # # class AddPeopleSalary < ActiveRecord::Migration - # def self.up + # def up # add_column :people, :salary, :integer # Person.reset_column_information # Person.find(:all).each do |p| @@ -240,7 +240,7 @@ module ActiveRecord # You can also insert your own messages and benchmarks by using the +say_with_time+ # method: # - # def self.up + # def up # ... # say_with_time "Updating salaries..." do # Person.find(:all).each do |p| -- cgit v1.2.3 From 43e2e10f4fd1111e485d4d1b1e509c00dc13c58c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 15:30:01 -0800 Subject: adding an initialize with name and version defaults --- activerecord/lib/active_record/migration.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 65befa7473..0eabc4a4aa 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -297,8 +297,11 @@ module ActiveRecord self.delegate = new self.verbose = true - def name - self.class.name + attr_accessor :name, :version + + def initialize + @name = self.class.name + @version = nil end def up @@ -338,8 +341,6 @@ module ActiveRecord end def announce(message) - version = defined?(@version) ? @version : nil - text = "#{version} #{name}: #{message}" length = [0, 75 - text.length].max write "== %s %s" % [text, "=" * length] -- cgit v1.2.3 From f978c4b2e44401260bbf4b5a954fda0b2bc71781 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 17 Nov 2010 16:12:21 -0500 Subject: remove the rescue block by returning a not asking Base for lookup_ancestors. It was also marked for later optimization. --- activerecord/lib/active_record/base.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b35f59d6df..60a9ca7464 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -736,15 +736,12 @@ module ActiveRecord #:nodoc: def lookup_ancestors #:nodoc: klass = self classes = [klass] + return classes if klass == ActiveRecord::Base + while klass != klass.base_class classes << klass = klass.superclass end classes - rescue - # OPTIMIZE this rescue is to fix this test: ./test/cases/reflection_test.rb:56:in `test_human_name_for_column' - # Apparently the method base_class causes some trouble. - # It now works for sure. - [self] end # Set the i18n scope to overwrite ActiveModel. -- cgit v1.2.3 From 9c1993bf6bdd0a3d2c46310c953631c1d5a7e3d1 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 17 Nov 2010 23:12:22 +0800 Subject: replace and with && as per rails coding convention --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 60a9ca7464..314f676711 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1283,7 +1283,7 @@ MSG # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'" def sanitize_sql_array(ary) statement, *values = ary - if values.first.is_a?(Hash) and statement =~ /:\w+/ + if values.first.is_a?(Hash) && statement =~ /:\w+/ replace_named_bind_variables(statement, values.first) elsif statement.include?('?') replace_bind_variables(statement, values) -- cgit v1.2.3 From 56c5820458fd3981161393c285cce67fdf35e60b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 16:14:17 -0800 Subject: use shorter form for sql literals --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 9c399d3333..514576564c 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -141,7 +141,7 @@ module ActiveRecord "#{@klass.table_name}.#{@klass.primary_key} DESC" : reverse_sql_order(order_clause).join(', ') - except(:order).order(Arel::SqlLiteral.new(order)) + except(:order).order(Arel.sql(order)) end def arel -- cgit v1.2.3 From 00693209ecc222842949d7cab076f89890cbd507 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 17:10:40 -0800 Subject: collapsing same table / column WHERE clauses to be OR [#4598 state:resolved] --- .../lib/active_record/relation/query_methods.rb | 29 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 514576564c..07ca2e2088 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -174,10 +174,7 @@ module ActiveRecord arel = build_joins(arel, @joins_values) unless @joins_values.empty? - (@where_values - ['']).uniq.each do |where| - where = Arel.sql(where) if String === where - arel = arel.where(Arel::Nodes::Grouping.new(where)) - end + arel = collapse_wheres(arel, (@where_values - ['']).uniq) arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty? @@ -198,6 +195,30 @@ module ActiveRecord private + def collapse_wheres(arel, wheres) + equalities = wheres.grep(Arel::Nodes::Equality) + + groups = equalities.group_by do |equality| + left = equality.left + # table, column + [left.relation.name, left.name] + end + + groups.each do |_, eqls| + head = eqls.first + test = eqls.inject(head) do |memo, expr| + expr == head ? expr : memo.or(expr) + end + arel = arel.where(test) + end + + (wheres - equalities).each do |where| + where = Arel.sql(where) if String === where + arel = arel.where(Arel::Nodes::Grouping.new(where)) + end + arel + end + def build_where(opts, other = []) case opts when String, Array -- cgit v1.2.3 From 80d9b724c3f2f49ce99f1c41eddbebe7cf16686d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 17 Nov 2010 17:28:40 -0800 Subject: group can be done by left side only --- activerecord/lib/active_record/relation/query_methods.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 07ca2e2088..9e7503a60d 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -199,15 +199,12 @@ module ActiveRecord equalities = wheres.grep(Arel::Nodes::Equality) groups = equalities.group_by do |equality| - left = equality.left - # table, column - [left.relation.name, left.name] + equality.left end groups.each do |_, eqls| - head = eqls.first - test = eqls.inject(head) do |memo, expr| - expr == head ? expr : memo.or(expr) + test = eqls.inject(eqls.shift) do |memo, expr| + memo.or(expr) end arel = arel.where(test) end -- cgit v1.2.3 From 126fbd7ed8a310bf940414c1b7ddab06b03d400e Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 17 Nov 2010 21:46:55 -0500 Subject: unscoped takes care of named_scopes too --- activerecord/lib/active_record/base.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f588475bbf..d7a7101404 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -856,8 +856,7 @@ module ActiveRecord #:nodoc: # limit(10) # Fires "SELECT * FROM posts LIMIT 10" # } # - # It is recommended to use block form of unscoped because chaining unscoped with named_scope - # does not work. Assuming that published is a named_scope following two statements are same. + # Assuming that published is a named_scope following two statements are same. # # Post.unscoped.published # Post.published -- cgit v1.2.3 From c5a284f8eb6113f06030ea7a18543905146e8768 Mon Sep 17 00:00:00 2001 From: Alex Rothenberg Date: Mon, 8 Nov 2010 15:30:27 -0500 Subject: Adapters can specify maximum number of ids they support in a list of expressions (default is nil meaning unlimited but Oracle imposes a limit of 1000) Limit is used to make multiple queries when preloading associated has_many or habtm records --- .../lib/active_record/association_preload.rb | 26 +++++++++++++++++----- .../connection_adapters/abstract_adapter.rb | 5 +++++ 2 files changed, 26 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 911a5155fd..a205549d60 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -193,13 +193,17 @@ module ActiveRecord conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}" conditions << append_conditions(reflection, preload_options) - associated_records = reflection.klass.unscoped.where([conditions, ids]). + associated_records_proxy = reflection.klass.unscoped. includes(options[:include]). joins("INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}"). select("#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id"). - order(options[:order]).to_a + order(options[:order]) - set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'the_parent_record_id') + all_associated_records = associated_records(ids) do |some_ids| + associated_records_proxy.where([conditions, ids]).to_a + end + + set_association_collection_records(id_to_record_map, reflection.name, all_associated_records, 'the_parent_record_id') end def preload_has_one_association(records, reflection, preload_options={}) @@ -358,13 +362,14 @@ module ActiveRecord find_options = { :select => preload_options[:select] || options[:select] || Arel::SqlLiteral.new("#{table_name}.*"), :include => preload_options[:include] || options[:include], - :conditions => [conditions, ids], :joins => options[:joins], :group => preload_options[:group] || options[:group], :order => preload_options[:order] || options[:order] } - reflection.klass.scoped.apply_finder_options(find_options).to_a + associated_records(ids) do |some_ids| + reflection.klass.scoped.apply_finder_options(find_options.merge(:conditions => [conditions, some_ids])).to_a + end end @@ -382,6 +387,17 @@ module ActiveRecord def in_or_equals_for_ids(ids) ids.size > 1 ? "IN (?)" : "= ?" end + + # Some databases impose a limit on the number of ids in a list (in Oracle its 1000) + # Make several smaller queries if necessary or make one query if the adapter supports it + def associated_records(ids) + max_ids_in_a_list = connection.ids_in_list_limit || ids.size + records = [] + ids.each_slice(max_ids_in_a_list) do |some_ids| + records += yield(some_ids) + end + records + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index f3fba9a3a9..ada1560ce2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -91,6 +91,11 @@ module ActiveRecord false end + # Does this adapter restrict the number of ids you can use in a list. Oracle has a limit of 1000. + def ids_in_list_limit + nil + end + # QUOTING ================================================== # Override to return the quoted table name. Defaults to column quoting. -- cgit v1.2.3 From 26923756fb23eb9c2993a365f9819027f20d5e77 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 18 Nov 2010 10:01:21 -0800 Subject: removing space errors --- activerecord/lib/active_record/association_preload.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index a205549d60..9743b1b4a7 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -387,7 +387,7 @@ module ActiveRecord def in_or_equals_for_ids(ids) ids.size > 1 ? "IN (?)" : "= ?" end - + # Some databases impose a limit on the number of ids in a list (in Oracle its 1000) # Make several smaller queries if necessary or make one query if the adapter supports it def associated_records(ids) -- cgit v1.2.3 From 07a74f196d6766c09e28ed696debb120a035dde7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 18 Nov 2010 15:53:59 -0800 Subject: connection is set from the connection pool during migrations --- activerecord/lib/active_record/migration.rb | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 0eabc4a4aa..06a8747fde 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -300,8 +300,9 @@ module ActiveRecord attr_accessor :name, :version def initialize - @name = self.class.name - @version = nil + @name = self.class.name + @version = nil + @connection = nil end def up @@ -326,7 +327,12 @@ module ActiveRecord end result = nil - time = Benchmark.measure { result = send(direction) } + time = nil + ActiveRecord::Base.connection_pool.with_connection do |conn| + @connection = conn + time = Benchmark.measure { result = send(direction) } + @connection = nil + end case direction when :up then announce "migrated (%.4fs)" % time.real; write @@ -367,7 +373,7 @@ module ActiveRecord end def connection - ActiveRecord::Base.connection + @connection || ActiveRecord::Base.connection end def method_missing(method, *arguments, &block) -- cgit v1.2.3 From 9280fbf795d26146fe149514a32e22612b0311ee Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 19 Nov 2010 10:13:56 -0800 Subject: instantiate the delegate object after initialize is defined so that our initialize method actually gets called --- activerecord/lib/active_record/migration.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 06a8747fde..9892c6c338 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -294,9 +294,6 @@ module ActiveRecord cattr_accessor :verbose - self.delegate = new - self.verbose = true - attr_accessor :name, :version def initialize @@ -305,6 +302,10 @@ module ActiveRecord @connection = nil end + # instantiate the delegate object after initialize is defined + self.verbose = true + self.delegate = new + def up self.class.delegate = self return unless self.class.respond_to?(:up) -- cgit v1.2.3 From 24174d1b3aa1b8ac4fec95f82f6204e2d095805d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 19 Nov 2010 10:23:13 -0800 Subject: this return value is not used, so stop returning it --- activerecord/lib/active_record/migration.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 9892c6c338..b303ff4225 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -327,11 +327,10 @@ module ActiveRecord when :down then announce "reverting" end - result = nil time = nil ActiveRecord::Base.connection_pool.with_connection do |conn| @connection = conn - time = Benchmark.measure { result = send(direction) } + time = Benchmark.measure { send(direction) } @connection = nil end @@ -339,8 +338,6 @@ module ActiveRecord when :up then announce "migrated (%.4fs)" % time.real; write when :down then announce "reverted (%.4fs)" % time.real; write end - - result end def write(text="") -- cgit v1.2.3 From 843e319f78631d056e5018a690d0e16fc4dee619 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 18 Nov 2010 14:59:25 -0800 Subject: partial implementation of the command recorder --- activerecord/lib/active_record/migration.rb | 2 + .../active_record/migration/command_recorder.rb | 48 ++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 activerecord/lib/active_record/migration/command_recorder.rb (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index b303ff4225..e7063bca11 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -284,6 +284,8 @@ module ActiveRecord # In application.rb. # class Migration + autoload :CommandRecorder, 'active_record/migration/command_recorder' + class << self attr_accessor :delegate # :nodoc: end diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb new file mode 100644 index 0000000000..25b8649350 --- /dev/null +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -0,0 +1,48 @@ +module ActiveRecord + class Migration + # ActiveRecord::Migration::CommandRecorder records commands done during + # a migration and knows how to reverse those commands. + class CommandRecorder + attr_reader :commands + + def initialize + @commands = [] + end + + # record +command+. +command+ should be a method name and arguments. + # For example: + # + # recorder.record(:method_name, [:arg1, arg2]) + def record(*command) + @commands << command + end + + # Returns a list that represents commands that are the inverse of the + # commands stored in +commands+. + def inverse + @commands.map { |name, args| send(:"invert_#{name}", args) } + end + + private + def invert_create_table(args) + [:drop_table, args] + end + + def invert_rename_table(args) + [:rename_table, args.reverse] + end + + def invert_add_column(args) + [:remove_column, args.first(2)] + end + + def invert_rename_index(args) + [:rename_index, args.reverse] + end + + def invert_rename_column(args) + [:rename_column, [args.first] + args.last(2).reverse] + end + end + end +end -- cgit v1.2.3 From 24b637a80f659eb03f8efe459f9d6aae2338e434 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 18 Nov 2010 15:05:29 -0800 Subject: inverting add_index --- activerecord/lib/active_record/migration/command_recorder.rb | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 25b8649350..032ce3aad8 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -43,6 +43,11 @@ module ActiveRecord def invert_rename_column(args) [:rename_column, [args.first] + args.last(2).reverse] end + + def invert_add_index(args) + table, columns, _ = *args + [:remove_index, [table, {:column => columns}]] + end end end end -- cgit v1.2.3 From 5d93900dc6a423fe8d7b0c6e330deeaca4b8b72c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 18 Nov 2010 15:07:25 -0800 Subject: add and remove timestamps can be inverted --- activerecord/lib/active_record/migration/command_recorder.rb | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 032ce3aad8..231e981e53 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -48,6 +48,14 @@ module ActiveRecord table, columns, _ = *args [:remove_index, [table, {:column => columns}]] end + + def invert_remove_timestamps(args) + [:add_timestamps, args] + end + + def invert_add_timestamps(args) + [:remove_timestamps, args] + end end end end -- cgit v1.2.3 From b29a24bb6f13b8af9c12b77ee0ddc1f84c79ab55 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 18 Nov 2010 15:09:25 -0800 Subject: commands are reversed --- activerecord/lib/active_record/migration/command_recorder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 231e981e53..73450c2390 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -20,7 +20,7 @@ module ActiveRecord # Returns a list that represents commands that are the inverse of the # commands stored in +commands+. def inverse - @commands.map { |name, args| send(:"invert_#{name}", args) } + @commands.reverse.map { |name, args| send(:"invert_#{name}", args) } end private -- cgit v1.2.3 From 96b50a039276b4391ddf07b0a74850ce7bad6863 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 18 Nov 2010 15:12:09 -0800 Subject: IrreversibleMigration is raised if we cannot invert the command --- activerecord/lib/active_record/migration/command_recorder.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 73450c2390..87ad603c04 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -20,7 +20,11 @@ module ActiveRecord # Returns a list that represents commands that are the inverse of the # commands stored in +commands+. def inverse - @commands.reverse.map { |name, args| send(:"invert_#{name}", args) } + @commands.reverse.map { |name, args| + method = :"invert_#{name}" + raise IrreversibleMigration unless respond_to?(method, true) + send(method, args) + } end private -- cgit v1.2.3 From 0d7410faabaafc6cd64f636b22a450faedc732ca Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 18 Nov 2010 15:15:03 -0800 Subject: updating documentation --- activerecord/lib/active_record/migration/command_recorder.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 87ad603c04..8a98a177d5 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -18,7 +18,13 @@ module ActiveRecord end # Returns a list that represents commands that are the inverse of the - # commands stored in +commands+. + # commands stored in +commands+. For example: + # + # recorder.record(:rename_table, [:old, :new]) + # recorder.inverse # => [:rename_table, [:new, :old]] + # + # This method will raise an IrreversibleMigration exception if it cannot + # invert the +commands+. def inverse @commands.reverse.map { |name, args| method = :"invert_#{name}" -- cgit v1.2.3 From 6519df4157a861c9c9d567ee57983ded0e967a10 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 19 Nov 2010 09:26:11 -0800 Subject: command recorder will record commands sent to a delegate object --- .../lib/active_record/migration/command_recorder.rb | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 8a98a177d5..fa189b8009 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -3,10 +3,11 @@ module ActiveRecord # ActiveRecord::Migration::CommandRecorder records commands done during # a migration and knows how to reverse those commands. class CommandRecorder - attr_reader :commands + attr_reader :commands, :delegate - def initialize + def initialize(delegate = nil) @commands = [] + @delegate = delegate end # record +command+. +command+ should be a method name and arguments. @@ -29,10 +30,19 @@ module ActiveRecord @commands.reverse.map { |name, args| method = :"invert_#{name}" raise IrreversibleMigration unless respond_to?(method, true) - send(method, args) + __send__(method, args) } end + def respond_to?(*args) # :nodoc: + super || delegate.respond_to?(*args) + end + + def send(method, *args) # :nodoc: + return super unless respond_to?(method) + record(method, args) + end + private def invert_create_table(args) [:drop_table, args] -- cgit v1.2.3 From 47017bd1697d6b4d6780356a403f91536eacd689 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 19 Nov 2010 10:31:03 -0800 Subject: invertable migrations are working --- activerecord/lib/active_record/migration.rb | 20 +++++++++++++++++++- .../lib/active_record/migration/command_recorder.rb | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index e7063bca11..99dd50ccb1 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -332,7 +332,25 @@ module ActiveRecord time = nil ActiveRecord::Base.connection_pool.with_connection do |conn| @connection = conn - time = Benchmark.measure { send(direction) } + if respond_to?(:change) + if direction == :down + recorder = CommandRecorder.new(@connection) + suppress_messages do + @connection = recorder + change + end + @connection = conn + time = Benchmark.measure { + recorder.inverse.each do |cmd, args| + send(cmd, *args) + end + } + else + time = Benchmark.measure { change } + end + else + time = Benchmark.measure { send(direction) } + end @connection = nil end diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index fa189b8009..fc669d2e89 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -3,7 +3,7 @@ module ActiveRecord # ActiveRecord::Migration::CommandRecorder records commands done during # a migration and knows how to reverse those commands. class CommandRecorder - attr_reader :commands, :delegate + attr_accessor :commands, :delegate def initialize(delegate = nil) @commands = [] -- cgit v1.2.3 From a4d9b1d329ef897f6b23216b01cb510db35a37b5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 19 Nov 2010 11:34:42 -0800 Subject: adding documentation for reversible migrations --- activerecord/lib/active_record/migration.rb | 32 ++++++++++++++++++++++ .../active_record/migration/command_recorder.rb | 12 +++++++- 2 files changed, 43 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 99dd50ccb1..f6321f1499 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -283,6 +283,38 @@ module ActiveRecord # # In application.rb. # + # == Reversible Migrations + # + # Starting with Rails 3.1, you will be able to define reversible migrations. + # Reversible migrations are migrations that know how to go +down+ for you. + # You simply supply the +up+ logic, and the Migration system will figure out + # how to execute the down commands for you. + # + # To define a reversible migration, define the +change+ method in your + # migration like this: + # + # class TenderloveMigration < ActiveRecord::Migration + # def change + # create_table(:horses) do + # t.column :content, :text + # t.column :remind_at, :datetime + # end + # end + # end + # + # This migration will create the horses table for you on the way up, and + # automatically figure out how to drop the table on the way down. + # + # Some commands like +remove_column+ cannot be reversed. If you care to + # define how to move up and down in these cases, you should define the +up+ + # and +down+ methods as before. + # + # If a command cannot be reversed, an + # ActiveRecord::IrreversibleMigration exception will be raised when + # the migration is moving down. + # + # For a list of commands that are reversible, please see + # ActiveRecord::Migration::CommandRecorder. class Migration autoload :CommandRecorder, 'active_record/migration/command_recorder' diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index fc669d2e89..d7e481905a 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -1,7 +1,17 @@ module ActiveRecord class Migration # ActiveRecord::Migration::CommandRecorder records commands done during - # a migration and knows how to reverse those commands. + # a migration and knows how to reverse those commands. The CommandRecorder + # knows how to invert the following commands: + # + # * add_column + # * add_index + # * add_timestamp + # * create_table + # * remove_timestamps + # * rename_column + # * rename_index + # * rename_table class CommandRecorder attr_accessor :commands, :delegate -- cgit v1.2.3 From d7db6a88734c3b666f4b85f266d223eff408b294 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Fri, 19 Nov 2010 18:29:33 +0100 Subject: class inheritable attributes is used no more! all internal use of class inheritable has been changed to class_attribute. class inheritable attributes has been deprecated. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/associations.rb | 13 +++++++------ .../associations/association_collection.rb | 2 +- .../lib/active_record/attribute_methods/dirty.rb | 3 ++- .../attribute_methods/time_zone_conversion.rb | 6 ++++-- activerecord/lib/active_record/base.rb | 20 ++++++++++++-------- activerecord/lib/active_record/named_scope.rb | 10 ++++++---- activerecord/lib/active_record/nested_attributes.rb | 13 +++++++++---- activerecord/lib/active_record/reflection.rb | 20 +++++++++----------- activerecord/lib/active_record/timestamp.rb | 8 +++++--- 9 files changed, 55 insertions(+), 40 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 08a4ebfd7e..7da38cd03f 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -4,6 +4,7 @@ require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/conversions' require 'active_support/core_ext/module/remove_method' +require 'active_support/core_ext/class/attribute' module ActiveRecord class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc: @@ -1810,12 +1811,12 @@ module ActiveRecord callbacks.each do |callback_name| full_callback_name = "#{callback_name}_for_#{association_name}" defined_callbacks = options[callback_name.to_sym] - if options.has_key?(callback_name.to_sym) - class_inheritable_reader full_callback_name.to_sym - write_inheritable_attribute(full_callback_name.to_sym, [defined_callbacks].flatten) - else - write_inheritable_attribute(full_callback_name.to_sym, []) - end + + full_callback_value = options.has_key?(callback_name.to_sym) ? [defined_callbacks].flatten : [] + + # TODO : why do i need method_defined? I think its because of the inheritance chain + class_attribute full_callback_name.to_sym unless method_defined?(full_callback_name) + self.send("#{full_callback_name}=", full_callback_value) end end diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 6090376bb8..774103342a 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -530,7 +530,7 @@ module ActiveRecord def callbacks_for(callback_name) full_callback_name = "#{callback_name}_for_#{@reflection.name}" - @owner.class.read_inheritable_attribute(full_callback_name.to_sym) || [] + @owner.class.send(full_callback_name.to_sym) || [] end def ensure_owner_is_not_new diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 439880c1fa..c19a33faa8 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/object/blank' module ActiveRecord @@ -88,7 +89,7 @@ module ActiveRecord end def clone_with_time_zone_conversion_attribute?(attr, old) - old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym) + old.class.name == "Time" && time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(attr.to_sym) end end end diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index d640b26b74..dc2785b6bf 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/class/attribute' + module ActiveRecord module AttributeMethods module TimeZoneConversion @@ -7,7 +9,7 @@ module ActiveRecord cattr_accessor :time_zone_aware_attributes, :instance_writer => false self.time_zone_aware_attributes = false - class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false + class_attribute :skip_time_zone_conversion_for_attributes, :instance_writer => false self.skip_time_zone_conversion_for_attributes = [] end @@ -54,7 +56,7 @@ module ActiveRecord private def create_time_zone_conversion_attribute?(name, column) - time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type) + time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type) end end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 314f676711..8bde1869c7 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -7,7 +7,7 @@ require 'active_support/time' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/class/delegating_attributes' -require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/hash/deep_merge' require 'active_support/core_ext/hash/indifferent_access' @@ -412,7 +412,7 @@ module ActiveRecord #:nodoc: self.store_full_sti_class = true # Stores the default scope for the class - class_inheritable_accessor :default_scoping, :instance_writer => false + class_attribute :default_scoping, :instance_writer => false self.default_scoping = [] # Returns a hash of all the attributes that have been specified for serialization as @@ -420,6 +420,9 @@ module ActiveRecord #:nodoc: class_attribute :serialized_attributes self.serialized_attributes = {} + class_attribute :_attr_readonly, :instance_writer => false + self._attr_readonly = [] + class << self # Class methods delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped delegate :find_each, :find_in_batches, :to => :scoped @@ -504,12 +507,12 @@ module ActiveRecord #:nodoc: # Attributes listed as readonly will be used to create a new record but update operations will # ignore these fields. def attr_readonly(*attributes) - write_inheritable_attribute(:attr_readonly, Set.new(attributes.map { |a| a.to_s }) + (readonly_attributes || [])) + self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || []) end # Returns an array of all the attributes that have been specified as readonly. def readonly_attributes - read_inheritable_attribute(:attr_readonly) || [] + self._attr_readonly end # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object, @@ -724,8 +727,8 @@ module ActiveRecord #:nodoc: @arel_engine = @relation = @arel_table = nil end - def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc: - descendants.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information } + def reset_column_information_for_all_subclasses#:nodoc: + descendants.each { |klass| klass.reset_column_information } end def attribute_method?(attribute) @@ -1126,7 +1129,8 @@ MSG # Article.create.published # => true def default_scope(options = {}) reset_scoped_methods - self.default_scoping << construct_finder_arel(options, default_scoping.pop) + default_scoping = self.default_scoping.dup + self.default_scoping = default_scoping << construct_finder_arel(options, default_scoping.pop) end def current_scoped_methods #:nodoc: @@ -1579,7 +1583,7 @@ MSG self.class.columns_hash[name.to_s] end - # Returns true if +comparison_object+ is the same exact object, or +comparison_object+ + # Returns true if +comparison_object+ is the same exact object, or +comparison_object+ # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+. # # Note that new records are different from any other record by definition, unless the diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 0b92ba5caa..0f421560f0 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -2,12 +2,18 @@ require 'active_support/core_ext/array' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/class/attribute' module ActiveRecord # = Active Record Named \Scopes module NamedScope extend ActiveSupport::Concern + included do + class_attribute :scopes + self.scopes = {} + end + module ClassMethods # Returns an anonymous \scope. # @@ -33,10 +39,6 @@ module ActiveRecord end end - def scopes - read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {}) - end - # Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query, # such as where(:color => :red).select('shirts.*').includes(:washing_instructions). # diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 0c3392263a..f1d3eaed38 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/hash/except' require 'active_support/core_ext/object/try' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/core_ext/class/attribute' module ActiveRecord module NestedAttributes #:nodoc: @@ -11,7 +12,7 @@ module ActiveRecord extend ActiveSupport::Concern included do - class_inheritable_accessor :nested_attributes_options, :instance_writer => false + class_attribute :nested_attributes_options, :instance_writer => false self.nested_attributes_options = {} end @@ -268,7 +269,11 @@ module ActiveRecord if reflection = reflect_on_association(association_name) reflection.options[:autosave] = true add_autosave_association_callbacks(reflection) + + nested_attributes_options = self.nested_attributes_options.dup nested_attributes_options[association_name.to_sym] = options + self.nested_attributes_options = nested_attributes_options + type = (reflection.collection? ? :collection : :one_to_one) # def pirate_attributes=(attributes) @@ -315,7 +320,7 @@ module ActiveRecord # update_only is true, and a :_destroy key set to a truthy value, # then the existing record will be marked for destruction. def assign_nested_attributes_for_one_to_one_association(association_name, attributes) - options = nested_attributes_options[association_name] + options = self.nested_attributes_options[association_name] attributes = attributes.with_indifferent_access check_existing_record = (options[:update_only] || !attributes['id'].blank?) @@ -364,7 +369,7 @@ module ActiveRecord # { :id => '2', :_destroy => true } # ]) def assign_nested_attributes_for_collection_association(association_name, attributes_collection) - options = nested_attributes_options[association_name] + options = self.nested_attributes_options[association_name] unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array) raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})" @@ -433,7 +438,7 @@ module ActiveRecord end def call_reject_if(association_name, attributes) - case callback = nested_attributes_options[association_name][:reject_if] + case callback = self.nested_attributes_options[association_name][:reject_if] when Symbol method(callback).arity == 0 ? send(callback) : send(callback, attributes) when Proc diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index a2260e9a19..a07c321960 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -1,8 +1,15 @@ +require 'active_support/core_ext/class/attribute' + module ActiveRecord # = Active Record Reflection module Reflection # :nodoc: extend ActiveSupport::Concern + included do + class_attribute :reflections + self.reflections = {} + end + # Reflection enables to interrogate Active Record classes and objects # about their associations and aggregations. This information can, # for example, be used in a form builder that takes an Active Record object @@ -20,18 +27,9 @@ module ActiveRecord when :composed_of reflection = AggregateReflection.new(macro, name, options, active_record) end - write_inheritable_hash :reflections, name => reflection - reflection - end - # Returns a hash containing all AssociationReflection objects for the current class. - # Example: - # - # Invoice.reflections - # Account.reflections - # - def reflections - read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {}) + self.reflections = self.reflections.merge(name => reflection) + reflection end # Returns an array of AggregateReflection objects for all the aggregations in the class. diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 230adf6b2b..2ecbd906bd 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/class/attribute' + module ActiveRecord # = Active Record Timestamp # @@ -29,14 +31,14 @@ module ActiveRecord extend ActiveSupport::Concern included do - class_inheritable_accessor :record_timestamps, :instance_writer => false + class_attribute :record_timestamps, :instance_writer => false self.record_timestamps = true end private def create #:nodoc: - if record_timestamps + if self.record_timestamps current_time = current_time_from_proper_timezone all_timestamp_attributes.each do |column| @@ -61,7 +63,7 @@ module ActiveRecord end def should_record_timestamps? - record_timestamps && (!partial_updates? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?) + self.record_timestamps && (!partial_updates? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?) end def timestamp_attributes_for_update_in_model -- cgit v1.2.3 From 8796f9a31a77bb2ab97ea6c2cf2036fb6e38497e Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Fri, 19 Nov 2010 18:30:48 +0100 Subject: removed an AR method which wasn't used internally, had no tests, and had no docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 4 ---- 1 file changed, 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 8bde1869c7..aee6f3a7eb 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -727,10 +727,6 @@ module ActiveRecord #:nodoc: @arel_engine = @relation = @arel_table = nil end - def reset_column_information_for_all_subclasses#:nodoc: - descendants.each { |klass| klass.reset_column_information } - end - def attribute_method?(attribute) super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, ''))) end -- cgit v1.2.3 From c8ab3992f85569e43055b9e57c67324f5d5088cd Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 17 Nov 2010 21:46:55 -0500 Subject: unscoped takes care of named_scopes too --- activerecord/lib/active_record/base.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index aee6f3a7eb..aa028c6f36 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -852,8 +852,7 @@ module ActiveRecord #:nodoc: # limit(10) # Fires "SELECT * FROM posts LIMIT 10" # } # - # It is recommended to use block form of unscoped because chaining unscoped with named_scope - # does not work. Assuming that published is a named_scope following two statements are same. + # Assuming that published is a named_scope following two statements are same. # # Post.unscoped.published # Post.published -- cgit v1.2.3 From 1c68e55ae5e90b7c072a1f6030ea3dd4becd8a07 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 17:32:15 -0500 Subject: Revert "unscoped takes care of named_scopes too" This reverts commit 126fbd7ed8a310bf940414c1b7ddab06b03d400e. --- activerecord/lib/active_record/base.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d7a7101404..f588475bbf 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -856,7 +856,8 @@ module ActiveRecord #:nodoc: # limit(10) # Fires "SELECT * FROM posts LIMIT 10" # } # - # Assuming that published is a named_scope following two statements are same. + # It is recommended to use block form of unscoped because chaining unscoped with named_scope + # does not work. Assuming that published is a named_scope following two statements are same. # # Post.unscoped.published # Post.published -- cgit v1.2.3 From 66c09372f3ef3d4ca2b99d44cf1859d585b9dcb3 Mon Sep 17 00:00:00 2001 From: Alex Rothenberg Date: Tue, 23 Nov 2010 06:10:19 +0800 Subject: Removed ids_in_list_limit in favor of in_clause_length defined in database_limits.rb --- activerecord/lib/active_record/association_preload.rb | 4 ++-- .../lib/active_record/connection_adapters/abstract_adapter.rb | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 9743b1b4a7..5eb1071ba2 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -391,9 +391,9 @@ module ActiveRecord # Some databases impose a limit on the number of ids in a list (in Oracle its 1000) # Make several smaller queries if necessary or make one query if the adapter supports it def associated_records(ids) - max_ids_in_a_list = connection.ids_in_list_limit || ids.size + in_clause_length = connection.in_clause_length || ids.size records = [] - ids.each_slice(max_ids_in_a_list) do |some_ids| + ids.each_slice(in_clause_length) do |some_ids| records += yield(some_ids) end records diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index ada1560ce2..f3fba9a3a9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -91,11 +91,6 @@ module ActiveRecord false end - # Does this adapter restrict the number of ids you can use in a list. Oracle has a limit of 1000. - def ids_in_list_limit - nil - end - # QUOTING ================================================== # Override to return the quoted table name. Defaults to column quoting. -- cgit v1.2.3 From 4d31ee1e0e2eccae0c9964214455ff75c647bd7f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 22 Nov 2010 16:14:37 -0800 Subject: removing unused variables --- .../lib/active_record/associations/through_association_scope.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index bd8e304e99..acddfda924 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -16,7 +16,7 @@ module ActiveRecord :readonly => @reflection.options[:readonly] } end - + def construct_create_scope construct_owner_attributes(@reflection) end @@ -51,7 +51,7 @@ module ActiveRecord def construct_select(custom_select = nil) distinct = "DISTINCT " if @reflection.options[:uniq] - selected = custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*" + custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*" end def construct_joins(custom_joins = nil) -- cgit v1.2.3 From 4ece7e06fd0009f024fd167107324122c564d551 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 22 Nov 2010 17:06:55 -0800 Subject: removing space error --- activerecord/lib/active_record/associations/belongs_to_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index b624951cd9..b438620c8f 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -61,7 +61,7 @@ module ActiveRecord set_inverse_instance(the_target, @owner) the_target end - + def construct_find_scope { :conditions => conditions } end -- cgit v1.2.3 From dc320d5873e54338a917ced26d849a64001edf28 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 22 Nov 2010 17:23:37 -0800 Subject: skip cloning if arguments are blank --- .../lib/active_record/relation/query_methods.rb | 30 ++++++++++++++++------ 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 9e7503a60d..0a4c119849 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -13,7 +13,7 @@ module ActiveRecord def includes(*args) args.reject! {|a| a.blank? } - return clone if args.empty? + return self if args.empty? relation = clone relation.includes_values = (relation.includes_values + args).flatten.uniq @@ -21,14 +21,18 @@ module ActiveRecord end def eager_load(*args) + return self if args.blank? + relation = clone - relation.eager_load_values += args unless args.blank? + relation.eager_load_values += args relation end def preload(*args) + return self if args.blank? + relation = clone - relation.preload_values += args unless args.blank? + relation.preload_values += args relation end @@ -43,22 +47,28 @@ module ActiveRecord end def group(*args) + return self if args.blank? + relation = clone - relation.group_values += args.flatten unless args.blank? + relation.group_values += args.flatten relation end def order(*args) + return self if args.blank? + relation = clone - relation.order_values += args.flatten unless args.blank? + relation.order_values += args.flatten relation end def joins(*args) + return self if args.blank? + relation = clone args.flatten! - relation.joins_values += args unless args.blank? + relation.joins_values += args relation end @@ -70,14 +80,18 @@ module ActiveRecord end def where(opts, *rest) + return self if opts.blank? + relation = clone - relation.where_values += build_where(opts, rest) unless opts.blank? + relation.where_values += build_where(opts, rest) relation end def having(*args) + return self if args.blank? + relation = clone - relation.having_values += build_where(*args) unless args.blank? + relation.having_values += build_where(*args) relation end -- cgit v1.2.3 From de829f871becc87424d75a5050883a02435c01e4 Mon Sep 17 00:00:00 2001 From: Ray Baxter Date: Mon, 22 Nov 2010 22:11:56 -0800 Subject: fix typo --- activerecord/lib/active_record/autosave_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index cb5bc06580..73ac8e82c6 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -89,7 +89,7 @@ module ActiveRecord # post = Post.create(:title => 'ruby rocks') # post.comments.create(:body => 'hello world') # post.comments[0].body = 'hi everyone' - # post.save # => saves both post and comment, with 'hi everyone' as title + # post.save # => saves both post and comment, with 'hi everyone' as body # # Destroying one of the associated models as part of the parent's save action # is as simple as marking it for destruction: -- cgit v1.2.3 From 14055ea28285107af04ed0ce4ef0ec542a9a9530 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 20 Nov 2010 07:38:08 +0800 Subject: No need to define a local var here. --- activerecord/lib/active_record/base.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index aa028c6f36..f9743b4fea 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1569,8 +1569,7 @@ MSG # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings). def attribute_present?(attribute) - value = read_attribute(attribute) - !value.blank? + !read_attribute(attribute).blank? end # Returns the column object for the named attribute. -- cgit v1.2.3 From a9e963d51d262a0693f0b83508c40a250421f826 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 20 Nov 2010 07:38:58 +0800 Subject: Remove confusing parenthesis. --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f9743b4fea..18d30d2540 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1595,7 +1595,7 @@ MSG # Delegates to == def eql?(comparison_object) - self == (comparison_object) + self == comparison_object end # Delegates to id in order to allow two records of the same type and id to work with something like: -- cgit v1.2.3 From 9c161599ac1e37eedd56bece4d975dde8cdaa151 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 20 Nov 2010 07:46:56 +0800 Subject: Remove uneeded local var definition. --- activerecord/lib/active_record/nested_attributes.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index f1d3eaed38..a2101a02eb 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -322,9 +322,8 @@ module ActiveRecord def assign_nested_attributes_for_one_to_one_association(association_name, attributes) options = self.nested_attributes_options[association_name] attributes = attributes.with_indifferent_access - check_existing_record = (options[:update_only] || !attributes['id'].blank?) - if check_existing_record && (record = send(association_name)) && + if (options[:update_only] || !attributes['id'].blank?) && (record = send(association_name)) && (options[:update_only] || record.id.to_s == attributes['id'].to_s) assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes) -- cgit v1.2.3 From 861cdc4c59dabdc8cca42a9f29b7cccb3cd23505 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 20 Nov 2010 07:48:49 +0800 Subject: Remove unneeded local var. --- activerecord/lib/active_record/nested_attributes.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index a2101a02eb..15f83a8579 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -446,8 +446,7 @@ module ActiveRecord end def raise_nested_attributes_record_not_found(association_name, record_id) - reflection = self.class.reflect_on_association(association_name) - raise RecordNotFound, "Couldn't find #{reflection.klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}" + raise RecordNotFound, "Couldn't find #{self.class.reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}" end end end -- cgit v1.2.3 From b8df3a9197252ec62f390ef1f8cd0e0e827c6252 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:41:39 +0800 Subject: Use params default. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index f3fba9a3a9..0282493219 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -204,8 +204,7 @@ module ActiveRecord protected - def log(sql, name) - name ||= "SQL" + def log(sql, name = "SQL") @instrumenter.instrument("sql.active_record", :sql => sql, :name => name, :connection_id => object_id) do yield -- cgit v1.2.3 From 1b531b7ee2054beadb23853d79b15d588b9ffd88 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:35:11 +0800 Subject: Remove explicit return. --- activerecord/lib/active_record/associations/has_many_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 830a82980d..a682ec52d8 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -46,7 +46,7 @@ module ActiveRecord count = [ @reflection.options[:limit], count ].min end - return count + count end def has_cached_counter? -- cgit v1.2.3 From 63c9185b62076f416c63b472a356bf9faac143db Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:38:35 +0800 Subject: Remove explicit return and avoid creating local var. --- activerecord/lib/active_record/associations/has_many_association.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index a682ec52d8..6423536fb9 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -112,8 +112,7 @@ module ActiveRecord end def we_can_set_the_inverse_on_this?(record) - inverse = @reflection.inverse_of - return !inverse.nil? + !@reflection.inverse_of.nil? end end end -- cgit v1.2.3 From 6ffe0ef55bbabf59a533402a6420942c7397e441 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:43:28 +0800 Subject: Avoid creating local var. --- activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 5ca1923d89..c2cd9e8d5e 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -40,8 +40,7 @@ module ActiveRecord if @connection.respond_to?(:encoding) @connection.encoding.to_s else - encoding = @connection.execute('PRAGMA encoding') - encoding[0]['encoding'] + @connection.execute('PRAGMA encoding')[0]['encoding'] end end -- cgit v1.2.3 From d29d793c9096a732274298cdba07c654e9128484 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:51:01 +0800 Subject: Don't create local vars. --- activerecord/lib/active_record/locking/optimistic.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index bf626301f1..9e1a33a6bf 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -110,12 +110,9 @@ module ActiveRecord return super unless locking_enabled? if persisted? - lock_col = self.class.locking_column - previous_value = send(lock_col).to_i - table = self.class.arel_table predicate = table[self.class.primary_key].eq(id) - predicate = predicate.and(table[self.class.locking_column].eq(previous_value)) + predicate = predicate.and(table[self.class.locking_column].eq(send(self.class.locking_column).to_i)) affected_rows = self.class.unscoped.where(predicate).delete_all -- cgit v1.2.3 From e2bad8a2e7e020901ddb74c3404a1c339b5a99f9 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:46:28 +0800 Subject: No need to create a variables to use them once. --- activerecord/lib/active_record/validations/uniqueness.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 3eba7510ac..853808eebf 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -14,17 +14,14 @@ module ActiveRecord def validate_each(record, attribute, value) finder_class = find_finder_class_for(record) - table = finder_class.unscoped - - table_name = record.class.quoted_table_name if value && record.class.serialized_attributes.key?(attribute.to_s) value = YAML.dump value end - sql, params = mount_sql_and_params(finder_class, table_name, attribute, value) + sql, params = mount_sql_and_params(finder_class, record.class.quoted_table_name, attribute, value) - relation = table.where(sql, *params) + relation = finder_class.unscoped.where(sql, *params) Array.wrap(options[:scope]).each do |scope_item| scope_value = record.send(scope_item) -- cgit v1.2.3 From 9f35799221c8a3c06b3f34a38525654b59598cfd Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 20 Nov 2010 13:37:54 -0300 Subject: Refactor && simplify count_records. --- activerecord/lib/active_record/associations/has_many_association.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 6423536fb9..23831e0b08 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -42,11 +42,7 @@ module ActiveRecord # documented side-effect of the method that may avoid an extra SELECT. @target ||= [] and loaded if count == 0 - if @reflection.options[:limit] - count = [ @reflection.options[:limit], count ].min - end - - count + @reflection.options[:limit] ? [@reflection.options[:limit], count].min : count end def has_cached_counter? -- cgit v1.2.3 From ca7b0a0d1a424aec0973fe22c299b8f04e309784 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 10:13:43 -0800 Subject: dup is working better --- activerecord/lib/active_record/base.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 9e15784f61..0d3df938e6 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1420,7 +1420,7 @@ MSG @attributes = coder['attributes'] @attributes_cache, @previously_changed, @changed_attributes = {}, {}, {} @readonly = @destroyed = @marked_for_destruction = false - @persisted = true + @persisted = false _run_find_callbacks _run_initialize_callbacks end @@ -1615,11 +1615,15 @@ MSG @attributes.frozen? end + def initialize_dup(other) + super + init_with 'attributes' => other.attributes + self + end + # Returns duplicated record with unfreezed attributes. def dup - obj = super - obj.instance_variable_set('@attributes', @attributes.dup) - obj + super end # Returns +true+ if the record is read only. Records loaded through joins with piggy-back -- cgit v1.2.3 From 5badf60d128cb958fa7a0e3f140517b71b88c7ac Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 10:58:19 -0800 Subject: dup keeps changes --- activerecord/lib/active_record/base.rb | 49 +++++++++++++++------------------- 1 file changed, 21 insertions(+), 28 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 0d3df938e6..be8ebd4d59 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1382,30 +1382,6 @@ MSG result end - # Cloned objects have no id assigned and are treated as new records. Note that this is a "shallow" clone - # as it copies the object's attributes only, not its associations. The extent of a "deep" clone is - # application specific and is therefore left to the application to implement according to its need. - def initialize_copy(other) - _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) - cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) - cloned_attributes.delete(self.class.primary_key) - - @attributes = cloned_attributes - - @changed_attributes = {} - attributes_from_column_definition.each do |attr, orig_value| - @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr]) - end - - clear_aggregation_cache - clear_association_cache - @attributes_cache = {} - @persisted = false - ensure_proper_type - - populate_with_current_scope_attributes - end - # Initialize an empty model object from +coder+. +coder+ must contain # the attributes necessary for initializing an empty model object. For # example: @@ -1420,7 +1396,7 @@ MSG @attributes = coder['attributes'] @attributes_cache, @previously_changed, @changed_attributes = {}, {}, {} @readonly = @destroyed = @marked_for_destruction = false - @persisted = false + @persisted = true _run_find_callbacks _run_initialize_callbacks end @@ -1615,15 +1591,32 @@ MSG @attributes.frozen? end + # Duped objects have no id assigned and are treated as new records. Note + # that this is a "shallow" clone as it copies the object's attributes + # only, not its associations. The extent of a "deep" dup is application + # specific and is therefore left to the application to implement according + # to its need. def initialize_dup(other) super - init_with 'attributes' => other.attributes + cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) + cloned_attributes.delete(self.class.primary_key) + + @attributes = cloned_attributes + @changed_attributes = other.changed_attributes.dup + @attributes_cache = {} + @persisted = false + + _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) + clear_aggregation_cache + clear_association_cache + ensure_proper_type + populate_with_current_scope_attributes self end - # Returns duplicated record with unfreezed attributes. - def dup + def initialize_clone(other) super + @persisted = other.persisted? end # Returns +true+ if the record is read only. Records loaded through joins with piggy-back -- cgit v1.2.3 From 4c1f76eaab670ffa95d185374ea91f0d2e2818c7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 10:59:58 -0800 Subject: initialize_clone can go away --- activerecord/lib/active_record/base.rb | 5 ----- 1 file changed, 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index be8ebd4d59..6f0e71cc7b 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1614,11 +1614,6 @@ MSG self end - def initialize_clone(other) - super - @persisted = other.persisted? - end - # Returns +true+ if the record is read only. Records loaded through joins with piggy-back # attributes will be marked as read only since they cannot be saved. def readonly? -- cgit v1.2.3 From 93d78b831831a1c8e324d4c5404b99e81fe77725 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 11:30:14 -0800 Subject: fixing more dup tests --- activerecord/lib/active_record/base.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 6f0e71cc7b..f9de4b7918 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1598,20 +1598,20 @@ MSG # to its need. def initialize_dup(other) super + _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) cloned_attributes.delete(self.class.primary_key) @attributes = cloned_attributes @changed_attributes = other.changed_attributes.dup - @attributes_cache = {} - @persisted = false - _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) clear_aggregation_cache clear_association_cache + @attributes_cache = {} + @persisted = false + ensure_proper_type populate_with_current_scope_attributes - self end # Returns +true+ if the record is read only. Records loaded through joins with piggy-back -- cgit v1.2.3 From 064c28d6c290cb9b6222b2348e38e713b82a89d6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 11:57:33 -0800 Subject: fixing dup regressions --- activerecord/lib/active_record/base.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f9de4b7918..7cc4f957fe 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1592,8 +1592,8 @@ MSG end # Duped objects have no id assigned and are treated as new records. Note - # that this is a "shallow" clone as it copies the object's attributes - # only, not its associations. The extent of a "deep" dup is application + # that this is a "shallow" copy as it copies the object's attributes + # only, not its associations. The extent of a "deep" copy is application # specific and is therefore left to the application to implement according # to its need. def initialize_dup(other) @@ -1602,8 +1602,12 @@ MSG cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) cloned_attributes.delete(self.class.primary_key) - @attributes = cloned_attributes - @changed_attributes = other.changed_attributes.dup + @attributes = cloned_attributes + + @changed_attributes = {} + attributes_from_column_definition.each do |attr, orig_value| + @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr]) + end clear_aggregation_cache clear_association_cache -- cgit v1.2.3 From d717cb29136b8e4f557e6f6ddf076ae3de8476fc Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 13:38:20 -0800 Subject: clone and dup are working on 1.8 --- activerecord/lib/active_record/base.rb | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7cc4f957fe..d2fa3bed35 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1591,19 +1591,28 @@ MSG @attributes.frozen? end + # Backport dup from 1.9 so that initialize_dup() gets called + unless Object.respond_to?(:initialize_dup) + def dup # :nodoc: + copy = super + copy.initialize_dup(self) + copy + end + end + # Duped objects have no id assigned and are treated as new records. Note # that this is a "shallow" copy as it copies the object's attributes # only, not its associations. The extent of a "deep" copy is application # specific and is therefore left to the application to implement according # to its need. def initialize_dup(other) - super - _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) cloned_attributes.delete(self.class.primary_key) @attributes = cloned_attributes + _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) + @changed_attributes = {} attributes_from_column_definition.each do |attr, orig_value| @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr]) @@ -1612,7 +1621,7 @@ MSG clear_aggregation_cache clear_association_cache @attributes_cache = {} - @persisted = false + @persisted = false ensure_proper_type populate_with_current_scope_attributes -- cgit v1.2.3 From d33dcba72d19beffc4a359f2fb89659f24122e9a Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 23 Nov 2010 14:58:10 -0500 Subject: Do not send id for quoting twice if the primary key is string. [#6022 state:resolved] --- activerecord/lib/active_record/relation/predicate_builder.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 32c7d08daa..7e2ce06dae 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -26,7 +26,8 @@ module ActiveRecord when Range, Arel::Relation attribute.in(value) when ActiveRecord::Base - attribute.eq(value.quoted_id) + sanitized_id = attribute.class == Arel::Attributes::String ? value.id : value.quoted_id + attribute.eq(sanitized_id) when Class # FIXME: I think we need to deprecate this behavior attribute.eq(value.name) -- cgit v1.2.3 From 8e8fb8a4291cc288b1189338f0de643c409eb028 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 14:42:55 -0800 Subject: just wrap as a sql literal --- activerecord/lib/active_record/relation/predicate_builder.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 7e2ce06dae..70d84619a1 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -26,8 +26,7 @@ module ActiveRecord when Range, Arel::Relation attribute.in(value) when ActiveRecord::Base - sanitized_id = attribute.class == Arel::Attributes::String ? value.id : value.quoted_id - attribute.eq(sanitized_id) + attribute.eq(Arel.sql(value.quoted_id)) when Class # FIXME: I think we need to deprecate this behavior attribute.eq(value.name) -- cgit v1.2.3 From 1aaa8edeb958a263c7a256344f442867c4b90c5f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 17:34:18 -0800 Subject: breaking classes up in to respective files --- activerecord/lib/active_record/associations.rb | 575 +-------------------- .../associations/class_methods/join_dependency.rb | 216 ++++++++ .../join_dependency/join_association.rb | 278 ++++++++++ .../class_methods/join_dependency/join_base.rb | 34 ++ .../class_methods/join_dependency/join_part.rb | 76 +++ 5 files changed, 605 insertions(+), 574 deletions(-) create mode 100644 activerecord/lib/active_record/associations/class_methods/join_dependency.rb create mode 100644 activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb create mode 100644 activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb create mode 100644 activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 7da38cd03f..0d9171d876 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -5,6 +5,7 @@ require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/conversions' require 'active_support/core_ext/module/remove_method' require 'active_support/core_ext/class/attribute' +require 'active_record/associations/class_methods/join_dependency' module ActiveRecord class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc: @@ -1832,580 +1833,6 @@ module ActiveRecord Array.wrap(extensions) end end - - class JoinDependency # :nodoc: - attr_reader :join_parts, :reflections, :table_aliases - - def initialize(base, associations, joins) - @join_parts = [JoinBase.new(base, joins)] - @associations = {} - @reflections = [] - @table_aliases = Hash.new(0) - @table_aliases[base.table_name] = 1 - build(associations) - end - - def graft(*associations) - associations.each do |association| - join_associations.detect {|a| association == a} || - build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type) - end - self - end - - def join_associations - join_parts.last(join_parts.length - 1) - end - - def join_base - join_parts.first - end - - def count_aliases_from_table_joins(name) - # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase - quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase - join_sql = join_base.table_joins.to_s.downcase - join_sql.blank? ? 0 : - # Table names - join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size + - # Table aliases - join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size - end - - def instantiate(rows) - primary_key = join_base.aliased_primary_key - parents = {} - - records = rows.map { |model| - primary_id = model[primary_key] - parent = parents[primary_id] ||= join_base.instantiate(model) - construct(parent, @associations, join_associations.dup, model) - parent - }.uniq - - remove_duplicate_results!(join_base.active_record, records, @associations) - records - end - - def remove_duplicate_results!(base, records, associations) - case associations - when Symbol, String - reflection = base.reflections[associations] - remove_uniq_by_reflection(reflection, records) - when Array - associations.each do |association| - remove_duplicate_results!(base, records, association) - end - when Hash - associations.keys.each do |name| - reflection = base.reflections[name] - remove_uniq_by_reflection(reflection, records) - - parent_records = [] - records.each do |record| - if descendant = record.send(reflection.name) - if reflection.collection? - parent_records.concat descendant.target.uniq - else - parent_records << descendant - end - end - end - - remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty? - end - end - end - - protected - - def cache_joined_association(association) - associations = [] - parent = association.parent - while parent != join_base - associations.unshift(parent.reflection.name) - parent = parent.parent - end - ref = @associations - associations.each do |key| - ref = ref[key] - end - ref[association.reflection.name] ||= {} - end - - def build(associations, parent = nil, join_type = Arel::InnerJoin) - parent ||= join_parts.last - case associations - when Symbol, String - reflection = parent.reflections[associations.to_s.intern] or - raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?" - unless join_association = find_join_association(reflection, parent) - @reflections << reflection - join_association = build_join_association(reflection, parent) - join_association.join_type = join_type - @join_parts << join_association - cache_joined_association(join_association) - end - join_association - when Array - associations.each do |association| - build(association, parent, join_type) - end - when Hash - associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name| - join_association = build(name, parent, join_type) - build(associations[name], join_association, join_type) - end - else - raise ConfigurationError, associations.inspect - end - end - - def find_join_association(name_or_reflection, parent) - if String === name_or_reflection - name_or_reflection = name_or_reflection.to_sym - end - - join_associations.detect { |j| - j.reflection == name_or_reflection && j.parent == parent - } - end - - def remove_uniq_by_reflection(reflection, records) - if reflection && reflection.collection? - records.each { |record| record.send(reflection.name).target.uniq! } - end - end - - def build_join_association(reflection, parent) - JoinAssociation.new(reflection, self, parent) - end - - def construct(parent, associations, join_parts, row) - case associations - when Symbol, String - name = associations.to_s - - join_part = join_parts.detect { |j| - j.reflection.name.to_s == name && - j.parent_table_name == parent.class.table_name } - - raise(ConfigurationError, "No such association") unless join_part - - join_parts.delete(join_part) - construct_association(parent, join_part, row) - when Array - associations.each do |association| - construct(parent, association, join_parts, row) - end - when Hash - associations.sort_by { |k,_| k.to_s }.each do |name, assoc| - association = construct(parent, name, join_parts, row) - construct(association, assoc, join_parts, row) if association - end - else - raise ConfigurationError, associations.inspect - end - end - - def construct_association(record, join_part, row) - return if record.id.to_s != join_part.parent.record_id(row).to_s - - macro = join_part.reflection.macro - if macro == :has_one - return if record.instance_variable_defined?("@#{join_part.reflection.name}") - association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil? - set_target_and_inverse(join_part, association, record) - else - return if row[join_part.aliased_primary_key].nil? - association = join_part.instantiate(row) - case macro - when :has_many, :has_and_belongs_to_many - collection = record.send(join_part.reflection.name) - collection.loaded - collection.target.push(association) - collection.__send__(:set_inverse_instance, association, record) - when :belongs_to - set_target_and_inverse(join_part, association, record) - else - raise ConfigurationError, "unknown macro: #{join_part.reflection.macro}" - end - end - association - end - - def set_target_and_inverse(join_part, association, record) - association_proxy = record.send("set_#{join_part.reflection.name}_target", association) - association_proxy.__send__(:set_inverse_instance, association, record) - end - - # A JoinPart represents a part of a JoinDependency. It is an abstract class, inherited - # by JoinBase and JoinAssociation. A JoinBase represents the Active Record which - # everything else is being joined onto. A JoinAssociation represents an association which - # is joining to the base. A JoinAssociation may result in more than one actual join - # operations (for example a has_and_belongs_to_many JoinAssociation would result in - # two; one for the join table and one for the target table). - class JoinPart # :nodoc: - # The Active Record class which this join part is associated 'about'; for a JoinBase - # this is the actual base model, for a JoinAssociation this is the target model of the - # association. - attr_reader :active_record - - delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record - - def initialize(active_record) - @active_record = active_record - @cached_record = {} - end - - def ==(other) - raise NotImplementedError - end - - # An Arel::Table for the active_record - def table - raise NotImplementedError - end - - # The prefix to be used when aliasing columns in the active_record's table - def aliased_prefix - raise NotImplementedError - end - - # The alias for the active_record's table - def aliased_table_name - raise NotImplementedError - end - - # The alias for the primary key of the active_record's table - def aliased_primary_key - "#{aliased_prefix}_r0" - end - - # An array of [column_name, alias] pairs for the table - def column_names_with_alias - unless defined?(@column_names_with_alias) - @column_names_with_alias = [] - - ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i| - @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"] - end - end - - @column_names_with_alias - end - - def extract_record(row) - Hash[column_names_with_alias.map{|cn, an| [cn, row[an]]}] - end - - def record_id(row) - row[aliased_primary_key] - end - - def instantiate(row) - @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row)) - end - end - - class JoinBase < JoinPart # :nodoc: - # Extra joins provided when the JoinDependency was created - attr_reader :table_joins - - def initialize(active_record, joins = nil) - super(active_record) - @table_joins = joins - end - - def ==(other) - other.class == self.class && - other.active_record == active_record - end - - def aliased_prefix - "t0" - end - - def table - Arel::Table.new(table_name, :engine => arel_engine, :columns => active_record.columns) - end - - def aliased_table_name - active_record.table_name - end - end - - class JoinAssociation < JoinPart # :nodoc: - # The reflection of the association represented - attr_reader :reflection - - # The JoinDependency object which this JoinAssociation exists within. This is mainly - # relevant for generating aliases which do not conflict with other joins which are - # part of the query. - attr_reader :join_dependency - - # A JoinBase instance representing the active record we are joining onto. - # (So in Author.has_many :posts, the Author would be that base record.) - attr_reader :parent - - # What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin - attr_accessor :join_type - - # These implement abstract methods from the superclass - attr_reader :aliased_prefix, :aliased_table_name - - delegate :options, :through_reflection, :source_reflection, :to => :reflection - delegate :table, :table_name, :to => :parent, :prefix => true - - def initialize(reflection, join_dependency, parent = nil) - reflection.check_validity! - - if reflection.options[:polymorphic] - raise EagerLoadPolymorphicError.new(reflection) - end - - super(reflection.klass) - - @reflection = reflection - @join_dependency = join_dependency - @parent = parent - @join_type = Arel::InnerJoin - - # This must be done eagerly upon initialisation because the alias which is produced - # depends on the state of the join dependency, but we want it to work the same way - # every time. - allocate_aliases - end - - def ==(other) - other.class == self.class && - other.reflection == reflection && - other.parent == parent - end - - def find_parent_in(other_join_dependency) - other_join_dependency.join_parts.detect do |join_part| - self.parent == join_part - end - end - - def join_to(relation) - send("join_#{reflection.macro}_to", relation) - end - - def join_relation(joining_relation) - self.join_type = Arel::OuterJoin - joining_relation.joins(self) - end - - def table - @table ||= Arel::Table.new( - table_name, :as => aliased_table_name, - :engine => arel_engine, :columns => active_record.columns - ) - end - - # More semantic name given we are talking about associations - alias_method :target_table, :table - - protected - - def aliased_table_name_for(name, suffix = nil) - if @join_dependency.table_aliases[name].zero? - @join_dependency.table_aliases[name] = @join_dependency.count_aliases_from_table_joins(name) - end - - if !@join_dependency.table_aliases[name].zero? # We need an alias - name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}" - @join_dependency.table_aliases[name] += 1 - if @join_dependency.table_aliases[name] == 1 # First time we've seen this name - # Also need to count the aliases from the table_aliases to avoid incorrect count - @join_dependency.table_aliases[name] += @join_dependency.count_aliases_from_table_joins(name) - end - table_index = @join_dependency.table_aliases[name] - name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index}" if table_index > 1 - else - @join_dependency.table_aliases[name] += 1 - end - - name - end - - def pluralize(table_name) - ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name - end - - def interpolate_sql(sql) - instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__) - end - - private - - def allocate_aliases - @aliased_prefix = "t#{ join_dependency.join_parts.size }" - @aliased_table_name = aliased_table_name_for(table_name) - - if reflection.macro == :has_and_belongs_to_many - @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join") - elsif [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through] - @aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join") - end - end - - def process_conditions(conditions, table_name) - Arel.sql(interpolate_sql(sanitize_sql(conditions, table_name))) - end - - def join_target_table(relation, *conditions) - relation = relation.join(target_table, join_type) - - # If the target table is an STI model then we must be sure to only include records of - # its type and its sub-types. - unless active_record.descends_from_active_record? - sti_column = target_table[active_record.inheritance_column] - - sti_condition = sti_column.eq(active_record.sti_name) - active_record.descendants.each do |subclass| - sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) - end - - conditions << sti_condition - end - - # If the reflection has conditions, add them - if options[:conditions] - conditions << process_conditions(options[:conditions], aliased_table_name) - end - - relation = relation.on(*conditions) - end - - def join_has_and_belongs_to_many_to(relation) - join_table = Arel::Table.new( - options[:join_table], :engine => arel_engine, - :as => @aliased_join_table_name - ) - - fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key - klass_fk = options[:association_foreign_key] || reflection.klass.to_s.foreign_key - - relation = relation.join(join_table, join_type) - relation = relation.on( - join_table[fk]. - eq(parent_table[reflection.active_record.primary_key]) - ) - - join_target_table( - relation, - target_table[reflection.klass.primary_key]. - eq(join_table[klass_fk]) - ) - end - - def join_has_many_to(relation) - if reflection.options[:through] - join_has_many_through_to(relation) - elsif reflection.options[:as] - join_has_many_polymorphic_to(relation) - else - foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key - primary_key = options[:primary_key] || parent.primary_key - - join_target_table( - relation, - target_table[foreign_key]. - eq(parent_table[primary_key]) - ) - end - end - alias :join_has_one_to :join_has_many_to - - def join_has_many_through_to(relation) - join_table = Arel::Table.new( - through_reflection.klass.table_name, :engine => arel_engine, - :as => @aliased_join_table_name - ) - - jt_conditions = [] - jt_foreign_key = first_key = second_key = nil - - if through_reflection.options[:as] # has_many :through against a polymorphic join - as_key = through_reflection.options[:as].to_s - jt_foreign_key = as_key + '_id' - - jt_conditions << - join_table[as_key + '_type']. - eq(parent.active_record.base_class.name) - else - jt_foreign_key = through_reflection.primary_key_name - end - - case source_reflection.macro - when :has_many - second_key = options[:foreign_key] || primary_key - - if source_reflection.options[:as] - first_key = "#{source_reflection.options[:as]}_id" - else - first_key = through_reflection.klass.base_class.to_s.foreign_key - end - - unless through_reflection.klass.descends_from_active_record? - jt_conditions << - join_table[through_reflection.active_record.inheritance_column]. - eq(through_reflection.klass.sti_name) - end - when :belongs_to - first_key = primary_key - - if reflection.options[:source_type] - second_key = source_reflection.association_foreign_key - - jt_conditions << - join_table[reflection.source_reflection.options[:foreign_type]]. - eq(reflection.options[:source_type]) - else - second_key = source_reflection.primary_key_name - end - end - - jt_conditions << - parent_table[parent.primary_key]. - eq(join_table[jt_foreign_key]) - - if through_reflection.options[:conditions] - jt_conditions << process_conditions(through_reflection.options[:conditions], aliased_table_name) - end - - relation = relation.join(join_table, join_type).on(*jt_conditions) - - join_target_table( - relation, - target_table[first_key].eq(join_table[second_key]) - ) - end - - def join_has_many_polymorphic_to(relation) - join_target_table( - relation, - target_table["#{reflection.options[:as]}_id"]. - eq(parent_table[parent.primary_key]), - target_table["#{reflection.options[:as]}_type"]. - eq(parent.active_record.base_class.name) - ) - end - - def join_belongs_to_to(relation) - foreign_key = options[:foreign_key] || reflection.primary_key_name - primary_key = options[:primary_key] || reflection.klass.primary_key - - join_target_table( - relation, - target_table[primary_key].eq(parent_table[foreign_key]) - ) - end - end - end end end end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb new file mode 100644 index 0000000000..79fdb2d4cb --- /dev/null +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -0,0 +1,216 @@ +require 'active_record/associations/class_methods/join_dependency/join_part' +require 'active_record/associations/class_methods/join_dependency/join_base' +require 'active_record/associations/class_methods/join_dependency/join_association' + +module ActiveRecord + module Associations + module ClassMethods + class JoinDependency # :nodoc: + attr_reader :join_parts, :reflections, :table_aliases + + def initialize(base, associations, joins) + @join_parts = [JoinBase.new(base, joins)] + @associations = {} + @reflections = [] + @table_aliases = Hash.new(0) + @table_aliases[base.table_name] = 1 + build(associations) + end + + def graft(*associations) + associations.each do |association| + join_associations.detect {|a| association == a} || + build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type) + end + self + end + + def join_associations + join_parts.last(join_parts.length - 1) + end + + def join_base + join_parts.first + end + + def count_aliases_from_table_joins(name) + # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase + quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase + join_sql = join_base.table_joins.to_s.downcase + join_sql.blank? ? 0 : + # Table names + join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size + + # Table aliases + join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size + end + + def instantiate(rows) + primary_key = join_base.aliased_primary_key + parents = {} + + records = rows.map { |model| + primary_id = model[primary_key] + parent = parents[primary_id] ||= join_base.instantiate(model) + construct(parent, @associations, join_associations.dup, model) + parent + }.uniq + + remove_duplicate_results!(join_base.active_record, records, @associations) + records + end + + def remove_duplicate_results!(base, records, associations) + case associations + when Symbol, String + reflection = base.reflections[associations] + remove_uniq_by_reflection(reflection, records) + when Array + associations.each do |association| + remove_duplicate_results!(base, records, association) + end + when Hash + associations.keys.each do |name| + reflection = base.reflections[name] + remove_uniq_by_reflection(reflection, records) + + parent_records = [] + records.each do |record| + if descendant = record.send(reflection.name) + if reflection.collection? + parent_records.concat descendant.target.uniq + else + parent_records << descendant + end + end + end + + remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty? + end + end + end + + protected + + def cache_joined_association(association) + associations = [] + parent = association.parent + while parent != join_base + associations.unshift(parent.reflection.name) + parent = parent.parent + end + ref = @associations + associations.each do |key| + ref = ref[key] + end + ref[association.reflection.name] ||= {} + end + + def build(associations, parent = nil, join_type = Arel::InnerJoin) + parent ||= join_parts.last + case associations + when Symbol, String + reflection = parent.reflections[associations.to_s.intern] or + raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?" + unless join_association = find_join_association(reflection, parent) + @reflections << reflection + join_association = build_join_association(reflection, parent) + join_association.join_type = join_type + @join_parts << join_association + cache_joined_association(join_association) + end + join_association + when Array + associations.each do |association| + build(association, parent, join_type) + end + when Hash + associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name| + join_association = build(name, parent, join_type) + build(associations[name], join_association, join_type) + end + else + raise ConfigurationError, associations.inspect + end + end + + def find_join_association(name_or_reflection, parent) + if String === name_or_reflection + name_or_reflection = name_or_reflection.to_sym + end + + join_associations.detect { |j| + j.reflection == name_or_reflection && j.parent == parent + } + end + + def remove_uniq_by_reflection(reflection, records) + if reflection && reflection.collection? + records.each { |record| record.send(reflection.name).target.uniq! } + end + end + + def build_join_association(reflection, parent) + JoinAssociation.new(reflection, self, parent) + end + + def construct(parent, associations, join_parts, row) + case associations + when Symbol, String + name = associations.to_s + + join_part = join_parts.detect { |j| + j.reflection.name.to_s == name && + j.parent_table_name == parent.class.table_name } + + raise(ConfigurationError, "No such association") unless join_part + + join_parts.delete(join_part) + construct_association(parent, join_part, row) + when Array + associations.each do |association| + construct(parent, association, join_parts, row) + end + when Hash + associations.sort_by { |k,_| k.to_s }.each do |name, assoc| + association = construct(parent, name, join_parts, row) + construct(association, assoc, join_parts, row) if association + end + else + raise ConfigurationError, associations.inspect + end + end + + def construct_association(record, join_part, row) + return if record.id.to_s != join_part.parent.record_id(row).to_s + + macro = join_part.reflection.macro + if macro == :has_one + return if record.instance_variable_defined?("@#{join_part.reflection.name}") + association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil? + set_target_and_inverse(join_part, association, record) + else + return if row[join_part.aliased_primary_key].nil? + association = join_part.instantiate(row) + case macro + when :has_many, :has_and_belongs_to_many + collection = record.send(join_part.reflection.name) + collection.loaded + collection.target.push(association) + collection.__send__(:set_inverse_instance, association, record) + when :belongs_to + set_target_and_inverse(join_part, association, record) + else + raise ConfigurationError, "unknown macro: #{join_part.reflection.macro}" + end + end + association + end + + def set_target_and_inverse(join_part, association, record) + association_proxy = record.send("set_#{join_part.reflection.name}_target", association) + association_proxy.__send__(:set_inverse_instance, association, record) + end + end + end + end +end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb new file mode 100644 index 0000000000..5e5c01c77a --- /dev/null +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -0,0 +1,278 @@ +module ActiveRecord + module Associations + module ClassMethods + class JoinDependency # :nodoc: + class JoinAssociation < JoinPart # :nodoc: + # The reflection of the association represented + attr_reader :reflection + + # The JoinDependency object which this JoinAssociation exists within. This is mainly + # relevant for generating aliases which do not conflict with other joins which are + # part of the query. + attr_reader :join_dependency + + # A JoinBase instance representing the active record we are joining onto. + # (So in Author.has_many :posts, the Author would be that base record.) + attr_reader :parent + + # What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin + attr_accessor :join_type + + # These implement abstract methods from the superclass + attr_reader :aliased_prefix, :aliased_table_name + + delegate :options, :through_reflection, :source_reflection, :to => :reflection + delegate :table, :table_name, :to => :parent, :prefix => true + + def initialize(reflection, join_dependency, parent = nil) + reflection.check_validity! + + if reflection.options[:polymorphic] + raise EagerLoadPolymorphicError.new(reflection) + end + + super(reflection.klass) + + @reflection = reflection + @join_dependency = join_dependency + @parent = parent + @join_type = Arel::InnerJoin + + # This must be done eagerly upon initialisation because the alias which is produced + # depends on the state of the join dependency, but we want it to work the same way + # every time. + allocate_aliases + end + + def ==(other) + other.class == self.class && + other.reflection == reflection && + other.parent == parent + end + + def find_parent_in(other_join_dependency) + other_join_dependency.join_parts.detect do |join_part| + self.parent == join_part + end + end + + def join_to(relation) + send("join_#{reflection.macro}_to", relation) + end + + def join_relation(joining_relation) + self.join_type = Arel::OuterJoin + joining_relation.joins(self) + end + + def table + @table ||= Arel::Table.new( + table_name, :as => aliased_table_name, + :engine => arel_engine, :columns => active_record.columns + ) + end + + # More semantic name given we are talking about associations + alias_method :target_table, :table + + protected + + def aliased_table_name_for(name, suffix = nil) + if @join_dependency.table_aliases[name].zero? + @join_dependency.table_aliases[name] = @join_dependency.count_aliases_from_table_joins(name) + end + + if !@join_dependency.table_aliases[name].zero? # We need an alias + name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}" + @join_dependency.table_aliases[name] += 1 + if @join_dependency.table_aliases[name] == 1 # First time we've seen this name + # Also need to count the aliases from the table_aliases to avoid incorrect count + @join_dependency.table_aliases[name] += @join_dependency.count_aliases_from_table_joins(name) + end + table_index = @join_dependency.table_aliases[name] + name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index}" if table_index > 1 + else + @join_dependency.table_aliases[name] += 1 + end + + name + end + + def pluralize(table_name) + ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name + end + + def interpolate_sql(sql) + instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__) + end + + private + + def allocate_aliases + @aliased_prefix = "t#{ join_dependency.join_parts.size }" + @aliased_table_name = aliased_table_name_for(table_name) + + if reflection.macro == :has_and_belongs_to_many + @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join") + elsif [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through] + @aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join") + end + end + + def process_conditions(conditions, table_name) + Arel.sql(interpolate_sql(sanitize_sql(conditions, table_name))) + end + + def join_target_table(relation, *conditions) + relation = relation.join(target_table, join_type) + + # If the target table is an STI model then we must be sure to only include records of + # its type and its sub-types. + unless active_record.descends_from_active_record? + sti_column = target_table[active_record.inheritance_column] + + sti_condition = sti_column.eq(active_record.sti_name) + active_record.descendants.each do |subclass| + sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) + end + + conditions << sti_condition + end + + # If the reflection has conditions, add them + if options[:conditions] + conditions << process_conditions(options[:conditions], aliased_table_name) + end + + relation = relation.on(*conditions) + end + + def join_has_and_belongs_to_many_to(relation) + join_table = Arel::Table.new( + options[:join_table], :engine => arel_engine, + :as => @aliased_join_table_name + ) + + fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key + klass_fk = options[:association_foreign_key] || reflection.klass.to_s.foreign_key + + relation = relation.join(join_table, join_type) + relation = relation.on( + join_table[fk]. + eq(parent_table[reflection.active_record.primary_key]) + ) + + join_target_table( + relation, + target_table[reflection.klass.primary_key]. + eq(join_table[klass_fk]) + ) + end + + def join_has_many_to(relation) + if reflection.options[:through] + join_has_many_through_to(relation) + elsif reflection.options[:as] + join_has_many_polymorphic_to(relation) + else + foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key + primary_key = options[:primary_key] || parent.primary_key + + join_target_table( + relation, + target_table[foreign_key]. + eq(parent_table[primary_key]) + ) + end + end + alias :join_has_one_to :join_has_many_to + + def join_has_many_through_to(relation) + join_table = Arel::Table.new( + through_reflection.klass.table_name, :engine => arel_engine, + :as => @aliased_join_table_name + ) + + jt_conditions = [] + jt_foreign_key = first_key = second_key = nil + + if through_reflection.options[:as] # has_many :through against a polymorphic join + as_key = through_reflection.options[:as].to_s + jt_foreign_key = as_key + '_id' + + jt_conditions << + join_table[as_key + '_type']. + eq(parent.active_record.base_class.name) + else + jt_foreign_key = through_reflection.primary_key_name + end + + case source_reflection.macro + when :has_many + second_key = options[:foreign_key] || primary_key + + if source_reflection.options[:as] + first_key = "#{source_reflection.options[:as]}_id" + else + first_key = through_reflection.klass.base_class.to_s.foreign_key + end + + unless through_reflection.klass.descends_from_active_record? + jt_conditions << + join_table[through_reflection.active_record.inheritance_column]. + eq(through_reflection.klass.sti_name) + end + when :belongs_to + first_key = primary_key + + if reflection.options[:source_type] + second_key = source_reflection.association_foreign_key + + jt_conditions << + join_table[reflection.source_reflection.options[:foreign_type]]. + eq(reflection.options[:source_type]) + else + second_key = source_reflection.primary_key_name + end + end + + jt_conditions << + parent_table[parent.primary_key]. + eq(join_table[jt_foreign_key]) + + if through_reflection.options[:conditions] + jt_conditions << process_conditions(through_reflection.options[:conditions], aliased_table_name) + end + + relation = relation.join(join_table, join_type).on(*jt_conditions) + + join_target_table( + relation, + target_table[first_key].eq(join_table[second_key]) + ) + end + + def join_has_many_polymorphic_to(relation) + join_target_table( + relation, + target_table["#{reflection.options[:as]}_id"]. + eq(parent_table[parent.primary_key]), + target_table["#{reflection.options[:as]}_type"]. + eq(parent.active_record.base_class.name) + ) + end + + def join_belongs_to_to(relation) + foreign_key = options[:foreign_key] || reflection.primary_key_name + primary_key = options[:primary_key] || reflection.klass.primary_key + + join_target_table( + relation, + target_table[primary_key].eq(parent_table[foreign_key]) + ) + end + end + end + end + end +end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb new file mode 100644 index 0000000000..ed05003f66 --- /dev/null +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb @@ -0,0 +1,34 @@ +module ActiveRecord + module Associations + module ClassMethods + class JoinDependency # :nodoc: + class JoinBase < JoinPart # :nodoc: + # Extra joins provided when the JoinDependency was created + attr_reader :table_joins + + def initialize(active_record, joins = nil) + super(active_record) + @table_joins = joins + end + + def ==(other) + other.class == self.class && + other.active_record == active_record + end + + def aliased_prefix + "t0" + end + + def table + Arel::Table.new(table_name, :engine => arel_engine, :columns => active_record.columns) + end + + def aliased_table_name + active_record.table_name + end + end + end + end + end +end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb new file mode 100644 index 0000000000..64d751344d --- /dev/null +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb @@ -0,0 +1,76 @@ +module ActiveRecord + module Associations + module ClassMethods + class JoinDependency # :nodoc: + # A JoinPart represents a part of a JoinDependency. It is an abstract class, inherited + # by JoinBase and JoinAssociation. A JoinBase represents the Active Record which + # everything else is being joined onto. A JoinAssociation represents an association which + # is joining to the base. A JoinAssociation may result in more than one actual join + # operations (for example a has_and_belongs_to_many JoinAssociation would result in + # two; one for the join table and one for the target table). + class JoinPart # :nodoc: + # The Active Record class which this join part is associated 'about'; for a JoinBase + # this is the actual base model, for a JoinAssociation this is the target model of the + # association. + attr_reader :active_record + + delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record + + def initialize(active_record) + @active_record = active_record + @cached_record = {} + end + + def ==(other) + raise NotImplementedError + end + + # An Arel::Table for the active_record + def table + raise NotImplementedError + end + + # The prefix to be used when aliasing columns in the active_record's table + def aliased_prefix + raise NotImplementedError + end + + # The alias for the active_record's table + def aliased_table_name + raise NotImplementedError + end + + # The alias for the primary key of the active_record's table + def aliased_primary_key + "#{aliased_prefix}_r0" + end + + # An array of [column_name, alias] pairs for the table + def column_names_with_alias + unless defined?(@column_names_with_alias) + @column_names_with_alias = [] + + ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i| + @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"] + end + end + + @column_names_with_alias + end + + def extract_record(row) + Hash[column_names_with_alias.map{|cn, an| [cn, row[an]]}] + end + + def record_id(row) + row[aliased_primary_key] + end + + def instantiate(row) + @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row)) + end + end + end + end + end +end -- cgit v1.2.3 From 0687b21de879d53157e52a2b688e34a1bd1e31f0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 17:52:51 -0800 Subject: removing ternary --- activerecord/lib/active_record/associations/has_many_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 23831e0b08..156279a67a 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -42,7 +42,7 @@ module ActiveRecord # documented side-effect of the method that may avoid an extra SELECT. @target ||= [] and loaded if count == 0 - @reflection.options[:limit] ? [@reflection.options[:limit], count].min : count + [@reflection.options[:limit], count].compact.min end def has_cached_counter? -- cgit v1.2.3 From c17cb7326d6af7c4226e955abd3f89db95fabb33 Mon Sep 17 00:00:00 2001 From: Franck Verrot Date: Wed, 24 Nov 2010 16:32:41 +0800 Subject: Dup should reset the timestamps as it is considered a new record --- activerecord/lib/active_record/base.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d2fa3bed35..49b30f4779 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1625,6 +1625,7 @@ MSG ensure_proper_type populate_with_current_scope_attributes + clear_timestamp_attributes end # Returns +true+ if the record is read only. Records loaded through joins with piggy-back @@ -1831,6 +1832,16 @@ MSG create_with.each { |att,value| self.respond_to?(:"#{att}=") && self.send("#{att}=", value) } if create_with end end + + # Clear attributes and changged_attributes + def clear_timestamp_attributes + %w(created_at created_on updated_at updated_on).each do |attribute_name| + if self.has_attribute?(attribute_name) + self[attribute_name] = nil + self.changed_attributes.delete(attribute_name) + end + end + end end Base.class_eval do -- cgit v1.2.3 From 7b77a1fc71e54f6e42e97b426380303d696051c0 Mon Sep 17 00:00:00 2001 From: Franck Verrot Date: Thu, 25 Nov 2010 02:53:28 +0800 Subject: Typo in the comments of the clear_timestamp_attributes method --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 49b30f4779..1e0fd7c6fd 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1833,7 +1833,7 @@ MSG end end - # Clear attributes and changged_attributes + # Clear attributes and changed_attributes def clear_timestamp_attributes %w(created_at created_on updated_at updated_on).each do |attribute_name| if self.has_attribute?(attribute_name) -- cgit v1.2.3 From 7f8ce38b0d294b5c5d38116f8de63f175f446bd4 Mon Sep 17 00:00:00 2001 From: Franck Verrot Date: Thu, 25 Nov 2010 02:54:59 +0800 Subject: Document the behavior of the dup method: does not preserve timestamps --- activerecord/lib/active_record/base.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 1e0fd7c6fd..b7aac7a7c9 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1605,6 +1605,7 @@ MSG # only, not its associations. The extent of a "deep" copy is application # specific and is therefore left to the application to implement according # to its need. + # The dup method does not preserve the timestamps (created|updated)_(at|on). def initialize_dup(other) cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) cloned_attributes.delete(self.class.primary_key) -- cgit v1.2.3 From 66212f69acc3d51af10ff76a18ff4c0bfa305ea5 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 11 Nov 2010 11:41:15 -0500 Subject: If a nested_attribute is being marked for destruction and at the same time an attr_accessor value is being assigned then the value being assigned is being ignored. This patch is a fix for that issue. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5939 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/nested_attributes.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 15f83a8579..050b521b6a 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -417,11 +417,8 @@ module ActiveRecord # Updates a record with the +attributes+ or marks it for destruction if # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+. def assign_to_or_mark_for_destruction(record, attributes, allow_destroy) - if has_destroy_flag?(attributes) && allow_destroy - record.mark_for_destruction - else - record.attributes = attributes.except(*UNASSIGNABLE_KEYS) - end + record.attributes = attributes.except(*UNASSIGNABLE_KEYS) + record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy end # Determines if a hash contains a truthy _destroy key. -- cgit v1.2.3 From a3ba60fd814a980cc929fcbe0f9d48fa4d0292e5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 18:08:23 -0800 Subject: reduce method calls --- activerecord/lib/active_record/associations/has_many_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 156279a67a..685d818ab3 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -108,7 +108,7 @@ module ActiveRecord end def we_can_set_the_inverse_on_this?(record) - !@reflection.inverse_of.nil? + @reflection.inverse_of end end end -- cgit v1.2.3 From d4b8d3bafa54e233d3f1ef43c43bfb460d9f2435 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 13:29:18 -0800 Subject: moving column_aliases to JoinDependency --- .../associations/class_methods/join_dependency.rb | 8 ++++++++ activerecord/lib/active_record/relation/finder_methods.rb | 11 +---------- 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 79fdb2d4cb..87541e0529 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -33,6 +33,14 @@ module ActiveRecord join_parts.first end + def columns(connection) + join_parts.collect { |join_part| + join_part.column_names_with_alias.collect{ |column_name, aliased_name| + "#{connection.quote_table_name join_part.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}" + } + }.flatten.join(", ") + end + def count_aliases_from_table_joins(name) # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 4192456447..74ec83091c 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -202,7 +202,7 @@ module ActiveRecord end def construct_relation_for_association_find(join_dependency) - relation = except(:includes, :eager_load, :preload, :select).select(column_aliases(join_dependency)) + relation = except(:includes, :eager_load, :preload, :select).select(join_dependency.columns(connection)) apply_join_dependency(relation, join_dependency) end @@ -349,17 +349,8 @@ module ActiveRecord end end - def column_aliases(join_dependency) - join_dependency.join_parts.collect { |join_part| - join_part.column_names_with_alias.collect{ |column_name, aliased_name| - "#{connection.quote_table_name join_part.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}" - } - }.flatten.join(", ") - end - def using_limitable_reflections?(reflections) reflections.none? { |r| r.collection? } end - end end -- cgit v1.2.3 From afe51afcc6b7c0245fddbd879d369818b54e460a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 13:32:01 -0800 Subject: remove useless join --- .../lib/active_record/associations/class_methods/join_dependency.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 87541e0529..7c7fa964f8 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -38,7 +38,7 @@ module ActiveRecord join_part.column_names_with_alias.collect{ |column_name, aliased_name| "#{connection.quote_table_name join_part.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}" } - }.flatten.join(", ") + }.flatten end def count_aliases_from_table_joins(name) -- cgit v1.2.3 From cdf6cf01cb47bdcbc1e011c8f5f72b161f12bf9c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 13:40:23 -0800 Subject: use ARel rather than generate SQL strings --- .../lib/active_record/associations/class_methods/join_dependency.rb | 5 +++-- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 7c7fa964f8..b1521b3706 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -33,10 +33,11 @@ module ActiveRecord join_parts.first end - def columns(connection) + def columns join_parts.collect { |join_part| + table = Arel::Nodes::TableAlias.new join_part.aliased_table_name, nil join_part.column_names_with_alias.collect{ |column_name, aliased_name| - "#{connection.quote_table_name join_part.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}" + table[column_name].as aliased_name } }.flatten end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 74ec83091c..23ae0b4325 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -202,7 +202,7 @@ module ActiveRecord end def construct_relation_for_association_find(join_dependency) - relation = except(:includes, :eager_load, :preload, :select).select(join_dependency.columns(connection)) + relation = except(:includes, :eager_load, :preload, :select).select(join_dependency.columns) apply_join_dependency(relation, join_dependency) end -- cgit v1.2.3 From 34d21b87bb237bf55214bd476be71116a2a5258f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 13:53:55 -0800 Subject: adding a factory method to the join part for generating a table alias --- .../lib/active_record/associations/class_methods/join_dependency.rb | 2 +- .../associations/class_methods/join_dependency/join_part.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index b1521b3706..19d2d55ca0 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -35,7 +35,7 @@ module ActiveRecord def columns join_parts.collect { |join_part| - table = Arel::Nodes::TableAlias.new join_part.aliased_table_name, nil + table = join_part.aliased_table join_part.column_names_with_alias.collect{ |column_name, aliased_name| table[column_name].as aliased_name } diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb index 64d751344d..5d55ca202e 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb @@ -21,6 +21,10 @@ module ActiveRecord @cached_record = {} end + def aliased_table + Arel::Nodes::TableAlias.new aliased_table_name, table + end + def ==(other) raise NotImplementedError end -- cgit v1.2.3 From 38eb01863c9281d142c6494bae485b9c215ec9b7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 14:11:12 -0800 Subject: initialize instance variables in initialize... o_O --- .../associations/class_methods/join_dependency/join_part.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb index 5d55ca202e..0b093b65e9 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb @@ -19,6 +19,7 @@ module ActiveRecord def initialize(active_record) @active_record = active_record @cached_record = {} + @column_names_with_alias = nil end def aliased_table @@ -51,14 +52,13 @@ module ActiveRecord # An array of [column_name, alias] pairs for the table def column_names_with_alias - unless defined?(@column_names_with_alias) + unless @column_names_with_alias @column_names_with_alias = [] ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i| @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"] end end - @column_names_with_alias end -- cgit v1.2.3 From ee74f2c6238dbaca00e9b667c3c239f3cae8aca5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 14:16:38 -0800 Subject: alias should be a SQL literal --- .../lib/active_record/associations/class_methods/join_dependency.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 19d2d55ca0..6ab7bd0b06 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -37,7 +37,7 @@ module ActiveRecord join_parts.collect { |join_part| table = join_part.aliased_table join_part.column_names_with_alias.collect{ |column_name, aliased_name| - table[column_name].as aliased_name + table[column_name].as Arel.sql(aliased_name) } }.flatten end -- cgit v1.2.3 From d8692985feb4db9fe8d113549535b658fe6058e5 Mon Sep 17 00:00:00 2001 From: raggi Date: Thu, 25 Nov 2010 06:21:55 +0800 Subject: Don't depend on rubygems loading thread (for Mutex) --- .../lib/active_record/connection_adapters/abstract/connection_pool.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord/lib/active_record') 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 ca9314ec99..cffa2387de 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -1,3 +1,4 @@ +require 'thread' require 'monitor' require 'set' require 'active_support/core_ext/module/synchronization' -- cgit v1.2.3 From c4d31d0f13783f3cfadbde666fd2a06996b456a2 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 25 Nov 2010 15:36:08 -0300 Subject: Reuse lock_col variable instead calling locking_column class method. Signed-off-by: Santiago Pastorino --- activerecord/lib/active_record/locking/optimistic.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 9e1a33a6bf..6503004e38 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -89,7 +89,7 @@ module ActiveRecord affected_rows = relation.where( relation.table[self.class.primary_key].eq(quoted_id).and( - relation.table[self.class.locking_column].eq(quote_value(previous_value)) + relation.table[lock_col].eq(quote_value(previous_value)) ) ).arel.update(arel_attributes_values(false, false, attribute_names)) @@ -111,8 +111,9 @@ module ActiveRecord if persisted? table = self.class.arel_table - predicate = table[self.class.primary_key].eq(id) - predicate = predicate.and(table[self.class.locking_column].eq(send(self.class.locking_column).to_i)) + lock_col = self.class.locking_column + predicate = table[self.class.primary_key].eq(id). + and(table[lock_col].eq(send(lock_col).to_i)) affected_rows = self.class.unscoped.where(predicate).delete_all -- cgit v1.2.3 From 1b6b80355c52cc97d96f6d747271ff43efebab0a Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 25 Nov 2010 16:01:41 -0300 Subject: Remove explicit return. Signed-off-by: Santiago Pastorino --- activerecord/lib/active_record/locking/optimistic.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 6503004e38..c0e1dda2bd 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -70,7 +70,7 @@ module ActiveRecord result[self.class.locking_column] ||= 0 end - return result + result end def update(attribute_names = @attributes.keys) #:nodoc: -- cgit v1.2.3 From ac6e9447e521a6ab2e3b60acd00a1ae2b863329e Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 25 Nov 2010 16:11:53 -0300 Subject: Remove return, we are already returning self. Signed-off-by: Santiago Pastorino --- .../lib/active_record/associations/association_collection.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 774103342a..8a98c0fcbe 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -235,12 +235,12 @@ module ActiveRecord # Removes all records from this association. Returns +self+ so method calls may be chained. def clear - return self if length.zero? # forces load_target if it hasn't happened already - - if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy - destroy_all - else - delete_all + unless length.zero? # forces load_target if it hasn't happened already + if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy + destroy_all + else + delete_all + end end self -- cgit v1.2.3 From c91a13f8f557cc1d21fccb5f964d50438cd60a52 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 25 Nov 2010 16:12:30 -0300 Subject: Use ternary instead explicit return. Signed-off-by: Santiago Pastorino --- activerecord/lib/active_record/associations/association_collection.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 8a98c0fcbe..e4e5ffd2b3 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -357,8 +357,7 @@ module ActiveRecord return false unless record.is_a?(@reflection.klass) return include_in_memory?(record) unless record.persisted? load_target if @reflection.options[:finder_sql] && !loaded? - return @target.include?(record) if loaded? - exists?(record) + loaded? ? @target.include?(record) : exists?(record) end def proxy_respond_to?(method, include_private = false) -- cgit v1.2.3 From a3bd62e1bae64569d0c2d45f8068f291e3e0e776 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 25 Nov 2010 16:12:57 -0300 Subject: Remove explicit return. Signed-off-by: Santiago Pastorino --- .../active_record/associations/has_and_belongs_to_many_association.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index da742fa668..2c72fd0004 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -67,7 +67,7 @@ module ActiveRecord relation.insert(attributes) end - return true + true end def delete_records(records) @@ -80,7 +80,7 @@ module ActiveRecord ).delete end end - + def construct_joins "INNER JOIN #{@owner.connection.quote_table_name @reflection.options[:join_table]} ON #{@reflection.quoted_table_name}.#{@reflection.klass.primary_key} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key}" end -- cgit v1.2.3 From 438c0188f8aa07d0e9895a2c13886901ca40ed5e Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 25 Nov 2010 20:00:40 -0200 Subject: nil check unneeded ht. exviva --- activerecord/lib/active_record/associations/association_collection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index e4e5ffd2b3..ba9373ba6a 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -236,7 +236,7 @@ module ActiveRecord # Removes all records from this association. Returns +self+ so method calls may be chained. def clear unless length.zero? # forces load_target if it hasn't happened already - if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy + if @reflection.options[:dependent] == :destroy destroy_all else delete_all -- cgit v1.2.3 From 9332cc582ebaa9b6755fa7bdfe0bc04dd51c098d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Fri, 26 Nov 2010 00:35:23 +0100 Subject: removes a couple of redundant selfs --- activerecord/lib/active_record/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b7aac7a7c9..9b09b14c87 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1837,9 +1837,9 @@ MSG # Clear attributes and changed_attributes def clear_timestamp_attributes %w(created_at created_on updated_at updated_on).each do |attribute_name| - if self.has_attribute?(attribute_name) + if has_attribute?(attribute_name) self[attribute_name] = nil - self.changed_attributes.delete(attribute_name) + changed_attributes.delete(attribute_name) end end end -- cgit v1.2.3