volt 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -2
- data/Readme.md +97 -56
- data/VERSION +1 -1
- data/app/volt/assets/js/sockjs-0.3.4.min.js +27 -0
- data/app/volt/assets/js/vertxbus.js +216 -0
- data/app/volt/tasks/live_query/live_query.rb +5 -5
- data/app/volt/tasks/live_query/live_query_pool.rb +1 -1
- data/app/volt/tasks/query_tasks.rb +5 -0
- data/app/volt/tasks/store_tasks.rb +44 -18
- data/docs/WHY.md +10 -0
- data/lib/volt/cli.rb +18 -7
- data/lib/volt/controllers/model_controller.rb +30 -8
- data/lib/volt/extra_core/inflections.rb +63 -0
- data/lib/volt/extra_core/inflector/inflections.rb +203 -0
- data/lib/volt/extra_core/inflector/methods.rb +63 -0
- data/lib/volt/extra_core/inflector.rb +4 -0
- data/lib/volt/extra_core/object.rb +9 -0
- data/lib/volt/extra_core/string.rb +10 -14
- data/lib/volt/models/array_model.rb +45 -27
- data/lib/volt/models/cursor.rb +6 -0
- data/lib/volt/models/model.rb +127 -12
- data/lib/volt/models/model_hash_behaviour.rb +8 -5
- data/lib/volt/models/model_helpers.rb +4 -4
- data/lib/volt/models/model_state.rb +22 -0
- data/lib/volt/models/persistors/array_store.rb +49 -35
- data/lib/volt/models/persistors/base.rb +3 -3
- data/lib/volt/models/persistors/model_store.rb +17 -6
- data/lib/volt/models/persistors/query/query_listener.rb +0 -2
- data/lib/volt/models/persistors/store.rb +0 -4
- data/lib/volt/models/persistors/store_state.rb +27 -0
- data/lib/volt/models/url.rb +2 -2
- data/lib/volt/models/validations/errors.rb +0 -0
- data/lib/volt/models/validations/length.rb +13 -0
- data/lib/volt/models/validations/validations.rb +82 -0
- data/lib/volt/models.rb +1 -1
- data/lib/volt/page/bindings/attribute_binding.rb +29 -14
- data/lib/volt/page/bindings/base_binding.rb +2 -2
- data/lib/volt/page/bindings/component_binding.rb +29 -25
- data/lib/volt/page/bindings/content_binding.rb +1 -0
- data/lib/volt/page/bindings/each_binding.rb +25 -33
- data/lib/volt/page/bindings/event_binding.rb +0 -1
- data/lib/volt/page/bindings/if_binding.rb +3 -1
- data/lib/volt/page/bindings/template_binding.rb +61 -28
- data/lib/volt/page/document_events.rb +3 -1
- data/lib/volt/page/draw_cycle.rb +22 -0
- data/lib/volt/page/page.rb +10 -1
- data/lib/volt/page/reactive_template.rb +23 -16
- data/lib/volt/page/sub_context.rb +1 -1
- data/lib/volt/page/targets/attribute_section.rb +3 -2
- data/lib/volt/page/targets/attribute_target.rb +0 -4
- data/lib/volt/page/targets/base_section.rb +25 -0
- data/lib/volt/page/targets/binding_document/component_node.rb +13 -14
- data/lib/volt/page/targets/binding_document/html_node.rb +4 -0
- data/lib/volt/page/targets/dom_section.rb +16 -67
- data/lib/volt/page/targets/dom_template.rb +99 -0
- data/lib/volt/page/targets/helpers/comment_searchers.rb +29 -0
- data/lib/volt/page/template_renderer.rb +2 -14
- data/lib/volt/reactive/array_extensions.rb +0 -1
- data/lib/volt/reactive/event_chain.rb +9 -2
- data/lib/volt/reactive/events.rb +44 -37
- data/lib/volt/reactive/object_tracking.rb +1 -1
- data/lib/volt/reactive/reactive_array.rb +18 -0
- data/lib/volt/reactive/reactive_count.rb +108 -0
- data/lib/volt/reactive/reactive_generator.rb +44 -0
- data/lib/volt/reactive/reactive_value.rb +73 -73
- data/lib/volt/reactive/string_extensions.rb +1 -1
- data/lib/volt/router/routes.rb +205 -88
- data/lib/volt/server/component_handler.rb +3 -1
- data/lib/volt/server/html_parser/view_parser.rb +20 -4
- data/lib/volt/server/rack/component_paths.rb +13 -10
- data/lib/volt/server/rack/index_files.rb +4 -4
- data/lib/volt/server/socket_connection_handler.rb +5 -1
- data/lib/volt/server.rb +10 -3
- data/spec/apps/kitchen_sink/.gitignore +8 -0
- data/spec/apps/kitchen_sink/Gemfile +32 -0
- data/spec/apps/kitchen_sink/app/home/views/index/index.html +3 -5
- data/spec/apps/kitchen_sink/config.ru +4 -0
- data/spec/apps/kitchen_sink/public/index.html +2 -2
- data/spec/extra_core/inflector_spec.rb +8 -0
- data/spec/models/event_chain_spec.rb +18 -0
- data/spec/models/model_buffers_spec.rb +9 -0
- data/spec/models/model_spec.rb +22 -9
- data/spec/models/reactive_array_spec.rb +26 -1
- data/spec/models/reactive_call_times_spec.rb +28 -0
- data/spec/models/reactive_value_spec.rb +19 -0
- data/spec/models/validations_spec.rb +39 -0
- data/spec/page/bindings/content_binding_spec.rb +1 -0
- data/spec/{templates → page/bindings}/template_binding_spec.rb +54 -0
- data/spec/router/routes_spec.rb +156 -8
- data/spec/server/html_parser/sandlebars_parser_spec.rb +55 -47
- data/spec/server/html_parser/view_parser_spec.rb +3 -0
- data/spec/server/rack/asset_files_spec.rb +1 -1
- data/spec/spec_helper.rb +25 -11
- data/spec/templates/targets/binding_document/component_node_spec.rb +12 -0
- data/templates/project/Gemfile.tt +11 -0
- data/templates/project/app/home/config/routes.rb +1 -1
- data/templates/project/app/home/controllers/index_controller.rb +5 -5
- data/templates/project/app/home/views/index/index.html +6 -6
- data/volt.gemspec +5 -6
- metadata +34 -76
- data/app/volt/assets/js/sockjs-0.2.1.min.js +0 -27
- data/lib/volt/reactive/object_tracker.rb +0 -107
@@ -8,7 +8,8 @@ class ComponentPaths
|
|
8
8
|
def app_folders
|
9
9
|
# Find all app folders
|
10
10
|
@app_folders ||= begin
|
11
|
-
|
11
|
+
volt_app = File.expand_path(File.join(File.dirname(__FILE__), "../../../../app"))
|
12
|
+
app_folders = [volt_app, "#{@root}/app", "#{@root}/vendor/app"].map {|f| File.expand_path(f) }
|
12
13
|
|
13
14
|
# Gem folders with volt in them
|
14
15
|
# TODO: we should probably qualify this a bit more
|
@@ -49,15 +50,17 @@ class ComponentPaths
|
|
49
50
|
|
50
51
|
# Makes each components classes available on the load path, require classes.
|
51
52
|
def require_in_components
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
53
|
+
if RUBY_PLATFORM == 'opal'
|
54
|
+
else
|
55
|
+
app_folders do |app_folder|
|
56
|
+
$LOAD_PATH.unshift(app_folder)
|
57
|
+
|
58
|
+
Dir["#{app_folder}/*/{controllers,models}/*.rb"].each do |ruby_file|
|
59
|
+
path = ruby_file.gsub(/^#{app_folder}\//, '')[0..-4]
|
60
|
+
require(path)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
61
64
|
|
62
65
|
# add each tasks folder directly
|
63
66
|
components.each do |name,component_folders|
|
@@ -17,16 +17,16 @@ class IndexFiles
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def route_match?(path)
|
20
|
-
@@router.
|
21
|
-
|
22
|
-
|
20
|
+
params = @@router.url_to_params(path)
|
21
|
+
|
22
|
+
return params if params
|
23
23
|
|
24
24
|
return false
|
25
25
|
end
|
26
26
|
|
27
27
|
def call(env)
|
28
28
|
if route_match?(env['PATH_INFO'])
|
29
|
-
[200, { 'Content-Type' => 'text/html' }, [html]]
|
29
|
+
[200, { 'Content-Type' => 'text/html; charset=utf-8' }, [html]]
|
30
30
|
else
|
31
31
|
@app.call env
|
32
32
|
end
|
@@ -43,7 +43,11 @@ class SocketConnectionHandler < SockJS::Session
|
|
43
43
|
def send_message(*args)
|
44
44
|
str = JSON.dump([*args])
|
45
45
|
|
46
|
-
|
46
|
+
begin
|
47
|
+
send(str)
|
48
|
+
rescue MetaState::WrongStateError => e
|
49
|
+
puts "Tried to send to closed connection: #{e.inspect}"
|
50
|
+
end
|
47
51
|
end
|
48
52
|
|
49
53
|
def closed
|
data/lib/volt/server.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
ENV['SERVER'] = 'true'
|
2
2
|
|
3
3
|
require 'opal'
|
4
|
-
|
4
|
+
if RUBY_PLATFORM == 'java'
|
5
|
+
require 'jubilee'
|
6
|
+
else
|
7
|
+
require 'thin'
|
8
|
+
end
|
9
|
+
|
5
10
|
require "rack"
|
6
11
|
if RUBY_PLATFORM != 'java'
|
7
12
|
require "rack/sockjs"
|
@@ -20,6 +25,7 @@ require 'volt/server/rack/component_paths'
|
|
20
25
|
require 'volt/server/rack/index_files'
|
21
26
|
require 'volt/server/rack/opal_files'
|
22
27
|
require 'volt/tasks/dispatcher'
|
28
|
+
require 'volt/page/page'
|
23
29
|
|
24
30
|
module Rack
|
25
31
|
# TODO: For some reason in Rack (or maybe thin), 304 headers close
|
@@ -96,9 +102,10 @@ class Server
|
|
96
102
|
# which JS/CSS files to serve.
|
97
103
|
@app.use IndexFiles, @component_paths, opal_files
|
98
104
|
|
105
|
+
component_paths.require_in_components
|
106
|
+
|
99
107
|
# Handle socks js connection
|
100
108
|
if RUBY_PLATFORM != 'java'
|
101
|
-
component_paths.require_in_components
|
102
109
|
SocketConnectionHandler.dispatcher = Dispatcher.new
|
103
110
|
|
104
111
|
@app.map "/channel" do
|
@@ -114,7 +121,7 @@ class Server
|
|
114
121
|
[:all, {'Cache-Control' => 'public, max-age=86400'}]
|
115
122
|
]
|
116
123
|
|
117
|
-
@app.run lambda{ |env| [ 404, { 'Content-Type' => 'text/html' }, ['404 - page not found'] ] }
|
124
|
+
@app.run lambda{ |env| [ 404, { 'Content-Type' => 'text/html; charset=utf-8' }, ['404 - page not found'] ] }
|
118
125
|
|
119
126
|
return @app
|
120
127
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'volt', path: '/Users/ryanstout/Sites/volt/volt'
|
4
|
+
|
5
|
+
|
6
|
+
# The following gem's are optional for themeing
|
7
|
+
|
8
|
+
# Twitter bootstrap
|
9
|
+
gem 'volt-bootstrap'
|
10
|
+
|
11
|
+
# Simple theme for bootstrap, remove to theme yourself.
|
12
|
+
gem 'volt-bootstrap-jumbotron-theme'
|
13
|
+
|
14
|
+
|
15
|
+
gem 'volt-fields', path: '/Users/ryanstout/Sites/volt/apps/volt-fields'
|
16
|
+
|
17
|
+
# Server for MRI
|
18
|
+
platform :mri do
|
19
|
+
gem 'thin', '~> 1.6.0'
|
20
|
+
gem 'bson_ext'
|
21
|
+
end
|
22
|
+
|
23
|
+
# Server for jruby
|
24
|
+
platform :jruby do
|
25
|
+
gem 'jubilee'
|
26
|
+
end
|
27
|
+
|
28
|
+
#---------------------
|
29
|
+
# Needed at the moment
|
30
|
+
gem 'opal', git: 'https://github.com/opal/opal.git'
|
31
|
+
gem 'opal-jquery', :git => 'https://github.com/opal/opal-jquery.git'
|
32
|
+
gem 'sockjs', git: 'https://github.com/kacperk/sockjs-ruby.git', require: false, platforms: :mri
|
@@ -128,4 +128,22 @@ describe EventChain do
|
|
128
128
|
expect(count).to eq(3)
|
129
129
|
end
|
130
130
|
end
|
131
|
+
|
132
|
+
it "should free events when events are unbound" do
|
133
|
+
a = ReactiveValue.new('cool')
|
134
|
+
b = a + 'dude'
|
135
|
+
|
136
|
+
expect(a.reactive_manager.event_chain.instance_variable_get('@event_counts')).to eq({})
|
137
|
+
|
138
|
+
listener1 = b.on('changed') { }
|
139
|
+
expect(a.reactive_manager.event_chain.instance_variable_get('@event_counts')).to_not eq({})
|
140
|
+
|
141
|
+
listener2 = b.on('changed') { }
|
142
|
+
listener1.remove
|
143
|
+
|
144
|
+
expect(a.reactive_manager.event_chain.instance_variable_get('@event_counts')).to_not eq({})
|
145
|
+
listener2.remove
|
146
|
+
|
147
|
+
expect(a.reactive_manager.event_chain.instance_variable_get('@event_counts')).to eq({})
|
148
|
+
end
|
131
149
|
end
|
data/spec/models/model_spec.rb
CHANGED
@@ -80,15 +80,20 @@ describe Model do
|
|
80
80
|
it "should not call changed on other attributes" do
|
81
81
|
a = ReactiveValue.new(Model.new)
|
82
82
|
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
blue_count = 0
|
84
|
+
green_count = 0
|
85
|
+
a._blue.on('changed') { blue_count += 1 }
|
86
|
+
a._green.on('changed') { green_count += 1}
|
87
|
+
expect(blue_count).to eq(0)
|
88
|
+
expect(green_count).to eq(0)
|
86
89
|
|
87
90
|
a._green = 'one'
|
88
|
-
expect(
|
91
|
+
expect(blue_count).to eq(0)
|
92
|
+
expect(green_count).to eq(1)
|
89
93
|
|
90
94
|
a._blue = 'two'
|
91
|
-
expect(
|
95
|
+
expect(blue_count).to eq(1)
|
96
|
+
expect(green_count).to eq(1)
|
92
97
|
|
93
98
|
end
|
94
99
|
|
@@ -110,6 +115,12 @@ describe Model do
|
|
110
115
|
end
|
111
116
|
|
112
117
|
it "should store reactive values in arrays and trigger updates when those values change" do
|
118
|
+
a = ReactiveValue.new(Model.new)
|
119
|
+
b = ReactiveValue.new('blue')
|
120
|
+
a._items << b
|
121
|
+
b.cur = 'two'
|
122
|
+
|
123
|
+
|
113
124
|
a = ReactiveValue.new(Model.new)
|
114
125
|
b = ReactiveValue.new('blue')
|
115
126
|
a._one = 1
|
@@ -241,10 +252,12 @@ describe Model do
|
|
241
252
|
expect(count).to eq(0)
|
242
253
|
|
243
254
|
a._blue._green = 5
|
244
|
-
expect(count).to eq(1)
|
245
255
|
|
246
|
-
|
256
|
+
# TODO: Should actually just equal one
|
247
257
|
expect(count).to eq(2)
|
258
|
+
|
259
|
+
a._blue = 22
|
260
|
+
expect(count).to eq(3)
|
248
261
|
end
|
249
262
|
|
250
263
|
it "should trigger changed when a value is deleted" do
|
@@ -407,11 +420,11 @@ describe Model do
|
|
407
420
|
c.on('changed') { count2 += 1 }
|
408
421
|
expect(count1).to eq(0)
|
409
422
|
|
410
|
-
|
423
|
+
a._complete = true
|
411
424
|
expect(count1).to eq(1)
|
412
425
|
expect(count2).to eq(1)
|
413
426
|
|
414
|
-
|
427
|
+
a._complete = false
|
415
428
|
expect(count1).to eq(2)
|
416
429
|
expect(count2).to eq(2)
|
417
430
|
|
@@ -252,7 +252,6 @@ describe ReactiveArray do
|
|
252
252
|
model._current.on('changed') { count += 1 }
|
253
253
|
expect(count).to eq(0)
|
254
254
|
|
255
|
-
ObjectTracker.process_queue
|
256
255
|
model._items.delete_at(0)
|
257
256
|
|
258
257
|
expect(count).to eq(1)
|
@@ -301,5 +300,31 @@ describe ReactiveArray do
|
|
301
300
|
|
302
301
|
|
303
302
|
end
|
303
|
+
|
304
|
+
it "should trigger changed with a negative index assignment" do
|
305
|
+
a = ReactiveValue.new(ReactiveArray.new([1,2,3]))
|
306
|
+
|
307
|
+
count_0 = 0
|
308
|
+
count_1 = 0
|
309
|
+
|
310
|
+
a[0].on('changed') { count_0 += 1 }
|
311
|
+
a[1].on('changed') { count_1 += 1 }
|
312
|
+
|
313
|
+
a[-2] = 50
|
314
|
+
|
315
|
+
expect(count_0).to eq(0)
|
316
|
+
expect(count_1).to eq(1)
|
317
|
+
end
|
318
|
+
|
319
|
+
it "should not trigger on other indicies" do
|
320
|
+
a = ReactiveValue.new(ReactiveArray.new([1,2,3]))
|
321
|
+
|
322
|
+
count = 0
|
323
|
+
a[0].on('changed') { count += 1 }
|
324
|
+
expect(count).to eq(0)
|
325
|
+
|
326
|
+
a[1] = 5
|
327
|
+
expect(count).to eq(0)
|
328
|
+
end
|
304
329
|
end
|
305
330
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# require 'volt/models'
|
2
|
+
#
|
3
|
+
# class Test1 < Model
|
4
|
+
# def test
|
5
|
+
# puts "YEP"
|
6
|
+
#
|
7
|
+
# {:yes => true}
|
8
|
+
# end
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# describe ReactiveValue do
|
12
|
+
# it "should not trigger a model method multiple times" do
|
13
|
+
# a = ReactiveValue.new(Test1.new)
|
14
|
+
#
|
15
|
+
# test = a.test
|
16
|
+
#
|
17
|
+
# test.on('changed') { puts "CH" }
|
18
|
+
# a._name.on('changed') { puts "NAME CH" }
|
19
|
+
#
|
20
|
+
# puts "--------"
|
21
|
+
# a._name = 'ok'
|
22
|
+
#
|
23
|
+
#
|
24
|
+
#
|
25
|
+
#
|
26
|
+
# # a.test
|
27
|
+
# end
|
28
|
+
# end
|
@@ -350,4 +350,23 @@ describe ReactiveValue do
|
|
350
350
|
|
351
351
|
expect(a.deep_cur).to eq({_names: ['bob', 'jim']})
|
352
352
|
end
|
353
|
+
|
354
|
+
it "should remove any event bindings bound through a reactive value when the value changes" do
|
355
|
+
a = ReactiveValue.new(Model.new)
|
356
|
+
|
357
|
+
a._info = {_name: 'Test'}
|
358
|
+
info = a._info.cur
|
359
|
+
|
360
|
+
expect(info.listeners.size).to eq(0)
|
361
|
+
|
362
|
+
listener = a._info.on('changed') { }
|
363
|
+
|
364
|
+
expect(info.listeners.size).to eq(1)
|
365
|
+
|
366
|
+
# Listener should be moved to the new object
|
367
|
+
a._info = {}
|
368
|
+
|
369
|
+
expect(info.listeners.size).to eq(0)
|
370
|
+
end
|
371
|
+
|
353
372
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'volt/models'
|
2
|
+
|
3
|
+
class TestModel < Model
|
4
|
+
validate :_name, length: 4
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Model do
|
8
|
+
it "should validate the name" do
|
9
|
+
expect(TestModel.new.errors).to eq({:_name => ["must be at least 4 chars"]})
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should show marked validations once they are marked" do
|
13
|
+
model = TestModel.new
|
14
|
+
|
15
|
+
expect(model.marked_errors).to eq({})
|
16
|
+
|
17
|
+
model.mark_field!(:_name)
|
18
|
+
|
19
|
+
expect(model.marked_errors).to eq(
|
20
|
+
{
|
21
|
+
:_name => ["must be at least 4 chars"]
|
22
|
+
}
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should show all fields in marked errors once saved" do
|
27
|
+
model = TestModel.new
|
28
|
+
|
29
|
+
expect(model.marked_errors).to eq({})
|
30
|
+
|
31
|
+
model.save!
|
32
|
+
|
33
|
+
expect(model.marked_errors).to eq(
|
34
|
+
{
|
35
|
+
:_name => ["must be at least 4 chars"]
|
36
|
+
}
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'volt/page/bindings/template_binding'
|
2
3
|
|
3
4
|
# Setup page stub
|
@@ -26,6 +27,59 @@ describe TemplateBinding do
|
|
26
27
|
$page = nil
|
27
28
|
end
|
28
29
|
|
30
|
+
it "should lookup nested controller action" do
|
31
|
+
@templates = {
|
32
|
+
'home/index/blog/nav' => '',
|
33
|
+
'home/comments/new/body' => '',
|
34
|
+
}
|
35
|
+
|
36
|
+
result = @template_binding.path_for_template('comments/new').last
|
37
|
+
expect(result).to eq(['home', 'comments_controller', 'new'])
|
38
|
+
end
|
39
|
+
|
40
|
+
it "it should not look in the local component/controller for a specified controller/action" do
|
41
|
+
@templates = {
|
42
|
+
'home/comments/new/body' => ''
|
43
|
+
}
|
44
|
+
|
45
|
+
path, result = @template_binding.path_for_template('comments/new')
|
46
|
+
expect(path).to eq('home/comments/new/body')
|
47
|
+
expect(result).to eq(['home', 'comments_controller', 'new'])
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
it "should handle a tripple lookup" do
|
52
|
+
@templates = {
|
53
|
+
'home/comments/new/errors' => '',
|
54
|
+
'comments/new/errors/body' => ''
|
55
|
+
}
|
56
|
+
|
57
|
+
path, result = @template_binding.path_for_template('comments/new/errors')
|
58
|
+
expect(path).to eq('home/comments/new/errors')
|
59
|
+
expect(result).to eq(nil)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should handle a tripple lookup to controllers" do
|
63
|
+
@templates = {
|
64
|
+
'comments/new/errors/body' => ''
|
65
|
+
}
|
66
|
+
|
67
|
+
path, result = @template_binding.path_for_template('comments/new/errors')
|
68
|
+
expect(path).to eq('comments/new/errors/body')
|
69
|
+
expect(result).to eq(['comments', 'new_controller', 'errors'])
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
it "should find a matching component" do
|
74
|
+
@templates = {
|
75
|
+
'comments/new/index/body' => ''
|
76
|
+
}
|
77
|
+
|
78
|
+
path, result = @template_binding.path_for_template('comments/new')
|
79
|
+
expect(path).to eq('comments/new/index/body')
|
80
|
+
expect(result).to eq(['comments', 'new_controller', 'index'])
|
81
|
+
end
|
82
|
+
|
29
83
|
it "should lookup sub-templates within its own file" do
|
30
84
|
@templates = {
|
31
85
|
'home/index/blog/nav' => '',
|