rack-app 3.0.0.alpha → 3.0.0.beta

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: 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