rack-app 5.10.2 → 5.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 983dee78b91bcdb205f8897297ef37cd2ebf7e3a
4
- data.tar.gz: b25e20aaaac647a04b6559ca59b9d4970f4a7670
3
+ metadata.gz: 070c847ed07e3494ed51e88ebf084c6eb661a30f
4
+ data.tar.gz: 4b836fb0a7d4c38491b4d2774b85289d528c1c56
5
5
  SHA512:
6
- metadata.gz: 05075fc0dc6dad4d1f01f9c3a5b3683c4dd490733db795bffc4b34e192de70b694f0d7faa9c62f30ed097559a5a3e9089a1f4aec365a1a21a1edf3e8075dc644
7
- data.tar.gz: 4f5d3a2ccff652a98c2a0369eee4ea7d823ddfc994c7984439620c9563857688ba616a31bedb1ccc658ab859d313cb56e505c114963532a9b8823a3eed63c089
6
+ metadata.gz: 566de7abd739139b8ac5980bcad9079995892d36c1f7213cadedd2451d62ce6ee7f41a2c23327c7174e0576b20ab1b7d71cbfab0b01a79424bb77ae63e35ade3
7
+ data.tar.gz: 91ac3f3615722f31dc483aa4cd4845902d8270655cb1a2e96c2fa72d78add151692ed0cc5a6304c6a5ad6f10a06d40e66c589ceb604b188b944d0fda8d18fb38
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --color
2
+ --fail-fast
2
3
  --require spec_helper
data/VERSION CHANGED
@@ -1 +1 @@
1
- 5.10.2
1
+ 5.11.0
@@ -51,8 +51,9 @@ module Rack::App::Constants
51
51
  PAYLOAD_GETTER = 'rack-app.payload.getter'.freeze
52
52
  PARSED_PAYLOAD = 'rack-app.payload.parsed'.freeze
53
53
 
54
+ PATH_PARAMS = 'rack-app.path_params'.freeze
54
55
  ORIGINAL_PATH_INFO = 'rack-app.original_path_info'.freeze
55
- PATH_PARAMS_MATCHER = 'rack-app.path_params_matcher'.freeze
56
+ FORMATTED_PATH_INFO = 'rack-app.formatted_path_info'.freeze
56
57
  METHODOVERRIDE_ORIGINAL_METHOD = 'rack-app.methodoverride.original_method'.freeze
57
58
 
58
59
  end
@@ -2,7 +2,7 @@ class Rack::App::Endpoint
2
2
 
3
3
  require "forwardable"
4
4
  extend Forwardable
5
- def_delegators :@config, :request_method, :request_path, :description
5
+ def_delegators :@config, :request_methods, :request_path, :description
6
6
 
7
7
  require "rack/app/endpoint/config"
8
8
  require "rack/app/endpoint/builder"
@@ -26,10 +26,6 @@ class Rack::App::Endpoint
26
26
  to_app.call(env)
27
27
  end
28
28
 
29
- def supported_extnames
30
- @config.serializer.extnames
31
- end
32
-
33
29
  def to_app
34
30
  @config.application || self.class::Builder.new(@config).build
35
31
  end
@@ -1,11 +1,12 @@
1
+ # frozen_string_literal: true
1
2
  class Rack::App::Endpoint::Config
2
-
3
3
  def to_hash
4
4
  error_handler
5
5
  middleware_builders_blocks
6
6
  request_path
7
- request_method
8
- return @raw
7
+ request_methods
8
+ defined_request_path
9
+ @raw
9
10
  end
10
11
 
11
12
  def payload_builder
@@ -48,16 +49,29 @@ class Rack::App::Endpoint::Config
48
49
  @raw[:method_name] ||= register_method_to_app_class
49
50
  end
50
51
 
51
- def request_method
52
- (@raw[:request_method] || raise('missing request_method!')).to_s.upcase
52
+ def request_methods
53
+ case @raw[:request_methods] || raise('missing config: request_methods')
54
+ when Rack::App::Constants::HTTP::METHOD::ANY
55
+ Rack::App::Constants::HTTP::METHODS
56
+ when ::Array
57
+ @raw[:request_methods].map(&:to_sym)
58
+ else
59
+ [@raw[:request_methods]].flatten.map(&:to_sym)
60
+ end
53
61
  end
54
62
 
55
63
  def request_path
56
64
  Rack::App::Utils.normalize_path(@raw[:request_path] || raise('missing request_path!'))
57
65
  end
58
66
 
67
+ def defined_request_path
68
+ Rack::App::Utils.normalize_path(@raw[:defined_request_path] ||= request_path)
69
+ end
70
+
59
71
  def description
