nano-sharp-gem 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/cuba-4.0.3/CHANGELOG +71 -0
  3. data/cuba-4.0.3/CONTRIBUTING +19 -0
  4. data/cuba-4.0.3/LICENSE +23 -0
  5. data/cuba-4.0.3/README.md +781 -0
  6. data/cuba-4.0.3/cuba.gemspec +18 -0
  7. data/cuba-4.0.3/examples/config.ru +18 -0
  8. data/cuba-4.0.3/examples/measure.rb +17 -0
  9. data/cuba-4.0.3/examples/rack-response.ru +21 -0
  10. data/cuba-4.0.3/examples/views/home.mote +7 -0
  11. data/cuba-4.0.3/examples/views/layout.mote +11 -0
  12. data/cuba-4.0.3/lib/cuba/capybara.rb +13 -0
  13. data/cuba-4.0.3/lib/cuba/render.rb +63 -0
  14. data/cuba-4.0.3/lib/cuba/safe/csrf.rb +47 -0
  15. data/cuba-4.0.3/lib/cuba/safe/secure_headers.rb +43 -0
  16. data/cuba-4.0.3/lib/cuba/safe.rb +23 -0
  17. data/cuba-4.0.3/lib/cuba/test.rb +11 -0
  18. data/cuba-4.0.3/lib/cuba.rb +436 -0
  19. data/cuba-4.0.3/makefile +4 -0
  20. data/cuba-4.0.3/test/accept.rb +77 -0
  21. data/cuba-4.0.3/test/captures.rb +162 -0
  22. data/cuba-4.0.3/test/composition.rb +103 -0
  23. data/cuba-4.0.3/test/cookie.rb +34 -0
  24. data/cuba-4.0.3/test/csrf.rb +140 -0
  25. data/cuba-4.0.3/test/extension.rb +21 -0
  26. data/cuba-4.0.3/test/helper.rb +11 -0
  27. data/cuba-4.0.3/test/host.rb +29 -0
  28. data/cuba-4.0.3/test/integration.rb +114 -0
  29. data/cuba-4.0.3/test/match.rb +86 -0
  30. data/cuba-4.0.3/test/middleware.rb +47 -0
  31. data/cuba-4.0.3/test/number.rb +36 -0
  32. data/cuba-4.0.3/test/on.rb +157 -0
  33. data/cuba-4.0.3/test/param.rb +66 -0
  34. data/cuba-4.0.3/test/path.rb +86 -0
  35. data/cuba-4.0.3/test/plugin.rb +68 -0
  36. data/cuba-4.0.3/test/rack.rb +22 -0
  37. data/cuba-4.0.3/test/redirect.rb +21 -0
  38. data/cuba-4.0.3/test/render.rb +128 -0
  39. data/cuba-4.0.3/test/root.rb +83 -0
  40. data/cuba-4.0.3/test/run.rb +23 -0
  41. data/cuba-4.0.3/test/safe.rb +74 -0
  42. data/cuba-4.0.3/test/segment.rb +45 -0
  43. data/cuba-4.0.3/test/session.rb +21 -0
  44. data/cuba-4.0.3/test/settings.rb +52 -0
  45. data/cuba-4.0.3/test/views/about.erb +1 -0
  46. data/cuba-4.0.3/test/views/about.str +1 -0
  47. data/cuba-4.0.3/test/views/content-yield.erb +1 -0
  48. data/cuba-4.0.3/test/views/custom/abs_path.mote +1 -0
  49. data/cuba-4.0.3/test/views/frag.mote +1 -0
  50. data/cuba-4.0.3/test/views/home.erb +2 -0
  51. data/cuba-4.0.3/test/views/home.mote +1 -0
  52. data/cuba-4.0.3/test/views/home.str +2 -0
  53. data/cuba-4.0.3/test/views/layout-alternative.erb +2 -0
  54. data/cuba-4.0.3/test/views/layout-yield.erb +3 -0
  55. data/cuba-4.0.3/test/views/layout.erb +2 -0
  56. data/cuba-4.0.3/test/views/layout.mote +2 -0
  57. data/cuba-4.0.3/test/views/layout.str +2 -0
  58. data/cuba-4.0.3/test/views/test.erb +1 -0
  59. data/cuba-4.0.3/test/with.rb +42 -0
  60. data/nano-sharp-gem.gemspec +11 -0
  61. metadata +99 -0
