aeonscope-rest 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,28 @@
1
+ = v1.1.0
2
+
3
+ * Clarified the readme documentation.
4
+ * Updated the JavaScript code so that events are bound for current and future instances instead of just once.
5
+ * Fixed a JavaScript bug where deleting nested inputs on a new form would cause an exception.
6
+ * Updated the JavaScript code so that new records are initially hidden.
7
+ * Updated the JavaScript code so the last record of a form is not deleted. Contents will be cleared and hidden (if last element).
8
+ * Applied fade in/out effects when adding/destroying input forms.
9
+ * Removed the hiding of the delete link via the various resource helpers.
10
+ * Modified the JavaScript incrementNumber(string, position) function for better handling of complex nested Rails model forms.
11
+ * Renamed all forms of the "new" action UJS classes to a single "new" class. Use the data-type attribute to distinguish type.
12
+ * Applied "group" and "record" classes to distinguish between a group of records and a single record. Very handy for creating and/or destroying form elements.
13
+ * Renamed the various resource helpers to be aware of input elements (this affects the destroy helpers mostly).
14
+ * Added default option support for the Will Paginate gem requirement (use config/initializers/pagination.rb to change settings).
15
+ * Renamed all "show_" helpers to "render_" helpers.
16
+ * Added a render_show_link helper.
17
+ * Added a render_new_link helper.
18
+ * Added a render_edit_link helper.
19
+ * Added icons for show, new, edit, and destroy actions. All corresponding helpers default to these icons.
20
+ * Changed the rendering of partials to templates for all REST actions.
21
+ * Added the ability to supply an index template if you wish to override the default index action behavior.
22
+ * Added basic AJAX handling for the destroy action so that a response can be sent back to the JavaScript caller.
23
+ * Moved all JavaScript REST functionality into a proper jQuery plugin.
24
+ * Renamed the "ujs_setup" generator to "rest_setup".
25
+
1
26
  = v1.0.0
2
27
 
3
28
  * Initial version.
@@ -1,12 +1,12 @@
1
1
  = Overview
2
2
 
3
- Enables default REST functionality, more than what you get with Rails out-of-the-box, and keeps your code DRY. This means you can write code like this:
3
+ Enables default REST functionality, more than what you get with Rails out-of-the-box, to keep your code DRY. This means you can write code like this:
4
4
 
5
5
  class Posts < ApplicationController
6
6
  include Rest
7
7
  end
8
8
 
9
- Which would automatically yield the following REST actions:
9
+ Which would automatically add the following REST actions to your controller:
10
10
 
11
11
  * index
12
12
  * show
@@ -29,9 +29,9 @@ See the CHANGELOG file for more info.
29
29
 
30
30
  = Requirements
31
31
 
