Skip to content

Instantly share code, notes, and snippets.

@lucatironi
Created December 1, 2011 16:12
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save lucatironi/1417872 to your computer and use it in GitHub Desktop.
Save lucatironi/1417872 to your computer and use it in GitHub Desktop.
Snippets for an API with grape
# Server App
# This file must be in lib/myapp/api.rb
module MyApp
module Entities
class Products < Grape::Entity
expose :id, :code, :name, :short_description
expose :description, :unless => { :collection => true }
expose (:category) { |model, options| model.category.name }
expose (:brand) { |model, options| model.brand.name }
end
class Category < Grape::Entity
expose :id, :name
end
class Brand < Grape::Entity
expose :id, :name
end
class Costumer < Grape::Entity
expose :id, :first_name, :last_name, :email
end
end
class API < Grape::API
prefix "api"
version "v1", :using => :path, :vendor => "foo", :format => :json
http_basic do |user, pass|
user == "apiuser" && pass == "secret"
end
# Products
resource :products do
get do
present Product.all, :with => Entities::Product
end
get ':id' do
present Product.find(params[:id]), :with => Entities::Product
end
end
# Category
resource :categories do
get do
present Category.all, :with => Entities::Category
end
get ':id' do
present Category.find(params[:id]), :with => Entities::Category
end
get ':id/products' do
present Product.where(:category_id => params[:id]).all, :with => Entities::Product
end
end
# Brands
resource :brands do
get do
present Brand.all, :with => Entities::Brand
end
get ':id' do
present Brand.find(params[:id]), :with => Entities::Brand
end
get ':id/products' do
present Product.where(:brand_id => params[:id]).all, :with => Entities::Product
end
end
# Customers
resource :customer do
post do
customer = Customer.find_by_email(params[:customer][:email])
if customer.nil?
customer = Customer.create(params[:customer])
end
present customer, :with => Entities::Customer
end
get ':id' do
present Customer.find(params[:id]), :with => Entities::Customer
end
end
# Subscribers
resource :subscribers do
post do
if subscriber = Subscriber.create(params[:subscriber])
present subscriber
else
error! "Unable to create your subscriber", 403
end
end
end
end
end
# References
Grape: An opinionated micro-framework for creating REST-like APIs in Ruby. https://github.com/intridea/grape
Mounting Grape API inside rails application: http://martinciu.com/2011/01/mounting-grape-api-inside-rails-application.html
RailsCast on ActiveModel: http://railscasts.com/episodes/219-active-model
# Client App: the product is not an active_record
# Require httparty in Gemfile
class Product
attr_accessor :id, :code, :name, :category, :brand, :short_description, :description
def initialize(product_id = nil)
return nil if product_id.nil?
response = HTTParty.get(APP_CONFIG[:api_domain] + "/api/v1/products/#{product_id}", :basic_auth => { :username => "apiuser", :password => "secret" })
response.parsed_response.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
MyApp::Application.routes.draw do
# snip..
mount Pvgest::API => "/" #API will be available under "/api/v1" url because of setting from MyApp::API line 4
end
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'webmock/rspec'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
# == Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
config.mock_with :rspec
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
config.before(:each) do
WebMock.reset!
categories = JSON.parse(File.open('spec/fixtures/categories.json', 'r').read)
produttori = JSON.parse(File.open('spec/fixtures/brands.json', 'r').read)
products = JSON.parse(File.open('spec/fixtures/products.json', 'r').read)
stub_request(:get, "http://apiuser:secret@test.host/api/v1/categories").to_return(:status => 200, :headers => {}, :body => categories)
stub_request(:get, "http://apiuser:secret@test.host/api/v1/brands").to_return(:status => 200, :headers => {}, :body => brands)
stub_request(:get, "http://apiuser:secret@test.host/api/v1/products").to_return(:status => 200, :headers => {}, :body => products)
stub_request(:post, "http://apiuser:secret@test.host/api/v1/customers").to_return(:status => 200, :headers => {}, :body => {:id => 1, :nome => "John", :cognome => "Doe"})
stub_request(:get, "http://apiuser:secret@test.host/api/v1/customers/1").to_return(:status => 200, :headers => {}, :body => {:nome => "John", :cognome => "Doe"})
stub_request(:post, "http://apiuser:secret@test.host/api/v1/customers").to_return(:status => 200, :headers => {}, :body => {:id => 1, :email => "example@email.com", :referer => "http://www.google.com", :ip => "10.10.10.100" })
end
end
# Client App: subscribers are sent to the api
class SubscribersController < ApplicationController
def create
options = { :body => { :subscriber => { :email => params[:email], :ip => request.env['REMOTE_ADDR'], :referer => request.env['HTTP_REFERER'] } }, :basic_auth => { :username => "apiuser", :password => "secret" } }
response = HTTParty.post(APP_CONFIG[:api_domain] + "/api/v1/subscribers", options)
if response.status == 200
redirect_to root_url, :notice => "Thanks!"
else
redirect_to root_url, :notice => "Error..."
end
end
end
# Client App: user model
class User < ActiveRecord::Base
has_secure_password
attr_accessible :email, :first_name, :last_name, :phone, :password, :password_confirmation
validates_presence_of :name, :fisrt_name, :email
validates_uniqueness_of :email
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates :phone,
:length => { :minimum => 6, :maximum => 15 },
:format => { :with => /^[+\/\-() 0-9]{6,15}$/ },
:allow_blank => true
before_validation :generate_random_password, :on => :create
before_create :set_customer
def self.default
new
end
def generate_random_password
self.password = Array.new(10).map { (65 + rand(58)).chr }.join
end
def set_customer
options = { :body => { :customer => { :email => self.email, :firt_name => self.first_name, :last_name => self.last_name, :phone => self.phone } }, :basic_auth => { :username => "apiuser", :password => "secret" } }
response = HTTParty.post(APP_CONFIG[:api_domain] + "/api/v1/customers", options)
self.cliente_id = response.parsed_response['id']
end
def customer
response = HTTParty.get(APP_CONFIG[:api_domain] + "/api/v1/customers/#{customer_id}", :basic_auth => { :username => "apiuser", :password => "secret" })
return response.parsed_response
end
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
PostOffice.password_reset(self).deliver
end
private
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment