module Camping::Controllers
Public Instance Methods
Calling REST "<resource name>"
creates a
controller with the appropriate routes and maps your #REST methods to standard Camping controller mehods. This is meant to be
used in your Controllers module in place of
R <routes>
.
Your #REST class should define the following methods:
-
create
-
read(id)
-
update(id)
-
destroy(id)
-
list
Routes will be automatically created based on the resource name fed to the #REST method. Your class must have the same (but CamelCaps'ed) name as the resource name. So if your resource name is 'kittens', your controller class must be Kittens.
For example:
module Foobar::Controllers class Kittens < REST 'kittens' # POST /kittens def create end # GET /kittens/(\d+) def read(id) end # PUT /kittens/(\d+) def update(id) end # DELETE /kittens/(\d+) def destroy(id) end # GET /kittens def list end end end
Custom actions are also possible. For example, to implement a 'meow' action simply add a 'meow' method to the above controller:
# POST/GET/PUT/DELETE /kittens/meow # POST/GET/PUT/DELETE /kittens/(\d+)/meow def meow(id) end
Note that a custom action will respond to all four HTTP methods (POST/GET/PUT/DELETE).
Optionally, you can specify a :prefix
key that will prepend
the given string to the routes. For example, the following will create all
of the above routes, prefixed with “/pets” (i.e. POST
'/pets/kittens'
, GET
'/pets/kittens/(\d+)'
, etc.):
module Foobar::Controllers class Items < REST 'kittens', :prefix => '/pets' # ... end end
Format-based routing similar to that in ActiveResource is also implemented.
For example, to get a list of kittens in XML format, place a
GET
call to /kittens.xml
. See the documentation
for the render() method for more info.
# File lib/reststop.rb, line 300 def REST(r, options = {}) crud = R "#{options[:prefix]}/#{r}/([0-9a-zA-Z]+)/([a-z_]+)(?:\.[a-z]+)?", "#{options[:prefix]}/#{r}/([0-9a-zA-Z]+)(?:\.[a-z]+)?", "#{options[:prefix]}/#{r}/([a-z_]+)(?:\.[a-z]+)?", "#{options[:prefix]}/#{r}(?:\.[a-z]+)?" crud.module_eval do meta_def(:restful?){true} $LOG.debug("Creating RESTful controller for #{r.inspect} using Reststop #{::Reststop::VERSION::STRING}") if $LOG def get(id_or_custom_action = nil, custom_action = nil) # :nodoc: id = @input[:id] if @input[:id] custom_action = @input[:action] if @input[:action] if self.methods.include? id_or_custom_action custom_action ||= id_or_custom_action id ||= nil else id ||= id_or_custom_action end id = id.to_i if id && id =~ /^[0-9]+$/ @format = Controllers.read_format(@input, @env) begin if id.nil? && @input[:id].nil? custom_action ? send(custom_action) : list else custom_action ? send(custom_action, id || @input[:id]) : read(id || @input[:id]) end rescue NoMethodError => e # FIXME: this is probably not a good way to do this, but we need to somehow differentiate # between 'no such route' vs. other NoMethodErrors if e.message =~ /no such method/ return no_method(e) else raise e end rescue ActiveRecord::RecordNotFound => e return not_found(e) end end def post(custom_action = nil) # :nodoc: @format = Controllers.read_format(@input, @env) custom_action ? send(custom_action) : create end def put(id, custom_action = nil) # :nodoc: id = id.to_i if id =~ /^[0-9]+$/ @format = Controllers.read_format(@input, @env) custom_action ? send(custom_action, id || @input[:id]) : update(id || @input[:id]) end def delete(id, custom_action = nil) # :nodoc: id = id.to_i if id =~ /^[0-9]+$/ @format = Controllers.read_format(@input, @env) custom_action ? send(custom_action, id || @input[:id]) : destroy(id || @input[:id]) end private def _error(message, status_code = 500, e = nil) @status = status_code @message = message begin render "error_#{status_code}".intern rescue NoMethodError if @format.to_s == 'XML' "<error code='#{status_code}'>#{@message}</error>" else out = "<strong>#{@message}</strong>" out += "<pre style='color: #bbb'><strong>#{e.class}: #{e}</strong>\n#{e.backtrace.join("\n")}</pre>" if e out end end end def no_method(e) _error("No controller method responds to this route!", 501, e) end def not_found(e) _error("Record not found!", 404, e) end end crud end
# File lib/reststop.rb, line 367 def _error(message, status_code = 500, e = nil) @status = status_code @message = message begin render "error_#{status_code}".intern rescue NoMethodError if @format.to_s == 'XML' "<error code='#{status_code}'>#{@message}</error>" else out = "<strong>#{@message}</strong>" out += "<pre style='color: #bbb'><strong>#{e.class}: #{e}</strong>\n#{e.backtrace.join("\n")}</pre>" if e out end end end
# File lib/reststop.rb, line 383 def no_method(e) _error("No controller method responds to this route!", 501, e) end
# File lib/reststop.rb, line 387 def not_found(e) _error("Record not found!", 404, e) end