rack-app 5.10.2 → 5.11.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.
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