adamwiggins-sinatra 0.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/AUTHORS +40 -0
  2. data/CHANGES +167 -0
  3. data/LICENSE +22 -0
  4. data/README.rdoc +529 -0
  5. data/Rakefile +180 -0
  6. data/compat/app_test.rb +300 -0
  7. data/compat/application_test.rb +334 -0
  8. data/compat/builder_test.rb +101 -0
  9. data/compat/custom_error_test.rb +62 -0
  10. data/compat/erb_test.rb +136 -0
  11. data/compat/events_test.rb +75 -0
  12. data/compat/filter_test.rb +30 -0
  13. data/compat/haml_test.rb +233 -0
  14. data/compat/helper.rb +21 -0
  15. data/compat/mapped_error_test.rb +72 -0
  16. data/compat/pipeline_test.rb +71 -0
  17. data/compat/public/foo.xml +1 -0
  18. data/compat/sass_test.rb +57 -0
  19. data/compat/sessions_test.rb +39 -0
  20. data/compat/streaming_test.rb +121 -0
  21. data/compat/sym_params_test.rb +19 -0
  22. data/compat/template_test.rb +30 -0
  23. data/compat/use_in_file_templates_test.rb +47 -0
  24. data/compat/views/foo.builder +1 -0
  25. data/compat/views/foo.erb +1 -0
  26. data/compat/views/foo.haml +1 -0
  27. data/compat/views/foo.sass +2 -0
  28. data/compat/views/foo_layout.erb +2 -0
  29. data/compat/views/foo_layout.haml +2 -0
  30. data/compat/views/layout_test/foo.builder +1 -0
  31. data/compat/views/layout_test/foo.erb +1 -0
  32. data/compat/views/layout_test/foo.haml +1 -0
  33. data/compat/views/layout_test/foo.sass +2 -0
  34. data/compat/views/layout_test/layout.builder +3 -0
  35. data/compat/views/layout_test/layout.erb +1 -0
  36. data/compat/views/layout_test/layout.haml +1 -0
  37. data/compat/views/layout_test/layout.sass +2 -0
  38. data/compat/views/no_layout/no_layout.builder +1 -0
  39. data/compat/views/no_layout/no_layout.haml +1 -0
  40. data/lib/sinatra/base.rb +818 -0
  41. data/lib/sinatra/compat.rb +239 -0
  42. data/lib/sinatra/images/404.png +0 -0
  43. data/lib/sinatra/images/500.png +0 -0
  44. data/lib/sinatra/main.rb +48 -0
  45. data/lib/sinatra/test/rspec.rb +2 -0
  46. data/lib/sinatra/test/spec.rb +2 -0
  47. data/lib/sinatra/test/unit.rb +11 -0
  48. data/lib/sinatra/test.rb +112 -0
  49. data/lib/sinatra.rb +3 -0
  50. data/sinatra.gemspec +107 -0
  51. data/test/base_test.rb +72 -0
  52. data/test/builder_test.rb +68 -0
  53. data/test/data/reload_app_file.rb +3 -0
  54. data/test/erb_test.rb +55 -0
  55. data/test/filter_test.rb +39 -0
  56. data/test/haml_test.rb +72 -0
  57. data/test/helpers_test.rb +368 -0
  58. data/test/mapped_error_test.rb +164 -0
  59. data/test/middleware_test.rb +63 -0
  60. data/test/options_test.rb +103 -0
  61. data/test/reload_test.rb +65 -0
  62. data/test/request_test.rb +11 -0
  63. data/test/result_test.rb +92 -0
  64. data/test/routing_test.rb +338 -0
  65. data/test/sass_test.rb +40 -0
  66. data/test/sinatra_test.rb +15 -0
  67. data/test/static_test.rb +60 -0
  68. data/test/templates_test.rb +92 -0
  69. data/test/views/hello.builder +1 -0
  70. data/test/views/hello.erb +1 -0
  71. data/test/views/hello.haml +1 -0
  72. data/test/views/hello.sass +2 -0
  73. data/test/views/hello.test +1 -0
  74. data/test/views/layout2.builder +3 -0
  75. data/test/views/layout2.erb +2 -0
  76. data/test/views/layout2.haml +2 -0
  77. data/test/views/layout2.test +1 -0
  78. metadata +159 -0
