roda-cj 0.9.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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'