60
- @raw[:route][:description] || @raw[:route][:desc] rescue nil
72
+ @raw[:route][:description] || @raw[:route][:desc]
73
+ rescue
74
+ nil
61
75
  end
62
76
 
63
77
  protected
@@ -69,11 +83,10 @@ class Rack::App::Endpoint::Config
69
83
  def register_method_to_app_class
70
84
  method_name = '__' + ::Rack::App::Utils.uuid
71
85
  app_class.__send__(:define_method, method_name, &logic_block)
72
- return method_name
86
+ method_name
73
87
  end
74
88
 
75
89
  def logic_block
76
90
  @raw[:user_defined_logic]
77
91
  end
78
-
79
92
  end
@@ -3,8 +3,6 @@ require "rack/request"
3
3
  require "rack/response"
4
4
  class Rack::App::Middlewares::Configuration
5
5
 
6
- require "rack/app/middlewares/configuration/path_params_matcher"
7
-
8
6
  def initialize(app, config)
9
7
  @app = app || raise
10
8
  @serializer = config.serializer || raise
@@ -13,12 +11,12 @@ class Rack::App::Middlewares::Configuration
13
11
  end
14
12
 
15
13
  def call(env)
16
- env[Rack::App::Constants::ENV::REQUEST_HANDLER]= handler(env)
14
+ env[::Rack::App::Constants::ENV::REQUEST_HANDLER]= handler(env)
17
15
  env[::Rack::App::Constants::ENV::SERIALIZER]= @serializer
18
16
  env[::Rack::App::Constants::ENV::PAYLOAD_PARSER]= @payload_parser
19
17
  env[::Rack::App::Constants::ENV::PAYLOAD_GETTER]= lambda do
20
- env[::Rack::App::Constants::ENV::PARSED_PAYLOAD] ||= env[::Rack::App::Constants::ENV::PAYLOAD_PARSER].parse_env(env)
21
- end
18
+ env[::Rack::App::Constants::ENV::PARSED_PAYLOAD] ||= env[::Rack::App::Constants::ENV::PAYLOAD_PARSER].parse_env(env)
19
+ end
22
20
  @app.call(env)
23
21
  end
24
22
 
@@ -38,32 +38,7 @@ class Rack::App::Params
38
38
  end
39
39
 
40
40
  def request_path_params
