dzl 1.0.0.beta0

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.
Files changed (62) hide show
  1. data/.gitignore +6 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +9 -0
  5. data/README.md +44 -0
  6. data/Rakefile +15 -0
  7. data/config.ru +41 -0
  8. data/dzl.gemspec +21 -0
  9. data/lib/dzl/doc/endpoint_doc.rb +70 -0
  10. data/lib/dzl/doc/router_doc.rb +26 -0
  11. data/lib/dzl/doc/task.rb +5 -0
  12. data/lib/dzl/doc/templates/endpoint.erb +55 -0
  13. data/lib/dzl/doc/templates/home.erb +8 -0
  14. data/lib/dzl/doc.rb +58 -0
  15. data/lib/dzl/dsl_proxies/defaults.rb +10 -0
  16. data/lib/dzl/dsl_proxies/endpoint.rb +30 -0
  17. data/lib/dzl/dsl_proxies/parameter.rb +75 -0
  18. data/lib/dzl/dsl_proxies/parameter_block.rb +61 -0
  19. data/lib/dzl/dsl_proxies/protection.rb +6 -0
  20. data/lib/dzl/dsl_proxies/router.rb +62 -0
  21. data/lib/dzl/dsl_proxy.rb +8 -0
  22. data/lib/dzl/dsl_subject.rb +15 -0
  23. data/lib/dzl/dsl_subjects/defaults.rb +12 -0
  24. data/lib/dzl/dsl_subjects/endpoint.rb +79 -0
  25. data/lib/dzl/dsl_subjects/parameter/allowed_values.rb +60 -0
  26. data/lib/dzl/dsl_subjects/parameter/type_conversion.rb +59 -0
  27. data/lib/dzl/dsl_subjects/parameter.rb +100 -0
  28. data/lib/dzl/dsl_subjects/parameter_block.rb +64 -0
  29. data/lib/dzl/dsl_subjects/protection.rb +31 -0
  30. data/lib/dzl/dsl_subjects/router.rb +87 -0
  31. data/lib/dzl/errors.rb +33 -0
  32. data/lib/dzl/examples/base.rb +9 -0
  33. data/lib/dzl/examples/fun_with_handlers.rb +33 -0
  34. data/lib/dzl/examples/fun_with_hooks.rb +65 -0
  35. data/lib/dzl/examples/fun_with_params.rb +71 -0
  36. data/lib/dzl/examples/fun_with_requests.rb +47 -0
  37. data/lib/dzl/examples/route_profile.rb +158 -0
  38. data/lib/dzl/examples/trey.rb +97 -0
  39. data/lib/dzl/logger.rb +65 -0
  40. data/lib/dzl/rack_interface.rb +95 -0
  41. data/lib/dzl/request.rb +54 -0
  42. data/lib/dzl/response_context/request_helpers.rb +11 -0
  43. data/lib/dzl/response_context.rb +47 -0
  44. data/lib/dzl/validator.rb +10 -0
  45. data/lib/dzl/validators/size.rb +32 -0
  46. data/lib/dzl/validators/value.rb +30 -0
  47. data/lib/dzl/value_or_error.rb +32 -0
  48. data/lib/dzl/version.rb +3 -0
  49. data/lib/dzl.rb +96 -0
  50. data/spec/dsl_subject_spec.rb +14 -0
  51. data/spec/endpoint_doc_spec.rb +25 -0
  52. data/spec/fun_with_handlers_spec.rb +37 -0
  53. data/spec/fun_with_hooks_spec.rb +61 -0
  54. data/spec/fun_with_params_spec.rb +197 -0
  55. data/spec/fun_with_requests_spec.rb +101 -0
  56. data/spec/logger_spec.rb +48 -0
  57. data/spec/route_params_spec.rb +13 -0
  58. data/spec/router_doc_spec.rb +32 -0
  59. data/spec/spec_helper.rb +8 -0
  60. data/spec/trey_spec.rb +135 -0
  61. data/spec/value_or_error_spec.rb +44 -0
  62. metadata +142 -0
