rory 0.3.8 → 0.3.9

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.
@@ -1,14 +1,29 @@
1
- require 'erb'
1
+ require_relative 'renderer'
2
2
 
3
3
  module Rory
4
4
  # Interface for Controller class. Subclass this to create controllers
5
5
  # with actions that will be called by the Dispatcher when a route matches.
6
6
  class Controller
7
- def initialize(request, context = nil)
7
+ attr_accessor :locals
8
+
9
+ def initialize(request, routing, app = nil)
8
10
  @request = request
9
- @route = request[:route]
11
+ @dispatcher = routing[:dispatcher]
12
+ @route = routing[:route]
10
13
  @params = request.params
11
- @context = context
14
+ @app = app
15
+ @locals = {}
16
+ end
17
+
18
+ def expose(hsh)
19
+ locals.merge!(hsh)
20
+ end
21
+
22
+ def params
23
+ @converted_params ||= @params.inject({}) { |memo, (key, value)|
24
+ memo[key.to_sym] = memo[key.to_s] = value
25
+ memo
26
+ }
12
27
  end
13
28
 
14
29
  def route_template
@@ -19,33 +34,45 @@ module Rory
19
34
  nil
20
35
  end
21
36
 
22
- def render(template, opts = {})
23
- opts = { :layout => layout }.merge(opts)
24
- file = view_path(template)
25
- output = ERB.new(File.read(file)).result(binding)
26
- if layout = opts[:layout]
27
- output = render(File.join('layouts', layout.to_s), { :layout => false }) { output }
28
- end
29
- @body = output
37
+ def default_renderer_options
38
+ {
39
+ :layout => layout,
40
+ :locals => locals,
41
+ :app => @app,
42
+ :base_url => @request.script_name
43
+ }
30
44
  end
31
45
 
32
- def view_path(template)
33
- root = @context ? @context.root : Rory.root
34
- File.expand_path(File.join('views', "#{template}.html.erb"), root)
46
+ def render(template_name, opts = {})
47
+ opts = default_renderer_options.merge(opts)
48
+ renderer = Rory::Renderer.new(template_name, opts)
49
+ @body = renderer.render
35
50
  end
36
51
 
37
52
  def redirect(path)
38
- @response = @request[:dispatcher].redirect(path)
53
+ @response = @dispatcher.redirect(path)
39
54
  end
40
55
 
41
56
  def render_not_found
42
- @response = @request[:dispatcher].render_not_found
57
+ @response = @dispatcher.render_not_found
58
+ end
59
+
60
+ # This method is called before the action method.
61
+ def before_action
62
+ #noop
63
+ end
64
+
65
+ # This method is called after the action method.
66
+ def after_action
67
+ #noop
43
68
  end
44
69
 
45
70
  def present
46
71
  # if a method exists on the controller for the requested action, call it.
47
72
  action = @route[:action]
73
+ before_action
48
74
  self.send(action) if self.respond_to?(action)
75
+ after_action
49
76
 
50
77
  if @response
51
78
  # that method may have resulted in a response already being generated
@@ -3,13 +3,14 @@ module Rory
3
3
  # appropriate controller, after examining the routes.
4
4
  class Dispatcher
5
5
  attr_reader :request
6
- def initialize(rack_request, context = nil)
6
+ def initialize(rack_request, app = nil)
7
7
  @request = rack_request
8
- @context = context
8
+ @routing = {}
9
+ @app = app
9
10
  end
10
11
 
11
12
  def route
12
- @request[:route] ||= get_route
13
+ @routing[:route] ||= get_route
13
14
  end
14
15
 
15
16
  def dispatch
@@ -45,9 +46,8 @@ module Rory
45
46
 
46
47
  def controller
47
48
  if klass = controller_class
48
- request_for_delivery = @request.dup
49
- request_for_delivery[:dispatcher] = self
50
- klass.new(request_for_delivery, @context)
49
+ @routing.merge!(:dispatcher => self)
50
+ klass.new(request, @routing, @app)
51
51
  end
52
52
  end
53
53
 