@@ -0,0 +1,781 @@
1
+ Cuba
2
+ ====
3
+
4
+ _n_. a microframework for web development.
5
+
6
+ ![Cuba and Rum, by Jan Sochor](http://farm3.static.flickr.com/2619/4032103097_8324c6fecf.jpg)
7
+
8
+ Community
9
+ ---------
10
+
11
+ Meet us on IRC: [#cuba.rb][irc] on [freenode.net][freenode].
12
+
13
+ [irc]: irc://chat.freenode.net/#cuba.rb
14
+ [freenode]: http://freenode.net/
15
+
16
+ Description
17
+ -----------
18
+
19
+ Cuba is a microframework for web development originally inspired
20
+ by [Rum][rum], a tiny but powerful mapper for [Rack][rack]
21
+ applications.
22
+
23
+ It integrates many templates via [Tilt][tilt], and testing via
24
+ [Cutest][cutest] and [Capybara][capybara].
25
+
26
+ [rum]: http://github.com/chneukirchen/rum
27
+ [rack]: http://github.com/rack/rack
28
+ [tilt]: http://github.com/rtomayko/tilt
29
+ [cutest]: http://github.com/djanowski/cutest
30
+ [capybara]: http://github.com/jnicklas/capybara
31
+ [rack-test]: https://github.com/brynary/rack-test
32
+
33
+ Installation
34
+ ------------
35
+
36
+ ``` console
37
+ $ gem install cuba
38
+ ```
39
+
40
+ Usage
41
+ -----
42
+
43
+ Here's a simple application:
44
+
45
+ ``` ruby
46
+ # cat hello_world.rb
47
+ require "cuba"
48
+ require "cuba/safe"
49
+
50
+ Cuba.use Rack::Session::Cookie, :secret => "__a_very_long_string__"
51
+
52
+ Cuba.plugin Cuba::Safe
53
+
54
+ Cuba.define do
55
+ on get do
56
+ on "hello" do
57
+ res.write "Hello world!"
58
+ end
59
+
60
+ on root do
61
+ res.redirect "/hello"
62
+ end
63
+ end
64
+ end
65
+ ```
66
+
67
+ And the test file:
68
+
69
+ ``` ruby
70
+ # cat hello_world_test.rb
71
+ require "cuba/test"
72
+ require "./hello_world"
73
+
74
+ scope do
75
+ test "Homepage" do
76
+ get "/"
77
+
78
+ follow_redirect!
79
+
80
+ assert_equal "Hello world!", last_response.body
81
+ end
82
+ end
83
+ ```
84
+
85
+ To run it, you can create a `config.ru` file:
86
+
87
+ ``` ruby
88
+ # cat config.ru
89
+ require "./hello_world"
90
+
91
+ run Cuba
92
+ ```
93
+
94
+ You can now run `rackup` and enjoy what you have just created.
95
+
96
+ Matchers
97
+ --------
98
+
99
+ Here's an example showcasing how different matchers work:
100
+
101
+ ``` ruby
102
+ require "cuba"
103
+ require "cuba/safe"
104
+
105
+ Cuba.use Rack::Session::Cookie, :secret => "__a_very_long_string__"
106
+
107
+ Cuba.plugin Cuba::Safe
108
+
109
+ Cuba.define do
110
+
111
+ # only GET requests
112
+ on get do
113
+
114
+ # /
115
+ on root do
116
+ res.write "Home"
117
+ end
118
+
119
+ # /about
120
+ on "about" do
121
+ res.write "About"
122
+ end
123
+
124
+ # /styles/basic.css
125
+ on "styles", extension("css") do |file|
126
+ res.write "Filename: #{file}" #=> "Filename: basic"
127
+ end
128
+
129
+ # /post/2011/02/16/hello
130
+ on "post/:y/:m/:d/:slug" do |y, m, d, slug|
131
+ res.write "#{y}-#{m}-#{d} #{slug}" #=> "2011-02-16 hello"
132
+ end
133
+
134
+ # /username/foobar
135
+ on "username/:username" do |username|
136
+ user = User.find_by_username(username) # username == "foobar"
137
+
138
+ # /username/foobar/posts
139
+ on "posts" do
140
+
141
+ # You can access `user` here, because the `on` blocks
142
+ # are closures.
143
+ res.write "Total Posts: #{user.posts.size}" #=> "Total Posts: 6"
144
+ end
145
+
146
+ # /username/foobar/following
147
+ on "following" do
148
+ res.write user.following.size #=> "1301"
149
+ end
150
+ end
151
+
152
+ # /search?q=barbaz
153
+ on "search", param("q") do |query|
154
+ res.write "Searched for #{query}" #=> "Searched for barbaz"
155
+ end
156
+ end
157
+
158
+ # only POST requests
159
+ on post do
160
+ on "login" do
161
+
162
+ # POST /login, user: foo, pass: baz
163
+ on param("user"), param("pass") do |user, pass|
164
+ res.write "#{user}:#{pass}" #=> "foo:baz"
165
+ end
166
+
167
+ # If the params `user` and `pass` are not provided, this
168
+ # block will get executed.
169
+ on true do
170
+ res.write "You need to provide user and pass!"
171
+ end
172
+ end
173
+ end
174
+ end
175
+ ```
176
+
177
+ Note that once an `on` block matches, processing halts at the conclusion of that block.
178
+
179
+ Status codes
180
+ ------------
181
+
182
+ If you don't assign a status code and you don't write to the `res`
183
+ object, the status will be set as `404`. The method `not_found` is
184
+ in charge of setting the proper status code, and you can redefine
185
+ it if you want to render a template or configure custom headers.
186
+
187
+ For example:
188
+
189
+ ``` ruby
190
+ Cuba.define do
191
+ on get do
192
+ on "hello" do
193
+ res.write "hello world"
194
+ end
195
+ end
196
+ end
197
+
198
+ # Requests:
199
+ #
200
+ # GET / # 404
201
+ # GET /hello # 200
202
+ # GET /hello/world # 200
203
+ ```
204
+
205
+ As you can see, as soon as something was written to the response,
206
+ the status code was changed to 200.
207
+
208
+ If you want to match just "hello", but not "hello/world", you can do
209
+ as follows:
210
+
211
+ ``` ruby
212
+ Cuba.define do
213
+ on get do
214
+ on "hello" do
215
+ on root do
216
+ res.write "hello world"
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ # Requests:
223
+ #
224
+ # GET / # 404
225
+ # GET /hello # 200
226
+ # GET /hello/world # 404
227
+ ```
228
+
229
+ You can also use a regular expression to match the end of line:
230
+
231
+ ``` ruby
232
+ Cuba.define do
233
+ on get do
234
+ on /hello\/?\z/ do
235
+ res.write "hello world"
236
+ end
237
+ end
238
+ end
239
+
240
+ # Requests:
241
+ #
242
+ # GET / # 404
243
+ # GET /hello # 200
244
+ # GET /hello/world # 404
245
+ ```
246
+
247
+ This last example is not a common usage pattern. It's here only to
248
+ illustrate how Cuba can be adapted for different use cases.
249
+
250
+ If you need this behavior, you can create a helper:
251
+
252
+ ``` ruby
253
+ module TerminalMatcher
254
+ def terminal(path)
255
+ /#{path}\/?\z/
256
+ end
257
+ end
258
+
259
+ Cuba.plugin TerminalMatcher
260
+
261
+ Cuba.define do
262
+ on get do
263
+ on terminal("hello") do
264
+ res.write "hello world"
265
+ end
266
+ end
267
+ end
268
+ ```
269
+
270
+ Security
271
+ --------
272
+
273
+ The most important security consideration is to use `https` for all
274
+ requests. If that's not the case, any attempt to secure the application
275
+ could be in vain. The rest of this section assumes `https` is
276
+ enforced.
277
+
278
+ When building a web application, you need to include a security
279
+ layer. Cuba ships with the `Cuba::Safe` plugin, which applies several
280
+ security related headers to prevent attacks like clickjacking and
281
+ cross-site scripting, among others. It is not included by default
282
+ because there are legitimate uses for plain Cuba (for instance,
283
+ when designing an API).
284
+
285
+ Here's how to include it:
286
+
287
+ ```ruby
288
+ require "cuba/safe"
289
+
290
+ Cuba.plugin Cuba::Safe
291
+ ```
292
+
293
+ You should also always set a session secret to some undisclosed
294
+ value. Keep in mind that the content in the session cookie is
295
+ *not* encrypted.
296
+
297
+ ``` ruby
298
+ Cuba.use(Rack::Session::Cookie, :secret => "__a_very_long_string__")
299
+ ```
300
+
301
+ In the end, your application should look like this:
302
+
303
+ ```ruby
304
+ require "cuba"
305
+ require "cuba/safe"
306
+
307
+ Cuba.use Rack::Session::Cookie, :secret => "__a_very_long_string__"
308
+
309
+ Cuba.plugin Cuba::Safe
310
+
311
+ Cuba.define do
312
+ on csrf.unsafe? do
313
+ csrf.reset!
314
+
315
+ res.status = 403
316
+ res.write("Not authorized")
317
+
318
+ halt(res.finish)
319
+ end
320
+
321
+ # Now your app is protected against a wide range of attacks.
322
+ ...
323
+ end
324
+ ```
325
+
326
+ The `Cuba::Safe` plugin is composed of two modules:
327
+
328
+ * `Cuba::Safe::SecureHeaders`
329
+ * `Cuba::Safe::CSRF`
330
+
331
+ You can include them individually, but while the modularity is good
332
+ for development, it's very common to use them in tandem. As that's
333
+ the normal use case, including `Cuba::Safe` is the preferred way.
334
+
335
+ Cross-Site Request Forgery
336
+ --------------------------
337
+
338
+ The `Cuba::Safe::CSRF` plugin provides a `csrf` object with the
339
+ following methods:
340
+
341
+ * `token`: the current security token.
342
+ * `reset!`: forces the token to be recreated.
343
+ * `safe?`: returns `true` if the request is safe.
344
+ * `unsafe?`: returns `true` if the request is unsafe.
345
+ * `form_tag`: returns a string with the `csrf_token` hidden input tag.
346
+ * `meta_tag`: returns a string with the `csrf_token` meta tag.
347
+
348
+ Here's an example of how to use it:
349
+
350
+ ```ruby
351
+ require "cuba"
352
+ require "cuba/safe"
353
+
354
+ Cuba.use Rack::Session::Cookie, :secret => "__a_very_long_string__"
355
+
356
+ Cuba.plugin Cuba::Safe
357
+
358
+ Cuba.define do
359
+ on csrf.unsafe? do
360
+ csrf.reset!
361
+
362
+ res.status = 403
363
+ res.write("Forbidden")
364
+
365
+ halt(res.finish)
366
+ end
367
+
368
+ # Here comes the rest of your application
369
+ # ...
370
+ end
371
+ ```
372
+
373
+ You have to include `csrf.form_tag` in your forms and `csrf.meta_tag`
374
+ among your meta tags. Here's an example that assumes you are using
375
+ `Cuba::Mote` from `cuba-contrib`:
376
+
377
+ ```html
378
+ <!DOCTYPE html>
379
+ <html>
380
+ <head>
381
+ {{ app.csrf.meta_tag }}
382
+ ...
383
+ </head>
384
+ ...
385
+ <body>
386
+ <form action="/foo" method="POST">
387
+ {{ app.csrf.form_tag }}
388
+ ...
389
+ </form>
390
+ ...
391
+ </body>
392
+ </html>
393
+ ```
394
+
395
+ HTTP Verbs
396
+ ----------
397
+
398
+ There are matchers defined for the following HTTP Verbs: `get`,
399
+ `post`, `put`, `patch`, `delete`, `head`, `options`, `link`, `unlink`
400
+ and `trace`. As you have the whole request available via the `req`
401
+ object, you can also query it with helper methods like `req.options?`
402
+ or `req.head?`, or you can even go to a lower level and inspect the
403
+ environment via the `env` object, and check for example if
404
+ `env["REQUEST_METHOD"]` equals the verb `PATCH`.
405
+
406
+ What follows is an example of different ways of saying the same thing:
407
+
408
+ ``` ruby
409
+ on env["REQUEST_METHOD"] == "GET", "api" do ... end
410
+
411
+ on req.get?, "api" do ... end
412
+
413
+ on get, "api" do ... end
414
+ ```
415
+
416
+ Actually, `get` is syntax sugar for `req.get?`, which in turn is syntax sugar
417
+ for `env["REQUEST_METHOD"] == "GET"`.
418
+
419
+ Headers
420
+ -------
421
+
422
+ You can set the headers by assigning values to the hash `req.headers`.
423
+ If you want to inspect the incoming headers, you have to read from
424
+ the `env` hash. For example, if you want to know the referrer you
425
+ can check `env["HTTP_REFERER"]`.
426
+
427
+ Request and Response
428
+ --------------------
429
+
430
+ You may have noticed we use `req` and `res` a lot. Those variables are
431
+ instances of [Rack::Request][request] and `Cuba::Response` respectively, and
432
+ `Cuba::Response` is just an optimized version of
433
+ [Rack::Response][response].
434
+
435
+ [request]: http://www.rubydoc.info/github/rack/rack/Rack/Request
436
+ [response]: http://www.rubydoc.info/github/rack/rack/Rack/Response
437
+
438
+ Those objects are helpers for accessing the request and for building
439
+ the response. Most of the time, you will just use `res.write`.
440
+
441
+ If you want to use custom `Request` or `Response` objects, you can
442
+ set the new values as follows:
443
+
444
+ ``` ruby
445
+ Cuba.settings[:req] = MyRequest
446
+ Cuba.settings[:res] = MyResponse
447
+ ```
448
+
449
+ Make sure to provide classes compatible with those from Rack.
450
+
451
+ Captures
452
+ --------
453
+
454
+ You may have noticed that some matchers yield a value to the block. The rules
455
+ for determining if a matcher will yield a value are simple:
456
+
457
+ 1. Regex captures: `"posts/(\\d+)-(.*)"` will yield two values, corresponding to each capture.
458
+ 2. Placeholders: `"users/:id"` will yield the value in the position of :id.
459
+ 3. Symbols: `:foobar` will yield if a segment is available.
460
+ 4. File extensions: `extension("css")` will yield the basename of the matched file.
461
+ 5. Parameters: `param("user")` will yield the value of the parameter user, if present.
462
+
463
+ The first case is important because it shows the underlying effect of regex
464
+ captures.
465
+
466
+ In the second case, the substring `:id` gets replaced by `([^\\/]+)` and the
467
+ string becomes `"users/([^\\/]+)"` before performing the match, thus it reverts
468
+ to the first form we saw.
469
+
470
+ In the third case, the symbol--no matter what it says--gets replaced
471
+ by `"([^\\/]+)"`, and again we are in presence of case 1.
472
+
473
+ The fourth case, again, reverts to the basic matcher: it generates the string
474
+ `"([^\\/]+?)\.#{ext}\\z"` before performing the match.
475
+
476
+ The fifth case is different: it checks if the the parameter supplied is present
477
+ in the request (via POST or QUERY_STRING) and it pushes the value as a capture.
478
+
479
+ Composition
480
+ -----------
481
+
482
+ You can mount a Cuba app, along with middlewares, inside another Cuba app:
483
+
484
+ ``` ruby
485
+ class API < Cuba; end
486
+
487
+ API.use SomeMiddleware
488
+
489
+ API.define do
490
+ on param("url") do |url|
491
+ ...
492
+ end
493
+ end
494
+
495
+ Cuba.define do
496
+ on "api" do
497
+ run API
498
+ end
499
+ end
500
+ ```
501
+
502
+ If you need to pass information to one sub-app, you can use the
503
+ `with` method and access it with `vars`:
504
+
505
+ ```ruby
506
+ class Platforms < Cuba
507
+ define do
508
+ platform = vars[:platform]
509
+
510
+ on default do
511
+ res.write(platform) # => "heroku" or "salesforce"
512
+ end
513
+ end
514
+ end
515
+
516
+ Cuba.define do
517
+ on "(heroku|salesforce)" do |platform|
518
+ with(platform: platform) do
519
+ run(Platforms)
520
+ end
521
+ end
522
+ end
523
+ ```
524
+
525
+ ## Embedding routes from other modules
526
+
527
+ While the `run` command allows you to handle over the control to a
528
+ sub app, sometimes you may want to just embed routes defined in
529
+ another module. There's no built-in method to do it, but if you are
530
+ willing to experiment you can try the following.
531
+
532
+ Let's say you have defined routes in modules `A` and `B`, and you
533
+ want to mount those routes in your application.
534
+
535
+ First, you will have to extend Cuba with this code:
536
+
537
+ ```ruby
538
+ class Cuba
539
+ def mount(app)
540
+ result = app.call(req.env)
541
+ halt result if result[0] != 404
542
+ end
543
+ end
544
+ ```
545
+
546
+ It doesn't matter where you define it as long as Cuba has already
547
+ been required. For instance, you could extract that to a plugin and
548
+ it would work just fine.
549
+
550
+ Then, in your application, you can use it like this:
551
+
552
+ ```ruby
553
+ Cuba.define do
554
+ on default do
555
+ mount A
556
+ mount B
557
+ end
558
+ end
559
+ ```
560
+
561
+ It should halt the request only if the resulting status from calling
562
+ the mounted app is not 404. If you run into some unexpected behavior,
563
+ let me know by creating an issue and we'll look at how to workaround
564
+ any difficulties.
565
+
566
+ Testing
567
+ -------
568
+
569
+ Given that Cuba is essentially Rack, it is very easy to test with
570
+ `Rack::Test`, `Webrat` or `Capybara`. Cuba's own tests are written
571
+ with a combination of [Cutest][cutest] and [Rack::Test][rack-test],
572
+ and if you want to use the same for your tests it is as easy as
573
+ requiring `cuba/test`:
574
+
575
+ ``` ruby
576
+ require "cuba/test"
577
+ require "your/app"
578
+
579
+ scope do
580
+ test "Homepage" do
581
+ get "/"
582
+
583
+ assert_equal "Hello world!", last_response.body
584
+ end
585
+ end
586
+ ```
587
+
588
+ If you prefer to use [Capybara][capybara], instead of requiring
589
+ `cuba/test` you can require `cuba/capybara`:
590
+
591
+ ``` ruby
592
+ require "cuba/capybara"
593
+ require "your/app"
594
+
595
+ scope do
596
+ test "Homepage" do
597
+ visit "/"
598
+
599
+ assert has_content?("Hello world!")
600
+ end
601
+ end
602
+ ```
603
+
604
+ To read more about testing, check the documentation for
605
+ [Cutest][cutest], [Rack::Test][rack-test] and [Capybara][capybara].
606
+
607
+ Settings
608
+ --------
609
+
610
+ Each Cuba app can store settings in the `Cuba.settings` hash. The settings are
611
+ inherited if you happen to subclass `Cuba`
612
+
613
+ ``` ruby
614
+ Cuba.settings[:layout] = "guest"
615
+
616
+ class Users < Cuba; end
617
+ class Admin < Cuba; end
618
+
619
+ Admin.settings[:layout] = "admin"
620
+
621
+ assert_equal "guest", Users.settings[:layout]
622
+ assert_equal "admin", Admin.settings[:layout]
623
+ ```
624
+
625
+ Feel free to store whatever you find convenient.
626
+
627
+ Rendering
628
+ ---------
629
+
630
+ Cuba includes a plugin called `Cuba::Render` that provides a couple of helper
631
+ methods for rendering templates. This plugin uses [Tilt][tilt], which serves as
632
+ an interface to a bunch of different Ruby template engines (ERB, Haml, Sass,
633
+ CoffeeScript, etc.), so you can use the template engine of your choice.
634
+
635
+ To set up `Cuba::Render`, do:
636
+
637
+ ```ruby
638
+ require "cuba"
639
+ require "cuba/render"
640
+ require "erb"
641
+
642
+ Cuba.plugin Cuba::Render
643
+ ```
644
+
645
+ This example uses ERB, a template engine that comes with Ruby. If you want to
646
+ use another template engine, one [supported by Tilt][templates], you need to
647
+ install the required gem and change the `template_engine` setting as shown
648
+ below.
649
+
650
+ ```ruby
651
+ Cuba.settings[:render][:template_engine] = "haml"
652
+ ```
653
+
654
+ The plugin provides three helper methods for rendering templates: `partial`,
655
+ `view` and `render`.
656
+
657
+ ```ruby
658
+ Cuba.define do
659
+ on "about" do
660
+ # `partial` renders a template called `about.erb` without a layout.
661
+ res.write partial("about")
662
+ end
663
+
664
+ on "home" do
665
+ # Opposed to `partial`, `view` renders the same template
666
+ # within a layout called `layout.erb`.
667
+ res.write view("about")
668
+ end
669
+
670
+ on "contact" do
671
+ # `render` is a shortcut to `res.write view(...)`
672
+ render("contact")
673
+ end
674
+ end
675
+ ```
676
+
677
+ By default, `Cuba::Render` assumes that all templates are placed in a folder
678
+ named `views` and that they use the proper extension for the chosen template
679
+ engine. Also for the `view` and `render` methods, it assumes that the layout
680
+ template is called `layout`.
681
+
682
+ The defaults can be changed through the `Cuba.settings` method:
683
+
684
+ ```ruby
685
+ Cuba.settings[:render][:template_engine] = "haml"
686
+ Cuba.settings[:render][:views] = "./views/admin/"
687
+ Cuba.settings[:render][:layout] = "admin"
688
+ ```
689
+
690
+ NOTE: Cuba doesn't ship with Tilt. You need to install it (`gem install tilt`).
691
+
692
+ [templates]: https://github.com/rtomayko/tilt/blob/master/docs/TEMPLATES.md
693
+
694
+ Plugins
695
+ -------
696
+
697
+ Cuba provides a way to extend its functionality with plugins.
698
+
699
+ ### How to create plugins
700
+
701
+ Authoring your own plugins is pretty straightforward.
702
+
703
+ ``` ruby
704
+ module MyOwnHelper
705
+ def markdown(str)
706
+ BlueCloth.new(str).to_html
707
+ end
708
+ end
709
+
710
+ Cuba.plugin MyOwnHelper
711
+ ```
712
+
713
+ That's the simplest kind of plugin you'll write. In fact, that's exactly how
714
+ the `markdown` helper is written in `Cuba::TextHelpers`.
715
+
716
+ A more complicated plugin can make use of `Cuba.settings` to provide default
717
+ values. In the following example, note that if the module has a `setup` method, it will
718
+ be called as soon as it is included:
719
+
720
+ ``` ruby
721
+ module Render
722
+ def self.setup(app)
723
+ app.settings[:template_engine] = "erb"
724
+ end
725
+
726
+ def partial(template, locals = {})
727
+ render("#{template}.#{settings[:template_engine]}", locals)
728
+ end
729
+ end
730
+
731
+ Cuba.plugin Render
732
+ ```
733
+
734
+ This sample plugin actually resembles how `Cuba::Render` works.
735
+
736
+ Finally, if a module called `ClassMethods` is present, `Cuba` will be extended
737
+ with it.
738
+
739
+ ``` ruby
740
+ module GetSetter
741
+ module ClassMethods
742
+ def set(key, value)
743
+ settings[key] = value
744
+ end
745
+
746
+ def get(key)
747
+ settings[key]
748
+ end
749
+ end
750
+ end
751
+
752
+ Cuba.plugin GetSetter
753
+
754
+ Cuba.set(:foo, "bar")
755
+
756
+ assert_equal "bar", Cuba.get(:foo)
757
+ assert_equal "bar", Cuba.settings[:foo]
758
+ ```
759
+
760
+ Contributing
761
+ ------------
762
+
763
+ A good first step is to meet us on IRC and discuss ideas. If that's
764
+ not possible, you can create an issue explaining the proposed change
765
+ and a use case. We pay a lot of attention to use cases, because our
766
+ goal is to keep the code base simple. In many cases, the result of
767
+ a conversation will be the creation of another tool, instead of the
768
+ modification of Cuba itself.
769
+
770
+ If you want to test Cuba, you may want to use a gemset to isolate
771
+ the requirements. We recommend the use of tools like [dep][dep] and
772
+ [gs][gs], but you can use similar tools like [gst][gst] or [bs][bs].
773
+
774
+ The required gems for testing and development are listed in the
775
+ `.gems` file. If you are using [dep][dep], you can create a gemset
776
+ and run `dep install`.
777
+
778
+ [dep]: http://cyx.github.io/dep/
779
+ [gs]: http://soveran.github.io/gs/
780
+ [gst]: https://github.com/tonchis/gst
781
+ [bs]: https://github.com/educabilia/bs