32
- 1. {Ruby on Rails}[http://rubyonrails.org] (automatically installed for you if you don't have 2.3.x or higher).
33
- 2. mislav-will_paginate[http://github.com/mislav/will_paginate/tree/master] gem (automatically installed for you).
34
- 3. Knowledge of the {Representational State Transfer (REST)}[http://en.wikipedia.com/wiki/REST]. Download and read {RESTful Rails}[http://www.b-simple.de/documents] if you need further info.
32
+ 1. Knowledge of {Representational State Transfer (REST)}[http://en.wikipedia.com/wiki/REST].
33
+ 2. {Ruby on Rails}[http://rubyonrails.org] (automatically installed for you if you don't have 2.3.x or higher).
34
+ 3. mislav-will_paginate[http://github.com/mislav/will_paginate/tree/master] gem (automatically installed for you).
35
35
 
36
36
  = Installation
37
37
 
@@ -42,11 +42,11 @@ Type the following from the command line to install:
42
42
 
43
43
  Update your environment.rb file to include the new gem:
44
44
 
45
- * config.gem "rest"
45
+ * config.gem "aeonscope-rest", :lib => "rest", :source => "http://gems.github.com"
46
46
 
47
- To apply unobtrusive jQuery support, run the following generator (TIP: suffix the command line with -h option for usage):
47
+ Then run the following generator to complete setup (TIP: suffix the command line with -h option for usage):
48
48
 
49
- * script/generate ujs_setup
49
+ * script/generate rest_setup
50
50
 
51
51
  = Usage
52
52
 
@@ -60,7 +60,11 @@ Example:
60
60
  include Rest
61
61
  end
62
62
 
63
- This will automatically create the seven REST actions (index, show, new, create, edit, update, and destroy) for your controller. The model (i.e. Post) and model instance (i.e. @posts or @post depending on the action) are automatically determined from the controller name and created for you as well which means you can immediately write the following code in your views:
63
+ This will automatically create the seven REST actions (index, show, new, create, edit, update, and destroy) for your controller. The model (i.e. Post) and model instance (i.e. @posts or @post depending on the action) are automatically determined from the controller name. Just make sure your routes are updated to reflect the new resource. For example:
64
+
65
+ map.resources :posts
66
+
67
+ This means you can immediately write the following code in your views:
64
68
 
65
69
  <b>index.html.erb</b>
66
70
 
@@ -91,7 +95,7 @@ This will automatically create the seven REST actions (index, show, new, create,
91
95
 
92
96
  <p><%= link_to "Back", :back %></p>
93
97
 
94
- <b>new.html.erb</b>
98
+ <b>new_or_edit.html.erb</b>
95
99
 
96
100
  <% form_for @post do |form| %>
97
101
  <%= form.error_messages :header_data => :strong, :header_message => "Error" %>
@@ -106,14 +110,10 @@ This will automatically create the seven REST actions (index, show, new, create,
106
110
  <%= form.text_area :content %>
107
111
  </div>
108
112
 
109
- <div><%= show_submit_and_cancel :cancel_options => posts_path %></div>
113
+ <div><%= submit_tag "Save", :class => "form-button" %> or <%= link_to "Cancel", posts_path %></div>
110
114
  <% end %>
111
115
 
112
- <b>edit.html.erb</b>
113
-
114
- Use the same code as shown in the new.html.erb view above. ;-) The Rails form_for helper will automatically determine what action to take as shown in the following code:
115
-
116
- <% form_for @post do |form| %>
116
+ Notice how the form_for[http://apidock.com/rails/ActionView/Helpers/FormHelper/form_for] helper method automatically determines what controller action to use based off the model object (i.e. @post). This allows you to use the same form for new and edit actions by creating one template called: new_or_edit.html.erb. Nice, eh?
117
117
 
118
118
  To customize the RESTful behavior of your controller, use any combination of these three macros:
119
119
 
@@ -133,7 +133,7 @@ Example:
133
133
  Here is the breakdown, line-by-line, of the example shown above:
134
134
 
135
135
  1. Enables restful behavior.
136
- 2. Identifies the controller as a nested resource of posts.
136
+ 2. Identifies the comments controller as a child of the posts controller (i.e. nested resource).
137
137
  3. Instead of using the default label "Comments", a customized label of "My Wicked Comments" is used instead.
138
138
  4. The "show" and "destroy" actions are disabled which means only the following actions will work: index, new, edit, create, and update.
139
139
 
@@ -142,28 +142,40 @@ Using the post and comment controller relationship as defined above, we can brea
142
142
  * *parent_key* = N/A
143
143
  * *parent_value* = N/A
144
144
  * *parent_resource_method* = N/A
145
- * *name* = "posts"
145
+ * *controller_class* = PostsController
146
+ * *controller_name* = "posts"
146
147
  * *label* = "Posts"
147
- * *controller* = PostsController
148
- * *model* = Post
149
- * *record* = #<Post id: 1, label: "Test", content: "Test", created_at: "2008-10-31 23:59:28", updated_at: "2008-10-31 23:59:28">
148
+ * *model_class* = Post
149
+ * *model_name* = post
150
+ * *model_object* = @post #<Post id: 1, label: "Test", content: "Test", created_at: "2008-10-31 23:59:28", updated_at: "2008-10-31 23:59:28">
150
151
  * *namespaces* = []
151
- * *show_partial* = "/posts/show"
152
- * *new_or_edit_partial* = "/posts/new_or_edit"
152
+ * *index_template* = "/posts/index"
153
+ * *show_template* = "/posts/show"
154
+ * *new_or_edit_template* = "/posts/new_or_edit"
153
155
 
154
156
  The comment (child) resource would have the following values:
155
-
157
+
156
158
  * *parent_key* = post_id
157
159
  * *parent_value* = 1
158
160
  * *parent_resource_method* = N/A
159
- * *name* = "comments"
161
+ * *controller_class* = CommentsController
162
+ * *controller_name* = "comments"
160
163
  * *label* = "My Wicked Comments"
161
- * *controller* = CommentsController
162
- * *model* = Comment
163
- * *record* = #<Post id: 1, post_id: nil, label: "Test", content: "Test", created_at: "2008-10-31 23:59:28", updated_at: "2008-10-31 23:59:28">
164
+ * *model_class* = Comment
165
+ * *model_name* = comment
166
+ * *model_object* = @comment #<Comment id: 1, post_id: 1, label: "Test", content: "Test", created_at: "2008-10-31 23:59:28", updated_at: "2008-10-31 23:59:28">
164
167
  * *namespaces* = []
165
- * *show_partial* = "/comments/show"
166
- * *new_or_edit_partial* = "/comments/new_or_edit"
168
+ * *index_template* = "/comments/index"
169
+ * *show_template* = "/comments/show"
170
+ * *new_or_edit_template* = "/comments/new_or_edit"
171
+
172
+ Resources
173
+
174
+ To learn more about REST in Rails, check out the following:
175
+
176
+ * {RESTful Rails}[http://www.b-simple.de/documents] - A PDF work downloading and reading that inspired me to write this gem.
177
+ * {Rails Routing}[http://guides.rails.info/routing.html] - The definitive guide to RESTful routing in Rails.
178
+ * {Taking Things Too Far}[http://www.therailsway.com/2009/6/22/taking-things-too-far-rest] - A good read on knowing when you have gone too far with REST API.
167
179
 
168
180
  = Contact/Feedback/Issues
169
181
 
data/Rakefile CHANGED
@@ -5,14 +5,14 @@ begin
5
5
  require 'jeweler'
6
6
  Jeweler::Tasks.new do |gem|
7
7
  gem.name = "rest"
8
- gem.summary = "Enables default REST functionality, more than what you get with Rails out-of-the-box, and keeps your code DRY."
8
+ gem.summary = "Enables default REST functionality, UJS support (jQuery), and keeps your code DRY."
9
9
  gem.description = "Ruby on Rails supports RESTful routing out-of-the-box. However, as you move along, you will often find yourself repeating the code for the seven REST actions: index, show, new, create, edit, update, destroy. This gem allows you to get all of the code for free by simply typing 'include Rest' at top of your controller code. That's it! Even better, you can have nested resources as well by adding a 'belongs_to' statement to your controllers much like you would for ActiveRecord models. This is just the tip of the iceburg so make sure to read the RDoc for more info."
10
10
  gem.authors = ["Brooke Kuhlmann"]
11
11
  gem.email = "aeonscope@gmail.com"
12
12
  gem.homepage = "http://github.com/aeonscope/rest"
13
13
  gem.required_ruby_version = ">= 1.8.6"
14
14
  gem.add_dependency "rails", ">= 2.3.2"
15
- gem.add_dependency "mislav-will_paginate", ">= 2.3.7"
15
+ gem.add_dependency "mislav-will_paginate", ">= 2.3.10"
16
16
  gem.rdoc_options << "CHANGELOG.rdoc"
17
17
  gem.files = FileList["[A-Z]*", "{bin,lib,generators,rails_generators,test,spec}/**/*"]
18
18
  end
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 1
3
- :minor: 0
3
+ :minor: 1
4
4
  :patch: 0
@@ -1,35 +1,35 @@
1
1
  module Rest
2
2
  module ClassMethods
3
- # Allows an object to belong to a parent object, thus creating a nested hierarchy. This behaves in a similar
4
- # fashion to the belongs_to ActiveRecord macro. Accepts the following parameters:
5
- # * *parent* - The parent symbol (including namespaces delimited by underscores). Be sure to reflect singular or plural forms on the name. Example: Public::PostsController, Result: :public_posts
3
+ # Describes the parent/child relationship of a resource. The behavior is similar to the belongs_to macro in ActiveRecord.
4
+ # * *parent* - The parent symbol (including namespaces delimited by underscores). Example: Public::PostsController, Result: :public_posts
6
5
  def belongs_to parent
7
6
  @parent_resource = parent.to_s
8
7
  class_eval "class << self; attr_accessor :parent_resource end"
9
8
  end
10
9
 
11
- # Allows one to specify the various options for a resource. The following options are accepted:
12
- # * *parent_key* - The ID key of the parent resource (for nested resources only). Defaults to: <belongs_to name>_id
13
- # * *parent_value* - The ID value of the parent resource (for nested resources only). Defaults to the record id.
14
- # * *parent_resource_method* - The instance method to call for acquiring child resource(s) of the parent (for nested resources only). Example: A post with many comments would be post.comments. Defaults to child resource name.
15
- # * *name* - The resource name. Defaults to the controller name.
16
- # * *label* - The resource label. Defaults to the controller name with capitalization.
17
- # * *controller* - The controller class. Defaults to the current class. You should not need to change this.
18
- # * *model* - The model class. Defaults to a model of the same name as the controller (minus namespace, suffix, and pluralization of course).
19
- # * *record* - The record (new or found) related to the resource.
20
- # * *namespaces* - The namespaces (if any) for routing. Defaults to whatever namespace your controller is using.
21
- # * *show_partial* - The show partial. Defaults to "/<namspace(s)>/<controller name>/_show".
22
- # * *new_or_edit_partial* - The new or edit partial. Defaults to "/<namspace(s)>/<controller name>/_new_or_edit".
10
+ # Allows one to specify the various options for a resource.
11
+ # * *parent_key* - Optional. The ID key of the parent resource (for nested resources only). Defaults to: <belongs_to name>_id
12
+ # * *parent_value* - Optional. The ID value of the parent resource (for nested resources only). Defaults to the record id.
13
+ # * *parent_resource_method* - Optional. The instance method to call for acquiring child resource(s) of the parent (for nested resources only). Example: A post with many comments would be post.comments. Defaults to child resource name.
14
+ # * *controller_class* - Optional. The controller class (for nested resources only). Defaults to the current class. You should never need to use this.
15
+ # * *controller_name* - Optional. The controller name. Defaults to the controller class name minus the "Controller" suffix (same behavior as used in routing). Example: PostsController => posts
16
+ # * *label* - Optional. The resource label. Defaults to the controller name with capitalization. Example: posts => Posts.
17
+ # * *model_class* - Optional. The model class. Defaults to the same name as the controller (minus namespace, suffix, and pluralization of course). Example: PostsController => Post
18
+ # * *model_name* - Optional. The model name. Defaults to the singularized version of the :controller_name. Example: posts (controller_name) => post (model_name).
19
+ # * *model_object* - Optional. The model object. The name defaults to the model_name. Example: post (model_name) => @post (model object). You should never need to use this.
20
+ # * *namespaces* - Optional. The namespaces (if any) for routing. Defaults to whatever namespace your controller is using.
21
+ # * *index_template* - Optional. The index template. Defaults to "/<namspace(s)>/<controller name>/index".
22
+ # * *show_template* - Optional. The show template. Defaults to "/<namspace(s)>/<controller name>/show".
23
+ # * *new_or_edit_template* - Optional. The new or edit template. Defaults to "/<namspace(s)>/<controller name>/new_or_edit".
23
24
  def resource_options options = {}
24
25
  @resource_options = options
25
26
  class_eval "class << self; attr_reader :resource_options end"
26
27
  end
27
28
 
28
- # Allows one to disable any number of default actions. Accepts the following parameters:
29
+ # Allows one to disable any number of default actions.
29
30
  # * *actions* - A variable list of REST action symbols you wish to disable. You may use any of the following: index, show, new, create, edit, update, and/or destroy.
30
31
  def disabled_actions *actions
31
32
  actions.uniq.each {|action| undef_method action}
32
33
  end
33
34
  end
34
35
  end
35
-
@@ -0,0 +1,261 @@
1
+ module Rest
2
+ module InstanceMethods
3
+ # Default index action.
4
+ # See config/initializers/pagination.rb to change default pagination settings.
5
+ def index
6
+ build_resources
7
+ if @resources.size > 1
8
+ # Model objects for a nested resource (act on the second-to-last parent).
9
+ parent = @resources[@resources.size - 2][:model_object]
10
+ mname = parent.class.name.underscore.pluralize
11
+ model_objects = parent.instance_eval("#{@resources.last[:parent_resource_method] || @resources.last[:controller_name]}").paginate PAGINATION_PARAM_NAME => params[PAGINATION_PARAM_NAME], :per_page => PAGINATION_PAGE_COUNT
12
+ else
13
+ # Model objects for a single resource.
14
+ if model_name
15
+ mname = model_name.pluralize
16
+ model_objects = model_class.all.paginate(PAGINATION_PARAM_NAME => params[PAGINATION_PARAM_NAME], :per_page => PAGINATION_PAGE_COUNT)
17
+ end
18
+ end
19
+ instance_variable_set "@#{mname}", model_objects if mname
20
+ render_action_template @resources.last[:index_template]
21
+ end
22
+
23
+ # Default show action.
24
+ def show
25
+ build_resources
26
+ render_action_template @resources.last[:show_template]
27
+ end
28
+
29
+ # Default new action.
30
+ def new
31
+ build_resources
32
+ render_new_or_edit_template
33
+ end
34
+
35
+ # Default edit action.
36
+ def edit
37
+ build_resources
38
+ render_new_or_edit_template
39
+ end
40
+
41
+ # Default create action.
42
+ def create
43
+ build_resources
44
+ if model_object.update_attributes params[model_symbol]
45
+ redirect_to build_resource_url(@resources)
46
+ else
47
+ render_new_or_edit_template
48
+ end
49
+ end
50
+
51
+ # Default update action.
52
+ def update
53
+ build_resources
54
+ if model_object.update_attributes params[model_symbol]
55
+ redirect_to build_resource_url(@resources)
56
+ else
57
+ render_new_or_edit_template
58
+ end
59
+ end
60
+
61
+ # Default destroy action.
62
+ def destroy
63
+ build_resources
64
+ model_object.destroy
65
+ @resources.last.delete :model_object
66
+ respond_to do |format|
67
+ format.html {redirect_to build_resource_url(@resources)}
68
+ format.js {render :text => "ok"}
69
+ end
70
+ end
71
+
72
+ protected
73
+
74
+ # Renders an action template.
75
+ def render_action_template template
76
+ unless template.blank?
77
+ symbol = model_symbol
78
+ object = model_object
79
+ if symbol && object
80
+ render :template => template, :locals => {symbol => object, :resources => @resources}
81
+ else
82
+ render :template => template, :locals => {:resources => @resources}
83
+ end
84
+ end
85
+ end
86
+
87
+ # Rendering a new/edit template.
88
+ def render_new_or_edit_template
89
+ render_action_template @resources.last[:new_or_edit_template]
90
+ end
91
+
92
+ # Builds the RESTful parent URL based on an array of resources.
93
+ def build_parent_url resources = []
94
+ namespaces = resources.last[:namespaces]
95
+ url = namespaces && !namespaces.empty? ? '/' + resources.last[:namespaces].join('/') : nil
96
+ if resources.size > 1
97
+ resources.slice(0...resources.size - 1).each do |resource|
98
+ url = [url, resource[:controller_name], resource[:parent_id]] * '/'
99
+ end
100
+ end
101
+ url
102
+ end
103
+
104
+ # Builds RESTful URLs based on an array of resources with the option to override the default action.
105
+ # This is also defined as a helper method (see base.rb).
106
+ def build_resource_url resources = [], action = :index
107
+ controller_name = resources.last[:controller_name]
108
+ id = resources.last[:model_object].id if resources.last[:model_object]
109
+ parent_url = build_parent_url(resources)
110
+ url = parent_url ? [parent_url, controller_name].join('/') : '/' + controller_name
111
+ case action
112
+ when :show then url = [url, id].compact * '/'
113
+ when :new then url = [url, "new"].compact * '/'
114
+ when :edit then url = [url, id, "edit"].compact * '/'
115
+ when :update then url = [url, id].compact.join('/') if controller_name == controller_name.pluralize
116
+ when :destroy then url = [url, id].compact.join('/') if controller_name == controller_name.pluralize
117
+ end
118
+ logger.debug "Resource URL: #{url}"
119
+ url
120
+ end
121
+
122
+ private
123
+
124
+ # Answers the current page from the session (if any).
125
+ def current_page
126
+ update_page
127
+ session[controller_symbol("page")] || 1
128
+ end
129
+
130
+ # Updates the current page for the session.
131
+ def update_page
132
+ key = controller_symbol("page")
133
+ case params[:action]
134
+ when "index" then session[key] = params[:page] if params[:page] && session[key] != params[:page]
135
+ when "show" then
136
+ ids = model_class.all.map {|model| model.id}
137
+ session[key] = (ids.index(params[:id].to_i) + 1) / PAGINATION_PAGE_COUNT
138
+ end
139
+ end
140
+
141
+ # Answers the class of the current model.
142
+ def model_class
143
+ @resources.last[:model_class]
144
+ end
145
+
146
+ # Answers the name of the current model.
147
+ def model_name
148
+ @resources.last[:model_name]
149
+ end
150
+
151
+ # Answers the symbol of the current model.
152
+ # NOTE: The symbol is always derived from the model class name (i.e. MasterPost => :master_post) rather
153
+ # than the :model_name resource option in order to comply with the auto-generated model attribute hash built by Rails.
154
+ # This is especially important when dealing with new, create, edit, and update actions.
155
+ def model_symbol
156
+ name = model_class.name.underscore
157
+ name ? name.to_sym : nil
158
+ end
159
+
160
+ # Answers the current model object.
161
+ def model_object
162
+ model_name ? instance_variable_get("@#{model_name}") : nil
163
+ end
164
+
165
+ # Answers the controller symbol
166
+ def controller_symbol suffix = nil
167
+ [self.class.to_s.underscore.gsub('/', '_').chomp("_controller"), suffix].compact.join('_').to_sym
168
+ end
169
+
170
+ # Convenience method for answering back a properly camelized controller name.
171
+ def camelize_controller_name name
172
+ name = name.gsub(/^(\w)/) {|c| c.capitalize}
173
+ name += "Controller" unless name.include? "Controller"
174
+ name
175
+ end
176
+
177
+ # Builds all resources for the controller(s).
178
+ def build_resources
179
+ @resources ||= []
180
+ if @resources.empty?
181
+ add_resource self.class.name, @resources
182
+ @resources.reverse!
183
+ end
184
+ # Convenience for accessing the current model object.
185
+ instance_variable_set "@#{model_name}", @resources.last[:model_object] if model_name
186
+ end
187
+
188
+ # Answers the child or parent controller namespace (array) and name (string).
189
+ # Accepts the following argruments:
190
+ # * *full_name* - The fully namespaced controller (i.e. PostsController) or parent name (i.e. protected_pages).
191
+ # Usage:
192
+ # * Input: Public::PostsController, Output: [Public], "PostsController"
193
+ # * Input: member_manage_posts, Output: [Member, Manage], "PostsController"
194
+ # * Input: posts, Output: nil, "PostsController"
195
+ def get_controller_namespaces_and_name full_name
196
+ if full_name
197
+ delimiter = full_name.include?("Controller") ? "::" : '_'
198
+ if full_name.include? delimiter
199
+ namespaces = full_name.split delimiter
200
+ name = camelize_controller_name namespaces.pop
201
+ return namespaces.collect(&:capitalize), name
202
+ else
203
+ return nil, camelize_controller_name(full_name)
204
+ end
205
+ end
206
+ end
207
+
208
+ # Recursively walks the controller belongs_to hierarchy (if any) adding new resources along the way.
209
+ def add_resource controller_name, resources
210
+ logger.debug "Adding resource '#{controller_name}' to resources (size = #{resources.size})."
211
+ resource = configure_resource controller_name
212
+ if resource
213
+ resources << resource
214
+ # Recursively add parents (if any).
215
+ if resource[:controller_class].methods.include? "parent_resource"
216
+ add_resource resource[:controller_class].parent_resource, resources
217
+ end
218
+ end
219
+ end
220
+
221
+ # Configures a resource via belongs_to name refrence in controller. By default the controller and model
222
+ # are assumed to use the same root name regardless of singular or plural context.
223
+ def configure_resource controller_name
224
+ namespaces, controller_name = get_controller_namespaces_and_name controller_name
225
+ controller_class = [namespaces, controller_name].compact.join("::").constantize
226
+ controller_name = controller_name.chomp("Controller").underscore
227
+ parent_key = controller_name.singularize + "_id"
228
+ resource = controller_class.resource_options || {}
229
+ resource.reverse_merge! :parent_key => parent_key,
230
+ :parent_id => params[parent_key],
231
+ :controller_name => controller_name,
232
+ :controller_class => controller_class,
233
+ :label => controller_name.capitalize,
234
+ :model_name => controller_name.singularize,
235
+ :namespaces => (namespaces.collect(&:downcase) if namespaces),
236
+ :new_or_edit_template => '/' + [namespaces, controller_name, "new_or_edit"].compact.join('/').downcase
237
+ begin
238
+ resource.reverse_merge! :model_class => controller_name.singularize.camelize.constantize
239
+ rescue NameError
240
+ logger.warn "Unable to constantize: " + controller_name
241
+ end
242
+ add_model_object resource
243
+ end
244
+
245
+ # Adds the current model object to the resource based on position in relationship chain.
246
+ def add_model_object resource
247
+ if resource[:model_class]
248
+ if params.include? resource[:parent_key]
249
+ # Nested parent.
250
+ resource[:model_object] = resource[:model_class].find resource[:parent_id] if resource[:parent_id]
251
+ else
252
+ # Single resource and/or end of nested chain.
253
+ resource[:model_object] = params[:id] ? resource[:model_class].find(params[:id]) : resource[:model_class].new
254
+ end
255
+ else
256
+ logger.error "Invalid model class. Check that your controller and model are of the same name, otherwise specify the model as a resource option."
257
+ end
258
+ resource
259
+ end
260
+ end
261
+ end