Closed senny closed 12 years ago
Agreed, building "true" values like this should be nice.
With respect to The Third Manifesto, which is somewhat related to this (through Veritas), this would provide the ability to create valid types (immutable sets of values) in an easy way.
Concerning,
place.latitude = -74.091796875 # => ERROR: Attempt to call private method
I definitely agree. The Third Manifesto goes one step further as it suggests something like this (I suppose that LatLng is actually GeoLocation ?)
place = LatLng.new(:latitute => 40.73, :longitude => -74.09)
place.latitude = -70,06 # ERROR
place.with_latitude(-70.06) == LatLng.new(:latitute => 40.73, :longitude => -70.06) # true
place.latitude # 40.73, that is, with_latitude is not an attr_writer at all.
i.e. with_XXX methods provide shortcuts to select values through single member updates.
@blambeau I'm not sure if veritas should automatically generate these with_XXX
methods, which return new ValueObjects based on self. I think this is more of a domain concern to provide the operations necessary to work with the value objects at hand. Sometimes this might be necessary but I guess we should not try to guess what the application needs.
Thanks for the LatLng hint ;) I fixed it in the Ticket description.
@senny well with_XXX
methods do not capture operations/operators to work with values but rather useful shortcuts for selectors. As your error message suggested the existence of private attr_writers, I just wanted to point out that IMHO the TTM is right in catching our attention that (when working with immutable values) with_xxx are actually very useful (even if kept private). They help building the operations/operators you are talking about. Just a 5 cents thought, though.
@blambeau if kept private I think it could be a good addition to make the code clean. I was a little confused since your example suggested that they were public and I don't like the idea that a wealth of public methods is created in case you once need them ;)
@senny strictly speaking, I think that TTM requires those operators to be automatically provided by the system and public. As I know that Veritas/Virtus are partly inspired by TTM, I just wanted to bring your attention here. Now, Veritas and Virtus are not building bricks for a valid D language (or do they @dkubb?), so I don't think conformance is actually required here. Anyway, knowing with_xxx
(THE_xxx(value) := ...
in TTM/TUTORIAL D) may help providing nice building blocks for implementing immutable types/values. I don't have a strong opinion about what should actually be supported by Virtus itself.
I really like the idea of a shortcut for Value Object (immutability and attribute-based equality). Virtus is certainly great for this use-case.
I think that this could be provide a decent starting point for value-object-like-equality. It's called Equalizable
, but I've considered changing the name to AttributeEquality
, since the whole purpose is to establish equality (#eql?
, #==
, and #hash
) and #inspect
based on attribute values, rather than identity.
(to be clear, I merely extracted Equalizable
from dm-core
, I didn't come up with it myself)
@emmanuel where will such code live? Since aequitas and virtus are separate I guess virtus will have it's own implementation of Equalizable
what are the thoughts about code-sharing between the DM-2 libraries?
A very good question. I'm not sure how best to share such 'support' code.
Something like Equalizable
hardly seems like it merits its own gem, and yet it also feels wasteful to have a private implementation of such a primitive (value-object-definition) in each DM2-related gem.
Any ideas? /cc @solnic, @dkubb
Fwiw, in Veritas I needed something similar to Equalizable, but the behaviour wasn't 100% the same since some of the semantics are defined in Relational Algebra, and I needed to respect those first., eg: https://github.com/dkubb/veritas/blob/master/lib/veritas/support/comparator.rb
Having gone through the process of doing this with Extlib, I'm not a huge fan of extracting things to a shared gem in the near term. Perhaps when it satisfies the rule of three, and we're using it 3 or more places, but for now I would focus on keeping the supporting libs in the "support" directory like we've done in Veritas/Virtus, and then revisit it later on.
Indeed that's a very good question.
I've been thinking about it many times because we already have a few cases where multiple libraries need common functionality. We will probably end up with a shared 'support' library. The best thing we can do is to make sure it's as tiny as possible and includes only truly essential shared functionality. We definitely don't want to create another ActiveSupport or Extlib though.
Time will tell what's actually required and we will figure it out later.
@senny, @blambeau — I've implemented Virtus::ValueObject
in #53. Would you mind taking a look and let me know if that fits your needs? I think I've implemented everything I'm going to want for Conformitas, but I haven't started building on top of Virtus::ValueObject
yet, so I'm not completely sure...
Also, I considered adding Virtus::ValueObject#with
like this:
module Virtus::ValueObject
def with(attribute_updates)
self.class.new(attributes.merge(attribute_updates))
end
end
What do you think about that?
closing this one since there's already a pending pull request with this feature
It would be nice if virtus had like a submodule for building ValueObjects. On top of my head this could look something like:
The
Virtus::ValueObject
module would set some sane defaults for building ValueObjects like: