Monkey-patch using modules in a gem - ruby

I'm building a Ruby gem that includes a module that's meant to monkey-patch the Hash class to add a new method. I'm following this guide to try to do it neatly: http://www.justinweiss.com/articles/3-ways-to-monkey-patch-without-making-a-mess/
I've placed the module in lib/core_extensions/hash/prune.rb, and the module is declared as such:
module CoreExtensions
module Hash
module Prune
##
# Removes all pairs from the Hash for which the value is nil. Destructive!
def prune!
self.reject! { |_, v| v.nil? }
end
end
end
end
And in order to make the monkey patch take effect, I'm calling this within the main gem file:
Hash.include(CoreExtensions::Hash::Prune)
But after building the gem and trying to require it in an irb console, I get the following error: NameError: uninitialized constant Gem::CoreExtensions (Gem is a placeholder name).
I made sure to include the prune.rb file in my gemspec's files array: s.files = ['lib/gem.rb', 'lib/core_extensions/hash/prune.rb'], so I'm not sure why it can't detect the file and its modules. Can anyone help me figure this out?
Thank you!
EDIT: In case it will help anyone else - I tried to require the module file using require 'lib/core_extensions/hash/prune' but received 'cannot load such file' errors. Sticking ./ in front of the path fixed it.

Related

Not able to call method in a gem

