solnic / virtus

[DISCONTINUED ] Attributes on Steroids for Plain Old Ruby Objects
MIT License
3.77k stars 228 forks source link

Modify attribute options after definition #309

Closed fgarcia closed 9 years ago

fgarcia commented 9 years ago

I am trying to modify on runtime already defined attributes. However the modified options are ignored.

See gist https://gist.github.com/fgarcia/80449b6d2754e66f557d

Is this supposed to be possible? I also played with Virtus.finalize without much success.

solnic commented 9 years ago

Nope, sorry this will never work. Model is being extended with behavior based on attribute definitions, after they are initialized they should not be changed. In virtus 2.0 they will be frozen (frankly I'm surprised they are not already)

fgarcia commented 9 years ago

I am still playing around with dynamically defined attributes but it did not took to much more to understand why non frozen attributes is an awful idea.

The title of this issue and my initial approach is flawed. Certainly a module with Virtus attributes should be frozen, however I am still considering an easy way to reuse current definitions to define new objects.

Normally I have a clean cut of my domain objects:

  1. One class just reflects behavior with internal state not shown to the outside (not a single attribute is exposed)
  2. Many classes with setter/getter attributes. Just state, no behavior

Hence I am very interested in exploring how to make multiple definitions from a central one. So far I am using instance_variable_get(:@attribute_set) to get the attribute_set of Virtus.model and define new ones inside another class

Should that be safe and viable on the long term?

solnic commented 9 years ago

Why not just use inheritance?

fgarcia commented 9 years ago

Normally I reserve inheritance to restrict the nature/type of a class. In my case I have the "Behaviour Domain Object" species and the "Simple State Holder Model" species. The "class species" is something where I tend to use inheritance over mixins

That both of them can share a similar subset of attributes is just an orthogonal concept, where I do prefer to use mixins.

In my current case the inheritance slot is already reserved, but will reevaluate. Also I was concerned about inherited attributes being frozen...

...or maybe I just took the long road of introspection for the fun of exploring Virtus. It is a long time I thought about a custom DSL to copy/redefine/imitate Virtus objects that "mostly" resemble an existing one ... which might overlap with your work with ROM

fgarcia commented 9 years ago

actually a custom DSL extension was the way I thought the Adapter objects where going to be implemented ;-)

like:

class PersonAdapter
   include Virtus.adapter( Person )

   map from('/age'), to('/current_age', &:to_i)

   # inherit / skip rest of attributes?
end

And that would be an whole extra gem, like https://github.com/ismasan/hash_mapper but with some extra knowledge assuming Virtus is being used.

Integration could also lead to things like Person.adapters. Almost useless in production code, but very nice to have with introspection / repl

fgarcia commented 9 years ago

@solnic Regarding not using inheritance, I am facing that issue in my current code:

I have a common set of attributes (for one concept) spread across many classes: events, commands, form objects, query results, widget model ... Normally I tend to add one generic model attribute, but as code evolves there are "slight" variations. Still not sure if having multiple adapters for each of those objects is really worth it or totally insane.

solnic commented 9 years ago

Maybe you could use Virtus.module to share common attributes

fgarcia commented 9 years ago

That is what I am doing right now, and it is almost my solution.

As mentioned earlier, the repeated set of attributes tend to have slight variations. However I wonder now about your comment in the chat about extracting data structure mapping out of Rom into an independent gem (without memory adapter).

Did you have in mind something like the hash_mapper gem syntax I wrote above? To me seems like something very useful and easy to add as an extra independent gem, but I believe you are solving already the same problem from another angle.

Since you have much more experience than me, I am curious about your point of view on my approach.

solnic commented 9 years ago

ROM mapper is standalone, it'll be probably extracted into a separate gem at some point. For repeated set of attributes you can also use inheritance and override some attributes if you want (frankly I don't recall how that would work with virtus models)