I've looked at the documentation for ActiveSupport::Notifications but I'm a little unclear about how to use it. Here's what I have right now:
class Foo
...
def xyz
# other stuff
ActiveSupport::Notifications.instrument(:interesting)
expensive_operation(123, 'apple') # We want to instrument this.
end
end
end
What I would like to do is have another class, Auditor, invoke puts "event #{event}" whenever the interesting event is raised. How do I do that?
A good resources is from railscast.com
http://railscasts.com/episodes/249-notifications-in-rails-3
Another resource that you can take a look at is Jose Valim's book Crafting Rails Application.
Here are a few good resources to get you started:
Using ActiveSupport::Notifications and ActiveSupport::Concern To Create An Audit Trail
Digging Deep with ActiveSupport::Notifications
ActiveSupport::Notifications API Documentation
Active Support Instrumentation Rails Guide - This contains a list of all available notifications that you can subscribe to.
RailsCasts #249 - Notifications in Rails 3
Related
I'm building a platform in Rails that will act as a basis for a few other Rails applications. Ideally I would like this to be a shared engine, on which the other concrete applications can build.
It would be nice if the applications could extend the base classes that the engine provides. AFAIK this can be done using monkey patching, but that feels 'hacky' to me.
I stumbled onto what looked like the solution, which was to just create 'mirror' classes in the main Rails app, which extend those of the engine:
# SharedEngine/models/shared_engine/post.rb
module SharedEngine
class Post < ActiveRecord::Base
def hello
"Hello"
end
end
end
# App/models/shared_engine/post.rb
require SharedEngine::Engine.root.join('app', 'models', 'shared_engine', 'post')
class Post < SharedEngine::Post
def hello
super + " world"
end
end
However, it looks like there are some autoloading problems. After server startup, it prints "Hello". Then after I save the app model, it says "Hello world".
The Rails engine guide suggests putting shared code into concerns. Is there any other way to get this working cleanly without having to create concerns for every class?
Since you need to create a basis for a few Rails apps to reduce boilerplate i suggest having a look a Rails Application Templates. Using templates you may provide all the necessary scaffolding without monkey-patching any class.
Background
I'm doing jRuby tests for a Grails app that sends emails. These tests are replacing tests that are normally done by manual testers. They check their inboxes to see that mail has been delivered.
Problem Statement
I want to do a similarly strong test that ensures the email is sent properly by the app.
I do not need any actual code to help do this necessarily, but it would help to know what technologies to use.
Why I've Been Stumped So Far
I've looked for solutions in the Ruby domain, but everybody that I've seen address this problem seems to be using Rails. I am not using action_mailer or pony (we have a grails app) so I can't use email_spec. If I could that would be great.
What can I do?
You can use the Rails approach to testing mails: monkey-patch mail send code during test setup to place all mail instances that should have been delivered into a globally accessible array, this way you can do asserts on that array.
Don't ask me for exact implementation details because I'm not into grails, but the general principle is this:
if you have a class MyMailThingy which has a deliver method on it's instance which performs the mail delivery (presumably mail subject, body etc are attributes on this object), then monkey patch:
class MyMailThingy
# same as `cattr_accessor :mock_queue` under rails
def self.mock_queue; ##mock_queue; end
def self.mock_queue=(arg); ##mock_queue=arg; end
def deliver
self.class.mock_queue ||= []
self.class.mock_queue << self.dup
self
end
end
and then in your tests you can assert on MyMailThingy.mock_queue how many mails have been sent, assert on their bodies, subjects, to/from fields etc without sending actual mails
I found an answer on how to send the mail here. It seems like Ruby has built in support for SMTP. I am looking at how to check the mail now.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
I need a suggestion on how to implement "client-server" web apps in ruby. Any guides and best practices are highly appreciated. I'm interested both in Ruby ways and gems required as it is a desired platform, and in general ways and logic to implement such things.
I'm not an excellent ruby programmer or a skilled system designer with years of expirience sadly so I really need your help as I still hope this thing would shine in the end.
The current look of the application should be like this:
DB + Auth DB <-> API App <-> Other Apps:
DB - a database or set of databases with one DB for group of users (regions).
Auth DB - one more DB with personal users' data and login information, probably single even if main databases would be split between regions.
API App - this thing maintains all the logic around data, access control and translations probably to have one translations base for all the apps.
Other Apps - bunch of different applications, tied with API, this could be data providers that pokes API with some data about some user, and UI of different kinds. All of the apps could not have own storage of user-related info and work with the data only through the API.
API App: looks like the best tool to make it is Sinatra. The questions are:
How to organize API in the clear way? Rails makes nice REST path settings and folders structure for models and controllers. Any tips or gems to improve API building expirience?
How to maintain access? Warden isn't looks like a good option as API clients would be the web apps themselves. So I need some kind of auth token. How could it be done? Some sort of custom OAuth provider? The thing is, I don't like the idea to store and pass session cookies through the API, some sort of access token passed with each request. This also
Other Apps: mainly web-based UIs. The logical selection for this part is Rails. The main question is how to implement client-side auth check. Devise is cool, but is it possible to make it work with token, or is it more suitable tool?
Okay, this is going to be a bit longer:
If you are already familiar with Rails, you can have a look at the Rails API gem. The project strives to remove additional cruft from Rails which is not needed for a JSON-based RESTful API.
This may sound smooth, but it has it's downsides. First and foremost, you have the readd everything that is needed for basic rails functionality, that you may be accustomed to, e.g. respond_to. This can be a bit tricky, but is pretty straightforward when you find out which rails module originally provided the functionality usually bundled in Rails within the ActionController::Base.
That being said, let me give you an example of what i did for a small API project I did last week out of curiosity:
Initial Situation
We have a mainly finished Rails App. It all works fine, but it is basically monolithic. Everything is served by the Rails Framework. Luckily for us, all the model logic is bundled into a gem called core. the application essentially lets logged in customers create products which are than searchable through an end user view.
The goal was to provide a RESTful API for this, which can handle concurrency and larger data files (i.e. CSV, XLS) a little bit more efficiently.
Introducing Rails API
The design goal let me to the Rails API gem. The basic installation works like in Rails, except the script is called rails-api, i.e.:
rails-api new jsonapi
The advantage for me here was that I could use the core from the other application, but nothing would stop me from just introducing my own models to the jsonapi application.
That being said, you can do all the standard Rails goodies, like routing, etc. It follows the same convention. then again, standard routes initially react to JSON only, which can be a bit confusing at times.
Let me give you an example of the API Controller that handles products:
class ProductsController < ApplicationController
include ActionController::HttpAuthentication::Token
before_filter :find_product, :except => [:create, :index]
def index
render :json => #products
end
def create
#product = product.new params[:product]
if #product.save
render :json => #product, :status => :created
else
render :json => #product.errors, :status => :unprocessable_entity
end
end
def show
render :json => #product
end
def update
if #product.update_attributes params[:product]
render :json => #product, :status => :ok
else
render :json => #product.errors
end
end
def destroy
if #product.destroy
render :json => #product, :status => :ok
else
render :json => {:note => I18n.t("messages.deletion_impossible")}, :status => :unprocessable_entity
end
end
protected
def find_product
#product = Product.find params[:id]
end
end
It is nothing special. The only thing to note is the second line where ActionController::HttpAuthentication::Token is included explicitly. This is so that your aPI may be secured by HTTP Token. If you want to know more about securing an API, I suggest Ryan Bates' Guide on Railscasts.
In essential, you provide a before filter in the ApplicationController like this:
class ApplicationController < ActionController::API
include ActionController::HttpAuthentication::Token::ControllerMethods
[...]
before_filter :restrict_access
[...]
def restrict_access
authenticate_or_request_with_http_token do |token, options|
# see if key is valid.
end
end
end
Again, note the second line, you have to include the ControllerMethods manually, otherwise no controller will know about authenticate_or_request_with_http_token.
Extension
You may know extend the API based on the Rails conventions. It works exactly the same way, with the exception that some stuff is intentionally missing by default. I suggest adding JBuilder (Railscast), if you need more flexibility in your JSON Templates.
Great, but what about clients?
Personally, there is a lot of choice when it comes to clients. Ultimately I find that it comes down to what you like most. I can personally recommend a small node.js layer on top of the Rails API which then gets a single page application based on backbone.js in front of it. You can also try AngularJS if you like. You can also build another Rails App around it and call the API from within your controller actions.
It also depends on what platform you want to target - a native application for iOS/Android comes to mind.
The choice I made was node.js + backbone. It currently makes the most sense for me at the time and for the project. The node layer essentially holds the Token necessary to communicate with the API and the backbone application has a small library to talk with the node layer. It can however be a double edged sword, depending on how complex your API will be. For a small example this seems to be fine, but there can be a lot of code duplication just to put the calls from the backbone application through to the Rails API.
Authentication
For authentication you can make customer based API-Keys (Tokens) and then limit the controller logic to only accept data operations which are allowed with that key. You could than manage the session via the node layer. Edit: This is authorization, not authentication. Nothing actually stops you from using Authlogic with Rails API - i have not tested it, but it should work.
I confess that i have not finished this part yet - I hope others can answer this architectural question :-)
I hope I could provide some insights.
P.S.: If you want to test your API i highly recommend httpie (It's awesome!)
According to this Ars Technica tutorial, "the streaming API makes it easy to detect when the user gets a new follower. To detect follow event objects, look for the "event" key and check if it has the string "follow" as the value."
http://arstechnica.com/information-technology/2010/04/tutorial-use-twitters-new-real-time-stream-api-in-python/2/
The tweetstream gem supposedly exposes all methods from the API. However I can't figure out, how to get to those follow requests!
Any ideas?
The tweetstream gem is fairly complete, but sometimes you need to delve into the source to find stuff:
client.on_event(:follow) do |event|
p event[:source][:screen_name]
end
https://github.com/intridea/tweetstream/blob/master/lib/tweetstream/client.rb#L375 :)
How do I access the app instance so I can test the foobar() method?
class App < Sinatra::Base
get '/' do
return foobar
end
def foobar
"hello world"
end
end
This is a late reply, but I am trying to find a flexible way to do that too.
I found in Sinatra's source code (1.4.5) that creating an instance of the app with new! allows testing the app's methods directly. Exert from a test setup with Test::Unit and Shoulda.
class AppTest < Test::Unit::TestCase
setup do
#app = App.new! # here is the point.
end
should 'say hello to the world' do
assert_equal "hello world", #app.foobar # or #app.send(:foobar) for private methods.
end
end
There are consequences. Using new! does not create the usual Sinatra::Wrapper that becomes the entry point to the middleware pipeline and the app in normal settings. So the new! approach will work only if the tested methods are really "helpers" that do not rely on middleware functionalities (e.g. SSL).
Alternatively, a post on Rspec proposes an alternative solution. I used something similar in the past, but it requires more work that is not always the best choice. It had the advantage to offer broader coverage of code to test. The isolation of the app with new! sounds good though, if we are taking about "unit" testing.
Note on #three's comment: A non-trivial app should separate API methods (usually in the app) from all helpers, etc. Helpers ending up in a separate file are cleaner, easier to maintain, and easier to test. But I definitely understand cases where a first version of an app would include a few helpers, with awareness that refactoring will be necessary. And even then having tests brings in some more confidence in the software itself, and in the future refactoring as well.
It doesn't matter what you test - it is how :) => http://www.sinatrarb.com/testing.html