Project

General

Profile

Download (10.2 KB) Statistics
| Branch: | Tag: | Revision:
module RestfulSupport
class ResourceNotFound < RuntimeError
end

class UnrelatedResources < RuntimeError
end
end

module RestfulSupport::Common
# suitable array of objects for polymorphic_path method
def resource_family(obj = nil)
# dup because polymorphic_path seems to eat the stuff
z = resource_namespace_components + @parent_resource_objects.dup
unless obj.nil?
if obj.is_a? Array
z += obj
else
z.push(obj)
end
end
return z.first if z.size == 1
return z
end

def parent_resource_path
polymorphic_path(resource_family)
end

def parent_resource_index_path
index_polymorphic_path(resource_family)
end

def resource_path(obj)
polymorphic_path(resource_family(obj))
end

def resource_index_path
index_polymorphic_path(resource_family(resource_model.new))
end

def index_polymorphic_path(params)
params = [params] unless params.is_a? Array
l_param = params.pop
if l_param.is_a? Class
klass = l_param
else
klass = l_param.class
end
params.push(klass.new)
# using polymorphic_path on a new object send back the path to the collection
polymorphic_path(params)
end

def child_polymorphic_path(obj, child)
polymorphic_path(resource_family([obj, child]))
end

def child_index_polymorphic_path(obj, model)
index_polymorphic_path(resource_family([obj, model.new]))
end

def resources_name
controller_name
end

def resource_namespace_components
list = controller_path.split("/")
list.pop
list.collect {|pc| pc.to_sym}
end

def convert_resources_name_to_resource_name(name)
name.singularize
end

def resource_name
convert_resources_name_to_resource_name(resources_name)
end

def resource_sym
resource_name.to_sym
end

def convert_resources_name_to_resource_model(name)
convert_resources_name_to_resource_name(name).camelize.constantize
end

def resource_model
convert_resources_name_to_resource_model(resources_name)
end

def convert_resources_name_to_resource_variable_sym(name)
("@" + convert_resources_name_to_resource_name(name)).to_sym
end
def convert_resource_model_to_resource_name(model)
model.to_s.underscore
end

def discover_associated_model_method(src_model, dst_model)
# singular method name
var_sym = convert_resource_model_to_resource_name(dst_model).to_sym
return var_sym if src_model.new.respond_to?(var_sym)
# plural method name
var_sym = var_sym.to_s.pluralize.to_sym
return var_sym if src_model.new.respond_to?(var_sym)
return nil
end

def resource_object
instance_variable_get(convert_resources_name_to_resource_variable_sym(resources_name))
end
end

module RestfulSupport::ActionView
def controller_name
@controller.controller_name
end

def controller_path
@controller.controller_path
end

def form_for_resource(*args, &block)
form_for resource_family(resource_object), *args, &block
end

def error_messages_for_resource
error_messages_for resource_sym
end
end

module RestfulSupport::ActionController
# implement a simple rest support for a resource
# the models are the class names for the associated DB objects, last being the model to be manipulated in this controller
# TODO: use resource instead of model, using 'camelize' method for String
def simple_rest_support(models = [])
class_eval do
include RestfulSupport::Common
helper_method RestfulSupport::Common.instance_methods

include RestfulSupport::ActionController::InstanceMethods

cattr_accessor :srs_models

prepend_before_filter :initialize_view

def initialize_view
unless @template.respond_to? :resource_family
@template.extend(RestfulSupport::ActionView)
end
end
end

srs_models = (models.kind_of? Array) ? models : [models]
srs_models << convert_resources_name_to_resource_model(controller_name)
class_variable_set(:@@srs_models, srs_models)

if srs_models.last.new.respond_to? :acts_as_list_class
class_eval do
include RestfulSupport::ActionController::InstanceMethods::ActsAsList
end
end
end
end

# how can i reach @template to extend it with the module instead of ApplicationHelper ?
#ApplicationHelper.module_eval('include RestfulSupport::Common')

module RestfulSupport::ActionController::InstanceMethods
def self.included(base)
base.extend ClassMethods

base.class_eval do
before_filter :load_objects, :only => [:index, :new, :edit, :show, :create, :update, :destroy, :move]
end
end

# helpers

def new_resource_object
instance_variable_set(convert_resources_name_to_resource_variable_sym(resources_name), resource_model.new)
end

def resource_params
params[resource_sym]
end

# filters

def load_objects
action = action_name.to_sym

ancestors_only = [:index, :new, :create].include?(action)
find_model(ancestors_only)

new_model() if [:new, :create].include?(action)
end

# listing request

def index
end

# forms request

def new
obj = resource_object

