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
@@ -1,63 +1,6 @@
1
- class Roda
2
- module RodaPlugins
3
- # The view_subdirs plugin is designed for sites that have
4
- # outgrown a flat view directory and use subdirectories
5
- # for views. It allows you to set the view directory to
6
- # use, and template names that do not contain a slash will
7
- # automatically use that view subdirectory. Example:
8
- #
9
- # plugin :render, :layout=>'./layout'
10
- # plugin :view_subdirs
11
- #
12
- # route do |r|
13
- # r.on "users" do
14
- # set_view_subdir 'users'
15
- #
16
- # r.get :id do
17
- # view 'profile' # uses ./views/users/profile.erb
18
- # end
19
- #
20
- # r.get 'list' do
21
- # view 'lists/users' # uses ./views/lists/users.erb
22
- # end
23
- # end
24
- # end
25
- #
26
- # Note that when a view subdirectory is set, the layout will
27
- # also be looked up in the subdirectory unless it contains
28
- # a slash. So if you want to use a view subdirectory for
29
- # templates but have a shared layout, you should make sure your
30
- # layout contains a slash, similar to the example above.
31
- module ViewSubdirs
32
- # Load the render plugin before this plugin, since this plugin
33
- # works by overriding a method in the render plugin.
34
- def self.load_dependencies(app)
35
- app.plugin :render
36
- end
37
-
38
- module InstanceMethods
39
- # Set the view subdirectory to use. This can be set to nil
40
- # to not use a view subdirectory.
41
- def set_view_subdir(v)
42
- @_view_subdir = v
43
- end
44
-
45
- private
46
-
47
- # Override the template name to use the view subdirectory if the
48
- # there is a view subdirectory and the template name does not
49
- # contain a slash.
50
- def template_name(opts)
51
- name = super
52
- if (v = @_view_subdir) && name !~ /\//
53
- "#{v}/#{name}"
54
- else
55
- name
56
- end
57
- end
58
- end
59
- end
60
-
61
- register_plugin(:view_subdirs, ViewSubdirs)
62
- end
63
- end
1
+ # View options is a superset of the view_subdirs plugin,
2
+ # which no longer exists. For backwards compatibility,
3
+ # make attempts to load the view_subdirs plugin load the
4
+ # view_options plugin instead.
5
+ require 'roda/plugins/view_options'
6
+ Roda::RodaPlugins.register_plugin(:view_subdirs, Roda::RodaPlugins::ViewOptions)
data/lib/roda/version.rb CHANGED
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 2
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 0
7
+ RodaMinorVersion = 1
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -16,4 +16,16 @@ describe "r.run" do
16
16
 
17
17
  body("/provider/services/101").should == 'View 101'
18
18
  end
19
+
20
+ it "modifies SCRIPT_NAME/PATH_INFO when calling run" do
21
+ a = app{|r| "#{r.script_name}|#{r.path_info}"}
22
+ app(:static_path_info){|r| r.on("a"){r.run a}}
23
+ body("/a/b").should == "/a|/b"
24
+ end
25
+
26
+ it "restores SCRIPT_NAME/PATH_INFO before returning from run" do
27
+ a = app{|r| "#{r.script_name}|#{r.path_info}"}
28
+ app(:static_path_info){|r| s = catch(:halt){r.on("a"){r.run a}}; "#{s[2].join}%#{r.script_name}|#{r.path_info}"}
29
+ body("/a/b").should == "/a|/b%|/a/b"
30
+ end
19
31
  end
@@ -291,6 +291,40 @@ describe "r.on" do
291
291
  body("/123").should == '+1'
292
292
  end
293
293
 