@@ -77,7 +77,7 @@ module Rory
77
77
  end
78
78
 
79
79
  def route_map
80
- @context ? @context.routes : []
80
+ @app ? @app.routes : []
81
81
  end
82
82
  end
83
83
  end
@@ -0,0 +1,43 @@
1
+ require 'erb'
2
+ require_relative 'renderer/context'
3
+
4
+ module Rory
5
+ class Renderer
6
+ def initialize(template_name, options = {})
7
+ @template_name = template_name
8
+ @options = options
9
+ @app = options[:app]
10
+ end
11
+
12
+ def render(&block)
13
+ erb = ERB.new(view_template)
14
+ output = erb.result(view_binding(&block))
15
+ if layout_path
16
+ layout_renderer = self.class.new(layout_path, @options.merge(:layout => false))
17
+ output = layout_renderer.render { output }
18
+ end
19
+ output
20
+ end
21
+
22
+ def view_binding
23
+ render_context = Context.new(@options)
24
+ render_context.get_binding { |*args|
25
+ yield(args) if block_given?
26
+ }
27
+ end
28
+
29
+ def view_template
30
+ File.read(view_path)
31
+ end
32
+
33
+ def layout_path
34
+ return nil unless @options[:layout]
35
+ File.join('layouts', @options[:layout].to_s)
36
+ end
37
+
38
+ def view_path
39
+ root = @app ? @app.root : Rory.root
40
+ File.expand_path(File.join('views', "#{@template_name}.html.erb"), root)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ module Rory
2
+ class Renderer
3
+ class Context
4
+ attr_reader :base_url
5
+
6
+ def initialize(options = {})
7
+ (options[:locals] || {}).each do |key, value|
8
+ singleton_class.send(:define_method, key) { value }
9
+ end
10
+ @app = options[:app]
11
+ @base_url = options[:base_url]
12
+ end
13
+
14
+ def get_binding
15
+ binding
16
+ end
17
+
18
+ def render(template_name, opts = {})
19
+ opts = { :layout => false, :app => @app }.merge(opts)
20
+ renderer = Rory::Renderer.new(template_name, opts)
21
+ renderer.render
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,3 @@
1
1
  module Rory
2
- VERSION = '0.3.8'
2
+ VERSION = '0.3.9'
3
3
  end
@@ -1,5 +1,6 @@
1
1
  class ForRealsController < Rory::Controller
2
2
  def srsly
3
- @gibbit = @params[:parbles]
3
+ expose :gibbit => @params[:parbles]
4
+ expose :but_when => 'again'
4
5
  end
5
6
  end
@@ -1,6 +1,6 @@
1
1
  module Goose
2
2
  class LumpiesController
3
- def initialize(args, context)
3
+ def initialize(args, routing, context)
4
4
  @args = args
5
5
  end
6
6
 
@@ -1,7 +1,7 @@
1
1
  module Goose
2
2
  module Wombat
3
3
  class RabbitsController
4
- def initialize(args, context)
4
+ def initialize(args, routing, context)
5
5
  @args = args
6
6
  end
7
7
 
@@ -1,5 +1,5 @@
1
1
  class StubController
2
- def initialize(args, context)
2
+ def initialize(args, routing, context)
3
3
  @args = args
4
4
  end
5
5
 
@@ -0,0 +1 @@
1
+ But it's <%= when_is_it %> enough!
@@ -1 +1 @@
1
- You've done it again, <%= @gibbit %>!
1
+ You've done it <%= but_when %>, <%= gibbit %>!
@@ -0,0 +1 @@
1
+ You came from <%= base_url %>.
@@ -0,0 +1 @@
1
+ Don't Say <%= render 'test/nested' %>
@@ -0,0 +1 @@
1
+ A Bad <%= render 'test/dynamic', :locals => { :word => 'Poop' } %>!
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  describe Rory::Application do
4
2
  describe ".configure" do
5
3
  it 'yields the given block to self' do
