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 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: