dzl 1.0.0.rc3 → 1.0.0.rc4
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/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:
|