@@ -0,0 +1,125 @@
1
+ describe Rory::Controller do
2
+ before :each do
3
+ @routing = {
4
+ :route => {
5
+ :controller => 'test',
6
+ :action => 'letsgo'
7
+ }
8
+ }
9
+
10
+ @request = double('Rack::Request', {
11
+ :params => { 'violet' => 'invisibility', :dash => 'superspeed' },
12
+ :script_name => 'script_root'
13
+ })
14
+ end
15
+
16
+ describe '#layout' do
17
+ it 'defaults to nil' do
18
+ controller = Rory::Controller.new(@request, @routing)
19
+ controller.layout.should be_nil
20
+ end
21
+ end
22
+
23
+ describe '#params' do
24
+ it 'returns params from request, converted for indifferent key access' do
25
+ controller = Rory::Controller.new(@request, @routing)
26
+ expect(controller.params).to eq({
27
+ 'violet' => 'invisibility',
28
+ 'dash' => 'superspeed',
29
+ :violet => 'invisibility',
30
+ :dash => 'superspeed'
31
+ })
32
+ end
33
+ end
34
+
35
+ describe "#render" do
36
+ it "returns renderer output" do
37
+ controller = Rory::Controller.new(@request, @routing)
38
+ allow(Rory::Renderer).to receive(:new).
39
+ with('not/real', controller.default_renderer_options).
40
+ and_return(double('Renderer', :render => 'Here ya go'))
41
+ controller.render('not/real').should == 'Here ya go'
42
+ end
43
+
44
+ it "passes layout, exposed locals, and app to renderer" do
45
+ controller = Rory::Controller.new(@request, @routing, :scooby)
46
+ controller.expose(:a => 1)
47
+ allow(controller).to receive(:layout).and_return('pretend')
48
+ renderer_options = {
49
+ :layout => 'pretend',
50
+ :locals => { :a => 1 },
51
+ :app => :scooby,
52
+ :base_url => 'script_root'
53
+ }
54
+ allow(Rory::Renderer).to receive(:new).
55
+ with('also/fake', renderer_options).
56
+ and_return(double('Renderer', :render => 'Scamazing!'))
57
+ controller.render('also/fake').should == 'Scamazing!'
58
+ end
59
+ end
60
+
61
+ describe "#redirect" do
62
+ it "delegates to dispatcher from request" do
63
+ @routing[:dispatcher] = dispatcher = double
64
+ dispatcher.should_receive(:redirect).with(:whatever)
65
+ controller = Rory::Controller.new(@request, @routing)
66
+ controller.redirect(:whatever)
67
+ end
68
+ end
69
+
70
+ describe "#render_not_found" do
71
+ it "delegates to dispatcher from request" do
72
+ @routing[:dispatcher] = dispatcher = double
73
+ dispatcher.should_receive(:render_not_found)
74
+ controller = Rory::Controller.new(@request, @routing)
75
+ controller.render_not_found
76
+ end
77
+ end
78
+
79
+ describe "#present" do
80
+ it "calls filters and action from route if exists on controller" do
81
+ controller = Rory::Controller.new(@request, @routing)
82
+ expect(controller).to receive('before_action').ordered
83
+ expect(controller).to receive('letsgo').ordered
84
+ expect(controller).to receive('after_action').ordered
85
+ expect(controller).to receive('render').ordered
86
+ controller.present
87
+ end
88
+
89
+ it "doesn't try to call action from route if nonexistent on controller" do
90
+ controller = Rory::Controller.new(@request, @routing)
91
+ allow(controller).to receive(:respond_to?).with('letsgo').and_return(false)
92
+ expect(controller).to receive('before_action').ordered
93
+ expect(controller).to receive('letsgo').never
94
+ expect(controller).to receive('after_action').ordered
95
+ expect(controller).to receive('render').ordered
96
+ controller.present
97
+ end
98
+
99
+ it "just returns a response if @response exists" do
100
+ controller = Rory::Controller.new(@request, @routing)
101
+ controller.instance_variable_set(:@response, 'Forced response')
102
+ controller.present.should == 'Forced response'
103
+ end
104
+
105
+ it "renders and returns the default template as a rack response" do
106
+ controller = Rory::Controller.new(@request, @routing)
107
+ controller.present.should == [
108
+ 200,
109
+ {'Content-type' => 'text/html', 'charset' => 'UTF-8'},
110
+ ["Let's go content"]
111
+ ]
112
+ end
113
+
114
+ it "returns previously set @body as a rack response" do
115
+ controller = Rory::Controller.new(@request, @routing)
116
+ controller.instance_variable_set(:@body, 'Forced body')
117
+ controller.should_receive(:render).never
118
+ controller.present.should == [
119
+ 200,
120
+ {'Content-type' => 'text/html', 'charset' => 'UTF-8'},
121
+ ["Forced body"]
122
+ ]
123
+ end
124
+ end
125
+ end
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  describe Rory::Dispatcher do
4
2
  describe "#redirect" do