294
+ it "does not modify SCRIPT_NAME/PATH_INFO during routing" do
295
+ app(:pass) do |r|
296
+ r.on "foo" do
297
+ r.is "bar" do
298
+ "bar|#{env['SCRIPT_NAME']}|#{env['PATH_INFO']}"
299
+ end
300
+ r.is "baz" do
301
+ r.pass
302
+ end
303
+ "foo|#{env['SCRIPT_NAME']}|#{env['PATH_INFO']}"
304
+ end
305
+ "#{env['SCRIPT_NAME']}|#{env['PATH_INFO']}"
306
+ end
307
+
308
+ body.should == '|/'
309
+ body('SCRIPT_NAME'=>'/a').should == '/a|/'
310
+ body('/foo').should == 'foo||/foo'
311
+ body('/foo', 'SCRIPT_NAME'=>'/a').should == 'foo|/a|/foo'
312
+ body('/foo/bar').should == 'bar||/foo/bar'
313
+ body('/foo/bar', 'SCRIPT_NAME'=>'/a').should == 'bar|/a|/foo/bar'
314
+ body('/foo/baz').should == 'foo||/foo/baz'
315
+ body('/foo/baz', 'SCRIPT_NAME'=>'/a').should == 'foo|/a|/foo/baz'
316
+ end
317
+
318
+ it "should have path/matched_path/remaining_path work correctly" do
319
+ app do |r|
320
+ r.on "foo" do
321
+ "#{r.path}:#{r.matched_path}:#{r.remaining_path}"
322
+ end
323
+ end
324
+
325
+ body("/foo/bar").should == "/foo/bar:/foo:/bar"
326
+ end
327
+
294
328
  it "ensures remaining_path is reverted if modified in failing matcher" do
295
329
  app do |r|
296
330
  r.on lambda { @remaining_path = "/blah"; false } do
@@ -11,9 +11,9 @@ rescue LoadError
11
11
  end
12
12
 
13
13
  if run_tests
14
- metadata_file = 'spec/assets/tmp/precompiled.json'
15
- js_file = 'spec/assets/js/head/app.js'
16
- css_file = 'spec/assets/css/no_access.css'
14
+ metadata_file = File.expand_path('spec/assets/tmp/precompiled.json')
15
+ js_file = File.expand_path('spec/assets/js/head/app.js')
16
+ css_file = File.expand_path('spec/assets/css/no_access.css')
17
17
  js_mtime = File.mtime(js_file)
18
18
  js_atime = File.atime(js_file)
19
19
  css_mtime = File.mtime(css_file)
@@ -48,44 +48,58 @@ if run_tests
48
48
  end
49
49
 
50
50
  it 'assets_opts should use correct paths given options' do
51
- keys = [:js_path, :css_path, :compiled_js_path, :compiled_css_path, :js_prefix, :css_prefix, :compiled_js_prefix, :compiled_css_prefix]
52
- app.assets_opts.values_at(*keys).should == %w"spec/assets/js/ spec/assets/css/ spec/assets/app spec/assets/app assets/js/ assets/css/ assets/app assets/app"
51
+ fpaths = [:js_path, :css_path, :compiled_js_path, :compiled_css_path]
52
+ rpaths = [:js_prefix, :css_prefix, :compiled_js_prefix, :compiled_css_prefix]
53
+ app.assets_opts.values_at(*fpaths).should == %w"spec/assets/js/ spec/assets/css/ spec/assets/app spec/assets/app".map{|s| File.join(Dir.pwd, s)}
54
+ app.assets_opts.values_at(*rpaths).should == %w"assets/js/ assets/css/ assets/app assets/app"
53
55
 
54
56
  app.plugin :assets, :path=>'bar/', :public=>'foo/', :prefix=>'as/', :js_dir=>'j/', :css_dir=>'c/', :compiled_name=>'a'
55
- app.assets_opts.values_at(*keys).should == %w"bar/j/ bar/c/ foo/as/a foo/as/a as/j/ as/c/ as/a as/a"
57
+ app.assets_opts.values_at(*fpaths).should == %w"bar/j/ bar/c/ foo/as/a foo/as/a".map{|s| File.join(Dir.pwd, s)}
58
+ app.assets_opts.values_at(*rpaths).should == %w"as/j/ as/c/ as/a as/a"
56
59
 
57
60
  app.plugin :assets, :path=>'bar', :public=>'foo', :prefix=>'as', :js_dir=>'j', :css_dir=>'c', :compiled_name=>'a'
58
- app.assets_opts.values_at(*keys).should == %w"bar/j/ bar/c/ foo/as/a foo/as/a as/j/ as/c/ as/a as/a"
61
+ app.assets_opts.values_at(*fpaths).should == %w"bar/j/ bar/c/ foo/as/a foo/as/a".map{|s| File.join(Dir.pwd, s)}
62
+ app.assets_opts.values_at(*rpaths).should == %w"as/j/ as/c/ as/a as/a"
59
63
 
60
64
  app.plugin :assets, :compiled_js_dir=>'cj', :compiled_css_dir=>'cs', :compiled_path=>'cp'
61
- app.assets_opts.values_at(*keys).should == %w"bar/j/ bar/c/ foo/cp/cj/a foo/cp/cs/a as/j/ as/c/ as/cj/a as/cs/a"
65
+ app.assets_opts.values_at(*fpaths).should == %w"bar/j/ bar/c/ foo/cp/cj/a foo/cp/cs/a".map{|s| File.join(Dir.pwd, s)}
66
+ app.assets_opts.values_at(*rpaths).should == %w"as/j/ as/c/ as/cj/a as/cs/a"
62
67
 
63
68
  app.plugin :assets, :compiled_js_route=>'cjr', :compiled_css_route=>'ccr', :js_route=>'jr', :css_route=>'cr'
64
- app.assets_opts.values_at(*keys).should == %w"bar/j/ bar/c/ foo/cp/cj/a foo/cp/cs/a as/jr/ as/cr/ as/cjr/a as/ccr/a"
69
+ app.assets_opts.values_at(*fpaths).should == %w"bar/j/ bar/c/ foo/cp/cj/a foo/cp/cs/a".map{|s| File.join(Dir.pwd, s)}
70
+ app.assets_opts.values_at(*rpaths).should == %w"as/jr/ as/cr/ as/cjr/a as/ccr/a"
65
71
 
66
72
  app.plugin :assets, :compiled_js_route=>'cj', :compiled_css_route=>'cs', :js_route=>'j', :css_route=>'c'
67
- app.assets_opts.values_at(*keys).should == %w"bar/j/ bar/c/ foo/cp/cj/a foo/cp/cs/a as/j/ as/c/ as/cj/a as/cs/a"
73
+ app.assets_opts.values_at(*fpaths).should == %w"bar/j/ bar/c/ foo/cp/cj/a foo/cp/cs/a".map{|s| File.join(Dir.pwd, s)}
74
+ app.assets_opts.values_at(*rpaths).should == %w"as/j/ as/c/ as/cj/a as/cs/a"
68
75
 
69
76
  app.plugin :assets
70
- app.assets_opts.values_at(*keys).should == %w"bar/j/ bar/c/ foo/cp/cj/a foo/cp/cs/a as/j/ as/c/ as/cj/a as/cs/a"
77
+ app.assets_opts.values_at(*fpaths).should == %w"bar/j/ bar/c/ foo/cp/cj/a foo/cp/cs/a".map{|s| File.join(Dir.pwd, s)}
78
+ app.assets_opts.values_at(*rpaths).should == %w"as/j/ as/c/ as/cj/a as/cs/a"
71
79
 
72
80
  app.plugin :assets, :compiled_js_dir=>'', :compiled_css_dir=>nil, :compiled_js_route=>nil, :compiled_css_route=>nil
73
- app.assets_opts.values_at(*keys).should == %w"bar/j/ bar/c/ foo/cp/a foo/cp/a as/j/ as/c/ as/a as/a"
81
+ app.assets_opts.values_at(*fpaths).should == %w"bar/j/ bar/c/ foo/cp/a foo/cp/a".map{|s| File.join(Dir.pwd, s)}
82
+ app.assets_opts.values_at(*rpaths).should == %w"as/j/ as/c/ as/a as/a"
74
83
 
75
84
  app.plugin :assets, :js_dir=>'', :css_dir=>nil, :js_route=>nil, :css_route=>nil
76
- app.assets_opts.values_at(*keys).should == %w"bar/ bar/ foo/cp/a foo/cp/a as/ as/ as/a as/a"
85
+ app.assets_opts.values_at(*fpaths).should == %w"bar/ bar/ foo/cp/a foo/cp/a".map{|s| File.join(Dir.pwd, s)}
86
+ app.assets_opts.values_at(*rpaths).should == %w"as/ as/ as/a as/a"
77
87
 
78
88
  app.plugin :assets, :public=>''
79
- app.assets_opts.values_at(*keys).should == %w"bar/ bar/ cp/a cp/a as/ as/ as/a as/a"
89
+ app.assets_opts.values_at(*fpaths).should == %w"bar/ bar/ cp/a cp/a".map{|s| File.join(Dir.pwd, s)}
90
+ app.assets_opts.values_at(*rpaths).should == %w"as/ as/ as/a as/a"
80
91
 
