wd_sinatra 0.0.1

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 (84) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +148 -0
  5. data/Rakefile +2 -0
  6. data/bin/wd_sinatra +47 -0
  7. data/lib/wd_sinatra.rb +7 -0
  8. data/lib/wd_sinatra/app_loader.rb +120 -0
  9. data/lib/wd_sinatra/sinatra_ext.rb +117 -0
  10. data/lib/wd_sinatra/test_helpers.rb +204 -0
  11. data/lib/wd_sinatra/version.rb +5 -0
  12. data/templates/Gemfile +23 -0
  13. data/templates/Guardfile +13 -0
  14. data/templates/Rakefile +32 -0
  15. data/templates/api/hello_world.rb +29 -0
  16. data/templates/bin/console +7 -0
  17. data/templates/config.ru +5 -0
  18. data/templates/config/app.rb +5 -0
  19. data/templates/config/environments/default.rb +3 -0
  20. data/templates/config/environments/production.rb +2 -0
  21. data/templates/config/environments/test.rb +3 -0
  22. data/templates/config/hooks.rb +74 -0
  23. data/templates/config/middleware.rb +0 -0
  24. data/templates/config/sinatra_config.rb +12 -0
  25. data/templates/lib/body_parser.rb +24 -0
  26. data/templates/lib/tasks/doc.rake +39 -0
  27. data/templates/lib/tasks/doc_generator/bootstrap/.gitignore +4 -0
  28. data/templates/lib/tasks/doc_generator/bootstrap/LICENSE +176 -0
  29. data/templates/lib/tasks/doc_generator/bootstrap/Makefile +47 -0
  30. data/templates/lib/tasks/doc_generator/bootstrap/README.md +105 -0
  31. data/templates/lib/tasks/doc_generator/bootstrap/bootstrap.css +2467 -0
  32. data/templates/lib/tasks/doc_generator/bootstrap/bootstrap.min.css +356 -0
  33. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/css/docs.css +317 -0
  34. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/ico/bootstrap-apple-114x114.png +0 -0
  35. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/ico/bootstrap-apple-57x57.png +0 -0
  36. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/ico/bootstrap-apple-72x72.png +0 -0
  37. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/ico/favicon.ico +0 -0
  38. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/img/bird.png +0 -0
  39. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/img/browsers.png +0 -0
  40. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/img/example-diagram-01.png +0 -0
  41. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/img/example-diagram-02.png +0 -0
  42. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/img/example-diagram-03.png +0 -0
  43. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/img/grid-18px.png +0 -0
  44. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/img/twitter-logo-no-bird.png +0 -0
  45. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/js/application.js +52 -0
  46. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/js/google-code-prettify/prettify.css +94 -0
  47. data/templates/lib/tasks/doc_generator/bootstrap/docs/assets/js/google-code-prettify/prettify.js +28 -0
  48. data/templates/lib/tasks/doc_generator/bootstrap/docs/index.html +2037 -0
  49. data/templates/lib/tasks/doc_generator/bootstrap/docs/javascript.html +798 -0
  50. data/templates/lib/tasks/doc_generator/bootstrap/examples/container-app.html +119 -0
  51. data/templates/lib/tasks/doc_generator/bootstrap/examples/fluid.html +122 -0
  52. data/templates/lib/tasks/doc_generator/bootstrap/examples/hero.html +79 -0
  53. data/templates/lib/tasks/doc_generator/bootstrap/js/bootstrap-alerts.js +124 -0
  54. data/templates/lib/tasks/doc_generator/bootstrap/js/bootstrap-buttons.js +64 -0
  55. data/templates/lib/tasks/doc_generator/bootstrap/js/bootstrap-dropdown.js +55 -0
  56. data/templates/lib/tasks/doc_generator/bootstrap/js/bootstrap-modal.js +260 -0
  57. data/templates/lib/tasks/doc_generator/bootstrap/js/bootstrap-popover.js +90 -0
  58. data/templates/lib/tasks/doc_generator/bootstrap/js/bootstrap-scrollspy.js +107 -0
  59. data/templates/lib/tasks/doc_generator/bootstrap/js/bootstrap-tabs.js +80 -0
  60. data/templates/lib/tasks/doc_generator/bootstrap/js/bootstrap-twipsy.js +321 -0
  61. data/templates/lib/tasks/doc_generator/bootstrap/js/tests/index.html +40 -0
  62. data/templates/lib/tasks/doc_generator/bootstrap/js/tests/unit/bootstrap-alerts.js +41 -0
  63. data/templates/lib/tasks/doc_generator/bootstrap/js/tests/unit/bootstrap-buttons.js +42 -0
  64. data/templates/lib/tasks/doc_generator/bootstrap/js/tests/unit/bootstrap-dropdown.js +52 -0
  65. data/templates/lib/tasks/doc_generator/bootstrap/js/tests/unit/bootstrap-modal.js +151 -0
  66. data/templates/lib/tasks/doc_generator/bootstrap/js/tests/unit/bootstrap-popover.js +76 -0
  67. data/templates/lib/tasks/doc_generator/bootstrap/js/tests/unit/bootstrap-scrollspy.js +31 -0
  68. data/templates/lib/tasks/doc_generator/bootstrap/js/tests/unit/bootstrap-tabs.js +77 -0
  69. data/templates/lib/tasks/doc_generator/bootstrap/js/tests/unit/bootstrap-twipsy.js +81 -0
  70. data/templates/lib/tasks/doc_generator/bootstrap/js/tests/vendor/qunit.css +232 -0
  71. data/templates/lib/tasks/doc_generator/bootstrap/js/tests/vendor/qunit.js +1510 -0
  72. data/templates/lib/tasks/doc_generator/bootstrap/lib/bootstrap.less +26 -0
  73. data/templates/lib/tasks/doc_generator/bootstrap/lib/forms.less +479 -0
  74. data/templates/lib/tasks/doc_generator/bootstrap/lib/mixins.less +222 -0
  75. data/templates/lib/tasks/doc_generator/bootstrap/lib/patterns.less +1060 -0
  76. data/templates/lib/tasks/doc_generator/bootstrap/lib/reset.less +141 -0
  77. data/templates/lib/tasks/doc_generator/bootstrap/lib/scaffolding.less +139 -0
  78. data/templates/lib/tasks/doc_generator/bootstrap/lib/tables.less +224 -0
  79. data/templates/lib/tasks/doc_generator/bootstrap/lib/type.less +187 -0
  80. data/templates/lib/tasks/doc_generator/bootstrap/lib/variables.less +60 -0
  81. data/templates/lib/tasks/doc_generator/template.erb +117 -0
  82. data/templates/public/favicon.ico +0 -0
  83. data/wd-sinatra.gemspec +22 -0
  84. metadata +154 -0
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in wd-sinatra.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Matt Aimonetti
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # Weasel Diesel Sinatra
2
+
3
+ Weasel-Diesel Sinatra app gem, allowing you to generate/update sinatra apps using the Weasel Diesel DSL
4
+
5
+
6
+ ## Installation
7
+
8
+ $ gem install 'wd_sinatra'
9
+
10
+ ## Usage
11
+
12
+ ### App generation
13
+
14
+ Once the gem is installed, you can use the generator to create a new
15
+ app. Go to the location where you want to generate a new app and type
16
+ the following command (replace `<app_name>` by the name you want to
17
+ application to have):
18
+
19
+ $ wd_sinatra <app_name>
20
+
21
+ Check the newly generated app
22
+
23
+ $ cd <app_name>
24
+
25
+ You'll need bundler to install the dependencies:
26
+
27
+ $ gem install bundler
28
+ $ bundle install
29
+
30
+ ### Starting the server
31
+
32
+ The app is now ready to use, to start it you can use rack:
33
+
34
+ $ bundle exec rackup
35
+
36
+ This will start the server on port 9292 and the default GET `/hello_world` service will be available at: `http://localhost:9292/hello_world'.
37
+
38
+ Note that the code won't be reloading automatically in the server when
39
+ you make a modification to the source code. For that, you might want to
40
+ use Puma + [Guard](https://github.com/jc00ke/guard-puma) or another tool that allows you to do that.
41
+ While it's a nice feature to have, a lot of developers like to do that
42
+ differently and it seems more sensitive to let them pick the way they
43
+ like the most.
44
+
45
+ ### Console
46
+
47
+ $ bundle exec bin/console
48
+
49
+ The console mode is like the server mode but without the server only
50
+ concerns such as the sinatra routes config and Rack middleware.
51
+
52
+ ### Documentation generation
53
+
54
+ $ rake doc:services
55
+
56
+ To generate documentation for the APIs you created in the api folder.
57
+
58
+ ### Testing
59
+
60
+ TODO
61
+
62
+ ## Writing a service
63
+
64
+ TODO see Weasel Diesel for now.
65
+
66
+ ## Config and hooks
67
+
68
+ ### app.rb
69
+
70
+ The `config/app.rb` file is being required after the environment is set
71
+ but before the models are loaded. This is the perfect place to load
72
+ custom libraries and set your datastore.
73
+
74
+ This is where you will for instance load ActiveRecord and set your DB
75
+ connection.
76
+
77
+ ### Environments
78
+
79
+ The files in `config/environments` can
80
+ be used to set environment specific configuration or other.
81
+ If you add a new environment such as staging, you can add a `staging.rb`
82
+ file in the environments folder that will only get required when running
83
+ in this env mode.
84
+ Whatever the environment is, the `config/environments/default.rb` is
85
+ being required before the specific env file.
86
+
87
+ ### Hooks
88
+
89
+ The request dispatcher offers 3 hooks which you can see demonstrated in
90
+ `config/hooks.rb`.
91
+
92
+ * `params_preprocessor_hook(params)`
93
+ * `params_postprocessor_hook(params)`
94
+ * `pre_dispatch_hook`
95
+
96
+ The two first hooks are used to process the params and when implemented
97
+ are expected to return the params that will be used in the request.
98
+
99
+ The `pre_dispatch_hook` is called just before the request is dispatched
100
+ to the service implementation. This is where you might want to implement
101
+ an authentication verification system for instance.
102
+
103
+ These 3 hooks have access to the entire request context, including the
104
+ `service` being called. You can use the service `extra` option to set
105
+ some custom settings that can then be used in this pre dispatch hook.
106
+
107
+ In the default generated application, a body parser is provided to parse
108
+ JSON requests when the HTTP verb is PUT, POST or DELETE. The json parser is set by default to use the `JSON` module but you might want to change it to use Yajl for instance.
109
+ To do that, edit the `config/hooks.rb` file and change the following:
110
+
111
+ ```ruby
112
+ BodyParser.json_parser = JSON
113
+ ```
114
+
115
+ to:
116
+
117
+ ```ruby
118
+ BodyParser.json_parser = Yajl::Parser
119
+ ```
120
+
121
+ Of course, you'll need to require `Yajl` first and add it to your
122
+ Gemfile if you want to use Bundler.
123
+
124
+
125
+
126
+ ## Using an ORM
127
+
128
+ TODO: see https://github.com/mattetti/sinatra-web-api-example/ for now
129
+ for an example of setting up ActiveRecord.
130
+ Eventually the generator will take an option to generate an AR, DM or
131
+ other ORM template.
132
+
133
+ ## Update
134
+
135
+ To update your app, just update your gem dependency on `wd_sinatra`, you
136
+ can also compare the difference between your app and a freshly generated
137
+ app by trying to generate a new app named the same as your old app.
138
+ The generator will detect conflicts and let you pick an action (diff,
139
+ overwrite, ignore...)
140
+
141
+
142
+ ## Contributing
143
+
144
+ 1. Fork it
145
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
146
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
147
+ 4. Push to the branch (`git push origin my-new-feature`)
148
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/wd_sinatra ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "thor/group"
4
+
5
+ class WdSinatra < Thor::Group
6
+ include Thor::Actions
7
+
8
+ # Define arguments and options
9
+ # argument :type, :type => :string, :desc => "The type to generate, app by default"
10
+ argument :name, :type => :string, :desc => "The name of the app to generate"
11
+ class_option :test_framework, :default => :test_unit
12
+
13
+ def self.source_root
14
+ File.expand_path(File.join('..', 'templates'), File.dirname(__FILE__))
15
+ end
16
+
17
+ def create_lib_directory
18
+ directory "lib", "#{name}/lib"
19
+ end
20
+
21
+ def create_config_directory
22
+ directory "config", "#{name}/config"
23
+ end
24
+
25
+ def create_bin_directory
26
+ directory "bin", "#{name}/bin"
27
+ chmod "#{name}/bin/console", 0755
28
+ end
29
+
30
+ def create_public_directory
31
+ directory "public", "#{name}/public"
32
+ end
33
+
34
+ def create_api_directory
35
+ directory "api", "#{name}/api"
36
+ end
37
+
38
+ def create_files
39
+ copy_file "Rakefile", "#{name}/Rakefile"
40
+ copy_file "Gemfile", "#{name}/Gemfile"
41
+ copy_file "config.ru", "#{name}/config.ru"
42
+ copy_file "Guardfile", "#{name}/Guardfile"
43
+ end
44
+
45
+ end
46
+
47
+ WdSinatra.start
data/lib/wd_sinatra.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "wd_sinatra/version"
2
+
3
+ module WD
4
+ module Sinatra
5
+ # Your code goes here...
6
+ end
7
+ end
@@ -0,0 +1,120 @@
1
+ if RUBY_VERSION =~ /1.8/
2
+ require 'rubygems'
3
+ require 'backports'
4
+ end
5
+ require 'bundler'
6
+ Bundler.setup
7
+ require 'logger'
8
+ require 'weasel_diesel'
9
+
10
+ module WDSinatra
11
+ module AppLoader
12
+ module_function
13
+
14
+ # Boot in server mode
15
+ def server(root_path)
16
+ @root = root_path
17
+ unless @booted
18
+ console(root_path)
19
+ load_middleware
20
+ set_sinatra_routes
21
+ set_sinatra_settings
22
+ end
23
+ end
24
+
25
+ # Boot in console mode
26
+ def console(root_path)
27
+ @root = root_path
28
+ unless @booted
29
+ set_env
30
+ set_loadpath
31
+ load_environment
32
+ load_lib_dependencies
33
+ load_app_config
34
+ load_models
35
+ load_apis
36
+ @booted = true
37
+ end
38
+ end
39
+
40
+ def root_path
41
+ @root
42
+ end
43
+
44
+ # PRIVATE
45
+
46
+ # Sets the environment (RACK_ENV) based on some env variables.
47
+ def set_env
48
+ if !Object.const_defined?(:RACK_ENV)
49
+ ENV['RACK_ENV'] ||= ENV['RAILS_ENV'] || 'development'
50
+ Object.const_set(:RACK_ENV, ENV['RACK_ENV'])
51
+ end
52
+ puts "Running in #{RACK_ENV} mode" if RACK_ENV == 'development'
53
+ end
54
+
55
+
56
+ # Loads an environment specific config if available, the config file is where the logger should be set
57
+ # if it was not, we are using stdout.
58
+ def load_environment(env=RACK_ENV)
59
+ # Load the default which can be overwritten or extended by specific
60
+ # env config files.
61
+ require File.join(root_path, 'config', 'environments', 'default.rb')
62
+ env_file = File.join(root_path, "config", "environments", "#{env}.rb")
63
+ if File.exist?(env_file)
64
+ require env_file
65
+ else
66
+ debug_msg = "Environment file: #{env_file} couldn't be found, using only the default environment config instead." unless env == 'development'
67
+ end
68
+ # making sure we have a LOGGER constant defined.
69
+ unless Object.const_defined?(:LOGGER)
70
+ Object.const_set(:LOGGER, Logger.new($stdout))
71
+ end
72
+ LOGGER.debug(debug_msg) if debug_msg
73
+ end
74
+
75
+ def set_loadpath(root=nil)
76
+ root ||= root_path
77
+ $: << root
78
+ $: << File.join(root, 'lib')
79
+ $: << File.join(root, 'models')
80
+ end
81
+
82
+ def load_lib_dependencies
83
+ # WeaselDiesel is the web service DSL gem used to define services.
84
+ require 'weasel_diesel'
85
+ require 'sinatra'
86
+ require 'wd_sinatra/sinatra_ext'
87
+ # TODO: hook to custom app dependencies
88
+ end
89
+
90
+ def load_app_config
91
+ require File.join(root_path, 'config', 'app')
92
+ end
93
+
94
+ def load_models
95
+ Dir.glob(File.join(root_path, "models", "**", "*.rb")).each do |model|
96
+ require model
97
+ end
98
+ end
99
+
100
+ # DSL routes are located in the api folder
101
+ def load_apis
102
+ Dir.glob(File.join(root_path, "api", "**", "*.rb")).each do |api|
103
+ require api
104
+ end
105
+ end
106
+
107
+ def set_sinatra_routes
108
+ WSList.all.sort.each{|api| api.load_sinatra_route }
109
+ end
110
+
111
+ def load_middleware
112
+ require File.join(root_path, 'config', 'middleware')
113
+ end
114
+
115
+ def set_sinatra_settings
116
+ require File.join(root_path, 'config', 'sinatra_config')
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,117 @@
1
+ require 'forwardable'
2
+ require 'params_verification'
3
+ require 'json'
4
+
5
+ class WeaselDiesel
6
+
7
+ class RequestHandler
8
+ extend Forwardable
9
+
10
+ # @return [WeaselDiesel] The service served by this controller
11
+ # @api public
12
+ attr_reader :service
13
+
14
+ # @return [Sinatra::Application]
15
+ # @api public
16
+ attr_reader :app
17
+
18
+ # @return [Hash]
19
+ # @api public
20
+ attr_reader :env
21
+
22
+ # @return [Sinatra::Request]
23
+ # @see http://rubydoc.info/github/sinatra/sinatra/Sinatra/Request
24
+ # @api public
25
+ attr_reader :request
26
+
27
+ # @return [Sinatra::Response]
28
+ # @see http://rubydoc.info/github/sinatra/sinatra/Sinatra/Response
29
+ # @api public
30
+ attr_reader :response
31
+
32
+ # @return [Hash]
33
+ # @api public
34
+ attr_accessor :params
35
+
36
+ attr_accessor :current_user
37
+
38
+ # The service controller might be loaded outside of a Sinatra App
39
+ # in this case, we don't need to load the helpers
40
+ if Object.const_defined?(:Sinatra)
41
+ include Sinatra::Helpers
42
+ end
43
+
44
+ def initialize(service, &block)
45
+ @service = service
46
+ @implementation = block
47
+ end
48
+
49
+ def dispatch(app)
50
+ @app = app
51
+ @env = app.env
52
+ @request = app.request
53
+ @response = app.response
54
+ @service = service
55
+
56
+ begin
57
+ # raises an exception if the params are not valid
58
+ # otherwise update the app params with potentially new params (using default values)
59
+ # note that if a type is mentioned for a params, the object will be cast to this object type
60
+ #
61
+ # removing the fake sinatra params since v1.3 added this. (should be eventually removed)
62
+ if app.params['splat']
63
+ processed_params = app.params.dup
64
+ processed_params.delete('splat')
65
+ processed_params.delete('captures')
66
+ end
67
+ @params = (processed_params || app.params)
68
+ @params = params_preprocessor_hook(@params) if self.respond_to?(:params_preprocessor_hook)
69
+ @params = ParamsVerification.validate!(@params, service.defined_params)
70
+ @params = params_postrocessor_hook(@params) if self.respond_to?(:params_postprocessor_hook)
71
+ rescue Exception => e
72
+ LOGGER.error e.message
73
+ LOGGER.error "passed params: #{app.params.inspect}"
74
+ halt 400, {:error => e.message}.to_json
75
+ end
76
+
77
+ # Define WeaselDiesel::RequestHandler#authorization_check in your app if
78
+ # you want to use an auth check.
79
+ pre_dispatch_hook if self.respond_to?(:pre_dispatch_hook)
80
+ service_dispatch
81
+ end
82
+
83
+ # Forwarding some methods to the underlying app object
84
+ def_delegators :app, :settings, :halt, :compile_template, :session
85
+
86
+ private ##################################################
87
+
88
+ end # of RequestHandler
89
+
90
+ attr_reader :handler
91
+
92
+ def implementation(&block)
93
+ if block_given?
94
+ @handler = RequestHandler.new(self, &block)
95
+ @handler.define_singleton_method(:service_dispatch, block)
96
+ end
97
+ @handler
98
+ end
99
+
100
+ def load_sinatra_route
101
+ service = self
102
+ upcase_verb = service.verb.to_s.upcase
103
+ unless ENV['DONT_PRINT_ROUTES']
104
+ LOGGER.info "Available endpoint: #{self.http_verb.upcase} /#{self.url}"
105
+ end
106
+ raise "DSL is missing the implementation block" unless self.handler && self.handler.respond_to?(:service_dispatch)
107
+
108
+ # Define the route directly to save some object allocations on the critical path
109
+ # Note that we are using a private API to define the route and that unlike sinatra usual DSL
110
+ # we do NOT define a HEAD route for every GET route.
111
+ Sinatra::Base.send(:route, upcase_verb, "/#{self.url}") do
112
+ service.handler.dispatch(self)
113
+ end
114
+
115
+ end
116
+
117
+ end