aldebaran 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/.gitignore +13 -0
  2. data/.travis.yml +16 -0
  3. data/.yardopts +4 -0
  4. data/AUTHORS +4 -0
  5. data/Gemfile +77 -0
  6. data/KNOWN_ISSUES +5 -0
  7. data/LICENSE +22 -0
  8. data/README.rdoc +1900 -0
  9. data/Rakefile +175 -0
  10. data/aldebaran.gemspec +19 -0
  11. data/lib/aldebaran.rb +7 -0
  12. data/lib/aldebaran/base.rb +1600 -0
  13. data/lib/aldebaran/images/404.png +0 -0
  14. data/lib/aldebaran/images/500.png +0 -0
  15. data/lib/aldebaran/main.rb +28 -0
  16. data/lib/aldebaran/showexceptions.rb +340 -0
  17. data/lib/aldebaran/version.rb +3 -0
  18. data/test/aldebaran_test.rb +17 -0
  19. data/test/base_test.rb +160 -0
  20. data/test/builder_test.rb +95 -0
  21. data/test/coffee_test.rb +92 -0
  22. data/test/contest.rb +98 -0
  23. data/test/creole_test.rb +65 -0
  24. data/test/delegator_test.rb +162 -0
  25. data/test/encoding_test.rb +20 -0
  26. data/test/erb_test.rb +104 -0
  27. data/test/extensions_test.rb +100 -0
  28. data/test/filter_test.rb +397 -0
  29. data/test/haml_test.rb +101 -0
  30. data/test/helper.rb +115 -0
  31. data/test/helpers_test.rb +1192 -0
  32. data/test/less_test.rb +67 -0
  33. data/test/liquid_test.rb +59 -0
  34. data/test/mapped_error_test.rb +259 -0
  35. data/test/markaby_test.rb +80 -0
  36. data/test/markdown_test.rb +81 -0
  37. data/test/middleware_test.rb +68 -0
  38. data/test/nokogiri_test.rb +69 -0
  39. data/test/public/favicon.ico +0 -0
  40. data/test/radius_test.rb +59 -0
  41. data/test/rdoc_test.rb +65 -0
  42. data/test/readme_test.rb +136 -0
  43. data/test/request_test.rb +45 -0
  44. data/test/response_test.rb +61 -0
  45. data/test/result_test.rb +98 -0
  46. data/test/route_added_hook_test.rb +59 -0
  47. data/test/routing_test.rb +1096 -0
  48. data/test/sass_test.rb +115 -0
  49. data/test/scss_test.rb +88 -0
  50. data/test/server_test.rb +48 -0
  51. data/test/settings_test.rb +493 -0
  52. data/test/slim_test.rb +98 -0
  53. data/test/static_test.rb +178 -0
  54. data/test/streaming_test.rb +100 -0
  55. data/test/templates_test.rb +298 -0
  56. data/test/textile_test.rb +65 -0
  57. data/test/views/a/in_a.str +1 -0
  58. data/test/views/ascii.erb +2 -0
  59. data/test/views/b/in_b.str +1 -0
  60. data/test/views/calc.html.erb +1 -0
  61. data/test/views/error.builder +3 -0
  62. data/test/views/error.erb +3 -0
  63. data/test/views/error.haml +3 -0
  64. data/test/views/error.sass +2 -0
  65. data/test/views/explicitly_nested.str +1 -0
  66. data/test/views/foo/hello.test +1 -0
  67. data/test/views/hello.builder +1 -0
  68. data/test/views/hello.coffee +1 -0
  69. data/test/views/hello.creole +1 -0
  70. data/test/views/hello.erb +1 -0
  71. data/test/views/hello.haml +1 -0
  72. data/test/views/hello.less +5 -0
  73. data/test/views/hello.liquid +1 -0
  74. data/test/views/hello.mab +1 -0
  75. data/test/views/hello.md +1 -0
  76. data/test/views/hello.nokogiri +1 -0
  77. data/test/views/hello.radius +1 -0
  78. data/test/views/hello.rdoc +1 -0
  79. data/test/views/hello.sass +2 -0
  80. data/test/views/hello.scss +3 -0
  81. data/test/views/hello.slim +1 -0
  82. data/test/views/hello.str +1 -0
  83. data/test/views/hello.test +1 -0
  84. data/test/views/hello.textile +1 -0
  85. data/test/views/layout2.builder +3 -0
  86. data/test/views/layout2.erb +2 -0
  87. data/test/views/layout2.haml +2 -0
  88. data/test/views/layout2.liquid +2 -0
  89. data/test/views/layout2.mab +2 -0
  90. data/test/views/layout2.nokogiri +3 -0
  91. data/test/views/layout2.radius +2 -0
  92. data/test/views/layout2.slim +3 -0
  93. data/test/views/layout2.str +2 -0
  94. data/test/views/layout2.test +1 -0
  95. data/test/views/nested.str +1 -0
  96. data/test/views/utf8.erb +2 -0
  97. metadata +231 -0
