How to know if I have required a library - ruby

Is there any way to do something like this pseudo-code?
puts was_required?('my-library')
=> false
require('my-library')
puts was_required?('my-library')
=> true
I want to be able to test if I have required some library, whether is a Gem o or a Rake task, by it's require-name.
I could test a gem testing if the Class the gem loads is available, but I would really love to test using the library name, no the class name.
For rake tasks is the same. I can find in Rake.tasks if there is a task from the library I have required, by I would like to test with the library name, no for tasks names
thanks

Perhaps you want to play around with $LOADED_FEATURES (alias of $"), that array that contains the module names loaded by require?
def was_required?(file)
rex = Regexp.new("/#{Regexp.quote(file)}\.(so|o|sl|rb)?")
$LOADED_FEATURES.find { |f| f =~ rex }
end
was_required?('uri')
#=> false
require 'uri'
#=> true
was_required?('uri')
#=> true
Btw this is exactly what Ruby does in the require method.

Related

Get class/module name

Is there a better way to get class/module name viz, C from A::B::C, B from A::B::C, and A From A::B::C. The following code uses string and split to get "Stegosaurus" from Cowsay::Character::Stegosaurus, How to do away with string and split?
require 'cowsay'
x = Cowsay.random_character().class
x.name.split("::")[2]
require 'cowsay'
true
x = Cowsay.random_character().class
Cowsay::Character::Stegosaurus
x.name.split("::")[2]
"Stegosaurus"
I don't think there's anything for handling this in core/standard library.
As an alternative to custom written methods there is always activesupport:
require 'active_support/core_ext/string/inflections'
Cowsay::Character::Stegosaurus.name.demodulize
#=> "Stegosaurus"
Cowsay::Character::Stegosaurus.name.deconstantize
#=> "Cowsay::Character"
These methods are implemented as follows:
def demodulize(path)
path = path.to_s
if i = path.rindex('::')
path[(i+2)..-1]
else
path
end
end
def deconstantize(path)
path.to_s[0, path.rindex('::') || 0] # implementation based on the one in facets' Module#spacename
end
Take a look into docs if interested in more methods.
As to
A From A::B::C
If you'd require the whole activesupport, you'd get bunch of Module methods, including parent:
require 'active_support'
Cowsay::Character::Stegosaurus.parent
#=> Cowsay
If you're not going to use it extensively, I recommend to just grab the needed methods from activesupport and put it into some helper, because loading it whole might be an overkill.

How can I mimic Node.js's require function in Ruby?

In node.js you can write:
var lib = require('lib');
but in Ruby the require function simply runs the code in the file and true is returned.
Currently, I'm using a very dirty solution:
main.rb:
$stuff = []
require './file1.rb'
require './file2.rb'
# and so on
file1.rb:
$stuff << something
and so on.
How can I eliminate the use of a global variable?
eg:
main.rb:
$stuff = []
$stuff << cool_require './file1.rb'
# etc
file1.rb:
exports.what = something
One of the biggest errors when working with a language, is trying to make the language working like a different one.
Ruby is not NodeJs, there are features built-in into each language that are unique to the language and cannot be reproduced easily.
In other words, there is no way to implement the NodeJS require behavior in Ruby because in Ruby there is no notion of export. When you require a file, every method/class included in the required file are made available to the scope.
In Ruby there are objects and method visibility. The way you have to make a method visible or not is to declare it as public or private/protected.
Well, first consider that Ruby is not Node.js. As Simone Carletti said, there are some features that are unique to each language. Sometimes it's good to take from other language but sometimes it's bad.
There are few things that you must keep in mind:
meth is method invocation, to pass method you use method method: method(:meth) or package it into module/class
you can use class/module by assigning it to some 2nd variable:
class A;
def self.aa; puts 'aa'; end;
end;
New_a = A;
New_a.aa # aa;
eval is dangerous method(you can evaluate unknown code)
Method:
Here is one way you can do. It is not idiot-proof tough. It is not 100% safe(eval). :
file1.rb:
Module.new do
def self.meth1
42
end
def self.meth2
'meth2'
end
end
This file contain module with 2 methods.
I am using Module.new because it returns object that you want. You can assign it later into variable/constant.
I am using self.meth* so you don't have to include but run instantly it like this: module_name.meth1()
req.rb:
def cool_require name
eval(File.read name)
end
Some_variable = cool_require('req.rb')
puts Some_variable.meth1 # 42
puts Some_variable.meth2 # meth2
cool_require reads filename(argument name) and evaluate it(it is just like you would type it in irb/pry)
Some_variable is constant. It won't disappear that easily.
2 last line is how it works. As fair I remember, that's how node.js' require works.

Is it possible to 'unload' ('un-require') a Ruby library?

I'm looking to load a few libraries, have them do some work, and then do the opposite of require to avoid compatibility errors later. I don't want to have to dump to a file and restart a shell, as the objects created (such as data) could be processed well by my other libraries, just not in the presence of the early ones I'm seeking to unload.
Anyone got any suggestions or know if this is possible? A conversation from 2006 didn't come to much conclusion-wise other than that 'it looks like Webrick manages to do this somehow'.
The libraries in question are Google_drive and Nokogiri (the spreadsheet processing library Roo depends on Google_drive for online spreadsheet reading/writing, as described at that link).
Like #Alex said, you could use the Kernel#fork to create a new ruby process where you will require your libraries. The new forked process will have access to data loaded in the parent process:
def talk(msg)
# this will allow us to see which process is
# talking
puts "#{Process.pid}: #{msg}"
end
# this data was loaded on the parent process
# and will be use in the child (and in the parent)
this_is_data = ["a", "b", "c"]
talk "I'm the father process, and I see #{this_is_data}"
# this will create a new ruby process
fork{
talk "I'm another process, and I also see #{this_is_data}"
talk "But when I change `this_is_data`, a new copy of it is created"
this_is_data << "d"
talk "My own #{this_is_data}"
}
# let's wait and give a chance to the child process
# finishes before the parent
sleep 3
talk "Now, in the father again, data is: #{this_is_data}"
The result of this execution will vary in your machine, the Process.id will return different values, but it will be like these:
23520: I'm the father process, and I see ["a", "b", "c"]
23551: I'm another process, and I also see ["a", "b", "c"]
23551: But when I change `this_is_data`, a new copy of it is created
23551: My own ["a", "b", "c", "d"]
23520: Now, in the father again, data is: ["a", "b", "c"]
And this is good! Each process created by fork is an O.S. level process and run in it's own memory space.
Another thing you can do to somehow manage the globals created by loading a file, is replace the use of require by load. This approach doesn't solve all the problems already pointed, but really can help. See the following specs:
require "minitest/autorun"
describe "Loading files inside a scope" do
def create_lib_file(version)
libfile = <<CODE
class MyLibrary#{version}
VERSION = "0.0.#{version}"
end
class String
def omg_danger!
end
end
puts "loaded \#{MyLibrary#{version}::VERSION}"
CODE
File.write("my_library.rb", libfile)
end
after do
File.delete("my_library.rb") if File.exists?("my_library.rb")
end
describe "loading with require" do
it "sees the MyLibrary definition" do
create_lib_file("1")
require_relative "my_library.rb"
MyLibrary1::VERSION.must_be :==, "0.0.1"
"".respond_to?(:omg_danger!).must_be :==, true
end
end
describe "loading with #load " do
describe "without wrapping" do
it "sees the MyLibrary definition" do
create_lib_file("2")
load "my_library.rb"
MyLibrary2::VERSION.must_be :==, "0.0.2"
"".respond_to?(:omg_danger!).must_be :==, true
end
end
describe "using anonymous module wraping" do
it "doesn't sees MyLibrary definition" do
create_lib_file("3")
load "my_library.rb", true
->{ MyLibrary3 }.must_raise NameError
"".respond_to?(:omg_danger!).must_be :==, false
end
end
end
end
And the result of execution:
Run options: --seed 16453
# Running tests:
loaded 0.0.3
.loaded 0.0.2
.loaded 0.0.1
.
Finished tests in 0.004707s, 637.3486 tests/s, 1274.6973 assertions/s.
3 tests, 6 assertions, 0 failures, 0 errors, 0 skips
Despite of what is usually said, it's possible to unrequire/unload packages using this process.
Assuming that the file required is stored as d:/foo.rb with this simple content :
class Foo
end
As any Class, Module or Method is defined as a constant in Ruby you can first unlink it :
irb(main):001:0> require 'd:/foo.rb'
=> true
irb(main):002:0> defined? Foo
=> "constant"
irb(main):003:0> Object.send(:remove_const, :Foo)
=> Foo
irb(main):004:0> defined? Foo
=> nil
The files already required/loaded are recorded in the global var $", you then need to purge what you've already required from it :
irb(main):005:0> $".select{|r| r.include? 'foo.rb'}
=> ["d:/foo.rb"]
irb(main):006:0> $".delete('d:/foo.rb')
=> "d:/foo.rb"
irb(main):007:0> $".select{|r| r.include? 'foo.rb'}
=> []
Now you can require your file again and everything will be refreshed and available.
irb(main):008:0> require 'd:/foo.rb'
=> true
irb(main):009:0> $".select{|r| r.include? 'foo.rb'}
=> ["d:/foo.rb"]
irb(main):010:0> defined? Foo
=> "constant"
irb(main):011:0> Foo.new
=> #<Foo:0x000000033ff8d8>
I'm not aware of any way to unload a file, but you can reset handpicked global variables to nil and undefine constants (which is close enough):
class Foo; end
Object.constants.include?(:Foo)
Object.send(:remove_const, :Foo)
Object.constants.include?(:Foo)
Foo # NameError: uninitialized constant Foo
Depending on what your conflicts are, you could also temporarily rename the conflicting classes:
Bar = Foo
Object.send(:remove_const, :Foo)
do_stuff
Foo = Bar
Unfortunately, a couple characteristics of Ruby are at ends with your desire to cleanly "unload" a library. First, "loading" a Ruby library can run arbitrary Ruby code. Second, existing constants and methods can be dynamically redefined in Ruby.
If a Ruby library only defines new classes and modules, you can simply undefine them as #Denis pointed out. However, in that case, "compatibility errors" are very unlikely to occur even if you just leave them as is. If a library monkey-patches core Ruby classes, creates signal handlers, or sets up tracing hooks or at_exit hooks, it will be very, very difficult to trace out everything which has changed and reverse the changes cleanly.
Your best bet would be to first load your data, then use something like Process#fork to fork off a new shell, then load the libraries. When you are finished, kill the child shell and go back to the parent. Your data will still be there.
https://github.com/burke/zeus and https://github.com/jonleighton/spring use similar techniques to avoid repeatedly waiting for Rails to load. Perhaps you can adapt some pieces of their work.

Unit testing with and without requiring ActiveSupport

I've extracted a single class from a Rails app into a gem. It's very, very simple, but of course I'd like to fully test it (I'm using rspec).
The class does some simple date-calculation. It's not dependent on Rails, but since it started out in a Rails app, and is still used there, it uses ActiveSupport's time zone-aware methods when it can. But, if ActiveSupport isn't available, it should use the std-lib Date methods.
Specifically, it only does this in one single place: Defaulting an optional argument to "today's date":
arg ||= if Date.respond_to?(:current)
Date.current # use ActiveSupport's time zone-aware mixin if possible
else
Date.today # stdlib fallback
end
Question is: How do I properly test this? If I require ActiveSupport in my spec_helper.rb, it'll obviously always use that. If I don't require it anywhere, it'll never use it. And if I require it for a single example group, rspec's random execution order makes the testing unpredictable, as I don't know when AS will be required.
I can require maybe it in a before(:all) in a nested group, as nested groups are (I believe) processed highest to deepest. But that seems terribly inelegant.
I could also split the specs into two files, and run them separately, but again, that seems unnecessary.
I could also disable rspec's random ordering, but that's sort of going against the grain. I'd rather have it as randomized as possible.
Any ideas?
Another solution is to mock the current and today methods, and use those for testing. Eg:
# you won't need these two lines, just there to make script work standalone
require 'rspec'
require 'rspec/mocks/standalone'
def test_method(arg = nil)
arg ||= if Date.respond_to?(:current)
Date.current # use ActiveSupport's time zone-aware mixin if possible
else
Date.today # stdlib fallback
end
arg
end
describe "test_method" do
let(:test_date) { Date.new(2001, 2, 3) }
it "returns arg unchanged if not nil" do
test_method(34).should == 34
end
context "without Date.current available" do
before(:all) do
Date.stub(:today) { test_date }
end
it "returns Date.today when arg isn't present" do
test_method.should == test_date
end
end
context "with Date.current available" do
before(:all) do
Date.stub(:current) { test_date }
end
it "returns Date.current when arg isn't present" do
test_method.should == test_date
end
end
end
Running with rspec test.rb results in the tests passing.
Also, the stubs are present only in each context, so it doesn't matter what order the specs are run in.
This is more than a little perverse, but it should work. Include ActiveSupport, and then:
context "without ActiveSupport's Date.current" do
before(:each) do
class Date
class << self
alias_method :current_backup, :current
undef_method :current
end
end
end
# your test
after(:each) do
class Date
class << self
alias_method :current, :current_backup
end
end
end
end
I can't really recommend this; I would prefer to split out this one spec and run it separately as you suggested.

