goliath 0.9.1 → 0.9.2

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.

Potentially problematic release.


This version of goliath might be problematic. Click here for more details.

Files changed (94) hide show
  1. data/.gitignore +1 -0
  2. data/HISTORY +50 -0
  3. data/README.md +2 -0
  4. data/examples/activerecord/srv.rb +1 -3
  5. data/examples/async_aroundware_demo.rb +81 -0
  6. data/examples/async_upload.rb +1 -2
  7. data/examples/auth_and_rate_limit.rb +143 -0
  8. data/examples/chunked_streaming.rb +37 -0
  9. data/examples/conf_test.rb +1 -3
  10. data/examples/config/auth_and_rate_limit.rb +30 -0
  11. data/examples/config/template.rb +8 -0
  12. data/examples/content_stream.rb +1 -3
  13. data/examples/echo.rb +8 -6
  14. data/examples/env_use_statements.rb +17 -0
  15. data/examples/gziped.rb +1 -3
  16. data/examples/public/stylesheets/style.css +296 -0
  17. data/examples/rack_routes.rb +65 -3
  18. data/examples/rasterize/rasterize.js +15 -0
  19. data/examples/rasterize/rasterize.rb +36 -0
  20. data/examples/rasterize/rasterize_and_shorten.rb +37 -0
  21. data/examples/stream.rb +2 -2
  22. data/examples/template.rb +48 -0
  23. data/examples/test_rig.rb +125 -0
  24. data/examples/valid.rb +4 -2
  25. data/examples/views/debug.haml +4 -0
  26. data/examples/views/joke.markdown +13 -0
  27. data/examples/views/layout.erb +12 -0
  28. data/examples/views/layout.haml +39 -0
  29. data/examples/views/root.haml +28 -0
  30. data/goliath.gemspec +10 -3
  31. data/lib/goliath.rb +0 -36
  32. data/lib/goliath/api.rb +137 -26
  33. data/lib/goliath/application.rb +71 -21
  34. data/lib/goliath/connection.rb +4 -2
  35. data/lib/goliath/constants.rb +1 -0
  36. data/lib/goliath/env.rb +40 -1
  37. data/lib/goliath/goliath.rb +30 -15
  38. data/lib/goliath/headers.rb +2 -2
  39. data/lib/goliath/plugins/latency.rb +8 -2
  40. data/lib/goliath/rack.rb +18 -0
  41. data/lib/goliath/rack/async_aroundware.rb +56 -0
  42. data/lib/goliath/rack/async_middleware.rb +93 -0
  43. data/lib/goliath/rack/builder.rb +42 -0
  44. data/lib/goliath/rack/default_response_format.rb +3 -15
  45. data/lib/goliath/rack/formatters.rb +11 -0
  46. data/lib/goliath/rack/formatters/html.rb +2 -18
  47. data/lib/goliath/rack/formatters/json.rb +2 -17
  48. data/lib/goliath/rack/formatters/plist.rb +32 -0
  49. data/lib/goliath/rack/formatters/xml.rb +23 -31
  50. data/lib/goliath/rack/formatters/yaml.rb +27 -0
  51. data/lib/goliath/rack/jsonp.rb +1 -13
  52. data/lib/goliath/rack/params.rb +55 -27
  53. data/lib/goliath/rack/render.rb +13 -22
  54. data/lib/goliath/rack/templates.rb +357 -0
  55. data/lib/goliath/rack/tracer.rb +11 -12
  56. data/lib/goliath/rack/validation.rb +12 -0
  57. data/lib/goliath/rack/validation/default_params.rb +0 -2
  58. data/lib/goliath/rack/validation/numeric_range.rb +11 -2
  59. data/lib/goliath/rack/validation/request_method.rb +3 -2
  60. data/lib/goliath/rack/validation/required_param.rb +13 -11
  61. data/lib/goliath/rack/validation/required_value.rb +11 -15
  62. data/lib/goliath/rack/validator.rb +51 -0
  63. data/lib/goliath/request.rb +34 -20
  64. data/lib/goliath/response.rb +3 -2
  65. data/lib/goliath/runner.rb +5 -11
  66. data/lib/goliath/server.rb +2 -1
  67. data/lib/goliath/synchrony/mongo_receiver.rb +64 -0
  68. data/lib/goliath/synchrony/response_receiver.rb +64 -0
  69. data/lib/goliath/test_helper.rb +39 -21
  70. data/lib/goliath/validation.rb +2 -0
  71. data/lib/goliath/{rack/validation_error.rb → validation/error.rb} +0 -16
  72. data/lib/goliath/validation/standard_http_errors.rb +31 -0
  73. data/lib/goliath/version.rb +1 -1
  74. data/spec/integration/http_log_spec.rb +16 -16
  75. data/spec/integration/rack_routes_spec.rb +144 -0
  76. data/spec/integration/reloader_spec.rb +4 -4
  77. data/spec/integration/template_spec.rb +54 -0
  78. data/spec/integration/trace_spec.rb +23 -0
  79. data/spec/integration/valid_spec.rb +21 -0
  80. data/spec/spec_helper.rb +3 -1
  81. data/spec/unit/api_spec.rb +30 -0
  82. data/spec/unit/rack/builder_spec.rb +40 -0
  83. data/spec/unit/rack/formatters/plist_spec.rb +51 -0
  84. data/spec/unit/rack/formatters/yaml_spec.rb +53 -0
  85. data/spec/unit/rack/params_spec.rb +22 -0
  86. data/spec/unit/rack/render_spec.rb +10 -5
  87. data/spec/unit/rack/validation/numeric_range_spec.rb +8 -1
  88. data/spec/unit/rack/validation/request_method_spec.rb +8 -8
  89. data/spec/unit/rack/validation/required_param_spec.rb +19 -15
  90. data/spec/unit/rack/validation/required_value_spec.rb +10 -13
  91. data/spec/unit/server_spec.rb +4 -4
  92. data/spec/unit/validation/standard_http_errors_spec.rb +21 -0
  93. metadata +177 -35
  94. data/spec/unit/rack/validation_error_spec.rb +0 -40
