rack-app 3.0.0.alpha → 3.0.0.beta

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: ebb99d6cbc443f59e0f17c2d1216ed44ae7d8b5f
4
- data.tar.gz: cb9d4c1a858802c241c76fcc4ba48c113b06b5e9
3
+ metadata.gz: 115055ba41521d7fee618f4d2fe57b624471a39e
4
+ data.tar.gz: 40fab600f70f7b89622a40106e94323e19b40d67
5
5
  SHA512:
6
- metadata.gz: 6398d60c1909301924d191e4875e81c945c8ec06cd41b3e2158a87744bd2080e1c0a3c9074a7ff6f3cdb3f7e217eefa60976a432259cb7bdfd6f48cb96d1dcae
7
- data.tar.gz: bb3c51ab85a38d507f685ebe3c7d61f1936f354c0cce2d1009072fa45ca1ee488f583805f75ede3c5c3b22957abeed9a6d6adc42cb73a79ab23185608e51508e
6
+ metadata.gz: b8bb4760e69bc2815fc09664845dd7b172ade97d3723667c48166553bb8c994a534adbf430498e46f9f75f2e39320c6544cdb3238e6042fb1da43aef716181a5
7
+ data.tar.gz: 333055adc24a03df566b36065931f921000901fc3f217204397964f96c1169d7140dbf28fc1b047079ef76486fa3186cac07d972fe8b64cfd2247c659d80de67
@@ -29,6 +29,8 @@ env:
29
29
  global:
30
30
  - VERBOSE=true
31
31
  - TIMEOUT=1
32
+ - BENCHMARK_QUANTITY=100000
33
+ - STREAM_FILE_SIZE=1024
32
34
 
33
35
  branches:
34
36
  only:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.0.0.alpha
1
+ 3.0.0.beta
@@ -1,4 +1,19 @@
1
1
  module Rack::App::Constants
2
- NORMALIZED_PATH_INFO = 'rack-app.path_info'.freeze
2
+
3
+ module HTTP
4
+ ANY = 'ANY'.freeze
5
+ GET='GET'.freeze
6
+ POST = 'POST'.freeze
7
+ PUT = 'PUT'.freeze
8
+ DELETE = 'DELETE'.freeze
9
+ PATCH = 'PATCH'.freeze
10
+ HEAD = 'HEAD'.freeze
11
+ OPTIONS = 'OPTIONS'.freeze
12
+ end
13
+
14
+ ORIGINAL_PATH_INFO = 'rack-app.original_path_info'.freeze
3
15
  PATH_PARAMS_MATCHER = 'rack-app.path_params_matcher'.freeze
16
+ RACK_BASED_APPLICATION = '[Mounted Rack Application]'.freeze
17
+ MOUNTED_DIRECTORY = '[Mounted Directory]'.freeze
18
+
4
19
  end
@@ -50,7 +50,7 @@ class Rack::App::Params
50
50
  end
51
51
 
52
52
  def request_path_parts
53
- @request_env[::Rack::App::Constants::NORMALIZED_PATH_INFO].split('/')
53
+ @request_env[::Rack::PATH_INFO].split('/')
54
54
  end
55
55
 
56
56
  def path_params_matcher
@@ -2,9 +2,17 @@ module Rack::App::RequestConfigurator
2
2
 
3
3
  extend self
4
4
 
5
- def configure(request_env)
6
- request_env[Rack::App::Constants::NORMALIZED_PATH_INFO]= Rack::App::Utils.normalize_path(request_env[::Rack::PATH_INFO])
7
- request_env
5
+ def configure(env)
6
+ path_info(env)
7
+ env
8
+ end
9
+
10
+ protected
11
+
12
+ def path_info(env)
13
+ path_info = env[::Rack::PATH_INFO]
14
+ env[::Rack::App::Constants::ORIGINAL_PATH_INFO]= path_info
15
+ env[::Rack::PATH_INFO]= Rack::App::Utils.normalize_path(path_info)
8
16
  end
9
17
 
10
18
  end
@@ -3,14 +3,24 @@ class Rack::App::Router
3
3
  require 'rack/app/router/base'
4
4
  require 'rack/app/router/static'
5
5
  require 'rack/app/router/dynamic'
