dzl 1.0.0.rc9 → 1.0.0.rc10
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.
- data/config.ru +8 -8
- data/lib/dzl/core_ext.rb +12 -2
- data/lib/dzl/dsl_proxies/parameter.rb +0 -4
- data/lib/dzl/dsl_proxies/protection.rb +5 -1
- data/lib/dzl/dsl_subjects/parameter_block.rb +3 -0
- data/lib/dzl/dsl_subjects/protection.rb +25 -7
- data/lib/dzl/dsl_subjects/router.rb +6 -1
- data/lib/dzl/examples/fun_with_hashes.rb +29 -0
- data/lib/dzl/examples/fun_with_params.rb +6 -0
- data/lib/dzl/rack_interface.rb +11 -1
- data/lib/dzl/version.rb +1 -1
- data/lib/hash_validator/hash_validator.rb +10 -1
- data/spec/fun_with_hashes_spec.rb +45 -10
- data/spec/fun_with_params_spec.rb +17 -0
- data/spec/hash_validator_spec.rb +34 -0
- metadata +5 -5
data/config.ru
CHANGED
@@ -30,10 +30,10 @@ end
|
|
30
30
|
# run Dzl::Examples::RouteProfile
|
31
31
|
# end
|
32
32
|
|
33
|
-
require 'dzl/examples/fun_with_handlers'
|
34
|
-
map '/' do
|
35
|
-
|
36
|
-
end
|
33
|
+
# require 'dzl/examples/fun_with_handlers'
|
34
|
+
# map '/' do
|
35
|
+
# run Dzl::Examples::FunWithHandlers
|
36
|
+
# end
|
37
37
|
|
38
38
|
# require 'dzl/examples/fun_with_hooks'
|
39
39
|
# map '/' do
|
@@ -45,10 +45,10 @@ end
|
|
45
45
|
# run Dzl::Examples::FunWithScopes
|
46
46
|
# end
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
require 'dzl/examples/fun_with_hashes'
|
49
|
+
map '/' do
|
50
|
+
run Dzl::Examples::FunWithHashes
|
51
|
+
end
|
52
52
|
|
53
53
|
# require 'dzl/examples/fun_with_pblocks'
|
54
54
|
# map '/' do
|
data/lib/dzl/core_ext.rb
CHANGED
@@ -6,10 +6,20 @@ class Hash
|
|
6
6
|
def recursively_symbolize_keys!
|
7
7
|
self.symbolize_keys!
|
8
8
|
self.values.each do |v|
|
9
|
-
if v.is_a?(Hash)
|
9
|
+
if v.is_a?(Hash) || v.is_a?(Array)
|
10
10
|
v.recursively_symbolize_keys!
|
11
11
|
end
|
12
12
|
end
|
13
13
|
self
|
14
14
|
end
|
15
|
-
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Array
|
18
|
+
def recursively_symbolize_keys!
|
19
|
+
self.each do |item|
|
20
|
+
if item.is_a?(Hash) || item.is_a?(Array)
|
21
|
+
item.recursively_symbolize_keys!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -48,10 +48,6 @@ class Dzl::DSLProxies::Parameter < Dzl::DSLProxy
|
|
48
48
|
end
|
49
49
|
|
50
50
|
if type == Hash
|
51
|
-
unless @subject.router.subject(2).is_a?(Dzl::DSLSubjects::Endpoint)
|
52
|
-
raise Dzl::NYI.new("Hash parameters may only be specified in endpoints, for now")
|
53
|
-
end
|
54
|
-
|
55
51
|
raise Dzl::RetryBlockPlease.new(
|
56
52
|
subject: HashValidator.new(
|
57
53
|
@subject.opts.except(:type_opts).reverse_merge(format: :json)
|
@@ -3,4 +3,8 @@ class Dzl::DSLProxies::Protection < Dzl::DSLProxy
|
|
3
3
|
raise ArgumentError unless [:username, :password].all? {|k| opts[k].present?}
|
4
4
|
@subject.opts[:http_basic] = opts
|
5
5
|
end
|
6
|
-
|
6
|
+
def api_key(opts)
|
7
|
+
raise ArgumentError unless [:header, :valid_keys].all? {|k| opts[k].present?}
|
8
|
+
@subject.opts[:api_key] = opts
|
9
|
+
end
|
10
|
+
end
|
@@ -21,6 +21,9 @@ class Dzl::DSLSubjects::ParameterBlock < Dzl::DSLSubject
|
|
21
21
|
if param.opts[:type] == Hash
|
22
22
|
param = if parandidate.nil? && param.opts[:required]
|
23
23
|
Dzl::ValueOrError.new(e: param.opts[:header] ? :missing_required_header : :missing_required_param)
|
24
|
+
elsif parandidate.nil?
|
25
|
+
# TODO HashValidator with default values
|
26
|
+
Dzl::ValueOrError.new(v: :__no_value__)
|
24
27
|
else
|
25
28
|
unless parandidate.is_a?(Hash)
|
26
29
|
if param.opts[:format] == :json
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'dzl/dsl_proxies/protection'
|
2
2
|
|
3
3
|
class Dzl::RespondWithHTTPBasicChallenge < StandardError; end
|
4
|
+
class Dzl::RespondWithInvalidAPIKey < StandardError; end
|
4
5
|
|
5
6
|
class Dzl::DSLSubjects::Protection < Dzl::DSLSubject
|
6
7
|
def initialize
|
@@ -15,17 +16,34 @@ class Dzl::DSLSubjects::Protection < Dzl::DSLSubject
|
|
15
16
|
if @opts[:http_basic].present?
|
16
17
|
@auth = Rack::Auth::Basic::Request.new(request.env)
|
17
18
|
if @auth.provided? && @auth.basic? && @auth.credentials
|
19
|
+
# Invalid basic auth credentials
|
18
20
|
unless @auth.credentials[0] == @opts[:http_basic][:username] &&
|
19
21
|
@auth.credentials[1] == @opts[:http_basic][:password]
|
20
|
-
Dzl::ValueOrError.new(e: :invalid_http_basic_credentials)
|
21
|
-
else
|
22
|
-
Dzl::ValueOrError.new(v: nil)
|
22
|
+
return Dzl::ValueOrError.new(e: :invalid_http_basic_credentials)
|
23
23
|
end
|
24
|
+
# No basic auth credentials provided
|
24
25
|
else
|
25
|
-
Dzl::ValueOrError.new(e: :no_http_basic_credentials)
|
26
|
+
return Dzl::ValueOrError.new(e: :no_http_basic_credentials)
|
26
27
|
end
|
27
|
-
else
|
28
|
-
Dzl::ValueOrError.new(v: nil)
|
29
28
|
end
|
29
|
+
|
30
|
+
if @opts[:api_key].present?
|
31
|
+
api_key_header = @opts[:api_key][:header]
|
32
|
+
allowed_keys = @opts[:api_key][:valid_keys]
|
33
|
+
request_key = request.headers[api_key_header]
|
34
|
+
|
35
|
+
if request_key
|
36
|
+
# Invalid API key provided
|
37
|
+
unless allowed_keys.include? request_key
|
38
|
+
return Dzl::ValueOrError.new(e: :invalid_api_key)
|
39
|
+
end
|
40
|
+
# No API key provided
|
41
|
+
else
|
42
|
+
return Dzl::ValueOrError.new(e: :no_api_key)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Auth passed
|
47
|
+
return Dzl::ValueOrError.new(v: nil)
|
30
48
|
end
|
31
|
-
end
|
49
|
+
end
|
@@ -99,7 +99,12 @@ class Dzl::DSLSubjects::Router < Dzl::DSLSubject
|
|
99
99
|
raise Dzl::RespondWithHTTPBasicChallenge
|
100
100
|
end
|
101
101
|
|
102
|
+
if !errors.empty? &&
|
103
|
+
errors.values.any? {|v| v == :no_api_key || v == :invalid_api_key}
|
104
|
+
raise Dzl::RespondWithInvalidAPIKey
|
105
|
+
end
|
106
|
+
|
102
107
|
endpoint || raise(Dzl::NotFound.new(errors))
|
103
108
|
end
|
104
109
|
|
105
|
-
end
|
110
|
+
end
|
@@ -28,4 +28,33 @@ class Dzl::Examples::FunWithHashes < Dzl::Examples::Base
|
|
28
28
|
allowed_values %w{one two three}
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
pblock :ingredients do
|
33
|
+
required :ingredients do
|
34
|
+
type Hash
|
35
|
+
required :cheese, :meat, :bread
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
post '/boring_sandwich' do
|
40
|
+
import_pblock :ingredients
|
41
|
+
end
|
42
|
+
|
43
|
+
post '/awesome_sandwich' do
|
44
|
+
import_pblock :ingredients
|
45
|
+
parameter :ingredients do
|
46
|
+
required(:meat) { type Array }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
post '/another_boring_sandwich' do
|
51
|
+
import_pblock :ingredients
|
52
|
+
end
|
53
|
+
|
54
|
+
post '/elis_bug' do
|
55
|
+
optional :hash do
|
56
|
+
type Hash
|
57
|
+
optional(:id) { type Fixnum }
|
58
|
+
end
|
59
|
+
end
|
31
60
|
end
|
data/lib/dzl/rack_interface.rb
CHANGED
@@ -15,6 +15,8 @@ module Dzl::RackInterface
|
|
15
15
|
[__router.handle_request(request), nil]
|
16
16
|
rescue Dzl::RespondWithHTTPBasicChallenge
|
17
17
|
[respond_with_http_basic_challenge, nil]
|
18
|
+
rescue Dzl::RespondWithInvalidAPIKey
|
19
|
+
[respond_with_invalid_api_key, nil]
|
18
20
|
rescue Dzl::Error => e
|
19
21
|
[respond_with_dzl_error_handler(e), nil]
|
20
22
|
rescue StandardError => e
|
@@ -51,6 +53,14 @@ module Dzl::RackInterface
|
|
51
53
|
response.finish
|
52
54
|
end
|
53
55
|
|
56
|
+
def respond_with_invalid_api_key
|
57
|
+
response = Rack::Response.new
|
58
|
+
response.status = 401
|
59
|
+
response.headers['Content-Type'] = 'text/html'
|
60
|
+
response.write("Not Authorized\n")
|
61
|
+
response.finish
|
62
|
+
end
|
63
|
+
|
54
64
|
def respond_with_standard_error_handler(e)
|
55
65
|
response = Rack::Response.new
|
56
66
|
response.headers['Content-Type'] = 'application/json'
|
@@ -108,4 +118,4 @@ module Dzl::RackInterface
|
|
108
118
|
end
|
109
119
|
end
|
110
120
|
end
|
111
|
-
end
|
121
|
+
end
|
data/lib/dzl/version.rb
CHANGED
@@ -24,6 +24,16 @@ class HashValidator
|
|
24
24
|
@template[:opts]
|
25
25
|
end
|
26
26
|
|
27
|
+
def overwrite_opts(opts)
|
28
|
+
@template[:opts].merge(opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
def clone
|
32
|
+
copy = HashValidator.new
|
33
|
+
copy.instance_variable_set(:@template, Marshal.load(Marshal.dump(@template)))
|
34
|
+
copy
|
35
|
+
end
|
36
|
+
|
27
37
|
def valid?(hsh)
|
28
38
|
return false unless hsh.keys.all? { |key| top[:keys].include?(key) }
|
29
39
|
|
@@ -71,7 +81,6 @@ class HashValidator
|
|
71
81
|
def key(k, opts, &block)
|
72
82
|
top[:keys][k] = {
|
73
83
|
opts: opts,
|
74
|
-
block: block,
|
75
84
|
keys: {}
|
76
85
|
}
|
77
86
|
|
@@ -29,16 +29,6 @@ describe 'hash parameters' do
|
|
29
29
|
}.to raise_exception(Dzl::Deprecated)
|
30
30
|
end
|
31
31
|
|
32
|
-
specify 'NYI: hash parameters are currently only allowed in anonymous pblocks' do
|
33
|
-
expect {
|
34
|
-
class T1 < Dzl::Examples::Base
|
35
|
-
pblock :broken do
|
36
|
-
required(:foo) { type Hash }
|
37
|
-
end
|
38
|
-
end
|
39
|
-
}.to raise_error(Dzl::NYI)
|
40
|
-
end
|
41
|
-
|
42
32
|
specify 'hash parameters retry their blocks against a hash validator' do
|
43
33
|
HashValidator.any_instance.should_receive(:key).with(:key, {required: true, type: String})
|
44
34
|
class T1 < Dzl::Examples::Base
|
@@ -86,6 +76,38 @@ describe 'hash parameters' do
|
|
86
76
|
end
|
87
77
|
end
|
88
78
|
|
79
|
+
context 'importing & reopening' do
|
80
|
+
specify 'works, basically' do
|
81
|
+
boring_sandwich = {
|
82
|
+
bread: 'multi-grain',
|
83
|
+
meat: 'roast beast',
|
84
|
+
cheese: 'swiss'
|
85
|
+
}
|
86
|
+
|
87
|
+
awesome_sandwich = {
|
88
|
+
bread: 'sourdough',
|
89
|
+
meat: ['roast beast', 'salami', 'turkey'],
|
90
|
+
cheese: 'cheddar'
|
91
|
+
}
|
92
|
+
|
93
|
+
post('/boring_sandwich', {ingredients: boring_sandwich.to_json}) do |response|
|
94
|
+
response.status.should == 200
|
95
|
+
end
|
96
|
+
|
97
|
+
post('/awesome_sandwich', {ingredients: awesome_sandwich.to_json}) do |response|
|
98
|
+
response.status.should == 200
|
99
|
+
end
|
100
|
+
|
101
|
+
post('/another_boring_sandwich', {ingredients: awesome_sandwich.to_json}) do |response|
|
102
|
+
response.status.should == 404
|
103
|
+
end
|
104
|
+
|
105
|
+
post('/another_boring_sandwich', {ingredients: boring_sandwich.to_json}) do |response|
|
106
|
+
response.status.should == 200
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
89
111
|
context 'validate' do
|
90
112
|
specify 'JSON post bodies' do
|
91
113
|
header 'Content-Type', 'application/json'
|
@@ -153,4 +175,17 @@ describe 'hash parameters' do
|
|
153
175
|
get('/mixed', valid).status.should == 200
|
154
176
|
end
|
155
177
|
end
|
178
|
+
|
179
|
+
context "eli's bug" do
|
180
|
+
specify 'omitted optional hashes are OK' do
|
181
|
+
post('/elis_bug', {hash: {id: 1}.to_json}) do |response|
|
182
|
+
response.status.should == 200
|
183
|
+
end
|
184
|
+
|
185
|
+
post('/elis_bug') do |response|
|
186
|
+
JSON.parse(response.body)['errors'].should == nil
|
187
|
+
response.status.should == 200
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
156
191
|
end
|
@@ -172,6 +172,23 @@ describe Dzl::Examples::FunWithParams do
|
|
172
172
|
end
|
173
173
|
end
|
174
174
|
|
175
|
+
describe '/api' do
|
176
|
+
it 'should 401 if no api key provided' do
|
177
|
+
get '/api'
|
178
|
+
last_response.status.should == 401
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should 401 if invalid api key provided' do
|
182
|
+
get '/api', {}, {"HTTP_X_API_KEY" => 'invalid-key'}
|
183
|
+
last_response.status.should == 401
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'should accept valid api key' do
|
187
|
+
get '/api', {}, {"HTTP_X_API_KEY" => 'valid-key'}
|
188
|
+
last_response.status.should == 200
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
175
192
|
describe '/arithmetic' do
|
176
193
|
it 'should not allow :int < 5' do
|
177
194
|
get('/arithmetic', {int: 4}) do |response|
|
data/spec/hash_validator_spec.rb
CHANGED
@@ -280,4 +280,38 @@ describe HashValidator do
|
|
280
280
|
v.valid?({foo: 'three'}).should == true
|
281
281
|
end
|
282
282
|
end
|
283
|
+
|
284
|
+
context 'cloning' do
|
285
|
+
before(:each) do
|
286
|
+
@original = HashValidator.new do
|
287
|
+
required(:foo)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
specify 'dupes the data in @template' do
|
292
|
+
orig_template = @original.instance_variable_get(:@template)
|
293
|
+
|
294
|
+
cloned = @original.clone
|
295
|
+
|
296
|
+
@original.instance_variable_get(:@template).object_id.should == orig_template.object_id
|
297
|
+
@original.instance_variable_get(:@template).object_id.should_not == cloned.instance_variable_get(:@template).object_id
|
298
|
+
@original.instance_variable_get(:@template).should == cloned.instance_variable_get(:@template)
|
299
|
+
|
300
|
+
cloned.required(:foo) { type Fixnum }
|
301
|
+
|
302
|
+
@original.instance_variable_get(:@template).should_not == cloned.instance_variable_get(:@template)
|
303
|
+
@original.instance_variable_get(:@template)[:keys][:foo][:opts][:type].should == String
|
304
|
+
cloned.instance_variable_get(:@template)[:keys][:foo][:opts][:type].should == Fixnum
|
305
|
+
end
|
306
|
+
|
307
|
+
specify 'validation works as expected' do
|
308
|
+
cloned = @original.clone
|
309
|
+
cloned.required(:foo) { type Fixnum }
|
310
|
+
|
311
|
+
@original.valid?({foo: 'hello'}).should == true
|
312
|
+
@original.valid?({foo: 1}).should == false
|
313
|
+
cloned.valid?({foo: 'hello'}).should == false
|
314
|
+
cloned.valid?({foo: 1}).should == true
|
315
|
+
end
|
316
|
+
end
|
283
317
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dzl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc10
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -14,7 +14,7 @@ date: 2012-05-02 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rack
|
17
|
-
requirement: &
|
17
|
+
requirement: &70114952344160 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: 1.4.1
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70114952344160
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: activesupport
|
28
|
-
requirement: &
|
28
|
+
requirement: &70114952343660 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
version: 3.2.2
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70114952343660
|
37
37
|
description: Small, fast racktivesupport web framework with handy DSL and explicit
|
38
38
|
parameter validation.
|
39
39
|
email:
|