5
3
  it "redirects to given path if path has scheme" do
@@ -35,31 +33,29 @@ describe Rory::Dispatcher do
35
33
  end
36
34
 
37
35
  it "instantiates a controller with the parsed request and calls present" do
38
- request[:route] = { :controller => 'stub' }
36
+ allow(dispatcher).to receive(:get_route).and_return({ :controller => 'stub' })
39
37
  dispatcher.dispatch.should == {
40
38
  :whatever => :yay,
41
- :route => request[:route],
42
- :dispatcher => dispatcher,
43
39
  :present_called => true # see StubController in /spec/fixture_app
44
40
  }
45
41
  end
46
42
 
47
43
  it "dispatches properly to a scoped controller" do
48
- request[:route] = { :controller => 'lumpies', :module => 'goose' }
44
+ allow(dispatcher).to receive(:get_route).and_return({
45
+ :controller => 'lumpies', :module => 'goose'
46
+ })
49
47
  dispatcher.dispatch.should == {
50
48
  :whatever => :yay,
51
- :route => request[:route],
52
- :dispatcher => dispatcher,
53
49
  :in_scoped_controller => true # see Goose::LumpiesController in /spec/fixture_app
54
50
  }
55
51
  end
56
52
 
57
53
  it "dispatches properly to a nested scoped controller" do
58
- request[:route] = { :controller => 'rabbits', :module => 'goose/wombat' }
54
+ allow(dispatcher).to receive(:get_route).and_return({
55
+ :controller => 'rabbits', :module => 'goose/wombat'
56
+ })
59
57
  dispatcher.dispatch.should == {
60
58
  :whatever => :yay,
61
- :route => request[:route],
62
- :dispatcher => dispatcher,
63
59
  :in_scoped_controller => true # see Goose::Wombat::RabbitsController in /spec/fixture_app
64
60
  }
65
61
  end
@@ -73,7 +69,7 @@ describe Rory::Dispatcher do
73
69
  end
74
70
 
75
71
  it "returns route from request if already set" do
76
- @request[:route] = 'snaky pigeons'
72
+ @dispatcher.instance_variable_set(:@routing, { :route => 'snaky pigeons' })
77
73
  @dispatcher.route.should == 'snaky pigeons'
78
74
  end
79
75
 