6
+ require 'rack/app/router/not_found'
7
+
8
+ def call(env)
9
+ response = nil
10
+ registered_endpoint_routers.find do |router|
11
+ response = router.call(env)
12
+ end
13
+ return response
14
+ end
6
15
 
7
16
  def endpoints
8
- (@static.endpoints + @dynamic.endpoints)
17
+ registered_endpoint_routers.map(&:endpoints).reduce([],:+)
9
18
  end
10
19
 
11
20
  def show_endpoints
12
21
 
13
22
  endpoints = self.endpoints
23
+
14
24
  wd0 = endpoints.map { |endpoint| endpoint[:request_method].to_s.length }.max
15
25
  wd1 = endpoints.map { |endpoint| endpoint[:request_path].to_s.length }.max
16
26
  wd2 = endpoints.map { |endpoint| endpoint[:description].to_s.length }.max
@@ -26,18 +36,11 @@ class Rack::App::Router
26
36
  end
27
37
 
28
38
  def register_endpoint!(request_method, request_path, description, endpoint)
29
- router = defined_path_is_dynamic?(request_path) ? @dynamic : @static
39
+ router = router_for(request_path)
30
40
  router.register_endpoint!(request_method, request_path, description, endpoint)
31
41
  end
32
42
 
33
- def fetch_endpoint(request_method, request_path)
34
- @static.fetch_endpoint(request_method, request_path) or
35
- @dynamic.fetch_endpoint(request_method, request_path) or
36
- Rack::App::Endpoint::NOT_FOUND
37
-
38
- end
39
-
40
- def merge_router!(router,prop={})
43
+ def merge_router!(router, prop={})
41
44
  raise(ArgumentError, 'invalid router object, must implement :endpoints interface') unless router.respond_to?(:endpoints)
42
45
  router.endpoints.each do |endpoint|
43
46
  request_path = ::Rack::App::Utils.join(prop[:namespaces], endpoint[:request_path])
@@ -57,11 +60,22 @@ class Rack::App::Router
57
60
  def initialize
58
61
  @static = Rack::App::Router::Static.new
59
62
  @dynamic = Rack::App::Router::Dynamic.new
63
+ @not_found = Rack::App::Router::NotFound.new
64
+ end
65
+
66
+ def registered_endpoint_routers
67
+ [@static, @dynamic, @not_found]
68
+ end
69
+
70
+ def router_for(request_path)
71
+ defined_path_is_dynamic?(request_path) ? @dynamic : @static
60
72
  end
61
73
 
62
74
  def defined_path_is_dynamic?(path_str)
63
75
  path_str = Rack::App::Utils.normalize_path(path_str)
64
- !!(path_str.to_s =~ /\/:\w+/i) or !!(path_str.to_s =~ /\/\*/i)
76
+ !!(path_str.to_s =~ /\/:\w+/i) or
77
+ !!(path_str.to_s =~ /\/\*/i) or
78
+ path_str.include?(Rack::App::Constants::RACK_BASED_APPLICATION)
65
79
  end
66
80
 
67
81
  end
@@ -1,5 +1,21 @@
1
1
  class Rack::App::Router::Base
2
2
 
3
+ def call(env)
4
+
5
+ request_method= env[Rack::REQUEST_METHOD]
6
+ path_info= env[Rack::PATH_INFO]
7
+
8
+ context = fetch_context(request_method, path_info)
9
+ return unless context.is_a?(Hash) and not context[:endpoint].nil?
10
+
11
+ format_env(context, env)
12
+ context[:endpoint].call(env)
13
+
14
+ end
15
+
16
+ def format_env(context, env)
17
+ end
18
+
3
19
  def endpoints
4
20
  @endpoints ||= []
5
21
  end
@@ -7,7 +23,7 @@ class Rack::App::Router::Base
7
23
  def register_endpoint!(request_method, request_path, description, endpoint)
