roda-cj 0.9.6 → 1.0.0

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.
@@ -88,8 +88,11 @@ class Roda
88
88
  always(&block) if #{verb == :get ? :is_get : verb}?
89
89
  else
90
90
  args << ::Roda::RodaPlugins::Base::RequestMethods::TERM
91
- if_match(args) do
92
- #{verb}(&block)
91
+ if_match(args) do |*args|
92
+ if #{verb == :get ? :is_get : verb}?
93
+ block_result(yield(*args))
94
+ throw :halt, response.finish
95
+ end
93
96
  response.status = 405
94
97
  response['Allow'] = '#{verb.to_s.upcase}'
95
98
  nil
@@ -112,7 +115,11 @@ class Roda
112
115
  begin
113
116
  @_is_verbs = []
114
117
 
115
- ret = yield
118
+ ret = if verbs.empty?
119
+ yield
120
+ else
121
+ yield(*captures)
122
+ end
116
123
 
117
124
  unless @_is_verbs.empty?
118
125
  response.status = 405
@@ -0,0 +1,38 @@
1
+ class Roda
2
+ module RodaPlugins
3
+ # The path plugin adds support for named paths. Using the +path+ class method, you can
4
+ # easily create <tt>*_path</tt> instance methods for each named path. Those instance
5
+ # methods can then be called if you need to get the path for a form action, link,
6
+ # redirect, or anything else. Example:
7
+ #
8
+ # plugin :path
9
+ # path :foo, '/foo'
10
+ # path :bar do |bar|
11
+ # "/bar/#{bar.id}"
12
+ # end
13
+ #
14
+ # route do |r|
15
+ # r.post 'bar' do
16
+ # bar = Bar.create(r.params['bar'])
17
+ # r.redirect bar_path(bar)
18
+ # end
19
+ # end
20
+ module Path
21
+ module ClassMethods
22
+ def path(name, path=nil, &block)
23
+ raise RodaError, "cannot provide both path and block to Roda.path" if path && block
24
+ raise RodaError, "must provide either path or block to Roda.path" unless path || block
25
+
26
+ if path
27
+ path = path.dup.freeze
28
+ block = lambda{path}
29
+ end
30
+
31
+ define_method("#{name}_path", &block)
32
+ end
33
+ end
34
+ end
35
+
36
+ register_plugin(:path, Path)
37
+ end
38
+ end
@@ -38,7 +38,7 @@ class Roda
38
38
  # Note that because of how segment matching works, :format, :opt, and :optd
39
39
  # are only going to work inside of a string, like this:
40
40
  #
41
- # r.is "album:opt" do |id|
41
+ # r.is "album:opt" do |id| end
42
42
  # # matches /album (yielding nil) and /album/foo (yielding "foo")
43
43
  # # does not match /album/ or /album/foo/bar
44
44
  module SymbolMatchers
@@ -6,7 +6,7 @@ class Roda
6
6
  # use, and template names that do not contain a slash will
7
7
  # automatically use that view subdirectory. Example:
8
8
  #
9
- # plugin :render
9
+ # plugin :render, :layout=>'./layout'
10
10
  # plugin :view_subdirs
11
11
  #
12
12
  # route do |r|
@@ -25,6 +25,12 @@ class Roda
25
25
  #
26
26
  # This plugin should be loaded after the render plugin, since
27
27
  # it works by overriding parts of the render plugin.
28
+ #
29
+ # Note that when a view subdirectory is set, the layout will
30
+ # also be looked up in the subdirectory unless it contains
31
+ # a slash. So if you want to use a view subdirectory for
32
+ # templates but have a shared layout, you should make sure your
33
+ # layout contains a slash, similar to the example above.
28
34
  module ViewSubdirs
29
35
  module InstanceMethods
30
36
  # Set the view subdirectory to use. This can be set to nil
@@ -1,3 +1,3 @@
1
1
  class Roda
2
- RodaVersion = '0.9.6'.freeze
2
+ RodaVersion = '1.0.0'.freeze
3
3
  end