@@ -0,0 +1,79 @@
1
+ require 'dzl/dsl_proxies/endpoint'
2
+
3
+ class Dzl::DSLSubjects::Endpoint < Dzl::DSLSubject
4
+ attr_reader :pblock, :route_regex, :route, :router, :hooks
5
+ attr_accessor :handler
6
+ include Dzl::EndpointDoc
7
+
8
+ def initialize(route, opts, router)
9
+ @route = route
10
+ @opts = opts
11
+ @router = router
12
+ @pblock = Dzl::DSLSubjects::ParameterBlock.new(:anonymous, {}, @router)
13
+ @pblock.dsl_proxy.import_pblock(:__default)
14
+ @dsl_proxy = Dzl::DSLProxies::Endpoint.new(self)
15
+ @hooks = {
16
+ after_validate: []
17
+ }
18
+
19
+ analyze_route
20
+ end
21
+
22
+ def as_json(opts=nil)
23
+ {
24
+ opts: @opts,
25
+ pblock: @pblock
26
+ }
27
+ end
28
+
29
+ def handle(request)
30
+ request.silent = true if @opts[:silent]
31
+ Dzl::ResponseContext.new(self, request, @handler).__respond__
32
+ end
33
+
34
+ def validate(request)
35
+ unless @opts[:request_methods].include?(request.request_method.downcase.to_sym)
36
+ return Dzl::ValueOrError.new(e: :request_method_not_supported)
37
+ end
38
+
39
+ route_params = extract_route_parameters(request.path)
40
+ params = {
41
+ params: request.params.merge(route_params).symbolize_keys,
42
+ headers: request.headers.symbolize_keys
43
+ }
44
+
45
+ pblock.validate(params, request)
46
+ end
47
+
48
+ def extract_route_parameters(path)
49
+ path_splits = path.split('/')
50
+ path_splits.delete('')
51
+
52
+ return nil if path_splits.size != @route_splits.size
53
+
54
+ Hash[
55
+ *@route_splits.collect do |rsplit|
56
+ psplit = path_splits.shift
57
+ next unless rsplit.starts_with?(':')
58
+ [rsplit[1..-1].to_sym, psplit]
59
+ end.compact.flatten
60
+ ]
61
+ end
62
+
63
+ private
64
+ def analyze_route
65
+ @route = "/#{@route}" if @route.is_a?(Symbol)
66
+
67
+ if params = /\/:([^\/]+)/.match(@route)
68
+ params[1..-1].each {|p| @pblock.dsl_proxy.required(p.to_sym, in_path: true)}
69
+ end
70
+
71
+ @route_splits = @route.split('/').select{|s| not s.empty?}
72
+
73
+ route_regex_string = @route_splits.collect do |route_part|
74
+ route_part.starts_with?(':') ? "/.*?" : "/#{route_part}"
75
+ end.push('$').join('')
76
+
77
+ @route_regex = Regexp.new(route_regex_string)
78
+ end
79
+ end
@@ -0,0 +1,60 @@
1
+ class Dzl::DSLSubjects::Parameter
2
+ module AllowedValues
3
+ def allowed_values(input)
4
+ unless (@validations[:allowed_values].present? ||
5
+ @validations[:disallowed_values].present?)
6
+ return Dzl::ValueOrError.new(v: input)
7
+ end
8
+
9
+ if @validations[:disallowed_values].present?
10
+ valid = true
11
+
12
+ if input.is_a?(Array)
13
+ valid = input.none? do |value|
14
+ @validations[:disallowed_values].include?(value)
15
+ end
16
+ elsif input.is_a?(String)
17
+ valid = !@validations[:disallowed_values].include?(input)
18
+ end
19
+
20
+ return Dzl::ValueOrError.new(e: :disallowed_values_failed) unless valid
21
+ end
22
+
23
+ if @validations[:allowed_values].present?
24
+ valid = true
25
+
26
+ if param_type == Array
27
+ valid = input.all? do |value|
28
+ @validations[:allowed_values].include?(value)
29
+ end
30
+ elsif param_type == String
31
+ valid = @validations[:allowed_values].include?(input)
32
+ end
33
+
34
+ return Dzl::ValueOrError.new(e: :allowed_values_failed) unless valid
35
+ end
36
+
37
+ Dzl::ValueOrError.new(v: input)
38
+ end
39
+
40
+ def regex_match(input)
41
+ return Dzl::ValueOrError.new(v: input) unless @validations[:matches].present?
42
+
43
+ error = nil
44
+
45
+ if input.is_a?(String)
46
+ unless @validations[:matches].any? {|re| re.match(input)}
47
+ error = Dzl::ValueOrError.new(e: :regex_match_fail)
48
+ end
49
+ elsif input.is_a?(Array)
50
+ input.each do |v|
51
+ unless @validations[:matches].any? {|re| re.match(v)}
52
+ error = Dzl::ValueOrError.new(e: :regex_match_fail)
53
+ end
54
+ end
55
+ end
56
+
57
+ return error ? error : Dzl::ValueOrError.new(v: input)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,59 @@
1
+ class Dzl::DSLSubjects::Parameter
2
+ module TypeConversion
3
+ def convert_type(input)
4
+ if param_type == String
5
+ v = Dzl::ValueOrError.new(v: input)
6
+
7
+ if v.value.empty? && @opts[:required]
8
+ v = Dzl::ValueOrError.new(
9
+ e: :missing_required_param
10
+ )
11
+ else
12
+ v
13
+ end
14
+ elsif param_type == Array
15
+ v = Dzl::ValueOrError.new(
16
+ v: input.split(
17
+ (@opts[:type_opts][:separator] rescue nil) || ' '
18
+ )
19
+ )
20
+
21
+ if v.value.empty? && @opts[:required]
22
+ Dzl::ValueOrError.new(
23
+ e: :empty_required_array
24
+ )
25
+ else
26
+ v
27
+ end
28
+ elsif param_type == Integer || param_type == Fixnum
29
+ if (input = input.to_i.to_s) == input
30
+ Dzl::ValueOrError.new(v: input.to_i)
31
+ else
32
+ Dzl::ValueOrError.new(
33
+ e: :type_conversion_error
34
+ )
35
+ end
36
+ elsif param_type == Date || param_type == Time
37
+ input = Time.parse(input) rescue nil
38
+ if input
39
+ input = input.to_date if param_type == Date
40
+ Dzl::ValueOrError.new(v: input)
41
+ else
42
+ Dzl::ValueOrError.new(
43
+ e: :type_conversion_error
44
+ )
45
+ end
46
+ end
47
+ end
48
+
49
+ def prevalidate_transform(input)
50
+ if @validations.has_key?(:prevalidate_transform)
51
+ @validations[:prevalidate_transform].each do |transform|
52
+ input = transform.call(input)
53
+ end
54
+ end
55
+
56
+ Dzl::ValueOrError.new(v: input)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,100 @@
1
+ require 'dzl/dsl_proxies/parameter'
2
+
3
+ class Dzl::ParameterError < StandardError; end
4
+
5
+ class Dzl::DSLSubjects::Parameter < Dzl::DSLSubject
6
+ require 'dzl/dsl_subjects/parameter/type_conversion'
7
+ require 'dzl/dsl_subjects/parameter/allowed_values'
8
+ include TypeConversion
9
+ include AllowedValues
10
+
11
+ attr_reader :validations, :opts
12
+ attr_writer :default
13
+
14
+ def initialize(name, opts)
15
+ @name = name
16
+ @opts = opts
17
+ @validations = {
18
+ type: String
19
+ }
20
+ @dsl_proxy = Dzl::DSLProxies::Parameter.new(self)
21
+ end
22
+
23
+ def clone
24
+ deep_copy = self.dup
25
+ deep_copy.dup_data
26
+ deep_copy
27
+ end
28
+
29
+ def dup_data
30
+ @opts = @opts.clone
31
+ @validations = @validations.clone
32
+ @dsl_proxy = Dzl::DSLProxies::Parameter.new(self)
33
+ end
34
+
35
+ def param_type
36
+ @validations[:type]
37
+ end
38
+
39
+ def overwrite_opts(opts)
40
+ if @opts[:in_path] && opts[:required] == false
41
+ raise Dzl::ParameterError.new("Cannot set in-path param #{name} to optional")
42
+ end
43
+
44
+ @opts.merge!(opts)
45
+ end
46
+
47
+ # Returns a symbol describe the error if error,
48
+ # returns the transformed value if not
49
+ # TODO symbol values?
50
+ def validate(input)
51
+ # Validate type
52
+ unless input
53
+ if @opts[:required]
54
+ return Dzl::ValueOrError.new(
55
+ e: @opts[:header] ? :missing_required_header : :missing_required_param
56
+ )
57
+ else
58
+ return Dzl::ValueOrError.new(
59
+ v: @opts.has_key?(:default_value) ? @opts[:default_value] : :__no_value__
60
+ )
61
+ end
62
+ end
63
+
64
+ input = convert_type(input)
65
+ return input if input.error?
66
+
67
+ input = prevalidate_transform(input.value)
68
+ return input if input.error?
69
+
70
+ input = allowed_values(input.value)
71
+ return input if input.error?
72
+
73
+ input = regex_match(input.value)
74
+ return input if input.error?
75
+
76
+ # Validator procs
77
+ if @validations.has_key?(:procs)
78
+ @validations[:procs].each do |vproc|
79
+ vproc.call(input.value) or
80
+ return Dzl::ValueOrError.new(e: :validator_poc_failed)
81
+ end
82
+ end
83
+
84
+ # Validator classes
85
+ @validations.select {|k, v| v.kind_of?(Dzl::Validator)}.each do |vary|
86
+ name, validator = vary
87
+ input = validator.validate(input.value)
88
+ return input if input.error?
89
+ end
90
+
91
+ Dzl::ValueOrError.new(v: input.value)
92
+ end
93
+
94
+ def as_json(opts=nil)
95
+ {
96
+ opts: @opts,
97
+ validations: @validations
98
+ }
99
+ end
100
+ end
@@ -0,0 +1,64 @@
1
+ require 'dzl/dsl_proxies/parameter_block'
2
+
3
+ class Dzl::DSLSubjects::ParameterBlock < Dzl::DSLSubject
4
+ attr_accessor :params
5
+ attr_reader :router
6
+
7
+ def initialize(name, opts, router)
8
+ @name = name
9
+ @opts = opts
10
+ @router = router
11
+ @params = {}
12
+ @dsl_proxy = Dzl::DSLProxies::ParameterBlock.new(self)
13
+ end
14
+
15
+ def validate(parandidates, request)
16
+ errors = @params.each_with_object({}) do |pary, errors|
17
+ pname, param = pary
18
+ parandidate_key = param.opts[:header] ? :headers : :params
19
+
20
+ # verror = value or error.
21
+ verror = @params[pname].validate(parandidates[parandidate_key][pname])
22
+ if verror.error?
23
+ errors[pname] = verror.error
24
+ else
25
+ parandidates[parandidate_key][pname] = verror.value unless verror.value == :__no_value__
26
+ end
27
+ end || {}
28
+
29
+ # Check for extra request params we are not expecting
30
+ parandidates[:params].each do |pname, value|
31
+ unless @params.keys.include?(pname)
32
+ parandidates[:params].delete(pname)
33
+ errors[pname] = :unknown_param
34
+ end
35
+ end
36
+
37
+ if !errors.empty?
38
+ Dzl::ValueOrError.new(e: errors)
39
+ elsif @opts[:protection]
40
+ protection_errors = @opts[:protection].collect do |protection|
41
+ protection.allow?(parandidates, request)
42
+ end.select { |result| result.error? }
43
+
44
+ if protection_errors.empty?
45
+ Dzl::ValueOrError.new(v: parandidates)
46
+ else
47
+ protection_errors[0]
48
+ end
49
+ else
50
+ Dzl::ValueOrError.new(v: parandidates)
51
+ end
52
+ end
53
+
54
+ def to_s
55
+ "pblock:#{name}"
56
+ end
57
+
58
+ def as_json(opts=nil)
59
+ {
60
+ opts: @opts,
61
+ params: @params
62
+ }
63
+ end
64
+ end
@@ -0,0 +1,31 @@
1
+ require 'dzl/dsl_proxies/protection'
2
+
3
+ class Dzl::RespondWithHTTPBasicChallenge < StandardError; end
4
+
5
+ class Dzl::DSLSubjects::Protection < Dzl::DSLSubject
6
+ def initialize
7
+ super
8
+ @dsl_proxy = Dzl::DSLProxies::Protection.new(self)
9
+ end
10
+
11
+ def allow?(parandidates, request)
12
+ params = parandidates[:params]
13
+ headers = parandidates[:headers]
14
+
15
+ if @opts[:http_basic].present?
16
+ @auth = Rack::Auth::Basic::Request.new(request.env)
17
+ if @auth.provided? && @auth.basic? && @auth.credentials
18
+ unless @auth.credentials[0] == @opts[:http_basic][:username] &&
19
+ @auth.credentials[1] == @opts[:http_basic][:password]
20
+ Dzl::ValueOrError.new(e: :invalid_http_basic_credentials)
21
+ else
22
+ Dzl::ValueOrError.new(v: nil)
23
+ end
24
+ else
25
+ Dzl::ValueOrError.new(e: :no_http_basic_credentials)
26
+ end
27
+ else
28
+ Dzl::ValueOrError.new(v: nil)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,87 @@
1
+ require 'dzl/dsl_proxies/router'
2
+ require 'dzl/dsl_subjects/defaults'
3
+
4
+ class Dzl::DSLSubjects::Router < Dzl::DSLSubject
5
+ include Dzl::RouterDoc
6
+ attr_reader :pblocks, :defaults_dslsub, :defaults, :app
7
+
8
+ def initialize(app)
9
+ @pblocks = {}
10
+ @endpoints_by_route = {}
11
+ @stack = []
12
+ @defaults = {}
13
+ @defaults_dslsub = Dzl::DSLSubjects::Defaults.new(self)
14
+ @dsl_proxy = Dzl::DSLProxies::Router.new(self)
15
+ @app = app
16
+ end
17
+
18
+ def call_with_subject(proc, subject)
19
+ @stack.push(subject)
20
+ proc.call
21
+ @stack.pop
22
+ end
23
+
24
+ def subject
25
+ @stack.last
26
+ end
27
+
28
+ def routes
29
+ @endpoints_by_route.keys
30
+ end
31
+
32
+ def endpoints
33
+ @endpoints = @endpoints_by_route.values.flatten
34
+ end
35
+
36
+ def add_endpoint(ept)
37
+ @endpoints_by_route[ept.route] ||= []
38
+ @endpoints_by_route[ept.route] << ept
39
+ end
40
+
41
+ def pblocks
42
+ @pblocks
43
+ end
44
+
45
+ def add_pblock(pb)
46
+ @pblocks[pb.name] = pb
47
+ end
48
+
49
+ def as_json(opts=nil)
50
+ @endpoints
51
+ end
52
+
53
+ def handle_request(request)
54
+ endpoint = find_endpoint(request)
55
+ response = request.handle_with_endpoint(endpoint)
56
+ end
57
+
58
+ def find_endpoint(request)
59
+ errors = {}
60
+ raise Dzl::NotFound if routes.empty?
61
+
62
+ endpoint = endpoints.find do |endpoint|
63
+ if request.path.match(endpoint.route_regex)
64
+ validation = endpoint.validate(request)
65
+ if validation.value?
66
+ # use our validated/transformed/params
67
+ request.params_and_headers_for_endpoint(
68
+ endpoint,
69
+ validation.value
70
+ )
71
+ true
72
+ else
73
+ errors[endpoint.route] = validation.error
74
+ false
75
+ end
76
+ end
77
+ end
78
+
79
+ if !errors.empty? &&
80
+ errors.values.any? {|v| v == :no_http_basic_credentials || v == :invalid_http_basic_credentials}
81
+ raise Dzl::RespondWithHTTPBasicChallenge
82
+ end
83
+
84
+ endpoint || raise(Dzl::NotFound.new(errors))
85
+ end
86
+
87
+ end
data/lib/dzl/errors.rb ADDED
@@ -0,0 +1,33 @@
1
+ class Dzl::Error < StandardError
2
+ attr_reader :data, :status
3
+
4
+ def initialize(data = {})
5
+ @data = data
6
+ @status = 500
7
+ end
8
+
9
+ def to_json
10
+ {
11
+ status: @status,
12
+ error_class: self.class.to_s,
13
+ errors: @data,
14
+ trace: self.backtrace
15
+ }.to_json
16
+ end
17
+ end
18
+
19
+ class Dzl::RequestError < Dzl::Error; end
20
+
21
+ class Dzl::NotFound < Dzl::RequestError
22
+ def initialize(data = {})
23
+ super(data)
24
+ @status = 404
25
+ end
26
+ end
27
+
28
+ class Dzl::BadRequest < Dzl::RequestError
29
+ def initialize(data = {})
30
+ super(data)
31
+ @status = 400
32
+ end
33
+ end
@@ -0,0 +1,9 @@
1
+ module Dzl::Examples
2
+ class Base
3
+ def self.root
4
+ @@root ||= File.expand_path('../../../../', __FILE__)
5
+ end
6
+
7
+ include Dzl
8
+ end
9
+ end
@@ -0,0 +1,33 @@
1
+ require 'dzl/examples/base'
2
+
3
+ class Dzl::Examples::FunWithHandlers < Dzl::Examples::Base
4
+ defaults do
5
+ content_type 'application/json'
6
+ end
7
+
8
+ endpoint '/say_bar' do
9
+ optional :foo, :bar, :baz, :bam
10
+
11
+ handle do
12
+ params[:bar]
13
+ end
14
+ end
15
+
16
+ endpoint '/say_bar_and_api_key' do
17
+ required :bar
18
+ required_header :api_key
19
+
20
+ handle do
21
+ {
22
+ bar: params[:bar],
23
+ api_key: headers[:api_key]
24
+ }.to_json
25
+ end
26
+ end
27
+
28
+ get '/raise' do
29
+ handle do
30
+ raise 'omg'
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,65 @@
1
+ require 'dzl/examples/base'
2
+
3
+ class Dzl::Examples::FunWithHooks < Dzl::Examples::Base
4
+ defaults do
5
+ content_type 'application/json'
6
+ end
7
+
8
+ endpoint '/pre' do
9
+ required :foo do
10
+ type Fixnum
11
+ value >= 4
12
+ prevalidate_transform do |input|
13
+ input == 1 ? 4 : input
14
+ end
15
+ end
16
+ end
17
+
18
+ endpoint '/post' do
19
+ required :foo do
20
+ type Fixnum
21
+ value >= 4
22
+ end
23
+
24
+ after_validate do
25
+ params[:foo] *= 2
26
+ end
27
+ end
28
+
29
+ endpoint '/multiply' do
30
+ required :x, :y do
31
+ type Fixnum
32
+ end
33
+
34
+ after_validate do
35
+ params[:z] = params[:x] * params[:y]
36
+ end
37
+ end
38
+
39
+ endpoint '/omg_math' do
40
+ optional :x, :y, :z do
41
+ type Fixnum
42
+ end
43
+
44
+ optional :prefix
45
+
46
+ # NEVER DO THIS IN YOUR APP IT IS SO UGLY
47
+ after_validate do
48
+ params[:multiply_then_add] = params[:x] * params[:y]
49
+ end
50
+
51
+ after_validate do
52
+ params[:multiply_then_add] += params[:z]
53
+ end
54
+
55
+ after_validate do
56
+ params[:speak] = "#{params[:prefix]} #{params[:multiply_then_add]}"
57
+ end
58
+ end
59
+
60
+ endpoint '/vomit' do
61
+ after_validate do
62
+ raise Dzl::BadRequest.new("This isn't quite what I was expecting")
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,71 @@
1
+ require 'dzl/examples/base'
2
+
3
+ class Dzl::Examples::FunWithParams < Dzl::Examples::Base
4
+ endpoint '/foo' do
5
+ required :foo do
6
+ type Array
7
+ disallowed_values %w{zilch zip nada}
8
+ end
9
+ end
10
+
11
+ endpoint '/bar' do
12
+ required :foo do
13
+ type Array, separator: '+'
14
+ end
15
+ end
16
+
17
+ endpoint '/baz' do
18
+ required :foo do
19
+ type Array, separator: ','
20
+ end
21
+ end
22
+
23
+ endpoint '/bar' do
24
+ required :foo
25
+ end
26
+
27
+ endpoint '/foo/:bar' do
28
+ required :bar do
29
+ type Time
30
+ end
31
+ end
32
+
33
+ endpoint '/protected' do
34
+ protect do
35
+ http_basic username: 'no', password: 'way'
36
+ end
37
+ end
38
+
39
+ endpoint '/arithmetic' do
40
+ optional :int do
41
+ type Fixnum
42
+ value >= 5
43
+ end
44
+
45
+ optional :str do
46
+ value == 'hello'
47
+ end
48
+
49
+ optional :date do
50
+ type Date
51
+ value > Date.parse('2012-01-01')
52
+ end
53
+ end
54
+
55
+ endpoint '/defaults' do
56
+ optional :foo do
57
+ default 'hello'
58
+ end
59
+
60
+ optional :bar
61
+ optional :baz do
62
+ default 'world'
63
+ end
64
+
65
+ optional :nil do
66
+ default nil
67
+ end
68
+ end
69
+
70
+ endpoint '/foo/:bar'
71
+ end