@@ -0,0 +1,13 @@
1
+ # please add general patterns to your global ignore list
2
+ # see https://github.com/github/gitignore#readme
3
+
4
+ /pkg
5
+ /doc/api
6
+ /Gemfile.lock
7
+ .nbproject/
8
+ .DS_Store
9
+ CHANGES
10
+ .project
11
+ aldebaran-*.gem
12
+ aldebaran*.gem.idea/
13
+ .idea/
@@ -0,0 +1,16 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - rbx
6
+ - rbx-2.0
7
+ - jruby
8
+ - ruby-head
9
+ env:
10
+ - "rack=1.3.0"
11
+ - "rack=master"
12
+ - "tilt=1.3.2"
13
+ - "tilt=master"
14
+ notifications:
15
+ recipients:
16
+ - mahmutbulut0@gmail.com
@@ -0,0 +1,4 @@
1
+ --readme README.rdoc
2
+ --title 'Aldebaran API Documentation'
3
+ --charset utf-8
4
+ 'lib/**/*.rb' - '*.rdoc'
data/AUTHORS ADDED
@@ -0,0 +1,4 @@
1
+ ALDEBARAN
2
+ ======
3
+
4
+ Mahmut Bulut
data/Gemfile ADDED
@@ -0,0 +1,77 @@
1
+ # Why use bundler?
2
+ # Well, not all development dependencies install on all rubies. Moreover, `gem
3
+ # install aldebaran --development` doesn't work, as it will also try to install
4
+ # development dependencies of our dependencies, and those are not conflict free.
5
+ # So, here we are, `bundle install`.
6
+ #
7
+ # If you have issues with a gem: `bundle install --without-coffee-script`.
8
+
9
+ RUBY_ENGINE = 'ruby' unless defined? RUBY_ENGINE
10
+ source :rubygems unless ENV['QUICK']
11
+
12
+ gem 'rake'
13
+ gem 'rack-test', '>= 0.5.6'
14
+ gem 'ci_reporter', :group => :ci
15
+
16
+ # Allows stuff like `tilt=1.2.2 bundle install` or `tilt=master ...`.
17
+ # Used by the CI.
18
+ github = "git://github.com/%s.git"
19
+ repos = { 'tilt' => github % "rtomayko/tilt", 'rack' => github % "rack/rack" }
20
+ %w[tilt rack].each do |lib|
21
+ dep = (ENV[lib] || 'stable').sub "#{lib}-", ''
22
+ dep = nil if dep == 'stable'
23
+ dep = {:git => repos[lib], :branch => dep} if dep and dep !~ /(\d+\.)+\d+/
24
+ gem lib, dep
25
+ end
26
+
27
+ gem 'haml', '>= 3.0'
28
+ gem 'sass'
29
+ gem 'builder'
30
+ gem 'erubis'
31
+ gem 'less', '~> 1.0'
32
+ if RUBY_ENGINE == "maglev"
33
+ gem 'liquid', :git => "https://github.com/Shopify/liquid.git"
34
+ else
35
+ gem 'liquid'
36
+ end
37
+ gem 'slim'
38
+ gem 'RedCloth' if RUBY_VERSION < "1.9.3" and not RUBY_ENGINE.start_with? 'ma'
39
+ gem 'coffee-script', '>= 2.0'
40
+ gem 'rdoc'
41
+ gem 'kramdown'
42
+ gem 'maruku'
43
+ gem 'creole'
44
+
45
+ if RUBY_ENGINE == 'jruby'
46
+ gem 'nokogiri', '!= 1.5.0'
47
+ gem 'jruby-openssl'
48
+ elsif RUBY_ENGINE != 'maglev'
49
+ gem 'nokogiri'
50
+ end
51
+
52
+ unless RUBY_ENGINE == 'jruby' && JRUBY_VERSION < "1.6.1"
53
+ # C extensions
54
+ gem 'rdiscount'
55
+ gem 'redcarpet'
56
+
57
+ ## bluecloth is broken
58
+ #gem 'bluecloth'
59
+ end
60
+
61
+ if RUBY_ENGINE == 'maglev'
62
+ gem 'json', :git => "https://github.com/MagLev/json.git"
63
+ gem 'markaby'
64
+ gem 'radius'
65
+ else
66
+ platforms :ruby_18, :jruby do
67
+ gem 'json'
68
+ gem 'markaby'
69
+ gem 'radius'
70
+ end
71
+ end
72
+
73
+ platforms :mri_18 do
74
+ # bundler platforms are broken
75
+ next if RUBY_ENGINE != 'ruby' or RUBY_VERSION > "1.8"
76
+ gem 'rcov'
77
+ end
@@ -0,0 +1,5 @@
1
+ = Third Party issues
2
+
3
+ * Nokogiri 1.5.0 does not work properly on JRuby
4
+ * RedCloth does not work properly on the upcoming Ruby 1.9.3
5
+ * BlueCloth randomly adds invalid bytes to generated strings
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011 Mahmut Bulut
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,1900 @@
1
+ = ALDEBARAN
2
+
3
+ {<img src="http://travis-ci.org/regularlambda/aldebaran" />}[http://travis-ci.org/regularlambda/aldebaran]
4
+
5
+ Aldebaran is a DSL micro-framework for quickly creating web applications in Ruby with minimal
6
+ effort and write services with benchmarked mathematical functions:
7
+
8
+ # myapp.rb
9
+ require 'aldebaran'
10
+
11
+ get '/' do
12
+ 'Hello world!'
13
+ end
14
+
15
+ Install the gem and run with:
16
+
17
+ gem install aldebaran
18
+ ruby -rubygems myapp.rb
19
+
20
+ View at: http://localhost:4567
21
+
22
+ It is recommended to also run <tt>gem install thin</tt>, which aldebaran will
23
+ pick up if available.
24
+
25
+ == Routes
26
+
27
+ In aldebaran, a route is an HTTP method paired with a URL-matching pattern.
28
+ Each route is associated with a block:
29
+
30
+ get '/' do
31
+ .. show something ..
32
+ end
33
+
34
+ post '/' do
35
+ .. create something ..
36
+ end
37
+
38
+ put '/' do
39
+ .. replace something ..
40
+ end
41
+
42
+ patch '/' do
43
+ .. modify something ..
44
+ end
45
+
46
+ delete '/' do
47
+ .. annihilate something ..
48
+ end
49
+
50
+ options '/' do
51
+ .. appease something ..
52
+ end
53
+
54
+ Routes are matched in the order they are defined. The first route that
55
+ matches the request is invoked.
56
+
57
+ Route patterns may include named parameters, accessible via the
58
+ <tt>params</tt> hash:
59
+
60
+ get '/hello/:name' do
61
+ # matches "GET /hello/foo" and "GET /hello/bar"
62
+ # params[:name] is 'foo' or 'bar'
63
+ "Hello #{params[:name]}!"
64
+ end
65
+
66
+ You can also access named parameters via block parameters:
67
+
68
+ get '/hello/:name' do |n|
69
+ "Hello #{n}!"
70
+ end
71
+
72
+ Route patterns may also include splat (or wildcard) parameters, accessible
73
+ via the <tt>params[:splat]</tt> array:
74
+
75
+ get '/say/*/to/*' do
76
+ # matches /say/hello/to/world
77
+ params[:splat] # => ["hello", "world"]
78
+ end
79
+
80
+ get '/download/*.*' do
81
+ # matches /download/path/to/file.xml
82
+ params[:splat] # => ["path/to/file", "xml"]
83
+ end
84
+
85
+ Or with block parameters:
86
+
87
+ get '/download/*.*' do |path, ext|
88
+ [path, ext] # => ["path/to/file", "xml"]
89
+ end
90
+
91
+ Route matching with Regular Expressions:
92
+
93
+ get %r{/hello/([\w]+)} do
94
+ "Hello, #{params[:captures].first}!"
95
+ end
96
+
97
+ Or with a block parameter:
98
+
99
+ get %r{/hello/([\w]+)} do |c|
100
+ "Hello, #{c}!"
101
+ end
102
+
103
+ Route patterns may have optional parameters:
104
+
105
+ get '/posts.?:format?' do
106
+ # matches "GET /posts" and any extension "GET /posts.json", "GET /posts.xml" etc.
107
+ end
108
+
109
+ === Conditions
110
+
111
+ Routes may include a variety of matching conditions, such as the user agent:
112
+
113
+ get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
114
+ "You're using Songbird version #{params[:agent][0]}"
115
+ end
116
+
117
+ get '/foo' do
118
+ # Matches non-songbird browsers
119
+ end
120
+
121
+ Other available conditions are +host_name+ and +provides+:
122
+
123
+ get '/', :host_name => /^admin\./ do
124
+ "Admin Area, Access denied!"
125
+ end
126
+
127
+ get '/', :provides => 'html' do
128
+ haml :index
129
+ end
130
+
131
+ get '/', :provides => ['rss', 'atom', 'xml'] do
132
+ builder :feed
133
+ end
134
+
135
+ You can easily define your own conditions:
136
+
137
+ set(:probability) { |value| condition { rand <= value } }
138
+
139
+ get '/win_a_car', :probability => 0.1 do
140
+ "You won!"
141
+ end
142
+
143
+ get '/win_a_car' do
144
+ "Sorry, you lost."
145
+ end
146
+
147
+ For a condition that takes multiple values use a splat:
148
+
149
+ set(:auth) do |*roles| # <- notice the splat here
150
+ condition do
151
+ unless logged_in? && roles.any? {|role| current_user.in_role? role }
152
+ redirect "/login/", 303
153
+ end
154
+ end
155
+ end
156
+
157
+ get "/my/account/", :auth => [:user, :admin] do
158
+ "Your Account Details"
159
+ end
160
+
161
+ get "/only/admin/", :auth => :admin do
162
+ "Only admins are allowed here!"
163
+ end
164
+
165
+ === Return Values
166
+
167
+ The return value of a route block determines at least the response body passed
168
+ on to the HTTP client, or at least the next middleware in the Rack stack.
169
+ Most commonly, this is a string, as in the above examples. But other values are
170
+ also accepted.
171
+
172
+ You can return any object that would either be a valid Rack response, Rack
173
+ body object or HTTP status code:
174
+
175
+ * An Array with three elements: <tt>[status (Fixnum), headers (Hash), response
176
+ body (responds to #each)]</tt>
177
+ * An Array with two elements: <tt>[status (Fixnum), response body (responds to
178
+ #each)]</tt>
179
+ * An object that responds to <tt>#each</tt> and passes nothing but strings to
180
+ the given block
181
+ * A Fixnum representing the status code
182
+
183
+ That way we can, for instance, easily implement a streaming example:
184
+
185
+ class Stream
186
+ def each
187
+ 100.times { |i| yield "#{i}\n" }
188
+ end
189
+ end
190
+
191
+ get('/') { Stream.new }
192
+
193
+ You can also use the +stream+ helper method (described below) to reduce boiler
194
+ plate and embed the streaming logic in the route.
195
+
196
+ === Custom Route Matchers
197
+
198
+ As shown above, aldebaran ships with built-in support for using String patterns
199
+ and regular expressions as route matches. However, it does not stop there. You
200
+ can easily define your own matchers:
201
+
202
+ class AllButPattern
203
+ Match = Struct.new(:captures)
204
+
205
+ def initialize(except)
206
+ @except = except
207
+ @captures = Match.new([])
208
+ end
209
+
210
+ def match(str)
211
+ @captures unless @except === str
212
+ end
213
+ end
214
+
215
+ def all_but(pattern)
216
+ AllButPattern.new(pattern)
217
+ end
218
+
219
+ get all_but("/index") do
220
+ # ...
221
+ end
222
+
223
+ Note that the above example might be over-engineered, as it can also be
224
+ expressed as:
225
+
226
+ get // do
227
+ pass if request.path_info == "/index"
228
+ # ...
229
+ end
230
+
231
+ Or, using negative look ahead:
232
+
233
+ get %r{^(?!/index$)} do
234
+ # ...
235
+ end
236
+
237
+ == Static Files
238
+
239
+ Static files are served from the <tt>./public</tt> directory. You can specify
240
+ a different location by setting the <tt>:public_folder</tt> option:
241
+
242
+ set :public_folder, File.dirname(__FILE__) + '/static'
243
+
244
+ Note that the public directory name is not included in the URL. A file
245
+ <tt>./public/css/style.css</tt> is made available as
246
+ <tt>http://example.com/css/style.css</tt>.
247
+
248
+ Use the <tt>:static_cache_control</tt> setting (see below) to add
249
+ <tt>Cache-Control</tt> header info.
250
+
251
+ == Views / Templates
252
+
253
+ Each template language is exposed as via its own rendering method. These
254
+ methods simply return a string:
255
+
256
+ get '/' do
257
+ erb :index
258
+ end
259
+
260
+ This renders <tt>views/index.erb</tt>.
261
+
262
+ Instead of a template name, you can also just pass in the template content
263
+ directly:
264
+
265
+ get '/' do
266
+ code = "<%= Time.now >"
267
+ erb code
268
+ end
269
+
270
+ Templates take a second argument, the options hash:
271
+
272
+ get '/' do
273
+ erb :index, :layout => :post
274
+ end
275
+
276
+ This will render <tt>views/index.erb</tt> embedded in the
277
+ <tt>views/post.erb</tt> (default is <tt>views/layout.erb</tt>, if it exists).
278
+
279
+ Any options not understood by aldebaran will be passed on to the template
280
+ engine:
281
+
282
+ get '/' do
283
+ haml :index, :format => :html5
284
+ end
285
+
286
+ You can also set options per template language in general:
287
+
288
+ set :haml, :format => :html5
289
+
290
+ get '/' do
291
+ haml :index
292
+ end
293
+
294
+ Options passed to the render method override options set via +set+.
295
+
296
+ Available Options:
297
+
298
+ [locals]
299
+ List of locals passed to the document. Handy with partials.
300
+ Example: <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
301
+
302
+ [default_encoding]
303
+ String encoding to use if uncertain. Defaults to
304
+ <tt>settings.default_encoding</tt>.
305
+
306
+ [views]
307
+ Views folder to load templates from. Defaults to <tt>settings.views</tt>.
308
+
309
+ [layout]
310
+ Whether to use a layout (+true+ or +false+), if it's a Symbol, specifies
311
+ what template to use. Example: <tt>erb :index, :layout => !request.xhr?</tt>
312
+
313
+ [content_type]
314
+ Content-Type the template produces, default depends on template language.
315
+
316
+ [scope]
317
+ Scope to render template under. Defaults to the application instance. If you
318
+ change this, instance variables and helper methods will not be available.
319
+
320
+ [layout_engine]
321
+ Template engine to use for rendering the layout. Useful for languages that
322
+ do not support layouts otherwise. Defaults to the engine used for the
323
+ template. Example: <tt>set :rdoc, :layout_engine => :erb</tt>
324
+
325
+ Templates are assumed to be located directly under the <tt>./views</tt>
326
+ directory. To use a different views directory:
327
+
328
+ set :views, settings.root + '/templates'
329
+
330
+ One important thing to remember is that you always have to reference
331
+ templates with symbols, even if they're in a subdirectory (in this
332
+ case, use <tt>:'subdir/template'</tt>). You must use a symbol because
333
+ otherwise rendering methods will render any strings passed to them
334
+ directly.
335
+
336
+ === Available Template Languages
337
+
338
+ Some languages have multiple implementations. To specify what implementation
339
+ to use (and to be thread-safe), you should simply require it first:
340
+
341
+ require 'rdiscount' # or require 'bluecloth'
342
+ get('/') { markdown :index }
343
+
344
+ === Haml Templates
345
+
346
+ Dependency:: {haml}[http://haml-lang.com/]
347
+ File Extensions:: <tt>.haml</tt>
348
+ Example:: <tt>haml :index, :format => :html5</tt>
349
+
350
+ === Erb Templates
351
+
352
+ Dependency:: {erubis}[http://www.kuwata-lab.com/erubis/] or
353
+ erb (included in Ruby)
354
+ File Extensions:: <tt>.erb</tt>, <tt>.rhtml</tt> or <tt>.erubis</tt> (Erubis
355
+ only)
356
+ Example:: <tt>erb :index</tt>
357
+
358
+ === Builder Templates
359
+
360
+ Dependency:: {builder}[http://builder.rubyforge.org/]
361
+ File Extensions:: <tt>.builder</tt>
362
+ Example:: <tt>builder { |xml| xml.em "hi" }</tt>
363
+
364
+ It also takes a block for inline templates (see example).
365
+
366
+ === Nokogiri Templates
367
+
368
+ Dependency:: {nokogiri}[http://nokogiri.org/]
369
+ File Extensions:: <tt>.nokogiri</tt>
370
+ Example:: <tt>nokogiri { |xml| xml.em "hi" }</tt>
371
+
372
+ It also takes a block for inline templates (see example).
373
+
374
+ === Sass Templates
375
+
376
+ Dependency:: {sass}[http://sass-lang.com/]
377
+ File Extensions:: <tt>.sass</tt>
378
+ Example:: <tt>sass :stylesheet, :style => :expanded</tt>
379
+
380
+ === SCSS Templates
381
+
382
+ Dependency:: {sass}[http://sass-lang.com/]
383
+ File Extensions:: <tt>.scss</tt>
384
+ Example:: <tt>scss :stylesheet, :style => :expanded</tt>
385
+
386
+ === Less Templates
387
+
388
+ Dependency:: {less}[http://www.lesscss.org/]
389
+ File Extensions:: <tt>.less</tt>
390
+ Example:: <tt>less :stylesheet</tt>
391
+
392
+ === Liquid Templates
393
+
394
+ Dependency:: {liquid}[http://www.liquidmarkup.org/]
395
+ File Extensions:: <tt>.liquid</tt>
396
+ Example:: <tt>liquid :index, :locals => { :key => 'value' }</tt>
397
+
398
+ Since you cannot call Ruby methods (except for +yield+) from a Liquid
399
+ template, you almost always want to pass locals to it.
400
+
401
+ === Markdown Templates
402
+
403
+ Dependency:: {rdiscount}[https://github.com/rtomayko/rdiscount],
404
+ {redcarpet}[https://github.com/tanoku/redcarpet],
405
+ {bluecloth}[http://deveiate.org/projects/BlueCloth],
406
+ {kramdown}[http://kramdown.rubyforge.org/] *or*
407
+ {maruku}[http://maruku.rubyforge.org/]
408
+ File Extensions:: <tt>.markdown</tt>, <tt>.mkd</tt> and <tt>.md</tt>
409
+ Example:: <tt>markdown :index, :layout_engine => :erb</tt>
410
+
411
+ It is not possible to call methods from markdown, nor to pass locals to it.
412
+ You therefore will usually use it in combination with another rendering
413
+ engine:
414
+
415
+ erb :overview, :locals => { :text => markdown(:introduction) }
416
+
417
+ Note that you may also call the +markdown+ method from within other templates:
418
+
419
+ %h1 Hello From Haml!
420
+ %p= markdown(:greetings)
421
+
422
+ Since you cannot call Ruby from Markdown, you cannot use layouts written in
423
+ Markdown. However, it is possible to use another rendering engine for the
424
+ template than for the layout by passing the <tt>:layout_engine</tt> option.
425
+
426
+ === Textile Templates
427
+
428
+ Dependency:: {RedCloth}[http://redcloth.org/]
429
+ File Extensions:: <tt>.textile</tt>
430
+ Example:: <tt>textile :index, :layout_engine => :erb</tt>
431
+
432
+ It is not possible to call methods from textile, nor to pass locals to it. You
433
+ therefore will usually use it in combination with another rendering engine:
434
+
435
+ erb :overview, :locals => { :text => textile(:introduction) }
436
+
437
+ Note that you may also call the +textile+ method from within other templates:
438
+
439
+ %h1 Hello From Haml!
440
+ %p= textile(:greetings)
441
+
442
+ Since you cannot call Ruby from Textile, you cannot use layouts written in
443
+ Textile. However, it is possible to use another rendering engine for the
444
+ template than for the layout by passing the <tt>:layout_engine</tt> option.
445
+
446
+ === RDoc Templates
447
+
448
+ Dependency:: {rdoc}[http://rdoc.rubyforge.org/]
449
+ File Extensions:: <tt>.rdoc</tt>
450
+ Example:: <tt>rdoc :README, :layout_engine => :erb</tt>
451
+
452
+ It is not possible to call methods from rdoc, nor to pass locals to it. You
453
+ therefore will usually use it in combination with another rendering engine:
454
+
455
+ erb :overview, :locals => { :text => rdoc(:introduction) }
456
+
457
+ Note that you may also call the +rdoc+ method from within other templates:
458
+
459
+ %h1 Hello From Haml!
460
+ %p= rdoc(:greetings)
461
+
462
+ Since you cannot call Ruby from RDoc, you cannot use layouts written in
463
+ RDoc. However, it is possible to use another rendering engine for the
464
+ template than for the layout by passing the <tt>:layout_engine</tt> option.
465
+
466
+ === Radius Templates
467
+
468
+ Dependency:: {radius}[http://radius.rubyforge.org/]
469
+ File Extensions:: <tt>.radius</tt>
470
+ Example:: <tt>radius :index, :locals => { :key => 'value' }</tt>
471
+
472
+ Since you cannot call Ruby methods directly from a Radius template, you almost
473
+ always want to pass locals to it.
474
+
475
+ === Markaby Templates
476
+
477
+ Dependency:: {markaby}[http://markaby.github.com/]
478
+ File Extensions:: <tt>.mab</tt>
479
+ Example:: <tt>markaby { h1 "Welcome!" }</tt>
480
+
481
+ It also takes a block for inline templates (see example).
482
+
483
+ === Slim Templates
484
+
485
+ Dependency:: {slim}[http://slim-lang.com/]
486
+ File Extensions:: <tt>.slim</tt>
487
+ Example:: <tt>slim :index</tt>
488
+
489
+ === Creole Templates
490
+
491
+ Dependency:: {creole}[https://github.com/minad/creole]
492
+ File Extensions:: <tt>.creole</tt>
493
+ Example:: <tt>creole :wiki, :layout_engine => :erb</tt>
494
+
495
+ It is not possible to call methods from creole, nor to pass locals to it. You
496
+ therefore will usually use it in combination with another rendering engine:
497
+
498
+ erb :overview, :locals => { :text => creole(:introduction) }
499
+
500
+ Note that you may also call the +creole+ method from within other templates:
501
+
502
+ %h1 Hello From Haml!
503
+ %p= creole(:greetings)
504
+
505
+ Since you cannot call Ruby from Creole, you cannot use layouts written in
506
+ Creole. However, it is possible to use another rendering engine for the
507
+ template than for the layout by passing the <tt>:layout_engine</tt> option.
508
+
509
+ === CoffeeScript Templates
510
+
511
+ Dependency:: {coffee-script}[https://github.com/josh/ruby-coffee-script]
512
+ and a {way to execute javascript}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
513
+ File Extensions:: <tt>.coffee</tt>
514
+ Example:: <tt>coffee :index</tt>
515
+
516
+ === Embedded Templates
517
+
518
+ get '/' do
519
+ haml '%div.title Hello World'
520
+ end
521
+
522
+ Renders the embedded template string.
523
+
524
+ === Accessing Variables in Templates
525
+
526
+ Templates are evaluated within the same context as route handlers. Instance
527
+ variables set in route handlers are directly accessible by templates:
528
+
529
+ get '/:id' do
530
+ @foo = Foo.find(params[:id])
531
+ haml '%h1= @foo.name'
532
+ end
533
+
534
+ Or, specify an explicit Hash of local variables:
535
+
536
+ get '/:id' do
537
+ foo = Foo.find(params[:id])
538
+ haml '%h1= bar.name', :locals => { :bar => foo }
539
+ end
540
+
541
+ This is typically used when rendering templates as partials from within
542
+ other templates.
543
+
544
+ === Inline Templates
545
+
546
+ Templates may be defined at the end of the source file:
547
+
548
+ require 'aldebaran'
549
+
550
+ get '/' do
551
+ haml :index
552
+ end
553
+
554
+ __END__
555
+
556
+ @@ layout
557
+ %html
558
+ = yield
559
+
560
+ @@ index
561
+ %div.title Hello world!!!!!
562
+
563
+ NOTE: Inline templates defined in the source file that requires aldebaran are
564
+ automatically loaded. Call <tt>enable :inline_templates</tt> explicitly if you
565
+ have inline templates in other source files.
566
+
567
+ === Named Templates
568
+
569
+ Templates may also be defined using the top-level <tt>template</tt> method:
570
+
571
+ template :layout do
572
+ "%html\n =yield\n"
573
+ end
574
+
575
+ template :index do
576
+ '%div.title Hello World!'
577
+ end
578
+
579
+ get '/' do
580
+ haml :index
581
+ end
582
+
583
+ If a template named "layout" exists, it will be used each time a template
584
+ is rendered. You can individually disable layouts by passing
585
+ <tt>:layout => false</tt> or disable them by default via
586
+ <tt>set :haml, :layout => false</tt>:
587
+
588
+ get '/' do
589
+ haml :index, :layout => !request.xhr?
590
+ end
591
+
592
+ === Associating File Extensions
593
+
594
+ To associate a file extension with a template engine, use
595
+ <tt>Tilt.register</tt>. For instance, if you like to use the file extension
596
+ +tt+ for Textile templates, you can do the following:
597
+
598
+ Tilt.register :tt, Tilt[:textile]
599
+
600
+ === Adding Your Own Template Engine
601
+
602
+ First, register your engine with Tilt, then create a rendering method:
603
+
604
+ Tilt.register :myat, MyAwesomeTemplateEngine
605
+
606
+ helpers do
607
+ def myat(*args) render(:myat, *args) end
608
+ end
609
+
610
+ get '/' do
611
+ myat :index
612
+ end
613
+
614
+ Renders <tt>./views/index.myat</tt>. See https://github.com/rtomayko/tilt to
615
+ learn more about Tilt.
616
+
617
+ == Filters
618
+
619
+ Before filters are evaluated before each request within the same
620
+ context as the routes will be and can modify the request and response. Instance
621
+ variables set in filters are accessible by routes and templates:
622
+
623
+ before do
624
+ @note = 'Hi!'
625
+ request.path_info = '/foo/bar/baz'
626
+ end
627
+
628
+ get '/foo/*' do
629
+ @note #=> 'Hi!'
630
+ params[:splat] #=> 'bar/baz'
631
+ end
632
+
633
+ After filters are evaluated after each request within the same context and can
634
+ also modify the request and response. Instance variables set in before filters
635
+ and routes are accessible by after filters:
636
+
637
+ after do
638
+ puts response.status
639
+ end
640
+
641
+ Note: Unless you use the +body+ method rather than just returning a String from
642
+ the routes, the body will not yet be available in the after filter, since it is
643
+ generated later on.
644
+
645
+ Filters optionally take a pattern, causing them to be evaluated only if the
646
+ request path matches that pattern:
647
+
648
+ before '/protected/*' do
649
+ authenticate!
650
+ end
651
+
652
+ after '/create/:slug' do |slug|
653
+ session[:last_slug] = slug
654
+ end
655
+
656
+ Like routes, filters also take conditions:
657
+
658
+ before :agent => /Songbird/ do
659
+ # ...
660
+ end
661
+
662
+ after '/blog/*', :host_name => 'example.com' do
663
+ # ...
664
+ end
665
+
666
+ == Helpers
667
+
668
+ Use the top-level <tt>helpers</tt> method to define helper methods for use in
669
+ route handlers and templates:
670
+
671
+ helpers do
672
+ def bar(name)
673
+ "#{name}bar"
674
+ end
675
+ end
676
+
677
+ get '/:name' do
678
+ bar(params[:name])
679
+ end
680
+
681
+ === Using Sessions
682
+
683
+ A session is used to keep state during requests. If activated, you have one
684
+ session hash per user session:
685
+
686
+ enable :sessions
687
+
688
+ get '/' do
689
+ "value = " << session[:value].inspect
690
+ end
691
+
692
+ get '/:value' do
693
+ session[:value] = params[:value]
694
+ end
695
+
696
+ Note that <tt>enable :sessions</tt> actually stores all data in a cookie. This
697
+ might not always be what you want (storing lots of data will increase your
698
+ traffic, for instance). You can use any Rack session middleware: in order to
699
+ do so, do *not* call <tt>enable :sessions</tt>, but instead pull in your
700
+ middleware of choice as you would any other middleware:
701
+
702
+ use Rack::Session::Pool, :expire_after => 2592000
703
+
704
+ get '/' do
705
+ "value = " << session[:value].inspect
706
+ end
707
+
708
+ get '/:value' do
709
+ session[:value] = params[:value]
710
+ end
711
+
712
+ To improve security, the session data in the cookie is signed with a session
713
+ secret. A random secret is generate for you by aldebaran. However, since this
714
+ secret will change with every start of your application, you might want to
715
+ set the secret yourself, so all your application instances share it:
716
+
717
+ set :session_secret, 'super secret'
718
+
719
+ If you want to configure it further, you may also store a hash with options in
720
+ the +sessions+ setting:
721
+
722
+ set :sessions, :domain => 'foo.com'
723
+
724
+ === Halting
725
+
726
+ To immediately stop a request within a filter or route use:
727
+
728
+ halt
729
+
730
+ You can also specify the status when halting:
731
+
732
+ halt 410
733
+
734
+ Or the body:
735
+
736
+ halt 'this will be the body'
737
+
738
+ Or both:
739
+
740
+ halt 401, 'go away!'
741
+
742
+ With headers:
743
+
744
+ halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
745
+
746
+ It is of course possible to combine a template with +halt+:
747
+
748
+ halt erb(:error)
749
+
750
+ === Passing
751
+
752
+ A route can punt processing to the next matching route using <tt>pass</tt>:
753
+
754
+ get '/guess/:who' do
755
+ pass unless params[:who] == 'Frank'
756
+ 'You got me!'
757
+ end
758
+
759
+ get '/guess/*' do
760
+ 'You missed!'
761
+ end
762
+
763
+ The route block is immediately exited and control continues with the next
764
+ matching route. If no matching route is found, a 404 is returned.
765
+
766
+ === Triggering Another Route
767
+
768
+ Sometimes +pass+ is not what you want, instead you would like to get the result
769
+ of calling another route. Simply use +call+ to achieve this:
770
+
771
+ get '/foo' do
772
+ status, headers, body = call env.merge("PATH_INFO" => '/bar')
773
+ [status, headers, body.map(&:upcase)]
774
+ end
775
+
776
+ get '/bar' do
777
+ "bar"
778
+ end
779
+
780
+ Note that in the example above, you would ease testing and increase performance
781
+ by simply moving <tt>"bar"</tt> into a helper used by both <tt>/foo</tt>
782
+ and <tt>/bar</tt>.
783
+
784
+ If you want the request to be sent to the same application instance rather than
785
+ a duplicate, use <tt>call!</tt> instead of <tt>call</tt>.
786
+
787
+ Check out the Rack specification if you want to learn more about <tt>call</tt>.
788
+
789
+ === Setting Body, Status Code and Headers
790
+
791
+ It is possible and recommended to set the status code and response body with the
792
+ return value of the route block. However, in some scenarios you might want to
793
+ set the body at an arbitrary point in the execution flow. You can do so with the
794
+ +body+ helper method. If you do so, you can use that method from there on to
795
+ access the body:
796
+
797
+ get '/foo' do
798
+ body "bar"
799
+ end
800
+
801
+ after do
802
+ puts body
803
+ end
804
+
805
+ It is also possible to pass a block to +body+, which will be executed by the
806
+ Rack handler (this can be used to implement streaming, see "Return Values").
807
+
808
+ Similar to the body, you can also set the status code and headers:
809
+
810
+ get '/foo' do
811
+ status 418
812
+ headers \
813
+ "Allow" => "BREW, POST, GET, PROPFIND, WHEN",
814
+ "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
815
+ body "I'm a tea pot!"
816
+ end
817
+
818
+ Like +body+, +headers+ and +status+ with no arguments can be used to access
819
+ their current values.
820
+
821
+ === Streaming Responses
822
+
823
+ Sometimes you want to start sending out data while still generating parts of
824
+ the response body. In extreme examples, you want to keep sending data until
825
+ the client closes the connection. You can use the +stream+ helper to avoid
826
+ creating your own wrapper:
827
+
828
+ get '/' do
829
+ stream do |out|
830
+ out << "It's gonna be legen -\n"
831
+ sleep 0.5
832
+ out << " (wait for it) \n"
833
+ sleep 1
834
+ out << "- dary!\n"
835
+ end
836
+ end
837
+
838
+ This allows you to implement streaming APIs,
839
+ {Server Sent Events}[http://dev.w3.org/html5/eventsource/] and can be used as
840
+ basis for {WebSockets}[http://en.wikipedia.org/wiki/WebSocket]. It can also be
841
+ used to increase throughput if some but not all content depends on a slow
842
+ resource.
843
+
844
+ Note that the streaming behavior, especially the number of concurrent request,
845
+ highly depends on the web server used to serve the application. Some servers,
846
+ like WEBRick, might not even support streaming at all. If the server does not
847
+ support streaming, the body will be sent all at once after the block passed to
848
+ +stream+ finished executing.
849
+
850
+ If the optional parameter is set to +false+, it will not call +close+ on the
851
+ stream object, allowing you to close it at any later point in the execution
852
+ flow. This only works on evented servers, like Thin and Rainbows. Other
853
+ servers will still close the stream.
854
+
855
+ set :server, :thin
856
+ connections = []
857
+
858
+ get '/' do
859
+ # keep stream open
860
+ stream(false) { |out| connections << out }
861
+ end
862
+
863
+ post '/' do
864
+ # write to all open streams
865
+ connections.each { |out| out << params[:message] << "\n" }
866
+ "message sent"
867
+ end
868
+
869
+ === Logging
870
+
871
+ In the request scope, the +logger+ helper exposes a +Logger+ instance:
872
+
873
+ get '/' do
874
+ logger.info "loading data"
875
+ # ...
876
+ end
877
+
878
+ This logger will automatically take your Rack handler's logging settings into
879
+ account. If logging is disabled, this method will return a dummy object, so
880
+ you do not have to worry in your routes and filters about it.
881
+
882
+ Note that logging is only enabled for <tt>aldebaran::Application</tt> by
883
+ default, so if you inherit from <tt>aldebaran::Base</tt>, you probably want to
884
+ enable it yourself:
885
+
886
+ class MyApp < aldebaran::Base
887
+ configure(:production, :development) do
888
+ enable :logging
889
+ end
890
+ end
891
+
892
+ === Mime Types
893
+
894
+ When using <tt>send_file</tt> or static files you may have mime types aldebaran
895
+ doesn't understand. Use +mime_type+ to register them by file extension:
896
+
897
+ configure do
898
+ mime_type :foo, 'text/foo'
899
+ end
900
+
901
+ You can also use it with the +content_type+ helper:
902
+
903
+ get '/' do
904
+ content_type :foo
905
+ "foo foo foo"
906
+ end
907
+
908
+ === Generating URLs
909
+
910
+ For generating URLs you should use the +url+ helper method, for instance, in
911
+ Haml:
912
+
913
+ %a{:href => url('/foo')} foo
914
+
915
+ It takes reverse proxies and Rack routers into account, if present.
916
+
917
+ This method is also aliased to +to+ (see below for an example).
918
+
919
+ === Browser Redirect
920
+
921
+ You can trigger a browser redirect with the +redirect+ helper method:
922
+
923
+ get '/foo' do
924
+ redirect to('/bar')
925
+ end
926
+
927
+ Any additional parameters are handled like arguments passed to +halt+:
928
+
929
+ redirect to('/bar'), 303
930
+ redirect 'http://google.com', 'wrong place, buddy'
931
+
932
+ You can also easily redirect back to the page the user came from with
933
+ <tt>redirect back</tt>:
934
+
935
+ get '/foo' do
936
+ "<a href='/bar'>do something</a>"
937
+ end
938
+
939
+ get '/bar' do
940
+ do_something
941
+ redirect back
942
+ end
943
+
944
+ To pass arguments with a redirect, either add them to the query:
945
+
946
+ redirect to('/bar?sum=42')
947
+
948
+ Or use a session:
949
+
950
+ enable :sessions
951
+
952
+ get '/foo' do
953
+ session[:secret] = 'foo'
954
+ redirect to('/bar')
955
+ end
956
+
957
+ get '/bar' do
958
+ session[:secret]
959
+ end
960
+
961
+ === Cache Control
962
+
963
+ Setting your headers correctly is the foundation for proper HTTP caching.
964
+
965
+ You can easily set the Cache-Control header with like this:
966
+
967
+ get '/' do
968
+ cache_control :public
969
+ "cache it!"
970
+ end
971
+
972
+ Pro tip: Set up caching in a before filter:
973
+
974
+ before do
975
+ cache_control :public, :must_revalidate, :max_age => 60
976
+ end
977
+
978
+ If you are using the +expires+ helper to set the corresponding header,
979
+ <tt>Cache-Control</tt> will be set automatically for you:
980
+
981
+ before do
982
+ expires 500, :public, :must_revalidate
983
+ end
984
+
985
+ To properly use caches, you should consider using +etag+ or +last_modified+.
986
+ It is recommended to call those helpers *before* doing heavy lifting, as they
987
+ will immediately flush a response if the client already has the current
988
+ version in its cache:
989
+
990
+ get '/article/:id' do
991
+ @article = Article.find params[:id]
992
+ last_modified @article.updated_at
993
+ etag @article.sha1
994
+ erb :article
995
+ end
996
+
997
+ It is also possible to use a
998
+ {weak ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation]:
999
+
1000
+ etag @article.sha1, :weak
1001
+
1002
+ These helpers will not do any caching for you, but rather feed the necessary
1003
+ information to your cache. If you are looking for a quick reverse-proxy caching solution,
1004
+ try {rack-cache}[http://rtomayko.github.com/rack-cache/]:
1005
+
1006
+ require "rack/cache"
1007
+ require "aldebaran"
1008
+
1009
+ use Rack::Cache
1010
+
1011
+ get '/' do
1012
+ cache_control :public, :max_age => 36000
1013
+ sleep 5
1014
+ "hello"
1015
+ end
1016
+
1017
+ Use the <tt>:static_cache_control</tt> setting (see below) to add
1018
+ <tt>Cache-Control</tt> header info to static files.
1019
+
1020
+ === Sending Files
1021
+
1022
+ For sending files, you can use the <tt>send_file</tt> helper method:
1023
+
1024
+ get '/' do
1025
+ send_file 'foo.png'
1026
+ end
1027
+
1028
+ It also takes a couple of options:
1029
+
1030
+ send_file 'foo.png', :type => :jpg
1031
+
1032
+ The options are:
1033
+
1034
+ [filename]
1035
+ file name, in response, defaults to the real file name.
1036
+
1037
+ [last_modified]
1038
+ value for Last-Modified header, defaults to the file's mtime.
1039
+
1040
+ [type]
1041
+ content type to use, guessed from the file extension if missing.
1042
+
1043
+ [disposition]
1044
+ used for Content-Disposition, possible values: +nil+ (default),
1045
+ <tt>:attachment</tt> and <tt>:inline</tt>
1046
+
1047
+ [length]
1048
+ Content-Length header, defaults to file size.
1049
+
1050
+ If supported by the Rack handler, other means than streaming from the Ruby
1051
+ process will be used. If you use this helper method, aldebaran will automatically
1052
+ handle range requests.
1053
+
1054
+ === Accessing the Request Object
1055
+
1056
+ The incoming request object can be accessed from request level (filter, routes,
1057
+ error handlers) through the <tt>request</tt> method:
1058
+
1059
+ # app running on http://example.com/example
1060
+ get '/foo' do
1061
+ t = %w[text/css text/html application/javascript]
1062
+ request.accept # ['text/html', '*/*']
1063
+ request.accept? 'text/xml' # true
1064
+ request.preferred_type(t) # 'text/html'
1065
+ request.body # request body sent by the client (see below)
1066
+ request.scheme # "http"
1067
+ request.script_name # "/example"
1068
+ request.path_info # "/foo"
1069
+ request.port # 80
1070
+ request.request_method # "GET"
1071
+ request.query_string # ""
1072
+ request.content_length # length of request.body
1073
+ request.media_type # media type of request.body
1074
+ request.host # "example.com"
1075
+ request.get? # true (similar methods for other verbs)
1076
+ request.form_data? # false
1077
+ request["SOME_HEADER"] # value of SOME_HEADER header
1078
+ request.referrer # the referrer of the client or '/'
1079
+ request.user_agent # user agent (used by :agent condition)
1080
+ request.cookies # hash of browser cookies
1081
+ request.xhr? # is this an ajax request?
1082
+ request.url # "http://example.com/example/foo"
1083
+ request.path # "/example/foo"
1084
+ request.ip # client IP address
1085
+ request.secure? # false (would be true over ssl)
1086
+ request.forwarded? # true (if running behind a reverse proxy)
1087
+ request.env # raw env hash handed in by Rack
1088
+ end
1089
+
1090
+ Some options, like <tt>script_name</tt> or <tt>path_info</tt>, can also be
1091
+ written:
1092
+
1093
+ before { request.path_info = "/" }
1094
+
1095
+ get "/" do
1096
+ "all requests end up here"
1097
+ end
1098
+
1099
+ The <tt>request.body</tt> is an IO or StringIO object:
1100
+
1101
+ post "/api" do
1102
+ request.body.rewind # in case someone already read it
1103
+ data = JSON.parse request.body.read
1104
+ "Hello #{data['name']}!"
1105
+ end
1106
+
1107
+ === Attachments
1108
+
1109
+ You can use the +attachment+ helper to tell the browser the response should be
1110
+ stored on disk rather than displayed in the browser:
1111
+
1112
+ get '/' do
1113
+ attachment
1114
+ "store it!"
1115
+ end
1116
+
1117
+ You can also pass it a file name:
1118
+
1119
+ get '/' do
1120
+ attachment "info.txt"
1121
+ "store it!"
1122
+ end
1123
+
1124
+ === Dealing with Date and Time
1125
+
1126
+ aldebaran offers a +time_for+ helper method, which, from the given value
1127
+ generates a Time object. It is also able to convert +DateTime+, +Date+ and
1128
+ similar classes.
1129
+
1130
+ get '/' do
1131
+ pass if Time.now > time_for('Dec 23, 2012')
1132
+ "still time"
1133
+ end
1134
+
1135
+ This method is used internally by +expires+, +last_modified+ and akin. You can
1136
+ therefore easily extend the behavior of those methods by overriding +time_for+
1137
+ in your application.
1138
+
1139
+ helpers do
1140
+ def time_for(value)
1141
+ case value
1142
+ when :yesterday then Time.now - 24*60*60
1143
+ when :tomorrow then Time.now + 24*60*60
1144
+ else super
1145
+ end
1146
+ end
1147
+ end
1148
+
1149
+ get '/' do
1150
+ last_modified :yesterday
1151
+ expires :tomorrow
1152
+ "hello"
1153
+ end
1154
+
1155
+ === Looking Up Template Files
1156
+
1157
+ The <tt>find_template</tt> helper is used to find template files for rendering:
1158
+
1159
+ find_template settings.views, 'foo', Tilt[:haml] do |file|
1160
+ puts "could be #{file}"
1161
+ end
1162
+
1163
+ This is not really useful. But it is useful that you can actually override this
1164
+ method to hook in your own lookup mechanism. For instance, if you want to be
1165
+ able to use more than one view directory:
1166
+
1167
+ set :views, ['views', 'templates']
1168
+
1169
+ helpers do
1170
+ def find_template(views, name, engine, &block)
1171
+ Array(views).each { |v| super(v, name, engine, &block) }
1172
+ end
1173
+ end
1174
+
1175
+ Another example would be using different directories for different engines:
1176
+
1177
+ set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views'
1178
+
1179
+ helpers do
1180
+ def find_template(views, name, engine, &block)
1181
+ _, folder = views.detect { |k,v| engine == Tilt[k] }
1182
+ folder ||= views[:default]
1183
+ super(folder, name, engine, &block)
1184
+ end
1185
+ end
1186
+
1187
+ You can also easily wrap this up in an extension and share with others!
1188
+
1189
+ Note that <tt>find_template</tt> does not check if the file really exists but
1190
+ rather calls the given block for all possible paths. This is not a performance
1191
+ issue, since +render+ will use +break+ as soon as a file is found. Also,
1192
+ template locations (and content) will be cached if you are not running in
1193
+ development mode. You should keep that in mind if you write a really crazy
1194
+ method.
1195
+
1196
+ == Configuration
1197
+
1198
+ Run once, at startup, in any environment:
1199
+
1200
+ configure do
1201
+ # setting one option
1202
+ set :option, 'value'
1203
+
1204
+ # setting multiple options
1205
+ set :a => 1, :b => 2
1206
+
1207
+ # same as `set :option, true`
1208
+ enable :option
1209
+
1210
+ # same as `set :option, false`
1211
+ disable :option
1212
+
1213
+ # you can also have dynamic settings with blocks
1214
+ set(:css_dir) { File.join(views, 'css') }
1215
+ end
1216
+
1217
+ Run only when the environment (RACK_ENV environment variable) is set to
1218
+ <tt>:production</tt>:
1219
+
1220
+ configure :production do
1221
+ ...
1222
+ end
1223
+
1224
+ Run when the environment is set to either <tt>:production</tt> or
1225
+ <tt>:test</tt>:
1226
+
1227
+ configure :production, :test do
1228
+ ...
1229
+ end
1230
+
1231
+ You can access those options via <tt>settings</tt>:
1232
+
1233
+ configure do
1234
+ set :foo, 'bar'
1235
+ end
1236
+
1237
+ get '/' do
1238
+ settings.foo? # => true
1239
+ settings.foo # => 'bar'
1240
+ ...
1241
+ end
1242
+
1243
+ === Available Settings
1244
+
1245
+ [absolute_redirects] If disabled, aldebaran will allow relative redirects,
1246
+ however, aldebaran will no longer conform with RFC 2616
1247
+ (HTTP 1.1), which only allows absolute redirects.
1248
+
1249
+ Enable if your app is running behind a reverse proxy that
1250
+ has not been set up properly. Note that the +url+ helper
1251
+ will still produce absolute URLs, unless you pass in
1252
+ +false+ as second parameter.
1253
+
1254
+ Disabled per default.
1255
+
1256
+ [add_charsets] mime types the <tt>content_type</tt> helper will
1257
+ automatically add the charset info to.
1258
+
1259
+ You should add to it rather than overriding this option:
1260
+
1261
+ settings.add_charsets << "application/foobar"
1262
+
1263
+ [app_file] main application file, used to detect project root,
1264
+ views and public folder and inline templates.
1265
+
1266
+ [bind] IP address to bind to (default: 0.0.0.0).
1267
+ Only used for built-in server.
1268
+
1269
+ [default_encoding] encoding to assume if unknown
1270
+ (defaults to <tt>"utf-8"</tt>).
1271
+
1272
+ [dump_errors] display errors in the log.
1273
+
1274
+ [environment] current environment, defaults to <tt>ENV['RACK_ENV']</tt>,
1275
+ or <tt>"development"</tt> if not available.
1276
+
1277
+ [logging] use the logger.
1278
+
1279
+ [lock] Places a lock around every request, only running
1280
+ processing on request per Ruby process concurrently.
1281
+
1282
+ Enabled if your app is not thread-safe.
1283
+ Disabled per default.
1284
+
1285
+ [method_override] use <tt>_method</tt> magic to allow put/delete forms in
1286
+ browsers that don't support it.
1287
+
1288
+ [port] Port to listen on. Only used for built-in server.
1289
+
1290
+ [prefixed_redirects] Whether or not to insert <tt>request.script_name</tt>
1291
+ into redirects if no absolute path is given. That way
1292
+ <tt>redirect '/foo'</tt> would behave like
1293
+ <tt>redirect to('/foo')</tt>. Disabled per default.
1294
+
1295
+ [public_folder] folder public files are served from
1296
+
1297
+ [reload_templates] whether or not to reload templates between requests.
1298
+ Enabled in development mode.
1299
+
1300
+ [root] project root folder.
1301
+
1302
+ [raise_errors] raise exceptions (will stop application).
1303
+
1304
+ [run] if enabled, aldebaran will handle starting the web server,
1305
+ do not enable if using rackup or other means.
1306
+
1307
+ [running] is the built-in server running now?
1308
+ do not change this setting!
1309
+
1310
+ [server] server or list of servers to use for built-in server.
1311
+ defaults to ['thin', 'mongrel', 'webrick'], order
1312
+ indicates priority.
1313
+
1314
+ [sessions] enable cookie based sessions.
1315
+
1316
+ [show_exceptions] show a stack trace in the browser.
1317
+
1318
+ [static] Whether aldebaran should handle serving static files.
1319
+ Disable when using a Server able to do this on its own.
1320
+ Disabling will boost performance.
1321
+ Enabled per default in classic style, disabled for
1322
+ modular apps.
1323
+
1324
+ [static_cache_control] When aldebaran is serving static files, set this to add
1325
+ <tt>Cache-Control</tt> headers to the responses. Uses the
1326
+ +cache_control+ helper. Disabled by default.
1327
+ Use an explicit array when setting multiple values:
1328
+ <tt>set :static_cache_control, [:public, :max_age => 300]</tt>
1329
+
1330
+ [threaded] If set to +true+, will tell Thin to use
1331
+ <tt>EventMachine.defer</tt> for processing the request.
1332
+
1333
+ [views] views folder.
1334
+
1335
+ == Error Handling
1336
+
1337
+ Error handlers run within the same context as routes and before filters, which
1338
+ means you get all the goodies it has to offer, like <tt>haml</tt>,
1339
+ <tt>erb</tt>, <tt>halt</tt>, etc.
1340
+
1341
+ === Not Found
1342
+
1343
+ When a <tt>aldebaran::NotFound</tt> exception is raised, or the response's status
1344
+ code is 404, the <tt>not_found</tt> handler is invoked:
1345
+
1346
+ not_found do
1347
+ 'This is nowhere to be found.'
1348
+ end
1349
+
1350
+ === Error
1351
+
1352
+ The +error+ handler is invoked any time an exception is raised from a route
1353
+ block or a filter. The exception object can be obtained from the
1354
+ <tt>aldebaran.error</tt> Rack variable:
1355
+
1356
+ error do
1357
+ 'Sorry there was a nasty error - ' + env['aldebaran.error'].name
1358
+ end
1359
+
1360
+ Custom errors:
1361
+
1362
+ error MyCustomError do
1363
+ 'So what happened was...' + env['aldebaran.error'].message
1364
+ end
1365
+
1366
+ Then, if this happens:
1367
+
1368
+ get '/' do
1369
+ raise MyCustomError, 'something bad'
1370
+ end
1371
+
1372
+ You get this:
1373
+
1374
+ So what happened was... something bad
1375
+
1376
+ Alternatively, you can install an error handler for a status code:
1377
+
1378
+ error 403 do
1379
+ 'Access forbidden'
1380
+ end
1381
+
1382
+ get '/secret' do
1383
+ 403
1384
+ end
1385
+
1386
+ Or a range:
1387
+
1388
+ error 400..510 do
1389
+ 'Boom'
1390
+ end
1391
+
1392
+ aldebaran installs special <tt>not_found</tt> and <tt>error</tt> handlers when
1393
+ running under the development environment.
1394
+
1395
+ == Rack Middleware
1396
+
1397
+ aldebaran rides on Rack[http://rack.rubyforge.org/], a minimal standard
1398
+ interface for Ruby web frameworks. One of Rack's most interesting capabilities
1399
+ for application developers is support for "middleware" -- components that sit
1400
+ between the server and your application monitoring and/or manipulating the
1401
+ HTTP request/response to provide various types of common functionality.
1402
+
1403
+ aldebaran makes building Rack middleware pipelines a cinch via a top-level
1404
+ +use+ method:
1405
+
1406
+ require 'aldebaran'
1407
+ require 'my_custom_middleware'
1408
+
1409
+ use Rack::Lint
1410
+ use MyCustomMiddleware
1411
+
1412
+ get '/hello' do
1413
+ 'Hello World'
1414
+ end
1415
+
1416
+ The semantics of +use+ are identical to those defined for the
1417
+ Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL
1418
+ (most frequently used from rackup files). For example, the +use+ method
1419
+ accepts multiple/variable args as well as blocks:
1420
+
1421
+ use Rack::Auth::Basic do |username, password|
1422
+ username == 'admin' && password == 'secret'
1423
+ end
1424
+
1425
+ Rack is distributed with a variety of standard middleware for logging,
1426
+ debugging, URL routing, authentication, and session handling. aldebaran uses
1427
+ many of these components automatically based on configuration so you
1428
+ typically don't have to +use+ them explicitly.
1429
+
1430
+ You can find useful middleware in
1431
+ {rack}[https://github.com/rack/rack/tree/master/lib/rack],
1432
+ {rack-contrib}[https://github.com/rack/rack-contrib#readme],
1433
+ with {CodeRack}[http://coderack.org/] or in the
1434
+ {Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware].
1435
+
1436
+ == Testing
1437
+
1438
+ aldebaran tests can be written using any Rack-based testing library
1439
+ or framework. {Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames]
1440
+ is recommended:
1441
+
1442
+ require 'my_aldebaran_app'
1443
+ require 'test/unit'
1444
+ require 'rack/test'
1445
+
1446
+ class MyAppTest < Test::Unit::TestCase
1447
+ include Rack::Test::Methods
1448
+
1449
+ def app
1450
+ aldebaran::Application
1451
+ end
1452
+
1453
+ def test_my_default
1454
+ get '/'
1455
+ assert_equal 'Hello World!', last_response.body
1456
+ end
1457
+
1458
+ def test_with_params
1459
+ get '/meet', :name => 'Frank'
1460
+ assert_equal 'Hello Frank!', last_response.body
1461
+ end
1462
+
1463
+ def test_with_rack_env
1464
+ get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
1465
+ assert_equal "You're using Songbird!", last_response.body
1466
+ end
1467
+ end
1468
+
1469
+ == aldebaran::Base - Middleware, Libraries, and Modular Apps
1470
+
1471
+ Defining your app at the top-level works well for micro-apps but has
1472
+ considerable drawbacks when building reusable components such as Rack
1473
+ middleware, Rails metal, simple libraries with a server component, or
1474
+ even aldebaran extensions. The top-level DSL pollutes the Object namespace
1475
+ and assumes a micro-app style configuration (e.g., a single application
1476
+ file, <tt>./public</tt> and <tt>./views</tt> directories, logging, exception
1477
+ detail page, etc.). That's where <tt>aldebaran::Base</tt> comes into play:
1478
+
1479
+ require 'aldebaran/base'
1480
+
1481
+ class MyApp < aldebaran::Base
1482
+ set :sessions, true
1483
+ set :foo, 'bar'
1484
+
1485
+ get '/' do
1486
+ 'Hello world!'
1487
+ end
1488
+ end
1489
+
1490
+ The methods available to <tt>aldebaran::Base</tt> subclasses are exactly as those
1491
+ available via the top-level DSL. Most top-level apps can be converted to
1492
+ <tt>aldebaran::Base</tt> components with two modifications:
1493
+
1494
+ * Your file should require <tt>aldebaran/base</tt> instead of +aldebaran+;
1495
+ otherwise, all of aldebaran's DSL methods are imported into the main
1496
+ namespace.
1497
+ * Put your app's routes, error handlers, filters, and options in a subclass
1498
+ of <tt>aldebaran::Base</tt>.
1499
+
1500
+ <tt>aldebaran::Base</tt> is a blank slate. Most options are disabled by default,
1501
+ including the built-in server. See {Options and Configuration}[http://aldebaran.github.com/configuration.html]
1502
+ for details on available options and their behavior.
1503
+
1504
+ === Modular vs. Classic Style
1505
+
1506
+ Contrary to common belief, there is nothing wrong with classic style. If it
1507
+ suits your application, you do not have to switch to a modular application.
1508
+
1509
+ There are only two downsides compared with modular style:
1510
+
1511
+ * You may only have one aldebaran application per Ruby process. If you plan to
1512
+ use more, switch to modular style.
1513
+
1514
+ * Classic style pollutes Object with delegator methods. If you plan to ship
1515
+ your application in a library/gem, switch to modular style.
1516
+
1517
+ There is no reason you cannot mix modular and classic style.
1518
+
1519
+ If switching from one style to the other, you should be aware of slightly
1520
+ different default settings:
1521
+
1522
+ Setting Classic Modular
1523
+
1524
+ app_file file loading aldebaran file subclassing aldebaran::Base
1525
+ run $0 == app_file false
1526
+ logging true false
1527
+ method_override true false
1528
+ inline_templates true false
1529
+ static true false
1530
+
1531
+
1532
+ === Serving a Modular Application
1533
+
1534
+ There are two common options for starting a modular app, actively starting with
1535
+ <tt>run!</tt>:
1536
+
1537
+ # my_app.rb
1538
+ require 'aldebaran/base'
1539
+
1540
+ class MyApp < aldebaran::Base
1541
+ # ... app code here ...
1542
+
1543
+ # start the server if ruby file executed directly
1544
+ run! if app_file == $0
1545
+ end
1546
+
1547
+ Start with:
1548
+
1549
+ ruby my_app.rb
1550
+
1551
+ Or with a <tt>config.ru</tt>, which allows using any Rack handler:
1552
+
1553
+ # config.ru
1554
+ require './my_app'
1555
+ run MyApp
1556
+
1557
+ Run:
1558
+
1559
+ rackup -p 4567
1560
+
1561
+ === Using a Classic Style Application with a config.ru
1562
+
1563
+ Write your app file:
1564
+
1565
+ # app.rb
1566
+ require 'aldebaran'
1567
+
1568
+ get '/' do
1569
+ 'Hello world!'
1570
+ end
1571
+
1572
+ And a corresponding <tt>config.ru</tt>:
1573
+
1574
+ require './app'
1575
+ run aldebaran::Application
1576
+
1577
+ === When to use a config.ru?
1578
+
1579
+ Good signs you probably want to use a <tt>config.ru</tt>:
1580
+
1581
+ * You want to deploy with a different Rack handler (Passenger, Unicorn,
1582
+ Heroku, ...).
1583
+ * You want to use more than one subclass of <tt>aldebaran::Base</tt>.
1584
+ * You want to use aldebaran only for middleware, but not as endpoint.
1585
+
1586
+ <b>There is no need to switch to a <tt>config.ru</tt> only because you
1587
+ switched to modular style, and you don't have to use modular style for running
1588
+ with a <tt>config.ru</tt>.</b>
1589
+
1590
+ === Using aldebaran as Middleware
1591
+
1592
+ Not only is aldebaran able to use other Rack middleware, any aldebaran application
1593
+ can in turn be added in front of any Rack endpoint as middleware itself. This
1594
+ endpoint could be another aldebaran application, or any other Rack-based
1595
+ application (Rails/Ramaze/Camping/...):
1596
+
1597
+ require 'aldebaran/base'
1598
+
1599
+ class LoginScreen < aldebaran::Base
1600
+ enable :sessions
1601
+
1602
+ get('/login') { haml :login }
1603
+
1604
+ post('/login') do
1605
+ if params[:name] == 'admin' && params[:password] == 'admin'
1606
+ session['user_name'] = params[:name]
1607
+ else
1608
+ redirect '/login'
1609
+ end
1610
+ end
1611
+ end
1612
+
1613
+ class MyApp < aldebaran::Base
1614
+ # middleware will run before filters
1615
+ use LoginScreen
1616
+
1617
+ before do
1618
+ unless session['user_name']
1619
+ halt "Access denied, please <a href='/login'>login</a>."
1620
+ end
1621
+ end
1622
+
1623
+ get('/') { "Hello #{session['user_name']}." }
1624
+ end
1625
+
1626
+ === Dynamic Application Creation
1627
+
1628
+ Sometimes you want to create new applications at runtime without having to
1629
+ assign them to a constant, you can do this with <tt>aldebaran.new</tt>:
1630
+
1631
+ require 'aldebaran/base'
1632
+ my_app = aldebaran.new { get('/') { "hi" } }
1633
+ my_app.run!
1634
+
1635
+ It takes the application to inherit from as optional argument:
1636
+
1637
+ # config.ru
1638
+ require 'aldebaran/base'
1639
+
1640
+ controller = aldebaran.new do
1641
+ enable :logging
1642
+ helpers MyHelpers
1643
+ end
1644
+
1645
+ map('/a') do
1646
+ run aldebaran.new(controller) { get('/') { 'a' } }
1647
+ end
1648
+
1649
+ map('/b') do
1650
+ run aldebaran.new(controller) { get('/') { 'b' } }
1651
+ end
1652
+
1653
+ This is especially useful for testing aldebaran extensions or using aldebaran in
1654
+ your own library.
1655
+
1656
+ This also makes using aldebaran as middleware extremely easy:
1657
+
1658
+ require 'aldebaran/base'
1659
+
1660
+ use aldebaran do
1661
+ get('/') { ... }
1662
+ end
1663
+
1664
+ run RailsProject::Application
1665
+
1666
+ == Scopes and Binding
1667
+
1668
+ The scope you are currently in determines what methods and variables are
1669
+ available.
1670
+
1671
+ === Application/Class Scope
1672
+
1673
+ Every aldebaran application corresponds to a subclass of <tt>aldebaran::Base</tt>.
1674
+ If you are using the top-level DSL (<tt>require 'aldebaran'</tt>), then this
1675
+ class is <tt>aldebaran::Application</tt>, otherwise it is the subclass you
1676
+ created explicitly. At class level you have methods like +get+ or +before+, but
1677
+ you cannot access the +request+ object or the +session+, as there only is a
1678
+ single application class for all requests.
1679
+
1680
+ Options created via +set+ are methods at class level:
1681
+
1682
+ class MyApp < aldebaran::Base
1683
+ # Hey, I'm in the application scope!
1684
+ set :foo, 42
1685
+ foo # => 42
1686
+
1687
+ get '/foo' do
1688
+ # Hey, I'm no longer in the application scope!
1689
+ end
1690
+ end
1691
+
1692
+ You have the application scope binding inside:
1693
+
1694
+ * Your application class body
1695
+ * Methods defined by extensions
1696
+ * The block passed to +helpers+
1697
+ * Procs/blocks used as value for +set+
1698
+ * The block passed to <tt>aldebaran.new</tt>
1699
+
1700
+ You can reach the scope object (the class) like this:
1701
+
1702
+ * Via the object passed to configure blocks (<tt>configure { |c| ... }</tt>)
1703
+ * +settings+ from within request scope
1704
+
1705
+ === Request/Instance Scope
1706
+
1707
+ For every incoming request, a new instance of your application class is
1708
+ created and all handler blocks run in that scope. From within this scope you
1709
+ can access the +request+ and +session+ object or call rendering methods like
1710
+ +erb+ or +haml+. You can access the application scope from within the request
1711
+ scope via the +settings+ helper:
1712
+
1713
+ class MyApp < aldebaran::Base
1714
+ # Hey, I'm in the application scope!
1715
+ get '/define_route/:name' do
1716
+ # Request scope for '/define_route/:name'
1717
+ @value = 42
1718
+
1719
+ settings.get("/#{params[:name]}") do
1720
+ # Request scope for "/#{params[:name]}"
1721
+ @value # => nil (not the same request)
1722
+ end
1723
+
1724
+ "Route defined!"
1725
+ end
1726
+ end
1727
+
1728
+ You have the request scope binding inside:
1729
+
1730
+ * get/head/post/put/delete/options blocks
1731
+ * before/after filters
1732
+ * helper methods
1733
+ * templates/views
1734
+
1735
+ === Delegation Scope
1736
+
1737
+ The delegation scope just forwards methods to the class scope. However, it
1738
+ does not behave 100% like the class scope, as you do not have the class
1739
+ binding. Only methods explicitly marked for delegation are available and you
1740
+ do not share variables/state with the class scope (read: you have a different
1741
+ +self+). You can explicitly add method delegations by calling
1742
+ <tt>aldebaran::Delegator.delegate :method_name</tt>.
1743
+
1744
+ You have the delegate scope binding inside:
1745
+
1746
+ * The top level binding, if you did <tt>require "aldebaran"</tt>
1747
+ * An object extended with the <tt>aldebaran::Delegator</tt> mixin
1748
+
1749
+ Have a look at the code for yourself: here's the
1750
+ {aldebaran::Delegator mixin}[http://github.com/aldebaran/aldebaran/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/aldebaran/base.rb#L1128]
1751
+ being {included into the main namespace}[http://github.com/aldebaran/aldebaran/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/aldebaran/main.rb#L28].
1752
+
1753
+ == Command Line
1754
+
1755
+ aldebaran applications can be run directly:
1756
+
1757
+ ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER]
1758
+
1759
+ Options are:
1760
+
1761
+ -h # help
1762
+ -p # set the port (default is 4567)
1763
+ -o # set the host (default is 0.0.0.0)
1764
+ -e # set the environment (default is development)
1765
+ -s # specify rack server/handler (default is thin)
1766
+ -x # turn on the mutex lock (default is off)
1767
+
1768
+ == Requirement
1769
+
1770
+ The following Ruby versions are officially supported:
1771
+
1772
+ [ Ruby 1.8.7 ]
1773
+ 1.8.7 is fully supported, however, if nothing is keeping you from it, we
1774
+ recommend upgrading to 1.9.2 or switching to JRuby or Rubinius.
1775
+
1776
+ [ Ruby 1.9.2 ]
1777
+ 1.9.2 is supported and recommended. Note that Radius and Markaby are
1778
+ currently not 1.9 compatible. Do not use 1.9.2p0, it is known to cause
1779
+ segmentation faults when running aldebaran.
1780
+
1781
+ [ Rubinius ]
1782
+ Rubinius is officially supported (Rubinius >= 1.2.3), everything, including
1783
+ all template languages, works.
1784
+
1785
+ [ JRuby ]
1786
+ JRuby is officially supported (JRuby >= 1.6.1). No issues with third party
1787
+ template libraries are known, however, if you choose to use JRuby, please
1788
+ look into JRuby rack handlers, as the Thin web server is not fully supported
1789
+ on JRuby. JRuby's support for C extensions is still experimental, which only
1790
+ affects RDiscount and Redcarpet at the moment.
1791
+
1792
+ <b>Ruby 1.8.6 is no longer supported.</b> If you want to run with 1.8.6,
1793
+ downgrade to aldebaran 1.2, which will receive bug fixes until aldebaran 1.4.0 is
1794
+ released.
1795
+
1796
+ We also keep an eye on upcoming Ruby versions.
1797
+
1798
+ The following Ruby implementations are not officially supported but still are
1799
+ known to run aldebaran:
1800
+
1801
+ * Older versions of JRuby and Rubinius
1802
+ * MacRuby, Maglev, IronRuby
1803
+ * Ruby 1.9.0 and 1.9.1
1804
+
1805
+ Not being officially supported means if things only break there and not on a
1806
+ supported platform, we assume it's not our issue but theirs.
1807
+
1808
+ We also run our CI against ruby-head (the upcoming 1.9.3), but we can't
1809
+ guarantee anything, since it is constantly moving. Expect 1.9.3p0 to be
1810
+ supported.
1811
+
1812
+ aldebaran should work on any operating system supported by the chosen Ruby
1813
+ implementation.
1814
+
1815
+ == The Bleeding Edge
1816
+
1817
+ If you would like to use aldebaran's latest bleeding code, feel free to run your
1818
+ application against the master branch, it should be rather stable.
1819
+
1820
+ We also push out prerelease gems from time to time, so you can do a
1821
+
1822
+ gem install aldebaran --pre
1823
+
1824
+ To get some of the latest features.
1825
+
1826
+ === With Bundler
1827
+
1828
+ If you want to run your application with the latest aldebaran, using
1829
+ {Bundler}[http://gembundler.com/] is the recommended way.
1830
+
1831
+ First, install bundler, if you haven't:
1832
+
1833
+ gem install bundler
1834
+
1835
+ Then, in your project directory, create a +Gemfile+:
1836
+
1837
+ source :rubygems
1838
+ gem 'aldebaran', :git => "git://github.com/aldebaran/aldebaran.git"
1839
+
1840
+ # other dependencies
1841
+ gem 'haml' # for instance, if you use haml
1842
+ gem 'activerecord', '~> 3.0' # maybe you also need ActiveRecord 3.x
1843
+
1844
+ Note that you will have to list all your applications dependencies in there.
1845
+ aldebaran's direct dependencies (Rack and Tilt) will, however, be automatically
1846
+ fetched and added by Bundler.
1847
+
1848
+ Now you can run your app like this:
1849
+
1850
+ bundle exec ruby myapp.rb
1851
+
1852
+ === Roll Your Own
1853
+
1854
+ Create a local clone and run your app with the <tt>aldebaran/lib</tt> directory
1855
+ on the <tt>$LOAD_PATH</tt>:
1856
+
1857
+ cd myapp
1858
+ git clone git://github.com/aldebaran/aldebaran.git
1859
+ ruby -Ialdebaran/lib myapp.rb
1860
+
1861
+ To update the aldebaran sources in the future:
1862
+
1863
+ cd myapp/aldebaran
1864
+ git pull
1865
+
1866
+ === Install Globally
1867
+
1868
+ You can build the gem on your own:
1869
+
1870
+ git clone git://github.com/aldebaran/aldebaran.git
1871
+ cd aldebaran
1872
+ rake aldebaran.gemspec
1873
+ rake install
1874
+
1875
+ If you install gems as root, the last step should be
1876
+
1877
+ sudo rake install
1878
+
1879
+ == Versioning
1880
+
1881
+ aldebaran follows {Semantic Versioning}[http://semver.org/], both SemVer and
1882
+ SemVerTag.
1883
+
1884
+ == Further Reading
1885
+
1886
+ * {Project Website}[http://www.aldebaranrb.com/] - Additional documentation,
1887
+ news, and links to other resources.
1888
+ * {Contributing}[http://www.aldebaranrb.com/contributing] - Find a bug? Need
1889
+ help? Have a patch?
1890
+ * {Issue tracker}[http://github.com/aldebaran/aldebaran/issues]
1891
+ * {Twitter}[http://twitter.com/aldebaran]
1892
+ * {Mailing List}[http://groups.google.com/group/aldebaranrb/topics]
1893
+ * {IRC: #aldebaran}[irc://chat.freenode.net/#aldebaran] on http://freenode.net
1894
+ * {aldebaran Book}[http://aldebaran-book.gittr.com] Cookbook Tutorial
1895
+ * {aldebaran Book Contrib}[http://aldebaran-book-contrib.com/] Community
1896
+ contributed recipes
1897
+ * API documentation for the {latest release}[http://rubydoc.info/gems/aldebaran]
1898
+ or the {current HEAD}[http://rubydoc.info/github/aldebaran/aldebaran] on
1899
+ http://rubydoc.info
1900
+ * {CI server}[http://ci.rkh.im/view/aldebaran/]