rack-app 3.6.0 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.rubocop.yml +2 -0
  4. data/.travis.yml +2 -0
  5. data/Gemfile +5 -1
  6. data/README.md +29 -18
  7. data/VERSION +1 -1
  8. data/lib/rack/app/constants.rb +4 -2
  9. data/lib/rack/app/instance_methods/core.rb +5 -2
  10. data/lib/rack/app/middlewares.rb +2 -1
  11. data/lib/rack/app/middlewares/params.rb +6 -0
  12. data/lib/rack/app/middlewares/params/definition.rb +22 -0
  13. data/lib/rack/app/middlewares/params/definition/options.rb +45 -0
  14. data/lib/rack/app/middlewares/params/parser.rb +40 -0
  15. data/lib/rack/app/middlewares/params/setter.rb +19 -0
  16. data/lib/rack/app/middlewares/params/validator.rb +102 -0
  17. data/lib/rack/app/params.rb +6 -2
  18. data/lib/rack/app/router.rb +11 -6
  19. data/lib/rack/app/router/base.rb +4 -4
  20. data/lib/rack/app/singleton_methods.rb +3 -1
  21. data/lib/rack/app/singleton_methods/http_methods.rb +4 -1
  22. data/lib/rack/app/singleton_methods/mounting.rb +14 -15
  23. data/lib/rack/app/singleton_methods/params_validator.rb +12 -0
  24. data/lib/rack/app/singleton_methods/route_handling.rb +12 -6
  25. data/lib/rack/app/singleton_methods/settings.rb +7 -1
  26. data/lib/rack/app/test.rb +7 -6
  27. data/lib/rack/app/test/utils.rb +6 -31
  28. data/lib/rack/app/utils.rb +2 -1
  29. data/lib/rack/app/utils/parser.rb +45 -0
  30. data/lib/rack/app/utils/parser/boolean.rb +30 -0
  31. data/lib/rack/app/utils/parser/custom.rb +16 -0
  32. data/lib/rack/app/utils/parser/date.rb +15 -0
  33. data/lib/rack/app/utils/parser/date_time.rb +18 -0
  34. data/lib/rack/app/utils/parser/float.rb +15 -0
  35. data/lib/rack/app/utils/parser/integer.rb +9 -0
  36. data/lib/rack/app/utils/parser/numeric.rb +26 -0
  37. data/lib/rack/app/utils/parser/string.rb +9 -0
  38. data/lib/rack/app/utils/parser/time.rb +15 -0
  39. data/src/Net__HTTP Cheat Sheet.pdf +0 -0
  40. metadata +21 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: daa93f45a998994d6b8185d95c868021905b191b
4
- data.tar.gz: 86656b162da4020cce261b1b74f9385e1e507bcc
3
+ metadata.gz: 6cf38306bf272547c7a780a6a6e92f7138d8ebe5
4
+ data.tar.gz: 05929c52596d77e1caca9c46895ade3aa6a5b36d
5
5
  SHA512:
6
- metadata.gz: 8c91d8539fcdf7e20ee8258b3a509a1b935cfb11ed32a18377a73114d7c854b4317620afc85603ce06b6034a49f90ee39cbb9c346969288304c00b4b407dd72a
7
- data.tar.gz: 43025f318e638dc90a3bbfb460a8730cd3c55b202e22d87b5c1a8c4ff828bd26dad111c7112d9a5996c43acfeec94c7f6ea3d3502c62608aa419263062cfed16
6
+ metadata.gz: acfe46345c8ac900aee026f9249f053e17252224354afc803732b0f3b878761c305a6dbc0e2c29054052e45ae004662d9380b82851559446dd7bbf74991752e5
7
+ data.tar.gz: b4f5c6d7ce3e35cc34b16626641ae10dabd369adca3606f48098f6aaaf9f1ed50f687ed75c2a3cb35e56346d43607d859a020d0fd8294fc950617ff053438f7a
data/.gitignore CHANGED
@@ -1 +1,3 @@
1
- /pkg/
1
+ /pkg/
2
+ Gemfile.lock
3
+ .vagrant/
@@ -0,0 +1,2 @@
1
+ Style/HashSyntax:
2
+ Enabled: false
@@ -12,6 +12,8 @@ rvm:
12
12
  - 2.0.0
13
13
  - 2.1.1
14
14
  - 2.1.2
15
+ - 2.2.2
16
+ - 2.3.1
15
17
 
16
18
  - jruby-18mode
17
19
  - jruby-19mode
data/Gemfile CHANGED
@@ -1,2 +1,6 @@
1
1
  source 'https://rubygems.org'
