I am implementing a poller service whose interface looks like this.
poller = Poller.new(SomeClass)
poller.start
poller.stop
The start method is supposed to continuously start hitting an http request and update stuff in the database. Once started, the process is supposed to continue till it is explicitly stoped.
I understand that implementation of start needs to spawn and run in a new process. I am not quite sure how to achieve that in Ruby. I want a ruby solution instead of a ruby framework specific solution (Not rails plugins or sinatra extensions. Just ruby gems). I am exploring eventmachine and starling-workling. I find eventmachine to be too huge to understand in short span and workling is a plugin and not a gem. So it is a pain get it working for Ruby application.
I need guidance on how do I achieve this. Any pointers? Code samples will help.
Edit
Eventmachine or starling-workling solution would be preferred over threading/forking.
Can't you use the example from Process#kill:
class Poller
def initialize klass
#klass = klass
end
def start
#pid = fork do
Signal.trap("HUP") { puts "Ouch!"; exit }
instance = #klass.new
# ... do some work ...
end
end
def stop
Process.kill("HUP", #pid)
Process.wait
end
end
class Poller
def start
#thread = Thread.new {
#Your code here
}
end
def stop
#thread.stop
end
end
Have you considered out the Daemons gem?
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. :)
I've got a codebase which is tested in two scenarios: run via entry point A, and B. When it's run via A, the db connection is used as is. When it's run via B, ActiveRecord::Base.connection is monkey patched.
Since B is just a helper script, it's currently tested in rspec by running it as an external command and checking the output. I'd like to bring some sanity back and test the behaviour without spawning new processes though.
Is there a way in rspec mocks to "temporarily extend" a class? I'd like to get the behaviour of doing:
before do
ActiveRecord::Base.connection.extend(App::SomePatch)
end
after do
ActiveRecord::Base.connection.unextend(App::SomePatch)
end
Of course unextend doesn't exist. I have only 3 methods to patch, so I could potentially use the mocks for each method instead, but a method alias makes this complicated.
The patch module looks like this:
module SomePatch
def SomePatch.included(mod)
alias :old_execute :execute
end
def execute(*args) ... end
def some_storage
#some_storage ||= []
end
end
I would go with cloning, something along this lines:
before do
#original_connection = ActiveRecord::Base.connection
ActiveRecord::Base.connection = #original_commention.dup
ActiveRecord::Base.connection.extend(App::SomePatch)
end
after do
ActiveRecord::Base.connection = #original_connection
end
I did not test that, but as long there are not "quirks" with cloning the object, this should be fine.
Edit: Ok, this does not work, because there's no connection= method, so you can probably try with mocking:
before do
#original_connection = ActiveRecord::Base.connection
new_connection = #original_connection.dup
new_connection.extend(App::SomePatch)
allow(ActiveRecord::Base).to receive(:connection).and_return(new_connection)
end
And you probably don't need after because the mock will be "undone"
I have a ruby app that uses ActiveRecord but not Rails.
I want to rescue from all database errors which in my case can include a SQLite3::BusyException.
Is there a better way than to wrap every Model.find, Model.where, obj.save, etc in a rescue?
I thought adding a module to every model that monkey patches/hijacks DB actions such as, but where appears complex and something not to be trifled with:
def where
super
rescue ActiveRecord::RecordNotFound
rescue SQLite3::BusyException => e
p [:warning, e.message]
end
When using Celluloid I ran into a similar issue where I needed to trap errors due to the connection pooling not working correctly with how Celluloid was using fibers. I used something like the following wrapper method to help make sure errors were trapped and resolved the connection reaping in my code. For your scenario, it could look like this:
module TrackActiveRecordErrors
def db(&block)
begin
yield block
rescue StandardError => e
p [:warning, e.message]
# ... and other sort of logging, alerting you'd like
raise e
ensure
# Put your clean-up code here
end
end
end
In classes you want to use this wrapper:
class DoSomething
include TrackActiveRecordErrors
def find_something(id)
db do
a_something = Model.find(id)
end
a_something
end
end
It's not pretty, but it's a lot easier than trying to tune AR's magic in the model classes.
I'm attempting to write tests around an application that makes heavy use of TCPSockets (an IRC bot to be specific). While writing my first class's tests, I had been skimping by with doing:
#In the describe block
before(:all) { TCPServer.new 6667 }
...which allowed for my TCPSockets to function (by connecting to localhost:6667), though they are not actually being properly mocked. However, this has now caused problems when moving onto my second class as I cannot create a TCPServer on the same port.
How can I mock the TCPSocket class in such a way that will allow me to test things such as its(:socket) { should be_kind_of(TCPSocket) } and other common operations like #readline and #write?
You could try keeping track and closing the TCPServer in your before and after:
before do
#server = TCPServer.new 6667
end
after do
#server.close
end
it ... do
end
it ... do
end
After each of the individual tests, the TCPServer is killed so you can create a new one with the same port.
I'm not quite sure if I understand your problem, but why don't you just install some kind irc server on your local machine? ircd-irc2, ircd-hybrid or something like that?
Suppose you have irc client implemented this way:
class Bot
attr_accessor :socket
def initialize
socket = TCPSocket.new("localhost", 6667)
end
end
You can then test it like this
let(:bot) { Bot.new }
it "should be kind of TCP Socket"
bot.should be_kind_of(TCPSocket)
bot.should be_a(TCPSocket)
end
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!