josh-rack-mount 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,164 @@
1
+ require 'action_controller'
2
+
3
+ module ActionController
4
+ module Routing
5
+ class RouteSet
6
+ NotFound = lambda { |env|
7
+ raise RoutingError, "No route matches #{env[::Rack::Mount::Const::PATH_INFO].inspect} with #{env.inspect}"
8
+ }
9
+
10
+ class Dispatcher
11
+ def initialize(options = {})
12
+ defaults = options[:defaults]
13
+ @glob_param = options.delete(:glob)
14
+ @app = controller(defaults) if bind_controller_const?
15
+ end
16
+
17
+ def call(env)
18
+ params = env[::Rack::Mount::Const::RACK_ROUTING_ARGS]
19
+ app = @app || controller(params)
20
+ merge_default_action!(params)
21
+ split_glob_param!(params) if @glob_param
22
+
23
+ # TODO: Rails response is not finalized by the controller
24
+ app.call(env).to_a
25
+ end
26
+
27
+ private
28
+ def bind_controller_const?
29
+ if defined? Rails
30
+ Rails.env.production?
31
+ else
32
+ true
33
+ end
34
+ end
35
+
36
+ def controller(params)
37
+ if params && params.has_key?(:controller)
38
+ controller = "#{params[:controller].camelize}Controller"
39
+ ActiveSupport::Inflector.constantize(controller)
40
+ end
41
+ end
42
+
43
+ def merge_default_action!(params)
44
+ params[:action] ||= 'index'
45
+ end
46
+
47
+ def split_glob_param!(params)
48
+ params[@glob_param] = params[@glob_param].split('/')
49
+ end
50
+ end
51
+
52
+ module RouteExtensions
53
+ def segment_keys
54
+ path.names.compact.map(&:to_sym)
55
+ end
56
+ end
57
+
58
+ class NamedRouteCollection
59
+ private
60
+ def generate_optimisation_block(*args)
61
+ end
62
+ end
63
+
64
+ def draw
65
+ yield Mapper.new(self)
66
+ @set.add_route(NotFound, :path => /.*/)
67
+ install_helpers
68
+ @set.freeze
69
+ end
70
+
71
+ def clear!
72
+ routes.clear
73
+ named_routes.clear
74
+ @combined_regexp = nil
75
+ @routes_by_controller = nil
76
+ @set = ::Rack::Mount::RouteSet.new
77
+ end
78
+
79
+ def add_route(path, options = {})
80
+ clear! unless @set
81
+
82
+ if path.is_a?(String)
83
+ path = path.gsub('.:format', '(.:format)')
84
+ path = optionalize_trailing_dynamic_segments(path)
85
+ end
86
+
87
+ if conditions = options.delete(:conditions)
88
+ method = conditions.delete(:method)
89
+ end
90
+
91
+ name = options.delete(:name)
92
+
93
+ requirements = options.delete(:requirements) || {}
94
+ defaults = {}
95
+ options.each do |k, v|
96
+ if v.is_a?(Regexp)
97
+ requirements[k.to_sym] = options.delete(k)
98
+ else
99
+ defaults[k.to_sym] = options.delete(k)
100
+ end
101
+ end
102
+
103
+ if path.is_a?(String)
104
+ glob = $1.to_sym if path =~ /\/\*(\w+)$/
105
+ path = ::Rack::Mount::Utils.convert_segment_string_to_regexp(path, requirements, %w( / . ? ))
106
+ end
107
+
108
+ app = Dispatcher.new(:defaults => defaults, :glob => glob)
109
+
110
+ conditions = { :method => method, :path => path }
111
+ route = @set.add_route(app, conditions, defaults, name)
112
+ route.extend(RouteExtensions)
113
+ route
114
+ end
115
+
116
+ def add_named_route(name, path, options = {})
117
+ options[:name] = name
118
+ named_routes[name.to_sym] = add_route(path, options)
119
+ end
120
+
121
+ def generate(options, recall = {}, method = :generate)
122
+ named_route = options.delete(:use_route)
123
+ expire_on = build_expiry(options, recall)
124
+ expire_on.each { |k, v| recall.delete(k) unless v }
125
+ options = recall.merge(options)
126
+ options.each { |k, v| options[k] = v.to_param }
127
+ @set.url_for(named_route, options)
128
+ end
129
+
130
+ def url_for(*args)
131
+ @set.url_for(*args)
132
+ end
133
+
134
+ def call(env)
135
+ @set.call(env)
136
+ end
137
+
138
+ private
139
+ def optionalize_trailing_dynamic_segments(path)
140
+ path = (path =~ /^\//) ? path.dup : "/#{path}"
141
+ optional, segments = true, []
142
+
143
+ old_segments = path.split('/')
144
+ old_segments.shift
145
+ length = old_segments.length
146
+
147
+ old_segments.reverse.each_with_index do |segment, index|
148
+ if optional && !(segment =~ /^:\w+$/) && !(segment =~ /^:\w+\(\.:format\)$/)
149
+ optional = false
150
+ end
151
+
152
+ if optional && index < length - 1
153
+ segments.unshift('(/', segment)
154
+ segments.push(')')
155
+ else
156
+ segments.unshift('/', segment)
157
+ end
158
+ end
159
+
160
+ segments.join
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,179 @@
1
+ require 'active_support/inflector'
2
+
3
+ module Rack
4
+ module Mount
5
+ class RouteSet
6
+ def new_draw(&block)
7
+ mapper = Mappers::RailsDraft.new(self)
8
+ mapper.instance_eval(&block)
9
+ add_route(Mappers::RailsDraft::NotFound, :path => /.*/)
10
+ freeze
11
+ end
12
+ end
13
+
14
+ module Mappers
15
+ class RailsDraft
16
+ class RoutingError < StandardError; end
17
+
18
+ NotFound = lambda { |env|
19
+ raise RoutingError, "No route matches #{env[Const::PATH_INFO].inspect} with #{env.inspect}"
20
+ }
21
+
22
+ DynamicController = lambda { |env|
23
+ app = "#{env[Const::RACK_ROUTING_ARGS][:controller].camelize}Controller"
24
+ app = ActiveSupport::Inflector.constantize(app)
25
+ app.call(env)
26
+ }
27
+
28
+ def initialize(set)
29
+ require 'action_controller'
30
+ @set = set
31
+ @scope_stack = []
32
+ end
33
+
34
+ def get(path, options = {})
35
+ match(path, options.merge(:via => :get))
36
+ end
37
+
38
+ def post(path, options = {})
39
+ match(path, options.merge(:via => :post))
40
+ end
41
+
42
+ def put(path, options = {})
43
+ match(path, options.merge(:via => :put))
44
+ end
45
+
46
+ def delete(path, options = {})
47
+ match(path, options.merge(:via => :delete))
48
+ end
49
+
50
+ def match(path, options = {}, &block)
51
+ if block
52
+ @scope_stack.push(options.merge({:path => path}))
53
+ begin
54
+ instance_eval(&block)
55
+ ensure
56
+ @scope_stack.pop
57
+ end
58
+
59
+ return
60
+ end
61
+
62
+ new_options = {}
63
+ method = options.delete(:via)
64
+ requirements = options.delete(:constraints) || {}
65
+ defaults = {}
66
+
67
+ if path.is_a?(Symbol) && scope_options.has_key?(:path)
68
+ defaults[:action] = path.to_s
69
+ path = scope_options[:path]
70
+ elsif path.is_a?(Regexp)
71
+ else
72
+ scoped_path = @scope_stack.map { |scope| scope[:path] }.compact
73
+ scoped_path << path if path.is_a?(String)
74
+ scoped_path.map! { |path| path =~ /^\// ? path : "/#{path}" }
75
+ path = scoped_path.join
76
+ end
77
+
78
+ if path.is_a?(String)
79
+ path = optionalize_trailing_dynamic_segments(path)
80
+ end
81
+
82
+ if controller = scope_options[:controller]
83
+ defaults[:controller] = controller.to_s
84
+ end
85
+
86
+ if to = options.delete(:to)
87
+ controller, action = to.to_s.split('#')
88
+
89
+ if controller && action && defaults[:controller]
90
+ defaults[:controller] = "#{defaults[:controller]}#{controller}"
91
+ defaults[:action] = action
92
+ elsif !action && defaults[:controller]
93
+ defaults[:action] = controller if controller
94
+ else
95
+ defaults[:controller] = controller if controller
96
+ defaults[:action] = action if action
97
+ end
98
+ end
99
+
100
+ app = defaults.has_key?(:controller) ?
101
+ ActiveSupport::Inflector.constantize("#{defaults[:controller].camelize}Controller") :
102
+ DynamicController
103
+
104
+ if path.is_a?(String)
105
+ path = Utils.convert_segment_string_to_regexp(path, requirements, %w( / . ? ))
106
+ end
107
+ conditions = { :method => method, :path => path }
108
+ @set.add_route(app, conditions, defaults)
109
+ end
110
+
111
+ def controller(controller, &block)
112
+ @scope_stack.push(:controller => controller)
113
+ begin
114
+ instance_eval(&block)
115
+ ensure
116
+ @scope_stack.pop
117
+ end
118
+ end
119
+
120
+ def namespace(namespace, &block)
121
+ @scope_stack.push(:path => namespace.to_s, :controller => "#{namespace}/")
122
+ begin
123
+ instance_eval(&block)
124
+ ensure
125
+ @scope_stack.pop
126
+ end
127
+ end
128
+
129
+ def resources(*entities, &block)
130
+ options = entities.extract_options!
131
+ entities.each { |entity| map_resource(entity, options.dup, &block) }
132
+ end
133
+
134
+ private
135
+ def scope_options
136
+ options = {}
137
+ @scope_stack.each { |opts| options.merge!(opts) }
138
+ options
139
+ end
140
+
141
+ def map_resource(entities, options = {}, &block)
142
+ resource = ActionController::Resources::Resource.new(entities, options)
143
+
144
+ get(resource.path, :to => "#{resource.controller}#index")
145
+ post(resource.path, :to => "#{resource.controller}#create")
146
+ get(resource.new_path, :to => "#{resource.controller}#new")
147
+ get("#{resource.member_path}/edit", :to => "#{resource.controller}#edit")
148
+ get(resource.member_path, :to => "#{resource.controller}#show")
149
+ put(resource.member_path, :to => "#{resource.controller}#update")
150
+ delete(resource.member_path, :to => "#{resource.controller}#destroy")
151
+ end
152
+
153
+ def optionalize_trailing_dynamic_segments(path)
154
+ path = (path =~ /^\//) ? path.dup : "/#{path}"
155
+ optional, segments = true, []
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
+ if optional && !(segment =~ /^:\w+$/) && !(segment =~ /^:\w+\(\.:format\)$/)
163
+ optional = false
164
+ end
165
+
166
+ if optional && index < length - 1
167
+ segments.unshift('(/', segment)
168
+ segments.push(')')
169
+ else
170
+ segments.unshift('/', segment)
171
+ end
172
+ end
173
+
174
+ segments.join
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,37 @@
1
+ module Rack
2
+ module Mount
3
+ class RouteSet
4
+ def prepare
5
+ map = Mappers::Simple.new(self)
6
+ yield map
7
+ freeze
8
+ end
9
+ end
10
+
11
+ module Mappers
12
+ class Simple
13
+ def initialize(set)
14
+ @set = set
15
+ end
16
+
17
+ def map(*args)
18
+ options = args.last.is_a?(Hash) ? args.pop : {}
19
+
20
+ app = options[:to]
21
+ path = args[0]
22
+ method = args[1]
23
+ defaults = options[:with]
24
+
25
+ requirements = options[:conditions] || {}
26
+ requirements.each { |k,v| requirements[k] = v.to_s unless v.is_a?(Regexp) }
27
+
28
+ if path.is_a?(String)
29
+ path = Utils.convert_segment_string_to_regexp(path, requirements, %w( / . ? ))
30
+ end
31
+ conditions = { :method => method, :path => path }
32
+ @set.add_route(app, conditions, defaults)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,106 @@
1
+ module Rack
2
+ module Mount
3
+ class NestedSet < Hash #:nodoc:
4
+ class List < Array #:nodoc:
5
+ def freeze
6
+ each { |e| e.freeze }
7
+ super
8
+ end
9
+ end
10
+
11
+ def initialize(default = List.new)
12
+ super(default)
13
+ end
14
+
15
+ alias_method :at, :[]
16
+
17
+ WILD_REGEXP = /.*/.freeze
18
+
19
+ def []=(*args)
20
+ args = args.flatten
21
+ value = args.pop
22
+ key = args.shift.freeze
23
+ key = WILD_REGEXP if key.nil?
24
+ keys = args.freeze
25
+
26
+ raise ArgumentError, 'missing value' unless value
27
+
28
+ case key
29
+ when Regexp
30
+ if keys.empty?
31
+ each { |k, v| v << value if key =~ k }
32
+ default << value
33
+ else
34
+ each { |k, v| v[keys.dup] = value if key =~ k }
35
+ self.default = NestedSet.new(default) if default.is_a?(List)
36
+ default[keys.dup] = value
37
+ end
38
+ when String
39
+ v = at(key)
40
+ v = v.dup if v.equal?(default)
41
+
42
+ if keys.empty?
43
+ v << value
44
+ else
45
+ v = NestedSet.new(v) if v.is_a?(List)
46
+ v[keys.dup] = value
47
+ end
48
+
49
+ super(key, v)
50
+ else
51
+ raise ArgumentError, 'unsupported key'
52
+ end
53
+ end
54
+
55
+ def [](*keys)
56
+ result, i = self, 0
57
+ until result.is_a?(Array)
58
+ result = result.at(keys[i])
59
+ i += 1
60
+ end
61
+ result
62
+ end
63
+
64
+ def <<(value)
65
+ values_with_default.each { |e| e << value }
66
+ nil
67
+ end
68
+
69
+ def values_with_default
70
+ values.push(default)
71
+ end
72
+
73
+ def inspect
74
+ super.gsub(/\}$/, ", nil => #{default.inspect}}")
75
+ end
76
+
77
+ def freeze
78
+ values_with_default.each { |v| v.freeze }
79
+ super
80
+ end
81
+
82
+ def lists
83
+ descendants = []
84
+ values_with_default.each do |v|
85
+ if v.is_a?(NestedSet)
86
+ v.lists.each do |descendant|
87
+ descendants << descendant
88
+ end
89
+ else
90
+ descendants << v
91
+ end
92
+ end
93
+ descendants
94
+ end
95
+
96
+ def height
97
+ longest_list_descendant.length
98
+ end
99
+
100
+ private
101
+ def longest_list_descendant
102
+ lists.max { |a, b| a.length <=> b.length }
103
+ end
104
+ end
105
+ end
106
+ end