wd_sinatra 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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