data/README.rdoc ADDED
@@ -0,0 +1,529 @@
1
+ = Sinatra
2
+
3
+ Sinatra is a DSL for quickly creating web-applications in Ruby with minimal
4
+ effort:
5
+
6
+ # myapp.rb
7
+ require 'rubygems'
8
+ require 'sinatra'
9
+ get '/' do
10
+ 'Hello world!'
11
+ end
12
+
13
+ Run with <tt>ruby myapp.rb</tt> and view at <tt>http://localhost:4567</tt>
14
+
15
+ == HTTP Methods
16
+
17
+ get '/' do
18
+ .. show things ..
19
+ end
20
+
21
+ post '/' do
22
+ .. create something ..
23
+ end
24
+
25
+ put '/' do
26
+ .. update something ..
27
+ end
28
+
29
+ delete '/' do
30
+ .. annihilate something ..
31
+ end
32
+
33
+ == Routes
34
+
35
+ Routes are matched based on the order of declaration. The first route that
36
+ matches the request is invoked.
37
+
38
+ Basic routes:
39
+
40
+ get '/hi' do
41
+ ...
42
+ end
43
+
44
+ Route patterns may include named parameters, accessible via the
45
+ <tt>params</tt> hash:
46
+
47
+ get '/:name' do
48
+ # matches "GET /foo" and "GET /bar"
49
+ # params[:name] is 'foo' or 'bar'
50
+ "Hello #{params[:name]}!"
51
+ end
52
+
53
+ Route patterns may also include splat (or wildcard) parameters, accessible
54
+ via the <tt>params[:splat]</tt> array.
55
+
56
+ get '/say/*/to/*' do
57
+ # matches /say/hello/to/world
58
+ params[:splat] # => ["hello", "world"]
59
+ end
60
+
61
+ get '/download/*.*' do
62
+ # matches /download/path/to/file.xml
63
+ params[:splat] # => ["path/to/file", "xml"]
64
+ end
65
+
66
+ Route matching with Regular Expressions:
67
+
68
+ get %r{/hello/([\w]+)} do
69
+ "Hello, #{params[:captures].first}!"
70
+ end
71
+
72
+ Routes may include a variety of matching conditions, such as the user agent:
73
+
74
+ get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
75
+ "You're using Songbird version #{params[:agent][0]}"
76
+ end
77
+
78
+ get '/foo' do
79
+ # Matches non-songbird browsers
80
+ end
81
+
82
+ == Static Files
83
+
84
+ Static files are served from the <tt>./public</tt> directory. You can specify
85
+ a different location by setting the <tt>:public</tt> option:
86
+
87
+ set :public, File.dirname(__FILE__) + '/static'
88
+
89
+ == Views / Templates
90
+
91
+ Templates are assumed to be located directly under a <tt>./views</tt>
92
+ directory. To use a different views directory:
93
+
94
+ set :views, File.dirname(__FILE__) + '/templates'
95
+
96
+ === Haml Templates
97
+
98
+ The haml gem/library is required to render HAML templates:
99
+
100
+ get '/' do
101
+ haml :index
102
+ end
103
+
104
+ Renders <tt>./views/index.haml</tt>.
105
+
106
+ === Erb Templates
107
+
108
+ get '/' do
109
+ erb :index
110
+ end
111
+
112
+ Renders <tt>./views/index.erb</tt>
113
+
114
+ === Builder Templates
115
+
116
+ The builder gem/library is required to render builder templates:
117
+
118
+ get '/' do
119
+ content_type 'application/xml', :charset => 'utf-8'
120
+ builder :index
121
+ end
122
+
123
+ Renders <tt>./views/index.builder</tt>.
124
+
125
+ === Sass Templates
126
+
127
+ The sass gem/library is required to render Sass templates:
128
+
129
+ get '/stylesheet.css' do
130
+ content_type 'text/css', :charset => 'utf-8'
131
+ sass :stylesheet
132
+ end
133
+
134
+ Renders <tt>./views/stylesheet.sass</tt>.
135
+
136
+ === Inline Templates
137
+
138
+ get '/' do
139
+ haml '%div.title Hello World'
140
+ end
141
+
142
+ Renders the inlined template string.
143
+
144
+ === Accessing Variables
145
+
146
+ Templates are evaluated within the same context as the route blocks. Instance
147
+ variables set in route blocks are available in templates:
148
+
149
+ get '/:id' do
150
+ @foo = Foo.find(params[:id])
151
+ haml '%h1= @foo.name'
152
+ end
153
+
154
+ Or, specify an explicit Hash of local variables:
155
+
156
+ get '/:id' do
157
+ foo = Foo.find(params[:id])
158
+ haml '%h1= foo.name', :locals => { :foo => foo }
159
+ end
160
+
161
+ This is typically used when rendering templates as partials from within
162
+ other templates.
163
+
164
+ === In-file Templates
165
+
166
+ Templates may be defined at the end of the source file:
167
+
168
+ get '/' do
169
+ haml :index
170
+ end
171
+
172
+ use_in_file_templates!
173
+
174
+ __END__
175
+
176
+ @@ layout
177
+ %html
178
+ = yield
179
+
180
+ @@ index
181
+ %div.title Hello world!!!!!
182
+
183
+ It's also possible to define named templates using the top-level template
184
+ method:
185
+
186
+ template :layout do
187
+ "%html\n =yield\n"
188
+ end
189
+
190
+ template :index do
191
+ '%div.title Hello World!'
192
+ end
193
+
194
+ get '/' do
195
+ haml :index
196
+ end
197
+
198
+ If a template named "layout" exists, it will be used each time a template
199
+ is rendered. You can disable layouts by passing <tt>:layout => false</tt>.
200
+
201
+ get '/' do
202
+ haml :index, :layout => !request.xhr?
203
+ end
204
+
205
+ == Helpers
206
+
207
+ Use the top-level <tt>helpers</tt> method to define helper methods for use in
208
+ route blocks and templates:
209
+
210
+ helpers do
211
+ def bar(name)
212
+ "#{name}bar"
213
+ end
214
+ end
215
+
216
+ get '/:name' do
217
+ bar(params[:name])
218
+ end
219
+
220
+ == Filters
221
+
222
+ Before filters are evaluated before each request within the context of the
223
+ request and can modify the request and response. Instance variables set in
224
+ filters are accessible by routes and templates.
225
+
226
+ before do
227
+ @note = 'Hi!'
228
+ request.path_info = '/foo/bar/baz'
229
+ end
230
+
231
+ get '/foo/*' do
232
+ @note #=> 'Hi!'
233
+ params[:splat] #=> 'bar/baz'
234
+ end
235
+
236
+ == Halting
237
+
238
+ To immediately stop a request during a before filter or route use:
239
+
240
+ halt
241
+
242
+ You can also specify a body when halting ...
243
+
244
+ halt 'this will be the body'
245
+
246
+ Set the status and body ...
247
+
248
+ halt 401, 'go away!'
249
+
250
+ == Passing
251
+
252
+ A route can punt processing to the next matching route using the <tt>pass</tt>
253
+ statement:
254
+
255
+ get '/guess/:who' do
256
+ pass unless params[:who] == 'Frank'
257
+ "You got me!"
258
+ end
259
+
260
+ get '/guess/*' do
261
+ "You missed!"
262
+ end
263
+
264
+ The route block is immediately exited and control continues with the next
265
+ matching route. If no matching route is found, a 404 is returned.
266
+
267
+ == Configuration and Reloading
268
+
269
+ Sinatra supports multiple environments and reloading. Reloading happens
270
+ before each request when running under the <tt>:development</tt>
271
+ environment. Wrap your configurations (e.g., database connections, constants,
272
+ etc.) in <tt>configure</tt> blocks to protect them from reloading or to
273
+ target specific environments.
274
+
275
+ Run once, at startup, in any environment:
276
+
277
+ configure do
278
+ ...
279
+ end
280
+
281
+ Run only when the environment (RACK_ENV environment variable) is set to
282
+ <tt>:production</tt>.
283
+
284
+ configure :production do
285
+ ...
286
+ end
287
+
288
+ Run when the environment (RACK_ENV environment variable) is set to
289
+ either <tt>:production</tt> or <tt>:test</tt>.
290
+
291
+ configure :production, :test do
292
+ ...
293
+ end
294
+
295
+ == Error handling
296
+
297
+ Error handlers run within the same context as routes and before filters, which
298
+ means you get all the goodies it has to offer, like <tt>haml</tt>, <tt>erb</tt>,
299
+ <tt>halt</tt>, etc.
300
+
301
+ === Not Found
302
+
303
+ When a <tt>Sinatra::NotFound</tt> exception is raised, or the response's status
304
+ code is 404, the <tt>not_found</tt> handler is invoked:
305
+
306
+ not_found do
307
+ 'This is nowhere to be found'
308
+ end
309
+
310
+ === Error
311
+
312
+ The +error+ handler is invoked any time an exception is raised from a route
313
+ block or before filter. The exception object can be obtained from the
314
+ 'sinatra.error' Rack variable:
315
+
316
+ error do
317
+ 'Sorry there was a nasty error - ' + env['sinatra.error'].name
318
+ end
319
+
320
+ Custom errors:
321
+
322
+ error MyCustomError do
323
+ 'So what happened was...' + request.env['sinatra.error'].message
324
+ end
325
+
326
+ Then, if this happens:
327
+
328
+ get '/' do
329
+ raise MyCustomError, 'something bad'
330
+ end
331
+
332
+ You get this:
333
+
334
+ So what happened was... something bad
335
+
336
+ Sinatra installs special not_found and error handlers when running under
337
+ the development environment.
338
+
339
+ == Mime types
340
+
341
+ When using <tt>send_file</tt> or static files you may have mime types Sinatra
342
+ doesn't understand. Use +mime+ to register them by file extension:
343
+
344
+ mime :foo, 'text/foo'
345
+
346
+ == Rack Middleware
347
+
348
+ Sinatra rides on Rack[http://rack.rubyforge.org/], a minimal standard
349
+ interface for Ruby web frameworks. One of Rack's most interesting capabilities
350
+ for application developers is support for "middleware" -- components that sit
351
+ between the server and your application monitoring and/or manipulating the
352
+ HTTP request/response to provide various types of common functionality.
353
+
354
+ Sinatra makes building Rack middleware pipelines a cinch via a top-level
355
+ +use+ method:
356
+
357
+ require 'sinatra'
358
+ require 'my_custom_middleware'
359
+
360
+ use Rack::Lint
361
+ use MyCustomMiddleware
362
+
363
+ get '/hello' do
364
+ 'Hello World'
365
+ end
366
+
367
+ The semantics of +use+ are identical to those defined for the
368
+ Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL
369
+ (most frequently used from rackup files). For example, the +use+ method
370
+ accepts multiple/variable args as well as blocks:
371
+
372
+ use Rack::Auth::Basic do |username, password|
373
+ username == 'admin' && password == 'secret'
374
+ end
375
+
376
+ Rack is distributed with a variety of standard middleware for logging,
377
+ debugging, URL routing, authentication, and session handling. Sinatra uses
378
+ many of of these components automatically based on configuration so you
379
+ typically don't have to +use+ them explicitly.
380
+
381
+ == Testing
382
+
383
+ === Test/Unit
384
+
385
+ require 'rubygems'
386
+ require 'sinatra'
387
+ require 'sinatra/test/unit'
388
+ require 'my_sinatra_app'
389
+
390
+ class MyAppTest < Test::Unit::TestCase
391
+
392
+ def test_my_default
393
+ get_it '/'
394
+ assert_equal 'My Default Page!', @response.body
395
+ end
396
+
397
+ def test_with_agent
398
+ get_it '/', :agent => 'Songbird'
399
+ assert_equal 'You're in Songbird!', @response.body
400
+ end
401
+
402
+ ...
403
+
404
+ end
405
+
406
+ === Test/Spec
407
+
408
+ require 'rubygems'
409
+ require 'sinatra'
410
+ require 'sinatra/test/spec'
411
+ require 'my_sinatra_app'
412
+
413
+ describe 'My app' do
414
+
415
+ it "should show a default page" do
416
+ get_it '/'
417
+ should.be.ok
418
+ body.should.equal 'My Default Page!'
419
+ end
420
+
421
+ ...
422
+
423
+ end
424
+
425
+ === RSpec
426
+
427
+ require 'rubygems'
428
+ require 'spec'
429
+ require 'sinatra'
430
+ require 'sinatra/test/rspec'
431
+ require 'my_sinatra_app'
432
+
433
+ describe 'My app' do
434
+ it 'should show a default page' do
435
+ get_it '/'
436
+ @response.should be_ok
437
+ @response.body.should == 'My Default Page!'
438
+ end
439
+
440
+ ...
441
+
442
+ end
443
+
444
+ See Sinatra::Test::Methods for more information on +get_it+, +post_it+,
445
+ +put_it+, and friends.
446
+
447
+ == Command line
448
+
449
+ Sinatra applications can be run directly:
450
+
451
+ ruby myapp.rb [-h] [-x] [-p PORT] [-e ENVIRONMENT]
452
+
453
+ Options are:
454
+
455
+ -h # help
456
+ -p # set the port (default is 4567)
457
+ -e # set the environment (default is development)
458
+ -x # turn on the mutex lock (default is off)
459
+
460
+ == Contributing
461
+
462
+ === Tools
463
+
464
+ Besides Ruby itself, you only need a text editor, preferably one that supports
465
+ Ruby syntax hilighting. VIM and Emacs are a fine choice on any platform, but
466
+ feel free to use whatever you're familiar with.
467
+
468
+ Sinatra uses the Git source code management system. If you're unfamiliar with
469
+ Git, you can find more information and tutorials on http://git.or.cz/ as well
470
+ as http://git-scm.com/. Scott Chacon created a great series of introductory
471
+ screencasts about Git, which you can find here: http://www.gitcasts.com/
472
+
473
+ === First Time: Cloning The Sinatra Repo
474
+
475
+ cd where/you/keep/your/projects
476
+ git clone git://github.com/bmizerany/sinatra.git
477
+ cd sinatra
478
+ cd path/to/your_project
479
+ ln -s ../sinatra/
480
+
481
+ === Updating Your Existing Sinatra Clone
482
+
483
+ cd where/you/keep/sinatra
484
+ git pull
485
+
486
+ === Using Edge Sinatra in Your App
487
+
488
+ at the top of your sinatra_app.rb file:
489
+
490
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib'
491
+ require 'sinatra'
492
+
493
+ get '/about' do
494
+ "I'm running on Version " + Sinatra::VERSION
495
+ end
496
+
497
+ === Contributing a Patch
498
+
499
+ There are several ways to do this. Probably the easiest (and preferred) way is
500
+ to fork Sinatra on GitHub (http://github.com/bmizerany/sinatra), push your
501
+ changes to your Sinatra repo, and then send Blake Mizerany (bmizerany on
502
+ GitHub) a pull request.
503
+
504
+ You can also create a patch file and attach it to a feature request or bug fix
505
+ on the issue tracker (see below) or send it to the mailing list (see Community
506
+ section).
507
+
508
+ === Issue Tracking and Feature Requests
509
+
510
+ http://sinatra.lighthouseapp.com/
511
+
512
+ == Community
513
+
514
+ === Mailing List
515
+
516
+ http://groups.google.com/group/sinatrarb
517
+
518
+ If you have a problem or question, please make sure to include all the
519
+ relevant information in your mail, like the Sinatra version you're using, what
520
+ version of Ruby you have, and so on.
521
+
522
+ === IRC Channel
523
+
524
+ You can find us on the Freenode network in the channel #sinatra
525
+ (irc://chat.freenode.net/#sinatra)
526
+
527
+ There's usually someone online at any given time, but we cannot pay attention
528
+ to the channel all the time, so please stick around for a while after asking a
529
+ question.
data/Rakefile ADDED
@@ -0,0 +1,180 @@
1
+ require 'rubygems'
2
+ require 'rake/clean'
3
+ require 'fileutils'
4
+
5
+ task :default => :test
6
+
7
+ # SPECS ===============================================================
8
+
9
+ desc 'Run specs with story style output'
10
+ task :spec do
11
+ pattern = ENV['TEST'] || '.*'
12
+ sh "specrb --testcase '#{pattern}' --specdox -Ilib:test test/*_test.rb"
13
+ end
14
+
15
+ desc 'Run specs with unit test style output'
16
+ task :test do |t|
17
+ sh "specrb -Ilib:test test/*_test.rb"
18
+ end
19
+
20
+ desc 'Run compatibility specs'
21
+ task :compat do |t|
22
+ pattern = ENV['TEST'] || '.*'
23
+ sh "specrb --testcase '#{pattern}' -Ilib:test compat/*_test.rb"
24
+ end
25
+
26
+ # PACKAGING ============================================================
27
+
28
+ # Load the gemspec using the same limitations as github
29
+ def spec
30
+ @spec ||=
31
+ begin
32
+ require 'rubygems/specification'
33
+ data = File.read('sinatra.gemspec')
34
+ spec = nil
35
+ Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
36
+ spec
37
+ end
38
+ end
39
+
40
+ def package(ext='')
41
+ "dist/sinatra-#{spec.version}" + ext
42
+ end
43
+
44
+ desc 'Build packages'
45
+ task :package => %w[.gem .tar.gz].map {|e| package(e)}
46
+
47
+ desc 'Build and install as local gem'
48
+ task :install => package('.gem') do
49
+ sh "gem install #{package('.gem')}"
50
+ end
51
+
52
+ directory 'dist/'
53
+
54
+ file package('.gem') => %w[dist/ sinatra.gemspec] + spec.files do |f|
55
+ sh "gem build sinatra.gemspec"
56
+ mv File.basename(f.name), f.name
57
+ end
58
+
59
+ file package('.tar.gz') => %w[dist/] + spec.files do |f|
60
+ sh "git archive --format=tar HEAD | gzip > #{f.name}"
61
+ end
62
+
63
+ # Rubyforge Release / Publish Tasks ==================================
64
+
65
+ desc 'Publish website to rubyforge'
66
+ task 'publish:doc' => 'doc/api/index.html' do
67
+ sh 'scp -rp doc/* rubyforge.org:/var/www/gforge-projects/sinatra/'
68
+ end
69
+
70
+ task 'publish:gem' => [package('.gem'), package('.tar.gz')] do |t|
71
+ sh <<-end
72
+ rubyforge add_release sinatra sinatra #{spec.version} #{package('.gem')} &&
73
+ rubyforge add_file sinatra sinatra #{spec.version} #{package('.tar.gz')}
74
+ end
75
+ end
76
+
77
+ # Website ============================================================
78
+ # Building docs requires HAML and the hanna gem:
79
+ # gem install mislav-hanna --source=http://gems.github.com
80
+
81
+ task 'doc' => ['doc:api','doc:site']
82
+
83
+ desc 'Generate Hanna RDoc under doc/api'
84
+ task 'doc:api' => ['doc/api/index.html']
85
+
86
+ file 'doc/api/index.html' => FileList['lib/**/*.rb','README.rdoc'] do |f|
87
+ rb_files = f.prerequisites
88
+ sh((<<-end).gsub(/\s+/, ' '))
89
+ hanna --charset utf8 \
90
+ --fmt html \
91
+ --inline-source \
92
+ --line-numbers \
93
+ --main README.rdoc \
94
+ --op doc/api \
95
+ --title 'Sinatra API Documentation' \
96
+ #{rb_files.join(' ')}
97
+ end
98
+ end
99
+ CLEAN.include 'doc/api'
100
+
101
+ def rdoc_to_html(file_name)
102
+ require 'rdoc/markup/to_html'
103
+ rdoc = RDoc::Markup::ToHtml.new
104
+ rdoc.convert(File.read(file_name))
105
+ end
106
+
107
+ def haml(locals={})
108
+ require 'haml'
109
+ template = File.read('doc/template.haml')
110
+ haml = Haml::Engine.new(template, :format => :html4, :attr_wrapper => '"')
111
+ haml.render(Object.new, locals)
112
+ end
113
+
114
+ desc 'Build website HTML and stuff'
115
+ task 'doc:site' => ['doc/index.html', 'doc/book.html']
116
+
117
+ file 'doc/index.html' => %w[README.rdoc doc/template.haml] do |file|
118
+ File.open(file.name, 'w') do |file|
119
+ file << haml(:title => 'Sinatra', :content => rdoc_to_html('README.rdoc'))
120
+ end
121
+ end
122
+ CLEAN.include 'doc/index.html'
123
+
124
+ file 'doc/book.html' => ['book/output/sinatra-book.html'] do |file|
125
+ File.open(file.name, 'w') do |file|
126
+ book_content = File.read('book/output/sinatra-book.html')
127
+ file << haml(:title => 'Sinatra Book', :content => book_content)
128
+ end
129
+ end
130
+ CLEAN.include 'doc/book.html'
131
+
132
+ file 'book/output/sinatra-book.html' => FileList['book/**'] do |f|
133
+ unless File.directory?('book')
134
+ sh 'git clone git://github.com/cschneid/sinatra-book.git book'
135
+ end
136
+ sh((<<-SH).strip.gsub(/\s+/, ' '))
137
+ cd book &&
138
+ git fetch origin &&
139
+ git rebase origin/master &&
140
+ thor book:build
141
+ SH
142
+ end
143
+ CLEAN.include 'book/output/sinatra-book.html'
144
+
145
+ desc 'Build the Sinatra book'
146
+ task 'doc:book' => ['book/output/sinatra-book.html']
147
+
148
+ # Gemspec Helpers ====================================================
149
+
150
+ def source_version
151
+ line = File.read('lib/sinatra/base.rb')[/^\s*VERSION = .*/]
152
+ line.match(/.*VERSION = '(.*)'/)[1]
153
+ end
154
+
155
+ project_files =
156
+ FileList[
157
+ '{lib,test,compat,images}/**',
158
+ 'Rakefile', 'CHANGES', 'README.rdoc'
159
+ ]
160
+ file 'sinatra.gemspec' => project_files do |f|
161
+ # read spec file and split out manifest section
162
+ spec = File.read(f.name)
163
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
164
+ # replace version and date
165
+ head.sub!(/\.version = '.*'/, ".version = '#{source_version}'")
166
+ head.sub!(/\.date = '.*'/, ".date = '#{Date.today.to_s}'")
167
+ # determine file list from git ls-files
168
+ files = `git ls-files`.
169
+ split("\n").
170
+ sort.
171
+ reject{ |file| file =~ /^\./ }.
172
+ reject { |file| file =~ /^doc/ }.
173
+ map{ |file| " #{file}" }.
174
+ join("\n")
175
+ # piece file back together and write...
176
+ manifest = " s.files = %w[\n#{files}\n ]\n"
177
+ spec = [head,manifest,tail].join(" # = MANIFEST =\n")
178
+ File.open(f.name, 'w') { |io| io.write(spec) }
179
+ puts "updated #{f.name}"
180
+ end