rack-mount 0.0.1 → 0.8.3

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 (43) hide show
  1. data/README.rdoc +12 -4
  2. data/lib/rack/mount/analysis/histogram.rb +55 -6
  3. data/lib/rack/mount/analysis/splitting.rb +103 -89
  4. data/lib/rack/mount/code_generation.rb +120 -0
  5. data/lib/rack/mount/generatable_regexp.rb +95 -48
  6. data/lib/rack/mount/multimap.rb +84 -41
  7. data/lib/rack/mount/prefix.rb +13 -8
  8. data/lib/rack/mount/regexp_with_named_groups.rb +27 -7
  9. data/lib/rack/mount/route.rb +75 -18
  10. data/lib/rack/mount/route_set.rb +308 -22
  11. data/lib/rack/mount/strexp/parser.rb +160 -0
  12. data/lib/rack/mount/strexp/tokenizer.rb +83 -0
  13. data/lib/rack/mount/strexp.rb +54 -79
  14. data/lib/rack/mount/utils.rb +65 -174
  15. data/lib/rack/mount/vendor/regin/regin/alternation.rb +40 -0
  16. data/lib/rack/mount/vendor/regin/regin/anchor.rb +4 -0
  17. data/lib/rack/mount/vendor/regin/regin/atom.rb +54 -0
  18. data/lib/rack/mount/vendor/regin/regin/character.rb +51 -0
  19. data/lib/rack/mount/vendor/regin/regin/character_class.rb +50 -0
  20. data/lib/rack/mount/vendor/regin/regin/collection.rb +77 -0
  21. data/lib/rack/mount/vendor/regin/regin/expression.rb +126 -0
  22. data/lib/rack/mount/vendor/regin/regin/group.rb +90 -0
  23. data/lib/rack/mount/vendor/regin/regin/options.rb +55 -0
  24. data/lib/rack/mount/vendor/regin/regin/parser.rb +546 -0
  25. data/lib/rack/mount/vendor/regin/regin/tokenizer.rb +255 -0
  26. data/lib/rack/mount/vendor/regin/regin/version.rb +3 -0
  27. data/lib/rack/mount/vendor/regin/regin.rb +75 -0
  28. data/lib/rack/mount/version.rb +3 -0
  29. data/lib/rack/mount.rb +13 -17
  30. metadata +88 -35
  31. data/lib/rack/mount/analysis/frequency.rb +0 -51
  32. data/lib/rack/mount/const.rb +0 -45
  33. data/lib/rack/mount/exceptions.rb +0 -3
  34. data/lib/rack/mount/generation/route.rb +0 -57
  35. data/lib/rack/mount/generation/route_set.rb +0 -163
  36. data/lib/rack/mount/meta_method.rb +0 -104
  37. data/lib/rack/mount/mixover.rb +0 -47
  38. data/lib/rack/mount/recognition/code_generation.rb +0 -99
  39. data/lib/rack/mount/recognition/route.rb +0 -59
  40. data/lib/rack/mount/recognition/route_set.rb +0 -88
  41. data/lib/rack/mount/vendor/multimap/multimap.rb +0 -466
  42. data/lib/rack/mount/vendor/multimap/multiset.rb +0 -153
  43. data/lib/rack/mount/vendor/multimap/nested_multimap.rb +0 -156
