waves 0.8.2 → 0.9.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.
Files changed (166) hide show
  1. data/bin/waves +4 -3
  2. data/doc/VERSION +1 -1
  3. data/lib/waves.rb +52 -40
  4. data/lib/{caches → waves/caches}/file.rb +3 -1
  5. data/lib/waves/caches/memcached.rb +56 -0
  6. data/lib/{caches → waves/caches}/simple.rb +6 -7
  7. data/lib/{caches → waves/caches}/synchronized.rb +15 -1
  8. data/lib/{commands → waves/commands}/console.rb +4 -4
  9. data/lib/{commands → waves/commands}/generate.rb +6 -5
  10. data/lib/{commands → waves/commands}/help.rb +0 -0
  11. data/lib/{commands → waves/commands}/server.rb +1 -1
  12. data/lib/{dispatchers → waves/dispatchers}/base.rb +17 -31
  13. data/lib/waves/dispatchers/default.rb +19 -0
  14. data/lib/{ext → waves/ext}/float.rb +0 -0
  15. data/lib/{ext → waves/ext}/hash.rb +0 -0
  16. data/lib/{ext → waves/ext}/integer.rb +16 -1
  17. data/lib/{ext → waves/ext}/kernel.rb +3 -7
  18. data/lib/{ext → waves/ext}/module.rb +3 -3
  19. data/lib/{ext → waves/ext}/object.rb +2 -0
  20. data/lib/waves/ext/string.rb +73 -0
  21. data/lib/{ext → waves/ext}/symbol.rb +0 -1
  22. data/lib/{ext → waves/ext}/tempfile.rb +0 -0
  23. data/lib/waves/ext/time.rb +5 -0
  24. data/lib/{foundations → waves/foundations}/classic.rb +9 -21
  25. data/lib/{foundations → waves/foundations}/compact.rb +15 -20
  26. data/lib/waves/foundations/rest.rb +311 -0
  27. data/lib/waves/helpers/basic.rb +13 -0
  28. data/lib/{helpers → waves/helpers}/doc_type.rb +3 -0
  29. data/lib/waves/helpers/form.rb +94 -0
  30. data/lib/waves/helpers/formatting.rb +14 -0
  31. data/lib/waves/layers/mvc.rb +65 -0
  32. data/lib/{layers → waves/layers}/mvc/controllers.rb +0 -0
  33. data/lib/{layers → waves/layers}/mvc/extensions.rb +23 -11
  34. data/lib/{layers → waves/layers}/orm/migration.rb +0 -0
  35. data/lib/{layers → waves/layers}/orm/providers/active_record.rb +2 -5
  36. data/lib/{layers → waves/layers}/orm/providers/active_record/migrations/empty.rb.erb +0 -0
  37. data/lib/{layers → waves/layers}/orm/providers/active_record/tasks/generate.rb +1 -1
  38. data/lib/{layers → waves/layers}/orm/providers/active_record/tasks/schema.rb +1 -1
  39. data/lib/{layers → waves/layers}/orm/providers/data_mapper.rb +0 -0
  40. data/lib/{layers → waves/layers}/orm/providers/filebase.rb +0 -0
  41. data/lib/{layers → waves/layers}/orm/providers/sequel.rb +28 -29
  42. data/lib/{layers → waves/layers}/orm/providers/sequel/migrations/empty.rb.erb +0 -0
  43. data/lib/{layers → waves/layers}/orm/providers/sequel/tasks/generate.rb +1 -1
  44. data/lib/{layers → waves/layers}/orm/providers/sequel/tasks/schema.rb +2 -0
  45. data/lib/waves/layers/rack/rack_cache.rb +32 -0
  46. data/lib/waves/layers/renderers/erubis.rb +52 -0
  47. data/lib/waves/layers/renderers/haml.rb +67 -0
  48. data/lib/waves/layers/renderers/markaby.rb +41 -0
  49. data/lib/waves/layers/text/inflect/english.rb +42 -0
  50. data/lib/waves/matchers/accept.rb +47 -0
  51. data/lib/waves/matchers/ext.rb +27 -0
  52. data/lib/waves/matchers/path.rb +72 -0
  53. data/lib/waves/matchers/query.rb +43 -0
  54. data/lib/waves/matchers/request.rb +86 -0
  55. data/lib/waves/matchers/requested.rb +31 -0
  56. data/lib/{matchers → waves/matchers}/resource.rb +8 -1
  57. data/lib/waves/matchers/traits.rb +30 -0
  58. data/lib/waves/matchers/uri.rb +69 -0
  59. data/lib/waves/media/mime_types.rb +542 -0
  60. data/lib/waves/renderers/mixin.rb +9 -0
  61. data/lib/waves/request/accept.rb +92 -0
  62. data/lib/{runtime → waves/request}/request.rb +77 -61
  63. data/lib/waves/resources/file_mixin.rb +11 -0
  64. data/lib/{resources → waves/resources}/mixin.rb +42 -44
  65. data/lib/waves/resources/paths.rb +132 -0
  66. data/lib/waves/response/client_errors.rb +10 -0
  67. data/lib/waves/response/packaged.rb +19 -0
  68. data/lib/waves/response/redirects.rb +35 -0
  69. data/lib/{runtime → waves/response}/response.rb +29 -11
  70. data/lib/{runtime → waves/response}/response_mixin.rb +30 -17
  71. data/lib/waves/runtime/applications.rb +18 -0
  72. data/lib/{runtime → waves/runtime}/configuration.rb +31 -25
  73. data/lib/waves/runtime/console.rb +24 -0
  74. data/lib/{runtime → waves/runtime}/logger.rb +3 -3
  75. data/lib/{runtime → waves/runtime}/mocks.rb +2 -2
  76. data/lib/waves/runtime/rackup.rb +37 -0
  77. data/lib/waves/runtime/runtime.rb +48 -0
  78. data/lib/waves/runtime/server.rb +33 -0
  79. data/lib/{servers → waves/servers}/base.rb +0 -0
  80. data/lib/{servers → waves/servers}/mongrel.rb +0 -0
  81. data/lib/{servers → waves/servers}/webrick.rb +0 -0
  82. data/lib/{tasks → waves/tasks}/gem.rb +0 -0
  83. data/lib/{tasks → waves/tasks}/generate.rb +0 -0
  84. data/lib/waves/views/cassy.rb +173 -0
  85. data/lib/{views → waves/views}/errors.rb +8 -7
  86. data/lib/waves/views/mixin.rb +23 -0
  87. data/lib/waves/views/templated.rb +40 -0
  88. data/samples/basic/basic_startup.rb +70 -0
  89. data/samples/basic/config.ru +9 -0
  90. data/samples/blog/configurations/development.rb +17 -16
  91. data/samples/blog/configurations/production.rb +0 -11
  92. data/samples/blog/resources/entry.rb +3 -3
  93. data/samples/blog/resources/map.rb +10 -3
  94. data/samples/blog/startup.rb +4 -3
  95. data/templates/classic/Rakefile +28 -29
  96. data/templates/classic/configurations/default.rb.erb +8 -3
  97. data/templates/classic/configurations/development.rb.erb +1 -20
  98. data/templates/classic/configurations/production.rb.erb +2 -16
  99. data/templates/classic/public/images/favicon.ico +0 -0
  100. data/templates/classic/resources/server.rb.erb +9 -0
  101. data/templates/classic/startup.rb.erb +3 -3
  102. data/templates/classic/views/css.rb.erb +14 -0
  103. data/templates/classic/views/default.rb.erb +17 -0
  104. data/templates/classic/views/errors.rb.erb +10 -0
  105. data/templates/classic/views/pages.rb.erb +14 -0
  106. data/templates/compact/startup.rb.erb +8 -3
  107. data/test/ext/object.rb +55 -0
  108. data/test/ext/shortcuts.rb +73 -0
  109. data/test/helpers.rb +17 -0
  110. data/test/match/accept.rb +78 -0
  111. data/test/match/ext.rb +156 -0
  112. data/test/match/methods.rb +22 -0
  113. data/test/match/params.rb +33 -0
  114. data/test/match/path.rb +106 -0
  115. data/test/match/query.rb +60 -0
  116. data/test/match/request.rb +91 -0
  117. data/test/match/requested.rb +149 -0
  118. data/test/match/uri.rb +136 -0
  119. data/test/process/request.rb +75 -0
  120. data/test/process/resource.rb +53 -0
  121. data/test/resources/path.rb +166 -0
  122. data/test/runtime/configurations.rb +19 -0
  123. data/test/runtime/request.rb +63 -0
  124. data/test/runtime/response.rb +85 -0
  125. data/test/views/views.rb +40 -0
  126. metadata +243 -157
  127. data/lib/caches/memcached.rb +0 -40
  128. data/lib/dispatchers/default.rb +0 -25
  129. data/lib/ext/string.rb +0 -20
  130. data/lib/helpers/basic.rb +0 -11
  131. data/lib/helpers/extended.rb +0 -21
  132. data/lib/helpers/form.rb +0 -42
  133. data/lib/helpers/formatting.rb +0 -30
  134. data/lib/helpers/layouts.rb +0 -37
  135. data/lib/helpers/model.rb +0 -37
  136. data/lib/helpers/view.rb +0 -22
  137. data/lib/layers/inflect/english.rb +0 -67
  138. data/lib/layers/mvc.rb +0 -54
  139. data/lib/layers/renderers/erubis.rb +0 -60
  140. data/lib/layers/renderers/haml.rb +0 -47
  141. data/lib/layers/renderers/markaby.rb +0 -29
  142. data/lib/matchers/accept.rb +0 -21
  143. data/lib/matchers/base.rb +0 -30
  144. data/lib/matchers/content_type.rb +0 -17
  145. data/lib/matchers/path.rb +0 -67
  146. data/lib/matchers/query.rb +0 -21
  147. data/lib/matchers/request.rb +0 -27
  148. data/lib/matchers/traits.rb +0 -19
  149. data/lib/matchers/uri.rb +0 -20
  150. data/lib/renderers/mixin.rb +0 -36
  151. data/lib/resources/paths.rb +0 -34
  152. data/lib/runtime/console.rb +0 -23
  153. data/lib/runtime/mime_types.rb +0 -536
  154. data/lib/runtime/monitor.rb +0 -32
  155. data/lib/runtime/runtime.rb +0 -67
  156. data/lib/runtime/server.rb +0 -20
  157. data/lib/runtime/session.rb +0 -27
  158. data/lib/runtime/worker.rb +0 -86
  159. data/lib/views/mixin.rb +0 -62
  160. data/samples/blog/blog.db +0 -0
  161. data/samples/blog/log/waves.production +0 -3
  162. data/templates/classic/resources/map.rb.erb +0 -8
  163. data/templates/classic/templates/errors/not_found_404.mab +0 -7
  164. data/templates/classic/templates/errors/server_error_500.mab +0 -7
  165. data/templates/classic/templates/layouts/default.mab +0 -14
  166. data/templates/classic/tmp/sessions/.gitignore +0 -0