2
- gemspec
2
+ gemspec
3
+
4
+ if RUBY_VERSION < '2.2.2'
5
+ gem 'rack', '< 2.0.0'
6
+ end
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  ![Rack::App](http://rack-app-website.herokuapp.com/img/msruby_old.png)
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
- 3.6.0
1
+ 4.0.1
@@ -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
- end
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
- @__params__ ||= Rack::App::Params.new(request.env).to_hash
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
-
@@ -1,3 +1,4 @@
1
1
  module Rack::App::Middlewares
2
2
  require 'rack/app/middlewares/header_setter'
3
- end
3
+ require 'rack/app/middlewares/params'
4
+ end
@@ -0,0 +1,6 @@
1
+ module Rack::App::Middlewares::Params
2
+ require "rack/app/middlewares/params/definition"
3
+ require "rack/app/middlewares/params/parser"
4
+ require "rack/app/middlewares/params/setter"
5
+ require "rack/app/middlewares/params/validator"
6
+ end
@@ -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
@@ -2,7 +2,11 @@ require 'cgi'
2
2
  class Rack::App::Params
3
3
 
4
4
  def to_hash
5
- query_params.merge(request_path_params)
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
@@ -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[:description].to_s.length }.max
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[:description].to_s.ljust(wd2)
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, description, endpoint)
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, description, endpoint)
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[:description],
48
- endpoint[: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
@@ -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, description, endpoint)
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
- :description => description,
29
- :endpoint => endpoint
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!(endpoint[:request_method], new_request_path, endpoint[:description], endpoint[: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(api_class, properties={})
4
- unless api_class.is_a?(Class) and api_class <= Rack::App
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
- duplication = ::Rack::App::Utils.deep_dup(api_class)
9
- duplication.on_mounted.each do |on_mount|
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!(duplication.cli)
14
- merge_prop = {:namespaces => [@namespaces, properties[:to]].flatten}
15
- router.merge_router!(duplication.router, merge_prop)
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, @last_description, file_server)
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
- ::Rack::App::Constants::HTTP::ANY,
51
- Rack::App::Utils.join(@namespaces, options[:to], ::Rack::App::Constants::RACK_BASED_APPLICATION),
52
- @last_description,
53
- rack_based_app)
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 '/', endpoint_path
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
- @last_description = description_texts.join("\n")
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, @last_description, endpoint)
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
- end
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
@@ -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
- request = Rack::MockRequest.new(rack_app)
20
- env = Rack::App::Test::Utils.env_by(properties)
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
- constructors << __rack_app_constructor__ if defined?(__rack_app_constructor__) and __rack_app_constructor__.is_a?(Proc)
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
@@ -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, &block)
14
-
15
- app_class = subject_class.respond_to?(:call) ? subject_class : Rack::App
16
- app = Rack::App::Utils.deep_dup(app_class)
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 = str.to_s.dup
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
@@ -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,16 @@
1
+ class Rack::App::Utils::Parser::Custom
2
+ def initialize(type)
3
+ @type = type
4
+ end
5
+
6
+ def parse(str)
7
+ @type.parse(str)
8
+ end
9
+
10
+ def validate(str)
11
+ parse(str)
12
+ return true
13
+ rescue Exception
14
+ return false
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ require 'date'
2
+ class Rack::App::Utils::Parser::Date
3
+ def parse(str)
4
+ ::Date.parse(str)
5
+ end
6
+
7
+ def validate(str)
8
+ [
9
+ /^\d+-\d\d-\d\d$/,
10
+ /^\w+, \d+ \w+ \d+$/
11
+ ].any? do |regexp|
12
+ !!(str =~ regexp)
13
+ end
14
+ end
15
+ 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,15 @@
1
+ class Rack::App::Utils::Parser::Float
2
+ def parse(str)
3
+ str.to_f
4
+ end
5
+
6
+ def validate(str)
7
+ case str.to_s
8
+ when /^\d+\.\d+$/, /^\d+,\d+$/
9
+ return true
10
+ else
11
+ return false
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,9 @@
1
+ class Rack::App::Utils::Parser::Integer
2
+ def parse(str)
3
+ str.to_s.to_i
4
+ end
5
+
6
+ def validate(str)
7
+ !!(str =~ /^\d+$/)
8
+ end
9
+ 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,9 @@
1
+ class Rack::App::Utils::Parser::String
2
+ def parse(str)
3
+ str.to_s
4
+ end
5
+
6
+ def validate(str)
7
+ true
8
+ end
9
+ 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
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.6.0
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-04-21 00:00:00.000000000 Z
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