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

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.

Related

Display all constants in 'Digest'

I would like to know how to see every constant available on the Digest module in advance programmatically. The behaviour seen below is due to const_missing used here:
require 'digest'
Digest.constants
#=> [:Class, :REQUIRE_MUTEX, :Instance, :Base]
Digest::MD5
Digest.constants
#=> [:Class, :REQUIRE_MUTEX, :Instance, :Base, :MD5]
Digest::SHA1
Digest.constants
#=> [:Class, :REQUIRE_MUTEX, :Instance, :Base, :MD5, :SHA1]
Given that they are metaprogramming in possible digests, how can I know all possible available digests?
For knowledge, the ones that appear to be available in Ruby 2.4.1 should be [:SHA256, :SHA384, :SHA512, :SHA1, :SHA2, :MD5, :RMD160]
Here's a code snipped from the current master branch of ruby:
module Digest
def self.const_missing(name) # :nodoc:
case name
when :SHA256, :SHA384, :SHA512
lib = 'digest/sha2.so'
else
lib = File.join('digest', name.to_s.downcase)
end
begin
require lib
rescue LoadError
raise LoadError, "library not found for class Digest::#{name} -- #{lib}", caller(1)
end
unless Digest.const_defined?(name)
raise NameError, "uninitialized constant Digest::#{name}", caller(1)
end
Digest.const_get(name)
end
# ...
end
...So, you can't really list all constants without knowing them already! You need to either require the necessary file, or reference the constant directly (which will load the file dynamically, as seen above).
Any workaround solution I could give would only be guaranteed to work for a specific version of ruby. You'd be better off to just read the documentation and load each library explicitly, unfortunately!

How can I mock something that "does not implement" a particular method?

