rails_legacy_mapper 1.0.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.
data/.gemtest ADDED
File without changes
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ *Rails Legacy Mapper 1.0.0 (March 29, 2011)
2
+
3
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Andrew White
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,25 @@
1
+ == Rails Legacy Mapper
2
+ This gem provides an extraction of the DeprecatedMapper from Rails 3.0.
3
+ If you have a legacy application with an old style routes.rb file this
4
+ allows you to get your application running quickly in Rails 3.1.
5
+
6
+ === Example
7
+ The trigger for using the legacy mapper is the arity (number of args)
8
+ of the block passed to draw, e.g:
9
+
10
+ LegacyApp::Application.routes.draw do |map|
11
+
12
+ map.root :controller => 'pages', :action => 'index'
13
+
14
+ map.namespace :admin do |admin|
15
+ admin.resources :pages
16
+ end
17
+
18
+ map.page '/pages/:id', :controller => 'pages', :action => 'show'
19
+
20
+ map.connect '/:controller/:action/:id/'
21
+
22
+ end
23
+
24
+ === License
25
+ Copyright (c) 2011 Andrew White, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+ require 'rake/testtask'
4
+ require 'rubygems'
5
+ require 'bundler/setup'
6
+
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ desc 'Default: run rails_legacy_mapper tests.'
10
+ task :default => :test
11
+
12
+ desc 'Test the sortifiable gem.'
13
+ Rake::TestTask.new(:test) do |t|
14
+ t.libs << 'lib'
15
+ t.libs << 'test'
16
+ t.pattern = 'test/**/*_test.rb'
17
+ t.verbose = true
18
+ end
19
+
20
+ desc 'Generate documentation for the rails_legacy_mapper gem.'
21
+ Rake::RDocTask.new(:rdoc) do |rdoc|
22
+ rdoc.rdoc_dir = 'rdoc'
23
+ rdoc.title = 'Rails Legacy Mapper'
24
+ rdoc.options << '--line-numbers' << '--inline-source'
25
+ rdoc.rdoc_files.include('README')
26
+ rdoc.rdoc_files.include('lib/**/*.rb')
27
+ end
@@ -0,0 +1,23 @@
1
+ module RailsLegacyMapper
2
+ # This gem provides an extraction of the DeprecatedMapper from Rails 3.0
3
+
4
+ # Returns the current resource action separator - defaults to '/'
5
+ def self.resource_action_separator
6
+ @resource_action_separator ||= '/'
7
+ end
8
+
9
+ # Set the resource action separator, e.g:
10
+ #
11
+ # # config/initializers/rails_legacy_mapper.rb
12
+ # RailsLegacyMapper.resource_action_separator = ';'
13
+ def self.resource_action_separator=(value)
14
+ @resource_action_separator = value
15
+ end
16
+ end
17
+
18
+ require 'rails_legacy_mapper/mapper'
19
+ require 'rails_legacy_mapper/route_set_extensions'
20
+ require 'rails_legacy_mapper/version'
21
+ require 'action_dispatch'
22
+
23
+ ActionDispatch::Routing::RouteSet.send(:include, RailsLegacyMapper::RouteSetExtensions)
@@ -0,0 +1,488 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'active_support/core_ext/object/with_options'
4
+ require 'active_support/core_ext/object/try'
5
+ require 'active_support/inflector'
6
+
7
+ module RailsLegacyMapper
8
+ class Mapper #:nodoc:
9
+ def initialize(set) #:nodoc:
10
+ @set = set
11
+ end
12
+
13
+ def connect(path, options = {})
14
+ options = options.dup
15
+
16
+ if conditions = options.delete(:conditions)
17
+ conditions = conditions.dup
18
+ method = Array.wrap(conditions.delete(:method))
19
+ method.map! { |m|
20
+ if m == :head
21
+ raise ArgumentError, "HTTP method HEAD is invalid in route conditions. Rails processes HEAD requests the same as GETs, returning just the response headers"
22
+ end
23
+
24
+ unless ActionDispatch::Routing::HTTP_METHODS.include?(m)
25
+ raise ArgumentError, "Invalid HTTP method specified in route conditions"
26
+ end
27
+
28
+ m.to_s.dasherize.upcase
29
+ }
30
+ end
31
+
32
+ path_prefix = options.delete(:path_prefix)
33
+ name_prefix = options.delete(:name_prefix)
34
+ namespace = options.delete(:namespace)
35
+
36
+ name = options.delete(:_name)
37
+ name = "#{name_prefix}#{name}" if name_prefix
38
+
39
+ requirements = options.delete(:requirements) || {}
40
+ defaults = options.delete(:defaults) || {}
41
+ options.each do |k, v|
42
+ if v.is_a?(Regexp)
43
+ if value = options.delete(k)
44
+ requirements[k.to_sym] = value
45
+ end
46
+ else
47
+ value = options.delete(k)
48
+ defaults[k.to_sym] = value.is_a?(Symbol) ? value : value.to_param
49
+ end
50
+ end
51
+
52
+ requirements.each do |_, requirement|
53
+ if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
54
+ raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
55
+ end
56
+ if requirement.multiline?
57
+ raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
58
+ end
59
+ end
60
+
61
+ requirements[:controller] ||= @set.controller_constraints
62
+
63
+ if defaults[:controller]
64
+ defaults[:action] ||= 'index'
65
+ defaults[:controller] = defaults[:controller].to_s
66
+ defaults[:controller] = "#{namespace}#{defaults[:controller]}" if namespace
67
+ end
68
+
69
+ if defaults[:action]
70
+ defaults[:action] = defaults[:action].to_s
71
+ end
72
+
73
+ if path.is_a?(String)
74
+ path = "#{path_prefix}/#{path}" if path_prefix
75
+ path = path.gsub('.:format', '(.:format)')
76
+ path = optionalize_trailing_dynamic_segments(path, requirements, defaults)
77
+ glob = $1.to_sym if path =~ /\/\*(\w+)$/
78
+ path = ::Rack::Mount::Utils.normalize_path(path)
79
+
80
+ if glob && !defaults[glob].blank?
81
+ raise ActionController::RoutingError, "paths cannot have non-empty default values"
82
+ end
83
+ end
84
+
85
+ app = ActionDispatch::Routing::RouteSet::Dispatcher.new(:defaults => defaults, :glob => glob)
86
+
87
+ conditions = {}
88
+ conditions[:request_method] = method if method && !method.empty?
89
+ conditions[:path_info] = path if path
90
+
91
+ @set.add_route(app, conditions, requirements, defaults, name)
92
+ end
93
+
94
+ def optionalize_trailing_dynamic_segments(path, requirements, defaults) #:nodoc:
95
+ path = (path =~ /^\//) ? path.dup : "/#{path}"
96
+ optional, segments = true, []
97
+
98
+ required_segments = requirements.keys
99
+ required_segments -= defaults.keys.compact
100
+
101
+ old_segments = path.split('/')
102
+ old_segments.shift
103
+ length = old_segments.length
104
+
105
+ old_segments.reverse.each_with_index do |segment, index|
106
+ required_segments.each do |required|
107
+ if segment =~ /#{required}/
108
+ optional = false
109
+ break
110
+ end
111
+ end
112
+
113
+ if optional
114
+ if segment == ":id" && segments.include?(":action")
115
+ optional = false
116
+ elsif segment == ":controller" || segment == ":action" || segment == ":id"
117
+ # Ignore
118
+ elsif !(segment =~ /^:\w+$/) &&
119
+ !(segment =~ /^:\w+\(\.:format\)$/)
120
+ optional = false
121
+ elsif segment =~ /^:(\w+)$/
122
+ if defaults.has_key?($1.to_sym)
123
+ defaults.delete($1.to_sym) if defaults[$1.to_sym].nil?
124
+ else
125
+ optional = false
126
+ end
127
+ end
128
+ end
129
+
130
+ if optional && index < length - 1
131
+ segments.unshift('(/', segment)
132
+ segments.push(')')
133
+ elsif optional
134
+ segments.unshift('/(', segment)
135
+ segments.push(')')
136
+ else
137
+ segments.unshift('/', segment)
138
+ end
139
+ end
140
+
141
+ segments.join
142
+ end
143
+ private :optionalize_trailing_dynamic_segments
144
+
145
+ # Creates a named route called "root" for matching the root level request.
146
+ def root(options = {})
147
+ if options.is_a?(Symbol)
148
+ if source_route = @set.named_routes.routes[options]
149
+ options = source_route.defaults.merge({ :conditions => source_route.conditions })
150
+ end
151
+ end
152
+ named_route("root", '', options)
153
+ end
154
+
155
+ def named_route(name, path, options = {}) #:nodoc:
156
+ options[:_name] = name
157
+ connect(path, options)
158
+ end
159
+
160
+ def namespace(name, options = {}, &block)
161
+ if options[:namespace]
162
+ with_options({:path_prefix => "#{options.delete(:path_prefix)}/#{name}", :name_prefix => "#{options.delete(:name_prefix)}#{name}_", :namespace => "#{options.delete(:namespace)}#{name}/" }.merge(options), &block)
163
+ else
164
+ with_options({:path_prefix => name, :name_prefix => "#{name}_", :namespace => "#{name}/" }.merge(options), &block)
165
+ end
166
+ end
167
+
168
+ def method_missing(route_name, *args, &proc) #:nodoc:
169
+ super unless args.length >= 1 && proc.nil?
170
+ named_route(route_name, *args)
171
+ end
172
+
173
+ INHERITABLE_OPTIONS = :namespace, :shallow
174
+
175
+ class Resource #:nodoc:
176
+ DEFAULT_ACTIONS = :index, :create, :new, :edit, :show, :update, :destroy
177
+
178
+ attr_reader :collection_methods, :member_methods, :new_methods
179
+ attr_reader :path_prefix, :name_prefix, :path_segment
180
+ attr_reader :plural, :singular
181
+ attr_reader :options, :defaults
182
+
183
+ def initialize(entities, options, defaults)
184
+ @plural ||= entities
185
+ @singular ||= options[:singular] || plural.to_s.singularize
186
+ @path_segment = options.delete(:as) || @plural
187
+
188
+ @options = options
189
+ @defaults = defaults
190
+
191
+ arrange_actions
192
+ add_default_actions
193
+ set_allowed_actions
194
+ set_prefixes
195
+ end
196
+
197
+ def controller
198
+ @controller ||= "#{options[:namespace]}#{(options[:controller] || plural).to_s}"
199
+ end
200
+
201
+ def requirements(with_id = false)
202
+ @requirements ||= @options[:requirements] || {}
203
+ @id_requirement ||= { :id => @requirements.delete(:id) || /[^#{ActionDispatch::Routing::SEPARATORS.join}]+/ }
204
+
205
+ with_id ? @requirements.merge(@id_requirement) : @requirements
206
+ end
207
+
208
+ def conditions
209
+ @conditions ||= @options[:conditions] || {}
210
+ end
211
+
212
+ def path
213
+ @path ||= "#{path_prefix}/#{path_segment}"
214
+ end
215
+
216
+ def new_path
217
+ new_action = self.options[:path_names][:new] if self.options[:path_names]
218
+ new_action ||= self.defaults[:path_names][:new]
219
+ @new_path ||= "#{path}/#{new_action}"
220
+ end
221
+
222
+ def shallow_path_prefix
223
+ @shallow_path_prefix ||= @options[:shallow] ? @options[:namespace].try(:sub, /\/$/, '') : path_prefix
224
+ end
225
+
226
+ def member_path
227
+ @member_path ||= "#{shallow_path_prefix}/#{path_segment}/:id"
228
+ end
229
+
230
+ def nesting_path_prefix
231
+ @nesting_path_prefix ||= "#{shallow_path_prefix}/#{path_segment}/:#{singular}_id"
232
+ end
233
+
234
+ def shallow_name_prefix
235
+ @shallow_name_prefix ||= @options[:shallow] ? @options[:namespace].try(:gsub, /\//, '_') : name_prefix
236
+ end
237
+
238
+ def nesting_name_prefix
239
+ "#{shallow_name_prefix}#{singular}_"
240
+ end
241
+
242
+ def action_separator
243
+ @action_separator ||= RailsLegacyMapper.resource_action_separator
244
+ end
245
+
246
+ def uncountable?
247
+ @singular.to_s == @plural.to_s
248
+ end
249
+
250
+ def has_action?(action)
251
+ !DEFAULT_ACTIONS.include?(action) || action_allowed?(action)
252
+ end
253
+
254
+ protected
255
+ def arrange_actions
256
+ @collection_methods = arrange_actions_by_methods(options.delete(:collection))
257
+ @member_methods = arrange_actions_by_methods(options.delete(:member))
258
+ @new_methods = arrange_actions_by_methods(options.delete(:new))
259
+ end
260
+
261
+ def add_default_actions
262
+ add_default_action(member_methods, :get, :edit)
263
+ add_default_action(new_methods, :get, :new)
264
+ end
265
+
266
+ def set_allowed_actions
267
+ only, except = @options.values_at(:only, :except)
268
+ @allowed_actions ||= {}
269
+
270
+ if only == :all || except == :none
271
+ only = nil
272
+ except = []
273
+ elsif only == :none || except == :all
274
+ only = []
275
+ except = nil
276
+ end
277
+
278
+ if only
279
+ @allowed_actions[:only] = Array(only).map {|a| a.to_sym }
280
+ elsif except
281
+ @allowed_actions[:except] = Array(except).map {|a| a.to_sym }
282
+ end
283
+ end
284
+
285
+ def action_allowed?(action)
286
+ only, except = @allowed_actions.values_at(:only, :except)
287
+ (!only || only.include?(action)) && (!except || !except.include?(action))
288
+ end
289
+
290
+ def set_prefixes
291
+ @path_prefix = options.delete(:path_prefix)
292
+ @name_prefix = options.delete(:name_prefix)
293
+ end
294
+
295
+ def arrange_actions_by_methods(actions)
296
+ (actions || {}).inject({}) do |flipped_hash, (key, value)|
297
+ (flipped_hash[value] ||= []) << key
298
+ flipped_hash
299
+ end
300
+ end
301
+
302
+ def add_default_action(collection, method, action)
303
+ (collection[method] ||= []).unshift(action)
304
+ end
305
+ end
306
+
307
+ class SingletonResource < Resource #:nodoc:
308
+ def initialize(entity, options, defaults)
309
+ @singular = @plural = entity
310
+ options[:controller] ||= @singular.to_s.pluralize
311
+ super
312
+ end
313
+
314
+ alias_method :shallow_path_prefix, :path_prefix
315
+ alias_method :shallow_name_prefix, :name_prefix
316
+ alias_method :member_path, :path
317
+ alias_method :nesting_path_prefix, :path
318
+ end
319
+
320
+ def resources(*entities, &block)
321
+ options = entities.extract_options!
322
+ entities.each { |entity| map_resource(entity, options.dup, &block) }
323
+ end
324
+
325
+ def resource(*entities, &block)
326
+ options = entities.extract_options!
327
+ entities.each { |entity| map_singleton_resource(entity, options.dup, &block) }
328
+ end
329
+
330
+ private
331
+ def map_resource(entities, options = {}, &block)
332
+ resource = Resource.new(entities, options, :path_names => @set.resources_path_names)
333
+
334
+ with_options :controller => resource.controller do |map|
335
+ map_associations(resource, options)
336
+
337
+ if block_given?
338
+ with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
339
+ end
340
+
341
+ map_collection_actions(map, resource)
342
+ map_default_collection_actions(map, resource)
343
+ map_new_actions(map, resource)
344
+ map_member_actions(map, resource)
345
+ end
346
+ end
347
+
348
+ def map_singleton_resource(entities, options = {}, &block)
349
+ resource = SingletonResource.new(entities, options, :path_names => @set.resources_path_names)
350
+
351
+ with_options :controller => resource.controller do |map|
352
+ map_associations(resource, options)
353
+
354
+ if block_given?
355
+ with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
356
+ end
357
+
358
+ map_collection_actions(map, resource)
359
+ map_new_actions(map, resource)
360
+ map_member_actions(map, resource)
361
+ map_default_singleton_actions(map, resource)
362
+ end
363
+ end
364
+
365
+ def map_associations(resource, options)
366
+ map_has_many_associations(resource, options.delete(:has_many), options) if options[:has_many]
367
+
368
+ path_prefix = "#{options.delete(:path_prefix)}#{resource.nesting_path_prefix}"
369
+ name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}"
370
+
371
+ Array(options[:has_one]).each do |association|
372
+ resource(association, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => path_prefix, :name_prefix => name_prefix))
373
+ end
374
+ end
375
+
376
+ def map_has_many_associations(resource, associations, options)
377
+ case associations
378
+ when Hash
379
+ associations.each do |association,has_many|
380
+ map_has_many_associations(resource, association, options.merge(:has_many => has_many))
381
+ end
382
+ when Array
383
+ associations.each do |association|
384
+ map_has_many_associations(resource, association, options)
385
+ end
386
+ when Symbol, String
387
+ resources(associations, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :has_many => options[:has_many]))
388
+ else
389
+ end
390
+ end
391
+
392
+ def map_collection_actions(map, resource)
393
+ resource.collection_methods.each do |method, actions|
394
+ actions.each do |action|
395
+ [method].flatten.each do |m|
396
+ action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
397
+ action_path ||= action
398
+
399
+ map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
400
+ end
401
+ end
402
+ end
403
+ end
404
+
405
+ def map_default_collection_actions(map, resource)
406
+ index_route_name = "#{resource.name_prefix}#{resource.plural}"
407
+
408
+ if resource.uncountable?
409
+ index_route_name << "_index"
410
+ end
411
+
412
+ map_resource_routes(map, resource, :index, resource.path, index_route_name)
413
+ map_resource_routes(map, resource, :create, resource.path, index_route_name)
414
+ end
415
+
416
+ def map_default_singleton_actions(map, resource)
417
+ map_resource_routes(map, resource, :create, resource.path, "#{resource.shallow_name_prefix}#{resource.singular}")
418
+ end
419
+
420
+ def map_new_actions(map, resource)
421
+ resource.new_methods.each do |method, actions|
422
+ actions.each do |action|
423
+ route_path = resource.new_path
424
+ route_name = "new_#{resource.name_prefix}#{resource.singular}"
425
+
426
+ unless action == :new
427
+ route_path = "#{route_path}#{resource.action_separator}#{action}"
428
+ route_name = "#{action}_#{route_name}"
429
+ end
430
+
431
+ map_resource_routes(map, resource, action, route_path, route_name, method)
432
+ end
433
+ end
434
+ end
435
+
436
+ def map_member_actions(map, resource)
437
+ resource.member_methods.each do |method, actions|
438
+ actions.each do |action|
439
+ [method].flatten.each do |m|
440
+ action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
441
+ action_path ||= @set.resources_path_names[action] || action
442
+
443
+ 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 })
444
+ end
445
+ end
446
+ end
447
+
448
+ route_path = "#{resource.shallow_name_prefix}#{resource.singular}"
449
+ map_resource_routes(map, resource, :show, resource.member_path, route_path)
450
+ map_resource_routes(map, resource, :update, resource.member_path, route_path)
451
+ map_resource_routes(map, resource, :destroy, resource.member_path, route_path)
452
+ end
453
+
454
+ def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil, resource_options = {} )
455
+ if resource.has_action?(action)
456
+ action_options = action_options_for(action, resource, method, resource_options)
457
+ formatted_route_path = "#{route_path}.:format"
458
+
459
+ if route_name && @set.named_routes[route_name.to_sym].nil?
460
+ map.named_route(route_name, formatted_route_path, action_options)
461
+ else
462
+ map.connect(formatted_route_path, action_options)
463
+ end
464
+ end
465
+ end
466
+
467
+ def add_conditions_for(conditions, method)
468
+ {:conditions => conditions.dup}.tap do |options|
469
+ options[:conditions][:method] = method unless method == :any
470
+ end
471
+ end
472
+
473
+ def action_options_for(action, resource, method = nil, resource_options = {})
474
+ default_options = { :action => action.to_s }
475
+ require_id = !resource.kind_of?(SingletonResource)
476
+ force_id = resource_options[:force_id] && !resource.kind_of?(SingletonResource)
477
+
478
+ case default_options[:action]
479
+ when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements)
480
+ when "create"; default_options.merge(add_conditions_for(resource.conditions, method || :post)).merge(resource.requirements)
481
+ when "show", "edit"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements(require_id))
482
+ when "update"; default_options.merge(add_conditions_for(resource.conditions, method || :put)).merge(resource.requirements(require_id))
483
+ when "destroy"; default_options.merge(add_conditions_for(resource.conditions, method || :delete)).merge(resource.requirements(require_id))
484
+ else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(force_id))
485
+ end
486
+ end
487
+ end
488
+ end