41
- path_params = {}
42
- path_params.merge!(extract_path_params) unless path_params_matcher.empty?
43
- path_params
44
- end
45
-
46
- def extract_path_params
47
- last_index = request_path_parts.length - 1
48
- request_path_parts.each.with_index.reduce({}) do |params_col, (path_part, index)|
49
- if path_params_matcher[index]
50
- if index == last_index && @env[::Rack::App::Constants::ENV::EXTNAME]
51
- matcher = Regexp.escape(@env[::Rack::App::Constants::ENV::EXTNAME])
52
- path_part = path_part.sub(/#{matcher}$/,'')
53
- end
54
- params_col[path_params_matcher[index]]= path_part
55
- end
56
-
57
- params_col
58
- end
59
- end
60
-
61
- def request_path_parts
62
- @env[::Rack::App::Constants::ENV::PATH_INFO].split('/')
63
- end
64
-
65
- def path_params_matcher
66
- @env[::Rack::App::Constants::ENV::PATH_PARAMS_MATCHER] || {}
41
+ @env[::Rack::App::Constants::ENV::PATH_PARAMS]
67
42
  end
68
43
 
69
44
  end
@@ -1,42 +1,35 @@
1
1
  class Rack::App::Router
2
2
 
3
- require 'rack/app/router/base'
4
- require 'rack/app/router/static'
5
- require 'rack/app/router/dynamic'
6
- require 'rack/app/router/not_found'
3
+ require 'rack/app/router/tree'
4
+
5
+ NOT_FOUND_APP = lambda do |env|
6
+ rack_response = Rack::Response.new
7
+ rack_response.status = 404
8
+ rack_response.write('404 Not Found')
9
+ rack_response.finish
10
+ end
7
11
 
8
12
  def call(env)
9
- Rack::App::RequestConfigurator.configure(env)
10
- @static.call(env) or @dynamic.call(env) or @not_found.call(env)
13
+ @tree.call(env) || NOT_FOUND_APP.call(env)
11
14
  end
12
15
 
13
16
  def endpoints
14
- [@static, @dynamic, @not_found].map(&:endpoints).reduce([], :+)
17
+ @endpoints ||= []
15
18
  end
16
19
 
17
- def show_endpoints
18
-
19
- endpoints = self.endpoints
20
-
21
- wd0 = endpoints.map { |endpoint| endpoint.request_method.to_s.length }.max
22
- wd1 = endpoints.map { |endpoint| endpoint.request_path.to_s.length }.max
23
- wd2 = endpoints.map { |endpoint| endpoint.description.to_s.length }.max
24
-
25
- return endpoints.sort_by { |endpoint| [endpoint.request_method, endpoint.request_path] }.map do |endpoint|
26
- [
27
- endpoint.request_method.to_s.ljust(wd0),
28
- endpoint.request_path.to_s.ljust(wd1),
29
- endpoint.description.to_s.ljust(wd2)
30
- ].join(' ')
31
- end
32
-
20
+ def register_endpoint!(endpoint)
21
+ endpoints.push(endpoint)
22
+ compile_endpoint!(endpoint)
23
+ return endpoint
33
24
  end
34
25
 
35
- def register_endpoint!(endpoint)
36
- router = router_for(endpoint.request_path)
37
- router.register_endpoint!(endpoint)
26
+ # add ! to method name
27
+ def reset
28
+ @tree = Rack::App::Router::Tree.new
29
+ compile_registered_endpoints!
38
30
  end
39
31
 
32
+ # rename to merge!
40
33
  def merge_router!(router, prop={})
41
34
  raise(ArgumentError, 'invalid router object, must implement :endpoints interface') unless router.respond_to?(:endpoints)
42
35
  router.endpoints.each do |endpoint|
@@ -47,27 +40,40 @@ class Rack::App::Router
47
40
  nil
48
41
  end
49
42
 
50
- def reset
51
- [@static, @dynamic].each(&:reset)
43
+
44
+ def show_endpoints
45
+
46
+ endpoints = self.endpoints
47
+
48
+ wd0 = endpoints.map { |endpoint| endpoint.request_methods.first.to_s.length }.max
49
+ wd1 = endpoints.map { |endpoint| endpoint.request_path.to_s.length }.max
50
+ wd2 = endpoints.map { |endpoint| endpoint.description.to_s.length }.max
51
+
52
+ return endpoints.sort_by { |endpoint| [endpoint.request_methods.first, endpoint.request_path] }.map do |endpoint|
53
+ [
54
+ endpoint.request_methods.first.to_s.ljust(wd0),
55
+ endpoint.request_path.to_s.ljust(wd1),
56
+ endpoint.description.to_s.ljust(wd2)
57
+ ].join(' ')
58
+ end
59
+
52
60
  end
53
61
 
54
62
  protected
55
63
 
64
+
56
65
  def initialize
57
- @static = Rack::App::Router::Static.new
58
- @dynamic = Rack::App::Router::Dynamic.new
59
- @not_found = Rack::App::Router::NotFound.new
66
+ reset
60
67
  end
61
68
 
62
- def router_for(request_path)
63
- defined_path_is_dynamic?(request_path) ? @dynamic : @static
69
+ def compile_registered_endpoints!
70
+ endpoints.each do |endpoint|
71
+ compile_endpoint!(endpoint)
72
+ end
64
73
  end
65
74
 
66
- def defined_path_is_dynamic?(path_str)
67
- path_str = Rack::App::Utils.normalize_path(path_str)
68
- !!(path_str.to_s =~ /\/:\w+/i) or
69
- !!(path_str.to_s =~ /\/\*/i) or
70
- path_str.include?(Rack::App::Constants::RACK_BASED_APPLICATION)
75
+ def compile_endpoint!(endpoint)
76
+ @tree.add(endpoint)
71
77
  end
72
78
 
73
79
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ class Rack::App::Router::Tree
3
+
4
+ require 'rack/app/router/tree/env'
5
+ require 'rack/app/router/tree/node'
6
+ require 'rack/app/router/tree/leaf'
7
+ require 'rack/app/router/tree/mounted'
8
+
9
+ def initialize
10
+ @root = self.class::Node.new
11
+ end
12
+
13
+ def add(endpoint)
14
+ @root.set(endpoint, *path_info_parts_by(endpoint.request_path))
15
+ end
16
+
17
+ def call(env)
18
+ self.class::Env.configure(env)
19
+ @root.get(env, *path_info_parts_by(env[Rack::App::Constants::ENV::FORMATTED_PATH_INFO]))
20
+ end
21
+
22
+ protected
23
+
24
+ def request_methods(endpoint)
25
+ case endpoint.request_method
26
+ when ::Rack::App::Constants::HTTP::METHOD::ANY
27
+ ::Rack::App::Constants::HTTP::METHODS.each{|m| yield(m) }
28
+ else
29
+ yield(endpoint.request_method)
30
+ end
31
+ end
32
+
33
+ def cluster(request_method)
34
+ @methods[request_method.to_s.upcase] ||= Rack::App::Router::Tree::Node.new
35
+ end
36
+
37
+ def path_info_parts_by(path_info)
38
+ Rack::App::Utils.split_path_info(Rack::App::Utils.normalize_path(path_info))
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ module Rack::App::Router::Tree::Env
3
+ extend(self)
4
+
5
+ E = Rack::App::Constants::ENV
6
+
7
+ def configure(env)
8
+ env[E::PATH_PARAMS] ||= {}
9
+ env[E::FORMATTED_PATH_INFO] = Rack::App::Utils.normalize_path(env[E::PATH_INFO])
10
+ env[E::EXTNAME] = File.extname(Rack::App::Utils.split_path_info(env[E::FORMATTED_PATH_INFO]).last)
11
+ env[E::FORMATTED_PATH_INFO].slice!(/#{Regexp.escape(env[E::EXTNAME])}$/)
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ class Rack::App::Router::Tree::Leaf < ::Hash
3
+ require 'rack/app/router/tree/leaf/vein'
4
+
5
+ def get(env)
6
+ vein = vein_by(env)
7
+ vein && vein.get(env)
8
+ end
9
+
10
+ def set(endpoint)
11
+ endpoint.request_methods.each do |request_method|
12
+ vein_for(request_method).set(endpoint)
13
+ end
14
+ end
15
+
16
+ def struct
17
+ hash = {}
18
+ self.each do |request_method, vein|
19
+ hash[request_method] = vein.struct
20
+ end
21
+ hash
22
+ end
23
+
24
+ protected
25
+
26
+ def vein_by(env)
27
+ self[env[Rack::App::Constants::ENV::REQUEST_METHOD].to_sym]
28
+ end
29
+
30
+ def vein_for(request_method)
31
+ self[request_method] ||= Rack::App::Router::Tree::Leaf::Vein.new
32
+ end
33
+
34
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ class Rack::App::Router::Tree::Leaf::Vein < ::Hash
3
+
4
+ E = Rack::App::Constants::ENV
5
+
6
+ def set(endpoint)
7
+ app = endpoint.to_app
8
+ endpoint.config.serializer.extnames.each do |extname|
9
+ self[extname] = app
10
+ end
11
+ self[extname_for(endpoint)] = app
12
+ end
13
+
14
+ def get(env)
15
+ app = self[env[E::EXTNAME]]
16
+ app && app.call(env)
17
+ end
18
+
19
+ def struct
20
+ self.reduce({}) do |hash, (extname, app)|
21
+ hash[extname]= :app
22
+ hash
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def extname_for(endpoint)
29
+ File.extname(Rack::App::Utils.split_path_info(endpoint.request_path).last)
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ class Rack::App::Router::Tree::Mounted
3
+
4
+ require 'rack/app/router/tree/mounted/application'
5
+
6
+ def get(env)
7
+ @app.call(env)
8
+ end
9
+
10
+ protected
11
+
12
+ def initialize(endpoint)
13
+ @app = endpoint.to_app
14
+ end
15
+
16
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ class Rack::App::Router::Tree::Mounted::Application < Rack::App::Router::Tree::Mounted
3
+ protected
4
+
5
+ def initialize(endpoint)
6
+ @app = build(endpoint)
7
+ end
8
+
9
+ def build(endpoint)
10
+ builder = Rack::Builder.new
11
+ builder.use(Rack::App::Middlewares::PathInfoCutter, mount_path(endpoint))
12
+ builder.run(endpoint.to_app)
13
+ builder.to_app
14
+ end
15
+
16
+ def mount_path(endpoint)
17
+ mount_path_parts = (endpoint.request_path.split('/') - [Rack::App::Constants::RACK_BASED_APPLICATION, ''])
18
+ mount_path_parts.empty? ? '' : Rack::App::Utils.join(mount_path_parts)
19
+ end
20
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+ class Rack::App::Router::Tree::Node < ::Hash
3
+
4
+ PARAM = :PARAM
5
+
6
+ def set(endpoint, current=nil, *path_info_parts)
7
+ case type(current)
8
+ when :NODE
9
+ node_for(save_key(current)).set(endpoint, *path_info_parts)
10
+ when :LEAF
11
+ save_to_leaf(endpoint)
12
+ when :RACK_BASED_APPLICATION
13
+ attach(:RACK_BASED_APPLICATION, Rack::App::Router::Tree::Mounted::Application.new(endpoint))
14
+ when :MOUNTED
15
+ attach(:MOUNT, Rack::App::Router::Tree::Mounted.new(endpoint))
16
+ else
17
+ raise('UNKNOWN')
18
+ end
19
+ end
20
+
21
+ def get(env, current=nil, *path_info_parts)
22
+ return app_for(env) if current.nil?
23
+ node = next_node(env, current)
24
+ (node && node.get(env, *path_info_parts)) || mounted(env)
25
+ end
26
+
27
+ def struct
28
+ self.reduce({}) do |hash, (k,v)|
29
+ if k == PARAM
30
+ hash[k] = v
31
+ else
32
+ hash[k] = v.struct
33
+ end
34
+ hash
35
+ end
36
+ end
37
+
38
+ protected
39
+
40
+ def app_for(env)
41
+ app = self[:LEAF] || mounted_app
42
+ app && app.get(env)
43
+ end
44
+
45
+ def mounted(env)
46
+ mounted_app && mounted_app.get(env)
47
+ end
48
+
49
+ def mounted_app
50
+ self[:MOUNT] || self[:RACK_BASED_APPLICATION]
51
+ end
52
+
53
+ def attach(key, app_or_endpoint)
54
+ self[key.to_sym] = app_or_endpoint
55
+ end
56
+
57
+ def save_to_leaf(endpoint)
58
+ self[:LEAF] ||= Rack::App::Router::Tree::Leaf.new
59
+ self[:LEAF].set(endpoint)
60
+ end
61
+
62
+ def next_node(env, current)
63
+ self[current] || any_path(env, current)
64
+ end
65
+
66
+ def any_path(env, current)
67
+ if self[:ANY]
68
+ env[Rack::App::Constants::ENV::PATH_PARAMS][self[PARAM]] = current
69
+ self[:ANY]
70
+ end
71
+ end
72
+
73
+ def node_for(key)
74
+ self[key] ||= self.class.new
75
+ end
76
+
77
+ def type(current)
78
+ case current
79
+ when Rack::App::Constants::RACK_BASED_APPLICATION
80
+ :RACK_BASED_APPLICATION
81
+ when '**','*'
82
+ :MOUNTED
83
+ when NilClass
84
+ :LEAF
85
+ else
86
+ :NODE
87
+ end
88
+ end
89
+
90
+ def save_key(path)
91
+ case path
92
+ when /^:\w+$/i
93
+ self[PARAM] = path.sub(/^:/, '')
94
+ :ANY
95
+ else
96
+ extnameless(path)
97
+ end
98
+ end
99
+
100
+ def extnameless(path)
101
+ path.sub(/\.\w+$/,'')
102
+ end
103
+
104
+ end
@@ -54,7 +54,10 @@ module Rack::App::SingletonMethods::Mounting
54
54
 
55
55
  endpoint = Rack::App::Endpoint.new(
56
56
  route_registration_properties.merge(
57
- :request_method => 'GET',
57
+ :request_methods => [
58
+ Rack::App::Constants::HTTP::METHOD::GET,
59
+ Rack::App::Constants::HTTP::METHOD::OPTIONS
60
+ ],
58
61
  :request_path => request_path,
59
62
  :application => file_server
60
63
  )
@@ -66,34 +69,17 @@ module Rack::App::SingletonMethods::Mounting
66
69
  end
67
70
 
68
71
  def mount_rack_interface_compatible_application(rack_based_app, options={})
69
- router.register_endpoint!(
70
- Rack::App::Endpoint.new(
71
- route_registration_properties.merge(
72
- :request_method => ::Rack::App::Constants::HTTP::METHOD::ANY,
72
+ properties = route_registration_properties.merge(
73
+ :request_methods => ::Rack::App::Constants::HTTP::METHOD::ANY,
73
74
  :request_path => Rack::App::Utils.join(
74
75
  @namespaces,
75
76
  options[:to],
76
77
  ::Rack::App::Constants::RACK_BASED_APPLICATION
77
78
  ),
78
79
  :application => rack_based_app
79
- )
80
- )
81
80
  )
82
- end
83
-
84
- alias mount_rack_based_application mount_rack_interface_compatible_application
85
- Rack::App::Utils.deprecate(self,:mount_rack_based_application, "mount or mount_rack_interface_compatible_application", 2016,9)
86
- alias mount_app mount_rack_interface_compatible_application
87
- Rack::App::Utils.deprecate(self,:mount_app, "mount or mount_rack_interface_compatible_application", 2016,9)
88
81
 
89
- protected
90
-
91
- def on_mounted(&block)
92
- @on_mounted ||= []
93
- @on_mounted << block unless block.nil?
94
- @on_mounted
82
+ router.register_endpoint!(Rack::App::Endpoint.new(properties))
95
83
  end
96
84
 
97
- alias while_being_mounted on_mounted
98
-
99
85
  end
@@ -28,7 +28,7 @@ module Rack::App::SingletonMethods::RouteHandling
28
28
  :payload => payload,
29
29
  :error_handler => error,
30
30
  :user_defined_logic => block,
31
- :request_method => request_method,
31
+ :request_methods => [request_method],
32
32
  :route => route_registration_properties.dup,
33
33
  :middleware_builders_blocks => next_endpoint_middlewares.dup,
34
34
  :request_path => ::Rack::App::Utils.join(@namespaces, request_path)
@@ -22,6 +22,10 @@ module Rack::App::Utils
22
22
  path
23
23
  end
24
24
 
25
+ def split_path_info(path_info)
26
+ path_info.to_s.split('/').tap { |a| a.empty? && a.push('') }
27
+ end
28
+
25
29
  def snake_case(camel_cased_word)
26
30
  word = camel_cased_word.to_s.gsub('::', '/')
27
31
  word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
data/spike/tree.rb ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ rack_api_lib_folder = File.join(File.dirname(__FILE__), '..', 'lib')
5
+ $LOAD_PATH.unshift(rack_api_lib_folder)
6
+ require 'rack/app'
7
+ require 'pp'
8
+
9
+ tree = Rack::App::Router::Tree.new
10
+
11
+ APP = Class.new(Rack::App)
12
+
13
+ payload = Rack::App::Payload::Builder.new
14
+
15
+ payload.parser_builder do
16
+ accept :json
17
+ end
18
+
19
+ serializer = Rack::App::Serializer::FormatsBuilder.new
20
+ serializer.instance_exec do
21
+ on '.json', 'application/json' do |obj|
22
+ JSON.dump(obj)
23
+ end
24
+ end
25
+
26
+ endpoint = Rack::App::Endpoint.new(
27
+ :route => {},
28
+ :app_class => APP,
29
+ :request_methods => ["GET"],
30
+ :request_path => '/hello/world/:id',
31
+ :middleware_builders_blocks => [],
32
+ :user_defined_logic => proc{ "hy" },
33
+ :serializer_builder => serializer,
34
+ :payload => payload,
35
+ :error_handler => Rack::App::ErrorHandler.new,
36
+ )
37
+
38
+
39
+ properties = {
40
+ :request_methods => ::Rack::App::Constants::HTTP::METHODS,
41
+ :request_path => Rack::App::Utils.join("hello", ::Rack::App::Constants::RACK_BASED_APPLICATION),
42
+ :application => proc{|env| Rack::Response.new.finish }
43
+ }
44
+
45
+ tree.add(Rack::App::Endpoint.new(properties))
46
+
47
+ endpoint = Rack::App::Endpoint.new(
48
+ :route => {},
49
+ :app_class => APP,
50
+ :request_methods => ["GET"],
51
+ :request_path => '/hello/world/:id',
52
+ :middleware_builders_blocks => [],
53
+ :user_defined_logic => proc{ "hy" },
54
+ :serializer_builder => serializer,
55
+ :payload => payload,
56
+ :error_handler => Rack::App::ErrorHandler.new,
57
+ )
58
+
59
+ tree.add(endpoint)
60
+ pp tree
61
+
62
+ require 'rack'
63
+ puts
64
+
65
+ [
66
+ "/hello/world/123.json",
67
+ "/hello/world/123",
68
+ "/hello/world",
69
+ ].each do |path_info|
70
+ env = Rack::MockRequest.env_for(path_info, :method => 'GET')
71
+ resp = tree.call(env)
72
+
73
+ puts(path_info)
74
+ p resp.is_a?(Array) && resp.length == 3
75
+ puts
76
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-app
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.10.2
4
+ version: 5.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Luzsi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-24 00:00:00.000000000 Z
11
+ date: 2017-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -124,7 +124,6 @@ files:
124
124
  - lib/rack/app/logger.rb
125
125
  - lib/rack/app/middlewares.rb
126
126
  - lib/rack/app/middlewares/configuration.rb
127
- - lib/rack/app/middlewares/configuration/path_params_matcher.rb
128
127
  - lib/rack/app/middlewares/header_setter.rb
129
128
  - lib/rack/app/middlewares/hooks.rb
130
129
  - lib/rack/app/middlewares/hooks/after.rb
@@ -149,10 +148,13 @@ files:
149
148
  - lib/rack/app/request_stream.rb
150
149
  - lib/rack/app/router.rb
151
150
  - lib/rack/app/router/base.rb
152
- - lib/rack/app/router/dynamic.rb
153
- - lib/rack/app/router/dynamic/request_path_part_placeholder.rb
154
- - lib/rack/app/router/not_found.rb
155
- - lib/rack/app/router/static.rb
151
+ - lib/rack/app/router/tree.rb
152
+ - lib/rack/app/router/tree/env.rb
153
+ - lib/rack/app/router/tree/leaf.rb
154
+ - lib/rack/app/router/tree/leaf/vein.rb
155
+ - lib/rack/app/router/tree/mounted.rb
156
+ - lib/rack/app/router/tree/mounted/application.rb
157
+ - lib/rack/app/router/tree/node.rb
156
158
  - lib/rack/app/serializer.rb
157
159
  - lib/rack/app/serializer/formats_builder.rb
158
160
  - lib/rack/app/singleton_methods.rb
@@ -197,6 +199,7 @@ files:
197
199
  - spike/method_vs_instance_exec.rb
198
200
  - spike/return_vs_throw.rb
199
201
  - spike/slice_vs_sub.rb
202
+ - spike/tree.rb
200
203
  - spike/validator_with_minitest.rb
201
204
  - spike/xml.rb
202
205
  - src/Net__HTTP Cheat Sheet.pdf
@@ -1,14 +0,0 @@
1
- class Rack::App::Middlewares::Configuration::PathParamsMatcher
2
-
3
- def initialize(app, path_params)
4
- @path_params = path_params
5
- @app = app
6
- end
7
-
8
- def call(env)
9
- env[::Rack::App::Constants::ENV::PATH_PARAMS_MATCHER]= @path_params.dup
10
-
11
- @app.call(env)
12
- end
13
-
14
- end
@@ -1,170 +0,0 @@
1
- class Rack::App::Router::Dynamic < Rack::App::Router::Base
2
-
3
- require 'rack/app/router/dynamic/request_path_part_placeholder'
4
- DYNAMIC_REQUEST_PATH_PART = RequestPathPartPlaceholder.new('DYNAMIC_REQUEST_PATH_PART')
5
- MOUNTED_DIRECTORY = RequestPathPartPlaceholder.new('MOUNTED_DIRECTORY')
6
- MOUNTED_APPLICATION = RequestPathPartPlaceholder.new('MOUNTED_APPLICATION')
7
-
8
- protected
9
-
10
- def initialize
11
- @http_method_cluster = {}
12
- end
13
-
14
- def path_part_is_dynamic?(path_part_str)
15
- !!(path_part_str.to_s =~ /^:\w+$/i)
16
- end
17
-
18
- def deep_merge!(hash, other_hash)
19
- Rack::App::Utils.deep_merge(hash, other_hash)
20
- end
21
-
22
- def main_cluster(request_method)
23
- (@http_method_cluster[request_method.to_s.upcase] ||= {})
24
- end
25
-
26
- def path_part_is_a_mounted_directory?(path_part)
27
- path_part == Rack::App::Constants::MOUNTED_DIRECTORY
28
-
29
- (path_part == '**' or path_part == '*')
30
- end
31
-
32
- def path_part_is_a_mounted_rack_based_application?(path_part)
33
- path_part == Rack::App::Constants::RACK_BASED_APPLICATION
34
- end
35
-
36
- def clean_routes!
37
- @http_method_cluster.clear
38
- end
39
-
40
- def compile_registered_endpoints!
41
- endpoints.each do |endpoint|
42
- compile_endpoint!(endpoint)
43
- end
44
- end
45
-
46
- def compile_endpoint!(endpoint)
47
- request_method = endpoint.request_method
48
- request_path = endpoint.request_path
49
-
50
- clusters_for(request_method) do |current_cluster|
51
-
52
- break_build = false
53
- path_params = {}
54
- options = {}
55
-
56
- builder = Rack::Builder.new
57
- builder.use(Rack::App::Middlewares::Configuration::PathParamsMatcher, path_params)
58
-
59
- request_path_parts = request_path.split('/')
60
- request_path_parts.each.with_index do |path_part, index|
61
-
62
- new_cluster_name = if path_part_is_dynamic?(path_part)
63
- path_params[index]= path_part.sub(/^:/, '')
64
- DYNAMIC_REQUEST_PATH_PART
65
-
66
- elsif path_part_is_a_mounted_directory?(path_part)
67
- break_build = true
68
- MOUNTED_DIRECTORY
69
-
70
- elsif path_part_is_a_mounted_rack_based_application?(path_part)
71
- break_build = true
72
- builder.use(
73
- Rack::App::Middlewares::PathInfoCutter,
74
- calculate_mount_path(request_path_parts)
75
- )
76
- MOUNTED_APPLICATION
77
-
78
- else
79
- path_part
80
- end
81
-
82
- current_cluster = (current_cluster[new_cluster_name] ||= {})
83
- break if break_build
84
-
85
- end
86
-
87
-
88
- builder.run(as_app(endpoint))
89
- current_cluster[:app]= builder.to_app
90
-
91
- current_cluster[:endpoint]= endpoint
92
- if current_cluster[:endpoint].respond_to?(:register_path_params_matcher)
93
- current_cluster[:endpoint].register_path_params_matcher(path_params)
94
- end
95
-
96
- current_cluster[:options]= options
97
- end
98
- end
99
-
100
- def calculate_mount_path(request_path_parts)
101
- mount_path_parts = (request_path_parts - [Rack::App::Constants::RACK_BASED_APPLICATION, ''])
102
- mount_path_parts.empty? ? '' : Rack::App::Utils.join(mount_path_parts)
103
- end
104
-
105
- def clusters_for(request_method)
106
- if ::Rack::App::Constants::HTTP::METHOD::ANY == request_method
107
- supported_http_protocols.each do |cluster_type|
108
- yield(main_cluster(cluster_type))
109
- end
110
- else
111
- yield(main_cluster(request_method))
112
- end
113
- end
114
-
115
- def supported_http_protocols
116
- ::Rack::App::Constants::HTTP::METHODS
117
- end
118
-
119
- def get_app(env)
120
- find_by_path_infos(env) do |path_info|
121
- fetch_context(get_request_method(env), path_info)
122
- end
123
- end
124
-
125
- def fetch_context(request_method, path_info)
126
- current_cluster = main_cluster(request_method)
127
-
128
- last_mounted_app = nil
129
- last_mounted_directory = nil
130
-
131
- path_parts = path_info.split('/')
132
- path_parts << '' if path_parts.empty?
133
- path_parts.each do |path_part|
134
-
135
- last_mounted_directory = current_cluster[MOUNTED_DIRECTORY] || last_mounted_directory
136
- last_mounted_app = current_cluster[MOUNTED_APPLICATION] || last_mounted_app
137
-
138
- current_cluster = current_cluster[path_part] || current_cluster[DYNAMIC_REQUEST_PATH_PART]
139
-
140
- last_mounted_directory = (current_cluster || {})[MOUNTED_DIRECTORY] || last_mounted_directory
141
- last_mounted_app = (current_cluster || {})[MOUNTED_APPLICATION] || last_mounted_app
142
-
143
- if current_cluster.nil?
144
-
145
-
146
- if last_mounted_directory
147
- current_cluster = last_mounted_directory
148
- break
149
-
150
- elsif last_mounted_app
151
- current_cluster = last_mounted_app
152
- break
153
-
154
- else
155
- return nil
156
-
157
- end
158
- end
159
-
160
- end
161
-
162
- return extract_app(current_cluster) || extract_app(last_mounted_app) || extract_app(last_mounted_directory)
163
-
164
- end
165
-
166
- def extract_app(hash)
167
- hash.is_a?(Hash) ? hash[:app] : nil
168
- end
169
-
170
- end
@@ -1,13 +0,0 @@
1
- class Rack::App::Router::Dynamic::RequestPathPartPlaceholder
2
-
3
- attr_reader :name
4
- def initialize(name)
5
- @name = name
6
- freeze
7
- end
8
-
9
- def ==(oth)
10
- oth.respond_to?(:name) && oth.name == @name
11
- end
12
-
13
- end
@@ -1,19 +0,0 @@
1
- require "rack/response"
2
- class Rack::App::Router::NotFound < Rack::App::Router::Base
3
-
4
- protected
5
-
6
- NOT_FOUND_APP = lambda do |env|
7
- rack_response = Rack::Response.new
8
- rack_response.status = 404
9
- rack_response.write('404 Not Found')
10
- rack_response.finish
11
- end
12
-
13
- def get_app(env)
14
- NOT_FOUND_APP
15
- end
16
-
17
- def compile_registered_endpoints!
18
- end
19
- end
@@ -1,32 +0,0 @@
1
- class Rack::App::Router::Static < Rack::App::Router::Base
2
-
3
- protected
4
-
5
- def get_app(env)
6
- request_method= get_request_method(env)
7
- find_by_path_infos(env) do |request_path|
8
- mapped_endpoint_routes[[request_method, request_path]]
9
- end
10
- end
11
-
12
- def mapped_endpoint_routes
13
- @mapped_endpoint_routes ||= {}
14
- end
15
-
16
- def clean_routes!
17
- mapped_endpoint_routes.clear
18
- end
19
-
20
- def compile_endpoint!(endpoint)
21
- routing_key = [endpoint.request_method, endpoint.request_path]
22
-
23
- mapped_endpoint_routes[routing_key]= as_app(endpoint)
24
- end
25
-
26
- def compile_registered_endpoints!
27
- endpoints.each do |endpoint|
28
- compile_endpoint!(endpoint)
29
- end
30
- end
31
-
32
- end