I'm currently developing a framework that basically executes another application, e.g. rails within the context of another ruby program. My initial attempt was simply to boot the app like this:
def load_app!
# Load the rails application
require './config/application'
# Initialize the rails application
#app = App::Application.initialize!
end
Problem here, is that the framework's requires conflict with the loaded application so the initialize! call never works although it would in a normal ruby program.
So my question is, if anyone knows a method to basically scope this calls into a unit that behaves like a blank RVM environment. So basically a behavior like this:
require 'json'
puts JSON.generate({:name => "test"})
blank_environment do
puts JSON.generate({:name => "test"})
#=> uninitialized constant JSON
require 'json'
puts JSON.generate({:name => "test"})
end
It's not done with undefining or unloading the currently loaded constants because I don't know all of them because I'm using gems that have other dependencies again.
So is there a cool way? Or any other way to handle this?
UPDATE:
Just came across an idea. Why is ruby's require method always requiring for the global scope? Wouldn't it be a very nice feature to actually scope the loaded modules under the the current module?
module ScopeA
require 'json' #> adds support for ScopeA::JSON
# due to normal ruby scoping everything can be called like normal in here
JSON.parse("something")
end
# JSON should not be available here
module ScopeB
require 'yaml'
YAML.parse("something") # but no JSON, of course
end
Doesn't something like this exist? include already has to know the constants...
Thanks in advance!
Well, after some more research it really doesn't seem possible the way I need it.
I now implemented a basic version using distributed ruby, which doesn't quite satisfy me:
require 'drb/drb'
URI = "druby://localhost:8787"
# Blank environment
pid = fork do
Signal.trap("INT") { puts "Stopping Server.."; exit }
class Application
def call(env)
[200,{},""]
end
end
DRb.start_service(URI, Application.new)
DRb.thread.join
end
# Working environment
DRb.start_service
app = DRbObject.new_with_uri(URI)
puts app.call({})
Process.kill("INT", pid)
Process.wait
If anyone comes up with a better approach, it's highly appreciated!
Related
I am trying to create performance automation framework for my company . Beinbg newbie to ruby field , I thought to keep it simple . Here is structure for performance framewrok
I do have multiple ruby files , as request.rb,payload.rb etc in 'common' folder(as these are containing some utilities) and then my test in test.rb (under one of 'TestFlows->SimpleFlow->test.rb) .
See above structure for more detail
Exact Code , which I am having right now under those files are
request.rb
require 'ruby-jmeter' #(Consider any 3rd party gem )
require 'rubygems'
module Company #Note that i am using same module name 'company'
module Request
def self.send_request()
visit '192.148.1.2' # this is function defined under ruby-jmeter
end
end
end
payload.rb
require 'ruby-jmeter' #(Consider any 3rd party gem )
require 'rubygems'
module Company #Note that i am using same module 'company'
module Payload
def self.get_payload()
------- Again some 'ruby-jmeter' function calls
end
end
end
etc files as well
Test.rb
require 'ruby-jmeter' #(Consider any 3rd party gem )
require 'rubygems'
require 'require_all' #(gem to load all files from folder)
require_all '../../common/'
module Company #Note that i am using same module 'company'
test name:'GooglePerformanceTest' do
defaults domain: 'http://google.com' ,protocol: http
threads name: 'NoOfUsers',scheduler: false,continue_forever:
false, count: 2 do
Request.send_request()
end
end #end of testPlan
view_results_tree
puts "JMX FILE IS GONNA SAVED #
"+Dir.pwd+"/CreatedJMeter_DB2Navigation.jmx"
end.jmx(file: Dir.pwd+"/CreatedJMeter_DB2Navigation.jmx")
end
When I run this programme , it goes to above , I am getting errors of those (3rd party ruby gem's function undefined).
Can anyone point me out my problem regarding above structure & suggest me proper way to do this ?
edit :
I am getting below error
Test.rb:3:in `send_request': undefined method `visit ' for
company::Request:Module (NoMethodError)
Edit
Have defined my changes here & it is working fine
Facing issues while calling method of module(having object of class , contained in another ruby file)
I'm assuming you're using the gem hosted at this repository: https://github.com/flood-io/ruby-jmeter, latest release version (2.13.8).
Looking through the source code and examples, there are a couple things to think about.
The 'visit' method is an alias for a method called 'get' defined here
.
That is an object method, for an object of class RubyJmeter::ExtendedDSL. You are using module methods, and should probably consider making an object and calling the method on that.
obj = RubyJmeter::ExtendedDSL.new
obj.visit '192.148.1.2' # 'visit' is a method alias for 'get'
However, the examples listed by the gem developer follow a different pattern, calling 'visit' from within a closure (the do..end code block, known simply as a 'block' in Ruby). You could rewrite your code to build test plans and run them in this fashion.
It's not clear from what you post why you are using modules. Typically, you use 'include' within a Module or Class to mix in the gem's methods into your namespace. But since you're attempting to use module methods, I'm not sure mixing in object methods will be fruitful.
Since I can't see how your Test.rb file is run, I can't say for sure that your test code is loading your own modules. Typically a 'load' or 'include' statement would be used to load the code you wrote in request.rb and payload.rb
Personally, I'd try to follow the pattern shown by the developer of RubyJmeter; as a second approach, I'd write a class that inherits from RubyJemeter's subclass and extend its behavior to suit. My test code would initialize an object and call its methods directly. (Your Mileage May Vary.)
Updated to add: if you want your module method to stand in place of the object method mentioned, you can simply call the latter inside your method:
def self.send_request()
RubyJmeter::ExtendedDSL.new.visit '192.148.1.2'
end
Doing this essentially creates a disposable object, which is destroyed after the :visit method returns its data.
P.S. Always capitalize your class and module names. It's a Ruby best practice and your module name 'company' will raise warnings from the interpreter.
I have an util method into a Sinatra application and I would like to tested from my TestCase.
The problem is that I don't know how to invoke it, if I just use app.util_method I have the error NameError: undefined local variable or method 'util_method' for #<Sinatra::ExtendedRack:0x007fc0c43305b8>
my_app.rb:
class MyApp < Sinatra::Base
# [...] routes methods
# utils methods
def util_method
return "hi"
end
end
my_app_test.rb:
require "my_app.rb"
require "test/unit"
require "rack/test"
class MyAppTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
MyApp.new
end
# [...] routes methods tests
def test_util_method
assert_equal( "hi", app.util_method )
end
end
Sinatra aliases the new method to new! before redefining it, so the simplest solution is to use that instead:
def app
MyApp.new!
end
Of course I only noticed that after I’d come up with the following, which I’ll leave in as it could be useful/informative.
A possible way to get round Sinatra redefining the new method and returning a complete Rack app a get hold of an instance your actual base class is to do what the “real” new method does yourself:
def app
a = MyApp.allocate
a.send :initialize
a
end
This is a bit of a hack, but it might be useful for testing.
Another technique would be to “walk” the middleware stack until you got to your class. The following is a little fragile, as it depends on all the middleware involved to use the name #app to refer to the next app in the stack, but this is fairly common.
def app
a = MyApp.new
while a.class != MyApp
a = a.instance_variable_get(:#app)
end
a
end
That won’t work on the yet to be released Sinatra 1.4 though (at least not on the current master, which is commit 41840746e866e8e8e9a0eaafc53d8b9fe6615b12), as new now returns a Wrapper class and the loop never ends. In this case you can grab the base class directly from the #instance variable:
def app
MyApp.new.instance_variable_get :#instance
end
(note this last technique may well change before the final 1.4 release).
The problem you are encountering is, that MyApp.new does not return an instance of MyApp but an instance of the middleware wrapping your App (usually Rack::Head or Sinatra::ShowExceptions). A good explanation can be found in the thread Sinatra Usage Question / Rack App.
The only solution I can think of is to change your instance method to a class method which can be called without the instance itself. As the instance of your App may be freshly instantiated for every request, an instance method probably doesn't have much advantages over a class method in your scenario.
Edit:
In the upcoming Sinatra 1.4 the initialization will change. Sinatra::Base.new will return a Sinatra::Wrapper instance, which exposes #settings and #helpers. This may help solve the problem of accessing Sinatra::Base instance methods. See the Sinatra Changelog for more information.
I saw the following source code in someone's repository:
module Twitter
module Bootstrap
module Rails
require 'twitter/bootstrap/rails/engine' if defined?(Rails)
end
end
end
require 'less-rails'
require 'twitter/bootstrap/rails/bootstrap' if defined?(Rails)
Source
I want to know what's difference when we put require in a module?
There is no difference as far as the require is concerned, i.e., require always loads the file into the global namespace.
It should be noted that, in this case, the inner require will always run, since Rails at that point refers to the module it is within, so the if statement there will always evaluate to true.
This means the code is equivalent to the possibly less confusing:
module Twitter
module Bootstrap
module Rails
end
end
end
require 'twitter/bootstrap/rails/engine'
require 'less-rails'
require 'twitter/bootstrap/rails/bootstrap' if defined?(Rails)
I've noticed in some gems, when you simply require 'some_gem', methods will appear (without any monkey patching to my knowledge). I've seen it in some gems like Sinatra, Rake, Rails, and many other helper libraries and such. How would one manage to accomplish this in ones own library?
Example:
require 'sinatra'
# Automatically recieve the 'get' method
get('/') { "I was monkeypatched or included automatically." }
If it is monkeypatching, what classes/modules are common for monkeypatching (other than String, Numeric, Array, etc).
Sinatra is essentially adding those as global methods. When you require sinatra, it extends the Object class with Sinatra::Delegator which is defined in sinatra/base.rb. Methods such as get and put are defined in base, and added via the delegator.
In addition to Beerlington's answer, Rails, for example, and specifically it's part ActiveSupport, uses exactly monkeypatching.
For example, declaration of blank? method from the ActiveSupport source (stripped):
class Object
def blank?
respond_to?(:empty?) ? empty? : !self
end
end
Also, very common approach to monkeypatch Kernel module to add methods that will be available everywhere:
# hello.rb
module Kernel
def say_hello
"Hello!"
end
end
And usage of it:
require 'hello.rb'
=> true
say_hello
=> "Hello!"
I'd like a very basic example of a tiny base program, that reads in two plugins and registers them. These two plugins hook into the base program in the same way in a unconflicting manner.
I'm very new to metaprogramming in any programming language for that matter, I'm not sure where to start.
i've been working on this very problem for a while now. i've tried a number of different ways to go about doing it and sought advice from a lot of people on it. i'm still not sure if what i have is 'the right way', but it works well and is easy to do.
in my case, i'm specifically looking at configuration and bringing in configuration plugins, but the principle is the same even if the terminology for mine is specific to cnfiguration.
at a very basic level, i have a Configuration class with nothing in it - it's empty. I also have a Configure method which returns the configuration class and lets you call methods on it:
# config.rb
class Configuration
end
class MySystem
def self.configure
#config ||= Configuration.new
yield(#config) if block_given?
#config
end
Dir.glob("plugins/**/*.rb").each{|f| require f}
end
MySystem.configure do |config|
config.some_method
config.some_value = "whatever"
config.test = "that thing"
end
puts "some value is: #{MySystem.configure.some_value}"
puts "test #{MySystem.configure.test}"
to get the some_method and some_value on the configuration class, I have the plugins extend the configuration object via modules:
# plugins/myconfig.rb
module MyConfiguration
attr_accessor :some_value
def some_method
puts "do stuff, here"
end
end
class Configuration
include MyConfiguration
end
and
# plugins/another.rb
module AnotherConfiguration
attr_accessor :test
end
class Configuration
include AnotherConfiguration
end
to load up the plugins, you only need one of code to look for the .rb files in a specific folder and 'require' them. this code can live anywhere as long as it's run right away when the file that contains it is loaded... i would probably put it in the class definition for MySystem or something like that to start with. maybe move it somewhere else when makes sense.
Dir.glob("plugins/**/*.rb").each{|f| require f}
run config.rb and you'll get output that looks like this:
do stuff, here
some value is: whatever
test that thing
there are a lot of options for implementing the various parts of this, but this should get you down the path.
It looks like this project may help: https://github.com/eadz/plugman
I haven't however found anything that will handle embedded (gem) dependencies however. Including Ruby files is straightforward but once your plugins start requiring other libraries then this simple model falls apart (either all the deps have to be installed with your application, or you need some other mechanism to get the plugin dependency gems into the process).