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

Changeset 8003

Show
Ignore:
Timestamp:
10/23/07 17:39:35 (2 years ago)
Author:
bitsweat
Message:

Assigning an instance of a foreign class to a composed_of aggregate calls an optional conversion block. Refactor and simplify composed_of implementation. Closes #6322.

Files:

Legend:

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

    r8002 r8003  
    11*SVN* 
     2 
     3* Assigning an instance of a foreign class to a composed_of aggregate calls an optional conversion block. Refactor and simplify composed_of implementation.  #6322 [brandon, Chris Cruft] 
    24 
    35* Assigning nil to a composed_of aggregate also sets its immediate value to nil.  #9843 [Chris Cruft] 
  • trunk/activerecord/lib/active_record/aggregations.rb

    r8002 r8003  
    123123      #   This defaults to +false+. 
    124124      # 
     125      # An optional block can be passed to convert the argument that is passed to the writer method into an instance of 
     126      # <tt>:class_name</tt>. The block will only be called if the arguement is not already an instance of <tt>:class_name</tt>. 
     127      # 
    125128      # Option examples: 
    126129      #   composed_of :temperature, :mapping => %w(reading celsius) 
    127       #   composed_of :balance, :class_name => "Money", :mapping => %w(balance amount) 
     130      #   composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) {|balance| balance.to_money } 
    128131      #   composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ] 
    129132      #   composed_of :gps_location 
    130133      #   composed_of :gps_location, :allow_nil => true 
    131134      # 
    132       def composed_of(part_id, options = {}
     135      def composed_of(part_id, options = {}, &block
    133136        options.assert_valid_keys(:class_name, :mapping, :allow_nil) 
    134137 
     
    136139        class_name  = options[:class_name] || name.camelize 
    137140        mapping     = options[:mapping]    || [ name, name ] 
     141        mapping     = [ mapping ] unless mapping.first.is_a?(Array) 
    138142        allow_nil   = options[:allow_nil]  || false 
    139143 
    140144        reader_method(name, class_name, mapping, allow_nil) 
    141         writer_method(name, class_name, mapping, allow_nil
     145        writer_method(name, class_name, mapping, allow_nil, block
    142146         
    143147        create_reflection(:composed_of, part_id, options, self) 
     
    146150      private 
    147151        def reader_method(name, class_name, mapping, allow_nil) 
    148           mapping = (Array === mapping.first ? mapping : [ mapping ]) 
    149  
    150           allow_nil_condition = if allow_nil 
    151             mapping.collect { |pair| "!read_attribute(\"#{pair.first}\").nil?"}.join(" || ") 
    152           else 
    153             "true" 
     152          module_eval do 
     153            define_method(name) do |*args| 
     154              force_reload = args.first || false 
     155              if (instance_variable_get("@#{name}").nil? || force_reload) && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? }) 
     156                instance_variable_set("@#{name}", class_name.constantize.new(*mapping.collect {|pair| read_attribute(pair.first)})) 
     157              end 
     158              return instance_variable_get("@#{name}") 
     159            end 
    154160          end 
    155161 
    156           module_eval <<-end_eval, __FILE__, __LINE__ 
    157             def #{name}(force_reload = false) 
    158               if (@#{name}.nil? || force_reload) && #{allow_nil_condition} 
    159                 @#{name} = #{class_name}.new(#{mapping.collect { |pair| "read_attribute(\"#{pair.first}\")"}.join(", ")}) 
    160               end 
    161               return @#{name} 
    162             end 
    163           end_eval 
    164162        end 
    165163 
    166         def writer_method(name, class_name, mapping, allow_nil) 
    167           mapping = (Array === mapping.first ? mapping : [ mapping ]) 
    168  
    169           if allow_nil 
    170             module_eval <<-end_eval, __FILE__, __LINE__ 
    171               def #{name}=(part) 
    172                 @#{name} = part.freeze 
    173                 if part.nil? 
    174                   #{mapping.collect { |pair| "@attributes[\"#{pair.first}\"] = nil" }.join("\n")} 
    175                 else 
    176                   #{mapping.collect { |pair| "@attributes[\"#{pair.first}\"] = part.#{pair.last}" }.join("\n")} 
    177                 end 
     164        def writer_method(name, class_name, mapping, allow_nil, conversion) 
     165          module_eval do 
     166            define_method("#{name}=") do |part| 
     167              if part.nil? && allow_nil 
     168                mapping.each { |pair| @attributes[pair.first] = nil } 
     169                instance_variable_set("@#{name}", nil) 
     170              else 
     171                part = conversion.call(part) unless part.is_a?(class_name.constantize) || conversion.nil? 
     172                mapping.each { |pair| @attributes[pair.first] = part.send(pair.last) } 
     173                instance_variable_set("@#{name}", part.freeze) 
    178174              end 
    179             end_eval 
    180           else 
    181             module_eval <<-end_eval, __FILE__, __LINE__ 
    182               def #{name}=(part) 
    183                 @#{name} = part.freeze 
    184                 #{mapping.collect{ |pair| "@attributes[\"#{pair.first}\"] = part.#{pair.last}" }.join("\n")} 
    185               end 
    186             end_eval 
     175            end 
    187176          end 
    188177        end 
  • trunk/activerecord/test/fixtures/customer.rb

    r4353 r8003  
    11class Customer < ActiveRecord::Base 
    22  composed_of :address, :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ], :allow_nil => true 
    3   composed_of :balance, :class_name => "Money", :mapping => %w(balance amount) 
     3  composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money } 
    44  composed_of :gps_location, :allow_nil => true 
    55end