roda 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +26 -0
  3. data/README.rdoc +83 -22
  4. data/Rakefile +1 -1
  5. data/doc/release_notes/2.1.0.txt +124 -0
  6. data/lib/roda/plugins/assets.rb +17 -9
  7. data/lib/roda/plugins/class_level_routing.rb +5 -2
  8. data/lib/roda/plugins/delegate.rb +6 -3
  9. data/lib/roda/plugins/indifferent_params.rb +7 -0
  10. data/lib/roda/plugins/mailer.rb +18 -1
  11. data/lib/roda/plugins/multi_route.rb +2 -1
  12. data/lib/roda/plugins/path.rb +75 -6
  13. data/lib/roda/plugins/render.rb +33 -14
  14. data/lib/roda/plugins/static.rb +35 -0
  15. data/lib/roda/plugins/view_options.rb +161 -0
  16. data/lib/roda/plugins/view_subdirs.rb +6 -63
  17. data/lib/roda/version.rb +1 -1
  18. data/spec/composition_spec.rb +12 -0
  19. data/spec/matchers_spec.rb +34 -0
  20. data/spec/plugin/assets_spec.rb +112 -17
  21. data/spec/plugin/delete_empty_headers_spec.rb +12 -0
  22. data/spec/plugin/mailer_spec.rb +46 -3
  23. data/spec/plugin/module_include_spec.rb +17 -0
  24. data/spec/plugin/multi_route_spec.rb +10 -0
  25. data/spec/plugin/named_templates_spec.rb +6 -0
  26. data/spec/plugin/not_found_spec.rb +1 -1
  27. data/spec/plugin/path_spec.rb +76 -0
  28. data/spec/plugin/render_each_spec.rb +6 -0
  29. data/spec/plugin/render_spec.rb +40 -1
  30. data/spec/plugin/sinatra_helpers_spec.rb +5 -0
  31. data/spec/plugin/static_spec.rb +30 -0
  32. data/spec/plugin/view_options_spec.rb +117 -0
  33. data/spec/spec_helper.rb +5 -1
  34. data/spec/views/multiple-layout.erb +1 -0
  35. data/spec/views/multiple.erb +1 -0
  36. metadata +10 -4
  37. data/spec/plugin/static_path_info_spec.rb +0 -56
  38. data/spec/plugin/view_subdirs_spec.rb +0 -44
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 035bdb54b566e77c48d1332fdd7ebf396a849193
4
- data.tar.gz: 682a8a39b4dc9e25d1395102890f8650f750e12d
3
+ metadata.gz: 3c4531548815188387cfc8a1cd6eb4803ca52228
4
+ data.tar.gz: f25f081bd7ed74c63d5b4da6ca87d8ad3a413c42
5
5
  SHA512:
