Module: Reststop::Controllers

Defined in:
lib/reststop.rb

Overview

module Helpers

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.determine_format(input, env) ⇒ Object

:nodoc:



270
271
272
273
274
275
276
# File 'lib/reststop.rb', line 270

def self.determine_format(input, env) #:nodoc:
  if input[:format] && !input[:format].empty?
    input[:format].upcase.intern
  elsif env['PATH_INFO'] =~ /\.([a-z]+)$/
    $~[1].upcase.intern
  end
end

Instance Method Details

#REST(r, options = {}) ⇒ Object

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.



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
# File 'lib/reststop.rb', line 350

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 #{'pull version number here'}") 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 = Reststop::Controllers.determine_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 = Reststop::Controllers.determine_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 = Reststop::Controllers.determine_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 = Reststop::Controllers.determine_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