Posted on 06 Dec 2012
Over the past few weeks I wrote a client for a hypermedia api that I created. I think I ended up coming across a pattern that I like in regards to how it’s structured.
I have three basic object types: services, loaders, and resources.
The http client used is the gem Farday.
Services
A service is an object that performs an action. For example I have one service that registers users.
class UserRegistrationService < Struct.new(:email, :password)
def perform
client.post(user_registration_url, {
:user => {
:email => email,
:password => password
}
}
end
end
Resources
A resource is a data only object. For example I might have a resource for an order.
class Order
def self.load(json_string)
new(JSON.parse(json_string))
end
def initialize(attrs)
@email = attrs.fetch("email")
@links = Links.new(attrs.fetch("_links"))
end
end
Loaders
A loader is an object that loads up resources for me, going through any of the hypermedia bits that might be necessary. For example I might have a loader that gets a list of orders.
class OrdersLoader < Struct.new(:user)
def load
client.basic_auth(user.email, user.password)
response = client.get("/")
root = Root.load(response.body)
response = client.get(root.links.fetch("orders"))
Orders.load(response.body)
end
end
In the end I was pretty happy with how this structure ended up and was able to fairly quickly come up with an app that used it.
Posted on 28 Nov 2012
This post was originally published on the
SmartLogic Blog.
At RubyConf, there’s so much more to dig into after every session. From apps, to people, to blog posts, I started to keep tracks of links I’d like to follow up on after the conference. I thought I’d share them here, for everyone’s reference. I’m updating from the conference, as quickly as I can manage.
Please feel free to comment if you’d like to add a link to the list.
From Day 1:
- Rubinius : Use Ruby
- odrk-http-client/perf/bm.rb at master - asakusarb/odrk-http-client
- Ruby HTTP clients features 2012nahi/httpclient
- MagLev
- Refinements in Ruby - Timeless
- Librato Metrics
- Home | Metrics
- Operational Intelligence, Log Management, Application Management, Enterprise Security and Compliance | Splunk
- amatsuda/rspec-refinements - GitHub
- eric/metriks
- Code Climate. Hosted static analysis for Ruby source code.
- RubySpec: The Standard You Trust
- Jesse Storimer
From Day 2:
- The Pragmatic Bookshelf | The dRuby Book
- tenderlove/tusk
- Improve your Code Quality at RubyConf 2012
- Celluloid: Actor-based Concurrent Objects for Ruby
- Ruby, Rails, DCI and OOP. Don’t just make abstractions, write clean, intention-revealing Ruby. Clean Ruby by Jim Gay teaches about OOP, DCI, and more!
- Ruby Under a Microscope - Pat Shaughnessy
- Unlimited Novelty
- 37signals/sub
- tdiary (tDiary)
- T by sferik
- Schneems - UT on Rails
- Resque Triage
- josephwilk/creative-machine
- burtlo/metro
From Day 3:
- Videos - WindyCityRails
- Gosu, 2D game development library
- Sikuli Script - Home
- tenderlove/racc
Reach out on Twitter @ericoestrich if you’d like to catch up at RubyConf, or ask a question about any of these links.
Posted on 21 Nov 2012
let
in rspec is one of my favorite features. The decent_exposure is similar to let
, but it ends up doing way more for you than I want it to. I have been doing mostly API work in Rails recently so it doesn’t fit.
To fix this I ended up writing my own simple version of let
to use in my controllers.
lib/let.rb
def self.let(method, &block)
define_method(method) do
@lets ||= {}
@lets[method] ||= instance_eval(&block)
end
end
Since I ended up pasting this code in most of the projects I’m in, I ended up writing a gem letter. It’s simple to use.
app/controllers/application_controller.rb
class ApplicationController
extend Letter
let(:current_user) { User.find(session[:user_id]) }
end
Currently this gem doesn’t work if you have views. So a simple addition to make it work with views would be:
app/controllers/application_controller.rb
class ApplicationController
extend Letter
def self.let(method, *args)
super
helper_method(method)
end
let(:current_user) { User.find(session[:user_id]) }
end
Posted on 14 Nov 2012
The other week I had to speed up an end point of an API I’m working on, and since I wanted to do it right I used metrics. A coworker pointed me on to ActiveSupport::Notification.instrument previously so I gave it a go here.
I ended up creating a module that you can include into a class which will instrument every method. I don’t think it’s the nicest code ever but it was handy in getting a somewhat large class set up quickly.
It’s available in this gist, but I’ve also included it below.
lib/notifications.rb
module Notifications
extend ActiveSupport::Concern
module ClassMethods
def method_added(method_name)
@methods ||= []
return if @methods.include?(method_name) || method_name =~ /_old$/
@methods << method_name
class_eval %{alias #{method_name}_old #{method_name}}
define_method(method_name) do |*args|
instrument_name = "#{method_name}.#{self.class.name.underscore}"
ActiveSupport::Notifications.instrument(instrument_name) do
send("#{method_name}_old", *args)
end
end
end
end
end
You also need to set up something that will log the instruments so you can see them.
config/initializers/notifications.rb
ActiveSupport::Notifications.subscribe(/my_class$/) do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
Rails.logger.warn "%7.2fms %s" % [event.duration, event.name]
end
Resources
- ActiveSupport::Notifications
Posted on 09 Nov 2012
This post was originally published on the
SmartLogic Blog.
When you’re developing an API, controller actions have a tendency to get large and inefficient. You can keep chopping controller actions into smaller pieces, but turning them into services may be where you want to go. The main reason for this is because you can get validation of the data before you use it to perform the service, making code more reliable.
What is a service and when would you need to make one?
A service is a class that interacts with multiple models. If a method on a model is going to access more than itself and maybe one neighbor then it should be pulled out into a service. Single Responsibility Principle (SRP) essentially states that a class should do just one thing. With this, we’re making our class do just one thing: create an order. That way, we don’t have a “fat model” that violates SRP.
Here’s an example app on github to supplement the code below.
app/controllers/orders_controller.rb
class OrdersController < ApplicationController
def create
service = OrderCreationService.new(current_user, params[:order])
service.perform
if service.successful?
respond_with service.order
else
respond_with service.errors, :status => 422
end
end
end
app/services/order_creation_service.rb
class OrderCreationService
include ActiveModel::Validations
validates :name, :presence => true
attr_reader :user, :params, :order, :name
delegate :as_json, :to => :order
def initialize(user, params)
@user = user
@params = params
params.each do |param, value|
instance_variable_set("@#{param}", value) if respond_to?(param)
end
end
def perform
return unless valid?
# This can be done with only one line, or you can go nuts and make
# this much more expansive if your desired functionality demands it
@order = user.orders.create(params)
end
def successful?
valid? && order.persisted?
end
end
Here’s how this gives you the nice ability of having validations around the parameters your API takes:
$ curl -X POST http://localhost:3000/orders
{ 'errors': { 'name': ["can't be blank"] } }
With this method, your API development results in cleaner, more reliable code that will be easier for both internal developers and external developers to work with. Also, be sure to test via RspecApiDocumentation and Raddocs.
For more like this, follow SmartLogic on LinkedIn or like us on Facebook.
Image Source