6
- metadata.gz: c219eb5e94a58c04d90256d87cbe5a86c1dc8c972666cbf61d97a34c5f3b4bc8d6b7de56fdee7aa65ef01ba70d7f4052d479c80211f49201dc94705e17e69308
7
- data.tar.gz: ba8d204672492c04de406eb87d276c59e92af57047c4cc6bb479e7fc9efb1785fd314df682e496bc905327450571497f77581fef04a29346159de747172f07bc
6
+ metadata.gz: 4fd832236f0c0a59129f10303c56c82f3ec9e63d27f8c85cd5080e0ac1cd3477cd3321f309618a6c2d4690b43b79ab5d687c9243957156d61532e472118943a1
7
+ data.tar.gz: ca178d1d5b1945ad3f94a9d4273d9cbbfd8726f5f5f924d7eefd9949d0be732eb028e6f2fd1f7f2e7eee526ae04ef796bb2899ebbcbb690bac4a13e0021241af
data/CHANGELOG CHANGED
@@ -1,3 +1,29 @@
1
+ = 2.1.0 (2015-03-13)
2
+
3
+ * Have add_file in the mailer plugin support blocks, which are called after the file has been added (jeremyevans)
4
+
5
+ * Add append_view_subdir to view_options, for appending to an existing view subdirectory (jeremyevans)
6
+
7
+ * Rename view_subdirs plugin to view_options, add support for branch/route specific view/layout options/locals (jeremyevans)
8
+
9
+ * Merge :locals set in the render plugin options into :locals provided in call to view/render (jeremyevans)
10
+
11
+ * Add support for registering classes in the path plugin for use with Roda#path (jeremyevans)
12
+
13
+ * Use :add_script_name app option as default for path method :add_script_name option in path plugin (jeremyevans)
14
+
15
+ * Support :add_script_name app option in assets plugin, to prefix URLs with SCRIPT_NAME (jeremyevans)
16
+
17
+ * Make r.multi_route in multi_route plugin work without any named routes defined (jeremyevans)
18
+
19
+ * Add :static plugin, for more easily serving static files (jeremyevans)
20
+
21
+ * Recognize Roda :root option in render and assets plugins (jeremyevans)
22
+
23
+ * Make :layout=>false option in render plugin override previous layout template (jeremyevans)
24
+
25
+ * Make add_file in the mailer plugin add the files after the email body instead of before (jeremyevans)
26
+
1
27
  = 2.0.0 (2015-02-13)
2
28
 
3
29
  * Allow Roda app to be used as a regular rack app even when using the middleware plugin (jeremyevans)
data/README.rdoc CHANGED
@@ -14,16 +14,39 @@ Bugs :: http://github.com/jeremyevans/roda/issues
14
14
  Google Group :: http://groups.google.com/group/ruby-roda
15
15
  IRC :: irc://chat.freenode.net/#roda
16
16
 
17
- == Inspiration
17
+ == Goals
18
18
 
