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 ofsharedbehavior
, 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