You can define the method for the DSL configuration file1 by Refinements.
For example, the hi
method in a following code can use only in this Rakefile, it shouldn't affect any code outside of the Rakefile.
# Rakefile
main = self
using Module.new {
refine(main.singleton_class) do
def hi
puts :hi
end
end
}
desc 'hi'
task :hi do
hi
end
% rake hi
hi
Common mistake 1
The following code defines the method at top level.
# Rakefile
def hi
puts :hi
end
desc 'hi'
task :hi do
hi
end
Common mistake 2
The following code defines the method at top level.
# Rakefile
desc 'hi'
task :hi do
def hi
puts :hi
end
hi
end
Common mistake 3
The following code defines the method at top level.
# Rakefile
m = Module.new do
def hi
puts :hi
end
end
desc 'hi'
task :hi do
include m
hi
end
Why should we avoid to define the method at top level?
It possibly breaks other libraries code.
For example:
# duck.rb
class Duck < Struct.new(:name)
def sound
puts 'quack'
end
end
# Rakefile
require_relative 'duck.rb'
def puts(*)
print 'Hi! '
super
end
desc 'hi'
task :hi do
duck = Duck.new('Donald')
puts duck.name
duck.sound
end
$ rake hi
Hi! Donald
Hi! quack
The benefit of the Refinements
The ruby's Refinements are lexical in scope2.
Thus the top level method that defined by the Refinements couldn't breaks other libraries code, It's totally fine!
# Rakefile
require_relative 'duck.rb'
main = self
using Module.new {
refine(main.singleton_class) do
def puts(*)
print 'Hi! '
super
end
end
}
desc 'hi'
task :hi do
duck = Duck.new('Donald')
puts duck.name
duck.sound
end
$ rake hi
Hi! Donald
quack
Other approach
Wrap the method and DSLs within a module.
# Rakefile
require_relative 'duck.rb'
Module.new {
def puts(*)
print 'Hi!, '
super
end
extend(self)
extend Rake::DSL
desc 'hi'
task :hi do
duck = Duck.new('Donald')
puts duck.name
duck.sound
end
}
$ rake hi
Hi!, Donald
quack
-
e.g.
Rakefile
,config/routes.rb
,unicorn.rb
, etc... ↩ -
https://docs.ruby-lang.org/en/trunk/syntax/refinements_rdoc.html#label-Scope ↩
Top comments (0)