@@ -0,0 +1,357 @@
1
+ require 'tilt'
2
+ require 'goliath/validation/standard_http_errors'
3
+
4
+ module Goliath
5
+ module Rack
6
+
7
+ # Template rendering methods. Each method takes t as a Symbol (for template
8
+ # file lookup) or as a String (to render directly), as well as an optional
9
+ # hashes giving additional options and local variables. It returns a String
10
+ # with the rendered output.
11
+ #
12
+ # This is mostly similar to the code from Sinatra (indeed, it's stolen from
13
+ # there). It does not compile or cache templates, and the find_template
14
+ # method is simpler.
15
+ #
16
+ # @author Sinatra project -- https://github.com/sinatra/sinatra/contributors
17
+ module Templates
18
+ # lets us decorate the string response with a .content_type accessor
19
+ # @private
20
+ module ContentTyped
21
+ attr_accessor :content_type
22
+ end
23
+
24
+ # Render an erb template
25
+ #
26
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
27
+ # @param options [Hash] Rendering options -- see {#render}
28
+ # @param locals [Hash] Template-local variables -- see {#render}
29
+ # @return [String] The rendered template
30
+ # @see #render
31
+ def erb(template, options = {}, locals = {})
32
+ render :erb, template, options, locals
33
+ end
34
+
35
+ # Render an erubis template
36
+ #
37
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
38
+ # @param options [Hash] Rendering options -- see {#render}
39
+ # @param locals [Hash] Template-local variables -- see {#render}
40
+ # @return [String] The rendered template
41
+ # @see #render
42
+ def erubis(template, options = {}, locals = {})
43
+ render :erubis, template, options, locals
44
+ end
45
+
46
+ # Render a haml template
47
+ #
48
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
49
+ # @param options [Hash] Rendering options -- see {#render}
50
+ # @param locals [Hash] Template-local variables -- see {#render}
51
+ # @return [String] The rendered template
52
+ # @see #render
53
+ def haml(template, options = {}, locals = {})
54
+ render :haml, template, options, locals
55
+ end
56
+
57
+ # Render a sass template
58
+ #
59
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
60
+ # @param options [Hash] Rendering options -- see {#render}
61
+ # @param locals [Hash] Template-local variables -- see {#render}
62
+ # @return [String] The rendered template
63
+ # @see #render
64
+ def sass(template, options = {}, locals = {})
65
+ options.merge! :layout => false, :default_content_type => :css
66
+ render :sass, template, options, locals
67
+ end
68
+
69
+ # Render an scss template
70
+ #
71
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
72
+ # @param options [Hash] Rendering options -- see {#render}
73
+ # @param locals [Hash] Template-local variables -- see {#render}
74
+ # @return [String] The rendered template
75
+ # @see #render
76
+ def scss(template, options = {}, locals = {})
77
+ options.merge! :layout => false, :default_content_type => :css
78
+ render :scss, template, options, locals
79
+ end
80
+
81
+ # Render a less template
82
+ #
83
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
84
+ # @param options [Hash] Rendering options -- see {#render}
85
+ # @param locals [Hash] Template-local variables -- see {#render}
86
+ # @return [String] The rendered template
87
+ # @see #render
88
+ def less(template, options = {}, locals = {})
89
+ options.merge! :layout => false, :default_content_type => :css
90
+ render :less, template, options, locals
91
+ end
92
+
93
+ # Render a builder template
94
+ #
95
+ # @example
96
+ # # produces:
97
+ # # <?xml version='1.0' encoding='UTF-8'?>
98
+ # # <person>
99
+ # # <name aka='Frank Sinatra'>Francis Albert Sinatra</name>
100
+ # # <email>Frank Sinatra</email>
101
+ # # </person>
102
+ # builder do |xml|
103
+ # xml.instruct!
104
+ # xml.person do
105
+ # xml.name "Francis Albert Sinatra", :aka => "Frank Sinatra"
106
+ # xml.email 'frank@capitolrecords.com'
107
+ # end
108
+ # end
109
+ #
110
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
111
+ # @param options [Hash] Rendering options -- see {#render}
112
+ # @param locals [Hash] Template-local variables -- see {#render}
113
+ # @yield block If the builder method is given a block, the block is called directly
114
+ # with an XmlMarkup instance and used as a template.
115
+ # @return [String] The rendered template
116
+ # @see #render
117
+ def builder(template = nil, options = {}, locals = {}, &block)
118
+ options[:default_content_type] = :xml
119
+ render_ruby(:builder, template, options, locals, &block)
120
+ end
121
+
122
+ # Render a liquid template
123
+ #
124
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
125
+ # @param options [Hash] Rendering options -- see {#render}
126
+ # @param locals [Hash] Template-local variables -- see {#render}
127
+ # @return [String] The rendered template
128
+ # @see #render
129
+ def liquid(template, options = {}, locals = {})
130
+ render :liquid, template, options, locals
131
+ end
132
+
133
+ # Render a markdown template
134
+ #
135
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
136
+ # @param options [Hash] Rendering options -- see {#render}
137
+ # @param locals [Hash] Template-local variables -- see {#render}
138
+ # @return [String] The rendered template
139
+ # @see #render
140
+ def markdown(template, options = {}, locals = {})
141
+ render :markdown, template, options, locals
142
+ end
143
+
144
+ # Render a textile template
145
+ #
146
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
147
+ # @param options [Hash] Rendering options -- see {#render}
148
+ # @param locals [Hash] Template-local variables -- see {#render}
149
+ # @return [String] The rendered template
150
+ # @see #render
151
+ def textile(template, options = {}, locals = {})
152
+ render :textile, template, options, locals
153
+ end
154
+
155
+ # Render an rdoc template
156
+ #
157
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
158
+ # @param options [Hash] Rendering options -- see {#render}
159
+ # @param locals [Hash] Template-local variables -- see {#render}
160
+ # @return [String] The rendered template
161
+ # @see #render
162
+ def rdoc(template, options = {}, locals = {})
163
+ render :rdoc, template, options, locals
164
+ end
165
+
166
+ # Render a radius template
167
+ #
168
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
169
+ # @param options [Hash] Rendering options -- see {#render}
170
+ # @param locals [Hash] Template-local variables -- see {#render}
171
+ # @return [String] The rendered template
172
+ # @see #render
173
+ def radius(template, options = {}, locals = {})
174
+ render :radius, template, options, locals
175
+ end
176
+
177
+ # Render a markaby template
178
+ #
179
+ # @example
180
+ # markaby do
181
+ # html do
182
+ # head { title "Sinatra With Markaby" }
183
+ # body { h1 "Markaby Is Fun!" }
184
+ # end
185
+ # end
186
+ #
187
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
188
+ # @param options [Hash] Rendering options -- see {#render}
189
+ # @param locals [Hash] Template-local variables -- see {#render}
190
+ # @yield block A block template
191
+ # @return [String] The rendered template
192
+ # @see #render
193
+ def markaby(template = nil, options = {}, locals = {}, &block)
194
+ render_ruby(:mab, template, options, locals, &block)
195
+ end
196
+
197
+ # Render a coffee template
198
+ #
199
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
200
+ # @param options [Hash] Rendering options -- see {#render}
201
+ # @param locals [Hash] Template-local variables -- see {#render}
202
+ # @return [String] The rendered template
203
+ # @see #render
204
+ def coffee(template, options = {}, locals = {})
205
+ options.merge! :layout => false, :default_content_type => :js
206
+ render :coffee, template, options, locals
207
+ end
208
+
209
+ # Render a nokogiri template
210
+ #
211
+ # @example
212
+ # # produces
213
+ # # <ul>
214
+ # # <li>hello</li>
215
+ # # <li class="current">admin</li>
216
+ # # </ul>
217
+ # nokogiri do |doc|
218
+ # doc.ul do
219
+ # doc.li 'hello'
220
+ # doc.li 'admin', :class => 'current' if current_user.is_admin?
221
+ # end
222
+ # end
223
+ #
224
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
225
+ # @param options [Hash] Rendering options -- see {#render}
226
+ # @param locals [Hash] Template-local variables -- see {#render}
227
+ # @yield block A block template
228
+ # @return [String] The rendered template
229
+ # @see #render
230
+ def nokogiri(template = nil, options = {}, locals = {}, &block)
231
+ options[:default_content_type] = :xml
232
+ render_ruby(:nokogiri, template, options, locals, &block)
233
+ end
234
+
235
+ # Render a slim template
236
+ #
237
+ # @param template [Symbol, String] Template Path (symbol) or contents (String)
238
+ # @param options [Hash] Rendering options -- see {#render}
239
+ # @param locals [Hash] Template-local variables -- see {#render}
240
+ # @return [String] The rendered template
241
+ # @see #render
242
+ def slim(template, options = {}, locals = {})
243
+ render :slim, template, options, locals
244
+ end
245
+
246
+ # Finds template file with same name as extension
247
+ #
248
+ # @param views [String] The view directory
249
+ # @param name [String] The template name
250
+ # @param engine [String] The template type
251
+ # @return [String | nil] Template file or nil if it doesn't exist.
252
+ def find_template(views, name, engine)
253
+ filename = ::File.join(views, "#{name}.#{engine}")
254
+ File.exists?(filename) ? filename : nil
255
+ end
256
+
257
+ # Renders a template with the given engine. Don't call this directly --
258
+ # call one of the sugar methods.
259
+ #
260
+ # @param engine [Symbol] The engine (:haml, :erb, :textile, etc) to use
261
+ # @param template [Symbol, String] Either the name or path of the template as symbol
262
+ # (Use `:'subdir/myview'` for views in subdirectories), or a string that
263
+ # will be rendered. It looks for templates in
264
+ # +Goliath::Application.root_path/views+ by default. It is not as clever
265
+ # as Sinatra: you must name your template file template_name.engine_name
266
+ # -- so 'foo.markdown', not 'foo.md'.
267
+ #
268
+ # @param options [Hash] Options for layout
269
+ # @option options :content_type [String] The MIME content type to use.
270
+ # @option options [String] :layout If false, no layout is rendered, otherwise the specified layout is used.
271
+ # @option options [String] :layout_engine Engine to use for rendering the layout.
272
+ # @option options [Hash] :locals A hash with local variables that should be available in the template
273
+ # @option options [Object] :scope If set, template is evaluate with the binding of the
274
+ # given object rather than the application instance.
275
+ # @option options [String] :views Views directory to use.
276
+ #
277
+ # You may set template-global defaults in config[:template], for example
278
+ #
279
+ # config[:template] = {
280
+ # :layout_engine => :haml,
281
+ # }
282
+ #
283
+ # and engine-specific defaults in config[:template_engines], for example
284
+ #
285
+ # config[:template_engines] = {
286
+ # :haml => {
287
+ # :escape_html => true
288
+ # }
289
+ # }
290
+ #
291
+ # @param locals [Hash] You can give a hash of local variables available to
292
+ # the template either directly in +options[:local]+ or in a
293
+ # separate hash as the last parameter.
294
+ # @return [String] The rendered template
295
+ def render(engine, data, options = {}, locals = {}, &block)
296
+ # merge app-level options
297
+ if config.has_key?(:template_engines) && config[:template_engines].has_key?(engine)
298
+ options = config[:template_engines][engine].merge(options)
299
+ end
300
+
301
+ options = config[:template].merge(options) if config.has_key?(:template)
302
+
303
+ # extract generic options
304
+ locals = options.delete(:locals) || locals || {}
305
+ views = options.delete(:views) || Goliath::Application.root_path('views')
306
+
307
+ default_layout = options.delete(:default_layout) || :layout
308
+
309
+ layout = options.delete(:layout)
310
+ layout = default_layout if layout.nil? || layout == true
311
+
312
+ content_type = options.delete(:content_type) || options.delete(:default_content_type)
313
+ layout_engine = options.delete(:layout_engine) || engine
314
+ scope = options.delete(:scope) || self
315
+
316
+ layout_filename = find_template(views, layout, layout_engine)
317
+
318
+ layout_template = if layout == false || layout_filename == nil
319
+ NullLayout
320
+ else
321
+ Tilt.new(layout_filename, nil, options)
322
+ end
323
+
324
+ template_filename = find_template(views, data, engine)
325
+ unless template_filename
326
+ raise Goliath::Validation::InternalServerError, "Template #{data} not found in #{views} for #{engine}"
327
+ end
328
+
329
+ template = Tilt.new(template_filename, nil, options)
330
+ output = layout_template.render(scope, locals) do
331
+ template.render(scope, locals)
332
+ end
333
+
334
+ output.extend(ContentTyped).content_type = content_type if content_type
335
+ output
336
+ end
337
+
338
+ private
339
+
340
+ # Acts like a layout, does nothing.
341
+ # @private
342
+ class NullLayout
343
+ def self.render(*args, &block)
344
+ block.call
345
+ end
346
+ end
347
+
348
+ # logic shared between builder and nokogiri
349
+ def render_ruby(engine, template, options = {}, locals = {}, &block)
350
+ options, template = template, nil if template.is_a?(Hash)
351
+ template = Proc.new { block } if template.nil?
352
+
353
+ render engine, template, options, locals
354
+ end
355
+ end
356
+ end
357
+ end
@@ -6,24 +6,23 @@ module Goliath
6
6
  # use Goliath::Rack::Tracer
7
7
  #
8
8
  class Tracer
9
- def initialize(app)
10
- @app = app
9
+ include Goliath::Rack::AsyncMiddleware
10
+
11
+ def initialize(app, header_name=nil)
12
+ super(app)
13
+ @header_name = header_name || 'X-PostRank'
11
14
  end
12
15
 
13
16
  def call(env)
14
- async_cb = env['async.callback']
15
-
16
- env['async.callback'] = Proc.new do |status, headers, body|
17
- async_cb.call(post_process(env, status, headers, body))
18
- env.logger.info env.trace_stats.collect{|s| s.join(':')}.join(', ')
19
- end
20
-
21
- status, headers, body = @app.call(env)
22
- post_process(env, status, headers, body)
17
+ env.trace 'trace.start'
18
+ shb = super(env)
19
+ env.logger.info env.trace_stats.collect{|s| s.join(':')}.join(', ')
20
+ shb
23
21
  end
24
22
 
25
23
  def post_process(env, status, headers, body)
26
- extra = { 'X-PostRank' => env.trace_stats.collect{|s| s.join(': ')}.join(', ')}
24
+ extra = { @header_name => env.trace_stats.collect{|s| s.join(': ')}.join(', ')}
25
+ env.logger.info env.trace_stats.collect{|s| s.join(':')}.join(', ')
27
26
  [status, headers.merge(extra), body]