19
- Roda was inspired by {Sinatra}[http://www.sinatrarb.com] and {Cuba}[http://cuba.is],
20
- two other Ruby web frameworks.
21
- It started out as a fork of Cuba, from which it borrows the idea of using a routing tree
22
- (which Cuba in turn took from {Rum}[https://github.com/chneukirchen/rum]).
23
- From Sinatra, it takes the ideas that route blocks should return the request bodies
24
- and that routes should be canonical.
25
- It pilfers the idea for an extensible plugin system from the Ruby database library
26
- {Sequel}[http://sequel.jeremyevans.net].
19
+ * Simplicity
20
+ * Reliability
21
+ * Extensibility
22
+ * Performance
23
+
24
+ === Simplicity
25
+
26
+ Roda is designed to be simple, both internally and externally.
27
+ It uses a routing tree to enable you to write simpler and DRYer
28
+ code.
29
+
30
+ === Reliability
31
+
32
+ Roda is one of the first ruby web frameworks where immutability
33
+ is supported and encouraged. Roda apps are designed to be frozen
34
+ in production, which eliminates possible thread safety issues.
35
+ Additionally, Roda limits the instance variables, constants, and
36
+ methods that it uses, so that they do not conflict with the ones
37
+ you use for your application.
38
+
39
+ === Extensibility
40
+
41
+ Roda is built completely out of plugins, which makes it very
42
+ extensible. You can override any part of Roda and call super
43
+ to get the default behavior.
44
+
45
+ === Performance
46
+
47
+ Roda has low per-request overhead, and the use of a routing tree
48
+ and intelligent caching of internal datastructures makes it one
49
+ of the fastest ruby web frameworks.
27
50
 
28
51
  == Usage
29
52
 
@@ -43,22 +66,24 @@ Here's a simple application, showing how the routing tree works:
43
66
 
44
67
  # /hello branch
45
68
  r.on "hello" do
69
+ # Set variable for all routes in /hello branch
70
+ @greeting = 'Hello'
46
71
 
47
72
  # GET /hello/world request
48
73
  r.get "world" do
49
- "Hello world!"
74
+ "#{@greeting} world!"
50
75
  end
51
76
 
52
77
  # /hello request
53
78
  r.is do
54
79
  # GET /hello request
55
80
  r.get do
56
- "Hello!"
81
+ "#{@greeting}!"
57
82
  end
58
83
 
59
84
  # POST /hello request
60
85
  r.post do
61
- puts "Someone said hello!"
86
+ puts "Someone said #{@greeting}!"
62
87
  r.redirect
63
88
  end
64
89
  end
@@ -588,27 +613,36 @@ The settings are inherited if you happen to subclass +Roda+.
588
613
  Roda.opts[:layout] = "guest"
589
614
 
590
615
  class Users < Roda; end
591
- class Admin < Roda; end
592
-
593
- Admin.opts[:layout] = "admin"
616
+ class Admin < Roda
617
+ opts[:layout] = "admin"
618
+ end
594
619
 
595
620
  Users.opts[:layout] # => 'guest'
596
621
  Admin.opts[:layout] # => 'admin'
597
622
 
598
623
  Feel free to store whatever you find convenient.
599
624
  Note that when subclassing, Roda only does a shallow clone of the settings.
625
+
600
626
  If you store nested structures and plan to mutate them in subclasses,
601
627
  it is your responsibility to dup the nested structures inside +Roda.inherited+
602
- (making sure to call +super+).
603
-
604
- The plugins that ship with Roda all handle this.
605
- Also, note that this means that modifications to the parent class
606
- made after subclassing do _not_ affect the subclass.
628
+ (making sure to call +super+). This should be is done so that that modifications
629
+ to the parent class made after subclassing do _not_ affect the subclass, and
630
+ vice-versa.
607
631
 
608
632
  The plugins that ship with Roda freeze their settings and only allow modification
609
633
  to their settings by reloading the plugin, and external plugins are encouraged
610
634
  to follow this approach.
611
635
 
636
+ The following options are respected by multiple plugins:
637
+
638
+ :add_script_name :: Prepend the SCRIPT_NAME for the request to paths. This is
639
+ useful if you mount the app as a path under another app.
640
+ :root :: Set the root path for the app. This defaults to the current working
641
+ directory of the process.
642
+
643
+ There may be other options supported by individual plugins, if so it will be
644
+ mentioned in the documentation for the plugin.
645
+
612
646
  == Rendering
613
647
 
614
648
  Roda ships with a +render+ plugin that provides helpers for rendering templates.
@@ -652,10 +686,10 @@ You can override the default rendering options by passing a hash to the plugin:
652
686
  class App < Roda
653
687
  plugin :render,
654
688
  :escape => true, # Automatically escape output in erb templates
655
- :views => 'admin_views' # Default views directory
689
+ :views => 'admin_views', # Default views directory
656
690
  :layout_opts => {:template=>'admin_layout',
657
691
  :ext=>'html.erb'}, # Default layout template options
658
- :template_opts => {:default_encoding=>'UTF-8'}, # Default template options
692
+ :template_opts => {:default_encoding=>'UTF-8'} # Default template options
659
693
  end
660
694
 
661
695
  == Sessions
@@ -791,6 +825,33 @@ to avoid polluting the scope. Finally, do not add any constants inside
791
825
  the InstanceMethods module, add constants to the plugin module itself
792
826
  (+Markdown+ in the above example).
793
827
 
828
+ If you are planning on shipping your plugin in an external gem, it is recommended that you follow
829
+ {standard gem naming conventions for extensions}[http://guides.rubygems.org/name-your-gem/].
830
+ So if your plugin module is named +FooBar+, your gem name should be <tt>roda-foo_bar</tt>.
831
+
832
+ == No Introspection
833
+
834
+ Because a routing tree does not store the routes in a data structure, but
835
+ directly executes the routing tree block, you cannot introspect the routes
836
+ when using a routing tree.
837
+
838
+ If you would like to introspect your routes when using Roda, there is an
839
+ external plugin named {roda-route_list}[https://github.com/jeremyevans/roda-route_list],
840
+ which allows you to add appropriate comments to your routing files, and
841
+ has a parser that will parse those comments into routing metadata that
842
+ you can then introspect.
843
+
844
+ == Inspiration
845
+
846
+ Roda was inspired by {Sinatra}[http://www.sinatrarb.com] and {Cuba}[http://cuba.is],
847
+ two other Ruby web frameworks.
848
+ It started out as a fork of Cuba, from which it borrows the idea of using a routing tree
849
+ (which Cuba in turn took from {Rum}[https://github.com/chneukirchen/rum]).
850
+ From Sinatra, it takes the ideas that route blocks should return the request bodies
851
+ and that routes should be canonical.
852
+ It pilfers the idea for an extensible plugin system from the Ruby database library
853
+ {Sequel}[http://sequel.jeremyevans.net].
854
+
794
855
  == License
795
856
 
796
857
  MIT
data/Rakefile CHANGED
@@ -52,7 +52,7 @@ end
52
52
 
53
53
  desc "Make local version of website"
54
54
  task :website_base do
55
- sh %{#{FileUtils::RUBY} www/make_www.rb}
55
+ sh %{#{FileUtils::RUBY} -I lib www/make_www.rb}
56
56
  end
57
57
 
58
58
  desc "Make local version of website, with rdoc"
@@ -0,0 +1,124 @@
1
+ = New Plugins
2
+
3
+ * A view_options plugin has been added, for branch/route specific
4
+ setting of view and layout options and locals. This allows for
5
+ DRYer code when you want to change the view or layout settings
6
+ for an entire routing branch. Options and locals set at the
7
+ branch or route level have higher priority than those set at
8
+ the plugin level, but lower priority than those provided as
9
+ arguments to the render/view methods. Example:
10
+
11
+ class App < Roda
12
+ plugin :view_options
13
+
14
+ route do |r|
15
+ r.on 'albums' do
16
+ layout_options :template=>'layouts/3_columns'
17
+ layout_locals :heading=>'Albums'
18
+ view_options :ext=>'haml'
19
+ view_locals :name=>'Foo'
20
+ # ...
21
+ end
22
+ end
23
+ end
24
+
25
+ The view_options plugin is also a superset of the previous
26
+ view_subdirs plugin, and attempts to load view_subdirs will
27
+ now load view_options. In addition to set_view_subdir, the
28
+ view_options plugin now supports append_view_subdir, which
29
+ will append a subdirectory to an existing subdirectory, which
30
+ makes it simpler to deal with nested view file hierarchies.
31
+
32
+ * A static plugin has been added for easily serving static files
33
+ using Rack::Static. Example:
34
+
35
+ class App < Roda
36
+ plugin :static, ['/js', '/css']
37
+ # or:
38
+ plugin :static, ['/js', '/css'], :root=>'pub'
39
+ end
40
+
41
+ = Other New Features
42
+
43
+ * Roda now supports a :root option for the application that sets
44
+ the root directory. This is useful if the application's files
45
+ are not stored in the process's working directory, which is
46
+ common for processes containing of multiple Roda applications.
47
+
48
+ By setting the :root option, plugins that use the file system
49
+ will default to making relative paths relative to the :root
50
+ option instead of the process's working directory. The
51
+ assets, render, and static plugins currently support the :root
52
+ option. Example:
53
+
54
+ class App < Roda
55
+ opts[:root] = File.dirname(__FILE__)
56
+ end
57
+
58
+ * Roda now supports an :add_script_name option for the application,
59
+ which makes plugins automatically prepend the SCRIPT_NAME for the
60
+ request's environment to any paths created. This allows Roda
61
+ applications to work transparently whenever they are mounted
62
+ inside of another rack application.
63
+
64
+ The assets and path plugins currently recognize the
65
+ :add_script_name option. Example:
66
+
67
+ class App < Roda
68
+ opts[:add_script_name] = true
69
+ end
70
+
71
+ * The path plugin now adds a Roda#path method, which creates paths
72
+ based on the type of argument used. You can register classes
73
+ with the path plugin by providing Roda.path with a class, which
74
+ will cause Roda#path to recognize them and handle them accordingly.
75
+
76
+ Example:
77
+
78
+ class App < Roda
79
+ plugin :path
80
+ path(Track){|track| "/albums/#{track.album_id}/tracks/#{track.number}"}
81
+
82
+ route do
83
+ r.get 'tracks/:id' |track_id|
84
+ r.redirect(path(Track[track_id]))
85
+ end
86
+ end
87
+ end
88
+
89
+ = Other Improvements
90
+
91
+ * add_file in the mailer plugin now adds the files after the email
92
+ body instead of before. This fixes some issues where the email
93
+ body would end up empty, due to issues with the mail gem's API.
94
+
95
+ add_file now accepts a block, and the block is called after the
96
+ file has been attached. Among other things, this allows you to
97
+ change the content_type for an attached file:
98
+
99
+ add_file 'path/to/file' do
100
+ response.mail.attachments.last.content_type = 'text/foo'
101
+ end
102
+
103
+ * r.multi_route in the multi_route plugin now works if there are
104
+ no named routes defined.
105
+
106
+ * A render plugin :locals option is now respected, setting defaults
107
+ to use for locals in views. Additionally, a :locals option in
108
+ the :layout_opts option is now respected for setting locals in
109
+ layouts. If both the render plugin option is set and :locals is
110
+ passed to render/view, the two will be merged together.
111
+ Previously, providing a :locals option to render/view would cause
112
+ the plugin level option to be ignored.
113
+
114
+ = Backwards Compatibility
115
+
116
+ * Using the render plugin :layout=>nil option now removes any
117
+ layout template set previously using :layout. Previously, the
118
+ layout template would still be kept, but it would not be used
119
+ by default.
120
+
121
+ * Accessing attachments after adding a file using add_file in the
122
+ mailer plugin no longer works, as the adding is now delayed until
123
+ after the body is set. You should now pass a block to add_file
124
+ if you want to access the attachment after it has been added.
@@ -52,6 +52,10 @@ class Roda
52
52
  #
53
53
  # <%= assets(:css, :media => 'print') %>
54
54
  #
55
+ # The assets method will respect the application's :add_script_name option,
56
+ # if it set it will automatically prefix the path with the SCRIPT_NAME for
57
+ # the request.
58
+ #
55
59
  # == Asset Groups
56
60
  #
57
61
  # The asset plugin supports groups for the cases where you have different
@@ -209,20 +213,20 @@ class Roda
209
213
  # :js_headers :: A hash of additional headers for your rendered javascript files
210
214
  # :js_opts :: Template options to pass to the render plugin (via :template_opts) when rendering javascript assets
211
215
  # :js_route :: Route under :prefix for javascript assets (default: :js_dir)
212
- # :path :: Path to your asset source directory (default: 'assets')
216
+ # :path :: Path to your asset source directory (default: 'assets'). Relative
217
+ # paths will be considered relative to the application's :root option.
213
218
  # :prefix :: Prefix for assets path in your URL/routes (default: 'assets')
214
219
  # :precompiled :: Path to the compiled asset metadata file. If the file exists, will use compiled
215
220
  # mode using the metadata in the file. If the file does not exist, will use
216
221
  # non-compiled mode, but will write the metadata to the file if compile_assets is called.
217
- # :public :: Path to your public folder, in which compiled files are placed (default: 'public')
222
+ # :public :: Path to your public folder, in which compiled files are placed (default: 'public'). Relative
223
+ # paths will be considered relative to the application's :root option.
218
224
  module Assets
219
225
  DEFAULTS = {
220
226
  :compiled_name => 'app'.freeze,
221
227
  :js_dir => 'js'.freeze,
222
228
  :css_dir => 'css'.freeze,
223
- :path => 'assets'.freeze,
224
229
  :prefix => 'assets'.freeze,
225
- :public => 'public'.freeze,
226
230
  :concat_only => false,
227
231
  :compiled => false,
228
232
  :add_suffix => false,
@@ -269,6 +273,8 @@ class Roda
269
273
  app.opts[:assets][:orig_opts] = opts
270
274
  end
271
275
  opts = app.opts[:assets]
276
+ opts[:path] = File.expand_path(opts[:path]||"assets", app.opts[:root]).freeze
277
+ opts[:public] = File.expand_path(opts[:public]||"public", app.opts[:root]).freeze
272
278
 
273
279
  # Combine multiple values into a path, ignoring trailing slashes
274
280
  j = lambda do |*v|
@@ -465,23 +471,25 @@ class Roda
465
471
  end
466
472
 
467
473
  if type == :js
468
- tag_start = "<script type=\"text/javascript\" #{attrs} src=\"/"
474
+ tag_start = "<script type=\"text/javascript\" #{attrs} src=\""
469
475
  tag_end = JS_END
470
476
  else
471
- tag_start = "<link rel=\"stylesheet\" #{attrs} href=\"/"
477
+ tag_start = "<link rel=\"stylesheet\" #{attrs} href=\""
472
478
  tag_end = CSS_END
473
479
  end
474
480
 
481
+ url_prefix = request.script_name if self.class.opts[:add_script_name]
482
+
475
483
  # Create a tag for each individual file
476
484
  if compiled = o[:compiled]
477
485
  if dirs && !dirs.empty?
478
486
  key = dirs.join(DOT)
479
487
  ckey = "#{stype}.#{key}"
480
488
  if ukey = compiled[ckey]
481
- "#{tag_start}#{o[:"compiled_#{stype}_prefix"]}.#{key}.#{ukey}.#{stype}#{tag_end}"
489
+ "#{tag_start}#{url_prefix}/#{o[:"compiled_#{stype}_prefix"]}.#{key}.#{ukey}.#{stype}#{tag_end}"
482
490
  end
483
491
  elsif ukey = compiled[stype]
484
- "#{tag_start}#{o[:"compiled_#{stype}_prefix"]}.#{ukey}.#{stype}#{tag_end}"
492
+ "#{tag_start}#{url_prefix}/#{o[:"compiled_#{stype}_prefix"]}.#{ukey}.#{stype}#{tag_end}"
485
493
  end
486
494
  else
487
495
  asset_dir = o[type]
@@ -489,7 +497,7 @@ class Roda
489
497
  dirs.each{|f| asset_dir = asset_dir[f]}
490
498
  prefix = "#{dirs.join(SLASH)}/" if o[:group_subdirs]
491
499
  end
492
- Array(asset_dir).map{|f| "#{tag_start}#{o[:"#{stype}_prefix"]}#{prefix}#{f}#{o[:"#{stype}_suffix"]}#{tag_end}"}.join(NEWLINE)
500
+ Array(asset_dir).map{|f| "#{tag_start}#{url_prefix}/#{o[:"#{stype}_prefix"]}#{prefix}#{f}#{o[:"#{stype}_suffix"]}#{tag_end}"}.join(NEWLINE)
493
501
  end
494
502
  end
495
503
 
@@ -24,14 +24,17 @@ class Roda
24
24
  #
25
25
  # # /hello request
26
26
  # is "hello" do
27
+ # # Set variable for both GET and POST requests
28
+ # @greeting = 'Hello'
29
+ #
27
30
  # # GET /hello request
28
31
  # request.get do
29
- # "Hello!"
32
+ # "#{@greeting}!"
30
33
  # end
31
34
  #
32
35
  # # POST /hello request
33
36
  # request.post do
34
- # puts "Someone said hello!"
37
+ # puts "Someone said #{@greeting}!"
35
38
  # request.redirect
36
39
  # end
37
40
  # end