rc_rails 2.1.0

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.
Files changed (98) hide show
  1. data/.gitignore +7 -0
  2. data/CHANGELOG +355 -0
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock.development +117 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +71 -0
  7. data/Rakefile +33 -0
  8. data/Todo.txt +1 -0
  9. data/lib/rc_rails.rb +9 -0
  10. data/lib/resources_controller/actions.rb +147 -0
  11. data/lib/resources_controller/active_record/saved.rb +15 -0
  12. data/lib/resources_controller/helper.rb +123 -0
  13. data/lib/resources_controller/include_actions.rb +37 -0
  14. data/lib/resources_controller/named_route_helper.rb +154 -0
  15. data/lib/resources_controller/railtie.rb +14 -0
  16. data/lib/resources_controller/request_path_introspection.rb +83 -0
  17. data/lib/resources_controller/resource_methods.rb +32 -0
  18. data/lib/resources_controller/singleton_actions.rb +21 -0
  19. data/lib/resources_controller/specification.rb +119 -0
  20. data/lib/resources_controller/version.rb +3 -0
  21. data/lib/resources_controller.rb +849 -0
  22. data/resources_controller.gemspec +29 -0
  23. data/spec/app/database.yml +5 -0
  24. data/spec/app/views/accounts/show.html.erb +0 -0
  25. data/spec/app/views/addresses/edit.html.erb +0 -0
  26. data/spec/app/views/addresses/index.html.erb +0 -0
  27. data/spec/app/views/addresses/new.html.erb +0 -0
  28. data/spec/app/views/addresses/show.html.erb +0 -0
  29. data/spec/app/views/admin/forums/create.html.erb +0 -0
  30. data/spec/app/views/admin/forums/destroy.html.erb +0 -0
  31. data/spec/app/views/admin/forums/edit.html.erb +0 -0
  32. data/spec/app/views/admin/forums/index.html.erb +0 -0
  33. data/spec/app/views/admin/forums/new.html.erb +0 -0
  34. data/spec/app/views/admin/forums/show.html.erb +0 -0
  35. data/spec/app/views/admin/forums/update.html.erb +0 -0
  36. data/spec/app/views/comments/edit.html.erb +0 -0
  37. data/spec/app/views/comments/index.html.erb +0 -0
  38. data/spec/app/views/comments/new.html.erb +0 -0
  39. data/spec/app/views/comments/show.html.erb +0 -0
  40. data/spec/app/views/forum_posts/edit.html.erb +0 -0
  41. data/spec/app/views/forum_posts/index.html.erb +0 -0
  42. data/spec/app/views/forum_posts/new.html.erb +0 -0
  43. data/spec/app/views/forum_posts/show.html.erb +0 -0
  44. data/spec/app/views/forums/create.html.erb +0 -0
  45. data/spec/app/views/forums/destroy.html.erb +0 -0
  46. data/spec/app/views/forums/edit.html.erb +0 -0
  47. data/spec/app/views/forums/index.html.erb +0 -0
  48. data/spec/app/views/forums/new.html.erb +0 -0
  49. data/spec/app/views/forums/show.html.erb +0 -0
  50. data/spec/app/views/forums/update.html.erb +0 -0
  51. data/spec/app/views/infos/edit.html.erb +0 -0
  52. data/spec/app/views/infos/show.html.erb +0 -0
  53. data/spec/app/views/interests/index.html.erb +0 -0
  54. data/spec/app/views/interests/show.html.erb +0 -0
  55. data/spec/app/views/owners/edit.html.erb +0 -0
  56. data/spec/app/views/owners/new.html.erb +0 -0
  57. data/spec/app/views/owners/show.html.erb +0 -0
  58. data/spec/app/views/tags/index.html.erb +0 -0
  59. data/spec/app/views/tags/new.html.erb +0 -0
  60. data/spec/app/views/tags/show.html.erb +0 -0
  61. data/spec/app/views/users/edit.html.erb +0 -0
  62. data/spec/app/views/users/index.html.erb +0 -0
  63. data/spec/app/views/users/show.html.erb +0 -0
  64. data/spec/app.rb +315 -0
  65. data/spec/controllers/accounts_controller_spec.rb +77 -0
  66. data/spec/controllers/addresses_controller_spec.rb +346 -0
  67. data/spec/controllers/admin_forums_controller_spec.rb +638 -0
  68. data/spec/controllers/comments_controller_spec.rb +380 -0
  69. data/spec/controllers/comments_controller_with_models_spec.rb +202 -0
  70. data/spec/controllers/forum_posts_controller_spec.rb +426 -0
  71. data/spec/controllers/forums_controller_spec.rb +694 -0
  72. data/spec/controllers/infos_controller_spec.rb +71 -0
  73. data/spec/controllers/interests_controller_via_forum_spec.rb +80 -0
  74. data/spec/controllers/interests_controller_via_user_spec.rb +114 -0
  75. data/spec/controllers/owners_controller_spec.rb +277 -0
  76. data/spec/controllers/resource_saved_spec.rb +47 -0
  77. data/spec/controllers/resource_service_in_forums_controller_spec.rb +37 -0
  78. data/spec/controllers/resource_service_in_infos_controller_spec.rb +36 -0
  79. data/spec/controllers/resource_service_in_interests_controller_via_forum_spec.rb +51 -0
  80. data/spec/controllers/tags_controller_spec.rb +83 -0
  81. data/spec/controllers/tags_controller_via_account_info_spec.rb +131 -0
  82. data/spec/controllers/tags_controller_via_forum_post_comment_spec.rb +144 -0
  83. data/spec/controllers/tags_controller_via_forum_post_spec.rb +133 -0
  84. data/spec/controllers/tags_controller_via_forum_spec.rb +173 -0
  85. data/spec/controllers/tags_controller_via_user_address_spec.rb +130 -0
  86. data/spec/controllers/users_controller_spec.rb +248 -0
  87. data/spec/lib/action_view_helper_spec.rb +143 -0
  88. data/spec/lib/bug_0001_spec.rb +22 -0
  89. data/spec/lib/include_actions_spec.rb +35 -0
  90. data/spec/lib/load_enclosing_resources_spec.rb +245 -0
  91. data/spec/lib/request_path_introspection_spec.rb +130 -0
  92. data/spec/lib/resource_methods_spec.rb +204 -0
  93. data/spec/lib/resources_controller_spec.rb +57 -0
  94. data/spec/models/comment_saved_spec.rb +24 -0
  95. data/spec/rspec_generator_task.rb +105 -0
  96. data/spec/spec_helper.rb +17 -0
  97. data/spec/verify_rcov.rb +52 -0
  98. metadata +193 -0
