How can I call Rake methods from a module - ruby

I have a lot of utility functions in my rake files, some of which create rake tasks. I want to move these utility functions into a module to avoid name clashes, but when I do the rake methods are no longer available.
require 'rake'
directory 'exampledir1'
module RakeUtilityFunctions
module_function
def createdirtask dirname
directory dirname
end
end
['test1', 'test2', 'test3'].each { |dirname|
RakeUtilityFunctions::createdirtask dirname
}
The error I get is:
$ rake
rake aborted!
undefined method `directory' for RakeUtilityFunctions:Module
C:/dev/rakefile.rb:8:in `createdirtask'
C:/dev/rakefile.rb:13:in `block in <top (required)>'
C:/dev/rakefile.rb:12:in `each'
C:/dev/rakefile.rb:12:in `<top (required)>'
As far as I can tell the directory method is placed on the ruby top-level by the following code in Rake:
# Extend the main object with the DSL commands. This allows top-level
# calls to task, etc. to work from a Rakefile without polluting the
# object inheritance tree.
self.extend Rake::DSL
Is there a simple way of call functions that have been placed on the top-level like this?

When you define a Module, the code within that module has a new scope.
So directory within RakeUtilityFunctions is in a different scope to the top-level code.
As you haven't defined directory within RakeUtilityFunctions you get an undefined method error.
Have a look at the Scope Gate section of this article.

I have figured it out now. With help from #ReggieB, I discovered this question: ways to define a global method in ruby.
It contained an excerpt from the rake change log.
If you need to call 'task :xzy' inside your class, include Rake::DSL into the class.
So, the easiest way to do this is to extend the module with Rake::DSL:
require 'rake'
directory 'exampledir1'
module RakeUtilityFunctions
self.extend Rake::DSL ### This line fixes the problem!
module_function
def createdirtask dirname
directory dirname
end
end
['test1', 'test2', 'test3'].each { |dirname|
RakeUtilityFunctions.createdirtask dirname
}

Related

Hanami rake task does not load repositories

I have hanami 1.3.0 app named booking. There is rake task in /rakelib/motel.rake :
require_relative '../lib/booking' # it requires booking/motel/booker file
namespace :motel do
task :book do
Booking::Motel::Booker.new.book
end
end
booking/motel/booker requires booking_repository file, and tries to instantiate BookingRepository, but fails with error:
NameError: uninitialized constant Hanami::Repository
<root>/lib/booking/repositories/booking_repository.rb:1:in <top (required)>'
However, when I run Booking::Motel::Booker.new.book in hanami console, it loads BookingRepository without problems.
Looked at numerous stack questions regarding hanami rake, but couldn't find an answer.
As it turns out, it was a foolish mistake. I forgot to add :environment to my task.
namespace :motel do
task book: :environment do
Booking::Motel::Booker.new.book
end
end
That fixed it and dropped the need to use manual file loading, of course.

RSpec auto loading behavior for gems

I have a gem project with rspec configured. My folder structure looks like this -
lib/
- foo/
- foo.rb
- alpha.rb
spec/
- spec_helper.rb
integration/
- alpha_spec.rb
The files foo.rb and alpha.rb have the following structures in terms of class definitions.
# lib/foo/foo.rb
class Foo
# do stuff
end
and
# lib/foo/alpha.rb
require_relative 'foo'
class Foo
class Alpha
# do stuff
end
end
The main takeaway here is that Alpha is a nested class that requires 'foo.rb' directly. (Someone using it would require foo/alpha from their script)
My spec_helper.rb file simply requires my library by loading foo/alpha -
# spec/spec_helper.rb
# Check if `Foo` class is already defined
Object.const_defined?('Foo') ? puts "IS DEFINED" : "IS NOT DEFINED"
require 'foo/alpha'
To my surprise, the constant Foo was already loaded even before requiring alpha/foo, as the output returned IS DEFINED.
Because of this, my require statement tries to load alpha.rb which in turn requires foo.rb and that errors out with
foo.rb:1:in `<top (required)>': Foo is not a class (TypeError)
According to this thread, that type of error is raised by RSpec when a class (Foo) is already defined.
How is this happening? Does RSpec attempt to do some magic behind the scenes and load my library? How do I get around this?
EDIT: I also removed the --require spec_helper line from my .rspec file so it's only getting loaded manually when I run the test.

Include module not working in gem

