rake
rake copied to clipboard
`namespace` not only for tasks, but also for functions hermetization
It's a known fact, that ruby function defined in a namespace
will be defined in a global scope rather than inside of a hermetized context. So ruby function defined in one namespace
will be visible in another and it may be unintentionally overridden.
It would be nice if namespace
could be a context for ruby functions as well as rake tasks.
What about using a module to "namespace" your methods? That's the Ruby style to do it…
This is how I do it, however having namespace
block for rake task is a bit confusing and I think a lot of people do a mistake of defining global functions inside of a namespace
block and expecting that they are hermetized. There is also no way to include the module in a task, so even when I define a module, I have to type its name to call its method.
module Hermetized
module Context
def self.echo(text)
puts text
end
end
end
namespace :hermetized do
namespace :context do
desc 'This task has hermetized function'
task :print do
Hermetized::Context::echo('this works, but is it nice?')
end
end
end
This is very inconvenient, and I think there should be a way to define functions namespaced in a rake task. It could work in a similar way to rspecs let
block.
namespace :hermetized do
namespace :context do
let(:echo) do
-> (text) { puts text }
end
desc 'This task has hermetized function'
task :print do
echo.call('this could work')
end
end
end
Or even with nicer syntax.
namespace :hermetized do
namespace :context do
fun(:echo, :text) do
puts text
end
desc 'This task has hermetized function'
task :print do
echo('this could work')
end
end
end
(@mblumtritt please, take a look)
I see what you like to have/use. The problem is that you deal with class instances when you deal with namespaces in Rake. But there may be a solution because defining namespace-specific methods is possible:
namespace :calc do |n|
def n.add(x)
x + 21
end
task :test do |t, a|
puts n.add(21)
end
end
It's not very elegant; I would prefer modules like suggested before…
Using Rake::DSL
and singleton methods is a way to do this.
First, add a file with a helper method define_rake_task
and require it in Rakefile
def define_rake_task(*args, &task_block)
Class.new do
include Rake::DSL
define_method(:initialize) do
task(*args) do |*task_args|
instance_exec(*task_args, &task_block)
end
end
end.new
end
Then replace old task
method with your helper method define_rake_task
.
namespace :user do
define_rake_task echo_test1: :environment do
def echo(x)
puts x
end
echo 'A'
echo 'B'
end
define_rake_task echo_test2: :environment do
echo 'C'
end
end
Test it:
$ rake user:echo_test1 user:echo_test2
A
B
rake aborted!
NoMethodError: undefined method `echo' for #<#<Class:0x00007f8f9b4bfbb8>:0x00007f8f9b4bf4d8>
It will be nice if it is supported by this gem in default. Allow we to define methods in any task or namespace by using Ruby's def
keyword. Just like how rspec
does (See: https://relishapp.com/rspec/rspec-core/v/3-8/docs/helper-methods/arbitrary-helper-methods).