halorgium-actionpack 3.0.pre

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 (154) hide show
  1. data/CHANGELOG +5179 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +409 -0
  4. data/lib/abstract_controller.rb +16 -0
  5. data/lib/abstract_controller/base.rb +158 -0
  6. data/lib/abstract_controller/callbacks.rb +113 -0
  7. data/lib/abstract_controller/exceptions.rb +12 -0
  8. data/lib/abstract_controller/helpers.rb +151 -0
  9. data/lib/abstract_controller/layouts.rb +250 -0
  10. data/lib/abstract_controller/localized_cache.rb +49 -0
  11. data/lib/abstract_controller/logger.rb +61 -0
  12. data/lib/abstract_controller/rendering_controller.rb +188 -0
  13. data/lib/action_controller.rb +72 -0
  14. data/lib/action_controller/base.rb +168 -0
  15. data/lib/action_controller/caching.rb +80 -0
  16. data/lib/action_controller/caching/actions.rb +163 -0
  17. data/lib/action_controller/caching/fragments.rb +116 -0
  18. data/lib/action_controller/caching/pages.rb +154 -0
  19. data/lib/action_controller/caching/sweeping.rb +97 -0
  20. data/lib/action_controller/deprecated.rb +4 -0
  21. data/lib/action_controller/deprecated/integration_test.rb +2 -0
  22. data/lib/action_controller/deprecated/performance_test.rb +1 -0
  23. data/lib/action_controller/dispatch/dispatcher.rb +57 -0
  24. data/lib/action_controller/metal.rb +129 -0
  25. data/lib/action_controller/metal/benchmarking.rb +73 -0
  26. data/lib/action_controller/metal/compatibility.rb +145 -0
  27. data/lib/action_controller/metal/conditional_get.rb +86 -0
  28. data/lib/action_controller/metal/configuration.rb +28 -0
  29. data/lib/action_controller/metal/cookies.rb +105 -0
  30. data/lib/action_controller/metal/exceptions.rb +55 -0
  31. data/lib/action_controller/metal/filter_parameter_logging.rb +77 -0
  32. data/lib/action_controller/metal/flash.rb +162 -0
  33. data/lib/action_controller/metal/head.rb +27 -0
  34. data/lib/action_controller/metal/helpers.rb +115 -0
  35. data/lib/action_controller/metal/hide_actions.rb +47 -0
  36. data/lib/action_controller/metal/http_authentication.rb +312 -0
  37. data/lib/action_controller/metal/layouts.rb +171 -0
  38. data/lib/action_controller/metal/mime_responds.rb +317 -0
  39. data/lib/action_controller/metal/rack_convenience.rb +27 -0
  40. data/lib/action_controller/metal/redirector.rb +22 -0
  41. data/lib/action_controller/metal/render_options.rb +103 -0
  42. data/lib/action_controller/metal/rendering_controller.rb +57 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +108 -0
  44. data/lib/action_controller/metal/rescuable.rb +13 -0
  45. data/lib/action_controller/metal/responder.rb +200 -0
  46. data/lib/action_controller/metal/session.rb +15 -0
  47. data/lib/action_controller/metal/session_management.rb +45 -0
  48. data/lib/action_controller/metal/streaming.rb +188 -0
  49. data/lib/action_controller/metal/testing.rb +39 -0
  50. data/lib/action_controller/metal/url_for.rb +41 -0
  51. data/lib/action_controller/metal/verification.rb +130 -0
  52. data/lib/action_controller/middleware.rb +38 -0
  53. data/lib/action_controller/notifications.rb +10 -0
  54. data/lib/action_controller/polymorphic_routes.rb +183 -0
  55. data/lib/action_controller/record_identifier.rb +91 -0
  56. data/lib/action_controller/testing/process.rb +111 -0
  57. data/lib/action_controller/testing/test_case.rb +345 -0
  58. data/lib/action_controller/translation.rb +13 -0
  59. data/lib/action_controller/url_rewriter.rb +204 -0
  60. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  61. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  62. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +176 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  65. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  66. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  67. data/lib/action_dispatch.rb +70 -0
  68. data/lib/action_dispatch/http/headers.rb +33 -0
  69. data/lib/action_dispatch/http/mime_type.rb +231 -0
  70. data/lib/action_dispatch/http/mime_types.rb +23 -0
  71. data/lib/action_dispatch/http/request.rb +539 -0
  72. data/lib/action_dispatch/http/response.rb +290 -0
  73. data/lib/action_dispatch/http/status_codes.rb +42 -0
  74. data/lib/action_dispatch/http/utils.rb +20 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +50 -0
  76. data/lib/action_dispatch/middleware/params_parser.rb +79 -0
  77. data/lib/action_dispatch/middleware/rescue.rb +26 -0
  78. data/lib/action_dispatch/middleware/session/abstract_store.rb +208 -0
  79. data/lib/action_dispatch/middleware/session/cookie_store.rb +235 -0
  80. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +47 -0
  81. data/lib/action_dispatch/middleware/show_exceptions.rb +143 -0
  82. data/lib/action_dispatch/middleware/stack.rb +116 -0
  83. data/lib/action_dispatch/middleware/static.rb +44 -0
  84. data/lib/action_dispatch/middleware/string_coercion.rb +29 -0
  85. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +24 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +26 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +10 -0
  88. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +29 -0
  89. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +2 -0
  90. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +10 -0
  91. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +21 -0
  92. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +2 -0
  93. data/lib/action_dispatch/routing.rb +381 -0
  94. data/lib/action_dispatch/routing/deprecated_mapper.rb +878 -0
  95. data/lib/action_dispatch/routing/mapper.rb +327 -0
  96. data/lib/action_dispatch/routing/route.rb +49 -0
  97. data/lib/action_dispatch/routing/route_set.rb +497 -0
  98. data/lib/action_dispatch/testing/assertions.rb +8 -0
  99. data/lib/action_dispatch/testing/assertions/dom.rb +35 -0
  100. data/lib/action_dispatch/testing/assertions/model.rb +19 -0
  101. data/lib/action_dispatch/testing/assertions/response.rb +145 -0
  102. data/lib/action_dispatch/testing/assertions/routing.rb +144 -0
  103. data/lib/action_dispatch/testing/assertions/selector.rb +639 -0
  104. data/lib/action_dispatch/testing/assertions/tag.rb +123 -0
  105. data/lib/action_dispatch/testing/integration.rb +504 -0
  106. data/lib/action_dispatch/testing/performance_test.rb +15 -0
  107. data/lib/action_dispatch/testing/test_request.rb +83 -0
  108. data/lib/action_dispatch/testing/test_response.rb +131 -0
  109. data/lib/action_pack.rb +24 -0
  110. data/lib/action_pack/version.rb +9 -0
  111. data/lib/action_view.rb +58 -0
  112. data/lib/action_view/base.rb +308 -0
  113. data/lib/action_view/context.rb +44 -0
  114. data/lib/action_view/erb/util.rb +48 -0
  115. data/lib/action_view/helpers.rb +62 -0
  116. data/lib/action_view/helpers/active_model_helper.rb +306 -0
  117. data/lib/action_view/helpers/ajax_helper.rb +68 -0
  118. data/lib/action_view/helpers/asset_tag_helper.rb +830 -0
  119. data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
  120. data/lib/action_view/helpers/cache_helper.rb +39 -0
  121. data/lib/action_view/helpers/capture_helper.rb +168 -0
  122. data/lib/action_view/helpers/date_helper.rb +988 -0
  123. data/lib/action_view/helpers/debug_helper.rb +38 -0
  124. data/lib/action_view/helpers/form_helper.rb +1102 -0
  125. data/lib/action_view/helpers/form_options_helper.rb +600 -0
  126. data/lib/action_view/helpers/form_tag_helper.rb +495 -0
  127. data/lib/action_view/helpers/javascript_helper.rb +208 -0
  128. data/lib/action_view/helpers/number_helper.rb +311 -0
  129. data/lib/action_view/helpers/prototype_helper.rb +1309 -0
  130. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  131. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  132. data/lib/action_view/helpers/record_tag_helper.rb +58 -0
  133. data/lib/action_view/helpers/sanitize_helper.rb +259 -0
  134. data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
  135. data/lib/action_view/helpers/tag_helper.rb +151 -0
  136. data/lib/action_view/helpers/text_helper.rb +594 -0
  137. data/lib/action_view/helpers/translation_helper.rb +39 -0
  138. data/lib/action_view/helpers/url_helper.rb +639 -0
  139. data/lib/action_view/locale/en.yml +117 -0
  140. data/lib/action_view/paths.rb +80 -0
  141. data/lib/action_view/render/partials.rb +342 -0
  142. data/lib/action_view/render/rendering.rb +134 -0
  143. data/lib/action_view/safe_buffer.rb +28 -0
  144. data/lib/action_view/template/error.rb +101 -0
  145. data/lib/action_view/template/handler.rb +36 -0
  146. data/lib/action_view/template/handlers.rb +52 -0
  147. data/lib/action_view/template/handlers/builder.rb +17 -0
  148. data/lib/action_view/template/handlers/erb.rb +53 -0
  149. data/lib/action_view/template/handlers/rjs.rb +18 -0
  150. data/lib/action_view/template/resolver.rb +165 -0
  151. data/lib/action_view/template/template.rb +131 -0
  152. data/lib/action_view/template/text.rb +38 -0
  153. data/lib/action_view/test_case.rb +163 -0
  154. metadata +236 -0