@@ -18,7 +18,7 @@ describe "integration" do
18
18
 
19
19
  end
20
20
 
21
- it "should setup middleware using use " do
21
+ it "should setup middleware using use" do
22
22
  c = @c
23
23
  app(:bare) do
24
24
  use c, "First", "Second" do
@@ -35,7 +35,24 @@ describe "integration" do
35
35
  body('/hello').should == 'D First Second Block'
36
36
  end
37
37
 
38
- it "should inherit middleware in subclass " do
38
+ it "should support adding middleware using use after route block setup" do
39
+ c = @c
40
+ app(:bare) do
41
+ route do |r|
42
+ r.get "hello" do
43
+ "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
44
+ end
45
+ end
46
+
47
+ use c, "First", "Second" do
48
+ "Block"
49
+ end
50
+ end
51
+
52
+ body('/hello').should == 'D First Second Block'
53
+ end
54
+
55
+ it "should inherit middleware in subclass" do
39
56
  c = @c
40
57
  @app = Class.new(app(:bare){use(c, '1', '2'){"3"}})
41
58
  @app.route do |r|
@@ -47,7 +64,55 @@ describe "integration" do
47
64
  body('/hello').should == 'D 1 2 3'
48
65
  end
49
66
 
50
- it "should not have future middleware additions to parent class affect subclass " do
67
+ it "should inherit route in subclass" do
68
+ c = @c
69
+ app(:bare) do
70
+ use(c, '1', '2'){"3"}
71
+ route do |r|
72
+ r.get "hello" do
73
+ "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
74
+ end
75
+ end
76
+ end
77
+ @app = Class.new(app)
78
+
79
+ body('/hello').should == 'D 1 2 3'
80
+ end
81
+
82
+ it "should use instance of subclass when inheriting routes" do
83
+ c = @c
84
+ obj = nil
85
+ app(:bare) do
86
+ use(c, '1', '2'){"3"}
87
+ route do |r|
88
+ r.get "hello" do
89
+ obj = self
90
+ "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
91
+ end
92
+ end
93
+ end
94
+ @app = Class.new(app)
95
+
96
+ body('/hello').should == 'D 1 2 3'
97
+ obj.should be_a_kind_of(@app)
98
+ end
99
+
100
+ it "should handle middleware added to subclass using superclass route" do
101
+ c = @c
102
+ app(:bare) do
103
+ route do |r|
104
+ r.get "hello" do
105
+ "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
106
+ end
107
+ end
108
+ end
109
+ @app = Class.new(app)
110
+ @app.use(c, '1', '2'){"3"}
111
+
112
+ body('/hello').should == 'D 1 2 3'
113
+ end
114
+
115
+ it "should not have future middleware additions to superclass affect subclass" do
51
116
  c = @c
52
117
  a = app
53
118
  @app = Class.new(a)
@@ -60,4 +125,31 @@ describe "integration" do
60
125
 
61
126
  body('/hello').should == 'D '
62
127
  end
128
+
129
+ it "should not have future middleware additions to subclass affect superclass" do
130
+ c = @c
131
+ a = app do |r|
132
+ r.get "hello" do
133
+ "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
134
+ end
135
+ end
136
+ @app = Class.new(a)
137
+ @app.use(c, '1', '2'){"3"}
138
+ @app = a
139
+
140
+ body('/hello').should == 'D '
141
+ end
142
+
143
+ it "should have app return the rack application to call" do
144
+ app(:bare){}.app.should == nil
145
+ app.route{|r|}
146
+ app.app.should be_a_kind_of(Proc)
147
+ c = Class.new{def initialize(app) @app = app end; def call(env) @app.call(env) end}
148
+ app.use c
149
+ app.app.should be_a_kind_of(c)
150
+ end
151
+
152
+ it "should have route_block return the route block" do
153
+ app{|r| 1}.route_block.call(nil).should == 1
154
+ end
63
155
  end
@@ -1,6 +1,7 @@
1
1
  require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
2
 
3
3
  begin
4
+ require 'erubis'
4
5
  require 'tilt/erb'
