resource_mapper 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +23 -0
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +84 -0
- data/VERSION +1 -0
- data/examples/example_helper.rb +42 -0
- data/examples/lib/sinatra/resource_example.rb +150 -0
- data/examples/models.rb +77 -0
- data/lib/resource_mapper.rb +31 -0
- data/lib/resource_mapper/accessors.rb +77 -0
- data/lib/resource_mapper/action_options.rb +35 -0
- data/lib/resource_mapper/actions.rb +56 -0
- data/lib/resource_mapper/class_methods.rb +65 -0
- data/lib/resource_mapper/controller.rb +56 -0
- data/lib/resource_mapper/failable_action_options.rb +25 -0
- data/lib/resource_mapper/helpers/current_objects.rb +82 -0
- data/lib/resource_mapper/helpers/internal.rb +58 -0
- data/lib/resource_mapper/helpers/nested.rb +67 -0
- data/lib/resource_mapper/response_collector.rb +27 -0
- data/lib/sinatra/resource.rb +104 -0
- data/resource_mapper.gemspec +80 -0
- metadata +170 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'active_support'
|
3
|
+
require 'mongo_mapper'
|
4
|
+
|
5
|
+
module ResourceMapper
|
6
|
+
ACTIONS = [:index, :show, :create, :update, :destroy]
|
7
|
+
SINGLETON_ACTIONS = (ACTIONS - [:index]).freeze
|
8
|
+
FAILABLE_ACTIONS = (ACTIONS - [:index]).freeze
|
9
|
+
NAME_ACCESSORS = [:model_name, :route_name, :object_name]
|
10
|
+
|
11
|
+
autoload :Accessors, 'resource_mapper/accessors'
|
12
|
+
autoload :ActionOptions, 'resource_mapper/action_options'
|
13
|
+
autoload :Actions, 'resource_mapper/actions'
|
14
|
+
autoload :ClassMethods, 'resource_mapper/class_methods'
|
15
|
+
autoload :Controller, 'resource_mapper/controller'
|
16
|
+
autoload :FailableActionOptions, 'resource_mapper/failable_action_options'
|
17
|
+
autoload :ResponseCollector, 'resource_mapper/response_collector'
|
18
|
+
|
19
|
+
module Helpers
|
20
|
+
autoload :Internal, 'resource_mapper/helpers/internal'
|
21
|
+
autoload :Nested, 'resource_mapper/helpers/nested'
|
22
|
+
autoload :CurrentObjects, 'resource_mapper/helpers/current_objects'
|
23
|
+
|
24
|
+
include ResourceMapper::Helpers::Internal
|
25
|
+
include ResourceMapper::Helpers::Nested
|
26
|
+
include ResourceMapper::Helpers::CurrentObjects
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'sinatra/resource'
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module ResourceMapper # :nodoc:
|
2
|
+
module Accessors # :nodoc:
|
3
|
+
private
|
4
|
+
def block_accessor(*accessors)
|
5
|
+
accessors.each do |block_accessor|
|
6
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
7
|
+
|
8
|
+
def #{block_accessor}(*args, &block)
|
9
|
+
unless args.empty? && block.nil?
|
10
|
+
args.push block if block_given?
|
11
|
+
@#{block_accessor} = [args].flatten
|
12
|
+
end
|
13
|
+
|
14
|
+
@#{block_accessor}
|
15
|
+
end
|
16
|
+
|
17
|
+
end_eval
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def scoping_reader(*accessor_names)
|
22
|
+
accessor_names.each do |accessor_name|
|
23
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
24
|
+
def #{accessor_name}(&block)
|
25
|
+
@#{accessor_name}.instance_eval &block if block_given?
|
26
|
+
@#{accessor_name}
|
27
|
+
end
|
28
|
+
end_eval
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def class_scoping_reader(accessor_name, start_value)
|
33
|
+
write_inheritable_attribute accessor_name, start_value
|
34
|
+
|
35
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
36
|
+
def self.#{accessor_name}(&block)
|
37
|
+
read_inheritable_attribute(:#{accessor_name}).instance_eval(&block) if block_given?
|
38
|
+
read_inheritable_attribute(:#{accessor_name})
|
39
|
+
end
|
40
|
+
end_eval
|
41
|
+
end
|
42
|
+
|
43
|
+
def reader_writer(accessor_name)
|
44
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
45
|
+
def #{accessor_name}(*args, &block)
|
46
|
+
args << block unless block.nil?
|
47
|
+
@#{accessor_name} = args.first unless args.empty?
|
48
|
+
@#{accessor_name}
|
49
|
+
end
|
50
|
+
end_eval
|
51
|
+
end
|
52
|
+
|
53
|
+
def class_reader_writer(*accessor_names)
|
54
|
+
accessor_names.each do |accessor_name|
|
55
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
56
|
+
def self.#{accessor_name}(*args)
|
57
|
+
unless args.empty?
|
58
|
+
write_inheritable_attribute :#{accessor_name}, args.first if args.length == 1
|
59
|
+
write_inheritable_attribute :#{accessor_name}, args if args.length > 1
|
60
|
+
end
|
61
|
+
|
62
|
+
read_inheritable_attribute :#{accessor_name}
|
63
|
+
end
|
64
|
+
|
65
|
+
def #{accessor_name}(*args)
|
66
|
+
unless args.empty?
|
67
|
+
self.class.write_inheritable_attribute :#{accessor_name}, args.first if args.length == 1
|
68
|
+
self.class.write_inheritable_attribute :#{accessor_name}, args if args.length > 1
|
69
|
+
end
|
70
|
+
|
71
|
+
self.class.read_inheritable_attribute :#{accessor_name}
|
72
|
+
end
|
73
|
+
end_eval
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ResourceMapper
|
2
|
+
class ActionOptions
|
3
|
+
extend ResourceMapper::Accessors
|
4
|
+
|
5
|
+
block_accessor :after, :before
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@collector = ResourceMapper::ResponseCollector.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def response(*args, &block)
|
12
|
+
if !args.empty? || block_given?
|
13
|
+
@collector.clear
|
14
|
+
args.flatten.each { |symbol| @collector.send(symbol) }
|
15
|
+
block.call(@collector) if block_given?
|
16
|
+
end
|
17
|
+
|
18
|
+
@collector.responses
|
19
|
+
end
|
20
|
+
alias_method :respond_to, :response
|
21
|
+
alias_method :responds_to, :response
|
22
|
+
|
23
|
+
def wants
|
24
|
+
@collector
|
25
|
+
end
|
26
|
+
|
27
|
+
def dup
|
28
|
+
returning self.class.new do |duplicate|
|
29
|
+
duplicate.instance_variable_set(:@collector, wants.dup)
|
30
|
+
duplicate.instance_variable_set(:@before, before.dup) unless before.nil?
|
31
|
+
duplicate.instance_variable_set(:@after, after.dup) unless after.nil?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module ResourceMapper
|
2
|
+
module Actions
|
3
|
+
|
4
|
+
def index
|
5
|
+
load_collection
|
6
|
+
before :index
|
7
|
+
response_for :index
|
8
|
+
end
|
9
|
+
|
10
|
+
def show
|
11
|
+
load_object
|
12
|
+
before :show
|
13
|
+
response_for :show
|
14
|
+
rescue #ActiveRecord::RecordNotFound
|
15
|
+
response_for :show_fails
|
16
|
+
end
|
17
|
+
|
18
|
+
def create
|
19
|
+
build_object
|
20
|
+
load_object
|
21
|
+
before :create
|
22
|
+
if object.save
|
23
|
+
after :create
|
24
|
+
response_for :create
|
25
|
+
else
|
26
|
+
after :create_fails
|
27
|
+
response_for :create_fails
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def update
|
32
|
+
load_object
|
33
|
+
before :update
|
34
|
+
if object.update_attributes object_params
|
35
|
+
after :update
|
36
|
+
response_for :update
|
37
|
+
else
|
38
|
+
after :update_fails
|
39
|
+
response_for :update_fails
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def destroy
|
44
|
+
load_object
|
45
|
+
before :destroy
|
46
|
+
if object.destroy
|
47
|
+
after :destroy
|
48
|
+
response_for :destroy
|
49
|
+
else
|
50
|
+
after :destroy_fails
|
51
|
+
response_for :destroy_fails
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module ResourceMapper
|
2
|
+
module ClassMethods
|
3
|
+
|
4
|
+
# Use this method in your controller to specify which actions you'd like it to respond to.
|
5
|
+
#
|
6
|
+
# class PostsController < ResourceController::Base
|
7
|
+
# actions :all, :except => :create
|
8
|
+
# end
|
9
|
+
def actions(*opts)
|
10
|
+
config = {}
|
11
|
+
config.merge!(opts.pop) if opts.last.is_a?(Hash)
|
12
|
+
|
13
|
+
all_actions = (singleton? ? ResourceMapper::SINGLETON_ACTIONS : ResourceMapper::ACTIONS)
|
14
|
+
|
15
|
+
actions_to_remove = []
|
16
|
+
actions_to_remove += all_actions - opts unless opts.first == :all
|
17
|
+
actions_to_remove += [*config[:except]] if config[:except]
|
18
|
+
actions_to_remove.uniq!
|
19
|
+
|
20
|
+
actions_to_remove.each { |action| undef_method(action) if method_defined?(action) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def key(sym)
|
24
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
25
|
+
def key
|
26
|
+
:#{sym}
|
27
|
+
end
|
28
|
+
end_eval
|
29
|
+
end
|
30
|
+
|
31
|
+
def before(*args, &block)
|
32
|
+
raise "Need to specify a block for a before set." unless block_given?
|
33
|
+
|
34
|
+
args.flatten.each do |arg|
|
35
|
+
self.method(arg).call.before &block
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO: After fail after success
|
40
|
+
def after(*args, &block)
|
41
|
+
raise "Need to specify a block for an after set." unless block_given?
|
42
|
+
|
43
|
+
args.flatten.each do |arg|
|
44
|
+
self.method(arg).call.after &block
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def read_params(*args)
|
49
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
50
|
+
def read_params
|
51
|
+
{:only => #{args}}
|
52
|
+
end
|
53
|
+
end_eval
|
54
|
+
end
|
55
|
+
|
56
|
+
def write_params(*args)
|
57
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
58
|
+
def write_params
|
59
|
+
#{args}
|
60
|
+
end
|
61
|
+
end_eval
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module ResourceMapper
|
2
|
+
module Controller
|
3
|
+
def self.included(subclass)
|
4
|
+
subclass.class_eval do
|
5
|
+
include ResourceMapper::Helpers
|
6
|
+
include ResourceMapper::Actions
|
7
|
+
extend ResourceMapper::Accessors
|
8
|
+
extend ResourceMapper::ClassMethods
|
9
|
+
|
10
|
+
class_reader_writer :belongs_to, *NAME_ACCESSORS
|
11
|
+
NAME_ACCESSORS.each { |accessor| send(accessor, controller_name.singularize.underscore) }
|
12
|
+
|
13
|
+
ACTIONS.each do |action|
|
14
|
+
class_scoping_reader action, FAILABLE_ACTIONS.include?(action) ? ResourceMapper::FailableActionOptions.new : ResourceMapper::ActionOptions.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
init_default_actions(subclass)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def self.init_default_actions(klass)
|
23
|
+
klass.class_eval do
|
24
|
+
index.wants.json { collection.to_json(read_params) }
|
25
|
+
|
26
|
+
show do
|
27
|
+
wants.json { object.to_json(read_params) }
|
28
|
+
failure.wants.json {
|
29
|
+
throw(:halt, [404, 'Not found.'])
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
create do
|
34
|
+
wants.json { object.to_json(read_params) }
|
35
|
+
failure.wants.json { "BOOOM!!!" }
|
36
|
+
end
|
37
|
+
|
38
|
+
update do
|
39
|
+
wants.json { object.to_json(read_params) }
|
40
|
+
failure.wants.json { "BOOOM!!!" }
|
41
|
+
end
|
42
|
+
|
43
|
+
destroy do
|
44
|
+
wants.json { "" }
|
45
|
+
failure.wants.json { "BOOM!!!" }
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
def singleton?
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ResourceMapper
|
2
|
+
class FailableActionOptions
|
3
|
+
extend ResourceMapper::Accessors
|
4
|
+
|
5
|
+
scoping_reader :success, :fails
|
6
|
+
alias_method :failure, :fails
|
7
|
+
|
8
|
+
block_accessor :before
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@success = ActionOptions.new
|
12
|
+
@fails = ActionOptions.new
|
13
|
+
end
|
14
|
+
|
15
|
+
delegate :after, :response, :wants, :to => :success
|
16
|
+
|
17
|
+
def dup
|
18
|
+
returning self.class.new do |duplicate|
|
19
|
+
duplicate.instance_variable_set(:@success, success.dup)
|
20
|
+
duplicate.instance_variable_set(:@fails, fails.dup)
|
21
|
+
duplicate.instance_variable_set(:@before, before.dup) unless before.nil?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module ResourceMapper
|
2
|
+
module Helpers
|
3
|
+
module CurrentObjects
|
4
|
+
protected
|
5
|
+
# Used internally to return the model for your resource.
|
6
|
+
#
|
7
|
+
def model
|
8
|
+
model_name.to_s.camelize.constantize
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
# Used to fetch the collection for the index method
|
13
|
+
#
|
14
|
+
# In order to customize the way the collection is fetched, to add something like pagination, for example, override this method.
|
15
|
+
#
|
16
|
+
def collection
|
17
|
+
end_of_association_chain.all
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the current param for key.
|
21
|
+
#
|
22
|
+
# Override this method if you'd like to use an alternate param name.
|
23
|
+
#
|
24
|
+
def param
|
25
|
+
params[:id]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the key used for finding.
|
29
|
+
def key
|
30
|
+
:id
|
31
|
+
end
|
32
|
+
|
33
|
+
# Used to fetch the current member object in all of the singular methods that operate on an existing member.
|
34
|
+
#
|
35
|
+
# Override this method if you'd like to fetch your objects in some alternate way, like using a permalink.
|
36
|
+
#
|
37
|
+
# class PostsController < ResourceController::Base
|
38
|
+
# private
|
39
|
+
# def object
|
40
|
+
# @object ||= end_of_association_chain.find_by_permalink(param)
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
def object
|
45
|
+
@object ||= end_of_association_chain.first({key => param}) unless param.nil?
|
46
|
+
@object
|
47
|
+
end
|
48
|
+
|
49
|
+
# Used internally to load the member object in to an instance variable @#{model_name} (i.e. @post)
|
50
|
+
#
|
51
|
+
def load_object
|
52
|
+
instance_variable_set "@#{parent_type}", parent_object if parent?
|
53
|
+
instance_variable_set "@#{object_name}", object
|
54
|
+
end
|
55
|
+
|
56
|
+
# Used internally to load the collection in to an instance variable @#{model_name.pluralize} (i.e. @posts)
|
57
|
+
#
|
58
|
+
def load_collection
|
59
|
+
instance_variable_set "@#{parent_type}", parent_object if parent?
|
60
|
+
instance_variable_set "@#{object_name.to_s.pluralize}", collection
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the form params. Defaults to params[model_name] (i.e. params["post"])
|
64
|
+
#
|
65
|
+
def object_params
|
66
|
+
ret = params["#{object_name}"]
|
67
|
+
ret.delete_if { |k, v| write_params.index(k.to_sym).nil? } unless write_params.nil?
|
68
|
+
ret
|
69
|
+
end
|
70
|
+
|
71
|
+
# Builds the object, but doesn't save it, during the new, and create action.
|
72
|
+
#
|
73
|
+
def build_object
|
74
|
+
@object = end_of_association_chain.send parent? ? :build : :new, object_params
|
75
|
+
end
|
76
|
+
|
77
|
+
def read_params ; nil end
|
78
|
+
def write_params ; nil end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Internal action lifecycle management.
|
2
|
+
#
|
3
|
+
# All of these methods are used internally to execute the options, set by the user in ActionOptions and FailableActionOptions
|
4
|
+
#
|
5
|
+
module ResourceMapper
|
6
|
+
module Helpers
|
7
|
+
module Internal
|
8
|
+
protected
|
9
|
+
# Used to actually pass the responses along to the controller's respond_to method.
|
10
|
+
#
|
11
|
+
def response_for(action)
|
12
|
+
respond_to do |wants|
|
13
|
+
options_for(action).response.each do |method, block|
|
14
|
+
if block.nil?
|
15
|
+
wants.send(method)
|
16
|
+
else
|
17
|
+
wants.send(method) { instance_eval(&block) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Calls the after callbacks for the action, if one is present.
|
24
|
+
#
|
25
|
+
def after(action)
|
26
|
+
invoke_callbacks *options_for(action).after
|
27
|
+
end
|
28
|
+
|
29
|
+
# Calls the before block for the action, if one is present.
|
30
|
+
#
|
31
|
+
def before(action)
|
32
|
+
invoke_callbacks *self.class.send(action).before
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the options for an action, which is a symbol.
|
36
|
+
#
|
37
|
+
# Manages splitting things like :create_fails.
|
38
|
+
#
|
39
|
+
def options_for(action)
|
40
|
+
action = "#{action}".split('_').map(&:to_sym)
|
41
|
+
options = self.class.send(action.first)
|
42
|
+
options = options.send(action.last == :fails ? :fails : :success) if ResourceMapper::FAILABLE_ACTIONS.include? action.first
|
43
|
+
|
44
|
+
options
|
45
|
+
end
|
46
|
+
|
47
|
+
def invoke_callbacks(*callbacks)
|
48
|
+
unless callbacks.empty?
|
49
|
+
callbacks.select { |callback| callback.is_a? Symbol }.each { |symbol| send(symbol) }
|
50
|
+
|
51
|
+
block = callbacks.detect { |callback| callback.is_a? Proc }
|
52
|
+
instance_eval &block unless block.nil?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|