I'm trying to use a Gem that provides me with a DSL i need to apply on some of my classes. But using it directly makes my class definitions not as clean as i want them to be, so i want to move the code that uses the DSL somewhere else, which bring me to this problem, that i will describe in an abstract/general way:
If i have a ruby class that includes methods from another gem as private, and in the docs they tell to call those methods inside class definition.
for example:
class A
include ModuleB::PrivateMethods
end
class B < A
do_z :param do
set_property 'a', :x, :y, false
set_property 'b', :x, :y, false
set_property 'only for class B', :x, :y, true
end
def whatever
end
end
# this is from the gem
module ModuleZ
module PrivateMethods
def self.included(base)
base.extend Zmethods
end
module Zmethods
private
def do_z(param1, &block)
# this method do something and calls the block
end
end
end
end
Is there a way to DRY up those calls to do_z if, for example any class that inherit from A have to do this:
do_z :param do
set_property 'a', :x, :y, false
set_property 'b', :x, :y, false
end
and
do_z :param do
set_property 'only for class B', :x, :y, true
end
is only needed for class B and i don't want to write this calls inside the class definition but somewhere else?
Like another module that, when included make those calls even when those methods are private?
So i can write the class definition with something like this?
class B < A
include TheModuleForAllClases::AndTheOneForBclass
def whatever
end
end
I could call #do_z on the base class, and then again for each specialized class to only make the calls needed on each implementation, but they are still many and the blocks are very large, so my class definitions get really long, and the actual method implementation of the class get buried behind those calls.
if wondering, the Gem is swagger-docs look: documenting-a-controller on Rails.
Greetings!
Something like this should work
module AllClassesMethods
def self.included(base)
base.class_eval do
do_z :param do
set_property 'a', :x, :y, false
set_property 'b', :x, :y, false
end
end
end
end
module OnlyBMethods
def self.included(base)
base.class_eval do
do_z :param do
set_property 'only for class B', :x, :y, true
end
end
end
end
class A
include ModuleB::PrivateMethods
include AllClassesMethods
def self.inherited(klass)
klass.include AllClassesMethods
end
end
class B < A
include OnlyBMethods
end
A
and any class that inherits from A
will include AllClassesMethods
, running the code in its included
method. It has to be explicitly included on each inherited class, or else the included
method will only get called for the parent A
. The class_eval
block executes within the including class's context, so it's just like opening up the class in your class definition. Only B
is including OnlyBMethods
, and therefore is the only one triggerring the included
implementation of both Modules.
There's another approach you could use. If you define a class method macro in an extend
ed module, the class method will be executed within the class context, also giving you easy access to it's private methods (I say "easy" access because in Ruby you can always access an object's private methods from any context by using send
)
module AllClassesMethods
def does_z
do_z :param do
set_property 'a', :x, :y, false
set_property 'b', :x, :y, false
end
end
def does_z_for_b
do_z :param do
set_property 'only for class B', :x, :y, true
end
end
end
class A
include ModuleB::PrivateMethods
extend AllClassesMethods
does_z
def self.inherited(klass)
klass.does_z
end
end
class B < A
does_z_for_b
end