I have a gem called private_lib.
The file lib/private_lib.rb contains the following:
require 'private_lib/version'
require 'private_lib/handicap'
require 'private_lib/traversal_cap'
module PrivateLib
end
The lib/private_lib/handicap.rb file contains the following
# module for handicap functions
class Handicap
include TraversalCap
-- other code
end
and the file lib/private_lib/traversal_cap.rb contains the following
module TraversalCap
def some_method
end
-- other code
end
I also have a test file spec/handicap_spec.rb which contains the following
require "spec_helper"
describe Handicap do
include TraversalCap
-- some tests that access the ```Handicap``` class
-- some tests that access directly the traversal_cap some_method.
end
When I run rspec spec/handicap_spec, I get the following error
private_lib/lib/private_lib/handicap.rb:3:in `<class:Handicap>': uninitialized constant Handicap::TraversalCap (NameError)
from private_lib/lib/private_lib/handicap.rb:2:in `<top (required)>'
Why isn't the handicap class seeing the traversal_cap module?
It is because of the order you require the files.
At the time the line require 'private_lib/handicap' is run it reads the handicap.rb file and hits the line where you include TraversalCap. But you haven't yet run require 'private_lib/traversal_cap' at this point so the module is undefined.
Quick fix is to change the order of the require calls, or alternatively put require 'private_lib/traversal_cap' at the top of the handicap file.

Weird LoadError on custom ruby gem

I have a custom gem and am encountering a really weird LoadError when I install it as a gem and attempt to require it in irb.
Everything works fine with my rspec tests inside the project folder. This only occurs when using it as an actual gem in irb.
The file it throws a LoadError exception at (/lib/mws/api/order_response.rb) does in fact exist. I've tried renaming the file and updating the file that requires it (/lib/mws.rb). I've tried recreating the file thinking maybe there was a permissions issue. Nothing works.
If I comment out the require line for that specific file, everything works. There's nothing special about the file. There's 4 other files nearly identical to it (*_response.rb).
I feel like I'm taking crazy pills. I must being overlooking something but I sure don't see it.
Trace:
chris#Samus:~$ irb
1.9.3p194 :001 > require 'mws'
LoadError: cannot load such file -- mws/api/order_response
from /Users/chris/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /Users/chris/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /Users/chris/.rvm/gems/ruby-1.9.3-p194/gems/mws-0.1.18/lib/mws.rb:14:in `<top (required)>'
from /Users/chris/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:60:in `require'
from /Users/chris/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:60:in `rescue in require'
from /Users/chris/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:35:in `require'
from (irb):1
from /Users/chris/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'
File with the requires (/lib/mws.rb)
require 'mws/base'
require 'mws/connection'
require 'mws/utility'
require 'mws/api/seller'
require 'mws/api/product'
require 'mws/api/order'
require 'mws/api/report'
require 'mws/api/general_response'
require 'mws/api/product_response'
require 'mws/api/report_response'
require 'mws/api/seller_response'
require 'mws/api/order_response' # <--- the offending line
module MWS
# #see Base#initialize MWS::Base for instantiation details.
# #return [Base] returns MWS::Base object.
def self.new(merchant_id, access_key, secret_key)
MWS::Base.new(merchant_id, access_key, secret_key)
end
end
# The below is for documentation generation purposes.
# MWS is a wrapper for the Amazon Marketplace Web Service (MWS) API.
module MWS
# API handles all the Amazon MWS API specific stuff.
module API
end
# Utilities contains various functions needed throughout MWS. Utilities is a mixin to multiple classes.
module Utilities
end
end
File I'm requiring (/lib/mws/api/order_response.rb):
module MWS
module API
# Class for parsing Amazon's XML responses into managable objects.
class OrderResponse
# Include GeneralResponse instance methods as class methods
extend GeneralResponse
end
end
end
And my file structure
For anyone interested, I was using Jeweler to handle building this gem. As it turns out, Jeweler uses your Git repository when building a gemspec.
If you haven't added all required files to your git repository, Jeweler's gemspec rake task will not include them when generating a new gemspec file.
Can should check in /Users/chris/.rvm/gems/ruby-1.9.3-p194/gems/mws-0.1.18/lib/mws/api if the file lies there (and doesn't have obscure permissions).
If that's not the case, you probably forgot to add it in your gemspec.
If it is there, please try requiring/loading it with the absolute path (for debugging purpose).

calling a ruby file from a RakeFile

I'm creating an application which requires migrations without rails. For that I have created a rake file to execute commands.
My problem is how can I call a ruby class function from a rake file. I want something like this. consider both are in the same directory
class A
def b
puts 'calling method B from class A'
end
end
in the RakeFile
task :create do
A.new.b
end
I want to execute it as
rake create
But currently I'm getting this error
rake aborted!
no such file to load -- a
I'm using ruby 1.9.1, rake (0.8.7)
thanks in advance
cheers
sameera
Have you required the file containing the class? Meaning, have you used any statement like
require "path/to/a.rb" #where a.rb contains the class A
It seems like ruby converter unable to find where to look for class A.

Resources