user_notification 0.0.1
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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1 -0
- data/Rakefile +18 -0
- data/lib/generators/user_notification/migration/migration_generator.rb +17 -0
- data/lib/generators/user_notification/migration/templates/migration.rb +24 -0
- data/lib/generators/user_notification/notification/notification_generator.rb +17 -0
- data/lib/generators/user_notification/notification/templates/notification.rb +3 -0
- data/lib/generators/user_notification.rb +14 -0
- data/lib/user_notification/actions/creation.rb +15 -0
- data/lib/user_notification/actions/destruction.rb +15 -0
- data/lib/user_notification/actions/update.rb +15 -0
- data/lib/user_notification/activity.rb +6 -0
- data/lib/user_notification/common.rb +342 -0
- data/lib/user_notification/config.rb +63 -0
- data/lib/user_notification/models/activist.rb +9 -0
- data/lib/user_notification/models/adapter.rb +5 -0
- data/lib/user_notification/models/notification.rb +13 -0
- data/lib/user_notification/models/trackable.rb +9 -0
- data/lib/user_notification/orm/active_record/activist.rb +48 -0
- data/lib/user_notification/orm/active_record/adapter.rb +16 -0
- data/lib/user_notification/orm/active_record/notification.rb +24 -0
- data/lib/user_notification/orm/active_record/trackable.rb +15 -0
- data/lib/user_notification/orm/active_record.rb +5 -0
- data/lib/user_notification/orm/mongo_mapper/activist.rb +46 -0
- data/lib/user_notification/orm/mongo_mapper/adapter.rb +12 -0
- data/lib/user_notification/orm/mongo_mapper/notification.rb +33 -0
- data/lib/user_notification/orm/mongo_mapper/trackable.rb +11 -0
- data/lib/user_notification/orm/mongo_mapper.rb +4 -0
- data/lib/user_notification/orm/mongoid/activist.rb +45 -0
- data/lib/user_notification/orm/mongoid/adapter.rb +12 -0
- data/lib/user_notification/orm/mongoid/notification.rb +26 -0
- data/lib/user_notification/orm/mongoid/trackable.rb +11 -0
- data/lib/user_notification/orm/mongoid.rb +4 -0
- data/lib/user_notification/renderable.rb +118 -0
- data/lib/user_notification/roles/deactivatable.rb +42 -0
- data/lib/user_notification/roles/tracked.rb +183 -0
- data/lib/user_notification/utility/store_controller.rb +37 -0
- data/lib/user_notification/utility/view_helpers.rb +26 -0
- data/lib/user_notification/version.rb +4 -0
- data/lib/user_notification.rb +68 -0
- data/test/migrations/001_create_notifications.rb +24 -0
- data/test/migrations/002_create_articles.rb +11 -0
- data/test/migrations/003_create_users.rb +8 -0
- data/test/migrations/004_add_nonstandard_to_notifications.rb +7 -0
- data/test/mongo_mapper.yml +4 -0
- data/test/mongoid.yml +6 -0
- data/test/test_activist.rb +56 -0
- data/test/test_common.rb +168 -0
- data/test/test_controller_integration.rb +41 -0
- data/test/test_generators.rb +30 -0
- data/test/test_helper.rb +124 -0
- data/test/test_notification.rb +67 -0
- data/test/test_tracking.rb +378 -0
- data/test/test_view_helpers.rb +36 -0
- data/test/views/layouts/_notification.erb +1 -0
- data/test/views/user_notification/_test.erb +8 -0
- metadata +260 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe 'UserNotification::Notification Rendering' do
|
4
|
+
describe '#text' do
|
5
|
+
subject { UserNotification::Notification.new(:key => 'notification.test', :parameters => {:one => 1}) }
|
6
|
+
|
7
|
+
specify '#text uses translations' do
|
8
|
+
subject.save
|
9
|
+
I18n.config.backend.store_translations(:en,
|
10
|
+
{:notification => {:test => '%{one} %{two}'}}
|
11
|
+
)
|
12
|
+
subject.text(:two => 2).must_equal('1 2')
|
13
|
+
subject.parameters.must_equal({:one => 1})
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#render' do
|
18
|
+
subject do
|
19
|
+
s = UserNotification::Notification.new(:key => 'notification.test', :parameters => {:one => 1})
|
20
|
+
s.save && s
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:template_output) { "<strong>1, 2</strong>\n<em>notification.test, #{subject.id}</em>\n" }
|
24
|
+
before { @controller.view_paths << File.expand_path('../views', __FILE__) }
|
25
|
+
|
26
|
+
it 'uses view partials when available' do
|
27
|
+
UserNotification.set_controller(Struct.new(:current_user).new('fake'))
|
28
|
+
subject.render(self, :two => 2)
|
29
|
+
rendered.must_equal template_output + "fake\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'uses requested partial'
|
33
|
+
|
34
|
+
it 'uses view partials without controller' do
|
35
|
+
UserNotification.set_controller(nil)
|
36
|
+
subject.render(self, :two => 2)
|
37
|
+
rendered.must_equal template_output + "\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'provides local variables' do
|
41
|
+
UserNotification.set_controller(nil)
|
42
|
+
subject.render(self, locals: {two: 2})
|
43
|
+
rendered.chomp.must_equal "2"
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'uses translations only when requested' do
|
47
|
+
I18n.config.backend.store_translations(:en,
|
48
|
+
{:notification => {:test => '%{one} %{two}'}}
|
49
|
+
)
|
50
|
+
@controller.view_paths.paths.clear
|
51
|
+
subject.render(self, two: 2, display: :i18n)
|
52
|
+
rendered.must_equal '1 2'
|
53
|
+
end
|
54
|
+
|
55
|
+
it "uses specified layout" do
|
56
|
+
UserNotification.set_controller(nil)
|
57
|
+
subject.render(self, :layout => "notification")
|
58
|
+
rendered.must_include "Here be the layouts"
|
59
|
+
|
60
|
+
subject.render(self, :layout => "layouts/notification")
|
61
|
+
rendered.must_include "Here be the layouts"
|
62
|
+
|
63
|
+
subject.render(self, :layout => :notification)
|
64
|
+
rendered.must_include "Here be the layouts"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,378 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe UserNotification::Tracked do
|
4
|
+
describe 'defining instance options' do
|
5
|
+
subject { article.new }
|
6
|
+
let :options do
|
7
|
+
{ :key => 'key',
|
8
|
+
:params => {:a => 1},
|
9
|
+
:owner => User.create,
|
10
|
+
:recipient => User.create }
|
11
|
+
end
|
12
|
+
before(:each) { subject.notification(options) }
|
13
|
+
let(:notification){ subject.save; subject.notifications.last }
|
14
|
+
|
15
|
+
specify { subject.notification_key.must_be_same_as options[:key] }
|
16
|
+
specify { notification.key.must_equal options[:key] }
|
17
|
+
|
18
|
+
specify { subject.notification_owner.must_be_same_as options[:owner] }
|
19
|
+
specify { notification.owner.must_equal options[:owner] }
|
20
|
+
|
21
|
+
specify { subject.notification_params.must_be_same_as options[:params] }
|
22
|
+
specify { notification.parameters.must_equal options[:params] }
|
23
|
+
|
24
|
+
specify { subject.notification_recipient.must_be_same_as options[:recipient] }
|
25
|
+
specify { notification.recipient.must_equal options[:recipient] }
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'can be tracked and be an activist at the same time' do
|
29
|
+
case UserNotification.config.orm
|
30
|
+
when :mongoid
|
31
|
+
class ActivistAndTrackedArticle
|
32
|
+
include Mongoid::Document
|
33
|
+
include Mongoid::Timestamps
|
34
|
+
include UserNotification::Model
|
35
|
+
|
36
|
+
belongs_to :user
|
37
|
+
|
38
|
+
field :name, type: String
|
39
|
+
field :published, type: Boolean
|
40
|
+
tracked
|
41
|
+
activist
|
42
|
+
end
|
43
|
+
when :mongo_mapper
|
44
|
+
class ActivistAndTrackedArticle
|
45
|
+
include MongoMapper::Document
|
46
|
+
include UserNotification::Model
|
47
|
+
|
48
|
+
belongs_to :user
|
49
|
+
|
50
|
+
key :name, String
|
51
|
+
key :published, Boolean
|
52
|
+
tracked
|
53
|
+
activist
|
54
|
+
timestamps!
|
55
|
+
end
|
56
|
+
when :active_record
|
57
|
+
class ActivistAndTrackedArticle < ActiveRecord::Base
|
58
|
+
self.table_name = 'articles'
|
59
|
+
include UserNotification::Model
|
60
|
+
tracked
|
61
|
+
activist
|
62
|
+
|
63
|
+
if ::ActiveRecord::VERSION::MAJOR < 4
|
64
|
+
attr_accessible :name, :published, :user
|
65
|
+
end
|
66
|
+
belongs_to :user
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
art = ActivistAndTrackedArticle.new
|
71
|
+
art.save
|
72
|
+
art.notifications.last.trackable_id.must_equal art.id
|
73
|
+
art.notifications.last.owner_id.must_equal nil
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'custom fields' do
|
77
|
+
describe 'global' do
|
78
|
+
it 'should resolve symbols' do
|
79
|
+
a = article(nonstandard: :name).new(name: 'Symbol resolved')
|
80
|
+
a.save
|
81
|
+
a.notifications.last.nonstandard.must_equal 'Symbol resolved'
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should resolve procs' do
|
85
|
+
a = article(nonstandard: proc {|_, model| model.name}).new(name: 'Proc resolved')
|
86
|
+
a.save
|
87
|
+
a.notifications.last.nonstandard.must_equal 'Proc resolved'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'instance' do
|
92
|
+
it 'should resolve symbols' do
|
93
|
+
a = article.new(name: 'Symbol resolved')
|
94
|
+
a.notification nonstandard: :name
|
95
|
+
a.save
|
96
|
+
a.notifications.last.nonstandard.must_equal 'Symbol resolved'
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should resolve procs' do
|
100
|
+
a = article.new(name: 'Proc resolved')
|
101
|
+
a.notification nonstandard: proc {|_, model| model.name}
|
102
|
+
a.save
|
103
|
+
a.notifications.last.nonstandard.must_equal 'Proc resolved'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should reset instance options on successful create_notification' do
|
109
|
+
a = article.new
|
110
|
+
a.notification key: 'test', params: {test: 1}
|
111
|
+
a.save
|
112
|
+
a.notifications.count.must_equal 1
|
113
|
+
->{a.create_notification}.must_raise UserNotification::NoKeyProvided
|
114
|
+
a.notification_params.must_be_empty
|
115
|
+
a.notification key: 'asd'
|
116
|
+
a.create_notification
|
117
|
+
->{a.create_notification}.must_raise UserNotification::NoKeyProvided
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should not accept global key option' do
|
121
|
+
# this example tests the lack of presence of sth that should not be here
|
122
|
+
a = article(key: 'asd').new
|
123
|
+
a.save
|
124
|
+
->{a.create_notification}.must_raise UserNotification::NoKeyProvided
|
125
|
+
a.notifications.count.must_equal 1
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should not change global custom fields' do
|
129
|
+
a = article(nonstandard: 'global').new
|
130
|
+
a.notification nonstandard: 'instance'
|
131
|
+
a.save
|
132
|
+
a.class.notification_custom_fields_global.must_equal nonstandard: 'global'
|
133
|
+
end
|
134
|
+
|
135
|
+
describe 'disabling functionality' do
|
136
|
+
it 'allows for global disable' do
|
137
|
+
UserNotification.enabled = false
|
138
|
+
notification_count_before = UserNotification::Notification.count
|
139
|
+
|
140
|
+
@article = article().new
|
141
|
+
@article.save
|
142
|
+
UserNotification::Notification.count.must_equal notification_count_before
|
143
|
+
|
144
|
+
UserNotification.enabled = true
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'allows for class-wide disable' do
|
148
|
+
notification_count_before = UserNotification::Notification.count
|
149
|
+
|
150
|
+
klass = article
|
151
|
+
klass.user_notification_off
|
152
|
+
@article = klass.new
|
153
|
+
@article.save
|
154
|
+
UserNotification::Notification.count.must_equal notification_count_before
|
155
|
+
|
156
|
+
klass.user_notification_on
|
157
|
+
@article.save
|
158
|
+
UserNotification::Notification.count.must_be :>, notification_count_before
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe '#tracked' do
|
163
|
+
subject { article(options) }
|
164
|
+
let(:options) { {} }
|
165
|
+
|
166
|
+
it 'allows skipping the tracking on CRUD actions' do
|
167
|
+
case UserNotification.config.orm
|
168
|
+
when :mongoid
|
169
|
+
art = Class.new do
|
170
|
+
include Mongoid::Document
|
171
|
+
include Mongoid::Timestamps
|
172
|
+
include UserNotification::Model
|
173
|
+
|
174
|
+
belongs_to :user
|
175
|
+
|
176
|
+
field :name, type: String
|
177
|
+
field :published, type: Boolean
|
178
|
+
tracked :skip_defaults => true
|
179
|
+
end
|
180
|
+
when :mongo_mapper
|
181
|
+
art = Class.new do
|
182
|
+
include MongoMapper::Document
|
183
|
+
include UserNotification::Model
|
184
|
+
|
185
|
+
belongs_to :user
|
186
|
+
|
187
|
+
key :name, String
|
188
|
+
key :published, Boolean
|
189
|
+
tracked :skip_defaults => true
|
190
|
+
|
191
|
+
timestamps!
|
192
|
+
end
|
193
|
+
when :active_record
|
194
|
+
art = article(:skip_defaults => true)
|
195
|
+
end
|
196
|
+
|
197
|
+
art.must_include UserNotification::Common
|
198
|
+
art.wont_include UserNotification::Creation
|
199
|
+
art.wont_include UserNotification::Update
|
200
|
+
art.wont_include UserNotification::Destruction
|
201
|
+
end
|
202
|
+
|
203
|
+
describe 'default options' do
|
204
|
+
subject { article }
|
205
|
+
|
206
|
+
specify { subject.must_include UserNotification::Creation }
|
207
|
+
specify { subject.must_include UserNotification::Destruction }
|
208
|
+
specify { subject.must_include UserNotification::Update }
|
209
|
+
|
210
|
+
specify { subject._create_callbacks.select do |c|
|
211
|
+
c.kind.eql?(:after) && c.filter == :notification_on_create
|
212
|
+
end.wont_be_empty }
|
213
|
+
|
214
|
+
specify { subject._update_callbacks.select do |c|
|
215
|
+
c.kind.eql?(:after) && c.filter == :notification_on_update
|
216
|
+
end.wont_be_empty }
|
217
|
+
|
218
|
+
specify { subject._destroy_callbacks.select do |c|
|
219
|
+
c.kind.eql?(:before) && c.filter == :notification_on_destroy
|
220
|
+
end.wont_be_empty }
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'accepts :except option' do
|
224
|
+
case UserNotification.config.orm
|
225
|
+
when :mongoid
|
226
|
+
art = Class.new do
|
227
|
+
include Mongoid::Document
|
228
|
+
include Mongoid::Timestamps
|
229
|
+
include UserNotification::Model
|
230
|
+
|
231
|
+
belongs_to :user
|
232
|
+
|
233
|
+
field :name, type: String
|
234
|
+
field :published, type: Boolean
|
235
|
+
tracked :except => [:create]
|
236
|
+
end
|
237
|
+
when :mongo_mapper
|
238
|
+
art = Class.new do
|
239
|
+
include MongoMapper::Document
|
240
|
+
include UserNotification::Model
|
241
|
+
|
242
|
+
belongs_to :user
|
243
|
+
|
244
|
+
key :name, String
|
245
|
+
key :published, Boolean
|
246
|
+
tracked :except => [:create]
|
247
|
+
|
248
|
+
timestamps!
|
249
|
+
end
|
250
|
+
when :active_record
|
251
|
+
art = article(:except => [:create])
|
252
|
+
end
|
253
|
+
|
254
|
+
art.wont_include UserNotification::Creation
|
255
|
+
art.must_include UserNotification::Update
|
256
|
+
art.must_include UserNotification::Destruction
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'accepts :only option' do
|
260
|
+
case UserNotification.config.orm
|
261
|
+
when :mongoid
|
262
|
+
art = Class.new do
|
263
|
+
include Mongoid::Document
|
264
|
+
include Mongoid::Timestamps
|
265
|
+
include UserNotification::Model
|
266
|
+
|
267
|
+
belongs_to :user
|
268
|
+
|
269
|
+
field :name, type: String
|
270
|
+
field :published, type: Boolean
|
271
|
+
|
272
|
+
tracked :only => [:create, :update]
|
273
|
+
end
|
274
|
+
when :mongo_mapper
|
275
|
+
art = Class.new do
|
276
|
+
include MongoMapper::Document
|
277
|
+
include UserNotification::Model
|
278
|
+
|
279
|
+
belongs_to :user
|
280
|
+
|
281
|
+
key :name, String
|
282
|
+
key :published, Boolean
|
283
|
+
|
284
|
+
tracked :only => [:create, :update]
|
285
|
+
end
|
286
|
+
when :active_record
|
287
|
+
art = article({:only => [:create, :update]})
|
288
|
+
end
|
289
|
+
|
290
|
+
art.must_include UserNotification::Creation
|
291
|
+
art.wont_include UserNotification::Destruction
|
292
|
+
art.must_include UserNotification::Update
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'accepts :owner option' do
|
296
|
+
owner = mock('owner')
|
297
|
+
subject.tracked(:owner => owner)
|
298
|
+
subject.notification_owner_global.must_equal owner
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'accepts :params option' do
|
302
|
+
params = {:a => 1}
|
303
|
+
subject.tracked(:params => params)
|
304
|
+
subject.notification_params_global.must_equal params
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'accepts :on option' do
|
308
|
+
on = {:a => lambda{}, :b => proc {}}
|
309
|
+
subject.tracked(:on => on)
|
310
|
+
subject.notification_hooks.must_equal on
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'accepts :on option with string keys' do
|
314
|
+
on = {'a' => lambda {}}
|
315
|
+
subject.tracked(:on => on)
|
316
|
+
subject.notification_hooks.must_equal on.symbolize_keys
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'accepts :on values that are procs' do
|
320
|
+
on = {:unpassable => 1, :proper => lambda {}, :proper_proc => proc {}}
|
321
|
+
subject.tracked(:on => on)
|
322
|
+
subject.notification_hooks.must_include :proper
|
323
|
+
subject.notification_hooks.must_include :proper_proc
|
324
|
+
subject.notification_hooks.wont_include :unpassable
|
325
|
+
end
|
326
|
+
|
327
|
+
describe 'global options' do
|
328
|
+
subject { article(recipient: :test, owner: :test2, params: {a: 'b'}) }
|
329
|
+
|
330
|
+
specify { subject.notification_recipient_global.must_equal :test }
|
331
|
+
specify { subject.notification_owner_global.must_equal :test2 }
|
332
|
+
specify { subject.notification_params_global.must_equal(a: 'b') }
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
describe 'notification hooks' do
|
337
|
+
subject { s = article; s.notification_hooks = {:test => hook}; s }
|
338
|
+
let(:hook) { lambda {} }
|
339
|
+
|
340
|
+
it 'retrieves hooks' do
|
341
|
+
assert_same hook, subject.get_hook(:test)
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'retrieves hooks by string keys' do
|
345
|
+
assert_same hook, subject.get_hook('test')
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'returns nil when no matching hook is present' do
|
349
|
+
nil.must_be_same_as subject.get_hook(:nonexistent)
|
350
|
+
end
|
351
|
+
|
352
|
+
it 'allows hooks to decide if notification should be created' do
|
353
|
+
subject.tracked
|
354
|
+
@article = subject.new(:name => 'Some Name')
|
355
|
+
UserNotification.set_controller(mock('controller'))
|
356
|
+
pf = proc { |model, controller|
|
357
|
+
controller.must_be_same_as UserNotification.get_controller
|
358
|
+
model.name.must_equal 'Some Name'
|
359
|
+
false
|
360
|
+
}
|
361
|
+
pt = proc { |model, controller|
|
362
|
+
controller.must_be_same_as UserNotification.get_controller
|
363
|
+
model.name.must_equal 'Other Name'
|
364
|
+
true # this will save the notification with *.update key
|
365
|
+
}
|
366
|
+
@article.class.notification_hooks = {:create => pf, :update => pt, :destroy => pt}
|
367
|
+
|
368
|
+
@article.notifications.to_a.must_be_empty
|
369
|
+
@article.save # create
|
370
|
+
@article.name = 'Other Name'
|
371
|
+
@article.save # update
|
372
|
+
@article.destroy # destroy
|
373
|
+
|
374
|
+
@article.notifications.count.must_equal 2
|
375
|
+
@article.notifications.first.key.must_equal 'article.update'
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe 'ViewHelpers Rendering' do
|
4
|
+
include UserNotification::ViewHelpers
|
5
|
+
|
6
|
+
# is this a proper test?
|
7
|
+
it 'provides render_notification helper' do
|
8
|
+
notification = mock('notification')
|
9
|
+
notification.stubs(:is_a?).with(UserNotification::Notification).returns(true)
|
10
|
+
notification.expects(:render).with(self, {})
|
11
|
+
render_notification(notification)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'handles multiple notifications' do
|
15
|
+
notification = mock('notification')
|
16
|
+
notification.expects(:render).with(self, {})
|
17
|
+
render_notifications([notification])
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'flushes content_for between partials renderes' do
|
21
|
+
@view_flow = mock('view_flow')
|
22
|
+
@view_flow.expects(:set).twice.with('name', ActiveSupport::SafeBuffer.new)
|
23
|
+
|
24
|
+
single_content_for('name', 'content')
|
25
|
+
@name.must_equal 'name'
|
26
|
+
@content.must_equal 'content'
|
27
|
+
single_content_for('name', 'content2')
|
28
|
+
@name.must_equal 'name'
|
29
|
+
@content.must_equal 'content2'
|
30
|
+
end
|
31
|
+
|
32
|
+
def content_for(name, content, &block)
|
33
|
+
@name = name
|
34
|
+
@content = content
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<h2>Here be the layouts</h2><%= yield %>
|