8
24
  endpoints.push(
9
25
  {
10
- :request_method => request_method.to_s.upcase,
26
+ :request_method => request_method,
11
27
  :request_path => Rack::App::Utils.normalize_path(request_path),
12
28
  :description => description,
13
29
  :endpoint => endpoint
@@ -18,12 +34,12 @@ class Rack::App::Router::Base
18
34
  return endpoint
19
35
  end
20
36
 
21
- def fetch_endpoint(request_method, request_path)
37
+ protected
38
+
39
+ def fetch_context(request_method, request_path)
22
40
  raise('IMPLEMENTATION MISSING ERROR')
23
41
  end
24
42
 
25
- protected
26
-
27
43
  def compile_registered_endpoints!
28
44
  raise('IMPLEMENTATION MISSING ERROR')
29
45
  end
@@ -1,27 +1,9 @@
1
1
  class Rack::App::Router::Dynamic < Rack::App::Router::Base
2
2
 
3
- PATH_PARAM = :"#{self}::PATH_PARAM".freeze
4
- PARTIAL = :"#{self}::PARTIAL".freeze
5
-
6
- def fetch_endpoint(request_method, request_path)
7
- normalized_request_path = Rack::App::Utils.normalize_path(request_path)
8
-
9
- current_cluster = main_cluster(request_method)
10
- normalized_request_path.split('/').each do |path_part|
11
- previous_cluster = current_cluster
12
- current_cluster = current_cluster[path_part] || current_cluster[PATH_PARAM]
13
- if current_cluster.nil?
14
- if previous_cluster[PARTIAL]
15
- current_cluster = previous_cluster[PARTIAL]
16
- break
17
- else
18
- return nil
19
- end
20
- end
21
- end
22
-
23
- current_cluster[:endpoint]
24
- end
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')
25
7
 
26
8
  protected
27
9
 
@@ -34,66 +16,136 @@ class Rack::App::Router::Dynamic < Rack::App::Router::Base
34
16
  end
35
17
 
36
18
  def deep_merge!(hash, other_hash)
37
- other_hash.each_pair do |current_key, other_value|
38
-
39
- this_value = hash[current_key]
40
-
41
- hash[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
42
- deep_merge!(this_value, other_value)
43
- else
44
- other_value
45
- end
46
- end
47
-
48
- hash
19
+ Rack::App::Utils.deep_merge(hash, other_hash)
49
20
  end
50
21
 
51
22
  def main_cluster(request_method)
52
23
  (@http_method_cluster[request_method.to_s.upcase] ||= {})
53
24
  end
54
25
 
55
- def path_part_is_a_partial?(path_part)
26
+ def path_part_is_a_mounted_directory?(path_part)
27
+ path_part == Rack::App::Constants::MOUNTED_DIRECTORY
28
+
56
29
  (path_part == '**' or path_part == '*')
57
30
  end
58
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
+
59
36
  def compile_registered_endpoints!
60
37
  @http_method_cluster.clear
61
38
  endpoints.each do |endpoint_prop|
62
- compile_endpoint(endpoint_prop[:request_method],endpoint_prop[:request_path],endpoint_prop[:endpoint])
39
+ compile_endpoint(endpoint_prop[:request_method], endpoint_prop[:request_path], endpoint_prop[:endpoint])
63
40
  end
64
41
  end
65
42
 
66
43
  def compile_endpoint(request_method, request_path, endpoint)
44
+ clusters_for(request_method) do |current_cluster|
67
45
 
68
- current_cluster = main_cluster(request_method)
69
- path_params = {}
70
- break_build = false
46
+ break_build = false
47
+ path_params = {}
48
+ mount_path = ''
49
+ options = {}
71
50
 
72
- request_path.split('/').each.with_index do |path_part, index|
51
+ request_path_parts = request_path.split('/')
52
+ request_path_parts.each.with_index do |path_part, index|
73
53
 
74
- new_cluster_name = if path_part_is_dynamic?(path_part)
75
- path_params[index]= path_part.sub(/^:/, '')
76
- PATH_PARAM
54
+ new_cluster_name = if path_part_is_dynamic?(path_part)
55
+ path_params[index]= path_part.sub(/^:/, '')
56
+ DYNAMIC_REQUEST_PATH_PART
77
57
 
78
- elsif path_part_is_a_partial?(path_part)
79
- break_build = true
80
- PARTIAL
58
+ elsif path_part_is_a_mounted_directory?(path_part)
59
+ break_build = true
60
+ MOUNTED_DIRECTORY
81
61
 
82
- else
83
- path_part
84
- end
62
+ elsif path_part_is_a_mounted_rack_based_application?(path_part)
63
+ break_build = true
64
+ mount_path = calculate_mount_path(request_path_parts)
65
+ MOUNTED_APPLICATION
85
66
 
86
- current_cluster = (current_cluster[new_cluster_name] ||= {})
87
- break if break_build
67
+ else
68
+ path_part
69
+ end
70
+
71
+ current_cluster = (current_cluster[new_cluster_name] ||= {})
72
+ break if break_build
73
+
74
+ end
75
+
76
+ current_cluster[:endpoint]= endpoint
77
+ if current_cluster[:endpoint].respond_to?(:register_path_params_matcher)
78
+ current_cluster[:endpoint].register_path_params_matcher(path_params)
79
+ end
80
+
81
+ options[:mount_path]= mount_path
82
+ options[:path_params]= path_params
83
+ current_cluster[:options]= options
88
84
 
89
85
  end
86
+ end
87
+
88
+ def calculate_mount_path(request_path_parts)
89
+ mount_path_parts = (request_path_parts - [Rack::App::Constants::RACK_BASED_APPLICATION, ''])
90
+ mount_path_parts.empty? ? '' : Rack::App::Utils.join(mount_path_parts)
91
+ end
90
92
 
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)
93
+ def clusters_for(request_method)
94
+ if ::Rack::App::Constants::HTTP::ANY == request_method
95
+ supported_http_protocols.each do |cluster_type|
96
+ yield(main_cluster(cluster_type))
97
+ end
98
+ else
99
+ yield(main_cluster(request_method))
94
100
  end
101
+ end
102
+
103
+ def supported_http_protocols
104
+ (::Rack::App::Constants::HTTP.constants - [:ANY]).map(&:to_s)
105
+ end
106
+
107
+
108
+ def fetch_context(request_method, path_info)
109
+ normalized_request_path = Rack::App::Utils.normalize_path(path_info)
110
+
111
+ last_mounted_directory = nil
112
+ last_mounted_app = nil
113
+ current_cluster = main_cluster(request_method)
114
+
115
+ normalized_request_path.split('/').each do |path_part|
116
+
117
+ last_mounted_directory = current_cluster[MOUNTED_DIRECTORY] || last_mounted_directory
118
+ last_mounted_app = current_cluster[MOUNTED_APPLICATION] || last_mounted_app
119
+
120
+ current_cluster = current_cluster[path_part] || current_cluster[DYNAMIC_REQUEST_PATH_PART]
121
+
122
+ last_mounted_directory = (current_cluster || {})[MOUNTED_DIRECTORY] || last_mounted_directory
123
+ last_mounted_app = (current_cluster || {})[MOUNTED_APPLICATION] || last_mounted_app
124
+
125
+ if current_cluster.nil?
126
+ if last_mounted_directory
127
+ current_cluster = last_mounted_directory
128
+ break
129
+
130
+ elsif last_mounted_app
131
+ current_cluster = last_mounted_app
132
+ break
133
+
134
+ else
135
+ return nil
136
+
137
+ end
138
+ end
139
+
140
+ end
141
+
142
+ return current_cluster
143
+
144
+ end
95
145
 
96
- endpoint
146
+ def format_env(context, env)
147
+ mount_path = context[:options][:mount_path] rescue ''
148
+ env[::Rack::PATH_INFO].sub!(mount_path, '')
97
149
  end
98
150
 
99
151
  end
@@ -0,0 +1,13 @@
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
@@ -0,0 +1,11 @@
1
+ class Rack::App::Router::NotFound < Rack::App::Router::Base
2
+
3
+ def fetch_context(request_method, path_info)
4
+ {:endpoint => ::Rack::App::Endpoint::NOT_FOUND}
5
+ end
6
+
7
+ def fetch_endpoint(request_method, path_info)
8
+ ::Rack::App::Endpoint::NOT_FOUND
9
+ end
10
+
11
+ end
@@ -1,9 +1,5 @@
1
1
  class Rack::App::Router::Static < Rack::App::Router::Base
2
2
 
3
- def fetch_endpoint(request_method, request_path)
4
- mapped_endpoint_routes[[request_method, request_path]]
5
- end
6
-
7
3
  def compile_registered_endpoints!
8
4
  mapped_endpoint_routes.clear
9
5
  endpoints.each do |endpoint|
@@ -14,6 +10,11 @@ class Rack::App::Router::Static < Rack::App::Router::Base
14
10
 
15
11
  protected
16
12
 
13
+ def fetch_context(request_method, request_path)
14
+ endpoint = mapped_endpoint_routes[[request_method, request_path]]
15
+ endpoint && {:endpoint => endpoint}
16
+ end
17
+
17
18
  def mapped_endpoint_routes
18
19
  @mapped_endpoint_routes ||= {}
19
20
  end
@@ -3,34 +3,37 @@ module Rack::App::SingletonMethods::HttpMethods
3
3
  protected
4
4
 
5
5
  def get(path = '/', &block)
6
- add_route('GET', path, &block)
6
+ add_route(::Rack::App::Constants::HTTP::GET, path, &block)
7
7
  end
8
8
 
9
9
  def post(path = '/', &block)
10
- add_route('POST', path, &block)
10
+ add_route(::Rack::App::Constants::HTTP::POST, path, &block)
11
11
  end
12
12
 
13
13
  def put(path = '/', &block)
14
- add_route('PUT', path, &block)
14
+ add_route(::Rack::App::Constants::HTTP::PUT, path, &block)
15
15
  end
16
16
 
17
- def head(path = '/', &block)
18
- add_route('HEAD', path, &block)
17
+ def delete(path = '/', &block)
18
+ add_route(::Rack::App::Constants::HTTP::DELETE, path, &block)
19
19
  end
20
20
 
21
- def delete(path = '/', &block)
22
- add_route('DELETE', path, &block)
21
+ def head(path = '/', &block)
22
+ add_route(::Rack::App::Constants::HTTP::HEAD, path, &block)
23
23
  end
24
24
 
25
25
  def options(path = '/', &block)
26
- add_route('OPTIONS', path, &block)
26
+ add_route(::Rack::App::Constants::HTTP::OPTIONS, path, &block)
27
27
  end
28
28
 
29
29
  def patch(path = '/', &block)
30
- add_route('PATCH', path, &block)
30
+ add_route(::Rack::App::Constants::HTTP::PATCH, path, &block)
31
31
  end
32
32
 
33
33
  def alias_endpoint(new_request_path, original_request_path)
34
+ new_request_path = Rack::App::Utils.normalize_path(new_request_path)
35
+ original_request_path = Rack::App::Utils.normalize_path(original_request_path)
36
+
34
37
  router.endpoints.select { |ep| ep[:request_path] == original_request_path }.each do |endpoint|
35
38
  router.register_endpoint!(endpoint[:request_method], new_request_path, endpoint[:description], endpoint[:endpoint])
36
39
  end
@@ -47,6 +47,18 @@ module Rack::App::SingletonMethods::Mounting
47
47
  @last_description = nil
48
48
  end
49
49
 
50
+ def mount_rack_based_application(rack_based_app, options={})
51
+ router.register_endpoint!(
52
+ ::Rack::App::Constants::HTTP::ANY,
53
+ Rack::App::Utils.join(@namespaces, options[:to], ::Rack::App::Constants::RACK_BASED_APPLICATION),
54
+ @last_description,
55
+ rack_based_app)
56
+
57
+ @last_description = nil
58
+ end
59
+
60
+ alias mount_app mount_rack_based_application
61
+
50
62
  protected
51
63
 
52
64
  def on_mounted(&block)
@@ -2,12 +2,9 @@ module Rack::App::SingletonMethods::RackInterface
2
2
 
3
3
  public
4
4
 
5
- def call(request_env)
6
- Rack::App::RequestConfigurator.configure(request_env)
7
- endpoint = router.fetch_endpoint(
8
- request_env[::Rack::REQUEST_METHOD],
9
- request_env[Rack::App::Constants::NORMALIZED_PATH_INFO])
10
- endpoint.call(request_env)
5
+ def call(env)
6
+ Rack::App::RequestConfigurator.configure(env)
7
+ return router.call(env)
11
8
  end
12
9
 
13
10
  end
@@ -7,11 +7,7 @@ module Rack::App::SingletonMethods::RouteHandling
7
7
  protected
8
8
 
9
9
  def root(endpoint_path)
10
- %W[GET POST PUT DELETE OPTIONS PATCH HEAD].each do |request_method|
11
- endpoint = router.fetch_endpoint(request_method, endpoint_path)
12
- next if endpoint == Rack::App::Endpoint::NOT_FOUND
13
- router.register_endpoint!(request_method, '/', 'Root endpoint', endpoint)
14
- end
10
+ alias_endpoint '/', endpoint_path
15
11
  end
16
12
 
17
13
  def description(*description_texts)
@@ -11,7 +11,7 @@ module Rack::App::Test
11
11
 
12
12
  attr_reader :last_response
13
13
 
14
- [:get, :post, :put, :delete, :options, :patch].each do |request_method|
14
+ [:get, :post, :put, :delete, :options, :patch, :head].each do |request_method|
15
15
  define_method(request_method) do |*args|
16
16
 
17
17
  properties = args.select { |e| e.is_a?(Hash) }.reduce({}, &:merge!)
@@ -24,4 +24,12 @@ module Rack::App::Test
24
24
  end
25
25
  end
26
26
 
27
+ def rack_app(&block)
28
+ app_class = defined?(__rack_app_class__) ? __rack_app_class__ : nil
29
+ constructors = []
30
+ constructors << __rack_app_constructor__ if defined?(__rack_app_constructor__) and __rack_app_constructor__.is_a?(Proc)
31
+ constructors << block unless block.nil?
32
+ Rack::App::Test::Utils.rack_app_by(app_class, constructors)
33
+ end
34
+
27
35
  end
@@ -1,15 +1,15 @@
1
1
  module Rack::App::Test::SingletonMethods
2
2
 
3
- def rack_app(*args, &constructor)
4
-
5
- begin
6
- let(:rack_app) { Rack::App::Test::Utils.rack_app_by(*args, &constructor) }
7
- rescue NoMethodError
8
- define_method(:rack_app) do
9
- rack_app_by(*args, &constructor)
10
- end
11
- end
3
+ def rack_app(rack_app_class=nil, &constructor)
4
+ in_this_context(:__rack_app_class__) { rack_app_class }
5
+ in_this_context(:__rack_app_constructor__) { constructor }
6
+ nil
7
+ end
12
8
 
9
+ def in_this_context(name, &block)
10
+ let(name, &block)
11
+ rescue NoMethodError
12
+ define_method(name, &block)
13
13
  end
14
14
 
15
15
  end
@@ -10,16 +10,14 @@ module Rack::App::Test::Utils
10
10
  properties
11
11
  end
12
12
 
13
- def rack_app_by(*args, &constructor)
13
+ def rack_app_by(rack_app_class, constructors)
14
14
  subject_app = nil
15
15
 
16
- rack_app_class = args.shift
17
-
18
- if constructor.nil?
16
+ if constructors.empty?
19
17
  subject_app = rack_app_class
20
18
  else
21
19
  subject_app = Class.new(rack_app_class || ::Rack::App)
22
- subject_app.class_eval(&constructor)
20
+ constructors.each { |constructor| subject_app.class_eval(&constructor) }
23
21
  end
24
22
 
25
23
  subject_app
@@ -51,7 +51,7 @@ module Rack::App::Utils
51
51
  end
52
52
 
53
53
  def join(*url_path_parts)
54
- url_path_parts = [url_path_parts].flatten.compact
54
+ url_path_parts = [url_path_parts].flatten.compact.map(&:to_s)
55
55
  File.join(*url_path_parts).gsub(File::Separator, '/').sub(/^\/?(.*)$/, '/\1')
56
56
  end
57
57
 
@@ -82,4 +82,19 @@ module Rack::App::Utils
82
82
  ::Rack::App::Utils::DeepDup.new(object).to_dup
83
83
  end
84
84
 
85
+ def deep_merge(hash,oth_hash)
86
+ oth_hash.each_pair do |current_key, other_value|
87
+
88
+ this_value = hash[current_key]
89
+
90
+ hash[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
91
+ deep_merge(this_value, other_value)
92
+ else
93
+ other_value
94
+ end
95
+ end
96
+
97
+ hash
98
+ end
99
+
85
100
  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: 3.0.0.alpha
4
+ version: 3.0.0.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Luzsi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-03-26 00:00:00.000000000 Z
11
+ date: 2016-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -103,6 +103,8 @@ files:
103
103
  - lib/rack/app/router.rb
104
104
  - lib/rack/app/router/base.rb
105
105
  - lib/rack/app/router/dynamic.rb
106
+ - lib/rack/app/router/dynamic/request_path_part_placeholder.rb
107
+ - lib/rack/app/router/not_found.rb
106
108
  - lib/rack/app/router/static.rb
107
109
  - lib/rack/app/serializer.rb
108
110
  - lib/rack/app/singleton_methods.rb