rack-app 3.6.0 → 4.0.1
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 +4 -4
- data/.gitignore +3 -1
- data/.rubocop.yml +2 -0
- data/.travis.yml +2 -0
- data/Gemfile +5 -1
- data/README.md +29 -18
- data/VERSION +1 -1
- data/lib/rack/app/constants.rb +4 -2
- data/lib/rack/app/instance_methods/core.rb +5 -2
- data/lib/rack/app/middlewares.rb +2 -1
- data/lib/rack/app/middlewares/params.rb +6 -0
- data/lib/rack/app/middlewares/params/definition.rb +22 -0
- data/lib/rack/app/middlewares/params/definition/options.rb +45 -0
- data/lib/rack/app/middlewares/params/parser.rb +40 -0
- data/lib/rack/app/middlewares/params/setter.rb +19 -0
- data/lib/rack/app/middlewares/params/validator.rb +102 -0
- data/lib/rack/app/params.rb +6 -2
- data/lib/rack/app/router.rb +11 -6
- data/lib/rack/app/router/base.rb +4 -4
- data/lib/rack/app/singleton_methods.rb +3 -1
- data/lib/rack/app/singleton_methods/http_methods.rb +4 -1
- data/lib/rack/app/singleton_methods/mounting.rb +14 -15
- data/lib/rack/app/singleton_methods/params_validator.rb +12 -0
- data/lib/rack/app/singleton_methods/route_handling.rb +12 -6
- data/lib/rack/app/singleton_methods/settings.rb +7 -1
- data/lib/rack/app/test.rb +7 -6
- data/lib/rack/app/test/utils.rb +6 -31
- data/lib/rack/app/utils.rb +2 -1
- data/lib/rack/app/utils/parser.rb +45 -0
- data/lib/rack/app/utils/parser/boolean.rb +30 -0
- data/lib/rack/app/utils/parser/custom.rb +16 -0
- data/lib/rack/app/utils/parser/date.rb +15 -0
- data/lib/rack/app/utils/parser/date_time.rb +18 -0
- data/lib/rack/app/utils/parser/float.rb +15 -0
- data/lib/rack/app/utils/parser/integer.rb +9 -0
- data/lib/rack/app/utils/parser/numeric.rb +26 -0
- data/lib/rack/app/utils/parser/string.rb +9 -0
- data/lib/rack/app/utils/parser/time.rb +15 -0
- data/src/Net__HTTP Cheat Sheet.pdf +0 -0
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6cf38306bf272547c7a780a6a6e92f7138d8ebe5
|
4
|
+
data.tar.gz: 05929c52596d77e1caca9c46895ade3aa6a5b36d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: acfe46345c8ac900aee026f9249f053e17252224354afc803732b0f3b878761c305a6dbc0e2c29054052e45ae004662d9380b82851559446dd7bbf74991752e5
|
7
|
+
data.tar.gz: b4f5c6d7ce3e35cc34b16626641ae10dabd369adca3606f48098f6aaaf9f1ed50f687ed75c2a3cb35e56346d43607d859a020d0fd8294fc950617ff053438f7a
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|