@@ -0,0 +1,47 @@
1
+ describe Rory::Renderer do
2
+ describe "#render" do
3
+ it "returns text of template" do
4
+ renderer = Rory::Renderer.new('test/static')
5
+ renderer.render.should == 'Static content'
6
+ end
7
+
8
+ it "returns text of template in given layout" do
9
+ controller = Rory::Renderer.new('test/static', :layout => 'surround')
10
+ controller.render.should == 'Surrounding Static content is fun'
11
+ end
12
+
13
+ it "handles symbolized layout name" do
14
+ controller = Rory::Renderer.new('test/static', :layout => :surround)
15
+ controller.render.should == 'Surrounding Static content is fun'
16
+ end
17
+
18
+ it "exposes locals to template" do
19
+ controller = Rory::Renderer.new('test/dynamic', :locals => { :word => 'hockey' })
20
+ controller.render.should == 'Word: hockey'
21
+ end
22
+
23
+ it "can render nested templates" do
24
+ controller = Rory::Renderer.new('test/double_nested', :locals => { :word => 'hockey' })
25
+ controller.render.should ==
26
+ "Don't Say A Bad Word: Poop!"
27
+ end
28
+
29
+ it "exposes base_url to template" do
30
+ controller = Rory::Renderer.new('test/a_link', :base_url => 'spoo')
31
+ controller.render.should == 'You came from spoo.'
32
+ end
33
+ end
34
+
35
+ describe '#view_path' do
36
+ it 'returns path to template from app root' do
37
+ fake_app = double('Application', :root => 'marbles')
38
+ renderer = Rory::Renderer.new('goose', :app => fake_app)
39
+ renderer.view_path.should == File.expand_path(File.join('views', 'goose.html.erb'), 'marbles')
40
+ end
41
+
42
+ it 'uses Rory.root if no app specified' do
43
+ renderer = Rory::Renderer.new('goose')
44
+ renderer.view_path.should == File.join(Rory.root, 'views', 'goose.html.erb')
45
+ end
46
+ end
47
+ end
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  describe Rory::Support do
4
2
  describe ".camelize" do
5
3
  it "camelizes given snake-case string" do
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  describe Rory do
4
2
  describe '.application' do
5
3
  it 'is by default set to the Rory::Application instance' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.3.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-01 00:00:00.000000000 Z
12
+ date: 2014-03-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -192,6 +192,8 @@ files:
192
192
  - lib/rory/application.rb
193
193
  - lib/rory/controller.rb
194
194
  - lib/rory/dispatcher.rb
195
+ - lib/rory/renderer/context.rb
196
+ - lib/rory/renderer.rb
195
197
  - lib/rory/route_mapper.rb
196
198
  - lib/rory/support.rb
197
199
  - lib/rory/tasks.rb
@@ -199,24 +201,29 @@ files:
199
201
  - lib/rory.rb
200
202
  - lib/tasks/db.rake
201
203
  - lib/tasks/rory.rake
202
- - spec/application_spec.rb
203
- - spec/controller_spec.rb
204
- - spec/dispatcher_spec.rb
205
204
  - spec/fixture_app/config/application.rb
206
205
  - spec/fixture_app/config/routes.rb
207
206
  - spec/fixture_app/controllers/for_reals_controller.rb
208
207
  - spec/fixture_app/controllers/goose/lumpies_controller.rb
209
208
  - spec/fixture_app/controllers/goose/wombat/rabbits_controller.rb
210
209
  - spec/fixture_app/controllers/stub_controller.rb
210
+ - spec/fixture_app/views/for_reals/but_wait.html.erb
211
211
  - spec/fixture_app/views/for_reals/srsly.html.erb
212
212
  - spec/fixture_app/views/layouts/surround.html.erb
213
+ - spec/fixture_app/views/test/a_link.html.erb
214
+ - spec/fixture_app/views/test/double_nested.html.erb
213
215
  - spec/fixture_app/views/test/dynamic.html.erb
214
216
  - spec/fixture_app/views/test/letsgo.html.erb
217
+ - spec/fixture_app/views/test/nested.html.erb
215
218
  - spec/fixture_app/views/test/static.html.erb
219
+ - spec/lib/rory/application_spec.rb
220
+ - spec/lib/rory/controller_spec.rb
221
+ - spec/lib/rory/dispatcher_spec.rb
222
+ - spec/lib/rory/renderer_spec.rb
223
+ - spec/lib/rory/support_spec.rb
224
+ - spec/lib/rory_spec.rb
216
225
  - spec/requests/controller_spec.rb
217
- - spec/rory_spec.rb
218
226
  - spec/spec_helper.rb
219
- - spec/support_spec.rb
220
227
  - LICENSE.txt
221
228
  - Rakefile
222
229
  - README.rdoc