81
92
  app.plugin :assets, :path=>'', :compiled_path=>nil
82
- app.assets_opts.values_at(*keys).should == ['', '', 'a', 'a', 'as/', 'as/', 'as/a', 'as/a']
93
+ app.assets_opts.values_at(*fpaths).should == ['', '', 'a', 'a'].map{|s| File.join(Dir.pwd, s)}
94
+ app.assets_opts.values_at(*rpaths).should == ['as/', 'as/', 'as/a', 'as/a']
83
95
 
84
96
  app.plugin :assets, :prefix=>''
85
- app.assets_opts.values_at(*keys).should == ['', '', 'a', 'a', '', '', 'a', 'a']
97
+ app.assets_opts.values_at(*fpaths).should == ['', '', 'a', 'a'].map{|s| File.join(Dir.pwd, s)}
98
+ app.assets_opts.values_at(*rpaths).should == ['', '', 'a', 'a']
86
99
 
87
100
  app.plugin :assets, :compiled_name=>nil
88
- app.assets_opts.values_at(*keys).should == ['', '', '', '', '', '', '', '']
101
+ app.assets_opts.values_at(*fpaths).should == ['', ''].map{|s| File.join(Dir.pwd, s)} + ['', ''].map{|s| File.join(Dir.pwd, s).chop}
102
+ app.assets_opts.values_at(*rpaths).should == ['', '', '', '']
89
103
  end
90
104
 
91
105
  it 'assets_opts should use headers and dependencies given options' do
@@ -126,6 +140,22 @@ if run_tests
126
140
  js.should include('console.log')
127
141
  end
128
142
 
143
+ it 'should handle rendering assets, linking to them, and accepting requests for them when :add_script_name app option is used' do
144
+ app.opts[:add_script_name] = true
145
+ app.plugin :assets
146
+ html = body('/test', 'SCRIPT_NAME'=>'/foo')
147
+ html.scan(/<link/).length.should == 2
148
+ html =~ %r{href="/foo(/assets/css/app\.scss)"}
149
+ css = body($1)
150
+ html =~ %r{href="/foo(/assets/css/raw\.css)"}
151
+ css2 = body($1)
152
+ html.scan(/<script/).length.should == 1
153
+ html =~ %r{src="/foo(/assets/js/head/app\.js)"}
154
+ js = body($1)
155
+ css.should =~ /color: red;/
156
+ css2.should =~ /color: blue;/
157
+ end
158
+
129
159
  it 'should handle rendering assets, linking to them, and accepting requests for them when not compiling, with different options' do
130
160
  app.plugin :assets, :path=>'spec/', :js_dir=>'assets/js', :css_dir=>'assets/css', :prefix=>'a',
131
161
  :js_route=>'foo', :css_route=>'bar', :add_suffix=>true, :css_opts=>{:style=>:compressed}
@@ -203,6 +233,21 @@ if run_tests
203
233
  js.should include('console.log')
204
234
  end
205
235
 
236
+ it 'should handle compiling assets, linking to them, and accepting requests for them when :add_script_name app option is used' do
237
+ app.opts[:add_script_name] = true
238
+ app.plugin :assets
239
+ app.compile_assets
240
+ html = body('/test', 'SCRIPT_NAME'=>'/foo')
241
+ html =~ %r{href="/foo(/assets/app\.[a-f0-9]{40}\.css)"}
242
+ css = body($1)
243
+ html.scan(/<script/).length.should == 1
244
+ html =~ %r{src="/foo(/assets/app\.head\.[a-f0-9]{40}\.js)"}
245
+ js = body($1)
246
+ css.should =~ /color: ?red/
247
+ css.should =~ /color: ?blue/
248
+ js.should include('console.log')
249
+ end
250
+
206
251
  it 'should handle compiling assets, linking to them, and accepting requests for them, with different options' do
207
252
  app.plugin :assets, :compiled_path=>nil, :js_dir=>'assets/js', :css_dir=>'assets/css', :prefix=>'a',
208
253
  :public=>'spec/assets', :path=>'spec', :compiled_js_route=>'foo', :compiled_css_route=>'bar'
