resourcelogic 0.0.11

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,134 @@
1
+ module Resourcelogic
2
+ # This module let's you define various aliases for your controller. For example,
3
+ # lets say you have the following routes:
4
+ #
5
+ # /account/addresses => UsersController
6
+ # /admin/users/5/addresses => UsersController
7
+ #
8
+ # Here is how your AddressesController would look:
9
+ #
10
+ # class AddressesController < ResourceController
11
+ # belongs_to :user
12
+ # end
13
+ #
14
+ # The problem is that sometimes the parent object is called user, sometimes its
15
+ # called account. So the solution is to do:
16
+ #
17
+ # class ResourceController < ApplicationController
18
+ # route_alias :account, :user
19
+ # end
20
+ #
21
+ # Now ResourceLogic knows that when it see account in the URL it will know the grab
22
+ # the User model.
23
+ #
24
+ # Now I know an alternative could be to do somethig like:
25
+ #
26
+ # belongs_to :user, :alias => :account
27
+ #
28
+ # The above presents a problem. Take the following URL:
29
+ #
30
+ # /productos/1/pictures/4/comments
31
+ #
32
+ # In order for Resourcelogic to do its magic relative URLs, it needs to know what
33
+ # model "productos" should be using. Which should be the Product model, yet we
34
+ # can't define that in the CommentsController because it's 2 levels above, and we
35
+ # only specify the parent.
36
+ #
37
+ # Now a lot of people say you should never nest more than 2 levels deep, and this
38
+ # is absolutely true 95% of the time. But what if I want to link back to the parent
39
+ # object from the comments controller and preserve it's context? In order to do
40
+ # this I have to go 3 levels deep, because maybe context for the PicturesController
41
+ # is really important / required. The only way to preserve context is with the URL.
42
+ #
43
+ # Sorry for rambling, this documentation is really more of an internal note for me
44
+ # and to hopefully clarify why I took this approach.
45
+ module Aliases
46
+ def self.included(klass)
47
+ klass.class_eval do
48
+ extend Config
49
+ include InstanceMethods
50
+ end
51
+ end
52
+
53
+ module Config
54
+ def path_alias(alias_name, model_name)
55
+ current_aliases = path_aliases
56
+ model_name = model_name.to_sym
57
+ current_aliases[model_name] ||= []
58
+ current_aliases[model_name] << alias_name.to_sym
59
+ write_inheritable_attribute(:path_aliases, current_aliases)
60
+ end
61
+
62
+ def path_aliases
63
+ read_inheritable_attribute(:path_aliases) || {}
64
+ end
65
+
66
+ def route_alias(alias_name, model_name)
67
+ current_aliases = route_aliases
68
+ model_name = model_name.to_sym
69
+ current_aliases[model_name] ||= []
70
+ current_aliases[model_name] << alias_name.to_sym
71
+ write_inheritable_attribute(:route_aliases, current_aliases)
72
+ end
73
+
74
+ def route_aliases
75
+ read_inheritable_attribute(:route_aliases) || {}
76
+ end
77
+ end
78
+
79
+ module InstanceMethods
80
+ private
81
+ def model_name_from_route_alias(alias_name)
82
+ route_aliases.each do |model_name, aliases|
83
+ return model_name if aliases.include?(alias_name.to_sym)
84
+ end
85
+ nil
86
+ end
87
+
88
+ def route_aliases
89
+ self.class.route_aliases
90
+ end
91
+
92
+ def model_name_from_path_alias(alias_name)
93
+ path_aliases.each do |model_name, aliases|
94
+ return model_name if aliases.include?(alias_name.to_sym)
95
+ end
96
+ nil
97
+ end
98
+
99
+ def path_aliases
100
+ self.class.path_aliases
101
+ end
102
+
103
+ def possible_model_names(model_name)
104
+ [model_name] + (route_aliases[model_name] || []) + (path_aliases[model_name] || [])
105
+ end
106
+
107
+ # The point of this method is to determine what the part of a url is really referring to.
108
+ # For example, let's say you did this:
109
+ #
110
+ # map.resources :users, :as => :accounts
111
+ #
112
+ # Resource logic looks at the request.path. It's going to see "accounts" in the urls. How
113
+ # is it to know that by "accounts" you are referring to the "users" resource. That's the
114
+ # point of this method, to say "hey, accounts is mapped to users".
115
+ def model_name_from_path_part(part)
116
+ part = part.to_s.singularize
117
+ model_name_from_route_alias(part) || model_name_from_path_alias(part) || part.to_sym
118
+ end
119
+
120
+ # The point of this method is to determine the name used in the route method. For example,
121
+ # let's say you did this:
122
+ #
123
+ # map.resources :accounts, :controller => "users"
124
+ #
125
+ # You would want to use "account" in your path and url helper, not "user".
126
+ def route_name_from_path_part(part)
127
+ part = part.to_s.singularize
128
+ model_name = model_name_from_path_alias(part)
129
+ return model_name if model_name
130
+ part.to_sym
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,130 @@
1
+ module Resourcelogic # :nodoc:
2
+ module Base # :nodoc:
3
+ def self.included(klass)
4
+ klass.class_eval do
5
+ extend ClassMethods
6
+ include InstanceMethods
7
+ end
8
+ end
9
+
10
+ module ClassMethods
11
+ def acts_as_resource(&block)
12
+ resourceful(true)
13
+ yield self if block_given?
14
+ acts_as_resource_modules.each { |mod| include mod }
15
+ init_default_actions
16
+ end
17
+
18
+ def init_default_actions
19
+ index.wants.html
20
+ edit.wants.html
21
+ new_action.wants.html
22
+
23
+ show do
24
+ wants.html
25
+ failure.wants.html { render :text => "#{model_name.to_s.singularize.humanize} not found." }
26
+ end
27
+
28
+ create do
29
+ flash { "#{model_name.to_s.singularize.humanize} successfully created!" }
30
+ wants.html { redirect_to object_url }
31
+ failure.wants.html { render :action => "new" }
32
+ end
33
+
34
+ update do
35
+ flash { "#{model_name.to_s.singularize.humanize} successfully updated!" }
36
+ wants.html { redirect_to object_url }
37
+ failure.wants.html { render :action => "edit" }
38
+ end
39
+
40
+ destroy do
41
+ flash { "#{model_name.to_s.singularize.humanize} successfully removed!" }
42
+ wants.html { redirect_to collection_url }
43
+ end
44
+ end
45
+
46
+ # Since this part of Authlogic deals with another class, ActiveRecord, we can't just start including things
47
+ # in ActiveRecord itself. A lot of these module includes need to be triggered by the acts_as_authentic method
48
+ # call. For example, you don't want to start adding in email validations and what not into a model that has
49
+ # nothing to do with Authlogic.
50
+ #
51
+ # That being said, this is your tool for extending Authlogic and "hooking" into the acts_as_authentic call.
52
+ def add_acts_as_resource_module(mod, action = :append)
53
+ modules = acts_as_resource_modules
54
+ case action
55
+ when :append
56
+ modules << mod
57
+ when :prepend
58
+ modules = [mod] + modules
59
+ end
60
+ modules.uniq!
61
+ write_inheritable_attribute(:acts_as_resource_modules, modules)
62
+ end
63
+
64
+ # This is the same as add_acts_as_authentic_module, except that it removes the module from the list.
65
+ def remove_acts_as_resource_module(mod)
66
+ write_inheritable_attribute(:acts_as_resource_modules, acts_as_resource_modules - [mod])
67
+ acts_as_resource_modules
68
+ end
69
+
70
+ def resourceful(value = nil)
71
+ rw_config(:resourceful, value, false)
72
+ end
73
+
74
+ def resourceful?
75
+ resourceful == true
76
+ end
77
+
78
+ private
79
+ def acts_as_resource_modules
80
+ key = :acts_as_resource_modules
81
+ inheritable_attributes.include?(key) ? read_inheritable_attribute(key) : []
82
+ end
83
+
84
+ def rw_config(key, value, default_value = nil, read_value = nil)
85
+ if value == read_value
86
+ inheritable_attributes.include?(key) ? read_inheritable_attribute(key) : default_value
87
+ else
88
+ write_inheritable_attribute(key, value)
89
+ end
90
+ end
91
+ end
92
+
93
+ module InstanceMethods
94
+ def self.included(klass)
95
+ klass.helper_method :resourceful?, :section
96
+ end
97
+
98
+ def resourceful?
99
+ self.class.resourceful?
100
+ end
101
+
102
+ def section
103
+ section = request.path.split("/")[1]
104
+ section && section.to_sym
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ if defined?(::ActionController)
111
+ module ::ActionController
112
+ class Base
113
+ extend Resourcelogic::Accessors
114
+ include Resourcelogic::Base
115
+ include Resourcelogic::Actions
116
+ include Resourcelogic::Child
117
+ include Resourcelogic::Context
118
+ include Resourcelogic::Scope
119
+ include Resourcelogic::Self
120
+ include Resourcelogic::Sibling
121
+ include Resourcelogic::SubViews
122
+ include Resourcelogic::Urligence
123
+
124
+ # Need to be loaded last to override methods
125
+ include Resourcelogic::Parent
126
+ include Resourcelogic::Aliases
127
+ include Resourcelogic::Singleton
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,55 @@
1
+ module Resourcelogic
2
+ module Child
3
+ def self.included(klass)
4
+ klass.class_eval do
5
+ add_acts_as_resource_module(Urls)
6
+ end
7
+ end
8
+
9
+ module Urls
10
+ private
11
+ # The following should work
12
+ #
13
+ # child_path(obj)
14
+ # child_path(obj, :id => 2) # where obj is then replaced by the obj with id 2
15
+ # child_path(:child_name, :id => 2) # where this is a literal build of the url
16
+ def child_url_parts(action = nil, child = nil, url_params = {})
17
+ child_base_parts(action, url_params) + [[child.is_a?(Symbol) ? child : child.class.name.underscore.to_sym, child], url_params]
18
+ end
19
+
20
+ def child_collection_url_parts(action = nil, child_name = nil, url_params = {})
21
+ child_base_parts(action, url_params) + [child_name, url_params]
22
+ end
23
+
24
+ # This determines if the child if off of an object or the collection. Most of the time,
25
+ # as assumed, it will be off of an object. But let's say you are at this url:
26
+ #
27
+ # /payments
28
+ #
29
+ # And you call this path:
30
+ #
31
+ # child_collection(:credit_cards)
32
+ #
33
+ # There is no object to be a child off, we are in the collection / index action. But
34
+ # we still want to call the following url:
35
+ #
36
+ # /payments/credit_cards
37
+ #
38
+ # That's what this method does, it makes the above possible. So you can still link
39
+ # to the "child" credit cards resource relatively, keeping the idea of contextual
40
+ # development intact. Maybe you only want to use payments as a context for the
41
+ # credit cards resource.
42
+ def child_base_parts(action, url_params)
43
+ object_to_use = (url_params.key?("#{model_name}_id".to_sym) && url_params["#{model_name}_id".to_sym]) || (id? && object)
44
+ base_parts = object_to_use || singleton? ? object_url_parts(action, object_to_use) : collection_url_parts(action)
45
+ base_parts.pop if base_parts.last.is_a?(Hash)
46
+ base_parts
47
+ end
48
+
49
+ #def current_object_to_use(url_params)
50
+ # result = (url_params.key?("#{model_name}_id".to_sym) && url_params["#{model_name}_id".to_sym]) || (id? && object)
51
+ # result ? result : nil
52
+ #end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,51 @@
1
+ # Nested and Polymorphic Resource Helpers
2
+ #
3
+ module Resourcelogic
4
+ module Context
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ add_acts_as_resource_module(Methods)
8
+ end
9
+ end
10
+
11
+ module Methods
12
+ def self.included(klass)
13
+ klass.helper_method :context, :contexts, :contexts_url_parts
14
+ klass.hide_action :context, :contexts
15
+ end
16
+
17
+ def context
18
+ @context ||= contexts.last
19
+ end
20
+
21
+ def contexts
22
+ return @contexts if defined?(@contexts)
23
+ path_parts = request.path.split("/")
24
+ path_parts.shift
25
+ @contexts = []
26
+ path_parts.each_with_index do |part, index|
27
+ break if model_name_from_path_part(part.split(".").first) == model_name
28
+ @contexts << (part.to_i > 0 ? @contexts.pop.to_s.singularize.to_sym : part.underscore.to_sym)
29
+ end
30
+ @contexts
31
+ end
32
+
33
+ private
34
+ def contexts_url_parts
35
+ return @contexts_url_parts if @contexts_url_parts
36
+ path_parts = request.path.split("/")
37
+ path_parts.shift
38
+ @contexts_url_parts = []
39
+ path_parts.each_with_index do |part, index|
40
+ break if model_name_from_path_part(part.split(".").first) == model_name
41
+ if part.to_i > 0
42
+ @contexts_url_parts << [route_name_from_path_part(@contexts_url_parts.pop), part.to_i]
43
+ else
44
+ @contexts_url_parts << part.underscore.to_sym
45
+ end
46
+ end
47
+ @contexts_url_parts
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ module Resourcelogic
2
+ class ContextOptions
3
+ extend Resourcelogic::Accessors
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ module Resourcelogic
2
+ class FailableActionOptions
3
+ extend Resourcelogic::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 :flash, :flash_now, :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,142 @@
1
+ module Resourcelogic
2
+ module Parent
3
+ def self.included(klass)
4
+ klass.class_eval do
5
+ extend Config
6
+ add_acts_as_resource_module(Urls)
7
+ add_acts_as_resource_module(Reflection)
8
+ end
9
+ end
10
+
11
+ module Config
12
+ def belongs_to(name = nil, options = {})
13
+ @belongs_to ||= {}
14
+ if name.nil?
15
+ @belongs_to
16
+ else
17
+ @belongs_to[name.to_sym] = options
18
+ end
19
+ end
20
+
21
+ def require_parent(value = nil)
22
+ rw_config(:require_parent, value, false)
23
+ end
24
+ end
25
+
26
+ module Urls
27
+ private
28
+ def parent_url_parts(action = nil, url_params = {})
29
+ [action] + contexts_url_parts + [url_params]
30
+ end
31
+
32
+ def parent_collection_url_parts(*args)
33
+ parent_url_parts(*args)
34
+ end
35
+ end
36
+
37
+ module Reflection
38
+ def self.included(klass)
39
+ klass.class_eval do
40
+ helper_method :parent?, :parent_model_name, :parent_object
41
+ before_filter :require_parent
42
+ end
43
+ end
44
+
45
+ private
46
+ def belongs_to
47
+ self.class.belongs_to
48
+ end
49
+
50
+ def parent_path_name
51
+ return @parent_path_name if defined?(@parent_path_name)
52
+ path_parts = request.path.split("/")
53
+ path_parts.reverse.each do |path_part|
54
+ next if path_part.blank?
55
+ if model_name_from_path_part(path_part) == parent_model_name
56
+ return @parent_path_name = path_part.to_sym
57
+ end
58
+ end
59
+ @parent_path_name = nil
60
+ end
61
+
62
+ def parent_route_name
63
+ return @parent_route_name if defined?(@parent_route_name)
64
+ path_parts = request.path.split("/")
65
+ path_parts.reverse.each do |path_part|
66
+ next if path_part.blank?
67
+ if model_name_from_path_part(path_part) == parent_model_name
68
+ return @parent_route_name = route_name_from_path_part(path_part)
69
+ end
70
+ end
71
+ @parent_route_name = parent_model_name
72
+ end
73
+
74
+ # Returns the type of the current parent
75
+ #
76
+ def parent_model_name
77
+ return @parent_model_name if defined?(@parent_model_name)
78
+ parent_from_path?
79
+ @parent_model_name
80
+ end
81
+
82
+ def parent_model
83
+ @parent_model ||= parent_model_name.to_s.camelize.constantize
84
+ end
85
+
86
+ # Returns the type of the current parent extracted form a request path
87
+ #
88
+ def parent_from_path?
89
+ return @parent_from_path if defined?(@parent_from_path)
90
+ belongs_to.each do |model_name, options|
91
+ request.path.split('/').reverse.each do |path_part|
92
+ possible_model_names(model_name).each_with_index do |possible_name, index|
93
+ if [possible_name.to_s, possible_name.to_s.pluralize].include?(path_part)
94
+ @parent_model_name = model_name
95
+ return @parent_from_path = true
96
+ end
97
+ end
98
+ end
99
+ end
100
+ @parent_from_path = false
101
+ end
102
+
103
+ # Returns true/false based on whether or not a parent is present.
104
+ #
105
+ def parent?
106
+ !parent_model_name.nil?
107
+ end
108
+
109
+ # Returns true/false based on whether or not a parent is a singleton.
110
+ #
111
+ def parent_singleton?
112
+ parent? && parent_id.nil?
113
+ end
114
+
115
+ # Returns the current parent param, if there is a parent. (i.e. params[:post_id])
116
+ def parent_id
117
+ params["#{parent_route_name}_id".to_sym]
118
+ end
119
+
120
+ # Returns the current parent object if a parent object is present.
121
+ #
122
+ def parent_object(reload = false)
123
+ return @parent_object if !reload && defined?(@parent_object)
124
+ if parent?
125
+ if parent_singleton? && respond_to?("current_#{parent_model_name}", true)
126
+ @parent_object = send("current_#{parent_model_name}")
127
+ elsif parent_singleton? && parent_scope.respond_to?(parent_model_name)
128
+ @parent_object = parent_scope.send(parent_model_name, reload)
129
+ else
130
+ @parent_object = parent_scope.find(parent_id)
131
+ end
132
+ else
133
+ @parent_object = nil
134
+ end
135
+ end
136
+
137
+ def require_parent
138
+ raise StandardError.new("A parent is required to access this resource and no parent was found") if !parent? && self.class.require_parent == true
139
+ end
140
+ end
141
+ end
142
+ end