Ruby 1.9.3 JSON Parsing

I'm encountering a weird problem when parsing JSON with Ruby 1.9.3-p392 under RVM on CentOS 6.4. Instead of decoding embedded objects into their appropriate Ruby classes, it's just loading the object as a hash. In Ruby 1.9.3-p194 it works correctly.
Take the following sample:
require 'json'
class TestMe
attr_accessor :me
def initialize(option_hash = nil)
if option_hash
#me = option_hash['me']
end
#me ||= "Hello"
end
def to_json(*a)
{
JSON.create_id => self.class.name,
'data' => {
"me" => #me
}
}.to_json(*a)
end
def self.json_create(o)
new(o['data'])
end
end
t = TestMe.new
t.me = "foo"
t2 = JSON.parse(t.to_json)
puts t2
If I run this on Ruby 1.9.3-p194, it outputs the following:
#<TestMe:0x00000001c877f0>
If I run the same snippet on Ruby 1.9.3-p392, it outputs the following:
{"json_class"=>"TestMe", "data"=>{"me"=>"foo"}}
The behavior in p194 is what I expect and what the documentation implies. Why isn't p392 parsing the JSON data correctly?
Still not sure why/what changed, but I found a work-around. Basically, you need to construct a Parser object and pass in the :create_additions option, instead of just calling JSON.parse.
Example:
p = JSON::Parser.new(json_string, {:create_additions => true})
result = p.parse
As others have stated, it sounds like the recent change to the way JSON objects are unmarshelled. I ran into a very similar issue here and got a great answer.
JSON by itself will return a hash. There is an extension to it that gives it added capability. Try using:
require 'json'
require 'json/add/core'
I think at one point in past Rubies JSON automatically loaded the extensions but that was dropped for comparability with the JSON spec.
"add/core" includes some to_json methods to base objects and might add the capability to recover custom objects. I ran into a similar situation passing regular expressions via JSON and that was the fix.
I'm not near my computer so that isn't confirmed, but it might help.

Resources