@@ -0,0 +1,147 @@
1
+ module ResourcesController
2
+ # standard CRUD actions, with html, js and xml responses, re-written to mnake best use of resources_cotroller.
3
+ # This helps if you're writing controllers that you want to share via mixin or inheritance.
4
+ #
5
+ # This module is used as the actions for the controller by default, but you can change this behaviour:
6
+ #
7
+ # resources_controller_for :foos, :actions_include => false # don't include any actions
8
+ # resources_controller_for :foos, :actions_include => Some::Other::Module # use this module instead
9
+ #
10
+ # == Why?
11
+ #
12
+ # The idea is to decouple the <b>model name</b> from the action code.
13
+ #
14
+ # Here's how:
15
+ #
16
+ # === finding and making new resources
17
+ # Instead of this:
18
+ # @post = Post.find(params[:id])
19
+ # @post = Post.new
20
+ # @posts = Post.find(:all)
21
+ #
22
+ # do this:
23
+ # self.resource = find_resource
24
+ # self.resource = new_resource
25
+ # self.resources = find_resources
26
+ #
27
+ # === referring to resources
28
+ # Instead of this:
29
+ # format.xml { render :xml => @post }
30
+ # format.xml { render :xml => @posts }
31
+ #
32
+ # do this:
33
+ # format.xml { render :xml => resource }
34
+ # format.xml { render :xml => resources }
35
+ #
36
+ # === urls
37
+ # Instead of this:
38
+ # redirect_to posts_url
39
+ # redirect_to new_post_url
40
+ #
41
+ # do this:
42
+ # redirect_to resources_url
43
+ # redirect_to new_resource_url
44
+ #
45
+ module Actions
46
+ # GET /events
47
+ # GET /events.xml
48
+ def index
49
+ self.resources = find_resources
50
+
51
+ respond_to do |format|
52
+ format.html # index.rhtml
53
+ format.js
54
+ format.xml { render :xml => resources }
55
+ end
56
+ end
57
+
58
+ # GET /events/1
59
+ # GET /events/1.xml
60
+ def show
61
+ self.resource = find_resource
62
+
63
+ respond_to do |format|
64
+ format.html # show.erb.html
65
+ format.js
66
+ format.xml { render :xml => resource }
67
+ end
68
+ end
69
+
70
+ # GET /events/new
71
+ def new
72
+ self.resource = new_resource
73
+
74
+ respond_to do |format|
75
+ format.html # new.html.erb
76
+ format.js
77
+ format.xml { render :xml => resource }
78
+ end
79
+ end
80
+
81
+ # GET /events/1/edit
82
+ def edit
83
+ self.resource = find_resource
84
+ respond_to do |format|
85
+ format.html # edit.html.erb
86
+ format.js
87
+ format.xml { render :xml => resource }
88
+ end
89
+ end
90
+
91
+ # POST /events
92
+ # POST /events.xml
93
+ def create
94
+ self.resource = new_resource
95
+
96
+ respond_to do |format|
97
+ if resource.save
98
+ format.html do
99
+ flash[:notice] = "#{resource_name.humanize} was successfully created."
100
+ redirect_to resource_url
101
+ end
102
+ format.js
103
+ format.xml { render :xml => resource, :status => :created, :location => resource_url }
104
+ else
105
+ format.html { render :action => "new" }
106
+ format.js { render :action => "new" }
107
+ format.xml { render :xml => resource.errors, :status => :unprocessable_entity }
108
+ end
109
+ end
110
+ end
111
+
112
+ # PUT /events/1
113
+ # PUT /events/1.xml
114
+ def update
115
+ self.resource = find_resource
116
+
117
+ respond_to do |format|
118
+ if resource.update_attributes(params[resource_name])
119
+ format.html do
120
+ flash[:notice] = "#{resource_name.humanize} was successfully updated."
121
+ redirect_to resource_url
122
+ end
123
+ format.js
124
+ format.xml { head :ok }
125
+ else
126
+ format.html { render :action => "edit" }
127
+ format.js { render :action => "edit" }
128
+ format.xml { render :xml => resource.errors, :status => :unprocessable_entity }
129
+ end
130
+ end
131
+ end
132
+
133
+ # DELETE /events/1
134
+ # DELETE /events/1.xml
135
+ def destroy
136
+ self.resource = destroy_resource
137
+ respond_to do |format|
138
+ format.html do
139
+ flash[:notice] = "#{resource_name.humanize} was successfully destroyed."
140
+ redirect_to resources_url
141
+ end
142
+ format.js
143
+ format.xml { head :ok }
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,15 @@
1
+ module ResourcesController
2
+ module ActiveRecord
3
+ module Saved
4
+ # returns true if this record is not new, and has no errors
5
+ def saved?
6
+ !new_record? && (@errors.nil? || errors.empty?)
7
+ end
8
+
9
+ # returns true if this instance has had validation (maybe via save) attempted
10
+ def validation_attempted?
11
+ !@errors.nil?
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,123 @@
1
+ module ResourcesController
2
+ # Often it won't be appropriate to re-use views, but
3
+ # sometimes it is. These helper methods enable reuse by referencing whatever resource the
4
+ # controller is for.
5
+ #
6
+ # ==== Example:
7
+ #
8
+ # instead of writing:
9
+ # <% for event in @events %>
10
+ # <%= link_to 'edit', edit_event_path(event) %>
11
+ #
12
+ # you may write:
13
+ # <% for event in resources %>
14
+ # <%= link_to 'edit', edit_resource_path(event) %>
15
+ #
16
+ # == Enclosing resource
17
+ #
18
+ # For controllers with enclosing resources instead of writing:
19
+ # <%= link_to 'back to Forum', forum_path(@forum) %>
20
+ #
21
+ # you may write: (which will work for any enclosing path)
22
+ # <%= link_to "back to #{enclosing_resource.class.name.titleize}", enclosing_resource_path %>
23
+ #
24
+ # == Enclosing named routes:
25
+ #
26
+ # In addition you can reference named routes that are 'below' the current resource
27
+ # by appending resource_ to that named route.
28
+ #
29
+ # ==== Example: shared polymorphic view
30
+ #
31
+ # Let's say you have a resource controller for tags, and you're writing the
32
+ # taggable views. In a view shared amongst taggables you can write
33
+ #
34
+ # <%= link_to 'tags', resource_tags_path %>
35
+ # <%= link_to 'edit tag', edit_resource_tag_path(@tag) %>
36
+ #
37
+ # or:
38
+ # <% for taggable in resources %>
39
+ # <%= link_to 'tags', resource_tags_path(taggable) %>
40
+ #
41
+ module Helper
42
+ def self.included(base)
43
+ base.class_eval do
44
+ alias_method_chain :method_missing, :named_route_helper
45
+ alias_method_chain :respond_to?, :named_route_helper
46
+ delegate :resource_name, :resources_name, :resource, :resources, :enclosing_resource, :enclosing_resource_name, :to => :controller
47
+ end
48
+ end
49
+
50
+ # DEPRECATED: you should just be able to use <tt>form_for resource</tt>
51
+ #
52
+ # Calls form_for with the apropriate action and method for the resource
53
+ #
54
+ # resource.new_record? is used to decide between a create or update action
55
+ #
56
+ # You can optionally pass a resource object, default is to use self.resource
57
+ #
58
+ # You may also override the url by passing <tt>:url</tt>, or pass extra options
59
+ # to resource path url with <tt>:url_options</tt>
60
+ #
61
+ # === Example
62
+ #
63
+ # <% form_for_resource do |f| %>
64
+ # <%= f.text_field :name %>
65
+ # <%= f.submit resource.new_record? ? 'Create' : 'Update'
66
+ # <% end %>
67
+ #
68
+ # <% for attachment in resources %>
69
+ # <% form_for_resource attachment, :html => {:multipart => true} %>
70
+ # <%= f.file_field :uploaded_data %>
71
+ # <%= f.submit 'Update' %>
72
+ # <% end %>
73
+ # <% end %>
74
+ #
75
+ def form_for_resource(*args, &block)
76
+ options = args.extract_options!
77
+ resource = args[0] || self.resource
78
+ form_for(resource_name, resource, form_for_resource_options(resource, options), &block)
79
+ end
80
+
81
+ # same API as form_for_resource
82
+ def remote_form_for_resource(*args, &block)
83
+ options = args.extract_options!
84
+ resource = args[0] || self.resource
85
+ remote_form_for(resource_name, resource, form_for_resource_options(resource, options), &block)
86
+ end
87
+
88
+ # print the error messages for the current resource
89
+ def error_messages_for_resource
90
+ error_messages_for resource_name
91
+ end
92
+
93
+ # Delegate named_route helper method to the controller. Create the delegation
94
+ # to short circuit the method_missing call for future invocations.
95
+ def method_missing_with_named_route_helper(method, *args, &block)
96
+ if controller.resource_named_route_helper_method?(method)
97
+ self.class.send(:delegate, method, :to => :controller)
98
+ controller.send(method, *args)
99
+ else
100
+ method_missing_without_named_route_helper(method, *args, &block)
101
+ end
102
+ end
103
+
104
+ # delegate url help method creation to the controller
105
+ def respond_to_with_named_route_helper?(*args)
106
+ respond_to_without_named_route_helper?(*args) || controller.resource_named_route_helper_method?(args.first)
107
+ end
108
+
109
+ private
110
+ def form_for_resource_options(resource, options)
111
+ options.dup.tap do |options|
112
+ options[:html] ||= {}
113
+ options[:html][:method] ||= resource.new_record? ? :post : :put
114
+ args = options[:url_options] ? [options.delete(:url_options)] : []
115
+ options[:url] ||= if resource.new_record?
116
+ controller.resource_specification.singleton? ? resource_path(*args) : resources_path(*args)
117
+ else
118
+ controller.resource_specification.singleton? ? resource_path(*args) : resource_path(*([resource] + args))
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,37 @@
1
+ module ResourcesController
2
+ # extension for any module that is used as an Actions module.
3
+ #
4
+ # After extending the module (say 'MyActions'), instead of doing this:
5
+ # self.include ActionsModule
6
+ # do this:
7
+ # ActionsModule.include_actions(self, <:only or :except options>)
8
+ #
9
+ # RC extends any actions module with this automatically, so you don't need to know about it.
10
+ #
11
+ # However, if you ahve any special behaviour in your actions module that is sensitive to
12
+ # :only and :except, you can define your own include_actions method on that module
13
+ # to effect this special behaviour.
14
+ module IncludeActions
15
+ def include_actions(controller, options = {})
16
+ options.assert_valid_keys(:only, :except)
17
+ raise ArgumentError, "you can only specify either :except or :only, not both" if options[:only] && options[:except]
18
+ mixin = self.clone
19
+ action_methods_to_remove(options).each {|a| mixin.remove_action_method(a) }
20
+ controller.send :include, mixin
21
+ end
22
+
23
+ def remove_action_method(action)
24
+ undef_method action
25
+ end
26
+
27
+ def action_methods_to_remove(options = {})
28
+ if options[:only]
29
+ instance_methods - Array(options[:only]).map(&:to_s)
30
+ elsif options[:except]
31
+ Array(options[:except]).map(&:to_s) & instance_methods
32
+ else
33
+ []
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,154 @@
1
+ module ResourcesController
2
+
3
+ class CantMapRoute < ArgumentError #:nodoc:
4
+ end
5
+
6
+ # This module provides methods are provided to aid in writing inheritable controllers.
7
+ #
8
+ # When writing an action that redirects to the list of resources, you may use *resources_url* and the controller
9
+ # will call the url_writer method appropriate to what the controller is a resources controller for.
10
+ #
11
+ # If the route specified requires a member argument and you don't provide it, the current resource is used.
12
+ #
13
+ # In general you may subsitute 'resource' for the current (maybe polymorphic) resource. e.g.
14
+ #
15
+ # You may also substitute 'enclosing_resource' to get urls for the enclosing resource
16
+ #
17
+ # (in attachable/attachments where attachable is a Post)
18
+ #
19
+ # resources_path # => post_attachments_path
20
+ # formatted_edit_resource_path('js') # => formatted_post_attachments_path(<current post>, <current attachment>, 'js')
21
+ # resource_tags_path # => post_attachments_tags_paths(<current post>, <current attachment>)
22
+ # resource_tags_path(foo) # => post_attachments_tags_paths(<current post>, foo)
23
+ #
24
+ # enclosing_resource_path # => post_path(<current post>)
25
+ # enclosing_resources_path # => posts_path
26
+ # enclosing_resource_tags_path # => post_tags_path(<current post>)
27
+ # enclosing_resource_path(2) # => post_path(2)
28
+ #
29
+ # The enclosing_resource stuff works with deep nesting if you're into that.
30
+ #
31
+ # These methods are defined as they are used. The ActionView Helper module delegates to the current controller to access these
32
+ # methods
33
+ module NamedRouteHelper
34
+ def self.included(base)
35
+ base.class_eval do
36
+ alias_method_chain :method_missing, :named_route_helper
37
+ alias_method_chain :respond_to?, :named_route_helper
38
+ end
39
+ base.hide_action *instance_methods
40
+ base.hide_action :method_missing_without_named_route_helper, :respond_to_without_named_route_helper?, :respond_to?
41
+ end
42
+
43
+ def method_missing_with_named_route_helper(method, *args, &block)
44
+ # TODO: test that methods are only defined once
45
+ if resource_named_route_helper_method?(method, raise_error = true)
46
+ define_resource_named_route_helper_method(method)
47
+ send(method, *args)
48
+ elsif resource_named_route_helper_method_for_name_prefix?(method)
49
+ define_resource_named_route_helper_method_for_name_prefix(method)
50
+ send(method, *args)
51
+ else
52
+ method_missing_without_named_route_helper(method, *args, &block)
53
+ end
54
+ end
55
+
56
+ def respond_to_with_named_route_helper?(*args)
57
+ respond_to_without_named_route_helper?(*args) || resource_named_route_helper_method?(args.first)
58
+ end
59
+
60
+ # return true if the passed method (e.g. 'resources_path') corresponds to a defined
61
+ # named route helper method
62
+ def resource_named_route_helper_method?(resource_method, raise_error = false)
63
+ if resource_method.to_s =~ /_(path|url)$/ && resource_method.to_s =~ /(^|^.*_)enclosing_resource(s)?_/
64
+ _, route_method = *route_and_method_from_enclosing_resource_method_and_name_prefix(resource_method, name_prefix)
65
+ elsif resource_method.to_s =~ /_(path|url)$/ && resource_method.to_s =~ /(^|^.*_)resource(s)?_/
66
+ _, route_method = *route_and_method_from_resource_method_and_name_prefix(resource_method, name_prefix)
67
+ else
68
+ return false
69
+ end
70
+ respond_to_without_named_route_helper?(route_method) || (raise_error && raise_resource_url_mapping_error(resource_method, route_method))
71
+ end
72
+
73
+ private
74
+ def raise_resource_url_mapping_error(resource_method, route_method)
75
+ raise CantMapRoute, <<-end_str
76
+ Tried to map :#{resource_method} to :#{route_method},
77
+ which doesn't exist. You may not have defined the route in config/routes.rb.
78
+
79
+ Or, if you have unconventianal route names or name prefixes, you may need
80
+ to explicictly set the :route option in resources_controller_for, and set
81
+ the :name_prefix option on your enclosing resources.
82
+
83
+ Currently:
84
+ :route is '#{route_name}'
85
+ generated name_prefix is '#{name_prefix}'
86
+ end_str
87
+ end
88
+
89
+ # passed something like (^|.*_)enclosing_resource(s)_.*(url|path)$, will
90
+ # return the [route, route_method] for the expanded resource
91
+ def route_and_method_from_enclosing_resource_method_and_name_prefix(method, name_prefix)
92
+ if enclosing_resource
93
+ enclosing_route = name_prefix.sub(/_$/,'')
94
+ route_method = method.to_s.sub(/enclosing_resource(s)?/) { $1 ? enclosing_route.pluralize : enclosing_route }
95
+ return [Rails.application.routes.named_routes.get(route_method.sub(/_(path|url)$/,'').to_sym), route_method]
96
+ else
97
+ raise NoMethodError, "Tried to map :#{method} but there is no enclosing_resource for this controller"
98
+ end
99
+ end
100
+
101
+ # passed something like (^|.*_)resource(s)_.*(url|path)$, will
102
+ # return the [route, route_method] for the expanded resource
103
+ def route_and_method_from_resource_method_and_name_prefix(method, name_prefix)
104
+ route_method = method.to_s.sub(/resource(s)?/) { $1 ? "#{name_prefix}#{route_name.pluralize}" : "#{name_prefix}#{route_name}" }
105
+ return [Rails.application.routes.named_routes.get(route_method.sub(/_(path|url)$/,'').to_sym), route_method]
106
+ end
107
+
108
+ # defines a method that calls the appropriate named route method, with appropraite args.
109
+ def define_resource_named_route_helper_method(method)
110
+ self.class.send :module_eval, <<-end_eval, __FILE__, __LINE__
111
+ def #{method}(*args)
112
+ send "#{method}_for_\#{name_prefix}", *args
113
+ end
114
+ end_eval
115
+ end
116
+
117
+ def resource_named_route_helper_method_for_name_prefix?(method)
118
+ method.to_s =~ /_for_.*$/ && resource_named_route_helper_method?(method.to_s.sub(/_for_.*$/,''))
119
+ end
120
+
121
+ def define_resource_named_route_helper_method_for_name_prefix(method)
122
+ resource_method = method.to_s.sub(/_for_.*$/,'')
123
+ name_prefix = method.to_s.sub(/^.*_for_/,'')
124
+ if resource_method =~ /enclosing_resource/
125
+ route, route_method = *route_and_method_from_enclosing_resource_method_and_name_prefix(resource_method, name_prefix)
126
+ required_args = (route.segment_keys - [:format]).size
127
+
128
+ self.class.send :module_eval, <<-end_eval, __FILE__, __LINE__
129
+ def #{method}(*args)
130
+ options = args.extract_options!
131
+ args = args.size < #{required_args} ? enclosing_collection_resources + args : enclosing_collection_resources - [enclosing_resource] + args
132
+ args = args + [options] if options.size > 0
133
+ send :#{route_method}, *args
134
+ end
135
+ end_eval
136
+
137
+ else
138
+ route, route_method = *route_and_method_from_resource_method_and_name_prefix(resource_method, name_prefix)
139
+ required_args = (route.segment_keys - [:format]).size
140
+
141
+ self.class.send :module_eval, <<-end_eval, __FILE__, __LINE__
142
+ def #{method}(*args)
143
+ options = args.extract_options!
144
+ #{"args = [resource] + args if enclosing_collection_resources.size + args.size < #{required_args}" if required_args > 0}
145
+ args = args + [options] if options.size > 0
146
+ send :#{route_method}, *(enclosing_collection_resources + args)
147
+ end
148
+ end_eval
149
+ end
150
+
151
+ self.class.send :private, method
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,14 @@
1
+ module ResourcesController
2
+ class Railtie < Rails::Railtie
3
+ initializer 'resources_controller' do
4
+ ActiveSupport.on_load(:action_controller) do
5
+ extend ResourcesController
6
+ include ResourcesController::RequestPathIntrospection
7
+ end
8
+
9
+ ActiveSupport.on_load(:active_record) do
10
+ include ResourcesController::ActiveRecord::Saved
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,83 @@
1
+ module ResourcesController
2
+ # included into ActionController::Base
3
+ #
4
+ # provides ability to determine what nesting segments are for a given request, and whether those segments are singletons,
5
+ # these methods are aware of resource specifications specified either by map_enclosing_resource.
6
+ module RequestPathIntrospection
7
+ protected
8
+ def request_path
9
+ @request_path ||= params[:resource_path] || request.path
10
+ end
11
+
12
+ def nesting_request_path
13
+ @nesting_request_path ||= remove_namespace(remove_current_segment(request_path))
14
+ end
15
+
16
+ # returns an array of hashes like {:segment => 'forum', :singleton => false}
17
+ def nesting_segments
18
+ @nesting_segments ||= segments_for_path_and_keys(nesting_request_path, param_keys)
19
+ end
20
+
21
+ # returns an array of segments correspopnding to the namespace of the controller.
22
+ # If your controller is at a non standard location wrt it's path, you can modify this array in a before filter
23
+ # to help resources_controller do the right thing
24
+ def namespace_segments
25
+ unless @namespace_segments
26
+ namespace = controller_path.sub(%r(#{controller_name}$), '')
27
+ @namespace_segments = (request_path =~ %r(^/#{namespace}) ? namespace.split('/') : [])
28
+ end
29
+ @namespace_segments
30
+ end
31
+
32
+ def param_keys
33
+ params.keys.map(&:to_s).select{|k| k[-3..-1] == '_id'}
34
+ end
35
+
36
+ private
37
+ def remove_current_segment(path)
38
+ if respond_to?(:resource_specification) && resource_specification.singleton?
39
+ path.sub(%r(/#{current_segment}(?!.*/#{current_segment}).*$), '')
40
+ else
41
+ path.sub(%r(/#{current_segment}(?!.+/#{current_segment}).*$), '')
42
+ end
43
+ end
44
+
45
+ def current_segment
46
+ respond_to?(:resource_specification) ? resource_specification.segment : controller_name
47
+ end
48
+
49
+ def remove_namespace(path)
50
+ if namespace_segments.any?
51
+ path.sub(%r(^/#{namespace_segments.join('/')}), '')
52
+ else
53
+ path
54
+ end
55
+ end
56
+
57
+ def segments_for_path_and_keys(path, keys)
58
+ key_segments = keys.map{|k| segment_for_key(k)}
59
+ path_segments = path[1..-1].to_s.split('/')
60
+ segments = []
61
+ while path_segments.any? do
62
+ segment = path_segments.shift
63
+ if key_segments.include?(segment)
64
+ segments << {:segment => segment, :singleton => false}
65
+ path_segments.shift # swallow following :id
66
+ else
67
+ segments << {:segment => segment, :singleton => true}
68
+ end
69
+ end
70
+ segments
71
+ end
72
+
73
+ def segment_for_key(key)
74
+ if respond_to?(:specifications) && spec = specifications.find{|s| s.respond_to?(:key) && s.key == key.to_s}
75
+ spec.segment
76
+ elsif spec = resource_specification_map.values.find{|s| s.key == key.to_s}
77
+ spec.segment
78
+ else
79
+ key.to_s[0..-4].pluralize
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,32 @@
1
+ module ResourcesController
2
+ # methods which communicate with the resource_service to find/create resources
3
+ module ResourceMethods
4
+ protected
5
+ # finds the collection of resources
6
+ def find_resources
7
+ resource_service.all
8
+ end
9
+
10
+ # finds the resource, using the passed id, defaults to the current params[:id]
11
+ def find_resource(id = nil)
12
+ id ||= respond_to?(:params) && params.is_a?(Hash) && params[:id]
13
+ resource_service.find id
14
+ end
15
+
16
+ # makes a new resource, if attributes are not supplied, determine them from the
17
+ # params hash and the current resource_class, or resource_name (the latter left in for BC)
18
+ def new_resource(attributes = nil, &block)
19
+ if attributes.blank? && respond_to?(:params) && params.is_a?(Hash)
20
+ resource_form_name = ActiveModel::Naming.singular(resource_class)
21
+ attributes = params[resource_form_name] || params[resource_name] || {}
22
+ end
23
+ resource_service.new attributes, &block
24
+ end
25
+
26
+ # destroys and returns the resource, using the passed id, defaults to the current params[:id]
27
+ def destroy_resource(id = nil)
28
+ id ||= respond_to?(:params) && params.is_a?(Hash) && params[:id]
29
+ resource_service.destroy id
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ module ResourcesController
2
+ module SingletonActions
3
+ include Actions
4
+
5
+ undef index
6
+
7
+ # DELETE /event
8
+ # DELETE /event.xml
9
+ def destroy
10
+ self.resource = destroy_resource
11
+ respond_to do |format|
12
+ format.html do
13
+ flash[:notice] = "#{resource_name.humanize} was successfully destroyed."
14
+ redirect_to enclosing_resource_url if enclosing_resource
15
+ end
16
+ format.js
17
+ format.xml { head :ok }
18
+ end
19
+ end
20
+ end
21
+ end