Really novice question ...
The many apps in my project have a lot of shared code in a gem. I'm trying to add a routine in that gem that will be shared by rake tasks in several apps.
So in the gem lib/utilities directory I have cleanup.rb:
module Utilities
class Cleanup
def self.perform
puts "Performing cleanup"
end
end
end
Then in the app, in lib/tasks, I have cleanup.rake:
require "utilities/cleanup"
namespace :mynamespace do
task :do_cleanup => :environment do
Utilities::Cleanup.perform
end
end
All I get from rake mynamespace:do_cleanup is "uninitialized constant Utilities::Cleanup".
I've tried several variations of the above. My gems are managed with bundler, but "bundle exec rake ..." gives the same result. I tried using gem "utilities/cleanup" instead of require.
(I swear, I tear my hair out trying to figure out how to communicate between modules and classes more than anything else in this language! It's always "which code am I referencing here?" and "why can't it find that class I wrote?")
You may need to write ::Utilities::Cleanup.perform to explicitly use the top level namespace.
Related
I'm very new to Ruby, so, sorry if the answer is obvious.
Given
Existing project which consists exclusively of Cucumber tests (features). The project has a Gemfile, and env.rb under features/support which appends project directories to $LOAD_PATH and requires several libraries. I can run these tests by executing
bundler exec cucumber -r features
Question
I want to be able to load the support files into a REPL (say, pry), for the purposes of code inspection. In other words, I'd like to create a console -like script that loads up all the code that is used in the tests, but doesn't execute the tests. I need this for the editor that uses this kind of REPL for things like code completion, navigation, refactoring etc. Since this "application" doesn't have anything resembling an entry point, I'm at a loss as to how to create one. My efforts to require all files in the support directory so far had been unsuccessful, in particular, due to the use of World() top-level method, which I believe is defined by Cucumber.
I'm not quite sure what you're trying to accomplish but if you have say pry gem loaded in your Gemfile in test group, you should be able to just throw in a binding.pry command inside any test to inspect the code / environment.
Might look something like this:
Gemfile
group :development, :test do
gem "pry"
end
You could also use a rake task and load the environment taking the same approach.
lib/rake/sometask.rake
namespace :load do
desc "inspect code"
task :code, => :environment do
binding.pry
end
end
and run bundle exec rake load:code for example
I have a simple gem that creates an MD5 from a string.
module SimpleMD5
require 'digest/md5'
def self.md5_string(string)
Digest::MD5.hexdigest(string)
end
end
Running bundle exec bin/console and calling the method works fine
require 'simple_md5'
SimpleMD5.md5_string('test')
=> 098f6bcd4621d373cade4e832627b4f6
Once the gem is built using rake build and rake install using the IRB console and the same example above I get an error.
NameError: uninitialized constant SimpleMD5::Digest
Am I missing a step when the gem is built?
You're missing the module definition, so use this pattern:
require 'digest/md5'
module SimpleMD5
def md5_string(string)
Digest::MD5.hexdigest(string)
end
extend self
end
The SimpleMD5 name is not generated automatically, you must declare it somewhere.
Here extend self means you can mix-in the module with include SimpleMD5 as well as just use it straight-up as you do in your example.
Don't forget two things: In Ruby return is implicit, there's no need to use that unless you're exiting your function early, and MD5 is a pretty terrible hashing algorithm for 2016. Unless you're using it for backwards compatibility, use something better like SHA256 or SHA512.
I have a directory full of scripts mostly written in ruby. Most of them have only require calls, so after some time it is not always clear which gem provides the file or if gem changes — the script can become incompatible.
I can add gem calls specifying versions, but if combined with require calls will look excessive for small scripts.
Is there something like inline bundler without lock file, so only specifying gem versions, handling version resolution, automatic requiring and everything inline in ruby script?
If it 'looks excessive' then perhaps it is? This is purely subjective.
Using gem 'gem_name', 'version.number.s' is the way to do so, as I imagine you are doing from your words.
Once you get to the point where you need more than one file, because the organization demands it, do so. Don't fight something that is naturally occurring without a reason.
For example, for a test that only runs when called directly in this "small script" for whatever measurement of small qualifies as that limitation...
def hello(name_ = 'World', name: name_)
"Hello #{name}"
end
if __FILE__ == $PROGRAM_NAME
gem 'minitest', '5.7.0'
require 'minitest/autorun'
describe hello do
it 'must return "Hello World:"' do
assert_equal hello, 'Hello World!'
end
end
end
Having the gem specified there as is, does not require a Gemfile.lock (it isn't a gem, and doesn't include or have a Gemfile) only requires that I have that gem available / installed.
It will complain if that gem is not available.
I've built a custom Ruby gem. Call it MyGem, then file lib/innermodule.rb contains:
module MyGem
module InnerModule
def self.foo(); puts "Hello world!"; end
end
end
To reference this from another gem that's in development I have to do:
require 'mygem'
require 'innermodule'
Is this normal behaviour, or is there a problem with the gemspec for MyGem?
I don't know if this is necessarily a problem with your gemspec since you usually just specify what files to include in a gem. Gemspecs don't really have anything to do with the way a gem gets required into another app.
It sounds like a problem with the way your gem is built/packaged specifically with regards to naming and file path conventions.
There are some common conventions that are usually followed for building gems and what I referenced above
http://guides.rubygems.org/patterns/ has a good overview.
Basically, you usually want to create a single file (usually the name of your gem) that sits in the "lib" directory. In this case, "lib/mygem.rb" would have individual requires for the internal dependencies of the gem.
#lib/mygem.rb
require 'innermodule'
Then to include the gem (as well as the inner module) in any other app, you could just do
require 'mygem'
I'm trying to execute cucumber tests for a project within a rake file in another project.
Currently I am trying this:
system "cd /path/to/project;rvm use --create 1.9.2-p290#test; cucumber features/test.feature"
This works for the cd, and the rvm seems to work if I run which ruby after the rvm use... but the problem is that the cucumber gem seems to be called from the current folder (not the app to test folder).
The error I get is:
cucumber is not part of the bundle. Add it to Gemfile. (Gem::LoadError)
It seems to be using the local gemset version of cucumber rather than the #test gemset.
Any thoughts on this?
Or is there a better way to run cucumber tests for another project that relies on rvm & a different bundle?
I've also been trying to do exactly the same thing; run an application's tests (or any rake task) from inside another 'control' application.
Reason: (just so I don't get served with a "why on earth?")
I am trying to build an application (rather like cruisecontrol.rb ) which can monitor, schedule and review the specs for a set of apps.
After some digging around in cruisecontrol's source I found that Bundler provides a solution;
Bundler.with_clean_env do
system "rake spec"
end
see line56 of https://github.com/thoughtworks/cruisecontrol.rb/blob/master/lib/platform.rb
That steps out of the bundle and the command is run without the control app's gems.
BUT as is most likely, the command uses bundle exec then this stops working.
Bundler.with_clean_env { system "bundle exec rake spec" }
And you are right back to the exact same problem. This is caused by some bundler variables still existing and being inherited by the sub-shell. Full (very good) explanation here.
The solution is to change the with_clean_env method on bundler like this;
BUNDLER_VARS = %w(BUNDLE_GEMFILE RUBYOPT BUNDLE_BIN_PATH)
module Bundler
def self.with_clean_env &blk
bundled_env = ENV.to_hash
BUNDLER_VARS.each{ |var| ENV.delete(var) }
yield
ensure
ENV.replace(bundled_env.to_hash)
end
end
above code from here
I put that in the environment.rb of my control application (it should probably be in a initializer?) and now I can run the specs of another app from within the control app.
#in control app
result = nil
Dir.chdir(test_app_path) #move into test app
Bundler.with_clean_env { result = `bundle exec rake spec` } #run test apps specs
puts result #display result inside control app
Changing the ; in your script to && seems to work.