Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

#364 composed_of: Access record information from converter #1436

Closed
wants to merge 4 commits into from

6 participants

@franckverrot

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.

@tenderlove tenderlove was assigned
@ilyakatz

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!

@isaacsanders

@cesario Is this still an issue?

@steveklabnik
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. :)

@steveklabnik
Collaborator

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

@pnegri pnegri referenced this pull request
Merged

Removing composed_of #6743

@steveklabnik steveklabnik referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@steveklabnik steveklabnik referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@steveklabnik steveklabnik closed this pull request from a commit
@steveklabnik steveklabnik 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
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.
View
6 activerecord/CHANGELOG
@@ -1,5 +1,11 @@
*Rails 3.1.0 (unreleased)*
+* AR#composed_of can now access the associated record
+
+ class User < ActiveRecord::Base
+ composed_of :some_aggregation, ..., :converter => Proc.new { |record, values| ... }
+ end
+
* AR#pluralize_table_names can be used to singularize/pluralize table name of an individual model:
class User < ActiveRecord::Base
View
17 activerecord/lib/active_record/aggregations.rb
@@ -185,9 +185,11 @@ module ClassMethods
# to instantiate a <tt>:class_name</tt> object.
# The default is <tt>:new</tt>.
# * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
- # or a Proc that is called when a new value is assigned to the value object. The converter is
- # passed the single value that is used in the assignment and is only called if the new value is
- # not an instance of <tt>:class_name</tt>.
+ # or a Proc that is called when a new value is assigned to the value object. Depending on its arity,
+ # the converter is passed either:
+ # * the single value that is used in the assignment
+ # * or the current object and this single value
+ # The converter is only called if the new value is not an instance of <tt>:class_name</tt>.
#
# Option examples:
# composed_of :temperature, :mapping => %w(reading celsius)
@@ -239,9 +241,12 @@ def writer_method(name, class_name, mapping, allow_nil, converter)
@aggregation_cache[name] = nil
else
unless part.is_a?(class_name.constantize) || converter.nil?
- part = converter.respond_to?(:call) ?
- converter.call(part) :
- class_name.constantize.send(converter, part)
+ part = if converter.respond_to?(:call)
+ converter.arity == 1 ? converter.call(part) : converter.call(self, part)
+ else
+ klass = class_name.constantize
+ klass.method(converter).arity == 1 ? klass.send(converter, part) : klass.send(converter, self, part)
+ end
end
mapping.each { |pair| self[pair.first] = part.send(pair.last) }
View
6 activerecord/test/cases/aggregations_test.rb
@@ -119,6 +119,12 @@ def test_custom_converter
assert_equal 'Barnoit GUMBLEAU', customers(:barney).fullname.to_s
assert_kind_of Fullname, customers(:barney).fullname
end
+
+ def test_custom_converter_with_arity_of_2
+ customers(:barney).fullname = 'Franck Verrot'
+ customers(:barney).location = %w(Lyon France)
+ assert_equal 'Franck VERROT from Lyon, France', customers(:barney).location.to_s
+ end
end
class OverridingAggregationsTest < ActiveRecord::TestCase
View
16 activerecord/test/models/customer.rb
@@ -3,6 +3,7 @@ class Customer < ActiveRecord::Base
composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| balance.to_money }
composed_of :gps_location, :allow_nil => true
composed_of :fullname, :mapping => %w(name to_s), :constructor => Proc.new { |name| Fullname.parse(name) }, :converter => :parse
+ composed_of :location, :class_name => "Location", :mapping => [ %w(address_city city), %w(address_country country) ], :converter => :convert_location
end
class Address
@@ -35,6 +36,21 @@ def exchange_to(other_currency)
end
end
+class Location
+ attr_reader :city, :country, :who
+ def initialize(city, country, who = "")
+ @city, @country, @who = city, country, who
+ end
+
+ def self.convert_location(customer, values)
+ new(values.first, values.last, customer.fullname)
+ end
+
+ def to_s
+ "#{who} from #{city}, #{country}"
+ end
+end
+
class GpsLocation
attr_reader :gps_location
Something went wrong with that request. Please try again.