28
27
  end
29
28
  end
@@ -0,0 +1,12 @@
1
+ module Goliath
2
+ module Rack
3
+ module Validation
4
+ autoload :BooleanValue, 'goliath/rack/validation/boolean_value'
5
+ autoload :DefaultParams, 'goliath/rack/validation/default_params'
6
+ autoload :NumericRange, 'goliath/rack/validation/numeric_range'
7
+ autoload :RequestMethod, 'goliath/rack/validation/request_method'
8
+ autoload :RequiredParam, 'goliath/rack/validation/required_param'
9
+ autoload :RequiredValue, 'goliath/rack/validation/required_value'
10
+ end
11
+ end
12
+ end
@@ -1,5 +1,3 @@
1
- require 'goliath/rack/validation_error'
2
-
3
1
  module Goliath
4
2
  module Rack
5
3
  module Validation
@@ -7,6 +7,7 @@ module Goliath
7
7
  #
8
8
  # @example
9
9
  # use Goliath::Rack::Validation::NumericRange, {:key => 'num', :min => 1, :max => 30, :default => 10}
10
+ # use Goliath::Rack::Validation::NumericRange, {:key => 'num', :min => 1.2, :max => 3.5, :default => 2.9, :as => Float}
10
11
  # use Goliath::Rack::Validation::NumericRange, {:key => 'num', :min => 1}
