Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 7935

Show
Ignore:
Timestamp:
10/16/07 05:07:58 (11 months ago)
Author:
bitsweat
Message:

Refactor association create and build so before & after callbacks behave consistently. Closes #8854.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activerecord/CHANGELOG

    r7932 r7935  
    11*SVN* 
     2 
     3* Refactor association create and build so before & after callbacks behave consistently.  #8854 [lifofifo, mortent] 
    24 
    35* Quote table names. Defaults to column quoting.  #4593 [Justin Lynn, gwcoffey, eadz, Dmitry V. Sabanin, Jeremy Kemper] 
  • trunk/activerecord/lib/active_record/associations/association_collection.rb

    r7767 r7935  
    8686       
    8787      def create(attrs = {}) 
    88         ensure_owner_is_not_new 
    89         record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { @reflection.klass.create(attrs) }                 
    90         @target ||= [] unless loaded? 
    91         @target << record  
    92         recor
     88        if attrs.is_a?(Array) 
     89          attrs.collect { |attr| create(attr) } 
     90        else 
     91          create_record(attrs) { |record| record.save } 
     92        en
    9393      end 
    9494 
    9595      def create!(attrs = {}) 
    96         ensure_owner_is_not_new 
    97         record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { @reflection.klass.create!(attrs) }                 
    98         @target ||= [] unless loaded? 
    99         @target << record  
    100         record 
     96        create_record(attrs) { |record| record.save! } 
    10197      end 
    10298 
     
    190186 
    191187      private 
     188 
     189        def create_record(attrs, &block) 
     190          ensure_owner_is_not_new 
     191          record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { @reflection.klass.new(attrs) } 
     192          add_record_to_target_with_callbacks(record, &block) 
     193        end 
     194 
     195        def build_record(attrs, &block) 
     196          record = @reflection.klass.new(attrs) 
     197          add_record_to_target_with_callbacks(record, &block) 
     198        end 
     199 
     200        def add_record_to_target_with_callbacks(record) 
     201          callback(:before_add, record) 
     202          yield(record) if block_given? 
     203          @target ||= [] unless loaded? 
     204          @target << record 
     205          callback(:after_add, record) 
     206          record 
     207        end 
     208 
    192209        def callback(method, record) 
    193210          callbacks_for(method).each do |callback| 
  • trunk/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb

    r7666 r7935  
    99      def build(attributes = {}) 
    1010        load_target 
    11         record = @reflection.klass.new(attributes) 
    12         @target << record 
    13         record 
     11        build_record(attributes) 
    1412      end 
    1513 
    1614      def create(attributes = {}) 
    17         # Can't use Base.create because the foreign key may be a protected attribute. 
    18         ensure_owner_is_not_new 
    19         if attributes.is_a?(Array) 
    20           attributes.collect { |attr| create(attr) } 
    21         else 
    22           record = build(attributes) 
    23           insert_record(record) unless @owner.new_record? 
    24           record 
    25         end 
     15        create_record(attributes) { |record| insert_record(record) } 
    2616      end 
    2717       
    2818      def create!(attributes = {}) 
    29         # Can't use Base.create! because the foreign key may be a protected attribute. 
    30         ensure_owner_is_not_new 
    31         if attributes.is_a?(Array) 
    32           attributes.collect { |attr| create(attr) } 
    33         else 
    34           record = build(attributes) 
    35           insert_record(record, true) unless @owner.new_record? 
    36           record 
    37         end         
     19        create_record(attributes) { |record| insert_record(record, true) } 
    3820      end 
    3921 
     
    161143          !select_clause && @owner.connection.columns(@reflection.options[:join_table], "Join Table Columns").size != 2 
    162144        end 
     145 
     146      private 
     147        def create_record(attributes) 
     148          # Can't use Base.create because the foreign key may be a protected attribute. 
     149          ensure_owner_is_not_new 
     150          if attributes.is_a?(Array) 
     151            attributes.collect { |attr| create(attr) } 
     152          else 
     153            record = build(attributes) 
     154            yield(record) 
     155            record 
     156          end 
     157        end 
    163158    end 
    164159  end 
  • trunk/activerecord/lib/active_record/associations/has_many_association.rb

    r7554 r7935  
    1111          attributes.collect { |attr| build(attr) } 
    1212        else 
    13           record = @reflection.klass.new(attributes) 
    14           set_belongs_to_association_for(record) 
    15            
    16           @target ||= [] unless loaded? 
    17           @target << record 
    18            
    19           record 
     13          build_record(attributes) { |record| set_belongs_to_association_for(record) } 
    2014        end 
    2115      end 
  • trunk/activerecord/test/associations_test.rb

    r7824 r7935  
    596596  end 
    597597   
     598  def test_create_with_bang_on_has_many_raises_when_record_not_saved 
     599    assert_raises(ActiveRecord::RecordInvalid) do 
     600      firm = Firm.find(:first) 
     601      firm.plain_clients.create! 
     602    end 
     603  end 
     604 
    598605  def test_create_with_bang_on_habtm_when_parent_is_new_raises 
    599606    assert_raises(ActiveRecord::RecordNotSaved) do  
  • trunk/activerecord/test/associations/callbacks_test.rb

    r6997 r7935  
    6161  end 
    6262 
     63  def test_has_many_callbacks_with_create 
     64    morten = Author.create :name => "Morten" 
     65    post = morten.posts_with_proc_callbacks.create! :title => "Hello", :body => "How are you doing?" 
     66    assert_equal ["before_adding<new>", "after_adding#{post.id}"], morten.post_log 
     67  end 
     68 
     69  def test_has_many_callbacks_with_create! 
     70    morten = Author.create! :name => "Morten" 
     71    post = morten.posts_with_proc_callbacks.create :title => "Hello", :body => "How are you doing?" 
     72    assert_equal ["before_adding<new>", "after_adding#{post.id}"], morten.post_log 
     73  end 
     74 
     75  def test_has_many_callbacks_for_save_on_parent 
     76    jack = Author.new :name => "Jack" 
     77    post = jack.posts_with_callbacks.build :title => "Call me back!", :body => "Before you wake up and after you sleep" 
     78 
     79    callback_log = ["before_adding<new>", "after_adding#{jack.posts_with_callbacks.first.id}"] 
     80    assert_equal callback_log, jack.post_log 
     81    assert jack.save 
     82    assert_equal 1, jack.posts_with_callbacks.count 
     83    assert_equal callback_log, jack.post_log 
     84  end 
     85 
    6386  def test_has_and_belongs_to_many_add_callback 
    6487    david = developers(:david) 
     
    99122  end 
    100123 
     124  def test_has_many_and_belongs_to_many_callbacks_for_save_on_parent 
     125    project = Project.new :name => "Callbacks" 
     126    project.developers_with_callbacks.build :name => "Jack", :salary => 95000 
     127 
     128    callback_log = ["before_adding<new>", "after_adding<new>"] 
     129    assert_equal callback_log, project.developers_log 
     130    assert project.save 
     131    assert_equal 1, project.developers_with_callbacks.count 
     132    assert_equal callback_log, project.developers_log 
     133  end 
     134 
    101135  def test_dont_add_if_before_callback_raises_exception 
    102136    assert !@david.unchangable_posts.include?(@authorless) 
  • trunk/activerecord/test/fixtures/author.rb

    r7325 r7935  
    3434           :after_remove  => :log_after_removing 
    3535  has_many :posts_with_proc_callbacks, :class_name => "Post", 
    36            :before_add    => Proc.new {|o, r| o.post_log << "before_adding#{r.id}"}, 
    37            :after_add     => Proc.new {|o, r| o.post_log << "after_adding#{r.id}"}, 
     36           :before_add    => Proc.new {|o, r| o.post_log << "before_adding#{r.id || '<new>'}"}, 
     37           :after_add     => Proc.new {|o, r| o.post_log << "after_adding#{r.id || '<new>'}"}, 
    3838           :before_remove => Proc.new {|o, r| o.post_log << "before_removing#{r.id}"}, 
    3939           :after_remove  => Proc.new {|o, r| o.post_log << "after_removing#{r.id}"} 
    4040  has_many :posts_with_multiple_callbacks, :class_name => "Post", 
    41            :before_add => [:log_before_adding, Proc.new {|o, r| o.post_log << "before_adding_proc#{r.id}"}], 
    42            :after_add  => [:log_after_adding,  Proc.new {|o, r| o.post_log << "after_adding_proc#{r.id}"}] 
     41           :before_add => [:log_before_adding, Proc.new {|o, r| o.post_log << "before_adding_proc#{r.id || '<new>'}"}], 
     42           :after_add  => [:log_after_adding,  Proc.new {|o, r| o.post_log << "after_adding_proc#{r.id || '<new>'}"}] 
    4343  has_many :unchangable_posts, :class_name => "Post", :before_add => :raise_exception, :after_add => :log_after_adding 
    4444 
     
    7575  private 
    7676    def log_before_adding(object) 
    77       @post_log << "before_adding#{object.id}" 
     77      @post_log << "before_adding#{object.id || '<new>'}" 
    7878    end 
    7979 
  • trunk/activerecord/test/fixtures/project.rb

    r4893 r7935  
    88  has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => 'SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id}' 
    99  has_and_belongs_to_many :developers_by_sql, :class_name => "Developer", :delete_sql => "DELETE FROM developers_projects WHERE project_id = \#{id} AND developer_id = \#{record.id}" 
    10   has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id}"}, 
    11                             :after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id}"},  
     10  has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id || '<new>'}"}, 
     11                            :after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || '<new>'}"}, 
    1212                            :before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"}, 
    1313                            :after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"}