This is an example from Goliath:
require 'goliath'
class HelloWorld < Goliath::API
def response(env)
[200, {}, "hello world"]
end
end
How does defining a class and subclassing Goliath::API results in a web server being started? Shouldn't this just define a class, not actually instantiate and execute one?
Goliath uses at_exit, not unlike Sinatra, Minitest, etc.
See some relevant code here, which highlights the additional handling this trick sometimes requires.
Related
I built a Thor script that connects to an HTTP API to perform some very simple actions. I've coded tests for the backend but the Thor script is basically untested, which is quite suboptimal.
My first approach was to capture the output of the commands itself and write test against such output, the resulting tests are unsurprisingly slow.
expect(`bin/script foo`).to eq('bar')
I then tried to use both webmock and vcr but using this approach none of these frameworks are invoked, even if I mock the exact request the mock is unused, most probably because both webmock and vcr are unable to hook into the thor script.
Has anybody found a nice solution for this? Invoking the Thor script directly (Thorclass.action('bar')) would be enough for my taste, but I haven't found a way to do it.
Any suggestion? Thanks in advance.
Thor is a wrapper
I tend to see Rake, Thor and friends as another interface to your code
I keep my Thor/Rake code as tiny as possible
All production code is kept in a standard Ruby class
That means unit testing via VCR becomes dead easy
Also allows you to reuse your production code in another interface: e.g. a Rails controller
Example
Thor wrapper
bin/seed
#!/usr/bin/env ruby
require "thor"
class Seed < Thor
desc "budgets", "Seeds budgets"
def budgets
puts 'Seeding currencies...'
SeedBudgets.new.call
puts 'Done.'
end
end
Seed.start
For more details on command line Thor see this excellent walkthrough
Production code
lib/services/seed_budgets.rb
class SeedBudgets
def initialize
# I find an initialize helpful for injecting dependencies
end
def call
# Code goes here
end
end
Unit tests
test/services/seed_budgets_test.rb
require 'minitest/autorun'
require 'vcr'
VCR.configure do |config|
config.cassette_library_dir = 'fixtures/vcr_cassettes'
config.hook_into :webmock
end
class SeedBudgetsTest < Minitest::Test
def test_seeds_one_budget
VCR.use_cassette('one_budget_from_api') do
SeedBudgets.new.call
assert_equal 1, Budget.count
end
end
end
That will allow you to decouple the command line interface from the actual code.
Then Thor becomes a very thin wrapper around your actual code.
Feel free to post more detailed code and I can help more. :)
My background is Java and I am new to Ruby. I saw a mocking/stubbing framework called Mocka. I saw this example test method:
require 'test/unit'
require 'mocha/test_unit'
class MiscExampleTest < Test::Unit::TestCase
# ...
def test_mocking_an_instance_method_on_a_real_object
product = Product.new
product.expects(:save).returns(true)
assert product.save
end
#...
end
What mechanism was used to "automatically" create a mock object of Person class (or object)? Not sure what to Google for.
If it was something like this
product = mock(Product.new)
I'd easily get it.
Thank You! :)
in general, this is referred to as "monkey patching".
ruby has the concept of open classes, so at runtime you can mess around with it.
in the specific case of mocha, i assume that it is this piece of code here: https://github.com/freerange/mocha/blob/a7bc1b53ace895503b4b5d4915382aead4632e3e/lib/mocha/api.rb#L18-L22
Spec helper:
def app
Sinatra::Application
end
App:
require 'sinatra'
get '/' do
track_request
"Welcome"
end
def track_request
puts self.inspect
....
end
How to mock track_request?
allow(app).to receive :track_request
allow_any_instance_of(Sinatra::Application).to receive :track_request
allow(Sinatra::Application).to receive :track_request
None of these worked.
self is main, so I guess we need to mock a method on main. But didn't find out how to do it.
Maybe not possible? (so far I'm stumped)
After reading this blog post: http://www.philandstuff.com/2012/02/12/surprises-while-testing-sinatra-controllers.html
It looks like Sinatra dups your controller before handling an HTTP request.
From 'sinatra/base.rb'
# Rack call interface.
def call(env)
dup.call!(env)
end
It seems like this would be a common thing to do when testing a Sinatra app. I'm surprised there isn't a clear solution.
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'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!