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 CHANGED
@@ -45,12 +45,12 @@ end
45
45
  # run Dzl::Examples::FunWithScopes
46
46
  # end
47
47
 
48
- # require 'dzl/examples/fun_with_hashes'
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::FunWithPblocks
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 && !type_opts.try_keys(:validator).is_a?(HashValidator)
47
- raise ArgumentError.new("Must pass :validator, an instance of HashValidator")
48
- elsif type == Hash && block_given?
49
- type_opts[:validator].instance_exec(&Proc.new)
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
- if @subject.params[name]
7
- # Don't clobber params we already know about
8
- @subject.params[name].overwrite_opts(opts)
9
- else
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 = @params[pname].validate(parandidates[parandidate_key][pname], {
21
- preformatted: request.preformatted_keys.include?(pname)
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
@@ -24,6 +24,10 @@ class Dzl::DSLSubjects::Router < Dzl::DSLSubject
24
24
  @stack.pop
25
25
  end
26
26
 
27
+ def __evil_subject_pop
28
+ @stack.pop
29
+ end
30
+
27
31
  def subject
28
32
  @stack.last
29
33
  end
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, validator: HashValidator.new do
7
- required :str
8
- required :ary do
9
- type Array
10
- allowed_values [3, 5, 7]
11
- end
6
+ type Hash
7
+ required :str
8
+ required :ary do
9
+ type Array
10
+ allowed_values [3, 5, 7]
11
+ end
12
12
 
13
- required :nest do
14
- type Hash
15
- required(:int) { type Fixnum }
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
@@ -89,8 +89,9 @@ module Dzl::RackInterface
89
89
  end
90
90
 
91
91
  def log_request(request, response, seconds)
92
- logger.info "#{request.request_method} #{request.path}"
93
- logger.info "PARAMS: #{request.params}"
94
- logger.info "#{response[0]} in #{seconds * 1000}ms"
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(@request_body).recursively_symbolize_keys!
72
+ JSON.parse(body).recursively_symbolize_keys!
69
73
  else
70
74
  {}
71
75
  end
data/lib/dzl/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dzl
2
- VERSION = "1.0.0.rc3"
2
+ VERSION = "1.0.0.rc4"
3
3
  end
@@ -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(k, opts = {}, &block)
104
- key(k, opts.merge({required: false}), &block)
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(k, opts = {}, &block)
108
- key(k, opts.merge({required: true}), &block)
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
- }.to raise_exception(ArgumentError)
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
- }.to_not raise_exception(ArgumentError)
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.rc3
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-16 00:00:00.000000000 Z
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: &70197974289540 !ruby/object:Gem::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: *70197974289540
25
+ version_requirements: *70105711904460
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: activesupport
28
- requirement: &70197974289020 !ruby/object:Gem::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: *70197974289020
36
+ version_requirements: *70105711903860
37
37
  description: Small, fast racktivesupport web framework with handy DSL and explicit
38
38
  parameter validation.
39
39
  email: