From 1b22071b276a2a2b3e65e6db40c6a0aa92d9ebc3 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 24 Feb 2009 11:34:32 -0600 Subject: Ensure ActiveRecord session store's connections are checked in after each request [#1927 state:resolved] --- .../connection_adapters/abstract/connection_pool.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'activerecord/lib') 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 901b17124c..aac84cc5f4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -351,5 +351,21 @@ module ActiveRecord retrieve_connection_pool klass.superclass end end + + class ConnectionManagement + def initialize(app) + @app = app + end + + def call(env) + @app.call(env) + ensure + # Don't return connection (and peform implicit rollback) if + # this request is a part of integration test + unless env.key?("rack.test") + ActiveRecord::Base.clear_active_connections! + end + end + end end end -- cgit v1.2.3 From 0dd2f96f5c90f8abacb0fe0757ef7e5db4a4d501 Mon Sep 17 00:00:00 2001 From: Diego Algorta Date: Fri, 13 Feb 2009 03:07:39 -0300 Subject: Fixed bug that makes named_scopes _forgot_ current scope --- activerecord/lib/active_record/named_scope.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 989b2a1ec5..022fdaf9b2 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -100,7 +100,7 @@ module ActiveRecord end class Scope - attr_reader :proxy_scope, :proxy_options + attr_reader :proxy_scope, :proxy_options, :current_scoped_methods_when_defined NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?).to_set [].methods.each do |m| unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s) @@ -113,6 +113,9 @@ module ActiveRecord def initialize(proxy_scope, options, &block) [options[:extend]].flatten.each { |extension| extend extension } if options[:extend] extend Module.new(&block) if block_given? + unless Scope === proxy_scope + @current_scoped_methods_when_defined = proxy_scope.send(:current_scoped_methods) + end @proxy_scope, @proxy_options = proxy_scope, options.except(:extend) end @@ -168,7 +171,13 @@ module ActiveRecord else with_scope :find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {} do method = :new if method == :build - proxy_scope.send(method, *args, &block) + if current_scoped_methods_when_defined + with_scope current_scoped_methods_when_defined do + proxy_scope.send(method, *args, &block) + end + else + proxy_scope.send(method, *args, &block) + end end end end -- cgit v1.2.3 From 5cda000bf0f6d85d1a1efedf9fa4d0b6eaf988a1 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 27 Feb 2009 13:50:24 +0100 Subject: Fixed that autosave should validate associations even if master is invalid [#1930 status:committed] --- activerecord/lib/active_record/associations.rb | 120 +---------- .../has_and_belongs_to_many_association.rb | 4 +- .../associations/has_many_association.rb | 4 +- .../associations/has_many_through_association.rb | 4 +- .../lib/active_record/autosave_association.rb | 237 ++++++++++++++++----- 5 files changed, 187 insertions(+), 182 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index e2dc883b1b..b6f87a0486 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -786,11 +786,7 @@ module ActiveRecord # 'ORDER BY p.first_name' def has_many(association_id, options = {}, &extension) reflection = create_has_many_reflection(association_id, options, &extension) - configure_dependency_for_has_many(reflection) - - add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false - add_multiple_associated_save_callbacks(reflection.name) add_association_callbacks(reflection.name, reflection.options) if options[:through] @@ -872,10 +868,10 @@ module ActiveRecord # [:source] # Specifies the source association name used by has_one :through queries. Only use it if the name cannot be # inferred from the association. has_one :favorite, :through => :favorites will look for a - # :favorite on Favorite, unless a :source is given. + # :favorite on Favorite, unless a :source is given. # [:source_type] # Specifies type of the source association used by has_one :through queries where the source - # association is a polymorphic +belongs_to+. + # association is a polymorphic +belongs_to+. # [:readonly] # If true, the associated object is readonly through the association. # [:validate] @@ -898,22 +894,9 @@ module ActiveRecord association_accessor_methods(reflection, ActiveRecord::Associations::HasOneThroughAssociation) else reflection = create_has_one_reflection(association_id, options) - - method_name = "has_one_after_save_for_#{reflection.name}".to_sym - define_method(method_name) do - association = association_instance_get(reflection.name) - if association && (new_record? || association.new_record? || association[reflection.primary_key_name] != id) - association[reflection.primary_key_name] = id - association.save(true) - end - end - after_save method_name - - add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true association_accessor_methods(reflection, HasOneAssociation) association_constructor_method(:build, reflection, HasOneAssociation) association_constructor_method(:create, reflection, HasOneAssociation) - configure_dependency_for_has_one(reflection) end end @@ -1006,40 +989,10 @@ module ActiveRecord if reflection.options[:polymorphic] association_accessor_methods(reflection, BelongsToPolymorphicAssociation) - - method_name = "polymorphic_belongs_to_before_save_for_#{reflection.name}".to_sym - define_method(method_name) do - association = association_instance_get(reflection.name) - if association && association.target - if association.new_record? - association.save(true) - end - - if association.updated? - self[reflection.primary_key_name] = association.id - self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s - end - end - end - before_save method_name else association_accessor_methods(reflection, BelongsToAssociation) association_constructor_method(:build, reflection, BelongsToAssociation) association_constructor_method(:create, reflection, BelongsToAssociation) - - method_name = "belongs_to_before_save_for_#{reflection.name}".to_sym - define_method(method_name) do - if association = association_instance_get(reflection.name) - if association.new_record? - association.save(true) - end - - if association.updated? - self[reflection.primary_key_name] = association.id - end - end - end - before_save method_name end # Create the callbacks to update counter cache @@ -1067,8 +1020,6 @@ module ActiveRecord ) end - add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true - configure_dependency_for_belongs_to(reflection) end @@ -1234,9 +1185,6 @@ module ActiveRecord # 'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}' def has_and_belongs_to_many(association_id, options = {}, &extension) reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension) - - add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false - add_multiple_associated_save_callbacks(reflection.name) collection_accessor_methods(reflection, HasAndBelongsToManyAssociation) # Don't use a before_destroy callback since users' before_destroy @@ -1358,70 +1306,6 @@ module ActiveRecord end end - def add_single_associated_validation_callbacks(association_name) - method_name = "validate_associated_records_for_#{association_name}".to_sym - define_method(method_name) do - if association = association_instance_get(association_name) - errors.add association_name unless association.target.nil? || association.valid? - end - end - - validate method_name - end - - def add_multiple_associated_validation_callbacks(association_name) - method_name = "validate_associated_records_for_#{association_name}".to_sym - define_method(method_name) do - association = association_instance_get(association_name) - - if association - if new_record? - association - elsif association.loaded? - association.select { |record| record.new_record? } - else - association.target.select { |record| record.new_record? } - end.each do |record| - errors.add association_name unless record.valid? - end - end - end - - validate method_name - end - - def add_multiple_associated_save_callbacks(association_name) - method_name = "before_save_associated_records_for_#{association_name}".to_sym - define_method(method_name) do - @new_record_before_save = new_record? - true - end - before_save method_name - - method_name = "after_create_or_update_associated_records_for_#{association_name}".to_sym - define_method(method_name) do - association = association_instance_get(association_name) - - records_to_save = if @new_record_before_save - association - elsif association && association.loaded? - association.select { |record| record.new_record? } - elsif association && !association.loaded? - association.target.select { |record| record.new_record? } - else - [] - end - records_to_save.each { |record| association.send(:insert_record, record) } unless records_to_save.blank? - - # reconstruct the SQL queries now that we know the owner's id - association.send(:construct_sql) if association.respond_to?(:construct_sql) - end - - # Doesn't use after_save as that would save associations added in after_create/after_update twice - after_create method_name - after_update method_name - end - def association_constructor_method(constructor, reflection, association_proxy_class) define_method("#{constructor}_#{reflection.name}") do |*params| attributees = params.first unless params.empty? 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 a5cc3bf091..af9ce3dfb2 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 @@ -28,12 +28,12 @@ module ActiveRecord load_target.size end - def insert_record(record, force=true) + def insert_record(record, force = true, validate = true) if record.new_record? if force record.save! else - return false unless record.save + return false unless record.save(validate) end end diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 3348079e9d..a2cbabfe0c 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -56,9 +56,9 @@ module ActiveRecord "#{@reflection.name}_count" end - def insert_record(record) + def insert_record(record, force = false, validate = true) set_belongs_to_association_for(record) - record.save + force ? record.save! : record.save(validate) end # Deletes the records according to the :dependent option. diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 2eeeb28d1f..d5d188ac2a 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -47,12 +47,12 @@ module ActiveRecord options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil? end - def insert_record(record, force=true) + def insert_record(record, force = true, validate = true) if record.new_record? if force record.save! else - return false unless record.save + return false unless record.save(validate) end end through_reflection = @reflection.through_reflection diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 680b41518c..6e09b13150 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -125,79 +125,63 @@ module ActiveRecord # post.author.name = '' # post.save(false) # => true module AutosaveAssociation + ASSOCIATION_TYPES = %w{ has_one belongs_to has_many has_and_belongs_to_many } + def self.included(base) base.class_eval do + base.extend(ClassMethods) alias_method_chain :reload, :autosave_associations - alias_method_chain :save, :autosave_associations - alias_method_chain :save!, :autosave_associations - alias_method_chain :valid?, :autosave_associations - %w{ has_one belongs_to has_many has_and_belongs_to_many }.each do |type| + ASSOCIATION_TYPES.each do |type| base.send("valid_keys_for_#{type}_association") << :autosave end end end - # Saves the parent, self, and any loaded autosave associations. - # In addition, it destroys all children that were marked for destruction - # with mark_for_destruction. - # - # This all happens inside a transaction, _if_ the Transactions module is included into - # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. - def save_with_autosave_associations(perform_validation = true) - returning(save_without_autosave_associations(perform_validation)) do |valid| - if valid - self.class.reflect_on_all_autosave_associations.each do |reflection| - if (association = association_instance_get(reflection.name)) && association.loaded? - if association.is_a?(Array) - association.proxy_target.each do |child| - child.marked_for_destruction? ? child.destroy : child.save(perform_validation) - end - else - association.marked_for_destruction? ? association.destroy : association.save(perform_validation) - end - end + module ClassMethods + private + + # def belongs_to(name, options = {}) + # super + # add_autosave_association_callbacks(reflect_on_association(name)) + # end + ASSOCIATION_TYPES.each do |type| + module_eval %{ + def #{type}(name, options = {}) + super + add_autosave_association_callbacks(reflect_on_association(name)) end - end + } end - end - # Attempts to save the record just like save_with_autosave_associations but - # will raise a RecordInvalid exception instead of returning false if the - # record is not valid. - def save_with_autosave_associations! - if valid_with_autosave_associations? - save_with_autosave_associations(false) || raise(RecordNotSaved) - else - raise RecordInvalid.new(self) - end - end + # Adds a validate and save callback for the association as specified by + # the +reflection+. + def add_autosave_association_callbacks(reflection) + save_method = "autosave_associated_records_for_#{reflection.name}" + validation_method = "validate_associated_records_for_#{reflection.name}" + validate validation_method - # Returns whether or not the parent, self, and any loaded autosave associations are valid. - def valid_with_autosave_associations? - if valid_without_autosave_associations? - self.class.reflect_on_all_autosave_associations.all? do |reflection| - if (association = association_instance_get(reflection.name)) && association.loaded? - if association.is_a?(Array) - association.proxy_target.all? { |child| autosave_association_valid?(reflection, child) } - else - autosave_association_valid?(reflection, association) - end - else - true # association not loaded yet, so it should be valid + case reflection.macro + when :has_many, :has_and_belongs_to_many + before_save :before_save_collection_association + + define_method(save_method) { save_collection_association(reflection) } + # Doesn't use after_save as that would save associations added in after_create/after_update twice + after_create save_method + after_update save_method + + define_method(validation_method) { validate_collection_association(reflection) } + else + case reflection.macro + when :has_one + define_method(save_method) { save_has_one_association(reflection) } + after_save save_method + when :belongs_to + define_method(save_method) { save_belongs_to_association(reflection) } + before_save save_method end + define_method(validation_method) { validate_single_association(reflection) } end - else - false # self was not valid - end - end - - # Returns whether or not the association is valid and applies any errors to the parent, self, if it wasn't. - def autosave_association_valid?(reflection, association) - returning(association.valid?) do |valid| - association.errors.each do |attribute, message| - errors.add "#{reflection.name}_#{attribute}", message - end unless valid end end @@ -221,5 +205,142 @@ module ActiveRecord def marked_for_destruction? @marked_for_destruction end + + private + + # Returns the record for an association collection that should be validated + # or saved. If +autosave+ is +false+ only new records will be returned, + # unless the parent is/was a new record itself. + def associated_records_to_validate_or_save(association, new_record, autosave) + if new_record + association + elsif association.loaded? + autosave ? association : association.select { |record| record.new_record? } + else + autosave ? association.target : association.target.select { |record| record.new_record? } + end + end + + # Validate the association if :validate or :autosave is + # turned on for the association specified by +reflection+. + def validate_single_association(reflection) + if reflection.options[:validate] == true || reflection.options[:autosave] == true + if (association = association_instance_get(reflection.name)) && !association.target.nil? + association_valid?(reflection, association) + end + end + end + + # Validate the associated records if :validate or + # :autosave is turned on for the association specified by + # +reflection+. + def validate_collection_association(reflection) + if reflection.options[:validate] != false && association = association_instance_get(reflection.name) + if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave]) + records.each { |record| association_valid?(reflection, record) } + end + end + end + + # Returns whether or not the association is valid and applies any errors to + # the parent, self, if it wasn't. + def association_valid?(reflection, association) + unless valid = association.valid? + if reflection.options[:autosave] + association.errors.each do |attribute, message| + attribute = "#{reflection.name}_#{attribute}" + errors.add(attribute, message) unless errors.on(attribute) + end + else + errors.add(reflection.name) + end + end + valid + end + + # Is used as a before_save callback to check while saving a collection + # association whether or not the parent was a new record before saving. + def before_save_collection_association + @new_record_before_save = new_record? + true + end + + # Saves any new associated records, or all loaded autosave associations if + # :autosave is enabled on the association. + # + # In addition, it destroys all children that were marked for destruction + # with mark_for_destruction. + # + # This all happens inside a transaction, _if_ the Transactions module is included into + # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. + def save_collection_association(reflection) + if association = association_instance_get(reflection.name) + autosave = reflection.options[:autosave] + + if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave) + records.each do |record| + if autosave && record.marked_for_destruction? + record.destroy + elsif @new_record_before_save || record.new_record? + if autosave + association.send(:insert_record, record, false, false) + else + association.send(:insert_record, record) + end + elsif autosave + record.save(false) + end + end + end + + # reconstruct the SQL queries now that we know the owner's id + association.send(:construct_sql) if association.respond_to?(:construct_sql) + end + end + + # Saves the associated record if it's new or :autosave is enabled + # on the association. + # + # In addition, it will destroy the association if it was marked for + # destruction with mark_for_destruction. + # + # This all happens inside a transaction, _if_ the Transactions module is included into + # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. + def save_has_one_association(reflection) + if association = association_instance_get(reflection.name) + if reflection.options[:autosave] && association.marked_for_destruction? + association.destroy + elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || reflection.options[:autosave] + association[reflection.primary_key_name] = id + association.save(false) + end + end + end + + # Saves the associated record if it's new or :autosave is enabled + # on the association. + # + # In addition, it will destroy the association if it was marked for + # destruction with mark_for_destruction. + # + # This all happens inside a transaction, _if_ the Transactions module is included into + # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. + def save_belongs_to_association(reflection) + if association = association_instance_get(reflection.name) + if reflection.options[:autosave] && association.marked_for_destruction? + association.destroy + else + association.save(false) if association.new_record? || reflection.options[:autosave] + + if association.updated? + self[reflection.primary_key_name] = association.id + # TODO: Removing this code doesn't seem to matter… + if reflection.options[:polymorphic] + self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s + end + end + end + end + end end end \ No newline at end of file -- cgit v1.2.3 From e2b925fa68203607fb4f9a9aa457c0435c969f9a Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Thu, 26 Feb 2009 11:56:27 +0100 Subject: Don't validate records of an :autosave association if they're marked for destruction. [#2064 status:resolved] Signed-off-by: David Heinemeier Hansson --- activerecord/lib/active_record/autosave_association.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 6e09b13150..1c3d0567c1 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -243,13 +243,16 @@ module ActiveRecord end # Returns whether or not the association is valid and applies any errors to - # the parent, self, if it wasn't. + # the parent, self, if it wasn't. Skips any :autosave + # enabled records if they're marked_for_destruction?. def association_valid?(reflection, association) unless valid = association.valid? if reflection.options[:autosave] - association.errors.each do |attribute, message| - attribute = "#{reflection.name}_#{attribute}" - errors.add(attribute, message) unless errors.on(attribute) + unless association.marked_for_destruction? + association.errors.each do |attribute, message| + attribute = "#{reflection.name}_#{attribute}" + errors.add(attribute, message) unless errors.on(attribute) + end end else errors.add(reflection.name) -- cgit v1.2.3 From 6de83562f91028629bd24447aa521bc72ef8277a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 27 Feb 2009 14:22:39 +0100 Subject: Force all internal calls to Array#to_sentence to use English [#2010 state:resolved] --- activerecord/lib/active_record/associations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index b6f87a0486..6e88c89737 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -22,7 +22,7 @@ module ActiveRecord through_reflection = reflection.through_reflection source_reflection_names = reflection.source_reflection_names source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect } - super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence :two_words_connector => ' or ', :last_word_connector => ', or '} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => '. Is it one of #{source_associations.to_sentence :two_words_connector => ' or ', :last_word_connector => ', or '}?") + super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => '. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?") end end -- cgit v1.2.3 From ff894b55dd787af1d6284567f2a898090aae64ca Mon Sep 17 00:00:00 2001 From: Zack Hobson Date: Fri, 13 Feb 2009 11:23:24 -0800 Subject: Update rdoc: all is not a named_scope [#1959 state:committed] Signed-off-by: David Heinemeier Hansson --- activerecord/lib/active_record/named_scope.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 022fdaf9b2..8a7449f95b 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -1,11 +1,12 @@ module ActiveRecord module NamedScope - # All subclasses of ActiveRecord::Base have two named \scopes: - # * all - which is similar to a find(:all) query, and + # All subclasses of ActiveRecord::Base have one named scope: # * scoped - which allows for the creation of anonymous \scopes, on the fly: Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions) # # These anonymous \scopes tend to be useful when procedurally generating complex queries, where passing # intermediate values (scopes) around as first-class objects is convenient. + # + # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope. def self.included(base) base.class_eval do extend ClassMethods -- cgit v1.2.3 From 77b0994c7835610982d708ce7ce5cd95e6e99e5a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 27 Feb 2009 14:46:23 +0100 Subject: Prep for RC2 later today --- activerecord/lib/active_record/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index 6ac4bdc905..809e91897e 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,7 +2,7 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 0 + TINY = 1 STRING = [MAJOR, MINOR, TINY].join('.') end -- cgit v1.2.3 From c653f700d3afb1208b15fb7fec6250bf3a3f0321 Mon Sep 17 00:00:00 2001 From: Mike Gunderloy Date: Sat, 28 Feb 2009 11:01:13 -0600 Subject: Fix that scoped find with :group and :having [#2006 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 55ab1facf2..206b4efa38 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1759,7 +1759,7 @@ module ActiveRecord #:nodoc: scope = scope(:find) if :auto == scope if scope && (scoped_group = scope[:group]) sql << " GROUP BY #{scoped_group}" - sql << " HAVING #{scoped_having}" if (scoped_having = scope[:having]) + sql << " HAVING #{scope[:having]}" if scope[:having] end end end -- cgit v1.2.3 From 0db6c3f51828e1a37e2c7b9245ffa8c12ac59c83 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 28 Feb 2009 19:44:25 +0000 Subject: Merge docrails and update the release notes --- .../lib/active_record/serializers/json_serializer.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/serializers/json_serializer.rb b/activerecord/lib/active_record/serializers/json_serializer.rb index 419b45d475..1fd65ed006 100644 --- a/activerecord/lib/active_record/serializers/json_serializer.rb +++ b/activerecord/lib/active_record/serializers/json_serializer.rb @@ -8,6 +8,25 @@ module ActiveRecord #:nodoc: # Returns a JSON string representing the model. Some configuration is # available through +options+. # + # The option ActiveRecord::Base.include_root_in_json controls the + # top-level behavior of to_json. In a new Rails application, it is set to + # true in initializers/new_rails_defaults.rb. When it is true, + # to_json will emit a single root node named after the object's type. For example: + # + # konata = User.find(1) + # ActiveRecord::Base.include_root_in_json = true + # konata.to_json + # # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16, + # "created_at": "2006/08/01", "awesome": true} } + # + # ActiveRecord::Base.include_root_in_json = false + # konata.to_json + # # => {"id": 1, "name": "Konata Izumi", "age": 16, + # "created_at": "2006/08/01", "awesome": true} + # + # The remainder of the examples in this section assume include_root_in_json is set to + # false. + # # Without any +options+, the returned JSON string will include all # the model's attributes. For example: # -- cgit v1.2.3 From 38136f86dc5504bde94dc7399d4a854023d7481f Mon Sep 17 00:00:00 2001 From: Jason King Date: Thu, 26 Feb 2009 16:51:38 +1100 Subject: DDL transactions and savepoints for sqlite Sqlite has had DDL transactions since 2.0.0[1] and savepoints since 3.6.8[2]. This patch updates the connection_adapters. [1] http://tinyurl.com/sqlite-v2-0-0 [2] http://tinyurl.com/sqlite-v3-6-8 Signed-off-by: Michael Koziarski [#2080 state:committed] --- .../connection_adapters/sqlite_adapter.rb | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 5390f49f04..8373163e56 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -72,10 +72,29 @@ module ActiveRecord # # * :database - Path to the database file. class SQLiteAdapter < AbstractAdapter + + class Version + include Comparable + def initialize(vs) + @v = vs.split('.').map(&:to_i) + end + def <=>(rhs) + @v <=> rhs.split('.').map(&:to_i) + end + end + def adapter_name #:nodoc: 'SQLite' end + def supports_ddl_transactions? + sqlite_version >= '2.0.0' + end + + def supports_savepoints? + sqlite_version >= '3.6.8' + end + def supports_migrations? #:nodoc: true end @@ -380,7 +399,7 @@ module ActiveRecord end def sqlite_version - @sqlite_version ||= select_value('select sqlite_version(*)') + @sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)')) end def default_primary_key_type -- cgit v1.2.3 From 818556ec4f237b19f28fdecdfe6037718cceba37 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Mon, 2 Mar 2009 19:07:39 +1300 Subject: Revert "DDL transactions and savepoints for sqlite" This reverts commit 38136f86dc5504bde94dc7399d4a854023d7481f. Caused several test failures on the ci box: http://ci.rubyonrails.org/builds/rails/38136f [#2080 state:open] --- .../connection_adapters/sqlite_adapter.rb | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 8373163e56..5390f49f04 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -72,29 +72,10 @@ module ActiveRecord # # * :database - Path to the database file. class SQLiteAdapter < AbstractAdapter - - class Version - include Comparable - def initialize(vs) - @v = vs.split('.').map(&:to_i) - end - def <=>(rhs) - @v <=> rhs.split('.').map(&:to_i) - end - end - def adapter_name #:nodoc: 'SQLite' end - def supports_ddl_transactions? - sqlite_version >= '2.0.0' - end - - def supports_savepoints? - sqlite_version >= '3.6.8' - end - def supports_migrations? #:nodoc: true end @@ -399,7 +380,7 @@ module ActiveRecord end def sqlite_version - @sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)')) + @sqlite_version ||= select_value('select sqlite_version(*)') end def default_primary_key_type -- cgit v1.2.3 From 9b1b88f09cf1498f04e1cd469d0d5ffccf2b93cc Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 3 Mar 2009 13:07:03 -0600 Subject: Fixed reset_session for ActiveRecord session store [#2108 state:resolved] --- activerecord/lib/active_record/session_store.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index de199d30bf..3cc4640f42 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -287,8 +287,7 @@ module ActiveRecord def get_session(env, sid) Base.silence do sid ||= generate_sid - session = @@session_class.find_by_session_id(sid) - session ||= @@session_class.new(:session_id => sid, :data => {}) + session = find_session(sid) env[SESSION_RECORD_KEY] = session [sid, session.data] end @@ -296,7 +295,7 @@ module ActiveRecord def set_session(env, sid, session_data) Base.silence do - record = env[SESSION_RECORD_KEY] + record = env[SESSION_RECORD_KEY] ||= find_session(sid) record.data = session_data return false unless record.save @@ -310,5 +309,10 @@ module ActiveRecord return true end + + def find_session(id) + @@session_class.find_by_session_id(id) || + @@session_class.new(:session_id => id, :data => {}) + end end end -- cgit v1.2.3 From 6a13376525f34a00e013fc3a6022838329dfe856 Mon Sep 17 00:00:00 2001 From: jvoorhis Date: Fri, 6 Mar 2009 16:47:50 +0000 Subject: Methods invoked within named scope Procs should respect the scope stack. [#1267 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/named_scope.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 8a7449f95b..43411dfb55 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -89,7 +89,12 @@ module ActiveRecord when Hash options when Proc - options.call(*args) + case parent_scope + when Scope + with_scope(:find => parent_scope.proxy_options) { options.call(*args) } + else + options.call(*args) + end end, &block) end (class << self; self end).instance_eval do -- cgit v1.2.3 From c896d56c6e5520346c260477f7d7f8bf951e72cc Mon Sep 17 00:00:00 2001 From: Tom Lea Date: Fri, 6 Mar 2009 18:26:34 +0000 Subject: Ensure self referential HABTM associations raise an exception if association_foreign_key is missing. [#1252 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 6e88c89737..80b3e23026 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -51,6 +51,12 @@ module ActiveRecord end end + class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc: + def initialize(reflection) + super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.") + end + end + class EagerLoadPolymorphicError < ActiveRecordError #:nodoc: def initialize(reflection) super("Can not eagerly load the polymorphic association #{reflection.name.inspect}") @@ -1526,6 +1532,10 @@ module ActiveRecord options[:extend] = create_extension_modules(association_id, extension, options[:extend]) reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self) + + if reflection.association_foreign_key == reflection.primary_key_name + raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection) + end reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name)) -- cgit v1.2.3 From 4863634a157a8e4a0033734617253b42325daf34 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 6 Mar 2009 18:36:30 +0000 Subject: Ensure supplie :from has precedence over scoped :from [#1370 state:resolved] --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 206b4efa38..ea791b5bfe 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1690,7 +1690,7 @@ module ActiveRecord #:nodoc: def construct_finder_sql(options) scope = scope(:find) sql = "SELECT #{options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))} " - sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} " + sql << "FROM #{options[:from] || (scope && scope[:from]) || quoted_table_name} " add_joins!(sql, options[:joins], scope) add_conditions!(sql, options[:conditions], scope) -- cgit v1.2.3 From 984bc7a614852944808739fae09a654b6e62872e Mon Sep 17 00:00:00 2001 From: Dimitri Krassovski Date: Fri, 6 Mar 2009 19:11:13 +0000 Subject: Ensure replacing has_one associations respects the supplied :dependent option. [#1305 state:resolved] Signed-off-by: Pratik Naik --- .../lib/active_record/associations/has_one_association.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 960323004d..b92cbbdeab 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -29,8 +29,17 @@ module ActiveRecord unless @target.nil? || @target == obj if dependent? && !dont_save - @target.destroy unless @target.new_record? - @owner.clear_association_cache + case @reflection.options[:dependent] + when :delete + @target.delete unless @target.new_record? + @owner.clear_association_cache + when :destroy + @target.destroy unless @target.new_record? + @owner.clear_association_cache + when :nullify + @target[@reflection.primary_key_name] = nil + @target.save unless @owner.new_record? || @target.new_record? + end else @target[@reflection.primary_key_name] = nil @target.save unless @owner.new_record? || @target.new_record? -- cgit v1.2.3 From 3ca5a0f9fd7b7921bca970859da8637011b22dd1 Mon Sep 17 00:00:00 2001 From: Adam Cooper Date: Wed, 31 Dec 2008 01:43:13 -0800 Subject: Ensure belongs_to association with a counter cache in name spaced model works [#1678 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 4 +--- activerecord/lib/active_record/reflection.rb | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 80b3e23026..65a1319c7e 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1003,9 +1003,7 @@ module ActiveRecord # Create the callbacks to update counter cache if options[:counter_cache] - cache_column = options[:counter_cache] == true ? - "#{self.to_s.demodulize.underscore.pluralize}_count" : - options[:counter_cache] + cache_column = reflection.counter_cache_column method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym define_method(method_name) do diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index e69bfb1355..2d4c1d5507 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -197,7 +197,7 @@ module ActiveRecord def counter_cache_column if options[:counter_cache] == true - "#{active_record.name.underscore.pluralize}_count" + "#{active_record.name.demodulize.underscore.pluralize}_count" elsif options[:counter_cache] options[:counter_cache] end -- cgit v1.2.3 From 7fb7b48a1f771e50896a82d749a70922a18afae7 Mon Sep 17 00:00:00 2001 From: Will Bryant Date: Fri, 6 Mar 2009 22:29:36 +0000 Subject: Allow :having conditions to be sanitized like regular :condition. [#2158 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/base.rb | 4 ++-- activerecord/lib/active_record/calculations.rb | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index ea791b5bfe..60a1221e66 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1754,12 +1754,12 @@ module ActiveRecord #:nodoc: def add_group!(sql, group, having, scope = :auto) if group sql << " GROUP BY #{group}" - sql << " HAVING #{having}" if having + sql << " HAVING #{sanitize_sql_for_conditions(having)}" if having else scope = scope(:find) if :auto == scope if scope && (scoped_group = scope[:group]) sql << " GROUP BY #{scoped_group}" - sql << " HAVING #{scope[:having]}" if scope[:having] + sql << " HAVING #{sanitize_sql_for_conditions(scope[:having])}" if scope[:having] end end end diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index b239c03284..7af97d7296 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -214,13 +214,15 @@ module ActiveRecord end if options[:group] && options[:having] + having = sanitize_sql_for_conditions(options[:having]) + # FrontBase requires identifiers in the HAVING clause and chokes on function calls if connection.adapter_name == 'FrontBase' - options[:having].downcase! - options[:having].gsub!(/#{operation}\s*\(\s*#{column_name}\s*\)/, aggregate_alias) + having.downcase! + having.gsub!(/#{operation}\s*\(\s*#{column_name}\s*\)/, aggregate_alias) end - sql << " HAVING #{options[:having]} " + sql << " HAVING #{having} " end sql << " ORDER BY #{options[:order]} " if options[:order] -- cgit v1.2.3 From fd3ccbe955ed58298df43f537a01358d15d8d4cf Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 7 Mar 2009 11:06:39 +0000 Subject: DRY with_kcode in Active Record tests --- activerecord/lib/active_record/test_case.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb index 211dd78874..8c6abaaccb 100644 --- a/activerecord/lib/active_record/test_case.rb +++ b/activerecord/lib/active_record/test_case.rb @@ -49,5 +49,18 @@ module ActiveRecord ActiveRecord::Base.clear_all_connections! ActiveRecord::Base.establish_connection(@connection) end + + def with_kcode(kcode) + if RUBY_VERSION < '1.9' + orig_kcode, $KCODE = $KCODE, kcode + begin + yield + ensure + $KCODE = orig_kcode + end + else + yield + end + end end end -- cgit v1.2.3 From f36513653fceb01691288dfdebfdc56d17dc85c9 Mon Sep 17 00:00:00 2001 From: Adam Milligan Date: Sat, 7 Mar 2009 12:34:56 +0000 Subject: Fix RDoc grammar and ensure hm:t tests can run in isolation. [#1644 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 2 +- .../lib/active_record/associations/has_many_through_association.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 65a1319c7e..301b3a3b58 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -71,7 +71,7 @@ module ActiveRecord # See ActiveRecord::Associations::ClassMethods for documentation. module Associations # :nodoc: - # These classes will be loaded when associatoins are created. + # These classes will be loaded when associations are created. # So there is no need to eager load them. autoload :AssociationCollection, 'active_record/associations/association_collection' autoload :AssociationProxy, 'active_record/associations/association_proxy' diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index d5d188ac2a..23fe76de2e 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -23,8 +23,8 @@ module ActiveRecord end # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and - # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero - # and you need to fetch that collection afterwards, it'll take one less SELECT query if you use length. + # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero, + # and you need to fetch that collection afterwards, it'll take one fewer SELECT query if you use #length. def size return @owner.send(:read_attribute, cached_counter_attribute_name) if has_cached_counter? return @target.size if loaded? -- cgit v1.2.3 From ccb0a92fa2ba0bf7be50033090c3a861e6d907be Mon Sep 17 00:00:00 2001 From: Arthur Klepchukov Date: Sat, 7 Mar 2009 13:52:17 +0000 Subject: Ensure validates_uniqueness_of considers limit on the field. [#1042 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/validations.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 8f3c80565e..4453047daf 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -720,20 +720,20 @@ module ActiveRecord # class (which has a database table to query from). finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? } - is_text_column = finder_class.columns_hash[attr_name.to_s].text? + column = finder_class.columns_hash[attr_name.to_s] if value.nil? comparison_operator = "IS ?" - elsif is_text_column + elsif column.text? comparison_operator = "#{connection.case_sensitive_equality_operator} ?" - value = value.to_s + value = column.limit ? value.to_s[0, column.limit] : value.to_s else comparison_operator = "= ?" end sql_attribute = "#{record.class.quoted_table_name}.#{connection.quote_column_name(attr_name)}" - if value.nil? || (configuration[:case_sensitive] || !is_text_column) + if value.nil? || (configuration[:case_sensitive] || !column.text?) condition_sql = "#{sql_attribute} #{comparison_operator}" condition_params = [value] else -- cgit v1.2.3 From 6543426c73fa9ccf3649d7cbacbbb0fda9b6a099 Mon Sep 17 00:00:00 2001 From: stopdropandrew Date: Sat, 7 Mar 2009 15:26:56 +0000 Subject: Ensure calculations respect scoped :select [#1334 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/calculations.rb | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 7af97d7296..f077818d3b 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -141,22 +141,30 @@ module ActiveRecord def construct_count_options_from_args(*args) options = {} column_name = :all - + # We need to handle # count() # count(:column_name=:all) # count(options={}) # count(column_name=:all, options={}) + # selects specified by scopes case args.size + when 0 + column_name = scope(:find)[:select] if scope(:find) when 1 - args[0].is_a?(Hash) ? options = args[0] : column_name = args[0] + if args[0].is_a?(Hash) + column_name = scope(:find)[:select] if scope(:find) + options = args[0] + else + column_name = args[0] + end when 2 column_name, options = args else raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}" - end if args.size > 0 - - [column_name, options] + end + + [column_name || :all, options] end def construct_calculation_sql(operation, column_name, options) #:nodoc: -- cgit v1.2.3 From 7a26a67b42bdabd89f3685945009ffe99a6d6162 Mon Sep 17 00:00:00 2001 From: John Aughey Date: Sun, 8 Mar 2009 14:47:32 +0000 Subject: Ensure SQLite adapters stores the config [#1947 state:resolved] [John Aughey, Pratik Naik] Signed-off-by: Pratik Naik --- .../lib/active_record/connection_adapters/sqlite3_adapter.rb | 2 +- .../lib/active_record/connection_adapters/sqlite_adapter.rb | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index cc9c46505f..75420f69aa 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -18,7 +18,7 @@ module ActiveRecord db.busy_timeout(config[:timeout]) unless config[:timeout].nil? - ConnectionAdapters::SQLite3Adapter.new(db, logger) + ConnectionAdapters::SQLite3Adapter.new(db, logger, config) end end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 5390f49f04..6077ddcdb6 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -17,9 +17,9 @@ module ActiveRecord # "Downgrade" deprecated sqlite API if SQLite.const_defined?(:Version) - ConnectionAdapters::SQLite2Adapter.new(db, logger) + ConnectionAdapters::SQLite2Adapter.new(db, logger, config) else - ConnectionAdapters::DeprecatedSQLiteAdapter.new(db, logger) + ConnectionAdapters::DeprecatedSQLiteAdapter.new(db, logger, config) end end end @@ -72,6 +72,11 @@ module ActiveRecord # # * :database - Path to the database file. class SQLiteAdapter < AbstractAdapter + def initialize(connection, logger, config) + super(connection, logger) + @config = config + end + def adapter_name #:nodoc: 'SQLite' end -- cgit v1.2.3 From 96eaeee4467a03cba3c4c30aeb6fc6afe545ae1d Mon Sep 17 00:00:00 2001 From: Jeffrey Hardy Date: Fri, 6 Mar 2009 17:43:00 -0500 Subject: Add ActiveRecord::Base#invalid? as the opposite of #valid? [#2159 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/validations.rb | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 4453047daf..ba60180ae0 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -1040,6 +1040,11 @@ module ActiveRecord errors.empty? end + # Performs the opposite of valid?. Returns true if errors were added, false otherwise. + def invalid? + !valid? + end + # Returns the Errors object that holds all information about attribute error messages. def errors @errors ||= Errors.new(self) -- cgit v1.2.3 From 277c799d58be4b3e0e885d7b3fd6d954facc111b Mon Sep 17 00:00:00 2001 From: Jan De Poorter Date: Mon, 9 Mar 2009 13:02:31 +0100 Subject: Fix find_by_last when order is given [#2127 state:committed] Signed-off-by: David Heinemeier Hansson --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 60a1221e66..d8337602e8 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2174,7 +2174,7 @@ module ActiveRecord #:nodoc: # Test whether the given method and optional key are scoped. def scoped?(method, key = nil) #:nodoc: if current_scoped_methods && (scope = current_scoped_methods[method]) - !key || scope.has_key?(key) + !key || !scope[key].nil? end end -- cgit v1.2.3 From faf4ba6b79e56dde383a8761fb1f485fcd07c72b Mon Sep 17 00:00:00 2001 From: Rob Anderton Date: Mon, 9 Mar 2009 12:51:55 +0000 Subject: Allow find(:last) :order be a symbol [#2024 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d8337602e8..edc145985d 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1537,7 +1537,7 @@ module ActiveRecord #:nodoc: end def reverse_sql_order(order_query) - reversed_query = order_query.split(/,/).each { |s| + reversed_query = order_query.to_s.split(/,/).each { |s| if s.match(/\s(asc|ASC)$/) s.gsub!(/\s(asc|ASC)$/, ' DESC') elsif s.match(/\s(desc|DESC)$/) -- cgit v1.2.3 From 1e6c50e21bdb8c99116a7dc6921ef3eb4ed9531a Mon Sep 17 00:00:00 2001 From: Jim Remsik and Tim Pope Date: Mon, 9 Mar 2009 13:42:51 +0000 Subject: Ensure has_many :through works with changed primary keys [#736 state:resolved] Signed-off-by: Pratik Naik --- .../lib/active_record/associations/has_many_through_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 23fe76de2e..1c091e7d5a 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -150,7 +150,7 @@ module ActiveRecord end else reflection_primary_key = @reflection.source_reflection.primary_key_name - source_primary_key = @reflection.klass.primary_key + source_primary_key = @reflection.through_reflection.klass.primary_key if @reflection.source_reflection.options[:as] polymorphic_join = "AND %s.%s = %s" % [ @reflection.quoted_table_name, "#{@reflection.source_reflection.options[:as]}_type", -- cgit v1.2.3 From 0d922885fb54c19f04680482f024452859218910 Mon Sep 17 00:00:00 2001 From: Curtis Hawthorne Date: Mon, 9 Mar 2009 14:56:09 +0000 Subject: Ensure Model#destroy respects optimistic locking [#1966 state:resolved] Signed-off-by: Pratik Naik --- .../lib/active_record/locking/optimistic.rb | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index ff9899d032..7fa7e267d8 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -23,6 +23,16 @@ module ActiveRecord # p2.first_name = "should fail" # p2.save # Raises a ActiveRecord::StaleObjectError # + # Optimistic locking will also check for stale data when objects are destroyed. Example: + # + # p1 = Person.find(1) + # p2 = Person.find(1) + # + # p1.first_name = "Michael" + # p1.save + # + # p2.destroy # Raises a ActiveRecord::StaleObjectError + # # You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging, # or otherwise apply the business logic needed to resolve the conflict. # @@ -39,6 +49,7 @@ module ActiveRecord base.lock_optimistically = true base.alias_method_chain :update, :lock + base.alias_method_chain :destroy, :lock base.alias_method_chain :attributes_from_column_definition, :lock class << base @@ -98,6 +109,28 @@ module ActiveRecord end end + def destroy_with_lock #:nodoc: + return destroy_without_lock unless locking_enabled? + + unless new_record? + lock_col = self.class.locking_column + previous_value = send(lock_col).to_i + + affected_rows = connection.delete( + "DELETE FROM #{self.class.quoted_table_name} " + + "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id} " + + "AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}", + "#{self.class.name} Destroy" + ) + + unless affected_rows == 1 + raise ActiveRecord::StaleObjectError, "Attempted to delete a stale object" + end + end + + freeze + end + module ClassMethods DEFAULT_LOCKING_COLUMN = 'lock_version' -- cgit v1.2.3 From 8272630ce8af0546e7d1aa9211a9d91b80700cbd Mon Sep 17 00:00:00 2001 From: Bruce Krysiak Date: Tue, 10 Mar 2009 11:17:16 +0000 Subject: Ensure ActiveRecord#to_xml respects :skip_types for included associations [#1632 state:resolved] Signed-off-by: Pratik Naik --- .../lib/active_record/serializers/xml_serializer.rb | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index 4749823b94..fa75874603 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -231,16 +231,22 @@ module ActiveRecord #:nodoc: def add_associations(association, records, opts) if records.is_a?(Enumerable) tag = reformat_name(association.to_s) + type = options[:skip_types] ? {} : {:type => "array"} + if records.empty? - builder.tag!(tag, :type => :array) + builder.tag!(tag, type) else - builder.tag!(tag, :type => :array) do + builder.tag!(tag, type) do association_name = association.to_s.singularize records.each do |record| - record.to_xml opts.merge( - :root => association_name, - :type => (record.class.to_s.underscore == association_name ? nil : record.class.name) - ) + if options[:skip_types] + record_type = {} + else + record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name + record_type = {:type => record_class} + end + + record.to_xml opts.merge(:root => association_name).merge(record_type) end end end -- cgit v1.2.3 From c3aa2bcdcffb42f578b0e89fe08e1c4e234ccf3b Mon Sep 17 00:00:00 2001 From: Manfred Stienstra Date: Tue, 10 Mar 2009 11:49:58 +0000 Subject: Ensure nested with_scope merges conditions inside out [#2193 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/base.rb | 19 ++++++++----------- activerecord/lib/active_record/named_scope.rb | 4 ++-- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index edc145985d..4f39761672 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -416,7 +416,7 @@ module ActiveRecord #:nodoc: end @@subclasses = {} - + ## # :singleton-method: # Contains the database configuration - as is typically stored in config/database.yml - @@ -661,7 +661,6 @@ module ActiveRecord #:nodoc: connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) } end - # Returns true if a record exists in the table that matches the +id+ or # conditions given, or false otherwise. The argument can take five forms: # @@ -1003,7 +1002,6 @@ module ActiveRecord #:nodoc: update_counters(id, counter_name => -1) end - # Attributes named in this macro are protected from mass-assignment, # such as new(attributes), # update_attributes(attributes), or @@ -1104,7 +1102,6 @@ module ActiveRecord #:nodoc: read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {}) end - # Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending # directly from ActiveRecord::Base. So if the hierarchy looks like: Reply < Message < ActiveRecord::Base, then Message is used # to guess the table name even when called on Reply. The rules used to do the guess are handled by the Inflector class @@ -1417,7 +1414,6 @@ module ActiveRecord #:nodoc: end end - def quote_value(value, column = nil) #:nodoc: connection.quote(value,column) end @@ -1486,7 +1482,7 @@ module ActiveRecord #:nodoc: elsif match = DynamicScopeMatch.match(method_id) return true if all_attributes_exists?(match.attribute_names) end - + super end @@ -2014,7 +2010,6 @@ module ActiveRecord #:nodoc: end end - # Defines an "attribute" method (like +inheritance_column+ or # +table_name+). A new (class) method will be created with the # given name. If a value is specified, the new method will @@ -2111,7 +2106,7 @@ module ActiveRecord #:nodoc: end # Merge scopings - if action == :merge && current_scoped_methods + if [:merge, :reverse_merge].include?(action) && current_scoped_methods method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)| case hash[method] when Hash @@ -2133,7 +2128,11 @@ module ActiveRecord #:nodoc: end end else - hash[method] = hash[method].merge(params) + if action == :reverse_merge + hash[method] = hash[method].merge(params) + else + hash[method] = params.merge(hash[method]) + end end else hash[method] = params @@ -2143,7 +2142,6 @@ module ActiveRecord #:nodoc: end self.scoped_methods << method_scoping - begin yield ensure @@ -2749,7 +2747,6 @@ module ActiveRecord #:nodoc: assign_multiparameter_attributes(multi_parameter_attributes) end - # Returns a hash of all the attributes with their names as keys and the values of the attributes as values. def attributes self.attribute_names.inject({}) do |attrs, name| diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 43411dfb55..519941dd94 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -104,7 +104,7 @@ module ActiveRecord end end end - + class Scope attr_reader :proxy_scope, :proxy_options, :current_scoped_methods_when_defined NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?).to_set @@ -175,7 +175,7 @@ module ActiveRecord if scopes.include?(method) scopes[method].call(self, *args) else - with_scope :find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {} do + with_scope({:find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {}}, :reverse_merge) do method = :new if method == :build if current_scoped_methods_when_defined with_scope current_scoped_methods_when_defined do -- cgit v1.2.3 From 19ad375e7afa99dc8bc5d859c478bae19c5ddc18 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 10 Mar 2009 23:11:05 -0700 Subject: Don't duplicate :order from scope and options, it makes mysql do extra work --- activerecord/lib/active_record/base.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 4f39761672..62bdf0483a 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1741,7 +1741,9 @@ module ActiveRecord #:nodoc: scoped_order = scope[:order] if scope if order sql << " ORDER BY #{order}" - sql << ", #{scoped_order}" if scoped_order + if scoped_order && scoped_order != order + sql << ", #{scoped_order}" + end else sql << " ORDER BY #{scoped_order}" if scoped_order end -- cgit v1.2.3 From 04333482bd665e4d4ced167ff4f566ecfc0cc919 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 11 Mar 2009 14:08:40 +0000 Subject: Rename ActiveRecord::Base.each to ActiveRecord::Base.find_each --- activerecord/lib/active_record/batches.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/batches.rb b/activerecord/lib/active_record/batches.rb index 9e9c8fbbf4..4ea932f34d 100644 --- a/activerecord/lib/active_record/batches.rb +++ b/activerecord/lib/active_record/batches.rb @@ -11,14 +11,14 @@ module ActiveRecord # # Example: # - # Person.each(:conditions => "age > 21") do |person| + # Person.find_each(:conditions => "age > 21") do |person| # person.party_all_night! # end # # Note: This method is only intended to use for batch processing of large amounts of records that wouldn't fit in # memory all at once. If you just need to loop over less than 1000 records, it's probably better just to use the # regular find methods. - def each(options = {}) + def find_each(options = {}) find_in_batches(options) do |records| records.each { |record| yield record } end -- cgit v1.2.3 From f23adf0ed4bd596c9e7688455489a79f8b35eb3f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 11 Mar 2009 14:52:51 +0000 Subject: Add tests for AssociationCollection#find_each and AssociationCollection#find_in_batches --- activerecord/lib/active_record/associations/association_collection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 0fefec1216..f024f99a34 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -60,7 +60,7 @@ module ActiveRecord @reflection.klass.find(*args) end end - + # Fetches the first one using SQL if possible. def first(*args) if fetch_first_or_last_using_find?(args) -- cgit v1.2.3 From 106976df0911e423042ec4abc165fd561766a047 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 11 Mar 2009 15:24:30 +0000 Subject: Ensure ActiveRecord::Base.find_in_batches fires doesnt fire an extra query unless needed --- activerecord/lib/active_record/batches.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/batches.rb b/activerecord/lib/active_record/batches.rb index 4ea932f34d..03bd4f9f93 100644 --- a/activerecord/lib/active_record/batches.rb +++ b/activerecord/lib/active_record/batches.rb @@ -49,12 +49,15 @@ module ActiveRecord raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit] start = options.delete(:start).to_i + batch_size = options.delete(:batch_size) || 1000 - with_scope(:find => options.merge(:order => batch_order, :limit => options.delete(:batch_size) || 1000)) do + with_scope(:find => options.merge(:order => batch_order, :limit => batch_size)) do records = find(:all, :conditions => [ "#{table_name}.#{primary_key} >= ?", start ]) while records.any? yield records + + break if records.size < batch_size records = find(:all, :conditions => [ "#{table_name}.#{primary_key} > ?", records.last.id ]) end end -- cgit v1.2.3 From 92dadf6d7927fde1482ba1d96d0916093bfb83ca Mon Sep 17 00:00:00 2001 From: Will Bryant Date: Thu, 12 Mar 2009 02:13:26 +1300 Subject: Fixed autosave checks on objects with hm:t in :include [#2213 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/autosave_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 1c3d0567c1..6dcc5005d1 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -310,7 +310,7 @@ module ActiveRecord # This all happens inside a transaction, _if_ the Transactions module is included into # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. def save_has_one_association(reflection) - if association = association_instance_get(reflection.name) + if (association = association_instance_get(reflection.name)) && !association.target.nil? if reflection.options[:autosave] && association.marked_for_destruction? association.destroy elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || reflection.options[:autosave] -- cgit v1.2.3 From db26ace030f6704da6fc80bcc6cd00a2aee664ce Mon Sep 17 00:00:00 2001 From: Murray Steele Date: Thu, 12 Mar 2009 13:49:16 +0000 Subject: Ensure NoMethodError isn't raised when some of the nested eager loaded associations are empty [#1696 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 301b3a3b58..6d25b36aea 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1856,9 +1856,10 @@ module ActiveRecord def construct(parent, associations, joins, row) case associations when Symbol, String - while (join = joins.shift).reflection.name.to_s != associations.to_s - raise ConfigurationError, "Not Enough Associations" if joins.empty? - end + join = joins.detect{|j| j.reflection.name.to_s == associations.to_s && j.parent_table_name == parent.class.table_name } + raise(ConfigurationError, "No such association") if join.nil? + + joins.delete(join) construct_association(parent, join, row) when Array associations.each do |association| @@ -1866,7 +1867,11 @@ module ActiveRecord end when Hash associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name| - association = construct_association(parent, joins.shift, row) + join = joins.detect{|j| j.reflection.name.to_s == name.to_s && j.parent_table_name == parent.class.table_name } + raise(ConfigurationError, "No such association") if join.nil? + + association = construct_association(parent, join, row) + joins.delete(join) construct(association, associations[name], joins, row) if association end else -- cgit v1.2.3 From 91b98cf0a5417ce4042a0b3cd1930d5a221b737f Mon Sep 17 00:00:00 2001 From: Elijah Miller Date: Thu, 12 Mar 2009 15:06:19 +0000 Subject: Returning nil from named scope lambda is equivalent to an empty hash [#1773 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/named_scope.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 519941dd94..1f3ef300f2 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -117,6 +117,7 @@ module ActiveRecord delegate :scopes, :with_scope, :to => :proxy_scope def initialize(proxy_scope, options, &block) + options ||= {} [options[:extend]].flatten.each { |extension| extend extension } if options[:extend] extend Module.new(&block) if block_given? unless Scope === proxy_scope -- cgit v1.2.3 From 47bdf3bf40ec17e1f8ca1c0e3d7f697d0c4cd1bf Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Thu, 12 Mar 2009 15:24:37 +0000 Subject: Ensure AutosaveAssociation runs remove callbacks [#2146 state:resolved] Signed-off-by: Eloy Duran Signed-off-by: Pratik Naik --- .../associations/association_collection.rb | 51 ++++++++++++++-------- .../lib/active_record/autosave_association.rb | 2 +- 2 files changed, 33 insertions(+), 20 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index f024f99a34..ad375be184 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -186,7 +186,6 @@ module ActiveRecord end end - # Removes +records+ from this association calling +before_remove+ and # +after_remove+ callbacks. # @@ -195,22 +194,25 @@ module ActiveRecord # are actually removed from the database, that depends precisely on # +delete_records+. They are in any case removed from the collection. def delete(*records) - records = flatten_deeper(records) - records.each { |record| raise_on_type_mismatch(record) } - - transaction do - records.each { |record| callback(:before_remove, record) } - - old_records = records.reject {|r| r.new_record? } + remove_records(records) do |records, old_records| delete_records(old_records) if old_records.any? - - records.each do |record| - @target.delete(record) - callback(:after_remove, record) - end + records.each { |record| @target.delete(record) } end end + # Destroy +records+ and remove from this association calling +before_remove+ + # and +after_remove+ callbacks. + # + # Note this method will always remove records from database ignoring the + # +:dependent+ option. + def destroy(*records) + remove_records(records) do |records, old_records| + old_records.each { |record| record.destroy } + end + + load_target + end + # 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 @@ -223,15 +225,14 @@ module ActiveRecord self end - - def destroy_all - transaction do - each { |record| record.destroy } - end + # Destory all the records from this association + def destroy_all + load_target + destroy(@target) reset_target! end - + def create(attrs = {}) if attrs.is_a?(Array) attrs.collect { |attr| create(attr) } @@ -431,6 +432,18 @@ module ActiveRecord record end + def remove_records(*records) + records = flatten_deeper(records) + records.each { |record| raise_on_type_mismatch(record) } + + transaction do + records.each { |record| callback(:before_remove, record) } + old_records = records.reject { |r| r.new_record? } + yield(records, old_records) + records.each { |record| callback(:after_remove, record) } + end + end + def callback(method, record) callbacks_for(method).each do |callback| ActiveSupport::Callbacks::Callback.new(method, callback, record).call(@owner, record) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 6dcc5005d1..741aa2acbe 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -283,7 +283,7 @@ module ActiveRecord if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave) records.each do |record| if autosave && record.marked_for_destruction? - record.destroy + association.destroy(record) elsif @new_record_before_save || record.new_record? if autosave association.send(:insert_record, record, false, false) -- cgit v1.2.3 From 3c64c9a5756193247ac87bb55326b6387dfd3070 Mon Sep 17 00:00:00 2001 From: Chris Kampmeier Date: Thu, 12 Mar 2009 15:41:31 +0000 Subject: Fix spelling of an internal method [#1734 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/base.rb | 6 +++--- activerecord/lib/active_record/validations.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 62bdf0483a..4ef3cc698b 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1344,7 +1344,7 @@ module ActiveRecord #:nodoc: subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information } end - def self_and_descendents_from_active_record#nodoc: + def self_and_descendants_from_active_record#nodoc: klass = self classes = [klass] while klass != klass.base_class @@ -1364,7 +1364,7 @@ module ActiveRecord #:nodoc: # module now. # Specify +options+ with additional translating options. def human_attribute_name(attribute_key_name, options = {}) - defaults = self_and_descendents_from_active_record.map do |klass| + defaults = self_and_descendants_from_active_record.map do |klass| :"#{klass.name.underscore}.#{attribute_key_name}" end defaults << options[:default] if options[:default] @@ -1379,7 +1379,7 @@ module ActiveRecord #:nodoc: # Default scope of the translation is activerecord.models # Specify +options+ with additional translating options. def human_name(options = {}) - defaults = self_and_descendents_from_active_record.map do |klass| + defaults = self_and_descendants_from_active_record.map do |klass| :"#{klass.name.underscore}" end defaults << self.name.humanize diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index ba60180ae0..4e084f071c 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -89,7 +89,7 @@ module ActiveRecord message, options[:default] = options[:default], message if options[:default].is_a?(Symbol) - defaults = @base.class.self_and_descendents_from_active_record.map do |klass| + defaults = @base.class.self_and_descendants_from_active_record.map do |klass| [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}", :"models.#{klass.name.underscore}.#{message}" ] end -- cgit v1.2.3 From b304b0be013a05b19708659a350b89fc071e9a7d Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Thu, 12 Mar 2009 16:37:50 +0100 Subject: Fixed some spelling errors in documentation about AssociationCollection::destroy and AssociationCollection::destroy_all. Also fixed some whitespace. --- .../associations/association_collection.rb | 48 ++++++++++++---------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index ad375be184..bcf4a91f0b 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -20,7 +20,7 @@ module ActiveRecord super construct_sql end - + def find(*args) options = args.extract_options! @@ -41,7 +41,7 @@ module ActiveRecord if sanitized_conditions = sanitize_sql(options[:conditions]) conditions << " AND (#{sanitized_conditions})" end - + options[:conditions] = conditions if options[:order] && @reflection.options[:order] @@ -49,12 +49,12 @@ module ActiveRecord elsif @reflection.options[:order] options[:order] = @reflection.options[:order] end - + # Build options specific to association construct_find_options!(options) - + merge_options_from_reflection!(options) - + # Pass through args exactly as we received them. args << options @reflection.klass.find(*args) @@ -142,13 +142,6 @@ module ActiveRecord end end - # Remove all records from this association - def delete_all - load_target - delete(@target) - reset_target! - end - # Calculate sum using SQL, not Enumerable def sum(*args) if block_given? @@ -200,11 +193,11 @@ module ActiveRecord end end - # Destroy +records+ and remove from this association calling +before_remove+ - # and +after_remove+ callbacks. + # Destroy +records+ and remove them from this association calling + # +before_remove+ and +after_remove+ callbacks. # - # Note this method will always remove records from database ignoring the - # +:dependent+ option. + # Note that this method will _always_ remove records from the database + # ignoring the +:dependent+ option. def destroy(*records) remove_records(records) do |records, old_records| old_records.each { |record| record.destroy } @@ -219,14 +212,25 @@ module ActiveRecord if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy destroy_all - else + else delete_all end self end - # Destory all the records from this association + # Remove all records from this association. + # + # See delete for more info. + def delete_all + load_target + delete(@target) + reset_target! + end + + # Destroy all the records from this association. + # + # See destroy for more info. def destroy_all load_target destroy(@target) @@ -357,7 +361,7 @@ module ActiveRecord loaded if target target end - + def method_missing(method, *args) if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method)) if block_given? @@ -367,7 +371,7 @@ module ActiveRecord end elsif @reflection.klass.scopes.include?(method) @reflection.klass.scopes[method].call(self, *args) - else + else with_scope(construct_scope) do if block_given? @reflection.klass.send(method, *args) { |*block_args| yield(*block_args) } @@ -453,8 +457,8 @@ 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) || [] - end - + end + def ensure_owner_is_not_new if @owner.new_record? raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved" -- cgit v1.2.3 From 8e277f89cdbf7266c700703e437525cc8ea35c1d Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Thu, 12 Mar 2009 16:46:38 +0100 Subject: Revert "Fixed some spelling errors in documentation about AssociationCollection::destroy and AssociationCollection::destroy_all." This reverts commit b304b0be013a05b19708659a350b89fc071e9a7d. --- .../associations/association_collection.rb | 48 ++++++++++------------ 1 file changed, 22 insertions(+), 26 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index bcf4a91f0b..ad375be184 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -20,7 +20,7 @@ module ActiveRecord super construct_sql end - + def find(*args) options = args.extract_options! @@ -41,7 +41,7 @@ module ActiveRecord if sanitized_conditions = sanitize_sql(options[:conditions]) conditions << " AND (#{sanitized_conditions})" end - + options[:conditions] = conditions if options[:order] && @reflection.options[:order] @@ -49,12 +49,12 @@ module ActiveRecord elsif @reflection.options[:order] options[:order] = @reflection.options[:order] end - + # Build options specific to association construct_find_options!(options) - + merge_options_from_reflection!(options) - + # Pass through args exactly as we received them. args << options @reflection.klass.find(*args) @@ -142,6 +142,13 @@ module ActiveRecord end end + # Remove all records from this association + def delete_all + load_target + delete(@target) + reset_target! + end + # Calculate sum using SQL, not Enumerable def sum(*args) if block_given? @@ -193,11 +200,11 @@ module ActiveRecord end end - # Destroy +records+ and remove them from this association calling - # +before_remove+ and +after_remove+ callbacks. + # Destroy +records+ and remove from this association calling +before_remove+ + # and +after_remove+ callbacks. # - # Note that this method will _always_ remove records from the database - # ignoring the +:dependent+ option. + # Note this method will always remove records from database ignoring the + # +:dependent+ option. def destroy(*records) remove_records(records) do |records, old_records| old_records.each { |record| record.destroy } @@ -212,25 +219,14 @@ module ActiveRecord if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy destroy_all - else + else delete_all end self end - # Remove all records from this association. - # - # See delete for more info. - def delete_all - load_target - delete(@target) - reset_target! - end - - # Destroy all the records from this association. - # - # See destroy for more info. + # Destory all the records from this association def destroy_all load_target destroy(@target) @@ -361,7 +357,7 @@ module ActiveRecord loaded if target target end - + def method_missing(method, *args) if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method)) if block_given? @@ -371,7 +367,7 @@ module ActiveRecord end elsif @reflection.klass.scopes.include?(method) @reflection.klass.scopes[method].call(self, *args) - else + else with_scope(construct_scope) do if block_given? @reflection.klass.send(method, *args) { |*block_args| yield(*block_args) } @@ -457,8 +453,8 @@ 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) || [] - end - + end + def ensure_owner_is_not_new if @owner.new_record? raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved" -- cgit v1.2.3 From de55c8d7453c5fdf385b5fef9228f3327afddfcf Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Thu, 12 Mar 2009 16:50:10 +0100 Subject: Fixed some spelling errors in documentation about AssociationCollection::destroy and AssociationCollection::destroy_all. This time without the whitespace fixes and move of the destroy_all method. --- .../active_record/associations/association_collection.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index ad375be184..3aef1b21e9 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -143,6 +143,8 @@ module ActiveRecord end # Remove all records from this association + # + # See delete for more info. def delete_all load_target delete(@target) @@ -200,11 +202,11 @@ module ActiveRecord end end - # Destroy +records+ and remove from this association calling +before_remove+ - # and +after_remove+ callbacks. + # Destroy +records+ and remove them from this association calling + # +before_remove+ and +after_remove+ callbacks. # - # Note this method will always remove records from database ignoring the - # +:dependent+ option. + # Note that this method will _always_ remove records from the database + # ignoring the +:dependent+ option. def destroy(*records) remove_records(records) do |records, old_records| old_records.each { |record| record.destroy } @@ -226,7 +228,9 @@ module ActiveRecord self end - # Destory all the records from this association + # Destory all the records from this association. + # + # See destroy for more info. def destroy_all load_target destroy(@target) -- cgit v1.2.3 From 655f95a8a6b79b629b7464522c9b0ecac7311dae Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Thu, 12 Mar 2009 23:28:33 +0100 Subject: in batches.rb, rewrap rdoc, and document options as symbols --- activerecord/lib/active_record/batches.rb | 38 +++++++++++++++++++------------ 1 file changed, 23 insertions(+), 15 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/batches.rb b/activerecord/lib/active_record/batches.rb index 03bd4f9f93..5a6cecd4ad 100644 --- a/activerecord/lib/active_record/batches.rb +++ b/activerecord/lib/active_record/batches.rb @@ -4,10 +4,12 @@ module ActiveRecord base.extend(ClassMethods) end - # When processing large numbers of records, it's often a good idea to do so in batches to prevent memory ballooning. + # When processing large numbers of records, it's often a good idea to do + # so in batches to prevent memory ballooning. module ClassMethods - # Yields each record that was found by the find +options+. The find is performed by find_in_batches - # with a batch size of 1000 (or as specified by the +batch_size+ option). + # Yields each record that was found by the find +options+. The find is + # performed by find_in_batches with a batch size of 1000 (or as + # specified by the :batch_size option). # # Example: # @@ -15,9 +17,10 @@ module ActiveRecord # person.party_all_night! # end # - # Note: This method is only intended to use for batch processing of large amounts of records that wouldn't fit in - # memory all at once. If you just need to loop over less than 1000 records, it's probably better just to use the - # regular find methods. + # Note: This method is only intended to use for batch processing of + # large amounts of records that wouldn't fit in memory all at once. If + # you just need to loop over less than 1000 records, it's probably + # better just to use the regular find methods. def find_each(options = {}) find_in_batches(options) do |records| records.each { |record| yield record } @@ -26,17 +29,22 @@ module ActiveRecord self end - # Yields each batch of records that was found by the find +options+ as an array. The size of each batch is - # set by the +batch_size+ option; the default is 1000. + # Yields each batch of records that was found by the find +options+ as + # an array. The size of each batch is set by the :batch_size + # option; the default is 1000. # - # You can control the starting point for the batch processing by supplying the +start+ option. This is especially - # useful if you want multiple workers dealing with the same processing queue. You can make worker 1 handle all the - # records between id 0 and 10,000 and worker 2 handle from 10,000 and beyond (by setting the +start+ option on that - # worker). + # You can control the starting point for the batch processing by + # supplying the :start option. This is especially useful if you + # want multiple workers dealing with the same processing queue. You can + # make worker 1 handle all the records between id 0 and 10,000 and + # worker 2 handle from 10,000 and beyond (by setting the :start + # option on that worker). # - # It's not possible to set the order. That is automatically set to ascending on the primary key ("id ASC") - # to make the batch ordering work. This also mean that this method only works with integer-based primary keys. - # You can't set the limit either, that's used to control the the batch sizes. + # It's not possible to set the order. That is automatically set to + # ascending on the primary key ("id ASC") to make the batch ordering + # work. This also mean that this method only works with integer-based + # primary keys. You can't set the limit either, that's used to control + # the the batch sizes. # # Example: # -- cgit v1.2.3 From ac3848201dfd7400708d3ccae0acb9388318fb99 Mon Sep 17 00:00:00 2001 From: Jason King Date: Sat, 14 Mar 2009 12:58:42 +0000 Subject: SQLite adapters now support DDL transactions [#2080 state:resolved] Signed-off-by: Pratik Naik --- .../connection_adapters/sqlite_adapter.rb | 55 +++++++++++++--------- 1 file changed, 33 insertions(+), 22 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 6077ddcdb6..afd6472db8 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -72,6 +72,18 @@ module ActiveRecord # # * :database - Path to the database file. class SQLiteAdapter < AbstractAdapter + class Version + include Comparable + + def initialize(version_string) + @version = version_string.split('.').map(&:to_i) + end + + def <=>(version_string) + @version <=> version_string.split('.').map(&:to_i) + end + end + def initialize(connection, logger, config) super(connection, logger) @config = config @@ -81,6 +93,10 @@ module ActiveRecord 'SQLite' end + def supports_ddl_transactions? + sqlite_version >= '2.0.0' + end + def supports_migrations? #:nodoc: true end @@ -88,6 +104,10 @@ module ActiveRecord def requires_reloading? true end + + def supports_add_column? + sqlite_version >= '3.1.6' + end def disconnect! super @@ -169,7 +189,6 @@ module ActiveRecord catch_schema_changes { @connection.rollback } end - # SELECT ... FOR UPDATE is redundant since the table is locked. def add_lock!(sql, options) #:nodoc: sql @@ -218,14 +237,20 @@ module ActiveRecord execute "ALTER TABLE #{name} RENAME TO #{new_name}" end + # See: http://www.sqlite.org/lang_altertable.html + # SQLite has an additional restriction on the ALTER TABLE statement + def valid_alter_table_options( type, options) + type.to_sym != :primary_key + end + def add_column(table_name, column_name, type, options = {}) #:nodoc: - if @connection.respond_to?(:transaction_active?) && @connection.transaction_active? - raise StatementInvalid, 'Cannot add columns to a SQLite database while inside a transaction' + if supports_add_column? && valid_alter_table_options( type, options ) + super(table_name, column_name, type, options) + else + alter_table(table_name) do |definition| + definition.column(column_name, type, options) + end end - - super(table_name, column_name, type, options) - # See last paragraph on http://www.sqlite.org/lang_altertable.html - execute "VACUUM" end def remove_column(table_name, *column_names) #:nodoc: @@ -385,7 +410,7 @@ module ActiveRecord end def sqlite_version - @sqlite_version ||= select_value('select sqlite_version(*)') + @sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)')) end def default_primary_key_type @@ -398,23 +423,9 @@ module ActiveRecord end class SQLite2Adapter < SQLiteAdapter # :nodoc: - def supports_count_distinct? #:nodoc: - false - end - def rename_table(name, new_name) move_table(name, new_name) end - - def add_column(table_name, column_name, type, options = {}) #:nodoc: - if @connection.respond_to?(:transaction_active?) && @connection.transaction_active? - raise StatementInvalid, 'Cannot add columns to a SQLite database while inside a transaction' - end - - alter_table(table_name) do |definition| - definition.column(column_name, type, options) - end - end end class DeprecatedSQLiteAdapter < SQLite2Adapter # :nodoc: -- cgit v1.2.3 From 73fc42cc0b5e94541480032c2941a50edd4080c2 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 15 Mar 2009 22:06:50 -0500 Subject: Prepare for final 2.3 release --- activerecord/lib/active_record/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index 809e91897e..852807b4c5 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,7 +2,7 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 1 + TINY = 2 STRING = [MAJOR, MINOR, TINY].join('.') end -- cgit v1.2.3