11
12
  # use Goliath::Rack::Validation::NumericRange, {:key => 'num', :max => 10}
12
13
  #
@@ -18,6 +19,7 @@ module Goliath
18
19
  # @option opts [String] :key The key to look for in the parameters
19
20
  # @option opts [Integer] :min The minimum value
20
21
  # @option opts [Integer] :max The maximum value
22
+ # @option opts [Class] :as How to convert: Float will use .to_f, Integer (the default) will use .to_i
21
23
  # @option opts [Integer] :default The default to set if outside the range
22
24
  # @return [Goliath::Rack::Validation::NumericRange] The validator
23
25
  def initialize(app, opts = {})
@@ -29,6 +31,9 @@ module Goliath
29
31
  @max = opts[:max]
30
32
  raise Exception.new("NumericRange requires :min or :max") if @min.nil? && @max.nil?
31
33
 
34
+ @coerce_as = opts[:as]
35
+ raise Exception.new("NumericRange requires :as to be Float or Integer (default)") unless [nil, Integer, Float].include?(@coerce_as)
36
+
32
37
  @default = opts[:default]
33
38
  end
34
39
 
@@ -40,7 +45,7 @@ module Goliath
40
45
  if env['params'][@key].instance_of?(Array) then
41
46
  env['params'][@key] = env['params'][@key].first
42
47
  end
43
- env['params'][@key] = env['params'][@key].to_i
48
+ env['params'][@key] = coerce(env['params'][@key])
44
49
 
45
50
  if (!@min.nil? && env['params'][@key] < @min) || (!@max.nil? && env['params'][@key] > @max)
46
51
  env['params'][@key] = value
@@ -50,10 +55,14 @@ module Goliath
50
55
  @app.call(env)
51
56
  end
52
57
 
58
+ def coerce(val)
59
+ (@coerce_as == Float) ? val.to_f : val.to_i
60
+ end
61
+
53
62
  def value
54
63
  @default || @min || @max
55
64
  end
56
65
  end
57
66
  end
58
67
  end
59
- end
68
+ end