Wednesday 15 April 2015

ruby - Only allow module to define method if including class/module does not -



ruby - Only allow module to define method if including class/module does not -

i'm having lots of fun activemodel's serialization, tangled web of as_json , serializable_hash.

my app has big collection of models share behavior including module, we'll phone call sharedbehavior.

my team has decided have default format want these classes follow when beingness cast json (for rendering in rails app), of them should behave little differently. due odd behavior in these 2 methods activemodel library, adding whitelisted or blacklisted attributes in models gets overridden method definition in module, , passed on super declarations in activemodel.

for reason, i'd module apply definition of these methods models if not explicitly overridden in models (in essence, take module out of ancestor chain few method calls), still need shared behavior module.

i tried solving conditionally, dynamically applying method on module inclusion in irb:

class def foo puts 'in a' end end module d def self.included(base) unless base.instance_methods(false).include?(:foo) define_method(:foo) puts 'in d' super() end end end end class b < include d end class c < include d def foo puts 'in c' super end end

with declaration, expected output of c.new.foo be

in c in

but instead

in c in d in

my other thought move logic out module , include module in every class (there 54 of them) not explicitly override method, there couple downsides that:

it introduces bit of implicit coupling in project new model include module iff not want override method implementation the current implementation of these serialization methods in module have behavior , attributes established module, sense unintuitive have sec module knows , depends on implementation details of sharedbehavior, though sec module have nil first.

can else think of solution, or maybe spot oversight of mine in code illustration above allow me create phone call in included hook? (i tried switching order in c class defined foo method , included d module, saw same behavior).

there 2 tricky bugs here.

ruby evaluates classes, order of expressions matters. include d before defining foo in c, when included hook called, foo won't defined in base. need include d @ end of class. you're defining foo in d. after including d in b, d#foo defined, meaning it's still included in c if prepare previous bug. need base receiver of define_method.

but there's interesting twist: fixing sec bug makes first bug irrelevant. defining foo in base directly, overwritten later definitions. doing

class c < def foo puts 'in d' super() end # overwrites previous definition! def foo puts 'in c' super end end

so summarize, need

# in d.included base.class_eval define_method(:foo) puts 'in d' super() end end

ruby module include active-model-serializers

No comments:

Post a Comment