respond_to do |format|
format.html # new.rhtml
format.xml { render :xml => obj.dup }
end
end

def edit
obj = resource_object

respond_to do |format|
format.html # edit.rhtml
format.xml { render :xml => prepared_object(obj.dup) }
end
end

# actions

def show
obj = resource_object

respond_to do |format|
format.html # show.rhtml
format.xml { render :xml => prepared_object(obj.dup) }
end
end

def create
obj = resource_object
obj.attributes=(prepared_params)
unless @parent_resource_objects.empty?
parent = @parent_resource_objects.last
method = (convert_resource_model_to_resource_name(parent.class) + "_id=").to_sym
obj.send(method, parent.id) if obj.respond_to?(method)
end
res = resource_family(obj)

respond_to do |format|
if obj.save
flash[:notice] = sprintf(_("%s successfully created."), _(obj.human_name).capitalize)
# If we're in HTML mode, redirect back to the master list.
format.html { redirect_to(index_polymorphic_path(res.dup)) }
# If we're in XML mode, just return a 201 Created response.
format.xml { head :created, :location => polymorphic_path(res.dup) }
else
format.html { render :action => "new" }
format.xml { render :xml => obj.errors, :status => :unprocessable_entity }
end
end
end

def update
obj = resource_object
obj.attributes=(prepared_params)
res = resource_family(obj)

respond_to do |format|
if obj.save
flash[:notice] = sprintf(_("%s successfully modified"), _(obj.human_name).capitalize)
# If we're in HTML mode, redirect back to the master list.
format.html { redirect_to(index_polymorphic_path(res.dup)) }
# If we're in XML mode, just return a 201 Created response.
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => obj.errors, :status => :unprocessable_entity }
end
end
end

def destroy
obj = resource_object
obj.destroy
res = resource_family(obj)

respond_to do |format|
flash[:notice] = sprintf(_("%s successfully deleted"), _(obj.human_name).capitalize)
format.html { redirect_to(index_polymorphic_path(res.dup)) }
format.xml { head :ok }
end
end

private

def new_model
new_resource_object
end
def find_model(ancestors_only = false)
srs_find_resources(ancestors_only)
return resource_object unless ancestors_only
rescue ActiveRecord::RecordNotFound
raise RestfulSupport::ResourceNotFound
end

def srs_find_resources(ancestors_only = false)
list = []
previous_obj = nil
self.class.srs_models.each do |model|
# skip current model if asked for ancestors only (probably because there is not yet a current model)
next if ancestors_only and model == resource_model

# fetch parent advertised via routing
key = (model == resource_model) ? :id : model.to_s.foreign_key.to_sym
obj = model.find(params[key])

# check if this is a valid parent
unless previous_obj.nil?
res_name = convert_resource_model_to_resource_name(model)
if previous_obj.respond_to? res_name.to_sym
raise RestfulSupport::UnrelatedResources unless previous_obj.send(res_name.to_sym) == obj
elsif previous_obj.respond_to? res_name.pluralize.to_sym
raise RestfulSupport::UnrelatedResources unless previous_obj.send(res_name.pluralize.to_sym).include?(obj)
else
raise RestfulSupport::UnrelatedResources
end
end

# create instance variable for easy access
var_sym = ("@" + convert_resource_model_to_resource_name(model)).to_sym
instance_variable_set(var_sym, obj)

# save ancestor
previous_obj = obj
list << obj unless model == resource_model
end
@parent_resource_objects = list
end

def prepared_params
resource_params
end

def prepared_object(obj)
obj
end

module ClassMethods
def convert_resources_name_to_resource_name(name)
name.singularize
end

def convert_resources_name_to_resource_model(name)
convert_resources_name_to_resource_name(name).camelize.constantize
end

def convert_resources_name_to_resource_variable_sym(name)
("@" + convert_resources_name_to_resource_name(name)).to_sym
end
def convert_resource_model_to_resource_name(model)
model.to_s.underscore
end
end

module ActsAsList
def move
obj = resource_object
res = resource_family(obj)

type = params[:type]
type_ok = false
if ["move_lower", "move_higher", "move_to_top", "move_to_bottom"].include?(type)
type_ok = true
obj.send(type)
end

respond_to do |format|
if type_ok
flash[:notice] = sprintf(_("%s successfully moved"), _(obj.human_name).capitalize)
format.html { redirect_to(index_polymorphic_path(res.dup)) }
format.xml { head :ok }
else
format.html { redirect_to(index_polymorphic_path(res.dup)) }
format.xml { render :xml => obj.errors, :status => :not_found }
end
end
end
end
end
    (1-1/1)