The Background:
I'm trying to use cucumber to do some test-driven (or behavior-driven) development around an interface to AWS, in ruby.
So, I have a step definition that looks like this:
Then(/^the mock object should have had :(.*?) called, setting "(.*?)" to "(.*?)"$/) do |method, param, value|
expect(#mock).to receive(method.to_sym).with(hash_including(param, value))
end
Where #mock was previously set using:
#mock = instance_double(AWS::AutoScaling::Client)
And where I invoke this step definition with a feature line like:
And the mock object should have had :update_auto_scaling_group called, setting "auto_scaling_group_name" to "Some-test-value"
When that step gets run, it gets the following error (leaving out the full error, as I believe this is the most relevant part):
AWS::AutoScaling::Client does not implement: update_auto_scaling_group (RSpec::Mocks::MockExpectationError)
I see that indeed, the checks that RSpec runs (as traced back from where the RSpec::Mocks::MockExpectationError gets thrown) are at least correctly reporting the information that they get from the class:
[1] pry(main)> require 'aws-sdk'
=> true
[2] pry(main)> klass = AWS::AutoScaling::Client
=> AWS::AutoScaling::Client
[3] pry(main)> klass.public_method_defined? "update_auto_scaling_group"
=> false
[4] pry(main)> klass.private_method_defined? "update_auto_scaling_group"
=> false
[5] pry(main)> klass.protected_method_defined? "update_auto_scaling_group"
=> false
And yet, if we ask an actual instance, it lets us know that this is a method it would respond to:
[6] pry(main)> x = klass.new
=> #<AWS::AutoScaling::Client::V20110101>
[7] pry(main)> x.respond_to? "update_auto_scaling_group"
=> true
Even while it doesn't say that about just anything:
[8] pry(main)> x.respond_to? "bogus"
=> false
First questions:
So... is this a bug in the AWS::AutoScaling::Client code (or really, probably here), for not defining the methods in a way that the extant checks ({public,private,protected}_method_defined?) would come back true?
Or perhaps a bug in RSpec's "doubles", for not doing all the checking it could do to try to find out that this is indeed a method that's callable in an instance of that class?
Or perhaps it's simply something that I'm doing wrong here? Other?
More generally:
How can I write tests for the code I'm writing, to ensure that it's making calls to what will be an AWS::AutoScaling::Client instance, with the correct parameters (as defined in several checks that I have)? Are there alternate ways I can write my step definitions that would make this work? Alternative ways to create my mock objects? Other?
I've found a way to dynamically mix in the methods I needed to mock
You could do this with empty methods and then stub them, or just include the stubs in the mixin
require 'rails_helper'
RSpec.describe "users/sessions/new.html.erb", :type => :view do
it "displays login form" do
module DeviseUserBits
def resource
#_DeviseUserBitsUser ||= User.new
end
def resource_name
:user
end
def devise_mapping
Devise.mappings[:user]
end
end
view.class.include DeviseUserBits
render
expect(rendered).to match /form/
end
end
It just adds methods on/after instantiating. It's pretty legal, all ruby classes/objects are open.
Proper answer - you do not want to test what you are trying to test in duck-typed language with open classes and objects. It just does not make sense.
The version 1 AWS SDK for Ruby uses #method_missing as a delegate for building and sending requests. The methods a client responds to are defined in an API definition. This eliminates boiler-plate code, but causes problems if you are trying to reflect the available methods at runtime.
Option A: Use a regular double and apply your assertions on the test double.
Option B: Use the mocking feature of the SDK via AWS.stub! When stubbing is enabled, all clients constructed will respond to their regular methods, but will return dummy responses (empty hashes and arrays). This approach provides the useful ability to specify the data to return from a stub. You can even create a stub response for the express purpose of returning from an assertion.
Going with Option B:
# use `:stub_requests` or call Aws.stub!
as = AWS::AutoScaling::Client.new(:stub_requests: true)
# validates parameters as normal, but returns empty response data
as.update_auto_scaling_group(auto_scaling_group_name: 'name')
#=> {}
# You can access the stub response for any operation by name:
stub = as.stub_for(:describe_auto_scaling_groups)
stub.data[:auto_scaling_group_names] = ["Group1", "Group2"]
# Now calling that operation will return the stubbed data
resp = as.describe_auto_scaling_groups
resp.auto_scaling_group_names
#=> ['Group1', 'Group2']
If you need to assert a method is called against the client, you can do so normally, returning the stubbed response:
expect(#client).to receive(:describe_auto_scaling_groups).
with(hash_including(param, value)).
and_return(#client.stub_for(:describe_auto_scaling_groups))

How to know if I have required a library

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.

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.

More natural way of Proc calling in Ruby 1.9

As we know, there are several way of Proc calling in Ruby 1.9
f =->n {[:hello, n]}
p f[:ruby] # => [:hello, :ruby]
p f.call(:ruby) # => [:hello, :ruby]
p f.(:ruby) # => [:hello, :ruby]
p f === :ruby # => [:hello, :ruby]
I am curious, what is more 'natural' way of calling Proc? 'Natural', probably, means more Computer Science - like way.
The second option is by far the most used.
p f.call(:ruby) # => [:hello, :ruby]
It makes it more similar to a standard method. Also, some libraries actually rely on duck typing when validating arguments checking the availability of the #call method. In this case, using #call ensures you can provide a lambda or any other object (including a Class) that responds to #call.
Rack middlewares are a great example of this convention. The basic middleware can be a lambda, or you can supply more complex logic by using classes.
I always use option 3. Considering the syntactic ambiguities of being able to call methods without parentheses, this is the closest you can get to actual method call syntax.
I saw the first way used in Rack source code. It confused me in a long time. It's picked from lib/rack/builder.rb (version: 1.6.0.alpha)
module Rack
class Builder
...
def to_app
app = #map ? generate_map(#run, #map) : #run
fail "missing run or map statement" unless app
# This is the first option calling a proc
# #use is a array of procs (rack middleware)
#use.reverse.inject(app) { |a,e| e[a] }
end
...
end
end

Resources