New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Removing composed_of #6743
Removing composed_of #6743
Conversation
If we are deprecating it, i think we will have to move it to a plugin |
@josevalim we were thinking that it might be okay to simply remove as it's a seldom used feature and this is a major release. also I think moving to a plugin might not be straightforward because there is interaction between aggregates and various other parts of rails, e.g. finders:
How strongly do you feel that it should be extracted? |
@josevalim also note that we were thinking to add deprecations to 3.2 |
an alternative would be to deprecate in 4.0 and remove in 4.1. extracting a plugin seems hard though. |
As a note, this is a pretty naive removal, so please, give it a good code review. I'd like to give it a much better commit message before it gets merged too. I've updated it to remove the AggregateReflection too. I'm pretty sure that this means that the AssociationReflection stuff and MacroReflection stuff can be simplified. |
We can remove as long as we provide clear instructions to the developer. If There is nothing more frustrating than having a warning that we cannot fix |
I use this feature in some projects. I would have no problem with rolling my own implementation, like this: class Fare
include ActiveRecord::Model
def price
Money.new(super)
end
end About the finder case: maybe add a convention for usage in finders? Like "if Arel doesn't know the object, it will try to call the |
I don't really think we need to find some alternative implementation for the finder. If they want people can do e.g. class Fare
def self.costing(money)
where(price: money.value)
end
end |
I use I believe it would send a useful message to simply remove it rather than extracting it. Namely: this particular abstraction rarely makes things simpler, and you are better off writing plain old Ruby code (which you can understand and maintain) to achieve what you want. |
I like @iain proposal to duck type and call to @tomstuart agreed. we just need to make the process clear. maybe write a section in the updating guide or release notes that people can refer to. |
@josevalim I think a small dose of before-and-after example code can go a long way. For example, |
I'm also OK with removing it. We've used it some times and the result normally it's enough complex to move it to another class/object. |
@tomstuart exactly. |
@steveklabnik it is true, I barely seen it used in projects. But seems composed_of is awesome, very nice sugar. p.s. sure, I'm against of removing it. It is rarely used but should be popular |
@homakov I agree that extracting stuff into composed value objects should be popular. However, passing procs in hashes is a pain, and is for me a big reason not to use it. At least not how it's currently implemented. |
Note also that for single-attribute class MoneySerializer
def dump(money)
money.value
end
def load(value)
Money.new(value)
end
end
class Fare
serialize :price, MoneySerializer.new
end |
@iain hmm it is really dirty. composed_of can be easily replaced with:
also for 1 attribute @jonleighton 's example is not bad. the only profit of composed_of for me - readers. writers(or 'load') are not so useful.
is nice too |
There is a problem with a class Fare
def price
Money.new(price_value, price_unit)
end
end Now what would need to happen if we wanted to call: Fare.where(price: Money.new(4, "USD")) Somehow, the key would need to change too. It would need to be converted to: Fare.where(price_value: 4, price_unit: "USD") This complexity is a good argument for the solution @jonleighton suggested a couple of comments back. Just create the finder yourself. Another pro of that solution is that the database logic remains inside the AR class. If you do decide to implement this, then you could also do this for existing objects, like relations. Now you would have to do: Employee.where(manager_id: Manager.find(params[:manager_id])) But when you allow changing the key as well, you could write: Employee.where(manager: Manager.find(params[:manager_id])) Still, this sounds complex and magical to me. |
Good point. |
Although I've already used @steveklabnik awesome, thanks for your work on that :) @iain agreed, seems better to go with @jonleighton's suggestion, create the finder yourself in such cases. Just want to point that this conversion should happen with the current sanitization code - from the docs: # Given:
# class Person < ActiveRecord::Base
# composed_of :address, :class_name => "Address",
# :mapping => [%w(address_street street), %w(address_city city)]
# end
# Then:
# { :address => Address.new("813 abc st.", "chicago") }
# # => { :address_street => "813 abc st.", :address_city => "chicago" } |
Removed some extra stuff from some extra places.
|
Okay, I think I've got it now. That should be removed properly. |
@@ -88,8 +88,6 @@ def expand_hash_conditions_for_aggregates(attrs) | |||
# { :address => Address.new("123 abc st.", "chicago") } | |||
# # => "address_street='123 abc st.' and address_city='chicago'" | |||
def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name) | |||
attrs = expand_hash_conditions_for_aggregates(attrs) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can remove the expand_hash_conditions_for_aggregates
method now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! Yes. An awkward git checkout
. Pushing that now.
Some tests used Customer stuff. So I killed all the tests relating to Aggregates, and left in a Customer without aggregate stuff and killed some classes that just had aggregate stuff in it. |
I think that plain objects can be an alternative to define complex domain model without exists an AR and composed_of method works for it. I really love composed_of because i've working with hibernate components for my java projects. what is alternative to use something like this: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/components.html ? |
Rebaed so that it merges cleanly again. |
@cesario yes, I agree that is verbose and error-prone, but It is easier to understand and customize, without use options like And, yes, I agree that is a metter of we want to make Value Object's first-class in the Rails, and given the actual user base of this feature, I would say that it is not adding any real value to the framework. I'll really appreciate if someone could extract this to a plugin, but I don't think that the Rails team should maintain that plugin. @steveklabnik could you move this forward improving the commit message? You could add some examples how the users can archive this behavior without use the |
@rafaelfranca Give me a few hours, I'll get that updated. |
@rafaelfranca updated |
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 rails#1436 Closes rails#2084 Closes rails#3807
@steveklabnik it is about time to remove it in master |
I just discovered |
In my opinion we should remove it. But right now it will live on. |
yup, we want to get rid of it, but not yet. |
Seems like this is a feature that does not lend itself to many use cases, but for the cases that it's meant for, it's a perfect fit. I do not believe that its prudent to remove an entire interface (which can always be improved, at least in theory) just because the benefits are not immediately obvious. As far as general purpose frameworks go, the competition is in the offered tools. This is a tool. Its only crime here is that the current implementation is still lacking according to this thread. Furthermore, value objects are an accepted, well defined, concept in of itself. It seems a bit odd to support the entity but not the value as a concept. |
Let's remove everything that is not popular in rails. |
Popular for whom? |
Honestly removing a feature from rails just because it is not popular it is a bad thing to do. I found about this feature today and i was really happy till a colleague of mine told me that they plan to deprecate this feature. A feature that allows an object to act as a decorator for a property and offers a feature that cant be replaced by anything in rails atm. If you want to remove something well offer a better alternative dont just cripple the framework cause that feature is not popular enough. |
No worries: If we're saying this is deprecated anywhere, please point it out and we'll correct it. |
@sirfilip please read this post about alternative way: http://blog.plataformatec.com.br/2012/06/about-the-composed_of-removal/ |
Thanks a lot guys you just made my day. @pftg thanks for the tip will check it out. |
👍 It will be sticking around. (and I think I updated the blog post to point this) |
Good to hear. What I think this discussion brings to light is that it's completely unclear what the actual API should be for the feature. Just about every comment seems to boil down to the fact that it's clunky in it's current state. So then the question is really, what's the expectation for this feature? How far can the integration go? How far should configuration go? What should it all look like? What kind of use cases are encountered regularly (beyond the famous example for money)? Essentially, what are we really missing if we assume this will stay? |
Hey all, my apologies for bringing this thread back from the dead. Could I trouble you individuals into pointing me to some recent resources (e.g. blog posts, tuts) regarding I can also move comment somewhere else if this isn't the appropriate place. |
I don't know any materials, and this should probably be asked at stack exchange. A case I can think of is proper names. Maybe international addresses. That sort of thing. |
@KurtRMueller here's a use case for composed_of that doesn't include Money or Timezones https://www.sitepoint.com/ddd-for-rails-developers-part-2-entities-and-values/ |
composed_of is a complicated feature that's rarely used. So let's deprecate it in 3-2 and remove it in master.
Related: #6742
/cc @jonleighton @tenderlove