Skip to content

Instantly share code, notes, and snippets.

@patmaddox
Created March 7, 2011 07:03
Show Gist options
  • Save patmaddox/858172 to your computer and use it in GitHub Desktop.
Save patmaddox/858172 to your computer and use it in GitHub Desktop.
Wondering what people think. Gratuitous refactoring, or marvelous use of Ruby?
module API
class Version1 < Grape::API
version 'v1'
resource :hotels do
get '/' do
if [:latlng, :query].all? {|key| params[key].blank? }
error! 'Please pass in latlng|query'
end
response = {}
finder = Hotel
if query = params[:query]
response[:query] = query
finder = finder.search query
end
if latlng = params[:latlng]
latitude, longitude = latlng.split(',').map(&:to_f)
location = {:latitude => latitude, :longitude => longitude}
response[:location] = location
finder = finder.nearby location
end
response[:hotels] = finder
response
end
end
end
end
module API
class Version1 < Grape::API
version 'v1'
helpers do
def hotels_response
@_method_collector = MethodCollector.new
@_queried_params = []
response = {}
yield response
if !@_queried_params.empty? && @_queried_params.all? {|key| params[key].blank? }
error! "Please pass in #{@_queried_params.join('|')}"
end
response[:hotels] = @_method_collector.chain Hotel
response
end
def on_param(param_name)
@_queried_params << param_name
return if (value = params[param_name]).blank?
yield value, @_method_collector
end
end
resource :hotels do
get '/' do
hotels_response do |response|
on_param(:query) do |query, finder|
response[:query] = query
finder.search query
end
on_param(:latlng) do |latlng, finder|
latitude, longitude = latlng.split(',').map(&:to_f)
location = {:latitude => latitude, :longitude => longitude}
response[:location] = location
finder.nearby location
end
end
end
end
end
end
class MethodCollector
def initialize
@called_methods = []
end
def method_missing(sym, *args, &block)
@called_methods << {:method => sym, :args => args, :block => block}
end
def chain(starting_point)
([starting_point] + @called_methods).inject {|finder, method_call|
finder.send(method_call[:method], *method_call[:args], &method_call[:block])
}
end
end
# I extended the previous code by adding a new 'show' action, as well as 404 handling
module API
class Version1 < Grape::API
version 'v1'
helpers do
def return_response(finder_key)
@_method_collector = MethodCollector.new
@_queried_params = []
response = {}
yield response
if !@_queried_params.empty? && @_queried_params.all? {|key| params[key].blank? }
error! "Please pass in #{@_queried_params.join('|')}"
end
begin
response[finder_key] = @_method_collector.chain Hotel
rescue Mongoid::Errors::DocumentNotFound
error! "Not Found", 404
end
response
end
def on_param(param_name)
@_queried_params << param_name
return if (value = params[param_name]).blank?
yield value, @_method_collector
end
end
resource :hotels do
get '/:id.json' do
return_response(:hotel) do |response|
on_param(:id) do |id, finder|
finder.find id
end
end
end
get '/' do
return_response(:hotels) do |response|
on_param(:query) do |query, finder|
response[:query] = query
finder.search query
end
on_param(:latlng) do |latlng, finder|
latitude, longitude = latlng.split(',').map(&:to_f)
location = {:latitude => latitude, :longitude => longitude}
response[:location] = location
finder.nearby location
end
end
end
end
end
end
@ajsharp
Copy link

ajsharp commented Mar 7, 2011

Very interesting. Intriguing, even. Seems like a nice, clean dsl for building publicly facing API's.

@ajsharp
Copy link

ajsharp commented Mar 7, 2011

So the model methods are expected to be scopes, correct? They get chained together, and the on_param blocks, in a way, serve as a dsl to build AND queries. Do I have that right?

@patmaddox
Copy link
Author

yeah so the point of this is to generate a JSON document to return to the API caller (web-based API). When they hit the index, they pass in some params...they need to pass in at least query OR latlng, and they can pass in both. So I need to support query on its own, latlng on its own, and the combo of query & latlng. More params are likely to come later too. Also if the param is missing, then I need to show an error.

Yeah the model methods are scopes that can be chained together (that's what MethodCollector#chain is doing)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment