dzl 1.0.0.beta0

Sign up to get free protection for your applications and to get access to all the features.
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
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ tmp/*
6
+ log/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3-p125
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+ gem 'pry'
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'thin'
8
+ gem 'rack-test'
9
+ gem 'ruby-prof'
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # Dzl
2
+
3
+ ### What is it?
4
+
5
+ Dzl is a *request routing* and *parameter validation* DSL. It is designed for rapid development of small API services. It is easy to use, read, maintain, and extend. It promotes some useful conventions for application design without Doing Too Much.
6
+
7
+ It is a Racktivesupport web framework like those to which you are probably accustomed. We will have proper documentation soon.
8
+
9
+ #### I hate reading and want to use this right now!
10
+
11
+ Ok... https://github.com/vitrue/dzl/tree/master/lib/dzl/examples
12
+
13
+ #### Quick philosophical point
14
+
15
+ In Dzl, the API flows in the direction that the request would be serviced. You define first the route, then a set of acceptable parameters for that route, and finally designate a handler.
16
+
17
+ #### Request routing
18
+
19
+ In this regard, Dzl looks a lot like Sinatra. Writing (and reading) routes and handlers is simple. There is no need to have multiple open files to figure out what code responds to `GET /foo`.
20
+
21
+ #### Parameter Validation
22
+
23
+ Inside of a route's DSL block (or in a named parameter block which can be used later from any route) you define a set of acceptable (required/optional/header) parameters. You may also define a set of transformations on each parameter, to be run before or after validation. For invalid requests, information about which parameters failed validation and which validations they failed is returned. Otherwise, the (optionally transformed) parameters are yielded to the route handler.
24
+
25
+ -----------
26
+
27
+ ## Contributing
28
+
29
+ We welcome pull requests. With specs.
30
+
31
+ ## Open Source
32
+
33
+ The MIT License (MIT)
34
+ Copyright (c) Vitrue
35
+
36
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
37
+
38
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
39
+
40
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41
+
42
+ ## Versions
43
+
44
+ We will stick to Semantic Versioning (http://semver.org/), as closely as Bundler will allow.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc 'Bust out a console'
5
+ task :console do
6
+ exec 'pry -I lib -r dzl'
7
+ end
8
+
9
+ desc 'Boot the test app on localhost:3000'
10
+ task :server do
11
+ exec 'rackup -p 3000'
12
+ end
13
+
14
+ desc 'Run the specs'
15
+ RSpec::Core::RakeTask.new('spec')
data/config.ru ADDED
@@ -0,0 +1,41 @@
1
+ require 'bundler/setup'
2
+ require 'dzl'
3
+ Bundler.require
4
+
5
+ favicon_app = lambda do |env|
6
+ [200, {'Content-Type' => 'text/html'}, ['OK']]
7
+ end
8
+
9
+ map '/favicon.ico' do
10
+ run favicon_app
11
+ end
12
+
13
+ # require 'distil/examples/trey'
14
+ # map '/' do
15
+ # run Dzl::Examples::Trey
16
+ # end
17
+
18
+ # require 'distil/examples/fun_with_params'
19
+ # map '/' do
20
+ # run Dzl::Examples::FunWithParams
21
+ # end
22
+
23
+ # require 'distil/examples/fun_with_requests'
24
+ # map '/' do
25
+ # run Dzl::Examples::FunWithRequests
26
+ # end
27
+
28
+ # require 'distil/examples/route_profile'
29
+ # map '/' do
30
+ # run Dzl::Examples::RouteProfile
31
+ # end
32
+
33
+ # require 'distil/examples/fun_with_handlers'
34
+ # map '/' do
35
+ # run Dzl::Examples::FunWithHandlers
36
+ # end
37
+
38
+ require 'distil/examples/fun_with_hooks'
39
+ map '/' do
40
+ run Dzl::Examples::FunWithHooks
41
+ end
data/dzl.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require 'dzl/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dzl"
7
+ s.version = Dzl::VERSION
8
+ s.authors = ["Kyle Brett", "Paul Bergeron"]
9
+ s.email = ["kyle@vitrue.com", "pbergeron@vitrue.com"]
10
+ s.homepage = "http://github.com/vitrue/dzl"
11
+ s.summary = %q{Parameter validation and request routing DSL & framework}
12
+ s.description = %q{Dzl zones live!}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_runtime_dependency 'rack'
20
+ s.add_runtime_dependency 'activesupport'
21
+ end
@@ -0,0 +1,70 @@
1
+ module Dzl::EndpointDoc
2
+
3
+ def to_md
4
+ endpoint_template = File.read("./lib/dzl/doc/templates/endpoint.erb")
5
+
6
+ ERB.new(endpoint_template, nil, "-%").result(binding)
7
+ end
8
+
9
+ def doc_file_name
10
+ route.titlecase.gsub("/", "")
11
+ end
12
+
13
+ def doc_endpoint_request_methods
14
+ upcased_array = opts[:request_methods].collect {|method_sym| method_sym.to_s.upcase}
15
+ upcased_array.collect {|method_str| method_str.gsub('"', '')}.sort.join(', ')
16
+ end
17
+
18
+ private
19
+ def doc_list(list)
20
+ # At this point, ranges were converted to arrays, so best guess
21
+ if list.all?{|e| e.class == Fixnum} && list.first < list.last
22
+ "#{list.first}..#{list.last}"
23
+ else
24
+ "#{list}"
25
+ end
26
+ end
27
+
28
+ def doc_conditions(conditions)
29
+ first_condition = conditions.shift
30
+ # All arrays have a size validiton, but the conditions are optional
31
+ doc_str = ""
32
+ if first_condition
33
+ doc_str = "Must be #{first_condition[0]} #{first_condition[1]}"
34
+ conditions.each do |condition|
35
+ doc_str += " and must be #{condition[0]} #{condition[1]}"
36
+ end
37
+ doc_str += "."
38
+ end
39
+ return doc_str
40
+ end
41
+
42
+ def doc_param_type(type)
43
+ "Format: #{type}"
44
+ end
45
+
46
+ def doc_param_size(size)
47
+ condition_doc = doc_conditions(size.conditions)
48
+ "Size: #{condition_doc}" unless condition_doc == ""
49
+ end
50
+
51
+ def doc_param_allowed_values(allowed_values)
52
+ "Allowed values: #{doc_list(allowed_values)}"
53
+ end
54
+
55
+ def doc_param_disallowed_values(disallowed_values)
56
+ "Disallowed values: #{doc_list(disallowed_values)}"
57
+ end
58
+
59
+ def doc_param_value(value)
60
+ condition_doc = doc_conditions(value.conditions)
61
+ "Value: #{condition_doc}" unless condition_doc == ""
62
+ end
63
+
64
+ # Don't doc transforms
65
+ def doc_param_prevalidate_transform(a); end
66
+
67
+ # Don't doc procs
68
+ def doc_param_procs(a); end
69
+
70
+ end
@@ -0,0 +1,26 @@
1
+ module Dzl::RouterDoc
2
+
3
+ def to_docs
4
+ app_name = app.name.split('::').last
5
+
6
+ root = app.root || "."
7
+
8
+ `mkdir -p #{root}/dzl_docs/#{app_name}/`
9
+
10
+ home = File.new("./dzl_docs/#{app_name}/Home.md", "w")
11
+ home.write(to_md(app_name, root))
12
+ home.close
13
+
14
+ endpoints.each do |endpoint|
15
+ endpoint_page = File.new("#{root}/dzl_docs/#{app_name}/#{endpoint.doc_file_name}.md", "w")
16
+ endpoint_page.write(endpoint.to_md)
17
+ endpoint_page.close
18
+ end
19
+ end
20
+
21
+ def to_md(app_name=nil, root=".")
22
+ home_template = File.read("#{root}/lib/dzl/doc/templates/home.erb")
23
+
24
+ ERB.new(home_template, nil, "-%").result(binding)
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+
2
+ desc 'Generate documentation templates'
3
+ task :dzl_doc do
4
+ ObjectSpace.each_object(Dzl::DSLSubjects::Router) {|obj| obj.to_docs}
5
+ end
@@ -0,0 +1,55 @@
1
+ <%= "#" %> <%= route.titlecase.gsub("/", "") %><% unless doc_endpoint_request_methods.empty? %> - (<%= doc_endpoint_request_methods %>)<% end %> - <%= route %>
2
+
3
+ {Endpoint Description}
4
+
5
+ % unless pblock.params.empty?
6
+ % if pblock.params.any?{|param_name, param_obj| param_obj.opts[:header]}
7
+ % if pblock.params.any?{|param_name, param_obj| param_obj.opts[:header] && param_obj.opts[:required]}
8
+ <%= "##" %> Headers:
9
+
10
+ <%= "###" %> Required:
11
+ % pblock.params.select{|param_name, param_obj| param_obj.opts[:header] && param_obj.opts[:required]}.each do |param_name, param_obj|
12
+ * **<%= param_name %>**<% param_obj.validations.each do |type, args| param_limits = send(:"doc_param_#{type}", args); if param_limits %> - <%= param_limits %><% end; end %>
13
+ % end
14
+
15
+ % end
16
+ % if pblock.params.any?{|param_name, param_obj| param_obj.opts[:header] && !param_obj.opts[:required]}
17
+ <%= "###" %> Optional:
18
+ % pblock.params.select{|param_name, param_obj| param_obj.opts[:header] && !param_obj.opts[:required]}.each do |param_name, param_obj|
19
+ * **<%= param_name %>**<% param_obj.validations.each do |type, args| param_limits = send(:"doc_param_#{type}", args); if param_limits %> - <%= param_limits %><% end; end %>
20
+ % end
21
+
22
+ % end
23
+ % end
24
+ % if pblock.params.any?{|param_name, param_obj| !param_obj.opts[:header]}
25
+ <%= "##" %> Params:
26
+
27
+ % if pblock.params.any?{|param_name, param_obj| !param_obj.opts[:header] && param_obj.opts[:required]}
28
+ <%= "###" %> Required:
29
+ % pblock.params.select{|param_name, param_obj| !param_obj.opts[:header] && param_obj.opts[:required]}.each do |param_name, param_obj|
30
+ * **<%= param_name %>**<% param_obj.validations.each do |type, args| param_limits = send(:"doc_param_#{type}", args); if param_limits %> - <%= param_limits %><% end; end %>
31
+ % end
32
+
33
+ % end
34
+ % end
35
+
36
+ % if pblock.params.any?{|param_name, param_obj| !param_obj.opts[:header] && !param_obj.opts[:required]}
37
+ <%= "###" %> Optional:
38
+ % pblock.params.select{|param_name, param_obj| !param_obj.opts[:header] && !param_obj.opts[:required]}.each do |param_name, param_obj|
39
+ * **<%= param_name %>**<% param_obj.validations.each do |type, args| param_limits = send(:"doc_param_#{type}", args); if param_limits %> - <%= param_limits %><% end; end %>
40
+ % end
41
+
42
+ % end
43
+ % end
44
+
45
+ <%= "##" %> Example Usage
46
+
47
+ {Endpoint Example Description}
48
+
49
+ ```
50
+ {Endpoint Example Request}
51
+ ```
52
+
53
+ ```js
54
+ {Endpoint Example Return Data}
55
+ ```
@@ -0,0 +1,8 @@
1
+ <%= "#" %> <% if app_name %><%= app_name %><% else %>{API Name}<% end %>
2
+
3
+ {API Description here}
4
+
5
+ <%= "##" %> Endpoints
6
+ % endpoints.each do |endpoint|
7
+ *[<% unless endpoint.doc_endpoint_request_methods.empty?%>(<%= endpoint.doc_endpoint_request_methods %>) <% end %><%= endpoint.doc_file_name %>](wiki/<%= endpoint.doc_file_name %>)
8
+ % end
data/lib/dzl/doc.rb ADDED
@@ -0,0 +1,58 @@
1
+ module Dzl::Doc
2
+
3
+ def to_md
4
+ index_template = File.read("./template.erb")
5
+
6
+ puts ERB.new(index_template, nil, "-%").result(binding)
7
+ end
8
+
9
+ private
10
+ def doc_list(list)
11
+ # At this point, ranges were converted to arrays, so best guess
12
+ if list.all?{|e| e.class == Fixnum} && list.first < list.last
13
+ "#{list.first}..#{list.last}"
14
+ else
15
+ "#{list}"
16
+ end
17
+ end
18
+
19
+ def doc_conditions(conditions)
20
+ first_condition = conditions.shift
21
+ # All arrays have a size validiton, but the conditions are optional
22
+ doc_str = ""
23
+ if first_condition
24
+ doc_str = "Must be #{first_condition[0]} #{first_condition[1]}"
25
+ conditions.each do |condition|
26
+ doc_str += " and must be #{condition[0]} #{condition[1]}"
27
+ end
28
+ doc_str += "."
29
+ end
30
+ return doc_str
31
+ end
32
+
33
+ def doc_param_type(type)
34
+ "Format: #{type}"
35
+ end
36
+
37
+ def doc_param_size(size)
38
+ condition_doc = doc_conditions(size.conditions)
39
+ "Size: #{condition_doc}" unless condition_doc == ""
40
+ end
41
+
42
+ def doc_param_allowed_values(allowed_values)
43
+ "Allowed values: #{doc_list(allowed_values)}"
44
+ end
45
+
46
+ def doc_param_disallowed_values(disallowed_values)
47
+ "Disallowed values: #{doc_list(disallowed_values)}"
48
+ end
49
+
50
+ def doc_param_value(value)
51
+ condition_doc = doc_conditions(value.conditions)
52
+ "Value: #{condition_doc}" unless condition_doc == ""
53
+ end
54
+
55
+ # Don't doc transforms
56
+ def doc_param_prevalidate_transform(a); end
57
+
58
+ end
@@ -0,0 +1,10 @@
1
+ class Dzl::DSLProxies::Defaults < Dzl::DSLProxy
2
+ def method_missing(m, *args, &block)
3
+ raise ArgumentError if args.size != 1
4
+ @subject.set_default(m, args[0])
5
+ end
6
+
7
+ def respond_to?(m)
8
+ true
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ class Dzl::DSLProxies::Endpoint < Dzl::DSLProxy
2
+ # Delegate to our pblock if we don't answer a method
3
+ alias_method :orig_respond_to?, :respond_to?
4
+ def respond_to?(m)
5
+ orig_respond_to?(m) || @subject.pblock.dsl_proxy.respond_to?(m)
6
+ end
7
+
8
+ alias_method :orig_mm, :method_missing
9
+ def method_missing(m, *args, &block)
10
+ if @subject.pblock.dsl_proxy.respond_to?(m)
11
+ @subject.pblock.dsl_proxy.send(m, *args, &block)
12
+ else
13
+ orig_mm(m, *args, &block)
14
+ end
15
+ end
16
+
17
+ def silent
18
+ @subject.opts[:silent] = true
19
+ end
20
+
21
+ def handle
22
+ raise ArgumentError unless block_given?
23
+ @subject.handler = Proc.new
24
+ end
25
+
26
+ def after_validate
27
+ raise ArgumentError unless block_given?
28
+ @subject.hooks[:after_validate] << Proc.new
29
+ end
30
+ end
@@ -0,0 +1,75 @@
1
+ require 'dzl/validator'
2
+
3
+ class Dzl::DSLProxies::Parameter < Dzl::DSLProxy
4
+ alias_method :orig_mm, :method_missing
5
+ attr_reader :default_value
6
+ def method_missing(m, *args, &block)
7
+ validator = "Dzl::Validators::#{m.to_s.camelize}".constantize rescue nil
8
+ if !validator
9
+ orig_mm(m, *args, &block)
10
+ else
11
+ @subject.validations[m] ||= validator.new
12
+ end
13
+ end
14
+
15
+ alias_method :orig_respond_to?, :respond_to?
16
+ def respond_to?(m, *args, &block)
17
+ orig_respond_to?(m, *args, &block) ||
18
+ ("Dzl::Validators::#{m.to_s.camelize}".constantize rescue nil)
19
+ end
20
+
21
+ # Currently we allow the parameter to match ANY of these,
22
+ # perhaps we should require it to match them ALL
23
+ # or make it configurable
24
+ # TODO
25
+ def matches(regex)
26
+ @subject.validations[:matches] ||= []
27
+ @subject.validations[:matches] << regex
28
+ end
29
+
30
+ def allowed_values(ary)
31
+ @subject.validations[:allowed_values] ||= []
32
+ ary = ary.to_a if ary.is_a?(Range) # TODO or whatever
33
+ (@subject.validations[:allowed_values] += ary).uniq!
34
+ end
35
+
36
+ def disallowed_values(ary)
37
+ @subject.validations[:disallowed_values] ||= []
38
+ ary = ary.to_a if ary.is_a?(Range) # TODO or whatever
39
+ (@subject.validations[:disallowed_values] += ary).uniq!
40
+ end
41
+
42
+ def type(type, type_opts = {})
43
+ @subject.validations[:type] = type
44
+ @subject.opts[:type_opts] = type_opts
45
+ end
46
+
47
+ def integer
48
+ matches(/\d+/)
49
+ end
50
+
51
+ def default(val)
52
+ @subject.opts[:default_value] = val
53
+ end
54
+
55
+ def validate_with
56
+ raise ArgumentError unless block_given?
57
+ @subject.validations[:procs] ||= []
58
+ @subject.validations[:procs] << Proc.new
59
+ end
60
+
61
+ def prevalidate_transform
62
+ raise ArgumentError unless block_given?
63
+ @subject.validations[:prevalidate_transform] ||= []
64
+ @subject.validations[:prevalidate_transform] << Proc.new
65
+ end
66
+
67
+ # The idea behind this method is to let one
68
+ # parameter validate against the value of another
69
+ # obviously there's the circular validation problem
70
+ # but perhaps we can catch a circular reference
71
+ # when it's defined and raise DontDoThat
72
+ def params
73
+ raise Dzl::NYI
74
+ end
75
+ end
@@ -0,0 +1,61 @@
1
+ class Dzl::DSLProxies::ParameterBlock < Dzl::DSLProxy
2
+ def parameter(*names)
3
+ opts = names.last.is_a?(Hash) ? names.pop : {required: false}
4
+
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)
11
+ end
12
+
13
+ @subject.router.call_with_subject(Proc.new, @subject.params[name]) if block_given?
14
+ end
15
+ end
16
+ alias_method :param, :parameter
17
+
18
+ def required(*names, &block)
19
+ opts = names.last.is_a?(Hash) ? names.pop : {}
20
+ parameter(*names, opts.merge(required: true), &block)
21
+ end
22
+
23
+ def optional(*names, &block)
24
+ opts = names.last.is_a?(Hash) ? names.pop : {}
25
+ parameter(*names, opts.merge(required: false), &block)
26
+ end
27
+
28
+ def required_header(*names, &block)
29
+ opts = names.last.is_a?(Hash) ? names.pop : {}
30
+ required(*names, opts.merge(header: true), &block)
31
+ end
32
+
33
+ def optional_header(*names, &block)
34
+ opts = names.last.is_a?(Hash) ? names.pop : {}
35
+ optional(*names, opts.merge(header: true), &block)
36
+ end
37
+
38
+ def forbid(*names)
39
+ names.each do |name|
40
+ @subject.params.delete(name)
41
+ end
42
+ end
43
+
44
+ def protect
45
+ raise ArgumentError unless block_given?
46
+ @subject.opts[:protection] ||= []
47
+ @subject.opts[:protection] << Dzl::DSLSubjects::Protection.new
48
+
49
+ @subject.router.call_with_subject(Proc.new, @subject.opts[:protection].last)
50
+ end
51
+
52
+ def import_pblock(*pblocks)
53
+ pblocks.each do |pblock|
54
+ next unless @subject.router.pblocks.has_key?(pblock)
55
+ @subject.router.pblocks[pblock].params.each do |name, param|
56
+ @subject.params[name] = param.clone
57
+ end
58
+ end
59
+ end
60
+ alias_method :import_parameters, :import_pblock
61
+ end
@@ -0,0 +1,6 @@
1
+ class Dzl::DSLProxies::Protection < Dzl::DSLProxy
2
+ def http_basic(opts)
3
+ raise ArgumentError unless [:username, :password].all? {|k| opts[k].present?}
4
+ @subject.opts[:http_basic] = opts
5
+ end
6
+ end
@@ -0,0 +1,62 @@
1
+ class Dzl::DSLProxies::Router < Dzl::DSLProxy
2
+ REQUEST_METHODS = [:get, :post, :put, :delete, :options]
3
+
4
+ def pblock(name, opts = {})
5
+ raise ArgumentError unless name.is_a?(Symbol) &&
6
+ opts.is_a?(Hash) &&
7
+ block_given?
8
+
9
+
10
+ pb = Dzl::DSLSubjects::ParameterBlock.new(name, opts, @subject)
11
+ @subject.add_pblock(pb)
12
+ @subject.call_with_subject(Proc.new, pb)
13
+ end
14
+ alias_method :parameters, :pblock
15
+
16
+ def endpoint(route, *request_methods)
17
+ request_methods = [:get] if request_methods.empty?
18
+ request_methods.uniq!
19
+
20
+ raise ArgumentError unless request_methods.all? {|m| REQUEST_METHODS.include?(m)}
21
+ opts = {
22
+ request_methods: request_methods
23
+ }
24
+
25
+ ept = Dzl::DSLSubjects::Endpoint.new(route, opts, @subject)
26
+ @subject.add_endpoint(ept)
27
+ @subject.call_with_subject(Proc.new, ept) if block_given?
28
+ end
29
+
30
+ def defaults(&block)
31
+ raise ArgumentError unless block_given?
32
+
33
+ @subject.call_with_subject(Proc.new, @subject.defaults_dslsub)
34
+ end
35
+
36
+ REQUEST_METHODS.each do |m|
37
+ define_method(m) do |route, *request_methods, &block|
38
+ request_methods << m
39
+ endpoint(route, *request_methods, &block)
40
+ end
41
+ end
42
+
43
+ def global_parameters(&block)
44
+ raise ArgumentError unless block_given?
45
+ pblock(:__default, &block)
46
+ end
47
+ alias_method :global_pblock, :global_parameters
48
+
49
+ alias_method :orig_respond_to?, :respond_to?
50
+ def respond_to?(m)
51
+ orig_respond_to?(m) || (@subject.subject &&
52
+ @subject.subject.dsl_proxy.respond_to?(m))
53
+ end
54
+
55
+ def method_missing(m, *args, &block)
56
+ if orig_respond_to?(m)
57
+ super.method_missing(m, *args, &block)
58
+ else
59
+ @subject.subject.dsl_proxy.send(m, *args, &block)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,8 @@
1
+ class Dzl::DSLProxy
2
+ def initialize(subject)
3
+ raise ArgumentError unless subject
4
+ @subject = subject
5
+ end
6
+ end
7
+
8
+ module Dzl::DSLProxies; end
@@ -0,0 +1,15 @@
1
+ require 'dzl/dsl_proxy'
2
+
3
+ class Dzl::DSLSubject
4
+ attr_reader :name, :opts, :router
5
+
6
+ def initialize
7
+ @opts = {}
8
+ end
9
+
10
+ def dsl_proxy
11
+ @dsl_proxy || raise("You must set @dsl_proxy in your DSLSubject subclass")
12
+ end
13
+ end
14
+
15
+ module Dzl::DSLSubjects; end
@@ -0,0 +1,12 @@
1
+ require 'dzl/dsl_proxies/defaults'
2
+
3
+ class Dzl::DSLSubjects::Defaults < Dzl::DSLSubject
4
+ def initialize(router)
5
+ @dsl_proxy = Dzl::DSLProxies::Defaults.new(self)
6
+ @router = router
7
+ end
8
+
9
+ def set_default(key, val)
10
+ @router.defaults[key] = val
11
+ end
12
+ end