@@ -1,57 +0,0 @@
1
- require 'rack/mount/utils'
2
-
3
- module Rack::Mount
4
- module Generation
5
- module Route #:nodoc:
6
- attr_reader :generation_keys
7
-
8
- def initialize(*args)
9
- super
10
-
11
- @required_params = {}
12
- @required_defaults = {}
13
- @generation_keys = @defaults.dup
14
-
15
- @conditions.each do |method, condition|
16
- @required_params[method] = @conditions[method].required_captures.map { |s| s.name }.reject { |s| @defaults.include?(s) }.freeze
17
- @required_defaults[method] = @defaults.dup
18
- @conditions[method].captures.inject({}) { |h, s| h.merge!(s.to_hash) }.keys.each { |name|
19
- @required_defaults[method].delete(name)
20
- @generation_keys.delete(name) if @defaults.include?(name)
21
- }
22
- @required_defaults[method].freeze
23
- end
24
-
25
- @required_params.freeze
26
- @required_defaults.freeze
27
- @generation_keys.freeze
28
- end
29
-
30
- def generate(methods, params = {}, recall = {})
31
- if methods.is_a?(Array)
32
- result = methods.map { |m| generate_method(m, params, recall, @defaults) || (return nil) }
33
- else
34
- result = generate_method(methods, params, recall, @defaults)
35
- end
36
-
37
- if result
38
- @defaults.each do |key, value|
39
- params.delete(key) if params[key] == value
40
- end
41
- end
42
-
43
- result
44
- end
45
-
46
- private
47
- def generate_method(method, params, recall, defaults)
48
- merged = recall.merge(params)
49
- return nil unless condition = @conditions[method]
50
- return nil if condition.segments.empty?
51
- return nil unless @required_params[method].all? { |p| merged.include?(p) }
52
- return nil unless @required_defaults[method].all? { |k, v| merged[k] == v }
53
- condition.generate(params, recall, defaults)
54
- end
55
- end
56
- end
57
- end
@@ -1,163 +0,0 @@
1
- require 'rack/mount/utils'
2
- require 'forwardable'
3
-
4
- module Rack::Mount
5
- module Generation
6
- module RouteSet
7
- # Adds generation related concerns to RouteSet.new.
8
- def initialize(*args)
9
- @named_routes = {}
10
- @generation_key_analyzer = Analysis::Frequency.new
11
-
12
- super
13
- end
14
-
15
- # Adds generation aspects to RouteSet#add_route.
16
- def add_route(*args)
17
- route = super
18
- @named_routes[route.name] = route if route.name
19
- @generation_key_analyzer << route.generation_keys
20
- route
21
- end
22
-
23
- # Generates path from identifiers or significant keys.
24
- #
25
- # To generate a url by named route, pass the name in as a +Symbol+.
26
- # url(:dashboard) # => "/dashboard"
27
- #
28
- # Additional parameters can be passed in as a hash
29
- # url(:people, :id => "1") # => "/people/1"
30
- #
31
- # If no name route is given, it will fall back to a slower
32
- # generation search.
33
- # url(:controller => "people", :action => "show", :id => "1")
34
- # # => "/people/1"
35
- def url(*args)
36
- named_route, params, recall = extract_params!(*args)
37
-
38
- params = URISegment.wrap_values(params)
39
- recall = URISegment.wrap_values(recall)
40
-
41
- unless result = generate(:path_info, named_route, params, recall)
42
- return
43
- end
44
-
45
- uri, params = result
46
- params.each do |k, v|
47
- if v._value
48
- params[k] = v._value
49
- else
50
- params.delete(k)
51
- end
52
- end
53
-
54
- uri << "?#{Utils.build_nested_query(params)}" if uri && params.any?
55
- uri
56
- end
57
-
58
- def generate(method, *args) #:nodoc:
59
- raise 'route set not finalized' unless @generation_graph
60
-
61
- named_route, params, recall = extract_params!(*args)
62
- merged = recall.merge(params)
63
- route = nil
64
-
65
- if named_route
66
- if route = @named_routes[named_route.to_sym]
67
- recall = route.defaults.merge(recall)
68
- url = route.generate(method, params, recall)
69
- [url, params]
70
- else
71
- raise RoutingError, "#{named_route} failed to generate from #{params.inspect}"
72
- end
73
- else
74
- keys = @generation_keys.map { |key|
75
- if k = merged[key]
76
- k.to_s
77
- else
78
- nil
79
- end
80
- }
81
- @generation_graph[*keys].each do |r|
82
- if url = r.generate(method, params, recall)
83
- return [url, params]
84
- end
85
- end
86
-
87
- raise RoutingError, "No route matches #{params.inspect}"
88
- end
89
- end
90
-
91
- def rehash #:nodoc:
92
- @generation_keys = build_generation_keys
93
- @generation_graph = build_generation_graph
94
-
95
- super
96
- end
97
-
98
- private
99
- def expire!
100
- @generation_keys = @generation_graph = nil
101
- super
102
- end
103
-
104
- def build_generation_graph
105
- build_nested_route_set(@generation_keys) { |k, i|
106
- if k = @generation_key_analyzer.possible_keys[i][k]
107
- k.to_s
108
- else
109
- nil
110
- end
111
- }
112
- end
113
-
114
- def build_generation_keys
115
- @generation_key_analyzer.report
116
- end
117
-
118
- def extract_params!(*args)
119
- case args.length
120
- when 3
121
- named_route, params, recall = args
122
- when 2
123
- if args[0].is_a?(Hash) && args[1].is_a?(Hash)
124
- params, recall = args
125
- else
126
- named_route, params = args
127
- end
128
- when 1
129
- if args[0].is_a?(Hash)
130
- params = args[0]
131
- else
132
- named_route = args[0]
133
- end
134
- else
135
- raise ArgumentError
136
- end
137
-
138
- named_route ||= nil
139
- params ||= {}
140
- recall ||= {}
141
-
142
- [named_route, params.dup, recall.dup]
143
- end
144
-
145
- class URISegment < Struct.new(:_value)
146
- def self.wrap_values(hash)
147
- hash.inject({}) { |h, (k, v)| h[k] = new(v); h }
148
- end
149
-
150
- extend Forwardable
151
- def_delegators :_value, :==, :eql?, :hash
152
-
153
- def to_param
154
- @to_param ||= begin
155
- v = _value.respond_to?(:to_param) ? _value.to_param : _value
156
- Utils.escape_uri(v)
157
- end
158
- end
159
- alias_method :to_s, :to_param
160
- end
161
- end
162
- end
163
- end
@@ -1,104 +0,0 @@
1
- module Rack::Mount
2
- class MetaMethod #:nodoc:
3
- class Block < Array #:nodoc:
4
- def initialize(*parts)
5
- replace(parts)
6
- yield(self) if block_given?
7
- end
8
-
9
- def multiline?
10
- length > 1
11
- end
12
-
13
- def inspect(indented = 2)
14
- return Const::EMPTY_STRING if empty?
15
- space = ' ' * indented
16
- space + map { |p|
17
- if p.is_a?(Condition)
18
- p.inspect(indented)
19
- else
20
- p
21
- end
22
- }.join("\n#{space}")
23
- end
24
-
25
- def to_str
26
- map { |p| p.to_str }.join('; ')
27
- end
28
- end
29
-
30
- class Condition #:nodoc:
31
- attr_accessor :body, :else
32
-
33
- def initialize(*conditions)
34
- @conditions = conditions.map { |c| c.is_a?(Block) ? c : Block.new(c) }
35
- @body = Block.new
36
- @else = Block.new
37
- yield(@body) if block_given?
38
- end
39
-
40
- def <<(condition)
41
- @conditions << condition
42
- end
43
-
44
- def inspect(indented = 2)
45
- return @body.inspect(indented) if @conditions.empty?
46
- space = ' ' * indented
47
- str = 'if '
48
- str << @conditions.map { |b|
49
- b.multiline? ?
50
- "begin\n#{b.inspect(indented + 4)}\n#{space} end" :
51
- b.inspect(0)
52
- }.join(' && ')
53
- str << "\n#{@body.inspect(indented + 2)}" if @body.any?
54
- if @else.any?
55
- str << "\n#{space}else\n#{@else.inspect(indented + 2)}"
56
- end
57
- str << "\n#{space}end"
58
- str
59
- end
60
-
61
- def to_str
62
- return @body.to_str if @conditions.empty?
63
- str = 'if '
64
- str << @conditions.map { |b|
65
- b.multiline? ? "(#{b.to_str})" : b.to_str
66
- }.join(' && ')
67
- str << "; #{@body.to_str}" if @body.any?
68
- if @else.any?
69
- str << "; else; #{@else.to_str}"
70
- end
71
- str << "; end"
72
- str
73
- end
74
- end
75
-
76
- def initialize(sym, *args)
77
- @sym = sym
78
- @args = args
79
- @body = Block.new
80
- end
81
-
82
- def <<(line)
83
- @body << line
84
- end
85
-
86
- def inspect
87
- str = ""
88
- str << "def #{@sym}"
89
- str << "(#{@args.join(', ')})" if @args.any?
90
- str << "\n#{@body.inspect}" if @body.any?
91
- str << "\nend\n"
92
- str
93
- end
94
-
95
- def to_str
96
- str = []
97
- str << "def #{@sym}"
98
- str << "(#{@args.join(', ')})" if @args.any?
99
- str << "\n#{@body.to_str}" if @body.any?
100
- str << "\nend"
101
- str.join
102
- end
103
- end
104
- end
@@ -1,47 +0,0 @@
1
- module Rack::Mount
2
- # A mixin that changes the behavior of +include+. Instead of modules
3
- # being chained as a superclass, they are mixed into the objects
4
- # metaclass. This allows mixins to be stacked ontop of the instance
5
- # methods.
6
- module Mixover
7
- module InstanceMethods #:nodoc:
8
- def dup
9
- obj = super
10
- included_modules = (class << self; included_modules; end) - (class << obj; included_modules; end)
11
- included_modules.reverse.each { |mod| obj.extend(mod) }
12
- obj
13
- end
14
- end
15
-
16
- # Replaces include with a lazy version.
17
- def include(*mod)
18
- (@included_modules ||= []).push(*mod)
19
- end
20
-
21
- def new(*args, &block) #:nodoc:
22
- obj = allocate
23
- obj.extend(InstanceMethods)
24
- (@included_modules ||= []).each { |mod| obj.extend(mod) }
25
- obj.send(:initialize, *args, &block)
26
- obj
27
- end
28
-
29
- # Create a new class without an included module.
30
- def new_without_module(mod, *args, &block)
31
- old_included_modules = (@included_modules ||= []).dup
32
- @included_modules.delete(mod)
33
- new(*args, &block)
34
- ensure
35
- @included_modules = old_included_modules
36
- end
37
-
38
- # Create a new class temporarily with a module.
39
- def new_with_module(mod, *args, &block)
40
- old_included_modules = (@included_modules ||= []).dup
41
- include(mod)
42
- new(*args, &block)
43
- ensure
44
- @included_modules = old_included_modules
45
- end
46
- end
47
- end
@@ -1,99 +0,0 @@
1
- require 'rack/mount/meta_method'
2
-
3
- module Rack::Mount
4
- module Recognition
5
- module CodeGeneration #:nodoc:
6
- def _expired_call(env) #:nodoc:
7
- raise 'route set not finalized'
8
- end
9
-
10
- def rehash
11
- super
12
- optimize_call!
13
- end
14
-
15
- private
16
- def expire!
17
- class << self
18
- alias_method :call, :_expired_call
19
- end
20
-
21
- super
22
- end
23
-
24
- def optimize_container_iterator(container)
25
- m = MetaMethod.new(:optimized_each, :req)
26
- m << 'env = req.env'
27
-
28
- container.each_with_index { |route, i|
29
- path_info_unanchored = route.conditions[:path_info] &&
30
- !Utils.regexp_anchored?(route.conditions[:path_info])
31
- m << "route = self[#{i}]"
32
- m << 'routing_args = route.defaults.dup'
33
-
34
- m << matchers = MetaMethod::Condition.new do |body|
35
- body << "env[#{@parameters_key.inspect}] = routing_args"
36
- body << "response = route.app.call(env)"
37
- body << "return response unless response[0].to_i == 417"
38
- end
39
-
40
- route.conditions.each do |method, condition|
41
- matchers << MetaMethod::Block.new do |matcher|
42
- matcher << c = MetaMethod::Condition.new("m = req.#{method}.match(#{condition.inspect})") do |b|
43
- b << "matches = m.captures" if route.named_captures[method].any?
44
- route.named_captures[method].each do |k, i|
45
- b << MetaMethod::Condition.new("p = matches[#{i}]") do |c2|
46
- c2 << "routing_args[#{k.inspect}] = Utils.unescape_uri(p)"
47
- end
48
- end
49
- if method == :path_info && !Utils.regexp_anchored?(condition)
50
- b << "env[Prefix::KEY] = m.to_s"
51
- end
52
- b << "true"
53
- end
54
- c.else = MetaMethod::Block.new("false")
55
- end
56
- end
57
- }
58
-
59
- m << 'nil'
60
- # puts "\n#{m.inspect}"
61
- container.instance_eval(m, __FILE__, __LINE__)
62
- end
63
-
64
- def optimize_call!
65
- method = MetaMethod.new(:call, :env)
66
-
67
- if @routes.empty?
68
- method << 'env[Const::EXPECT] != Const::CONTINUE ? Const::NOT_FOUND_RESPONSE : Const::EXPECTATION_FAILED_RESPONSE'
69
- else
70
- method << 'begin'
71
- method << 'set_expectation = env[Const::EXPECT] != Const::CONTINUE'
72
- method << 'env[Const::EXPECT] = Const::CONTINUE if set_expectation'
73
-
74
- method << 'env[Const::PATH_INFO] = Utils.normalize_path(env[Const::PATH_INFO])'
75
- method << "req = #{@request_class.name}.new(env)"
76
- cache = false
77
- keys = @recognition_keys.map { |key|
78
- if key.is_a?(Array)
79
- cache = true
80
- key.call_source(:cache, :req)
81
- else
82
- "req.#{key}"
83
- end
84
- }.join(', ')
85
- method << 'cache = {}' if cache
86
- method << "container = @recognition_graph[#{keys}]"
87
- method << "optimize_container_iterator(container) unless container.respond_to?(:optimized_each)"
88
- method << "container.optimized_each(req) || (set_expectation ? Const::NOT_FOUND_RESPONSE : Const::EXPECTATION_FAILED_RESPONSE)"
89
- method << 'ensure'
90
- method << 'env.delete(Const::EXPECT) if set_expectation'
91
- method << 'end'
92
- end
93
-
94
- # puts "\n#{method.inspect}"
95
- instance_eval(method, __FILE__, __LINE__)
96
- end
97
- end
98
- end
99
- end
@@ -1,59 +0,0 @@
1
- require 'rack/mount/prefix'
2
-
3
- module Rack::Mount
4
- module Recognition
5
- module Route #:nodoc:
6
- attr_reader :named_captures
7
-
8
- def initialize(*args)
9
- super
10
-
11
- # TODO: Don't explict check for :path_info condition
12
- if @conditions.has_key?(:path_info) &&
13
- !Utils.regexp_anchored?(@conditions[:path_info])
14
- @app = Prefix.new(@app)
15
- end
16
-
17
- @named_captures = {}
18
- @conditions.map { |method, condition|
19
- @named_captures[method] = condition.named_captures.inject({}) { |named_captures, (k, v)|
20
- named_captures[k.to_sym] = v.last - 1
21
- named_captures
22
- }.freeze
23
- }
24
- @named_captures.freeze
25
- end
26
-
27
- def call(req)
28
- env = req.env
29
-
30
- routing_args = @defaults.dup
31
- if @conditions.all? { |method, condition|
32
- value = req.send(method)
33
- if m = value.match(condition)
34
- matches = m.captures
35
- @named_captures[method].each { |k, i|
36
- if v = matches[i]
37
- # TODO: We only want to unescape params from
38
- # uri related methods
39
- routing_args[k] = Utils.unescape_uri(v)
40
- end
41
- }
42
- # TODO: Don't explict check for :path_info condition
43
- if method == :path_info && !Utils.regexp_anchored?(condition)
44
- env[Prefix::KEY] = m.to_s
45
- end
46
- true
47
- else
48
- false
49
- end
50
- }
51
- env[@set.parameters_key] = routing_args
52
- @app.call(env)
53
- else
54
- Const::EXPECTATION_FAILED_RESPONSE
55
- end
56
- end
57
- end
58
- end
59
- end
@@ -1,88 +0,0 @@
1
- require 'rack/mount/utils'
2
-
3
- module Rack::Mount
4
- module Recognition
5
- module RouteSet
6
- attr_reader :parameters_key
7
-
8
- # Adds recognition related concerns to RouteSet.new.
9
- def initialize(options = {})
10
- @parameters_key = options.delete(:parameters_key) || Const::RACK_ROUTING_ARGS
11
- @parameters_key.freeze
12
- @recognition_key_analyzer = Analysis::Frequency.new_with_module(Analysis::Splitting)
13
-
14
- super
15
- end
16
-
17
- # Adds recognition aspects to RouteSet#add_route.
18
- def add_route(*args)
19
- route = super
20
- @recognition_key_analyzer << route.conditions
21
- route
22
- end
23
-
24
- # Rack compatible recognition and dispatching method. Routes are
25
- # tried until one returns a non-catch status code. If no routes
26
- # match, the catch status code is returned.
27
- #
28
- # This method can only be invoked after the RouteSet has been
29
- # finalized.
30
- def call(env)
31
- raise 'route set not finalized' unless @recognition_graph
32
-
33
- set_expectation = env[Const::EXPECT] != Const::CONTINUE
34
- env[Const::EXPECT] = Const::CONTINUE if set_expectation
35
-
36
- env[Const::PATH_INFO] = Utils.normalize_path(env[Const::PATH_INFO])
37
-
38
- cache = {}
39
- req = @request_class.new(env)
40
- keys = @recognition_keys.map { |key|
41
- if key.is_a?(Array)
42
- key.call(cache, req)
43
- else
44
- req.send(key)
45
- end
46
- }
47
- @recognition_graph[*keys].each do |route|
48
- result = route.call(req)
49
- return result unless result[0].to_i == 417
50
- end
51
- set_expectation ? Const::NOT_FOUND_RESPONSE : Const::EXPECTATION_FAILED_RESPONSE
52
- ensure
53
- env.delete(Const::EXPECT) if set_expectation
54
- end
55
-
56
- def rehash #:nodoc:
57
- @recognition_keys = build_recognition_keys
58
- @recognition_graph = build_recognition_graph
59
-
60
- super
61
- end
62
-
63
- def valid_conditions #:nodoc:
64
- @valid_conditions ||= begin
65
- conditions = @request_class.instance_methods(false)
66
- conditions.map! { |m| m.to_sym }
67
- conditions.freeze
68
- end
69
- end
70
-
71
- private
72
- def expire!
73
- @recognition_keys = @recognition_graph = nil
74
- super
75
- end
76
-
77
- def build_recognition_graph
78
- build_nested_route_set(@recognition_keys) { |k, i|
79
- @recognition_key_analyzer.possible_keys[i][k]
80
- }
81
- end
82
-
83
- def build_recognition_keys
84
- @recognition_key_analyzer.report
85
- end
86
- end
87
- end
88
- end