@@ -0,0 +1,32 @@
1
+ require 'rack/cache'
2
+
3
+ module Waves
4
+
5
+ module Cache
6
+
7
+ module RackCache
8
+
9
+ def self.included(app)
10
+
11
+ #registering the default configuration for rack-cache
12
+ app.application.use Rack::Cache,
13
+ #set cache related options
14
+ :verbose => true,
15
+ # default_ttl will be add to any cacheable response without explicit indication of max-age.
16
+ # set :default_ttl, 60 * 60 * 24
17
+ # store can be heap, memcache or disk. Default option is heap.
18
+ #set :metastore, 'file:/var/cache/rack/meta'
19
+ :entitystore => 'file:./cache/rack/body',
20
+ # request containing 'Authorization' and 'Cookie' headers are defined 'private' and thus not cacheable.
21
+ # overriding the private_headers will define which headers make the request not cacheable.
22
+ # instead of overriding this config, you may choose to use the header 'Vary' in your application.
23
+ :private_headers => ['Authorization']
24
+ #end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,52 @@
1
+ require 'erubis'
2
+
3
+ module Waves
4
+
5
+ module Renderers
6
+
7
+ module Erubis
8
+
9
+ Extension = :erb
10
+
11
+ # extend Waves::Renderers::Mixin
12
+
13
+ def self.included( app )
14
+ Waves::Views.renderers << self
15
+ app.auto_eval :Views do
16
+ auto_eval true do
17
+ include ViewMethods
18
+ end
19
+ end
20
+ end
21
+
22
+ # def self.render( path, assigns={} )
23
+ # eruby = ::Erubis::Eruby.new( template( path ) )
24
+ # helper = helper( path )
25
+ # context = ::Erubis::Context.new( assigns )
26
+ # ( class << context ; self ; end ).module_eval do
27
+ # include( helper )
28
+ # def << (s) ; s ; end
29
+ # end
30
+ # eruby.evaluate( context )
31
+ # end
32
+
33
+ module ViewMethods
34
+
35
+ def erb(string, assigns={})
36
+ eruby = ::Erubis::Eruby.new( string )
37
+ helper = Waves.main::Helpers[self.class.basename]
38
+ context = ::Erubis::Context.new( assigns )
39
+ ( class << context ; self ; end ).module_eval do
40
+ include( helper )
41
+ def << (s) ; s ; end
42
+ end
43
+ eruby.evaluate( context )
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,67 @@
1
+ require 'haml'
2
+
3
+ module Waves
4
+
5
+ module Renderers
6
+
7
+ module Haml
8
+
9
+ Extension = :haml
10
+
11
+ # extend Waves::Renderers::Mixin
12
+
13
+ def self.included(app)
14
+ Waves::Views.renderers << self
15
+ Waves::Views::Base.send(:include, self::ViewMethods)
16
+ end
17
+
18
+ module ViewMethods
19
+
20
+ def haml(string, assigns={})
21
+ engine = ::Haml::Engine.new( string )
22
+ scope = Scope.new
23
+ helper = Waves.main::Helpers[self.class.basename]
24
+ scope.meta_eval { include( helper ) }
25
+ scope.instance_eval do
26
+ assigns.each { |key,val| instance_variable_set("@#{key}",val) unless key == :request }
27
+ end
28
+ engine.render(scope, assigns)
29
+ end
30
+
31
+ end
32
+
33
+ # def self.render( path, assigns )
34
+ # engine = ::Haml::Engine.new( template( path ) )
35
+ # scope = Scope.new
36
+ # helper = helper( path )
37
+ # scope.meta_eval { include( helper ) }
38
+ # scope.instance_eval do
39
+ # assigns.each { |key,val| instance_variable_set("@#{key}",val) unless key == :request }
40
+ # end
41
+ # engine.render(scope, assigns)
42
+ # end
43
+
44
+ class Scope
45
+ include Waves::Helpers::DocType
46
+ include Waves::Helpers::Layouts
47
+ include Waves::Helpers::Model
48
+ include Waves::Helpers::View
49
+
50
+ def <<(s)
51
+ eval("@haml_buffer", @binding).push_text s # add to rendered output
52
+ end
53
+
54
+ def capture(&block)
55
+ capture_haml(nil, &block)
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+
67
+
@@ -0,0 +1,41 @@
1
+ module Waves
2
+
3
+ module Renderers
4
+
5
+ module Markaby
6
+
7
+ Extension = :mab
8
+
9
+ # extend Waves::Renderers::Mixin
10
+
11
+ def self.included( app )
12
+ require 'markaby'
13
+ ::Markaby::Builder.set( :indent, 2 )
14
+ Waves::Views.renderers << self
15
+ # Waves::Views::Base.send(:include, self::ViewMethods)
16
+ app.auto_eval :Views do
17
+ auto_eval :Default do
18
+ include ViewMethods
19
+ end
20
+ end
21
+ end
22
+
23
+ module ViewMethods
24
+
25
+ def mab(string, assigns={})
26
+ builder = ::Markaby::Builder.new( assigns )
27
+ helper = Waves.main::Helpers[self.class.basename]
28
+ builder.meta_eval { include( helper ) }
29
+ builder.instance_eval( string )
30
+ builder.to_s
31
+ end
32
+
33
+ end
34
+
35
+
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,42 @@
1
+ module Waves
2
+ module Layers
3
+ module Text
4
+ module Inflect
5
+
6
+ # Adds plural/singular methods for English to String
7
+ module English
8
+
9
+ def self.included(app)
10
+
11
+ require 'english/inflect'
12
+
13
+ Waves::Resources::Mixin::ClassMethods.module_eval do
14
+ def singular ; basename.snake_case.singular ; end
15
+ def plural ; basename.snake_case.plural ; end
16
+ end
17
+
18
+ Waves::ResponseMixin.module_eval do
19
+ def singular ; self.class.basename.snake_case.singular ; end
20
+ def plural ; self.class.basename.snake_case.plural ; end
21
+ end
22
+
23
+ Waves::Resources::Mixin.module_eval do
24
+ def singular ; self.class.singular ; end
25
+ def plural ; self.class.plural ; end
26
+ end
27
+
28
+ Waves::Resources::Paths.module_eval do
29
+ def resource ; self.class.resource.singular ; end
30
+ def resources ; self.class.resource.plural ; end
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+
42
+
@@ -0,0 +1,47 @@
1
+ module Waves
2
+ module Matchers
3
+
4
+ # @todo Rename to Negotiation? --rue
5
+ #
6
+ class Accept
7
+
8
+ # Set up Accept parsing.
9
+ #
10
+ # Only the defined constraints are included.
11
+ #
12
+ def initialize(options)
13
+
14
+ @constraints = {}
15
+
16
+ { :accept => :accept, :charset => :accept_charset, :lang => :accept_lang }.each { |key,method|
17
+ if options[key]
18
+ if options[key].is_a? Array
19
+ @constraints[method] = options[key] unless options[key].empty?
20
+ else
21
+ @constraints[method] = [ options[key] ]
22
+ end
23
+ end
24
+ }
25
+
26
+ end
27
+
28
+ # Verify that any and all Accept constraints match.
29
+ #
30
+ # Request handles these.
31
+ #
32
+ def call(request)
33
+ @constraints.all? { |key, val| request.send(key).include? val }
34
+ end
35
+
36
+ # Proc-like interface
37
+ #
38
+ def [](request)
39
+ call request
40
+ end
41
+
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,27 @@
1
+ module Waves
2
+ module Matchers
3
+
4
+ class Ext
5
+
6
+ def initialize( ext )
7
+ @ext = ext
8
+ end
9
+
10
+ def call(request)
11
+ test( request.extension, @ext )
12
+ end
13
+
14
+ def test( val, pat )
15
+ case pat
16
+ when false then val.nil?
17
+ when true, '.*', val then true
18
+ when Symbol, Symbol then val == ".#{pat}"
19
+ when Array then pat.any? { |e| test( val, e ) }
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,72 @@
1
+ module Waves
2
+
3
+ module Matchers
4
+
5
+ class Path
6
+
7
+ # Takes an array of pattern elements ... coming soon, support for formatted strings!
8
+ #
9
+ # Empty Array means no path, but nil is not processed.
10
+ #
11
+ def initialize(pattern)
12
+ @pattern = pattern
13
+ end
14
+
15
+ # returns a hash of captured values
16
+ def call( request )
17
+ if @pattern.is_a? Array
18
+ path = extract_path( request ).reverse
19
+ return {} if @pattern.empty? && path.empty?
20
+ capture = {}
21
+ match = @pattern.all? do | want |
22
+ case want
23
+ when true # same as a Range of 1..-1
24
+ path = [] unless path.empty?
25
+ when Range
26
+ if want.end == -1
27
+ path = [] if path.length >= want.begin
28
+ else
29
+ path = [] if want.include? path.length
30
+ end
31
+ when String then want == path.pop
32
+ when Symbol then capture[ want ] = path.pop
33
+ when Regexp then want === path.pop
34
+ when Hash
35
+ key, value = want.to_a.first
36
+ case value
37
+ when true
38
+ ( capture[ key ], path = path.reverse, [] ) unless path.empty?
39
+ when Range
40
+ if value.end == -1
41
+ ( capture[ key ], path = path.reverse, [] ) if path.length >= value.begin
42
+ else
43
+ ( capture[ key ], path = path.reverse, [] ) if value.include? path.length
44
+ end
45
+ when String, Symbol
46
+ got = path.pop
47
+ capture[ key ] = got ? got : value.to_s
48
+ when Regexp then
49
+ got = path.pop
50
+ capture[ key ] = got if value === got
51
+ end
52
+ end
53
+ end
54
+ capture if match && path.empty?
55
+ elsif @pattern == true or @pattern == false or @pattern == nil
56
+ {}
57
+ end
58
+ end
59
+
60
+ # private
61
+
62
+ # just a little helper method
63
+ def extract_path( request )
64
+ request.traits.waves.path ||= request.path.chomp(request.ext).scan(/[^\/]+/).map { |e| Rack::Utils.unescape(e) }
65
+ end
66
+
67
+ end
68
+
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,43 @@
1
+ module Waves
2
+
3
+ module Matchers
4
+
5
+ # Query parameter matching.
6
+ #
7
+ class Query
8
+
9
+ # Create query matcher or fail.
10
+ #
11
+ # @todo Should map Symbols to Strings here. --rue
12
+ #
13
+ def initialize(pattern)
14
+ raise ArgumentError, "No Query constraints!" unless pattern
15
+ @pattern = pattern
16
+ end
17
+
18
+ # Match query parameters.
19
+ #
20
+ def call(request)
21
+ @pattern.all? {|key, val|
22
+ # @todo Is this right? I do not see how even a
23
+ # Proc would be useful just given nil from
24
+ # a nonexisting key. We just fail in those
25
+ # cases for now. --rue
26
+ if given = request.query[key.to_s]
27
+ val == true or val === given or (val.call(given) rescue false)
28
+ end
29
+ }
30
+ end
31
+
32
+ # Proc-like interface
33
+ #
34
+ def [](request)
35
+ call request
36
+ end
37
+
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,86 @@
1
+ module Waves
2
+ module Matchers
3
+
4
+ class Request
5
+
6
+ attr_accessor :constraints
7
+
8
+
9
+ #
10
+ # @todo Further optimise the cases where there are no
11
+ # constraints. --rue
12
+ #
13
+
14
+ def initialize(options)
15
+
16
+ @uri = Matchers::URI.new( options )
17
+
18
+ @constraints = {}
19
+
20
+ if options[ :requested ]
21
+ @constraints[ :requested ] = Matchers::Requested.new( options[ :requested ] )
22
+ end
23
+
24
+ if options.key?( :accept ) || options.key?( :lang ) || options.key?( :charset )
25
+ @constraints[:accept] = Matchers::Accept.new( options )
26
+ end
27
+
28
+ if options.key?( :ext )
29
+ @constraints[ :ext ] = Matchers::Ext.new( options[ :ext ] )
30
+ elsif options.key?( :extension )
31
+ @constraints[ :ext ] = Matchers::Ext.new( options[ :extension ] )
32
+ end
33
+
34
+ if options.key?( :query )
35
+ @constraints[:query] = Matchers::Query.new( options[:query] )
36
+ end
37
+
38
+ if options[ :traits ]
39
+ @constraints[ :traits ] = Matchers::Traits.new( options[ :traits ] )
40
+ end
41
+
42
+ if options[ :when ]
43
+ @constraints[ :when ] = options[ :when ]
44
+ end
45
+
46
+ end
47
+
48
+ # Process all matchers for request.
49
+ #
50
+ def call(request)
51
+ if captured = @uri.call(request) and test(request)
52
+ request.traits.waves.captured = captured
53
+ end
54
+ end
55
+
56
+ #
57
+ # @todo This could maybe be optimised by detecting
58
+ # empty constraints before calling. Not high
59
+ # importance. --rue
60
+ #
61
+ def test(request)
62
+ constraints.all? {|key, val|
63
+ if val.nil? or val == true
64
+ true
65
+ else
66
+ if val.respond_to? :call
67
+ val.call( request )
68
+ else
69
+ val == request.send( key ) or val === request.send( key ) or request.send( key ) === val
70
+ end
71
+ end
72
+ }
73
+ end
74
+
75
+
76
+ # Proc-like interface
77
+ #
78
+ def [](request)
79
+ call request
80
+ end
81
+
82
+ end
83
+
84
+ end
85
+
86
+ end