This might be an easy question but I was unfortunately not able to find the answer on Google.
Context:
I am working on a project of my own, and I am externalizing some code in a gem (FrenchTaxSystem). It is the first I create a gem and I have difficulties using it properly.
Problem:
When calling a method (like testit) defined in the main file (french_tax_system.rb) of my gem I get a "NoMethodError: undefined method `testit' for FrenchTaxSystem:Module", though I can call constants from this same file (like FISCAL_NB_PARTS_FOR_MARRIED_COUPLE) and it puzzles me.
E.g in IRB I get that when calling a method:
[
And it is the same in my Rspecs tests inside my gem
However when calling a constant I have no error:
Main file in my gem:
french_tax_system.rb
module FrenchTaxSystem
class Error < StandardError; end
# Constants
...
FISCAL_NB_PARTS_FOR_MARRIED_COUPLE = 2
...
# Methods
## Main method
def testit
"test me"
end
end
Gem file structure:
Thank you in advance for your help,
Mth0158
This should work:
module FrenchTaxSystem
def self.testit
"test me"
end
end

Ruby on Rails Uninitialized constant SomeModule::SomeClass

I have this class under lib/some_module in my project:
module SomeModule
class SomeClass
def initialize
end
end
end
When I go into rails console and I type in SomeModule::SomeClass.new, it works just finel. But when I start up the server and try and access it from some other class, I get the error:
uninitialized constant SomeModule::SomeClass
I have added lib to my autoload in Application.rb. Not sure what might be going wrong
You need to add wildcard path to autoload (I'm not sure why - this fixed this error on my machine).
Therefore, add to application.rb:
config.autoload_paths += Dir["#{config.root}/lib/**/"]

How I include a module (this module has a module inside that) inside another module in Ruby

I have a module as following,
main.rb:
module Main
include Dad::Mam
end
and
in dad.rb:
module Dad
module Mam
puts "Mam is saying you are very lazy..."
end
end
How can I name this file? dad.rb is right?
but when running
$ ruby main.rb
I am getting an Error like,
main.rb:2:in <module:Main>': uninitialized constant Main::Dad
(NameError) from main.rb:1:in'
I need to show the sentance inside the puts under Mam module while running ruby main.rb,
I am confused about using ruby's modules, please anyone help me and guide me..
In this case, since you're just writing a simple script, use #require_relative
require_relative 'dad'
module Main
include Dad::Mam
end
For an actual app or library, you would want to manage the load path (a global variable holding an array that tells ruby where to look for files) and then use a normal require

Load two Ruby Modules/Gems with the same name

I'm trying to use two Gems to access Amazon Web Services (AWS). One is the Amazon 'aws-sdk', the other is 'amazon-ec2'. I'm using the second as the aws-sdk does not cover the cloudwatch section of the amazon services.
The issue is that both load into the same namespace.
require 'aws-sdk' # aws-sdk gem
require 'AWS' # amazon-ec2 gem
config = {:access_key_id => 'abc', :secret_key => 'xyz'}
# start using the API with aws-sdk
ec2 = AWS::EC2.new(config)
# start using the API for anazon-ec2
cw = AWS::Cloudwatch::Base.new(config)
Now this understandably throws an error on the last line as the AWS module is pointing at the first required library, in this case aws-sdk.
NameError: uninitialized constant AWS::Cloudwatch
So, is it possible for me to load one of those into another namespace? Something like
require 'aws-sdk', 'AWS_SDK'
require 'AWS', 'AWS_EC2'
ec2 = AWS_SDK::EC2.new(config)
cw = AWS_EC2::Cloudwatch::Base.new(config)
Or is there another trick I could use here?
Thanks
In Ruby, modules with the same name from different gems don't replace each other. If one gem implements
module AWS
class Foo
end
end
and another implements
module AWS
class Bar
end
end
and you require them both, you will end up with an AWS module that contains both a class Foo and a class Bar (unless the second does something really tricky like explicitly undefining anything already present in the module, before defining its own stuff, which is very unlikely). As long as the second gem doesn't redefine any methods in the first gem (or attempts to use a module as a class or vice versa), they should both work fine. I think you may be looking for the wrong solution.
Edit:
And in fact, what happens for me (in an environment with only these gems present (aws-sdk 1.2.3 and amazon-ec2 0.9.17) and the exact code you listed above) is exactly that:
.rvm/gems/ree-1.8.7-2011.03#ec2/gems/amazon-ec2-0.9.17/lib/AWS/EC2.rb:2: EC2 is not a module (TypeError)
Could it be that an error gets swallowed somewhere and that the module AWS::Cloudwatch hasn't been defined, simply because the initialization of the gem goes awry?
I think I've found a solution that works, let me illustrate it with an example. Suppose we have to files a.rb and b.rb that define the same module with actual name clashes:
#file a.rb
module A
def self.greet
puts 'A'
end
end
#file b.rb
module A
def self.greet
puts 'other A'
end
end
If you need to require both of them, the following seems to do the trick:
require_relative 'a'
TMP_A = A.dup
A.greet # => A
TMP_A.greet # => A
require_relative 'b'
TMP_A2 = A
A.greet # => other A
TMP_A2.greet # => other A
TMP_A.greet # => A
Without the dup, TMP_A will also point to the A defined in b.rb after the require_relative, but the dup will ensure that a real copy is produced instead of simply holding a reference to the module.

How can I get rid of the following warning: Problem while setting context on example startundefined local variable or method `selenium_driver'

Still making my first steps in Ruby (while dealing with some written code). I am getting the following warning each time I run spec (listed as is):
Problem while setting context on example startundefined local variable or method `selenium_driver' for #<Spec::Example::ExampleGroup::Subclass_1::Subclass_1:0x7f2d2cd840e0>
(Edit: Split into two lines, it says)
Problem while setting context on example start
undefined local variable or method `selenium_driver' for #<Spec::Example::ExampleGroup::Subclass_1::Subclass_1:0x7f2d2cd840e0>
While grep-ing through Ruby code - could find the following:
/home/user/.rvm/gems/ruby-1.8.7-p334#frontend/gems/selenium-client-1.2.18/lib/selenium/rspec/spec_helper.rb: STDERR.puts "Problem while setting context on example start" + e
So here is the excerpt from the source code of spec_helper.rb:
config.append_before(:each) do
begin
if selenium_driver && selenium_driver.session_started?
selenium_driver.set_context "Starting example '#{self.description}'"
end
rescue Exception => e
STDERR.puts "Problem while setting context on example start" + e
end
end
Kindly advise how can I solve the (potential) problem.
Update: This grep might be helpful as well:
user#vm-ubuntu:~/dev/branch/tests$
grep selenium_driver *
my_module.rb: #selenium_driver = driver
my_module.rb: ['TERM', 'INT'].each {|s| Signal.trap(s) { #selenium_driver.stop && Process.exit(1) } }
my_module.rb: return #selenium_driver
Update N2:
My Gemfile:
source "http://rubygems.org" # Default source
gem "hpricot", "~>0.8.4"
gem "json", "~>1.5.1"
gem "rspec", "~>1.3.2"
gem "selenium-client", "~>1.2.18"
My selenium_helper.rb file:
require 'selenium/client'
require "selenium/rspec/spec_helper"
...
The problem is that selenium-client gem expects that you name your driver object 'selenium_driver' and make it visible from the spec.
For example if you initialize selenium like this:
before(:all) do
#driver = create_driver($hub_url, $hub_port, $browser)
#driver.start_new_browser_session
end
You need to change it to look like this:
attr_reader :selenium_driver
before(:all) do
#selenium_driver = create_driver($hub_url, $hub_port, $browser)
#selenium_driver.start_new_browser_session
end
Basically it's same code just different variable name. The selenium-client uses that convention to apply context information to the tests.
It's saying it can't find the variable selenium_driver.
Problem while setting context on example startundefined local variable or method `selenium_driver' for #<Spec::Example::ExampleGroup::Subclass_1::Subclass_1:0x7f2d2cd840e0>
is made up of the string "Problem while setting context on example start" plus the exception error message (what's produced by e) of
"undefined local variable or method `selenium_driver' for #<Spec::Example::ExampleGroup::Subclass_1::Subclass_1:0x7f2d2cd840e0>"`.
Add
gem "selenium-client"
to your Gemfile (don't forget to run $ bundle install)
And add following to spec/spec_helper.rb
require "selenium/client"
require "selenium/rspec/spec_helper"

Resources