dzl 1.0.0.rc3 → 1.0.0.rc4
Sign up to get free protection for your applications and to get access to all the features.
- data/config.ru +8 -8
- data/lib/dzl/dsl_proxies/parameter.rb +10 -4
- data/lib/dzl/dsl_proxies/parameter_block.rb +17 -8
- data/lib/dzl/dsl_subjects/parameter_block.rb +24 -3
- data/lib/dzl/dsl_subjects/router.rb +4 -0
- data/lib/dzl/errors.rb +8 -1
- data/lib/dzl/examples/fun_with_hashes.rb +21 -10
- data/lib/dzl/rack_interface.rb +4 -3
- data/lib/dzl/request.rb +6 -2
- data/lib/dzl/version.rb +1 -1
- data/lib/hash_validator/hash_validator.rb +21 -6
- data/spec/fun_with_hashes_spec.rb +78 -3
- metadata +6 -6
data/config.ru
CHANGED
@@ -45,12 +45,12 @@ end
|
|
45
45
|
# run Dzl::Examples::FunWithScopes
|
46
46
|
# end
|
47
47
|
|
48
|
-
|
49
|
-
# map '/' do
|
50
|
-
# run Dzl::Examples::FunWithHashes
|
51
|
-
# end
|
52
|
-
|
53
|
-
require 'dzl/examples/fun_with_pblocks'
|
48
|
+
require 'dzl/examples/fun_with_hashes'
|
54
49
|
map '/' do
|
55
|
-
run Dzl::Examples::
|
56
|
-
end
|
50
|
+
run Dzl::Examples::FunWithHashes
|
51
|
+
end
|
52
|
+
|
53
|
+
# require 'dzl/examples/fun_with_pblocks'
|
54
|
+
# map '/' do
|
55
|
+
# run Dzl::Examples::FunWithPblocks
|
56
|
+
# end
|
@@ -43,10 +43,16 @@ class Dzl::DSLProxies::Parameter < Dzl::DSLProxy
|
|
43
43
|
@subject.validations[:type] = type
|
44
44
|
@subject.opts[:type_opts] = type_opts
|
45
45
|
|
46
|
-
if type == Hash &&
|
47
|
-
raise
|
48
|
-
|
49
|
-
|
46
|
+
if type == Hash && type_opts.has_key?(:validator)
|
47
|
+
raise Dzl::Deprecated.new("The hash validator syntax is no longer supported.")
|
48
|
+
end
|
49
|
+
|
50
|
+
if type == Hash
|
51
|
+
raise Dzl::RetryBlockPlease.new(
|
52
|
+
subject: HashValidator.new(
|
53
|
+
@subject.opts.except(:type_opts).reverse_merge(format: :json)
|
54
|
+
)
|
55
|
+
)
|
50
56
|
end
|
51
57
|
end
|
52
58
|
|
@@ -1,19 +1,28 @@
|
|
1
1
|
class Dzl::DSLProxies::ParameterBlock < Dzl::DSLProxy
|
2
|
-
def parameter(*names)
|
2
|
+
def parameter(*names, &block)
|
3
3
|
opts = names.last.is_a?(Hash) ? names.pop : {required: false}
|
4
4
|
|
5
5
|
names.each do |name|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@subject.params[name] = Dzl::DSLSubjects::Parameter.new(name, opts, @subject.router)
|
11
|
-
end
|
6
|
+
single_parameter(name, opts, &block)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
alias_method :param, :parameter
|
12
10
|
|
11
|
+
def single_parameter(name, opts, &block)
|
12
|
+
if @subject.params[name] && !opts[:retry]
|
13
|
+
# Don't clobber params we already know about
|
14
|
+
@subject.params[name].overwrite_opts(opts)
|
15
|
+
else
|
16
|
+
@subject.params[name] = opts[:subject] || Dzl::DSLSubjects::Parameter.new(name, opts, @subject.router)
|
17
|
+
end
|
18
|
+
|
19
|
+
begin
|
13
20
|
@subject.router.call_with_subject(Proc.new, @subject.params[name]) if block_given?
|
21
|
+
rescue Dzl::RetryBlockPlease => e
|
22
|
+
@subject.router.__evil_subject_pop
|
23
|
+
single_parameter(name, opts.merge(retry: true, subject: e[:subject]), &block)
|
14
24
|
end
|
15
25
|
end
|
16
|
-
alias_method :param, :parameter
|
17
26
|
|
18
27
|
def required(*names, &block)
|
19
28
|
opts = names.last.is_a?(Hash) ? names.pop : {}
|
@@ -16,10 +16,31 @@ class Dzl::DSLSubjects::ParameterBlock < Dzl::DSLSubject
|
|
16
16
|
errors = @params.each_with_object({}) do |pary, errors|
|
17
17
|
pname, param = pary
|
18
18
|
parandidate_key = param.opts[:header] ? :headers : :params
|
19
|
+
parandidate = parandidates[parandidate_key][pname]
|
19
20
|
|
20
|
-
param
|
21
|
-
|
22
|
-
|
21
|
+
if param.opts[:type] == Hash
|
22
|
+
param = if parandidate.nil? && param.opts[:required]
|
23
|
+
Dzl::ValueOrError.new(e: param.opts[:header] ? :missing_required_header : :missing_required_param)
|
24
|
+
else
|
25
|
+
unless parandidate.is_a?(Hash)
|
26
|
+
if param.opts[:format] == :json
|
27
|
+
parandidate = JSON.parse(parandidate).recursively_symbolize_keys! rescue nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
if parandidate.nil?
|
32
|
+
Dzl::ValueOrError.new(e: :type_conversion_failed)
|
33
|
+
elsif param.valid?(parandidate)
|
34
|
+
Dzl::ValueOrError.new(v: parandidate)
|
35
|
+
else
|
36
|
+
Dzl::ValueOrError.new(e: :hash_validation_failed)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
else
|
40
|
+
param = param.validate(parandidate, {
|
41
|
+
preformatted: request.preformatted_keys.include?(pname)
|
42
|
+
})
|
43
|
+
end
|
23
44
|
|
24
45
|
if param.error?
|
25
46
|
errors[pname] = param.error
|
data/lib/dzl/errors.rb
CHANGED
@@ -6,6 +6,10 @@ class Dzl::Error < StandardError
|
|
6
6
|
@status = 500
|
7
7
|
end
|
8
8
|
|
9
|
+
def [](key)
|
10
|
+
@data[key]
|
11
|
+
end
|
12
|
+
|
9
13
|
def to_json
|
10
14
|
{
|
11
15
|
status: @status,
|
@@ -30,4 +34,7 @@ class Dzl::BadRequest < Dzl::RequestError
|
|
30
34
|
super(data)
|
31
35
|
@status = 400
|
32
36
|
end
|
33
|
-
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Dzl::RetryBlockPlease < Dzl::Error; end
|
40
|
+
class Dzl::Deprecated < Dzl::Error; end
|
@@ -3,18 +3,29 @@ require 'dzl/examples/base'
|
|
3
3
|
class Dzl::Examples::FunWithHashes < Dzl::Examples::Base
|
4
4
|
post '/h' do
|
5
5
|
required :foo do
|
6
|
-
type Hash
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
type Hash
|
7
|
+
required :str
|
8
|
+
required :ary do
|
9
|
+
type Array
|
10
|
+
allowed_values [3, 5, 7]
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
13
|
+
required :nest do
|
14
|
+
type Hash
|
15
|
+
required(:int) { type Fixnum }
|
17
16
|
end
|
18
17
|
end
|
19
18
|
end
|
19
|
+
|
20
|
+
post '/mixed', :get do
|
21
|
+
required :hsh do
|
22
|
+
type Hash
|
23
|
+
required :zim, :zam
|
24
|
+
end
|
25
|
+
|
26
|
+
required :ary do
|
27
|
+
type Array, separator: '|'
|
28
|
+
allowed_values %w{one two three}
|
29
|
+
end
|
30
|
+
end
|
20
31
|
end
|
data/lib/dzl/rack_interface.rb
CHANGED
@@ -89,8 +89,9 @@ module Dzl::RackInterface
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def log_request(request, response, seconds)
|
92
|
-
logger.info
|
93
|
-
logger.info
|
94
|
-
logger.
|
92
|
+
logger.info "#{request.request_method} #{request.path}"
|
93
|
+
logger.info "PARAMS: #{request.params}"
|
94
|
+
logger.debug "BODY: #{request.body}" unless request.body.blank?
|
95
|
+
logger.info "#{response[0]} in #{seconds * 1000}ms"
|
95
96
|
end
|
96
97
|
end
|
data/lib/dzl/request.rb
CHANGED
@@ -20,9 +20,13 @@ class Dzl::Request < Rack::Request
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
alias_method :orig_body, :body
|
24
|
+
def body
|
25
|
+
@request_body ||= orig_body.read
|
26
|
+
end
|
27
|
+
|
23
28
|
def params
|
24
29
|
@params ||= begin
|
25
|
-
@request_body = body.read
|
26
30
|
unless preformatted_params.empty?
|
27
31
|
@preformatted_keys = preformatted_params.keys
|
28
32
|
end
|
@@ -65,7 +69,7 @@ class Dzl::Request < Rack::Request
|
|
65
69
|
def preformatted_params
|
66
70
|
@preformatted_params ||= begin
|
67
71
|
if content_type == "application/json"
|
68
|
-
JSON.parse(
|
72
|
+
JSON.parse(body).recursively_symbolize_keys!
|
69
73
|
else
|
70
74
|
{}
|
71
75
|
end
|
data/lib/dzl/version.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
class HashValidator
|
2
|
+
attr_reader :dsl_proxy
|
3
|
+
|
2
4
|
class << self
|
3
5
|
alias_method :orig_new, :new
|
4
6
|
def new(*args)
|
@@ -8,15 +10,20 @@ class HashValidator
|
|
8
10
|
end
|
9
11
|
end
|
10
12
|
|
11
|
-
def initialize
|
13
|
+
def initialize(opts = {})
|
12
14
|
@template = {
|
13
|
-
keys: {}
|
15
|
+
keys: {},
|
16
|
+
opts: opts
|
14
17
|
}
|
15
18
|
|
16
19
|
@key_stack = []
|
17
20
|
@dsl_proxy = DSLProxy.new(self)
|
18
21
|
end
|
19
22
|
|
23
|
+
def opts
|
24
|
+
@template[:opts]
|
25
|
+
end
|
26
|
+
|
20
27
|
def valid?(hsh)
|
21
28
|
top[:keys].each do |k, v|
|
22
29
|
if !hsh.has_key?(k)
|
@@ -88,6 +95,8 @@ class HashValidator
|
|
88
95
|
end
|
89
96
|
|
90
97
|
class DSLProxy
|
98
|
+
attr_reader :subject
|
99
|
+
|
91
100
|
def initialize(subject)
|
92
101
|
@subject = subject
|
93
102
|
end
|
@@ -100,12 +109,18 @@ class HashValidator
|
|
100
109
|
@subject.key(k, opts, &block)
|
101
110
|
end
|
102
111
|
|
103
|
-
def optional(
|
104
|
-
|
112
|
+
def optional(*args, &block)
|
113
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
114
|
+
args.each do |key|
|
115
|
+
key(key, opts.merge({required: false}), &block)
|
116
|
+
end
|
105
117
|
end
|
106
118
|
|
107
|
-
def required(
|
108
|
-
|
119
|
+
def required(*args, &block)
|
120
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
121
|
+
args.each do |key|
|
122
|
+
key(key, opts.merge({required: true}), &block)
|
123
|
+
end
|
109
124
|
end
|
110
125
|
|
111
126
|
def type(klass)
|
@@ -7,7 +7,7 @@ describe 'hash parameters' do
|
|
7
7
|
def app; Dzl::Examples::FunWithHashes; end
|
8
8
|
|
9
9
|
context 'DSL' do
|
10
|
-
specify 'hash parameters must have a validator, for now' do
|
10
|
+
specify 'DEPRECATED: hash parameters must have a validator, for now' do
|
11
11
|
expect {
|
12
12
|
class T1 < Dzl::Examples::Base
|
13
13
|
get '/foo' do
|
@@ -16,7 +16,7 @@ describe 'hash parameters' do
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
19
|
-
}.
|
19
|
+
}.to_not raise_exception(ArgumentError)
|
20
20
|
|
21
21
|
expect {
|
22
22
|
class T1 < Dzl::Examples::Base
|
@@ -26,7 +26,53 @@ describe 'hash parameters' do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
|
-
}.
|
29
|
+
}.to raise_exception(Dzl::Deprecated)
|
30
|
+
end
|
31
|
+
|
32
|
+
specify 'hash parameters retry their blocks against a hash validator' do
|
33
|
+
HashValidator.any_instance.should_receive(:key).with(:key, {required: true, type: String})
|
34
|
+
class T1 < Dzl::Examples::Base
|
35
|
+
get '/foo' do
|
36
|
+
required :bar do
|
37
|
+
type Hash
|
38
|
+
required(:key) do
|
39
|
+
type Array
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
specify 'hash validator is built properly for required params' do
|
47
|
+
class T1 < Dzl::Examples::Base
|
48
|
+
get '/foo' do
|
49
|
+
required :bar do
|
50
|
+
type Hash
|
51
|
+
self.subject.opts.should == {type: Hash, required: true, format: :json}
|
52
|
+
|
53
|
+
required(:key) do
|
54
|
+
self.subject.should_receive(:add_option).with(:type, Array)
|
55
|
+
type Array
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
specify 'nested hashes do not retry their blocks against a new hash validator' do
|
63
|
+
class T1 < Dzl::Examples::Base
|
64
|
+
get '/foo' do
|
65
|
+
required :bar do
|
66
|
+
type Hash
|
67
|
+
HashValidator.any_instance.should_receive(:key)
|
68
|
+
required(:nested) do
|
69
|
+
HashValidator.any_instance.should_not_receive(:new)
|
70
|
+
HashValidator.any_instance.should_receive(:add_option).with(:type, Hash)
|
71
|
+
type Hash
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
30
76
|
end
|
31
77
|
end
|
32
78
|
|
@@ -68,4 +114,33 @@ describe 'hash parameters' do
|
|
68
114
|
post('/h', invalid_body.to_json).status.should == 404
|
69
115
|
end
|
70
116
|
end
|
117
|
+
|
118
|
+
context 'mixed input' do
|
119
|
+
specify 'is fine' do
|
120
|
+
valid = {
|
121
|
+
hsh: {
|
122
|
+
zim: 'ok',
|
123
|
+
zam: 'sweet'
|
124
|
+
}.to_json,
|
125
|
+
ary: ['one', 'three'].join('|')
|
126
|
+
}
|
127
|
+
|
128
|
+
get('/mixed', valid).status.should == 200
|
129
|
+
JSON.parse(get('/mixed', valid.except(:ary)).body)['errors']['/mixed'].should == {
|
130
|
+
'ary' => 'missing_required_param'
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
specify 'hashes can have & and = and ? in them' do
|
135
|
+
valid = {
|
136
|
+
hsh: {
|
137
|
+
zim: 'ok?',
|
138
|
+
zam: 'swe&et=omg'
|
139
|
+
}.to_json,
|
140
|
+
ary: ['one', 'three'].join('|')
|
141
|
+
}
|
142
|
+
|
143
|
+
get('/mixed', valid).status.should == 200
|
144
|
+
end
|
145
|
+
end
|
71
146
|
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.rc4
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-04-
|
13
|
+
date: 2012-04-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rack
|
17
|
-
requirement: &
|
17
|
+
requirement: &70105711904460 !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: *70105711904460
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: activesupport
|
28
|
-
requirement: &
|
28
|
+
requirement: &70105711903860 !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: *70105711903860
|
37
37
|
description: Small, fast racktivesupport web framework with handy DSL and explicit
|
38
38
|
parameter validation.
|
39
39
|
email:
|