resource_mapper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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