5
6
  begin
6
7
  require 'tilt/erubis'
@@ -0,0 +1,86 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ begin
4
+ require 'tilt'
5
+ require 'tilt/sass'
6
+ require 'tilt/coffee'
7
+ rescue LoadError
8
+ warn 'tilt not installed, skipping assets plugin test'
9
+ else
10
+ describe 'assets plugin' do
11
+ before do
12
+ app(:bare) do
13
+ plugin(:assets, {
14
+ path: './spec/dummy/assets',
15
+ css_engine: 'scss',
16
+ js_engine: 'coffee',
17
+ headers: {
18
+ "Cache-Control" => 'public, max-age=2592000, no-transform',
19
+ 'Connection' => 'keep-alive',
20
+ 'Age' => '25637',
21
+ 'Strict-Transport-Security' => 'max-age=31536000',
22
+ 'Content-Disposition' => 'inline'
23
+ }
24
+ })
25
+
26
+ assets_opts[:css] = ['app', '../raw.css']
27
+ assets_opts[:js] = { head: ['app'] }
28
+
29
+ route do |r|
30
+ r.assets
31
+
32
+ r.is 'test' do
33
+ response.write assets :css
34
+ response.write assets [:js, :head]
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ it 'should contain proper configuration' do
41
+ app.assets_opts[:path].should == './spec/dummy/assets'
42
+ app.assets_opts[:css].should include('app')
43
+ end
44
+
45
+ it 'should serve proper assets' do
46
+ body('/assets/css/app.css').should include('color: red')
47
+ body('/assets/css/%242E%242E/raw.css').should include('color: blue')
48
+ body('/assets/js/head/app.js').should include('console.log')
49
+ body('/assets/css/http://google.com').should include('google.com')
50
+ end
51
+
52
+ it 'should contain proper assets html tags' do
53
+ html = body '/test'
54
+ html.scan(/<link/).length.should eq 2
55
+ html.scan(/<script/).length.should eq 1
56
+ html.should include('link')
57
+ html.should include('script')
58
+ end
59
+
60
+ it 'should only show one link when concat/compile is true' do
61
+ app.assets_opts[:concat] = true
62
+ html = body '/test'
63
+ html.scan(/<link/).length.should eq 1
64
+
65
+ app.assets_opts[:compiled] = true
66
+ html = body '/test'
67
+ html.scan(/<link/).length.should eq 1
68
+ end
69
+
70
+ it 'should join all files when concat is true' do
71
+ app.assets_opts[:concat] = true
72
+ path = app.assets_opts[:concat_name] + '/css/123'
73
+ css = body("/assets/css/#{path}.css")
74
+ css.should include('color: red')
75
+ css.should include('color: blue')
76
+ end
77
+
78
+ it 'should grab compiled files' do
79
+ app.compile_assets
80
+ app.assets_opts[:compiled] = true
81
+ path = app.assets_opts[:compiled_name] + '/js-head/123'
82
+ js = body("/assets/js/#{path}.js")
83
+ js.should include('console.log')
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,68 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "error_email plugin" do
4
+ def app(opts={})
5
+ @emails = emails = [] unless defined?(@emails)
6
+ @app ||= super(:bare) do
7
+ plugin :error_email, {:to=>'t', :from=>'f', :emailer=>lambda{|h| emails << h}}.merge(opts)
8
+
9
+ route do |r|
10
+ raise ArgumentError rescue error_email($!)
11
+ 'e'
12
+ end
13
+ end
14
+ end
15
+
16
+ def email
17
+ @emails.last
18
+ end
19
+
20
+ it "adds error_email method for emailing exceptions" do
21
+ app
22
+ body('rack.input'=>StringIO.new).should == 'e'
23
+ email[:to].should == 't'
24
+ email[:from].should == 'f'
25
+ email[:host].should == 'localhost'
26
+ email[:message].should =~ /^Subject: ArgumentError/
27
+ email[:message].should =~ /Backtrace.*ENV/m
28
+ end
29
+
30
+ it "uses :host option" do
31
+ app(:host=>'foo.bar.com')
32
+ body('rack.input'=>StringIO.new).should == 'e'
33
+ email[:host].should == 'foo.bar.com'
34
+ end
35
+
36
+ it "adds :prefix option to subject line" do
37
+ app(:prefix=>'TEST ')
38
+ body('rack.input'=>StringIO.new).should == 'e'
39
+ email[:message].should =~ /^Subject: TEST ArgumentError/
40
+ end
41
+
42
+ it "uses :headers option for additional headers" do
43
+ app(:headers=>{'Foo'=>'Bar', 'Baz'=>'Quux'})
44
+ body('rack.input'=>StringIO.new).should == 'e'
45
+ email[:message].should =~ /^Foo: Bar/
46
+ email[:message].should =~ /^Baz: Quux/
47
+ end
48
+
49
+ it "requires the :to and :from options" do
50
+ proc{app :from=>nil}.should raise_error(Roda::RodaError)
51
+ proc{app :to=>nil}.should raise_error(Roda::RodaError)
52
+ end
53
+
54
+ it "works correctly in subclasses" do
55
+ @app = Class.new(app)
56
+ @app.route do |r|
57
+ raise ArgumentError rescue error_email($!)
58
+ 'e'
59
+ end
60
+ body('rack.input'=>StringIO.new).should == 'e'
61
+ email[:to].should == 't'
62
+ email[:from].should == 'f'
63
+ email[:host].should == 'localhost'
64
+ email[:message].should =~ /^Subject: ArgumentError/
65
+ email[:message].should =~ /Backtrace.*ENV/m
66
+ end
67
+
68
+ end
@@ -25,11 +25,21 @@ describe "multi_route plugin" do
25
25
  end