@@ -0,0 +1,878 @@
1
+ module ActionDispatch
2
+ module Routing
3
+ # Mapper instances are used to build routes. The object passed to the draw
4
+ # block in config/routes.rb is a Mapper instance.
5
+ #
6
+ # Mapper instances have relatively few instance methods, in order to avoid
7
+ # clashes with named routes.
8
+ #
9
+ # == Overview
10
+ #
11
+ # ActionController::Resources are a way of defining RESTful \resources. A RESTful \resource, in basic terms,
12
+ # is something that can be pointed at and it will respond with a representation of the data requested.
13
+ # In real terms this could mean a user with a browser requests an HTML page, or that a desktop application
14
+ # requests XML data.
15
+ #
16
+ # RESTful design is based on the assumption that there are four generic verbs that a user of an
17
+ # application can request from a \resource (the noun).
18
+ #
19
+ # \Resources can be requested using four basic HTTP verbs (GET, POST, PUT, DELETE), the method used
20
+ # denotes the type of action that should take place.
21
+ #
22
+ # === The Different Methods and their Usage
23
+ #
24
+ # * GET - Requests for a \resource, no saving or editing of a \resource should occur in a GET request.
25
+ # * POST - Creation of \resources.
26
+ # * PUT - Editing of attributes on a \resource.
27
+ # * DELETE - Deletion of a \resource.
28
+ #
29
+ # === Examples
30
+ #
31
+ # # A GET request on the Posts resource is asking for all Posts
32
+ # GET /posts
33
+ #
34
+ # # A GET request on a single Post resource is asking for that particular Post
35
+ # GET /posts/1
36
+ #
37
+ # # A POST request on the Posts resource is asking for a Post to be created with the supplied details
38
+ # POST /posts # with => { :post => { :title => "My Whizzy New Post", :body => "I've got a brand new combine harvester" } }
39
+ #
40
+ # # A PUT request on a single Post resource is asking for a Post to be updated
41
+ # PUT /posts # with => { :id => 1, :post => { :title => "Changed Whizzy Title" } }
42
+ #
43
+ # # A DELETE request on a single Post resource is asking for it to be deleted
44
+ # DELETE /posts # with => { :id => 1 }
45
+ #
46
+ # By using the REST convention, users of our application can assume certain things about how the data
47
+ # is requested and how it is returned. Rails simplifies the routing part of RESTful design by
48
+ # supplying you with methods to create them in your routes.rb file.
49
+ #
50
+ # Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer
51
+ class DeprecatedMapper #:doc:
52
+ def initialize(set) #:nodoc:
53
+ @set = set
54
+ end
55
+
56
+ # Create an unnamed route with the provided +path+ and +options+. See
57
+ # ActionDispatch::Routing for an introduction to routes.
58
+ def connect(path, options = {})
59
+ options = options.dup
60
+
61
+ if conditions = options.delete(:conditions)
62
+ conditions = conditions.dup
63
+ method = [conditions.delete(:method)].flatten.compact
64
+ method.map! { |m|
65
+ m = m.to_s.upcase
66
+
67
+ if m == "HEAD"
68
+ raise ArgumentError, "HTTP method HEAD is invalid in route conditions. Rails processes HEAD requests the same as GETs, returning just the response headers"
69
+ end
70
+
71
+ unless HTTP_METHODS.include?(m.downcase.to_sym)
72
+ raise ArgumentError, "Invalid HTTP method specified in route conditions"
73
+ end
74
+
75
+ m
76
+ }
77
+
78
+ if method.length > 1
79
+ method = Regexp.union(*method)
80
+ elsif method.length == 1
81
+ method = method.first
82
+ else
83
+ method = nil
84
+ end
85
+ end
86
+
87
+ path_prefix = options.delete(:path_prefix)
88
+ name_prefix = options.delete(:name_prefix)
89
+ namespace = options.delete(:namespace)
90
+
91
+ name = options.delete(:_name)
92
+ name = "#{name_prefix}#{name}" if name_prefix
93
+
94
+ requirements = options.delete(:requirements) || {}
95
+ defaults = options.delete(:defaults) || {}
96
+ options.each do |k, v|
97
+ if v.is_a?(Regexp)
98
+ if value = options.delete(k)
99
+ requirements[k.to_sym] = value
100
+ end
101
+ else
102
+ value = options.delete(k)
103
+ defaults[k.to_sym] = value.is_a?(Symbol) ? value : value.to_param
104
+ end
105
+ end
106
+
107
+ requirements.each do |_, requirement|
108
+ if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
109
+ raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
110
+ end
111
+ if requirement.multiline?
112
+ raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
113
+ end
114
+ end
115
+
116
+ possible_names = Routing.possible_controllers.collect { |n| Regexp.escape(n) }
117
+ requirements[:controller] ||= Regexp.union(*possible_names)
118
+
119
+ if defaults[:controller]
120
+ defaults[:action] ||= 'index'
121
+ defaults[:controller] = defaults[:controller].to_s
122
+ defaults[:controller] = "#{namespace}#{defaults[:controller]}" if namespace
123
+ end
124
+
125
+ if defaults[:action]
126
+ defaults[:action] = defaults[:action].to_s
127
+ end
128
+
129
+ if path.is_a?(String)
130
+ path = "#{path_prefix}/#{path}" if path_prefix
131
+ path = path.gsub('.:format', '(.:format)')
132
+ path = optionalize_trailing_dynamic_segments(path, requirements, defaults)
133
+ glob = $1.to_sym if path =~ /\/\*(\w+)$/
134
+ path = ::Rack::Mount::Utils.normalize_path(path)
135
+
136
+ if glob && !defaults[glob].blank?
137
+ raise ActionController::RoutingError, "paths cannot have non-empty default values"
138
+ end
139
+ end
140
+
141
+ app = Routing::RouteSet::Dispatcher.new(:defaults => defaults, :glob => glob)
142
+
143
+ conditions = {}
144
+ conditions[:request_method] = method if method
145
+ conditions[:path_info] = path if path
146
+
147
+ @set.add_route(app, conditions, requirements, defaults, name)
148
+ end
149
+
150
+ def optionalize_trailing_dynamic_segments(path, requirements, defaults) #:nodoc:
151
+ path = (path =~ /^\//) ? path.dup : "/#{path}"
152
+ optional, segments = true, []
153
+
154
+ required_segments = requirements.keys
155
+ required_segments -= defaults.keys.compact
156
+
157
+ old_segments = path.split('/')
158
+ old_segments.shift
159
+ length = old_segments.length
160
+
161
+ old_segments.reverse.each_with_index do |segment, index|
162
+ required_segments.each do |required|
163
+ if segment =~ /#{required}/
164
+ optional = false
165
+ break
166
+ end
167
+ end
168
+
169
+ if optional
170
+ if segment == ":id" && segments.include?(":action")
171
+ optional = false
172
+ elsif segment == ":controller" || segment == ":action" || segment == ":id"
173
+ # Ignore
174
+ elsif !(segment =~ /^:\w+$/) &&
175
+ !(segment =~ /^:\w+\(\.:format\)$/)
176
+ optional = false
177
+ elsif segment =~ /^:(\w+)$/
178
+ if defaults.has_key?($1.to_sym)
179
+ defaults.delete($1.to_sym)
180
+ else
181
+ optional = false
182
+ end
183
+ end
184
+ end
185
+
186
+ if optional && index < length - 1
187
+ segments.unshift('(/', segment)
188
+ segments.push(')')
189
+ elsif optional
190
+ segments.unshift('/(', segment)
191
+ segments.push(')')
192
+ else
193
+ segments.unshift('/', segment)
194
+ end
195
+ end
196
+
197
+ segments.join
198
+ end
199
+ private :optionalize_trailing_dynamic_segments
200
+
201
+ # Creates a named route called "root" for matching the root level request.
202
+ def root(options = {})
203
+ if options.is_a?(Symbol)
204
+ if source_route = @set.named_routes.routes[options]
205
+ options = source_route.defaults.merge({ :conditions => source_route.conditions })
206
+ end
207
+ end
208
+ named_route("root", '', options)
209
+ end
210
+
211
+ def named_route(name, path, options = {}) #:nodoc:
212
+ options[:_name] = name
213
+ connect(path, options)
214
+ end
215
+
216
+ # Enables the use of resources in a module by setting the name_prefix, path_prefix, and namespace for the model.
217
+ # Example:
218
+ #
219
+ # map.namespace(:admin) do |admin|
220
+ # admin.resources :products,
221
+ # :has_many => [ :tags, :images, :variants ]
222
+ # end
223
+ #
224
+ # This will create +admin_products_url+ pointing to "admin/products", which will look for an Admin::ProductsController.
225
+ # It'll also create +admin_product_tags_url+ pointing to "admin/products/#{product_id}/tags", which will look for
226
+ # Admin::TagsController.
227
+ def namespace(name, options = {}, &block)
228
+ if options[:namespace]
229
+ with_options({:path_prefix => "#{options.delete(:path_prefix)}/#{name}", :name_prefix => "#{options.delete(:name_prefix)}#{name}_", :namespace => "#{options.delete(:namespace)}#{name}/" }.merge(options), &block)
230
+ else
231
+ with_options({:path_prefix => name, :name_prefix => "#{name}_", :namespace => "#{name}/" }.merge(options), &block)
232
+ end
233
+ end
234
+
235
+ def method_missing(route_name, *args, &proc) #:nodoc:
236
+ super unless args.length >= 1 && proc.nil?
237
+ named_route(route_name, *args)
238
+ end
239
+
240
+ INHERITABLE_OPTIONS = :namespace, :shallow
241
+
242
+ class Resource #:nodoc:
243
+ DEFAULT_ACTIONS = :index, :create, :new, :edit, :show, :update, :destroy
244
+
245
+ attr_reader :collection_methods, :member_methods, :new_methods
246
+ attr_reader :path_prefix, :name_prefix, :path_segment
247
+ attr_reader :plural, :singular
248
+ attr_reader :options
249
+
250
+ def initialize(entities, options)
251
+ @plural ||= entities
252
+ @singular ||= options[:singular] || plural.to_s.singularize
253
+ @path_segment = options.delete(:as) || @plural
254
+
255
+ @options = options
256
+
257
+ arrange_actions
258
+ add_default_actions
259
+ set_allowed_actions
260
+ set_prefixes
261
+ end
262
+
263
+ def controller
264
+ @controller ||= "#{options[:namespace]}#{(options[:controller] || plural).to_s}"
265
+ end
266
+
267
+ def requirements(with_id = false)
268
+ @requirements ||= @options[:requirements] || {}
269
+ @id_requirement ||= { :id => @requirements.delete(:id) || /[^#{Routing::SEPARATORS.join}]+/ }
270
+
271
+ with_id ? @requirements.merge(@id_requirement) : @requirements
272
+ end
273
+
274
+ def conditions
275
+ @conditions ||= @options[:conditions] || {}
276
+ end
277
+
278
+ def path
279
+ @path ||= "#{path_prefix}/#{path_segment}"
280
+ end
281
+
282
+ def new_path
283
+ new_action = self.options[:path_names][:new] if self.options[:path_names]
284
+ new_action ||= ActionController::Base.resources_path_names[:new]
285
+ @new_path ||= "#{path}/#{new_action}"
286
+ end
287
+
288
+ def shallow_path_prefix
289
+ @shallow_path_prefix ||= @options[:shallow] ? @options[:namespace].try(:sub, /\/$/, '') : path_prefix
290
+ end
291
+
292
+ def member_path
293
+ @member_path ||= "#{shallow_path_prefix}/#{path_segment}/:id"
294
+ end
295
+
296
+ def nesting_path_prefix
297
+ @nesting_path_prefix ||= "#{shallow_path_prefix}/#{path_segment}/:#{singular}_id"
298
+ end
299
+
300
+ def shallow_name_prefix
301
+ @shallow_name_prefix ||= @options[:shallow] ? @options[:namespace].try(:gsub, /\//, '_') : name_prefix
302
+ end
303
+
304
+ def nesting_name_prefix
305
+ "#{shallow_name_prefix}#{singular}_"
306
+ end
307
+
308
+ def action_separator
309
+ @action_separator ||= ActionController::Base.resource_action_separator
310
+ end
311
+
312
+ def uncountable?
313
+ @singular.to_s == @plural.to_s
314
+ end
315
+
316
+ def has_action?(action)
317
+ !DEFAULT_ACTIONS.include?(action) || action_allowed?(action)
318
+ end
319
+
320
+ protected
321
+ def arrange_actions
322
+ @collection_methods = arrange_actions_by_methods(options.delete(:collection))
323
+ @member_methods = arrange_actions_by_methods(options.delete(:member))
324
+ @new_methods = arrange_actions_by_methods(options.delete(:new))
325
+ end
326
+
327
+ def add_default_actions
328
+ add_default_action(member_methods, :get, :edit)
329
+ add_default_action(new_methods, :get, :new)
330
+ end
331
+
332
+ def set_allowed_actions
333
+ only, except = @options.values_at(:only, :except)
334
+ @allowed_actions ||= {}
335
+
336
+ if only == :all || except == :none
337
+ only = nil
338
+ except = []
339
+ elsif only == :none || except == :all
340
+ only = []
341
+ except = nil
342
+ end
343
+
344
+ if only
345
+ @allowed_actions[:only] = Array(only).map {|a| a.to_sym }
346
+ elsif except
347
+ @allowed_actions[:except] = Array(except).map {|a| a.to_sym }
348
+ end
349
+ end
350
+
351
+ def action_allowed?(action)
352
+ only, except = @allowed_actions.values_at(:only, :except)
353
+ (!only || only.include?(action)) && (!except || !except.include?(action))
354
+ end
355
+
356
+ def set_prefixes
357
+ @path_prefix = options.delete(:path_prefix)
358
+ @name_prefix = options.delete(:name_prefix)
359
+ end
360
+
361
+ def arrange_actions_by_methods(actions)
362
+ (actions || {}).inject({}) do |flipped_hash, (key, value)|
363
+ (flipped_hash[value] ||= []) << key
364
+ flipped_hash
365
+ end
366
+ end
367
+
368
+ def add_default_action(collection, method, action)
369
+ (collection[method] ||= []).unshift(action)
370
+ end
371
+ end
372
+
373
+ class SingletonResource < Resource #:nodoc:
374
+ def initialize(entity, options)
375
+ @singular = @plural = entity
376
+ options[:controller] ||= @singular.to_s.pluralize
377
+ super
378
+ end
379
+
380
+ alias_method :shallow_path_prefix, :path_prefix
381
+ alias_method :shallow_name_prefix, :name_prefix
382
+ alias_method :member_path, :path
383
+ alias_method :nesting_path_prefix, :path
384
+ end
385
+
386
+ # Creates named routes for implementing verb-oriented controllers
387
+ # for a collection \resource.
388
+ #
389
+ # For example:
390
+ #
391
+ # map.resources :messages
392
+ #
393
+ # will map the following actions in the corresponding controller:
394
+ #
395
+ # class MessagesController < ActionController::Base
396
+ # # GET messages_url
397
+ # def index
398
+ # # return all messages
399
+ # end
400
+ #
401
+ # # GET new_message_url
402
+ # def new
403
+ # # return an HTML form for describing a new message
404
+ # end
405
+ #
406
+ # # POST messages_url
407
+ # def create
408
+ # # create a new message
409
+ # end
410
+ #
411
+ # # GET message_url(:id => 1)
412
+ # def show
413
+ # # find and return a specific message
414
+ # end
415
+ #
416
+ # # GET edit_message_url(:id => 1)
417
+ # def edit
418
+ # # return an HTML form for editing a specific message
419
+ # end
420
+ #
421
+ # # PUT message_url(:id => 1)
422
+ # def update
423
+ # # find and update a specific message
424
+ # end
425
+ #
426
+ # # DELETE message_url(:id => 1)
427
+ # def destroy
428
+ # # delete a specific message
429
+ # end
430
+ # end
431
+ #
432
+ # Along with the routes themselves, +resources+ generates named routes for use in
433
+ # controllers and views. <tt>map.resources :messages</tt> produces the following named routes and helpers:
434
+ #
435
+ # Named Route Helpers
436
+ # ============ =====================================================
437
+ # messages messages_url, hash_for_messages_url,
438
+ # messages_path, hash_for_messages_path
439
+ #
440
+ # message message_url(id), hash_for_message_url(id),
441
+ # message_path(id), hash_for_message_path(id)
442
+ #
443
+ # new_message new_message_url, hash_for_new_message_url,
444
+ # new_message_path, hash_for_new_message_path
445
+ #
446
+ # edit_message edit_message_url(id), hash_for_edit_message_url(id),
447
+ # edit_message_path(id), hash_for_edit_message_path(id)
448
+ #
449
+ # You can use these helpers instead of +url_for+ or methods that take +url_for+ parameters. For example:
450
+ #
451
+ # redirect_to :controller => 'messages', :action => 'index'
452
+ # # and
453
+ # <%= link_to "edit this message", :controller => 'messages', :action => 'edit', :id => @message.id %>
454
+ #
455
+ # now become:
456
+ #
457
+ # redirect_to messages_url
458
+ # # and
459
+ # <%= link_to "edit this message", edit_message_url(@message) # calls @message.id automatically
460
+ #
461
+ # Since web browsers don't support the PUT and DELETE verbs, you will need to add a parameter '_method' to your
462
+ # form tags. The form helpers make this a little easier. For an update form with a <tt>@message</tt> object:
463
+ #
464
+ # <%= form_tag message_path(@message), :method => :put %>
465
+ #
466
+ # or
467
+ #
468
+ # <% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>
469
+ #
470
+ # or
471
+ #
472
+ # <% form_for @message do |f| %>
473
+ #
474
+ # which takes into account whether <tt>@message</tt> is a new record or not and generates the
475
+ # path and method accordingly.
476
+ #
477
+ # The +resources+ method accepts the following options to customize the resulting routes:
478
+ # * <tt>:collection</tt> - Add named routes for other actions that operate on the collection.
479
+ # Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>,
480
+ # an array of any of the previous, or <tt>:any</tt> if the method does not matter.
481
+ # These routes map to a URL like /messages/rss, with a route of +rss_messages_url+.
482
+ # * <tt>:member</tt> - Same as <tt>:collection</tt>, but for actions that operate on a specific member.
483
+ # * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new \resource action.
484
+ # * <tt>:controller</tt> - Specify the controller name for the routes.
485
+ # * <tt>:singular</tt> - Specify the singular name used in the member routes.
486
+ # * <tt>:requirements</tt> - Set custom routing parameter requirements; this is a hash of either
487
+ # regular expressions (which must match for the route to match) or extra parameters. For example:
488
+ #
489
+ # map.resource :profile, :path_prefix => ':name', :requirements => { :name => /[a-zA-Z]+/, :extra => 'value' }
490
+ #
491
+ # will only match if the first part is alphabetic, and will pass the parameter :extra to the controller.
492
+ # * <tt>:conditions</tt> - Specify custom routing recognition conditions. \Resources sets the <tt>:method</tt> value for the method-specific routes.
493
+ # * <tt>:as</tt> - Specify a different \resource name to use in the URL path. For example:
494
+ # # products_path == '/productos'
495
+ # map.resources :products, :as => 'productos' do |product|
496
+ # # product_reviews_path(product) == '/productos/1234/comentarios'
497
+ # product.resources :product_reviews, :as => 'comentarios'
498
+ # end
499
+ #
500
+ # * <tt>:has_one</tt> - Specify nested \resources, this is a shorthand for mapping singleton \resources beneath the current.
501
+ # * <tt>:has_many</tt> - Same has <tt>:has_one</tt>, but for plural \resources.
502
+ #
503
+ # You may directly specify the routing association with +has_one+ and +has_many+ like:
504
+ #
505
+ # map.resources :notes, :has_one => :author, :has_many => [:comments, :attachments]
506
+ #
507
+ # This is the same as:
508
+ #
509
+ # map.resources :notes do |notes|
510
+ # notes.resource :author
511
+ # notes.resources :comments
512
+ # notes.resources :attachments
513
+ # end
514
+ #
515
+ # * <tt>:path_names</tt> - Specify different path names for the actions. For example:
516
+ # # new_products_path == '/productos/nuevo'
517
+ # # bids_product_path(1) == '/productos/1/licitacoes'
518
+ # map.resources :products, :as => 'productos', :member => { :bids => :get }, :path_names => { :new => 'nuevo', :bids => 'licitacoes' }
519
+ #
520
+ # You can also set default action names from an environment, like this:
521
+ # config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' }
522
+ #
523
+ # * <tt>:path_prefix</tt> - Set a prefix to the routes with required route variables.
524
+ #
525
+ # Weblog comments usually belong to a post, so you might use +resources+ like:
526
+ #
527
+ # map.resources :articles
528
+ # map.resources :comments, :path_prefix => '/articles/:article_id'
529
+ #
530
+ # You can nest +resources+ calls to set this automatically:
531
+ #
532
+ # map.resources :articles do |article|
533
+ # article.resources :comments
534
+ # end
535
+ #
536
+ # The comment \resources work the same, but must now include a value for <tt>:article_id</tt>.
537
+ #
538
+ # article_comments_url(@article)
539
+ # article_comment_url(@article, @comment)
540
+ #
541
+ # article_comments_url(:article_id => @article)
542
+ # article_comment_url(:article_id => @article, :id => @comment)
543
+ #
544
+ # If you don't want to load all objects from the database you might want to use the <tt>article_id</tt> directly:
545
+ #
546
+ # articles_comments_url(@comment.article_id, @comment)
547
+ #
548
+ # * <tt>:name_prefix</tt> - Define a prefix for all generated routes, usually ending in an underscore.
549
+ # Use this if you have named routes that may clash.
550
+ #
551
+ # map.resources :tags, :path_prefix => '/books/:book_id', :name_prefix => 'book_'
552
+ # map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_'
553
+ #
554
+ # You may also use <tt>:name_prefix</tt> to override the generic named routes in a nested \resource:
555
+ #
556
+ # map.resources :articles do |article|
557
+ # article.resources :comments, :name_prefix => nil
558
+ # end
559
+ #
560
+ # This will yield named \resources like so:
561
+ #
562
+ # comments_url(@article)
563
+ # comment_url(@article, @comment)
564
+ #
565
+ # * <tt>:shallow</tt> - If true, paths for nested resources which reference a specific member
566
+ # (ie. those with an :id parameter) will not use the parent path prefix or name prefix.
567
+ #
568
+ # The <tt>:shallow</tt> option is inherited by any nested resource(s).
569
+ #
570
+ # For example, 'users', 'posts' and 'comments' all use shallow paths with the following nested resources:
571
+ #
572
+ # map.resources :users, :shallow => true do |user|
573
+ # user.resources :posts do |post|
574
+ # post.resources :comments
575
+ # end
576
+ # end
577
+ # # --> GET /users/1/posts (maps to the PostsController#index action as usual)
578
+ # # also adds the usual named route called "user_posts"
579
+ # # --> GET /posts/2 (maps to the PostsController#show action as if it were not nested)
580
+ # # also adds the named route called "post"
581
+ # # --> GET /posts/2/comments (maps to the CommentsController#index action)
582
+ # # also adds the named route called "post_comments"
583
+ # # --> GET /comments/2 (maps to the CommentsController#show action as if it were not nested)
584
+ # # also adds the named route called "comment"
585
+ #
586
+ # You may also use <tt>:shallow</tt> in combination with the +has_one+ and +has_many+ shorthand notations like:
587
+ #
588
+ # map.resources :users, :has_many => { :posts => :comments }, :shallow => true
589
+ #
590
+ # * <tt>:only</tt> and <tt>:except</tt> - Specify which of the seven default actions should be routed to.
591
+ #
592
+ # <tt>:only</tt> and <tt>:except</tt> may be set to <tt>:all</tt>, <tt>:none</tt>, an action name or a
593
+ # list of action names. By default, routes are generated for all seven actions.
594
+ #
595
+ # For example:
596
+ #
597
+ # map.resources :posts, :only => [:index, :show] do |post|
598
+ # post.resources :comments, :except => [:update, :destroy]
599
+ # end
600
+ # # --> GET /posts (maps to the PostsController#index action)
601
+ # # --> POST /posts (fails)
602
+ # # --> GET /posts/1 (maps to the PostsController#show action)
603
+ # # --> DELETE /posts/1 (fails)
604
+ # # --> POST /posts/1/comments (maps to the CommentsController#create action)
605
+ # # --> PUT /posts/1/comments/1 (fails)
606
+ #
607
+ # If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied.
608
+ #
609
+ # Examples:
610
+ #
611
+ # map.resources :messages, :path_prefix => "/thread/:thread_id"
612
+ # # --> GET /thread/7/messages/1
613
+ #
614
+ # map.resources :messages, :collection => { :rss => :get }
615
+ # # --> GET /messages/rss (maps to the #rss action)
616
+ # # also adds a named route called "rss_messages"
617
+ #
618
+ # map.resources :messages, :member => { :mark => :post }
619
+ # # --> POST /messages/1/mark (maps to the #mark action)
620
+ # # also adds a named route called "mark_message"
621
+ #
622
+ # map.resources :messages, :new => { :preview => :post }
623
+ # # --> POST /messages/new/preview (maps to the #preview action)
624
+ # # also adds a named route called "preview_new_message"
625
+ #
626
+ # map.resources :messages, :new => { :new => :any, :preview => :post }
627
+ # # --> POST /messages/new/preview (maps to the #preview action)
628
+ # # also adds a named route called "preview_new_message"
629
+ # # --> /messages/new can be invoked via any request method
630
+ #
631
+ # map.resources :messages, :controller => "categories",
632
+ # :path_prefix => "/category/:category_id",
633
+ # :name_prefix => "category_"
634
+ # # --> GET /categories/7/messages/1
635
+ # # has named route "category_message"
636
+ #
637
+ # The +resources+ method sets HTTP method restrictions on the routes it generates. For example, making an
638
+ # HTTP POST on <tt>new_message_url</tt> will raise a RoutingError exception. The default route in
639
+ # <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for \resource routes.
640
+ def resources(*entities, &block)
641
+ options = entities.extract_options!
642
+ entities.each { |entity| map_resource(entity, options.dup, &block) }
643
+ end
644
+
645
+ # Creates named routes for implementing verb-oriented controllers for a singleton \resource.
646
+ # A singleton \resource is global to its current context. For unnested singleton \resources,
647
+ # the \resource is global to the current user visiting the application, such as a user's
648
+ # <tt>/account</tt> profile. For nested singleton \resources, the \resource is global to its parent
649
+ # \resource, such as a <tt>projects</tt> \resource that <tt>has_one :project_manager</tt>.
650
+ # The <tt>project_manager</tt> should be mapped as a singleton \resource under <tt>projects</tt>:
651
+ #
652
+ # map.resources :projects do |project|
653
+ # project.resource :project_manager
654
+ # end
655
+ #
656
+ # See +resources+ for general conventions. These are the main differences:
657
+ # * A singular name is given to <tt>map.resource</tt>. The default controller name is still taken from the plural name.
658
+ # * To specify a custom plural name, use the <tt>:plural</tt> option. There is no <tt>:singular</tt> option.
659
+ # * No default index route is created for the singleton \resource controller.
660
+ # * When nesting singleton \resources, only the singular name is used as the path prefix (example: 'account/messages/1')
661
+ #
662
+ # For example:
663
+ #
664
+ # map.resource :account
665
+ #
666
+ # maps these actions in the Accounts controller:
667
+ #
668
+ # class AccountsController < ActionController::Base
669
+ # # GET new_account_url
670
+ # def new
671
+ # # return an HTML form for describing the new account
672
+ # end
673
+ #
674
+ # # POST account_url
675
+ # def create
676
+ # # create an account
677
+ # end
678
+ #
679
+ # # GET account_url
680
+ # def show
681
+ # # find and return the account
682
+ # end
683
+ #
684
+ # # GET edit_account_url
685
+ # def edit
686
+ # # return an HTML form for editing the account
687
+ # end
688
+ #
689
+ # # PUT account_url
690
+ # def update
691
+ # # find and update the account
692
+ # end
693
+ #
694
+ # # DELETE account_url
695
+ # def destroy
696
+ # # delete the account
697
+ # end
698
+ # end
699
+ #
700
+ # Along with the routes themselves, +resource+ generates named routes for
701
+ # use in controllers and views. <tt>map.resource :account</tt> produces
702
+ # these named routes and helpers:
703
+ #
704
+ # Named Route Helpers
705
+ # ============ =============================================
706
+ # account account_url, hash_for_account_url,
707
+ # account_path, hash_for_account_path
708
+ #
709
+ # new_account new_account_url, hash_for_new_account_url,
710
+ # new_account_path, hash_for_new_account_path
711
+ #
712
+ # edit_account edit_account_url, hash_for_edit_account_url,
713
+ # edit_account_path, hash_for_edit_account_path
714
+ def resource(*entities, &block)
715
+ options = entities.extract_options!
716
+ entities.each { |entity| map_singleton_resource(entity, options.dup, &block) }
717
+ end
718
+
719
+ private
720
+ def map_resource(entities, options = {}, &block)
721
+ resource = Resource.new(entities, options)
722
+
723
+ with_options :controller => resource.controller do |map|
724
+ map_associations(resource, options)
725
+
726
+ if block_given?
727
+ with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
728
+ end
729
+
730
+ map_collection_actions(map, resource)
731
+ map_default_collection_actions(map, resource)
732
+ map_new_actions(map, resource)
733
+ map_member_actions(map, resource)
734
+ end
735
+ end
736
+
737
+ def map_singleton_resource(entities, options = {}, &block)
738
+ resource = SingletonResource.new(entities, options)
739
+
740
+ with_options :controller => resource.controller do |map|
741
+ map_associations(resource, options)
742
+
743
+ if block_given?
744
+ with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
745
+ end
746
+
747
+ map_collection_actions(map, resource)
748
+ map_new_actions(map, resource)
749
+ map_member_actions(map, resource)
750
+ map_default_singleton_actions(map, resource)
751
+ end
752
+ end
753
+
754
+ def map_associations(resource, options)
755
+ map_has_many_associations(resource, options.delete(:has_many), options) if options[:has_many]
756
+
757
+ path_prefix = "#{options.delete(:path_prefix)}#{resource.nesting_path_prefix}"
758
+ name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}"
759
+
760
+ Array(options[:has_one]).each do |association|
761
+ resource(association, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => path_prefix, :name_prefix => name_prefix))
762
+ end
763
+ end
764
+
765
+ def map_has_many_associations(resource, associations, options)
766
+ case associations
767
+ when Hash
768
+ associations.each do |association,has_many|
769
+ map_has_many_associations(resource, association, options.merge(:has_many => has_many))
770
+ end
771
+ when Array
772
+ associations.each do |association|
773
+ map_has_many_associations(resource, association, options)
774
+ end
775
+ when Symbol, String
776
+ resources(associations, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :has_many => options[:has_many]))
777
+ else
778
+ end
779
+ end
780
+
781
+ def map_collection_actions(map, resource)
782
+ resource.collection_methods.each do |method, actions|
783
+ actions.each do |action|
784
+ [method].flatten.each do |m|
785
+ action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
786
+ action_path ||= action
787
+
788
+ map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
789
+ end
790
+ end
791
+ end
792
+ end
793
+
794
+ def map_default_collection_actions(map, resource)
795
+ index_route_name = "#{resource.name_prefix}#{resource.plural}"
796
+
797
+ if resource.uncountable?
798
+ index_route_name << "_index"
799
+ end
800
+
801
+ map_resource_routes(map, resource, :index, resource.path, index_route_name)
802
+ map_resource_routes(map, resource, :create, resource.path, index_route_name)
803
+ end
804
+
805
+ def map_default_singleton_actions(map, resource)
806
+ map_resource_routes(map, resource, :create, resource.path, "#{resource.shallow_name_prefix}#{resource.singular}")
807
+ end
808
+
809
+ def map_new_actions(map, resource)
810
+ resource.new_methods.each do |method, actions|
811
+ actions.each do |action|
812
+ route_path = resource.new_path
813
+ route_name = "new_#{resource.name_prefix}#{resource.singular}"
814
+
815
+ unless action == :new
816
+ route_path = "#{route_path}#{resource.action_separator}#{action}"
817
+ route_name = "#{action}_#{route_name}"
818
+ end
819
+
820
+ map_resource_routes(map, resource, action, route_path, route_name, method)
821
+ end
822
+ end
823
+ end
824
+
825
+ def map_member_actions(map, resource)
826
+ resource.member_methods.each do |method, actions|
827
+ actions.each do |action|
828
+ [method].flatten.each do |m|
829
+ action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
830
+ action_path ||= ActionController::Base.resources_path_names[action] || action
831
+
832
+ map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m, { :force_id => true })
833
+ end
834
+ end
835
+ end
836
+
837
+ route_path = "#{resource.shallow_name_prefix}#{resource.singular}"
838
+ map_resource_routes(map, resource, :show, resource.member_path, route_path)
839
+ map_resource_routes(map, resource, :update, resource.member_path, route_path)
840
+ map_resource_routes(map, resource, :destroy, resource.member_path, route_path)
841
+ end
842
+
843
+ def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil, resource_options = {} )
844
+ if resource.has_action?(action)
845
+ action_options = action_options_for(action, resource, method, resource_options)
846
+ formatted_route_path = "#{route_path}.:format"
847
+
848
+ if route_name && @set.named_routes[route_name.to_sym].nil?
849
+ map.named_route(route_name, formatted_route_path, action_options)
850
+ else
851
+ map.connect(formatted_route_path, action_options)
852
+ end
853
+ end
854
+ end
855
+
856
+ def add_conditions_for(conditions, method)
857
+ returning({:conditions => conditions.dup}) do |options|
858
+ options[:conditions][:method] = method unless method == :any
859
+ end
860
+ end
861
+
862
+ def action_options_for(action, resource, method = nil, resource_options = {})
863
+ default_options = { :action => action.to_s }
864
+ require_id = !resource.kind_of?(SingletonResource)
865
+ force_id = resource_options[:force_id] && !resource.kind_of?(SingletonResource)
866
+
867
+ case default_options[:action]
868
+ when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements)
869
+ when "create"; default_options.merge(add_conditions_for(resource.conditions, method || :post)).merge(resource.requirements)
870
+ when "show", "edit"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements(require_id))
871
+ when "update"; default_options.merge(add_conditions_for(resource.conditions, method || :put)).merge(resource.requirements(require_id))
872
+ when "destroy"; default_options.merge(add_conditions_for(resource.conditions, method || :delete)).merge(resource.requirements(require_id))
873
+ else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(force_id))
874
+ end
875
+ end
876
+ end
877
+ end
878
+ end