|
8
8
|
|
9
|
-
Your next favourite rack based micro framework that is totally addition free!
|
9
|
+
Your next favourite rack based micro framework that is totally addition free!
|
10
10
|
Have a cup of awesomeness with your performance designed framework!
|
11
11
|
|
12
12
|
The idea behind is simple.
|
@@ -62,17 +62,17 @@ Yes, in fact it's already powering heroku hosted micro-services.
|
|
62
62
|
|
63
63
|
## Features
|
64
64
|
|
65
|
-
* easy to understand syntax
|
65
|
+
* easy to understand syntax
|
66
66
|
* module method level endpoint definition inspirited heavily by the Sinatra DSL
|
67
67
|
* unified error handling
|
68
|
-
* syntax sugar for default header definitions
|
68
|
+
* syntax sugar for default header definitions
|
69
69
|
* namespaces for endpoint request path declarations so it can be dry and unified
|
70
|
-
* no Class method bloat, so you can enjoy pure ruby without any surprises
|
70
|
+
* no Class method bloat, so you can enjoy pure ruby without any surprises
|
71
71
|
* App mounting so you can crete separated controllers for different task
|
72
72
|
* Null time look up routing
|
73
73
|
* allows as many endpoint registration to you as you want, without impact on route look up speed
|
74
74
|
* only basic sets for instance method lvl for the must need tools, such as params, payload
|
75
|
-
* simple to use class level response serializer
|
75
|
+
* simple to use class level response serializer
|
76
76
|
* so you can choose what type of serialization you want without any enforced convention
|
77
77
|
* static file serving so you can mount even filesystem based endpoints too
|
78
78
|
* built in testing module so your app can easily written with BDD approach
|
@@ -81,6 +81,7 @@ Yes, in fact it's already powering heroku hosted micro-services.
|
|
81
81
|
* you can define middleware stack before endpoints and it will only applied to them, similar like protected method workflow
|
82
82
|
* File Upload and file download in a efficient and elegant way with minimal memory consuming
|
83
83
|
* note that this is not only memory friendly way pure rack solution, but also 2x faster than the usualy solution which includes buffering in memory
|
84
|
+
* params validation with ease
|
84
85
|
|
85
86
|
## [Contributors](CONTRIBUTORS.md)
|
86
87
|
|
@@ -90,7 +91,7 @@ Yes, in fact it's already powering heroku hosted micro-services.
|
|
90
91
|
|
91
92
|
config.ru
|
92
93
|
|
93
|
-
#### basic
|
94
|
+
#### basic
|
94
95
|
|
95
96
|
```ruby
|
96
97
|
|
@@ -116,7 +117,7 @@ require 'rack/app'
|
|
116
117
|
class App < Rack::App
|
117
118
|
|
118
119
|
apply_extensions :front_end
|
119
|
-
|
120
|
+
|
120
121
|
mount SomeAppClass
|
121
122
|
|
122
123
|
headers 'Access-Control-Allow-Origin' => '*',
|
@@ -127,21 +128,27 @@ class App < Rack::App
|
|
127
128
|
end
|
128
129
|
|
129
130
|
desc 'some hello endpoint'
|
131
|
+
validate_params do
|
132
|
+
required 'words', :class => Array, :of => String, :desc => 'some word', :example => ['pug']
|
133
|
+
optional 'word', :class => String, :desc => 'one word', :example => 'pug'
|
134
|
+
end
|
130
135
|
get '/hello' do
|
136
|
+
puts(validate_params['words'])
|
137
|
+
|
131
138
|
return 'Hello World!'
|
132
139
|
end
|
133
140
|
|
134
|
-
namespace '/users' do
|
135
|
-
|
141
|
+
namespace '/users' do
|
142
|
+
|
136
143
|
desc 'some restful endpoint'
|
137
144
|
get '/:user_id' do
|
138
145
|
response.status = 201
|
139
146
|
params['user_id'] #=> restful parameter :user_id
|
140
147
|
say #=> "hello world!"
|
141
148
|
end
|
142
|
-
|
149
|
+
|
143
150
|
end
|
144
|
-
|
151
|
+
|
145
152
|
desc 'some endpoint that has error and will be rescued'
|
146
153
|
get '/make_error' do
|
147
154
|
raise(StandardError,'error block rescued')
|
@@ -161,12 +168,12 @@ end
|
|
161
168
|
|
162
169
|
```
|
163
170
|
|
164
|
-
you can access Rack::Request with the request method and
|
165
|
-
Rack::Response as response method.
|
171
|
+
you can access Rack::Request with the request method and
|
172
|
+
Rack::Response as response method.
|
166
173
|
|
167
174
|
By default if you dont write anything to the response 'body' the endpoint block logic return will be used
|
168
175
|
|
169
|
-
## Testing
|
176
|
+
## Testing
|
170
177
|
|
171
178
|
for testing use rack/test or the bundled testing module for writing unit test for your rack application
|
172
179
|
|
@@ -214,9 +221,13 @@ end
|
|
214
221
|
|
215
222
|
## Example Apps To start with
|
216
223
|
|
224
|
+
* [Official website How To examples](http://rack-app.com/)
|
225
|
+
|
226
|
+
* [Rack::App Team Github repositories](https://github.com/rack-app)
|
227
|
+
|
217
228
|
* [Basic](https://github.com/rack-app/rack-app-example-basic)
|
218
|
-
* bare bone simple example app
|
219
|
-
|
229
|
+
* bare bone simple example app
|
230
|
+
|
220
231
|
* [Escher Authorized Api](https://github.com/rack-app/rack-app-example-escher)
|
221
232
|
* complex authorization for corporal level api use
|
222
233
|
|
@@ -250,7 +261,7 @@ the benchmarking was taken on the following hardware specification:
|
|
250
261
|
|
251
262
|
For more reports check the Benchmark repo out :)
|
252
263
|
|
253
|
-
## Roadmap
|
264
|
+
## Roadmap
|
254
265
|
|
255
266
|
### Team [Backlog](https://docs.google.com/spreadsheets/d/19GGX51i6uCQQz8pQ-lvsIxu43huKCX-eC1526-RL3YA/edit?usp=sharing)
|
256
267
|
|
@@ -267,6 +278,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
267
278
|
Bug reports and pull requests are welcome on GitHub at https://github.com/adamluzsi/rack-app.rb This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
268
279
|
|
269
280
|
## License and Copyright
|
270
|
-
|
281
|
+
|
271
282
|
Rack::App is free software released under the [Apache License V2](https://opensource.org/licenses/Apache-2.0) License.
|
272
283
|
The logo was designed by Zsófia Gebauer. It is Copyright © 2015 Adam Luzsi. All Rights Reserved.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
4.0.1
|
data/lib/rack/app/constants.rb
CHANGED
@@ -15,5 +15,7 @@ module Rack::App::Constants
|
|
15
15
|
PATH_PARAMS_MATCHER = 'rack-app.path_params_matcher'.freeze
|
16
16
|
RACK_BASED_APPLICATION = '[Mounted Rack Application]'.freeze
|
17
17
|
MOUNTED_DIRECTORY = '[Mounted Directory]'.freeze
|
18
|
-
|
19
|
-
|
18
|
+
VALIDATED_PARAMS = 'rack-app.validated_params'
|
19
|
+
PARSED_PARAMS = 'rack-app.parsed_params'
|
20
|
+
|
21
|
+
end
|
@@ -3,7 +3,11 @@ module Rack::App::InstanceMethods::Core
|
|
3
3
|
attr_writer :request, :response
|
4
4
|
|
5
5
|
def params
|
6
|
-
|
6
|
+
request.env[::Rack::App::Constants::PARSED_PARAMS] ||= Rack::App::Params.new(request.env).to_hash
|
7
|
+
end
|
8
|
+
|
9
|
+
def validated_params
|
10
|
+
request.env[::Rack::App::Constants::VALIDATED_PARAMS]
|
7
11
|
end
|
8
12
|
|
9
13
|
def request
|
@@ -15,4 +19,3 @@ module Rack::App::InstanceMethods::Core
|
|
15
19
|
end
|
16
20
|
|
17
21
|
end
|
18
|
-
|
data/lib/rack/app/middlewares.rb
CHANGED
@@ -0,0 +1,22 @@
|
|
1
|
+
class Rack::App::Middlewares::Params::Definition
|
2
|
+
|
3
|
+
require "rack/app/middlewares/params/definition/options"
|
4
|
+
|
5
|
+
def initialize(&descriptor)
|
6
|
+
@required = {}
|
7
|
+
@optional = {}
|
8
|
+
instance_exec(&descriptor)
|
9
|
+
end
|
10
|
+
|
11
|
+
def required(params_key,options)
|
12
|
+
@required[params_key.to_s]= self.class::Options.new(options).formatted
|
13
|
+
end
|
14
|
+
|
15
|
+
def optional(params_key,options)
|
16
|
+
@optional[params_key.to_s]= self.class::Options.new(options).formatted
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_descriptor
|
20
|
+
{:required => @required, :optional => @optional}
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Rack::App::Middlewares::Params::Definition::Options
|
2
|
+
|
3
|
+
ERROR_EACH = 'class must implement #each method to use :of expression in parameter definition'
|
4
|
+
|
5
|
+
def initialize(descriptor)
|
6
|
+
@descriptor = descriptor
|
7
|
+
end
|
8
|
+
|
9
|
+
def formatted
|
10
|
+
{
|
11
|
+
:class => parameter_class,
|
12
|
+
:of => parameter_class_elements,
|
13
|
+
:description => parameter_description,
|
14
|
+
:example => parameter_example
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def parameter_example
|
21
|
+
@descriptor[:example]
|
22
|
+
end
|
23
|
+
|
24
|
+
def parameter_description
|
25
|
+
@descriptor[:desc] || @descriptor[:description]
|
26
|
+
end
|
27
|
+
|
28
|
+
def parameter_class
|
29
|
+
@descriptor[:class] || @descriptor[:type]
|
30
|
+
end
|
31
|
+
|
32
|
+
def parameter_class_elements
|
33
|
+
if @descriptor[:of]
|
34
|
+
raise "#{parameter_class} #{ERROR_EACH}" unless parameter_class_iterable?
|
35
|
+
@descriptor[:of]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def parameter_class_iterable?
|
40
|
+
parameter_class.instance_method(:each)
|
41
|
+
rescue NameError
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Rack::App::Middlewares::Params::Parser
|
2
|
+
|
3
|
+
def initialize(app, descriptor)
|
4
|
+
@app = app
|
5
|
+
@descriptor = descriptor
|
6
|
+
@merged_params_descriptor = descriptor.values.reduce(:merge)
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
set_params(env)
|
11
|
+
|
12
|
+
@app.call(env)
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def set_params(env)
|
18
|
+
params = Rack::App::Params.new(env).to_hash
|
19
|
+
validated_params = (env[::Rack::App::Constants::VALIDATED_PARAMS] ||= {})
|
20
|
+
parse_params(validated_params, params)
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_params(validated_params, params)
|
24
|
+
@merged_params_descriptor.each do |key, properties|
|
25
|
+
next if params[key].nil?
|
26
|
+
|
27
|
+
if properties[:of]
|
28
|
+
validated_params[key]= [*params[key]].map{ |str| parse(properties[:of], str) }
|
29
|
+
else
|
30
|
+
validated_params[key]= parse(properties[:class], params[key])
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse(type, str)
|
37
|
+
Rack::App::Utils::Parser.parse(type, str)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Rack::App::Middlewares::Params::Setter
|
2
|
+
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
env[::Rack::App::Constants::PARSED_PARAMS] ||= params_hash(env)
|
9
|
+
|
10
|
+
@app.call(env)
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def params_hash(env)
|
16
|
+
Rack::App::Params.new(env).to_hash
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
class Rack::App::Middlewares::Params::Validator
|
2
|
+
ValidateError = Class.new(ArgumentError)
|
3
|
+
|
4
|
+
def initialize(app, descriptor)
|
5
|
+
@descriptor = descriptor
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
# => 422 required field missing | "422 Unprocessable Entity"
|
10
|
+
def call(env)
|
11
|
+
validate(env)
|
12
|
+
@app.call(env)
|
13
|
+
rescue ValidateError => ex
|
14
|
+
unprocessable_response(ex)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def unprocessable_response(validate_error)
|
20
|
+
response = Rack::Response.new
|
21
|
+
response.status = 422
|
22
|
+
response.write("422 Unprocessable Entity\n")
|
23
|
+
response.write(validate_error.message)
|
24
|
+
response.finish
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate(env)
|
28
|
+
params = Rack::App::Params.new(env).to_hash
|
29
|
+
validation_results(env, params)
|
30
|
+
end
|
31
|
+
|
32
|
+
def validation_results(env, params)
|
33
|
+
validate_required_params(env, params)
|
34
|
+
validate_optional_params(env, params)
|
35
|
+
validate_invalid_params(params)
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_required_params(env, params)
|
39
|
+
@descriptor[:required].each do |key, properties|
|
40
|
+
validate_key(key,properties,params)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_optional_params(env, params)
|
45
|
+
@descriptor[:optional].each do |key, properties|
|
46
|
+
next unless params.has_key?(key)
|
47
|
+
|
48
|
+
validate_key(key,properties,params)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate_key(key,properties,params)
|
53
|
+
unless params.has_key?(key)
|
54
|
+
missing_key_error(key, properties[:class])
|
55
|
+
end
|
56
|
+
|
57
|
+
if properties[:of]
|
58
|
+
validate_array(properties[:class], properties[:of], key, *params[key])
|
59
|
+
elsif parse(properties[:class], params[key]).nil?
|
60
|
+
invalid_type_error(key, properties[:class])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_invalid_params(params)
|
65
|
+
valid_keys = @descriptor[:required].keys + @descriptor[:optional].keys
|
66
|
+
(params.keys - valid_keys).each do |key|
|
67
|
+
invalid_key_error(key)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def validate_array(type, elements_type, key, *elements)
|
72
|
+
values = elements.map{ |str| parse(elements_type, str) }
|
73
|
+
|
74
|
+
if values.include?(nil)
|
75
|
+
invalid_type_of_error(key, type, elements_type)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse(type, str)
|
80
|
+
::Rack::App::Utils::Parser.parse(type, str)
|
81
|
+
end
|
82
|
+
|
83
|
+
def error(message)
|
84
|
+
raise(ValidateError.new(message))
|
85
|
+
end
|
86
|
+
|
87
|
+
def missing_key_error(key, klass)
|
88
|
+
error "missing key: #{key}(#{klass})"
|
89
|
+
end
|
90
|
+
|
91
|
+
def invalid_key_error(key)
|
92
|
+
error "invalid key: #{key}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def invalid_type_error(key,klass)
|
96
|
+
error "invalid type for #{key}: #{klass} expected"
|
97
|
+
end
|
98
|
+
|
99
|
+
def invalid_type_of_error(key,klass,of)
|
100
|
+
error "invalid type for #{key}: #{klass} of #{of} expected"
|
101
|
+
end
|
102
|
+
end
|
data/lib/rack/app/params.rb
CHANGED
@@ -2,7 +2,11 @@ require 'cgi'
|
|
2
2
|
class Rack::App::Params
|
3
3
|
|
4
4
|
def to_hash
|
5
|
-
|
5
|
+
if @request_env[::Rack::App::Constants::PARSED_PARAMS]
|
6
|
+
@request_env[::Rack::App::Constants::PARSED_PARAMS]
|
7
|
+
else
|
8
|
+
query_params.merge(request_path_params)
|
9
|
+
end
|
6
10
|
end
|
7
11
|
|
8
12
|
protected
|
@@ -57,4 +61,4 @@ class Rack::App::Params
|
|
57
61
|
@request_env[::Rack::App::Constants::PATH_PARAMS_MATCHER] || {}
|
58
62
|
end
|
59
63
|
|
60
|
-
end
|
64
|
+
end
|
data/lib/rack/app/router.rb
CHANGED
@@ -19,21 +19,21 @@ class Rack::App::Router
|
|
19
19
|
|
20
20
|
wd0 = endpoints.map { |endpoint| endpoint[:request_method].to_s.length }.max
|
21
21
|
wd1 = endpoints.map { |endpoint| endpoint[:request_path].to_s.length }.max
|
22
|
-
wd2 = endpoints.map { |endpoint| endpoint
|
22
|
+
wd2 = endpoints.map { |endpoint| extract_description(endpoint).to_s.length }.max
|
23
23
|
|
24
24
|
return endpoints.sort_by { |endpoint| [endpoint[:request_method], endpoint[:request_path]] }.map do |endpoint|
|
25
25
|
[
|
26
26
|
endpoint[:request_method].to_s.ljust(wd0),
|
27
27
|
endpoint[:request_path].to_s.ljust(wd1),
|
28
|
-
endpoint
|
28
|
+
extract_description(endpoint).to_s.ljust(wd2)
|
29
29
|
].join(' ')
|
30
30
|
end
|
31
31
|
|
32
32
|
end
|
33
33
|
|
34
|
-
def register_endpoint!(request_method, request_path,
|
34
|
+
def register_endpoint!(request_method, request_path, endpoint, properties)
|
35
35
|
router = router_for(request_path)
|
36
|
-
router.register_endpoint!(request_method, request_path,
|
36
|
+
router.register_endpoint!(request_method, request_path, endpoint, properties)
|
37
37
|
end
|
38
38
|
|
39
39
|
def merge_router!(router, prop={})
|
@@ -44,8 +44,8 @@ class Rack::App::Router
|
|
44
44
|
register_endpoint!(
|
45
45
|
endpoint[:request_method],
|
46
46
|
request_path,
|
47
|
-
endpoint[:
|
48
|
-
endpoint[:
|
47
|
+
endpoint[:endpoint],
|
48
|
+
endpoint[:properties]
|
49
49
|
)
|
50
50
|
end
|
51
51
|
nil
|
@@ -70,4 +70,9 @@ class Rack::App::Router
|
|
70
70
|
path_str.include?(Rack::App::Constants::RACK_BASED_APPLICATION)
|
71
71
|
end
|
72
72
|
|
73
|
+
def extract_description(endpoint_struct)
|
74
|
+
if endpoint_struct[:properties]
|
75
|
+
endpoint_struct[:properties][:description]
|
76
|
+
end
|
77
|
+
end
|
73
78
|
end
|
data/lib/rack/app/router/base.rb
CHANGED
@@ -20,13 +20,13 @@ class Rack::App::Router::Base
|
|
20
20
|
@endpoints ||= []
|
21
21
|
end
|
22
22
|
|
23
|
-
def register_endpoint!(request_method, request_path,
|
23
|
+
def register_endpoint!(request_method, request_path, endpoint, properties={})
|
24
24
|
endpoints.push(
|
25
25
|
{
|
26
26
|
:request_method => request_method,
|
27
27
|
:request_path => Rack::App::Utils.normalize_path(request_path),
|
28
|
-
:
|
29
|
-
:
|
28
|
+
:endpoint => endpoint,
|
29
|
+
:properties => properties
|
30
30
|
}
|
31
31
|
)
|
32
32
|
|
@@ -44,4 +44,4 @@ class Rack::App::Router::Base
|
|
44
44
|
raise('IMPLEMENTATION MISSING ERROR')
|
45
45
|
end
|
46
46
|
|
47
|
-
end
|
47
|
+
end
|
@@ -3,6 +3,7 @@ module Rack::App::SingletonMethods
|
|
3
3
|
require 'rack/app/singleton_methods/http_methods'
|
4
4
|
require 'rack/app/singleton_methods/inheritance'
|
5
5
|
require 'rack/app/singleton_methods/mounting'
|
6
|
+
require 'rack/app/singleton_methods/params_validator'
|
6
7
|
require 'rack/app/singleton_methods/rack_interface'
|
7
8
|
require 'rack/app/singleton_methods/route_handling'
|
8
9
|
require 'rack/app/singleton_methods/settings'
|
@@ -11,9 +12,10 @@ module Rack::App::SingletonMethods
|
|
11
12
|
include Rack::App::SingletonMethods::HttpMethods
|
12
13
|
include Rack::App::SingletonMethods::Inheritance
|
13
14
|
include Rack::App::SingletonMethods::Mounting
|
15
|
+
include Rack::App::SingletonMethods::ParamsValidator
|
14
16
|
include Rack::App::SingletonMethods::RackInterface
|
15
17
|
include Rack::App::SingletonMethods::RouteHandling
|
16
18
|
include Rack::App::SingletonMethods::Settings
|
17
19
|
include Rack::App::SingletonMethods::Extensions
|
18
20
|
|
19
|
-
end
|
21
|
+
end
|
@@ -35,7 +35,10 @@ module Rack::App::SingletonMethods::HttpMethods
|
|
35
35
|
original_request_path = Rack::App::Utils.normalize_path(original_request_path)
|
36
36
|
|
37
37
|
router.endpoints.select { |ep| ep[:request_path] == original_request_path }.each do |endpoint|
|
38
|
-
router.register_endpoint!(
|
38
|
+
router.register_endpoint!(
|
39
|
+
endpoint[:request_method], new_request_path,
|
40
|
+
endpoint[:endpoint], endpoint[:properties]
|
41
|
+
)
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
@@ -1,18 +1,16 @@
|
|
1
1
|
module Rack::App::SingletonMethods::Mounting
|
2
|
+
MOUNT = Rack::App::SingletonMethods::Mounting
|
2
3
|
|
3
|
-
def mount(
|
4
|
-
|
5
|
-
raise(ArgumentError, 'Invalid class given for mount, must be a Rack::App')
|
6
|
-
end
|
4
|
+
def mount(app, options={})
|
5
|
+
options.freeze
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
duplication.class_exec(::Rack::App::Utils.deep_dup(properties), &on_mount)
|
7
|
+
unless app.is_a?(Class) and app <= Rack::App
|
8
|
+
raise(ArgumentError, 'Invalid class given for mount, must be a Rack::App')
|
11
9
|
end
|
12
10
|
|
13
|
-
cli.merge!(
|
14
|
-
merge_prop = {:namespaces => [@namespaces,
|
15
|
-
router.merge_router!(
|
11
|
+
cli.merge!(app.cli)
|
12
|
+
merge_prop = {:namespaces => [@namespaces, options[:to]].flatten}
|
13
|
+
router.merge_router!(app.router, merge_prop)
|
16
14
|
|
17
15
|
nil
|
18
16
|
end
|
@@ -41,16 +39,17 @@ module Rack::App::SingletonMethods::Mounting
|
|
41
39
|
def serve_files_from(file_path, options={})
|
42
40
|
file_server = Rack::App::FileServer.new(Rack::App::Utils.expand_path(file_path))
|
43
41
|
request_path = Rack::App::Utils.join(@namespaces, options[:to], '**', '*')
|
44
|
-
router.register_endpoint!('GET', request_path,
|
42
|
+
router.register_endpoint!('GET', request_path, file_server, route_registration_properties)
|
45
43
|
@last_description = nil
|
46
44
|
end
|
47
45
|
|
48
46
|
def mount_rack_based_application(rack_based_app, options={})
|
49
47
|
router.register_endpoint!(
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
48
|
+
::Rack::App::Constants::HTTP::ANY,
|
49
|
+
Rack::App::Utils.join(@namespaces, options[:to], ::Rack::App::Constants::RACK_BASED_APPLICATION),
|
50
|
+
rack_based_app,
|
51
|
+
route_registration_properties
|
52
|
+
)
|
54
53
|
|
55
54
|
@last_description = nil
|
56
55
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Rack::App::SingletonMethods::ParamsValidator
|
2
|
+
def validate_params(&block)
|
3
|
+
descriptor = Rack::App::Middlewares::Params::Definition.new(&block).to_descriptor
|
4
|
+
route_registration_properties[:params]= descriptor
|
5
|
+
only_next_endpoint_middlewares do |builder|
|
6
|
+
builder.use(Rack::App::Middlewares::Params::Setter)
|
7
|
+
builder.use(Rack::App::Middlewares::Params::Validator, descriptor)
|
8
|
+
builder.use(Rack::App::Middlewares::Params::Parser, descriptor)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
alias params validate_params
|
12
|
+
end
|
@@ -7,11 +7,15 @@ module Rack::App::SingletonMethods::RouteHandling
|
|
7
7
|
protected
|
8
8
|
|
9
9
|
def root(endpoint_path)
|
10
|
-
alias_endpoint
|
10
|
+
alias_endpoint('/', endpoint_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def route_registration_properties
|
14
|
+
@route_registration_properties ||= {}
|
11
15
|
end
|
12
16
|
|
13
17
|
def description(*description_texts)
|
14
|
-
|
18
|
+
route_registration_properties[:description]= description_texts.join("\n")
|
15
19
|
end
|
16
20
|
|
17
21
|
alias desc description
|
@@ -21,17 +25,18 @@ module Rack::App::SingletonMethods::RouteHandling
|
|
21
25
|
request_path = ::Rack::App::Utils.join(@namespaces, request_path)
|
22
26
|
|
23
27
|
builder = Rack::Builder.new
|
24
|
-
middlewares.each do |builder_block|
|
28
|
+
(middlewares + only_next_endpoint_middlewares).each do |builder_block|
|
25
29
|
builder_block.call(builder)
|
26
30
|
end
|
27
31
|
|
32
|
+
only_next_endpoint_middlewares.clear
|
33
|
+
|
28
34
|
properties = {
|
29
35
|
:user_defined_logic => block,
|
30
36
|
:request_method => request_method,
|
31
37
|
:request_path => request_path,
|
32
38
|
|
33
39
|
:error_handler => error,
|
34
|
-
:description => @last_description,
|
35
40
|
:serializer => serializer,
|
36
41
|
:middleware => builder,
|
37
42
|
:app_class => self
|
@@ -39,8 +44,9 @@ module Rack::App::SingletonMethods::RouteHandling
|
|
39
44
|
|
40
45
|
|
41
46
|
endpoint = Rack::App::Endpoint.new(properties)
|
42
|
-
router.register_endpoint!(request_method, request_path,
|
47
|
+
router.register_endpoint!(request_method, request_path, endpoint, route_registration_properties)
|
43
48
|
|
49
|
+
@route_registration_properties = nil
|
44
50
|
@last_description = nil
|
45
51
|
return endpoint
|
46
52
|
|
@@ -55,4 +61,4 @@ module Rack::App::SingletonMethods::RouteHandling
|
|
55
61
|
nil
|
56
62
|
end
|
57
63
|
|
58
|
-
end
|
64
|
+
end
|
@@ -43,4 +43,10 @@ module Rack::App::SingletonMethods::Settings
|
|
43
43
|
|
44
44
|
alias middleware middlewares
|
45
45
|
|
46
|
-
|
46
|
+
def only_next_endpoint_middlewares(&block)
|
47
|
+
@only_next_endpoint_middlewares ||= []
|
48
|
+
@only_next_endpoint_middlewares << block unless block.nil?
|
49
|
+
@only_next_endpoint_middlewares
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/rack/app/test.rb
CHANGED
@@ -16,10 +16,9 @@ module Rack::App::Test
|
|
16
16
|
|
17
17
|
properties = args.select { |e| e.is_a?(Hash) }.reduce({}, &:merge!)
|
18
18
|
url = args.select { |e| e.is_a?(String) }.first || properties.delete(:url)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
return @last_response = request.__send__(request_method, url, env)
|
19
|
+
mock_request = Rack::MockRequest.new(rack_app)
|
20
|
+
request_env = Rack::App::Test::Utils.env_by(properties)
|
21
|
+
return @last_response = mock_request.request(request_method, url, request_env)
|
23
22
|
|
24
23
|
end
|
25
24
|
end
|
@@ -29,7 +28,9 @@ module Rack::App::Test
|
|
29
28
|
@app ||= lambda do
|
30
29
|
app_class = defined?(__rack_app_class__) ? __rack_app_class__ : nil
|
31
30
|
constructors = []
|
32
|
-
|
31
|
+
if defined?(__rack_app_constructor__) and __rack_app_constructor__.is_a?(Proc)
|
32
|
+
constructors << __rack_app_constructor__
|
33
|
+
end
|
33
34
|
Rack::App::Test::Utils.rack_app_by(app_class, constructors)
|
34
35
|
end.call
|
35
36
|
|
@@ -37,4 +38,4 @@ module Rack::App::Test
|
|
37
38
|
|
38
39
|
end
|
39
40
|
|
40
|
-
end
|
41
|
+
end
|
data/lib/rack/app/test/utils.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "cgi"
|
1
2
|
module Rack::App::Test::Utils
|
2
3
|
|
3
4
|
extend self
|
@@ -10,13 +11,10 @@ module Rack::App::Test::Utils
|
|
10
11
|
properties
|
11
12
|
end
|
12
13
|
|
13
|
-
def rack_app_by(subject_class, constructors
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
constructors.each { |constructor| app.class_eval(&constructor) }
|
18
|
-
|
19
|
-
return app
|
14
|
+
def rack_app_by(subject_class, constructors)
|
15
|
+
app_class = subject_class.respond_to?(:call) ? subject_class : Class.new(Rack::App)
|
16
|
+
constructors.each { |constructor| app_class.class_eval(&constructor) }
|
17
|
+
return app_class
|
20
18
|
end
|
21
19
|
|
22
20
|
def env_by(properties)
|
@@ -28,7 +26,6 @@ module Rack::App::Test::Utils
|
|
28
26
|
env[::Rack::QUERY_STRING]= encode_www_form(properties[:params].to_a)
|
29
27
|
env.merge!(properties[:env] || {})
|
30
28
|
|
31
|
-
|
32
29
|
env
|
33
30
|
end
|
34
31
|
|
@@ -67,30 +64,8 @@ module Rack::App::Test::Utils
|
|
67
64
|
end.join('&')
|
68
65
|
end
|
69
66
|
|
70
|
-
TBLENCWWWCOMP_ = {} # :nodoc:
|
71
|
-
256.times do |i|
|
72
|
-
TBLENCWWWCOMP_['%%%02X' % i] = i.chr
|
73
|
-
end
|
74
|
-
TBLENCWWWCOMP_[' '] = '+'
|
75
|
-
TBLENCWWWCOMP_.freeze
|
76
|
-
TBLDECWWWCOMP = {} # :nodoc:
|
77
|
-
256.times do |i|
|
78
|
-
h, l = i>>4, i&15
|
79
|
-
TBLDECWWWCOMP[i.chr]= '%%%X%X' % [h, l]
|
80
|
-
TBLDECWWWCOMP[i.chr]= '%%%X%X' % [h, l]
|
81
|
-
TBLDECWWWCOMP[i.chr]= '%%%x%X' % [h, l]
|
82
|
-
TBLDECWWWCOMP[i.chr]= '%%%X%x' % [h, l]
|
83
|
-
TBLDECWWWCOMP[i.chr]= '%%%x%x' % [h, l]
|
84
|
-
end
|
85
|
-
TBLDECWWWCOMP['+'] = ' '
|
86
|
-
TBLDECWWWCOMP.freeze
|
87
|
-
|
88
67
|
def encode_www_form_component(str)
|
89
|
-
str
|
90
|
-
TBLENCWWWCOMP_.each do |from, to|
|
91
|
-
str.gsub!(from, to)
|
92
|
-
end
|
93
|
-
str
|
68
|
+
CGI.escape(str.to_s)
|
94
69
|
end
|
95
70
|
|
96
71
|
end
|
data/lib/rack/app/utils.rb
CHANGED
@@ -3,6 +3,7 @@ module Rack::App::Utils
|
|
3
3
|
extend self
|
4
4
|
|
5
5
|
require 'rack/app/utils/deep_dup'
|
6
|
+
require 'rack/app/utils/parser'
|
6
7
|
|
7
8
|
# Normalizes URI path.
|
8
9
|
#
|
@@ -103,4 +104,4 @@ module Rack::App::Utils
|
|
103
104
|
RUBY_PLATFORM =~ /mswin|mingw/ ? 'NUL:' : '/dev/null'
|
104
105
|
end
|
105
106
|
|
106
|
-
end
|
107
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Rack::App::Utils::Parser
|
2
|
+
extend(self)
|
3
|
+
|
4
|
+
require 'rack/app/utils/parser/custom'
|
5
|
+
require 'rack/app/utils/parser/string'
|
6
|
+
require 'rack/app/utils/parser/boolean'
|
7
|
+
require 'rack/app/utils/parser/date'
|
8
|
+
require 'rack/app/utils/parser/time'
|
9
|
+
require 'rack/app/utils/parser/date_time'
|
10
|
+
require 'rack/app/utils/parser/float'
|
11
|
+
require 'rack/app/utils/parser/integer'
|
12
|
+
require 'rack/app/utils/parser/numeric'
|
13
|
+
|
14
|
+
def parse(type, str)
|
15
|
+
string = str.to_s
|
16
|
+
parser = get_parser(type)
|
17
|
+
return unless parser.validate(string)
|
18
|
+
parser.parse(string)
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def get_parser(type)
|
24
|
+
case true
|
25
|
+
when [::String, :string].include?(type)
|
26
|
+
self::String.new
|
27
|
+
when [::TrueClass, ::FalseClass, :boolean].include?(type)
|
28
|
+
self::Boolean.new
|
29
|
+
when [::Date, :date].include?(type)
|
30
|
+
self::Date.new
|
31
|
+
when [::Time, :time].include?(type)
|
32
|
+
self::Time.new
|
33
|
+
when [::DateTime, :date_time, :datetime].include?(type)
|
34
|
+
self::DateTime.new
|
35
|
+
when [::Integer, :integer].include?(type)
|
36
|
+
self::Integer.new
|
37
|
+
when [::Float, :float].include?(type)
|
38
|
+
self::Float.new
|
39
|
+
when [::Numeric, :numeric].include?(type)
|
40
|
+
self::Numeric.new
|
41
|
+
else
|
42
|
+
self::Custom.new(type)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Rack::App::Utils::Parser::Boolean
|
2
|
+
def parse(str)
|
3
|
+
case true
|
4
|
+
|
5
|
+
when true?(str)
|
6
|
+
true
|
7
|
+
|
8
|
+
when false?(str)
|
9
|
+
false
|
10
|
+
|
11
|
+
else
|
12
|
+
str
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate(str)
|
18
|
+
false?(str) || true?(str)
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def false?(obj)
|
24
|
+
!!(obj =~ /^false$/)
|
25
|
+
end
|
26
|
+
|
27
|
+
def true?(obj)
|
28
|
+
!!(obj =~ /^true$/)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'date'
|
2
|
+
class Rack::App::Utils::Parser::DateTime
|
3
|
+
def parse(str)
|
4
|
+
::DateTime.parse(str)
|
5
|
+
end
|
6
|
+
|
7
|
+
def validate(str)
|
8
|
+
[
|
9
|
+
/(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})[+-](\d{2})\:(\d{2})/,
|
10
|
+
/^\w+, \d+ \w+ \d+ \d\d:\d\d:\d\d \+\d+$/,
|
11
|
+
/^-?\d+-\d\d-\d\d\w\d\d:\d\d:\d\d\+\d\d:\d\d$/,
|
12
|
+
/\w+ \w+ \d+ \d+ \d+:\d+:\d+ \w+\+\d+ \(\w+\)/,
|
13
|
+
/^-?\d+-\d\d?-\d\d?\w\d\d?:\d\d?:\d\d?\w$/
|
14
|
+
].any? do |regexp|
|
15
|
+
!!(str =~ regexp)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Rack::App::Utils::Parser::Numeric
|
2
|
+
def parse(str)
|
3
|
+
case true
|
4
|
+
when int_parser.validate(str)
|
5
|
+
int_parser.parse(str)
|
6
|
+
when float_parser.validate(str)
|
7
|
+
float_parser.parse(str)
|
8
|
+
else
|
9
|
+
str
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate(str)
|
14
|
+
int_parser.validate(str) || float_parser.validate(str)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def int_parser
|
20
|
+
Rack::App::Utils::Parser::Integer.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def float_parser
|
24
|
+
Rack::App::Utils::Parser::Float.new
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'time'
|
2
|
+
class Rack::App::Utils::Parser::Time
|
3
|
+
def parse(str)
|
4
|
+
::Time.parse(str)
|
5
|
+
end
|
6
|
+
|
7
|
+
def validate(str)
|
8
|
+
[
|
9
|
+
/(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})[+-](\d{2})\:(\d{2})/,
|
10
|
+
/^\d+-\d\d-\d\d \d\d:\d\d:\d\d \+\d+$/
|
11
|
+
].any? do |regexp|
|
12
|
+
!!(str =~ regexp)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
Binary file
|
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:
|
4
|
+
version: 4.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Luzsi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -77,6 +77,7 @@ extra_rdoc_files: []
|
|
77
77
|
files:
|
78
78
|
- ".gitignore"
|
79
79
|
- ".rspec"
|
80
|
+
- ".rubocop.yml"
|
80
81
|
- ".travis.yml"
|
81
82
|
- CODE_OF_CONDUCT.md
|
82
83
|
- CONTRIBUTING.md
|
@@ -112,6 +113,12 @@ files:
|
|
112
113
|
- lib/rack/app/instance_methods/upload.rb
|
113
114
|
- lib/rack/app/middlewares.rb
|
114
115
|
- lib/rack/app/middlewares/header_setter.rb
|
116
|
+
- lib/rack/app/middlewares/params.rb
|
117
|
+
- lib/rack/app/middlewares/params/definition.rb
|
118
|
+
- lib/rack/app/middlewares/params/definition/options.rb
|
119
|
+
- lib/rack/app/middlewares/params/parser.rb
|
120
|
+
- lib/rack/app/middlewares/params/setter.rb
|
121
|
+
- lib/rack/app/middlewares/params/validator.rb
|
115
122
|
- lib/rack/app/params.rb
|
116
123
|
- lib/rack/app/request_configurator.rb
|
117
124
|
- lib/rack/app/router.rb
|
@@ -126,6 +133,7 @@ files:
|
|
126
133
|
- lib/rack/app/singleton_methods/http_methods.rb
|
127
134
|
- lib/rack/app/singleton_methods/inheritance.rb
|
128
135
|
- lib/rack/app/singleton_methods/mounting.rb
|
136
|
+
- lib/rack/app/singleton_methods/params_validator.rb
|
129
137
|
- lib/rack/app/singleton_methods/rack_interface.rb
|
130
138
|
- lib/rack/app/singleton_methods/route_handling.rb
|
131
139
|
- lib/rack/app/singleton_methods/settings.rb
|
@@ -134,8 +142,19 @@ files:
|
|
134
142
|
- lib/rack/app/test/utils.rb
|
135
143
|
- lib/rack/app/utils.rb
|
136
144
|
- lib/rack/app/utils/deep_dup.rb
|
145
|
+
- lib/rack/app/utils/parser.rb
|
146
|
+
- lib/rack/app/utils/parser/boolean.rb
|
147
|
+
- lib/rack/app/utils/parser/custom.rb
|
148
|
+
- lib/rack/app/utils/parser/date.rb
|
149
|
+
- lib/rack/app/utils/parser/date_time.rb
|
150
|
+
- lib/rack/app/utils/parser/float.rb
|
151
|
+
- lib/rack/app/utils/parser/integer.rb
|
152
|
+
- lib/rack/app/utils/parser/numeric.rb
|
153
|
+
- lib/rack/app/utils/parser/string.rb
|
154
|
+
- lib/rack/app/utils/parser/time.rb
|
137
155
|
- lib/rack/app/version.rb
|
138
156
|
- rack-app.gemspec
|
157
|
+
- src/Net__HTTP Cheat Sheet.pdf
|
139
158
|
homepage: http://www.rack-app.com/
|
140
159
|
licenses:
|
141
160
|
- Apache License 2.0
|