@@ -241,6 +286,29 @@ if run_tests
241
286
  js.should include('console.log')
242
287
  end
243
288
 
289
+ it 'should handle rendering assets, linking to them, and accepting requests for them when not compiling with a multi-level hash when :add_script_name app option is used' do
290
+ app.opts[:add_script_name] = true
291
+ app.plugin :assets, :path=>'spec', :js_dir=>nil, :css_dir=>nil, :compiled_js_dir=>nil, :compiled_css_dir=>nil,
292
+ :css=>{:assets=>{:css=>%w'app.scss raw.css'}}, :js=>{:assets=>{:js=>{:head=>'app.js'}}}
293
+ app.compile_assets
294
+ app.route do |r|
295
+ r.assets
296
+ r.is 'test' do
297
+ "#{assets([:css, :assets, :css])}\n#{assets([:js, :assets, :js, :head])}"
298
+ end
299
+ end
300
+ html = body('/test', 'SCRIPT_NAME'=>'/foo')
301
+ html.scan(/<link/).length.should == 1
302
+ html =~ %r{href="/foo(/assets/app\.assets\.css\.[a-f0-9]{40}\.css)"}
303
+ css = body($1)
304
+ html.scan(/<script/).length.should == 1
305
+ html =~ %r{src="/foo(/assets/app\.assets\.js\.head\.[a-f0-9]{40}\.js)"}
306
+ js = body($1)
307
+ css.should =~ /color: ?red/
308
+ css.should =~ /color: ?blue/
309
+ js.should include('console.log')
310
+ end
311
+
244
312
  it 'should handle :group_subdirs => false when compiling' do
245
313
  app.plugin :assets, :path=>'spec', :js_dir=>nil, :css_dir=>nil, :compiled_js_dir=>nil, :compiled_css_dir=>nil, :group_subdirs=>false,
246
314
  :css=>{:assets=>{:css=>%w'assets/css/app.scss assets/css/raw.css'}}, :js=>{:assets=>{:js=>{:head=>'assets/js/head/app.js'}}}
@@ -410,4 +478,31 @@ if run_tests
410
478
  app.allocate.assets([:js, :head]).should =~ %r{src="(/assets/app\.head\.[a-f0-9]{40}\.js)"}
411
479
  end
412
480
  end
481
+
482
+ describe 'assets plugin' do
483
+ it "app :root option affects :views default" do
484
+ app.plugin :assets
485
+ app.assets_opts[:path].should == File.join(Dir.pwd, 'assets')
486
+ app.assets_opts[:js_path].should == File.join(Dir.pwd, 'assets/js/')
487
+ app.assets_opts[:css_path].should == File.join(Dir.pwd, 'assets/css/')
488
+
489
+ app.opts[:root] = '/foo'
490
+ app.plugin :assets
491
+ app.assets_opts[:path].should == '/foo/assets'
492
+ app.assets_opts[:js_path].should == '/foo/assets/js/'
493
+ app.assets_opts[:css_path].should == '/foo/assets/css/'
494
+
495
+ app.opts[:root] = '/foo/bar'
496
+ app.plugin :assets
497
+ app.assets_opts[:path].should == '/foo/bar/assets'
498
+ app.assets_opts[:js_path].should == '/foo/bar/assets/js/'
499
+ app.assets_opts[:css_path].should == '/foo/bar/assets/css/'
500
+
501
+ app.opts[:root] = nil
502
+ app.plugin :assets
503
+ app.assets_opts[:path].should == File.join(Dir.pwd, 'assets')
504
+ app.assets_opts[:js_path].should == File.join(Dir.pwd, 'assets/js/')
505
+ app.assets_opts[:css_path].should == File.join(Dir.pwd, 'assets/css/')
506
+ end
507
+ end
413
508
  end
@@ -12,4 +12,16 @@ describe "delete_empty_headers plugin" do
12
12
 
13
13
  req[1].should == {'Bar'=>'1'}
14
14
  end
15
+
16
+ it "is called when finishing with a body" do
17
+ app(:delete_empty_headers) do |r|
18
+ response['Foo'] = ''
19
+ response['Content-Type'] = ''
20
+ response['Content-Length'] = ''
21
+ response['Bar'] = '1'
22
+ r.halt response.finish_with_body(['a'])
23
+ end
24
+
25
+ req[1].should == {'Bar'=>'1'}
26
+ end
15
27
  end
