Unload a Ruby Class

Unload a ruby class

Object.send(:remove_const, :Foo)

assuming your class is named Foo.

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

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

how do I unload & load a class?

Not sure it that is the problem, but in your application.rb you need to add the following line:

config.autoload_paths += %W( #{config.root}/lib )

And secondly, to make sure that classes/modules are found correctly on reload, the naming has to follow Rails conventions. This means that snake-casing a module or class name should give the filename, and different namespaces (or nesting) should be in different folders.

Some examples to make this more clear :)

class SomeClass     --> /lib/some_class.rb
class SomeHTTPStuff --> /lib/some_http_stuff.rb
class API::Stuff --> /lib/api/stuff.rb

HTH.

Force Class Loading and Unloading (RoR)

Since you tagged the question with Rails, then you can do

some_class_name.constantize

Example

"User".constantize
# => User

remove_const works, but you need to call it from the object space where the constant is defined. Also, remember to pass a symbol as the constant name.

Example

Object.send(:remove_const, :Foo)

Can I Unload a Rails Metal Class after one time use?

A simplest solution (although not quite as clean) would be to store the fact that an admin user exists in the class.

class EnsureAdminUser
def self.call(env)
if @admin_defined or Admin.any?
@admin_defined = true
[404, {"Content-Type" => "text/html"}, "Not Found"]
else

end
end
end

This saves you the DB hit on each request.

To actually delete the metal you will need to do something a bit more radical (and evil):

ObjectSpace.each_object(Rails::Rack::Metal){|metal_handler|
metal_handler.instance_eval{ @metals.delete(EnsureAdminUser) }
}

how to unload the files loaded by the require statement in rails

I have resolved that problem using the config.cache_classes = true in the environment.

Is it possible to unload an ActiveRecord association?

Part 1: Solution to the stated problem:

To empty the loaded association data, use the target method on the association:

location.state.cities.target=[]

If you access the cities now:

location.state.cities
=> []

Part 2: Clumsy way to avoid the problem:

Avoid loading the association while accessing cities. Use

self.state.cities.all 

Instead of

self.state.cities

Part 3: Something smells fishy:

Why are you storing objects in to flash? Flash is generally meant for sending text messages. If you assign non strings to flash you will run in to the cookie size limit in no time.

Also when you are validating why do you need to load all the cities? Does this mean you are validating in Ruby? Can you post the validation code. In most cases you can optimize this by moving validation to DB.

Edit: Extended the answer based on the comment

I follow this pattern in Rails 2.3.x. The variables I want to access in the view associated with edit action is always a subset of variables available in update action.

class ProductsController < ApplicationController

before_filter :init_data, :only => [:new, :create, :edit, :update,
:destroy, :show]
def update
@product.attributes = params[:product]
if @product.save
flash[:notice] = "Successfully saved the product."
redirect_to product_path(@product)
else
render :action => 'edit'
end
end

private
def init_data
switch(action.to_sym)
when :new, :create
@product = Product.new(params[:product])
when :edit, update, :destroy, :show
@product = Product.find(params[:id])
end
end
end


Related Topics



Leave a reply



Submit