Skip to content
This repository

#364 composed_of: Access record information from converter #1436

Closed
wants to merge 4 commits into from

6 participants

Franck Verrot Ilya Katz lichtamberg Isaac Sanders Steve Klabnik Aaron Patterson
Franck Verrot

AR#composed_of can now access the associated record

    class User < ActiveRecord::Base
      composed_of :some_aggregation, ...,  :converter => Proc.new { |record, values| ... }
    end

Tested against REE and 1.9.2-p180.

Ilya Katz

hey there, this was a great addition, however, i noticed something strange - looks like it make a difference in what order the attributes were specified (i guess the one that is being specified in the composed_of, in this case value)

In the examples below, currency field is not populated

Rate.new(:start_at=>"2012-12-5", :end_at=>"2012-12-8",:value=>43,:currency=>"EUR")
/Users/ilyakatz/NetBeansProjects/xxxx/app/models/rate.rb:17
:converter => Proc.new { |record, value, currency| debugger; Money.new(value || 0, currency) }
(rdb:1) pp record
#

Rate.new(:start_at=>"2012-12-5", :end_at=>"2012-12-8",:currency=>"EUR",:value=>43)
/Users/ilyakatz/NetBeansProjects/xxxx/app/models/rate.rb:17
:converter => Proc.new { |record, value, currency| debugger; Money.new(value || 0, currency) }
(rdb:1) pp record
#

lichtamberg

Coool!!!

Please apply this patch to the master branch!

Isaac Sanders

@cesario Is this still an issue?

Steve Klabnik
Collaborator

@cesario This pull request can't be cleanly merged any more. While I don't have the authority to merge it in anyway, clean patches are always faster to get merged than ones that aren't. :)

Steve Klabnik
Collaborator

If #6743 gets merged, this feature won't be useful.

Patrick Negri pnegri referenced this pull request June 16, 2012
Merged

Removing composed_of #6743

Steve Klabnik steveklabnik referenced this pull request from a commit June 18, 2012
Commit has since been removed from the repository and is no longer available.
Steve Klabnik steveklabnik referenced this pull request from a commit June 18, 2012
Commit has since been removed from the repository and is no longer available.
Steve Klabnik steveklabnik closed this pull request from a commit June 15, 2012
Steve Klabnik Removing composed_of from ActiveRecord.
This feature adds a lot of complication to ActiveRecord for dubious
value. Let's talk about what it does currently:

class Customer < ActiveRecord::Base
  composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
end

Instead, you can do something like this:

    def balance
      @balance ||= Money.new(value, currency)
    end

    def balance=(balance)
      self[:value] = balance.value
      self[:currency] = balance.currency
      @balance = balance
    end

Since that's fairly easy code to write, and doesn't need anything
extra from the framework, if you use composed_of today, you'll
have to add accessors/mutators like that.

Closes #1436
Closes #2084
Closes #3807
14fc8b3
Steve Klabnik steveklabnik closed this in 14fc8b3 June 18, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
6  activerecord/CHANGELOG
... ...
@@ -1,5 +1,11 @@
1 1
 *Rails 3.1.0 (unreleased)*
2 2
 
  3
+* AR#composed_of can now access the associated record
  4
+
  5
+    class User < ActiveRecord::Base
  6
+      composed_of :some_aggregation, ...,  :converter => Proc.new { |record, values| ... }
  7
+    end
  8
+
3 9
 * AR#pluralize_table_names can be used to singularize/pluralize table name of an individual model:
4 10
 
5 11
     class User < ActiveRecord::Base
17  activerecord/lib/active_record/aggregations.rb
@@ -185,9 +185,11 @@ module ClassMethods
185 185
       #   to instantiate a <tt>:class_name</tt> object.
186 186
       #   The default is <tt>:new</tt>.
187 187
       # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
188  
-      #   or a Proc that is called when a new value is assigned to the value object. The converter is
189  
-      #   passed the single value that is used in the assignment and is only called if the new value is
190  
-      #   not an instance of <tt>:class_name</tt>.
  188
+      #   or a Proc that is called when a new value is assigned to the value object. Depending on its arity,
  189
+      #   the converter is passed either:
  190
+      #   * the single value that is used in the assignment
  191
+      #   * or the current object and this single value
  192
+      #   The converter is only called if the new value is not an instance of <tt>:class_name</tt>.
191 193
       #
192 194
       # Option examples:
193 195
       #   composed_of :temperature, :mapping => %w(reading celsius)
@@ -239,9 +241,12 @@ def writer_method(name, class_name, mapping, allow_nil, converter)
239 241
               @aggregation_cache[name] = nil
240 242
             else
241 243
               unless part.is_a?(class_name.constantize) || converter.nil?
242  
-                part = converter.respond_to?(:call) ?
243  
-                  converter.call(part) :
244  
-                  class_name.constantize.send(converter, part)
  244
+                part = if converter.respond_to?(:call)
  245
+                         converter.arity == 1 ? converter.call(part) : converter.call(self, part)
  246
+                       else
  247
+                         klass = class_name.constantize
  248
+                         klass.method(converter).arity == 1 ? klass.send(converter, part) : klass.send(converter, self, part)
  249
+                       end
245 250
               end
246 251
 
247 252
               mapping.each { |pair| self[pair.first] = part.send(pair.last) }
6  activerecord/test/cases/aggregations_test.rb
@@ -119,6 +119,12 @@ def test_custom_converter
119 119
     assert_equal 'Barnoit GUMBLEAU', customers(:barney).fullname.to_s
120 120
     assert_kind_of Fullname, customers(:barney).fullname
121 121
   end
  122
+
  123
+  def test_custom_converter_with_arity_of_2
  124
+    customers(:barney).fullname = 'Franck Verrot'
  125
+    customers(:barney).location = %w(Lyon France)
  126
+    assert_equal 'Franck VERROT from Lyon, France', customers(:barney).location.to_s
  127
+  end
122 128
 end
123 129
 
124 130
 class OverridingAggregationsTest < ActiveRecord::TestCase
16  activerecord/test/models/customer.rb
@@ -3,6 +3,7 @@ class Customer < ActiveRecord::Base
3 3
   composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| balance.to_money }
4 4
   composed_of :gps_location, :allow_nil => true
5 5
   composed_of :fullname, :mapping => %w(name to_s), :constructor => Proc.new { |name| Fullname.parse(name) }, :converter => :parse
  6
+  composed_of :location, :class_name => "Location", :mapping => [ %w(address_city city), %w(address_country country) ], :converter => :convert_location
6 7
 end
7 8
 
8 9
 class Address
@@ -35,6 +36,21 @@ def exchange_to(other_currency)
35 36
   end
36 37
 end
37 38
 
  39
+class Location
  40
+  attr_reader :city, :country, :who
  41
+  def initialize(city, country, who = "")
  42
+    @city, @country, @who = city, country, who
  43
+  end
  44
+
  45
+  def self.convert_location(customer, values)
  46
+    new(values.first, values.last, customer.fullname)
  47
+  end
  48
+
  49
+  def to_s
  50
+    "#{who} from #{city}, #{country}"
  51
+  end
  52
+end
  53
+
38 54
 class GpsLocation
39 55
   attr_reader :gps_location
40 56
 
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.