@@ -87,6 +87,46 @@ describe "mailer plugin" do
87
87
  m.attachments.length.should == 1
88
88
  m.attachments.first.content_type.should =~ /mailer_spec\.rb/
89
89
  m.content_type.should =~ /\Amultipart\/mixed/
90
+ m.parts.length.should == 1
91
+ m.parts.first.body.should == File.read(__FILE__)
92
+ end
93
+
94
+ it "supports attachments with blocks" do
95
+ app(:mailer) do |r|
96
+ r.mail do
97
+ instance_exec(&setup_email)
98
+ add_file __FILE__ do
99
+ response.mail.attachments.last.content_type = 'text/foo'
100
+ end
101
+ end
102
+ end
103
+
104
+ m = app.mail('foo')
105
+ m.attachments.length.should == 1
106
+ m.attachments.first.content_type.should == 'text/foo'
107
+ m.content_type.should =~ /\Amultipart\/mixed/
108
+ m.parts.length.should == 1
109
+ m.parts.first.body.should == File.read(__FILE__)
110
+ end
111
+
112
+ it "supports plain-text attachments with an email body" do
113
+ app(:mailer) do |r|
114
+ r.mail do
115
+ instance_exec(&setup_email)
116
+ add_file :filename=>'a.txt', :content=>'b'
117
+ 'c'
118
+ end
119
+ end
120
+
121
+ m = app.mail('foo')
122
+ m.parts.length.should == 2
123
+ m.parts.first.content_type.should =~ /text\/plain/
124
+ m.parts.first.body.should == 'c'
125
+ m.parts.last.content_type.should =~ /text\/plain/
126
+ m.parts.last.body.should == 'b'
127
+ m.attachments.length.should == 1
128
+ m.attachments.first.content_type.should =~ /a\.txt/
129
+ m.content_type.should =~ /\Amultipart\/mixed/
90
130
  end
91
131
 
92
132
  it "supports regular web requests in same application" do
@@ -174,7 +214,7 @@ describe "mailer plugin" do
174
214
  app.mail('/').content_type.should =~ /\Atext\/foo/
175
215
  end
176
216
 
177
- it "supports handle setting the default content type when attachments are used" do
217
+ it "supports setting the default content type when attachments are used" do
178
218
  app(:bare) do
179
219
  plugin :mailer, :content_type=>'text/html'
180
220
  route do
@@ -184,8 +224,11 @@ describe "mailer plugin" do
184
224
  end
185
225
  m = app.mail('/')
186
226
  m.content_type.should =~ /\Amultipart\/mixed/
187
- m.parts.first.content_type.should =~ /\Atext\/css/
188
- m.parts.last.content_type.should =~ /\Atext\/html/
227
+ m.parts.length.should == 2
228
+ m.parts.first.content_type.should =~ /\Atext\/html/
229
+ m.parts.first.body.should == "a"
230
+ m.parts.last.content_type.should =~ /\Atext\/css/
231
+ m.parts.last.body.should == File.read('spec/assets/css/raw.css')
189
232
  end
190
233
  end
191
234
  end
@@ -28,4 +28,21 @@ describe "module_include plugin" do
28
28
 
29
29
  req.should == [1, {}, []]
30
30
  end
31
+
32
+ it "should work if called multiple times with a block" do
33
+ app(:bare) do
34
+ plugin :module_include
35
+ request_module{def h; halt response.f end}
36
+ request_module{def i; h end}
37
+ response_module{def f; finish end}
38
+ response_module{def finish; [1, {}, []] end}
39
+
40
+ route do |r|
41
+ r.i
42
+ end
43
+ end
44
+
45
+ req.should == [1, {}, []]
46
+ end
47
+
31
48
  end
@@ -170,6 +170,16 @@ describe "multi_route plugin" do
170
170
  end
171
171
  end
172
172
 
173
+ describe "multi_route plugin" do
174
+ it "r.multi_route works even without routes defined" do
175
+ app(:multi_route) do |r|
176
+ r.multi_route
177
+ 'a'
178
+ end
179
+ body.should == 'a'
180
+ end
181
+ end
182
+
173
183
  describe "multi_route plugin" do
174
184
  before do
175
185
  app(:bare) do