josh-rack-mount 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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