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
@@ -0,0 +1,204 @@
1
+ ENV['RACK_ENV'] ||= 'test'
2
+ require 'test/unit'
3
+ require 'rack'
4
+ require 'rack/test'
5
+ require 'json'
6
+ require 'json_response_verification'
7
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'bootloader')
8
+
9
+ require 'minitest/autorun'
10
+
11
+ ENV['NO_PRINT_ROUTES'] = 'true'
12
+ Bootloader.start
13
+ WeaselDiesel.send(:include, JSONResponseVerification)
14
+
15
+ ActiveRecord::Base.logger = nil
16
+
17
+ class Requester
18
+ include ::Rack::Test::Methods
19
+
20
+ def app
21
+ Sinatra::Application
22
+ end
23
+ end
24
+
25
+ module TestApi
26
+ module_function
27
+
28
+ URL_PLACEHOLDER = /\/*(:[a-z A-Z _]+)\/*/
29
+ INTERNAL_X_HEADER = AuthHelpers::INTERNAL_X_HEADER[/HTTP_(.*)/, 1] # strip the header marker added by Rack
30
+ MOBILE_X_HEADER = AuthHelpers::MOBILE_X_HEADER[/HTTP_(.*)/, 1] # strip the header marker added by Rack
31
+
32
+ def request(verb, uri, params={}, headers=nil)
33
+ params ||= {}
34
+ service_uri = uri.dup
35
+ matching = uri.scan URL_PLACEHOLDER
36
+ unless matching.empty?
37
+ # replace the placeholder by real value
38
+ matching.flatten.each_with_index do |str, idx|
39
+ key = str.delete(":").to_sym
40
+ value = params[key].to_s
41
+ # delete the value from the params
42
+ params.delete(key)
43
+ uri = uri.gsub(str, value)
44
+ end
45
+ end
46
+
47
+ request = Requester.new
48
+ yield request if block_given?
49
+ headers.each {|name, value| request.header(name, value) } if headers
50
+ response = request.send(verb, uri, params)
51
+ @json_response = JsonWrapperResponse.new(response, :verb => verb, :uri => service_uri)
52
+ end
53
+
54
+ def mobile_account=(account)
55
+ @account = account
56
+ end
57
+
58
+ def get(uri, params=nil, headers=nil)
59
+ request(:get, uri, params, headers)
60
+ end
61
+
62
+ def internal_get(uri, params=nil, headers=nil)
63
+ get(uri, params, valid_internal_api_headers(headers))
64
+ end
65
+
66
+ def mobile_get(uri, params=nil, headers=nil)
67
+ request(:get, uri, params, mobile_headers(headers))
68
+ end
69
+
70
+ def post(uri, params=nil, headers=nil)
71
+ request(:post, uri, params, headers)
72
+ end
73
+
74
+ def internal_post(uri, params=nil, headers=nil)
75
+ post(uri, params, valid_internal_api_headers(headers))
76
+ end
77
+
78
+ def mobile_post(uri, params=nil, headers=nil)
79
+ request(:post, uri, params, mobile_headers(headers))
80
+ end
81
+
82
+ def put(uri, params=nil, headers=nil)
83
+ request(:put, uri, params, headers)
84
+ end
85
+
86
+ def internal_put(uri, params=nil, headers=nil)
87
+ put(uri, params, valid_internal_api_headers(headers))
88
+ end
89
+
90
+ def mobile_put(uri, params=nil, headers=nil)
91
+ request(:put, uri, params, mobile_headers(headers))
92
+ end
93
+
94
+ def delete(uri, params=nil, headers=nil)
95
+ request(:delete, uri, params, headers)
96
+ end
97
+
98
+ def internal_delete(uri, params=nil, headers=nil)
99
+ delete(uri, params, valid_internal_api_headers(headers))
100
+ end
101
+
102
+ def mobile_delete(uri, params=nil, headers=nil)
103
+ request(:delete, uri, params, mobile_headers(headers))
104
+ end
105
+
106
+ def head(uri, params=nil, headers=nil)
107
+ request(:head, uri, params, headers)
108
+ end
109
+
110
+ def internal_head(uri, params=nil, headers=nil)
111
+ head(uri, params, valid_internal_api_headers(headers))
112
+ end
113
+
114
+ def mobile_head(uri, params=nil, headers=nil)
115
+ request(:head, uri, params, mobile_headers(headers))
116
+ end
117
+
118
+ def json_response
119
+ @json_response
120
+ end
121
+
122
+ def last_response
123
+ @json_response.rest_response if @json_response
124
+ end
125
+
126
+ def valid_internal_api_headers(headers)
127
+ custom_headers = {INTERNAL_X_HEADER => AuthHelpers::ALLOWED_API_KEYS[0]}
128
+ custom_headers.merge!(headers) if headers
129
+ custom_headers
130
+ end
131
+
132
+ def mobile_headers(headers)
133
+ custom_headers = {MOBILE_X_HEADER => @account ? Base64.urlsafe_encode64(@account.mobile_token) : nil}
134
+ custom_headers.merge!(headers) if headers
135
+ custom_headers
136
+ end
137
+
138
+ end
139
+
140
+
141
+ # Wrapper around a rest response
142
+ class JsonWrapperResponse
143
+ extend Forwardable
144
+
145
+ attr_reader :rest_response
146
+ attr_reader :verb
147
+ attr_reader :uri
148
+
149
+ def initialize(response, opts={})
150
+ @rest_response = response
151
+ @verb = opts[:verb]
152
+ @uri = opts[:uri]
153
+ end
154
+
155
+ def body
156
+ @body ||= JSON.load(rest_response.body) rescue rest_response.body
157
+ end
158
+
159
+ def success?
160
+ @rest_response.status == 200
161
+ end
162
+
163
+ def redirected?
164
+ @rest_response.status.to_s =~ /30\d/
165
+ end
166
+
167
+ def [](val)
168
+ if body
169
+ body[val.to_s]
170
+ else
171
+ nil
172
+ end
173
+ end
174
+
175
+ def method_missing(meth, *args)
176
+ body.send(meth, args)
177
+ end
178
+
179
+ def_delegators :rest_response, :code, :headers, :raw_headers, :cookies, :status, :errors
180
+ end
181
+
182
+
183
+ # Custom assertions
184
+ def assert_api_response(response=nil, message=nil)
185
+ response ||= TestApi.json_response
186
+ print response.rest_response.errors if response.status === 500
187
+ assert response.success?, message || ["Body: #{response.rest_response.body}", "Errors: #{response.errors}", "Status code: #{response.status}"].join("\n")
188
+ service = WSList.all.find{|s| s.verb == response.verb && s.url == response.uri[1..-1]}
189
+ raise "Service for (#{response.verb.upcase} #{response.uri[1..-1]}) not found" unless service
190
+ unless service.response.nodes.empty?
191
+ assert response.body.is_a?(Hash), "Invalid JSON response:\n#{response.body}"
192
+ valid, errors = service.validate_hash_response(response.body)
193
+ assert valid, errors.join(" & ") || message
194
+ end
195
+ end
196
+
197
+ def assert_api_response_with_redirection(redirection_url=nil)
198
+ response = TestApi.json_response
199
+ print response.rest_response.errors if response.status === 500
200
+ assert response.status == 302, "Redirection expect, but got #{response.status}"
201
+ if redirection_url
202
+ assert response.headers["location"], redirection_url
203
+ end
204
+ end
@@ -0,0 +1,5 @@
1
+ module WD
2
+ module Sinatra
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
data/templates/Gemfile ADDED
@@ -0,0 +1,23 @@
1
+ source "http://rubygems.org"
2
+
3
+ # web engine
4
+ gem "sinatra", "1.3.2"
5
+ # service DSL
6
+ gem "weasel_diesel"
7
+ gem "wd_sinatra"
8
+
9
+
10
+ if RUBY_VERSION =~ /1.8/
11
+ gem 'backports', '2.3.0'
12
+ gem 'json'
13
+ end
14
+
15
+ if ENV['RACK_ENV'] != "production"
16
+ gem "rack-test", "0.6.1"
17
+ # gem "foreman"
18
+ # gem "puma"
19
+ gem "minitest"
20
+ # gem "guard-puma"
21
+ # gem "guard-minitest"
22
+ gem "rake"
23
+ end
@@ -0,0 +1,13 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'puma' do
5
+ watch('Gemfile.lock')
6
+ watch(%r{^config|lib/.*})
7
+ end
8
+
9
+ guard 'minitest', :test_file_patterns => '*_test.rb' do
10
+ watch(%r|^test/(.*)_test\.rb|)
11
+ watch(%r{^api/(.*/)?([^/]+)\.rb$}) { |m| "test/api/#{m[1]}#{m[2]}_test.rb" }
12
+ watch(%r|^test/test_helper\.rb|) { "test" }
13
+ end
@@ -0,0 +1,32 @@
1
+ require 'rbconfig'
2
+ require 'rake/testtask'
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ Bundler.setup
6
+ require 'wd_sinatra/app_loader'
7
+
8
+ root = File.expand_path(File.dirname(__FILE__))
9
+
10
+ Rake::TestTask.new do |t|
11
+ t.libs << "."
12
+ t.libs << 'test'
13
+ t.pattern = "test/**/*_test.rb"
14
+ end
15
+
16
+ task :default => :test
17
+
18
+ # boot the app
19
+ task :setup_app do
20
+ ENV['DONT_CONNECT'] = 'true'
21
+ WDSinatra::AppLoader.server(root)
22
+ end
23
+
24
+ task :environment do
25
+ ENV['DONT_CONNECT'] = nil
26
+ WDSinatra::AppLoader.server(root)
27
+ end
28
+
29
+ WDSinatra::AppLoader.set_loadpath(root)
30
+ Dir.glob("lib/tasks/**/*.rake").each do |task_file|
31
+ load task_file
32
+ end
@@ -0,0 +1,29 @@
1
+ describe_service "hello_world" do |service|
2
+ service.formats :json
3
+ service.http_verb :get
4
+ service.disable_auth # on by default
5
+
6
+ # INPUT
7
+ service.param.string :name, :default => 'World'
8
+
9
+ # OUTPUT
10
+ service.response do |response|
11
+ response.object do |obj|
12
+ obj.string :message, :doc => "The greeting message sent back. Defaults to 'World'"
13
+ obj.datetime :at, :doc => "The timestamp of when the message was dispatched"
14
+ end
15
+ end
16
+
17
+ # DOCUMENTATION
18
+ service.documentation do |doc|
19
+ doc.overall "This service provides a simple hello world implementation example."
20
+ doc.param :name, "The name of the person to greet."
21
+ doc.example "<code>curl -I 'http://localhost:9292/hello_world?name=Matt'</code>"
22
+ end
23
+
24
+ # ACTION/IMPLEMENTATION
25
+ service.implementation do
26
+ {:message => "Hello #{params[:name]}", :at => Time.now}.to_json
27
+ end
28
+
29
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ require 'irb'
3
+ require 'rubygems'
4
+ require 'wd_sinatra/app_loader'
5
+ root = File.expand_path('..', File.dirname(__FILE__))
6
+ WDSinatra::AppLoader.console(root)
7
+ IRB.start(__FILE__)
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'wd_sinatra/app_loader'
3
+ root = File.expand_path(File.dirname(__FILE__))
4
+ WDSinatra::AppLoader.server(root)
5
+ run Sinatra::Application
@@ -0,0 +1,5 @@
1
+ # called after the environment is setup and the basic dependencies are loaded
2
+ # but before the models are loaded.
3
+ #
4
+ # Here you want to load your dependencies and maybe set your
5
+ # datastore.
@@ -0,0 +1,3 @@
1
+ # This is the default environment setup.
2
+ # It can be overwritten or extended by a custom environment file.
3
+ # Note: the LOGGER constant isn't yet defined and shouldn't be defined here.
@@ -0,0 +1,2 @@
1
+ require 'logger'
2
+ LOGGER.level = Logger::INFO
@@ -0,0 +1,3 @@
1
+ require 'logger'
2
+ LOGGER = Logger.new($stdout)
3
+ LOGGER.level = Logger::FATAL
@@ -0,0 +1,74 @@
1
+ require 'body_parser' # from the lib folder
2
+ # Change the JSON parser if you want to use Yajl for instance.
3
+ require 'json'
4
+ BodyParser.json_parser = JSON
5
+
6
+ module WDSinatraHooks
7
+
8
+ MOBILE_X_HEADER = 'HTTP_X_MOBILE_TOKEN'
9
+ INTERNAL_X_HEADER = 'HTTP_X_INTERNAL_API_KEY'
10
+ JSON_BODY_VERBS = %w(POST PUT DELETE)
11
+
12
+ ####### HOOKS #############################
13
+ #
14
+ # This hook gets called before the params
15
+ # are being verified. It's a good place to add a content type
16
+ # handler for instance like shown below.
17
+ # If you don't need this hook, just delete it.
18
+ # Note that you have access to the request context, see sinatra_ext.rb
19
+ # in the wd_sinatra repository to see how this is implemented.
20
+ #
21
+ # @param [Hash] params the incoming params.
22
+ # processed.
23
+ # @returns [Hash] the pre processed params.
24
+ def params_preprocessor_hook(params)
25
+ if JSON_BODY_VERBS.include?(request.request_method)
26
+ BodyParser.parse(params, request.body, request.content_type)
27
+ else
28
+ params
29
+ end
30
+ end
31
+
32
+ # This hook gets called after the params are being verified.
33
+ # You can use this hook to modify the params before sending them to
34
+ # your service.
35
+ #
36
+ # @param [Hash] params the incoming params.
37
+ # processed.
38
+ # @returns [Hash] the post processed params.
39
+ # def params_postprocessor_hook(params)
40
+ # end
41
+
42
+ # This hook gets called before dispatching any
43
+ # requests.
44
+ #
45
+ # Implementation example
46
+ def pre_dispatch_hook
47
+ if service.extra[:mobile]
48
+ mobile_auth_check
49
+ elsif service.extra[:internal]
50
+ internal_api_key_check
51
+ elsif !service.auth_required
52
+ return
53
+ else
54
+ halt 403 # protect by default
55
+ end
56
+ end
57
+ #
58
+ #########################################
59
+
60
+ # AUTHENTICATION
61
+
62
+ # Implementation example
63
+ def mobile_auth_check
64
+ true
65
+ end
66
+
67
+ # Implementation example
68
+ def internal_api_key_check
69
+ true
70
+ end
71
+
72
+ end
73
+
74
+ Sinatra::Helpers.send(:include, WDSinatraHooks)
File without changes
@@ -0,0 +1,12 @@
1
+ set :environment, RACK_ENV
2
+ set :root, WDSinata.root_path
3
+ set :app_file, __FILE__
4
+ set :public_folder, File.join(WDSinata.root_path, "public")
5
+ # Checks on static files before dispatching calls
6
+ enable :static
7
+ # enable rack session
8
+ enable :session
9
+ set :raise_errors, false
10
+ # enable that option to run by calling this file automatically (without using the config.ru file)
11
+ # enable :run
12
+ use Rack::ContentLength
@@ -0,0 +1,24 @@
1
+ module BodyParser
2
+ module_function
3
+
4
+ JSON_TYPE = "application/json"
5
+
6
+ # Parses the body
7
+ def parse(params, body, content_type)
8
+ case content_type
9
+ when JSON_TYPE
10
+ params.merge(json_parser.parse(body.string))
11
+ else
12
+ params
13
+ end
14
+ end
15
+
16
+ def json_parser=(parser_klass)
17
+ @json_parser = parser_klass
18
+ end
19
+
20
+ def json_parser
21
+ @json_parser
22
+ end
23
+
24
+ end