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.
@@ -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