@@ -1,125 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Rory::Controller do
4
- before :each do
5
- @request = {
6
- :route => {
7
- :controller => 'test',
8
- :action => 'letsgo'
9
- }
10
- }
11
-
12
- @request.stub(:params => {})
13
- end
14
-
15
- describe '#layout' do
16
- it 'defaults to nil' do
17
- controller = Rory::Controller.new(@request)
18
- controller.layout.should be_nil
19
- end
20
- end
21
-
22
- describe "#render" do
23
- it "returns text of template" do
24
- controller = Rory::Controller.new(@request)
25
- controller.render('test/static').should == 'Static content'
26
- end
27
-
28
- it "returns text of template in controller's layout" do
29
- controller = Rory::Controller.new(@request)
30
- controller.stub(:layout => 'surround')
31
- controller.render('test/static').should == 'Surrounding Static content is fun'
32
- end
33
-
34
- it "handles symbolized layout name" do
35
- controller = Rory::Controller.new(@request)
36
- controller.stub(:layout => :surround)
37
- controller.render('test/static').should == 'Surrounding Static content is fun'
38
- end
39
-
40
- it "returns text of template in given layout from options" do
41
- controller = Rory::Controller.new(@request)
42
- controller.render('test/static', :layout => 'surround').should == 'Surrounding Static content is fun'
43
- end
44
-
45
- it "evaluates ERB in controller's context" do
46
- controller = Rory::Controller.new(@request)
47
- controller.stub(:word).and_return('hockey')
48
- controller.render('test/dynamic').should == 'Word: hockey'
49
- end
50
- end
51
-
52
- describe "#redirect" do
53
- it "delegates to dispatcher from request" do
54
- @request[:dispatcher] = dispatcher = double
55
- dispatcher.should_receive(:redirect).with(:whatever)
56
- controller = Rory::Controller.new(@request)
57
- controller.redirect(:whatever)
58
- end
59
- end
60
-
61
- describe "#render_not_found" do
62
- it "delegates to dispatcher from request" do
63
- @request[:dispatcher] = dispatcher = double
64
- dispatcher.should_receive(:render_not_found)
65
- controller = Rory::Controller.new(@request)
66
- controller.render_not_found
67
- end
68
- end
69
-
70
- describe "#present" do
71
- it "calls action from route if exists on controller" do
72
- controller = Rory::Controller.new(@request)
73
- controller.stub(:render)
74
- controller.should_receive('letsgo')
75
- controller.present
76
- end
77
-
78
- it "doesn't try to call action from route if nonexistent on controller" do
79
- controller = Rory::Controller.new(@request)
80
- controller.stub(:render)
81
- controller.stub(:respond_to?).with('letsgo').and_return(false)
82
- controller.should_receive('letsgo').never
83
- controller.present
84
- end
85
-
86
- it "just returns a response if @response exists" do
87
- controller = Rory::Controller.new(@request)
88
- controller.instance_variable_set(:@response, 'Forced response')
89
- controller.present.should == 'Forced response'
90
- end
91
-
92
- it "renders and returns the default template as a rack response" do
93
- controller = Rory::Controller.new(@request)
94
- controller.present.should == [
95
- 200,
96
- {'Content-type' => 'text/html', 'charset' => 'UTF-8'},
97
- ["Let's go content"]
98
- ]
99
- end
100
-
101
- it "returns previously set @body as a rack response" do
102
- controller = Rory::Controller.new(@request)
103
- controller.instance_variable_set(:@body, 'Forced body')
104
- controller.should_receive(:render).never
105
- controller.present.should == [
106
- 200,
107
- {'Content-type' => 'text/html', 'charset' => 'UTF-8'},
108
- ["Forced body"]
109
- ]
110
- end
111
- end
112
-
113
- describe '#view_path' do
114
- it 'returns path to template from context root' do
115
- fake_context = double('Context', :root => 'marbles')
116
- controller = Rory::Controller.new(@request, fake_context)
117
- controller.view_path('goose').should == File.expand_path(File.join('views', 'goose.html.erb'), 'marbles')
118
- end
119
-
120
- it 'uses Rory.root if no context' do
121
- controller = Rory::Controller.new(@request)
122
- controller.view_path('goose').should == File.join(Rory.root, 'views', 'goose.html.erb')
123
- end
124
- end
125
- end