DEV Community

Seiei Miyagi
Seiei Miyagi

Posted on

How to define the method for the DSL configuration file

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 
Enter fullscreen mode Exit fullscreen mode
% rake hi hi 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode
# 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 
Enter fullscreen mode Exit fullscreen mode
$ rake hi Hi! Donald Hi! quack 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode
$ rake hi Hi! Donald quack 
Enter fullscreen mode Exit fullscreen mode

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 } 
Enter fullscreen mode Exit fullscreen mode
$ rake hi Hi!, Donald quack 
Enter fullscreen mode Exit fullscreen mode

  1. e.g. Rakefile, config/routes.rb, unicorn.rb, etc... 

  2. https://docs.ruby-lang.org/en/trunk/syntax/refinements_rdoc.html#label-Scope 

Top comments (0)