26
26
  end
27
27
 
28
+ route(:p) do |r|
29
+ r.is do
30
+ 'p'
31
+ end
32
+ end
33
+
28
34
  route do |r|
29
35
  r.on 'foo' do
30
36
  r.multi_route do
31
37
  "foo"
32
38
  end
39
+
40
+ r.on "p" do
41
+ r.route(:p)
42
+ end
33
43
  end
34
44
 
35
45
  r.get do
@@ -71,6 +81,18 @@ describe "multi_route plugin" do
71
81
  body('/foo/post/b').should == 'foo'
72
82
  end
73
83
 
84
+ it "does not have multi_route match non-String named routes" do
85
+ body('/foo/p').should == 'p'
86
+ status('/foo/p/2').should == 404
87
+ end
88
+
89
+ it "Can have multi_route pick up routes newly added" do
90
+ body('/foo/get/').should == 'get'
91
+ status('/foo/delete').should == 404
92
+ app.route('delete'){|r| r.on{'delete'}}
93
+ body('/foo/delete').should == 'delete'
94
+ end
95
+
74
96
  it "handles loading the plugin multiple times correctly" do
75
97
  app.plugin :multi_route
76
98
  body.should == 'get'
@@ -23,6 +23,12 @@ describe "not_allowed plugin" do
23
23
  r.is 'b' do
24
24
  'b'
25
25
  end
26
+ r.is /(d)/ do |s|
27
+ s
28
+ end
29
+ r.get /(e)/ do |s|
30
+ s
31
+ end
26
32
  end
27
33
  end
28
34
 
@@ -33,6 +39,13 @@ describe "not_allowed plugin" do
33
39
  body('/b').should == 'b'
34
40
  status('/b', 'REQUEST_METHOD'=>'POST').should == 404
35
41
 
42
+ body('/d').should == 'd'
43
+ status('/d', 'REQUEST_METHOD'=>'POST').should == 404
44
+
45
+ body('/e').should == 'e'
46
+ status('/d', 'REQUEST_METHOD'=>'POST').should == 404
47
+
48
+ body('/c').should == 'cg'
36
49
  body('/c').should == 'cg'
37
50
  body('/c', 'REQUEST_METHOD'=>'POST').should == 'cp'
38
51
  body('/c', 'REQUEST_METHOD'=>'PATCH').should == 'c'