dry-view 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +18 -0
  3. data/.travis.yml +15 -10
  4. data/.yardopts +5 -0
  5. data/CHANGELOG.md +60 -1
  6. data/Gemfile +15 -5
  7. data/README.md +38 -13
  8. data/bin/setup +5 -0
  9. data/bin/setup_helpers.rb +27 -0
  10. data/dry-view.gemspec +8 -9
  11. data/lib/dry-view.rb +3 -1
  12. data/lib/dry/view.rb +503 -2
  13. data/lib/dry/view/context.rb +80 -0
  14. data/lib/dry/view/decorated_attributes.rb +81 -0
  15. data/lib/dry/view/exposure.rb +15 -2
  16. data/lib/dry/view/exposures.rb +15 -5
  17. data/lib/dry/view/part.rb +154 -61
  18. data/lib/dry/view/part_builder.rb +136 -0
  19. data/lib/dry/view/path.rb +22 -5
  20. data/lib/dry/view/render_environment.rb +62 -0
  21. data/lib/dry/view/render_environment_missing.rb +44 -0
  22. data/lib/dry/view/rendered.rb +55 -0
  23. data/lib/dry/view/renderer.rb +22 -19
  24. data/lib/dry/view/scope.rb +146 -14
  25. data/lib/dry/view/scope_builder.rb +98 -0
  26. data/lib/dry/view/tilt.rb +78 -0
  27. data/lib/dry/view/tilt/erb.rb +26 -0
  28. data/lib/dry/view/tilt/erbse.rb +21 -0
  29. data/lib/dry/view/tilt/haml.rb +26 -0
  30. data/lib/dry/view/version.rb +5 -2
  31. metadata +50 -88
  32. data/benchmarks/templates/button.html.erb +0 -1
  33. data/benchmarks/view.rb +0 -24
  34. data/lib/dry/view/controller.rb +0 -159
  35. data/lib/dry/view/decorator.rb +0 -45
  36. data/lib/dry/view/missing_renderer.rb +0 -15
  37. data/spec/fixtures/templates/_hello.html.slim +0 -1
  38. data/spec/fixtures/templates/controller_renderer_options.html.erb +0 -3
  39. data/spec/fixtures/templates/decorated_parts.html.slim +0 -4
  40. data/spec/fixtures/templates/edit.html.slim +0 -11
  41. data/spec/fixtures/templates/empty.html.slim +0 -1
  42. data/spec/fixtures/templates/greeting.html.slim +0 -2
  43. data/spec/fixtures/templates/hello.html.slim +0 -1
  44. data/spec/fixtures/templates/layouts/app.html.slim +0 -6
  45. data/spec/fixtures/templates/layouts/app.txt.erb +0 -3
  46. data/spec/fixtures/templates/parts_with_args.html.slim +0 -3
  47. data/spec/fixtures/templates/parts_with_args/_box.html.slim +0 -3
  48. data/spec/fixtures/templates/shared/_index_table.html.slim +0 -2
  49. data/spec/fixtures/templates/shared/_shared_hello.html.slim +0 -1
  50. data/spec/fixtures/templates/tasks.html.slim +0 -3
  51. data/spec/fixtures/templates/user.html.slim +0 -2
  52. data/spec/fixtures/templates/users.html.slim +0 -5
  53. data/spec/fixtures/templates/users.txt.erb +0 -3
  54. data/spec/fixtures/templates/users/_row.html.slim +0 -2
  55. data/spec/fixtures/templates/users/_tbody.html.slim +0 -5
  56. data/spec/fixtures/templates/users_with_count.html.slim +0 -5
  57. data/spec/fixtures/templates/users_with_count_inherit.html.slim +0 -6
  58. data/spec/fixtures/templates_override/_hello.html.slim +0 -1
  59. data/spec/fixtures/templates_override/users.html.slim +0 -5
  60. data/spec/integration/decorator_spec.rb +0 -80
  61. data/spec/integration/exposures_spec.rb +0 -392
  62. data/spec/integration/part/decorated_attributes_spec.rb +0 -193
  63. data/spec/integration/view_spec.rb +0 -133
  64. data/spec/spec_helper.rb +0 -46
  65. data/spec/unit/controller_spec.rb +0 -83
  66. data/spec/unit/decorator_spec.rb +0 -61
  67. data/spec/unit/exposure_spec.rb +0 -227
  68. data/spec/unit/exposures_spec.rb +0 -103
  69. data/spec/unit/part_spec.rb +0 -104
  70. data/spec/unit/renderer_spec.rb +0 -57
  71. data/spec/unit/scope_spec.rb +0 -53
@@ -1,392 +0,0 @@
1
- RSpec.describe 'exposures' do
2
- let(:context) { Struct.new(:title, :assets).new('dry-view rocks!', -> input { "#{input}.jpg" }) }
3
-
4
- it 'uses exposures with blocks to build view locals' do
5
- vc = Class.new(Dry::View::Controller) do
6
- configure do |config|
7
- config.paths = SPEC_ROOT.join('fixtures/templates')
8
- config.layout = 'app'
9
- config.template = 'users'
10
- config.default_format = :html
11
- end
12
-
13
- expose :users do |users:|
14
- users.map { |user|
15
- user.merge(name: user[:name].upcase)
16
- }
17
- end
18
- end.new
19
-
20
- users = [
21
- { name: 'Jane', email: 'jane@doe.org' },
22
- { name: 'Joe', email: 'joe@doe.org' }
23
- ]
24
-
25
- expect(vc.(users: users, context: context)).to eql(
26
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><div class="users"><table><tbody><tr><td>JANE</td><td>jane@doe.org</td></tr><tr><td>JOE</td><td>joe@doe.org</td></tr></tbody></table></div><img src="mindblown.jpg" /></body></html>'
27
- )
28
- end
29
-
30
- it 'gives the exposure blocks access to the view controller instance' do
31
- vc = Class.new(Dry::View::Controller) do
32
- configure do |config|
33
- config.paths = SPEC_ROOT.join('fixtures/templates')
34
- config.layout = 'app'
35
- config.template = 'users'
36
- config.default_format = :html
37
- end
38
-
39
- attr_reader :prefix
40
-
41
- def initialize
42
- super
43
- @prefix = "My friend "
44
- end
45
-
46
- expose :users do |users:|
47
- users.map { |user|
48
- user.merge(name: prefix + user[:name])
49
- }
50
- end
51
- end.new
52
-
53
- users = [
54
- { name: 'Jane', email: 'jane@doe.org' },
55
- { name: 'Joe', email: 'joe@doe.org' }
56
- ]
57
-
58
- expect(vc.(users: users, context: context)).to eql(
59
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><div class="users"><table><tbody><tr><td>My friend Jane</td><td>jane@doe.org</td></tr><tr><td>My friend Joe</td><td>joe@doe.org</td></tr></tbody></table></div><img src="mindblown.jpg" /></body></html>'
60
- )
61
- end
62
-
63
- it 'supports instance methods as exposures' do
64
- vc = Class.new(Dry::View::Controller) do
65
- configure do |config|
66
- config.paths = SPEC_ROOT.join('fixtures/templates')
67
- config.layout = 'app'
68
- config.template = 'users'
69
- config.default_format = :html
70
- end
71
-
72
- expose :users
73
-
74
- private
75
-
76
- def users(users:)
77
- users.map { |user|
78
- user.merge(name: user[:name].upcase)
79
- }
80
- end
81
- end.new
82
-
83
- users = [
84
- { name: 'Jane', email: 'jane@doe.org' },
85
- { name: 'Joe', email: 'joe@doe.org' }
86
- ]
87
-
88
- expect(vc.(users: users, context: context)).to eql(
89
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><div class="users"><table><tbody><tr><td>JANE</td><td>jane@doe.org</td></tr><tr><td>JOE</td><td>joe@doe.org</td></tr></tbody></table></div><img src="mindblown.jpg" /></body></html>'
90
- )
91
- end
92
-
93
- it 'passes matching input data if no proc or instance method is available' do
94
- vc = Class.new(Dry::View::Controller) do
95
- configure do |config|
96
- config.paths = SPEC_ROOT.join('fixtures/templates')
97
- config.layout = 'app'
98
- config.template = 'users'
99
- config.default_format = :html
100
- end
101
-
102
- expose :users
103
- end.new
104
-
105
- users = [
106
- { name: 'Jane', email: 'jane@doe.org' },
107
- { name: 'Joe', email: 'joe@doe.org' }
108
- ]
109
-
110
- expect(vc.(users: users, context: context)).to eql(
111
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><div class="users"><table><tbody><tr><td>Jane</td><td>jane@doe.org</td></tr><tr><td>Joe</td><td>joe@doe.org</td></tr></tbody></table></div><img src="mindblown.jpg" /></body></html>'
112
- )
113
- end
114
-
115
- it 'using default values' do
116
- vc = Class.new(Dry::View::Controller) do
117
- configure do |config|
118
- config.paths = SPEC_ROOT.join('fixtures/templates')
119
- config.layout = 'app'
120
- config.template = 'users'
121
- config.default_format = :html
122
- end
123
-
124
- expose :users, default: [{name: 'John', email: 'john@william.org'}]
125
- end.new
126
-
127
- expect(vc.(context: context)).to eql(
128
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><div class="users"><table><tbody><tr><td>John</td><td>john@william.org</td></tr></tbody></table></div><img src="mindblown.jpg" /></body></html>'
129
- )
130
- end
131
-
132
- it 'having default values but passing nil as value for exposure' do
133
- vc = Class.new(Dry::View::Controller) do
134
- configure do |config|
135
- config.paths = SPEC_ROOT.join('fixtures/templates')
136
- config.layout = 'app'
137
- config.template = 'greeting'
138
- config.default_format = :html
139
- end
140
-
141
- expose :greeting, default: 'Hello Dry-rb'
142
- end.new
143
-
144
- expect(vc.(greeting: nil, context: context)).to eql(
145
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><p></p></body></html>'
146
- )
147
- end
148
-
149
- it 'allows exposures to depend on each other' do
150
- vc = Class.new(Dry::View::Controller) do
151
- configure do |config|
152
- config.paths = SPEC_ROOT.join('fixtures/templates')
153
- config.layout = 'app'
154
- config.template = 'users_with_count'
155
- config.default_format = :html
156
- end
157
-
158
- expose :users
159
-
160
- expose :users_count do |users:|
161
- "#{users.length} users"
162
- end
163
- end.new
164
-
165
- users = [
166
- {name: 'Jane', email: 'jane@doe.org'},
167
- {name: 'Joe', email: 'joe@doe.org'}
168
- ]
169
-
170
- expect(vc.(users: users, context: context)).to eql(
171
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ul><li>Jane (jane@doe.org)</li><li>Joe (joe@doe.org)</li></ul><div class="count">2 users</div></body></html>'
172
- )
173
- end
174
-
175
- it 'allows exposures to depend on each other and access keywords args from input' do
176
- vc = Class.new(Dry::View::Controller) do
177
- configure do |config|
178
- config.paths = SPEC_ROOT.join('fixtures/templates')
179
- config.layout = 'app'
180
- config.template = 'greeting'
181
- config.default_format = :html
182
- end
183
-
184
- expose :greeting do |prefix, greeting:|
185
- "#{prefix} #{greeting}"
186
- end
187
-
188
- expose :prefix do
189
- 'Hello'
190
- end
191
- end.new
192
-
193
- expect(vc.(greeting: 'From dry-view internals', context: context)).to eql(
194
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><p>Hello From dry-view internals</p></body></html>'
195
- )
196
- end
197
-
198
- it 'set default values for keyword arguments' do
199
- vc = Class.new(Dry::View::Controller) do
200
- configure do |config|
201
- config.paths = SPEC_ROOT.join('fixtures/templates')
202
- config.layout = 'app'
203
- config.template = 'greeting'
204
- config.default_format = :html
205
- end
206
-
207
- expose :greeting do |prefix, greeting: 'From the defaults'|
208
- "#{prefix} #{greeting}"
209
- end
210
-
211
- expose :prefix do
212
- 'Hello'
213
- end
214
- end.new
215
-
216
- expect(vc.(context: context)).to eql(
217
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><p>Hello From the defaults</p></body></html>'
218
- )
219
- end
220
-
221
- it 'only pass keywords arguments that are needit in the block and allow for default values' do
222
- vc = Class.new(Dry::View::Controller) do
223
- configure do |config|
224
- config.paths = SPEC_ROOT.join('fixtures/templates')
225
- config.layout = 'app'
226
- config.template = 'edit'
227
- config.default_format = :html
228
- end
229
-
230
- expose :pretty_id do |id:|
231
- "Beautiful #{id}"
232
- end
233
-
234
- expose :errors do |errors: []|
235
- errors
236
- end
237
- end.new
238
-
239
- expect(vc.(id: 1, context: context)).to eql(
240
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><h1>Edit</h1><p>No Errors</p><p>Beautiful 1</p></body></html>'
241
- )
242
- end
243
-
244
- it 'supports defining multiple exposures at once' do
245
- vc = Class.new(Dry::View::Controller) do
246
- configure do |config|
247
- config.paths = SPEC_ROOT.join('fixtures/templates')
248
- config.layout = 'app'
249
- config.template = 'users_with_count'
250
- config.default_format = :html
251
- end
252
-
253
- expose :users, :users_count
254
-
255
- private
256
-
257
- def users_count(users:)
258
- "#{users.length} users"
259
- end
260
- end.new
261
-
262
- users = [
263
- {name: 'Jane', email: 'jane@doe.org'},
264
- {name: 'Joe', email: 'joe@doe.org'}
265
- ]
266
-
267
- expect(vc.(users: users, context: context)).to eql(
268
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ul><li>Jane (jane@doe.org)</li><li>Joe (joe@doe.org)</li></ul><div class="count">2 users</div></body></html>'
269
- )
270
- end
271
-
272
- it 'allows exposures to be hidden from the view' do
273
- vc = Class.new(Dry::View::Controller) do
274
- configure do |config|
275
- config.paths = SPEC_ROOT.join('fixtures/templates')
276
- config.layout = 'app'
277
- config.template = 'users_with_count'
278
- config.default_format = :html
279
- end
280
-
281
- private_expose :prefix do
282
- "COUNT: "
283
- end
284
-
285
- expose :users
286
-
287
- expose :users_count do |prefix, users:|
288
- "#{prefix}#{users.length} users"
289
- end
290
- end.new
291
-
292
- users = [
293
- {name: 'Jane', email: 'jane@doe.org'},
294
- {name: 'Joe', email: 'joe@doe.org'}
295
- ]
296
-
297
- input = {users: users, context: context}
298
-
299
- expect(vc.(input)).to eql(
300
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ul><li>Jane (jane@doe.org)</li><li>Joe (joe@doe.org)</li></ul><div class="count">COUNT: 2 users</div></body></html>'
301
- )
302
-
303
- expect(vc.locals(input)).to include(:users, :users_count)
304
- expect(vc.locals(input)).not_to include(:prefix)
305
- end
306
-
307
- it 'inherit exposures from parent class' do
308
- parent = Class.new(Dry::View::Controller) do
309
- configure do |config|
310
- config.paths = SPEC_ROOT.join('fixtures/templates')
311
- config.layout = 'app'
312
- config.template = 'users_with_count_inherit'
313
- config.default_format = :html
314
- end
315
-
316
- private_expose :prefix do
317
- "COUNT: "
318
- end
319
-
320
- expose :users
321
-
322
- expose :users_count do |prefix, users:|
323
- "#{prefix}#{users.length} users"
324
- end
325
- end
326
-
327
- child = Class.new(parent) do
328
- expose :child_expose do
329
- 'Child expose'
330
- end
331
- end.new
332
-
333
- users = [
334
- {name: 'Jane', email: 'jane@doe.org'},
335
- {name: 'Joe', email: 'joe@doe.org'}
336
- ]
337
-
338
- input = {users: users, context: context}
339
-
340
- expect(child.(input)).to eql(
341
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ul><li>Jane (jane@doe.org)</li><li>Joe (joe@doe.org)</li></ul><div class="count">COUNT: 2 users</div><div class="inherit">Child expose</div></body></html>'
342
- )
343
-
344
- expect(child.locals(input)).to include(:users, :users_count, :child_expose)
345
- expect(child.locals(input)).not_to include(:prefix)
346
- end
347
-
348
- it 'inherit exposures from parent class and allow to override them' do
349
- parent = Class.new(Dry::View::Controller) do
350
- configure do |config|
351
- config.paths = SPEC_ROOT.join('fixtures/templates')
352
- config.layout = 'app'
353
- config.template = 'users_with_count_inherit'
354
- config.default_format = :html
355
- end
356
-
357
- private_expose :prefix do
358
- "COUNT: "
359
- end
360
-
361
- expose :users
362
-
363
- expose :users_count do |prefix, users:|
364
- "#{prefix}#{users.length} users"
365
- end
366
- end
367
-
368
- child = Class.new(parent) do
369
- expose :child_expose do
370
- 'Child expose'
371
- end
372
-
373
- expose :users_count do |prefix, users:|
374
- "#{prefix}#{users.length} users overrided"
375
- end
376
- end.new
377
-
378
- users = [
379
- {name: 'Jane', email: 'jane@doe.org'},
380
- {name: 'Joe', email: 'joe@doe.org'}
381
- ]
382
-
383
- input = {users: users, context: context}
384
-
385
- expect(child.(input)).to eql(
386
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ul><li>Jane (jane@doe.org)</li><li>Joe (joe@doe.org)</li></ul><div class="count">COUNT: 2 users overrided</div><div class="inherit">Child expose</div></body></html>'
387
- )
388
-
389
- expect(child.locals(input)).to include(:users, :users_count, :child_expose)
390
- expect(child.locals(input)).not_to include(:prefix)
391
- end
392
- end
@@ -1,193 +0,0 @@
1
- RSpec.describe 'Part / Decorated attributes' do
2
- let(:article_class) {
3
- Class.new do
4
- attr_reader :title, :author, :comments
5
-
6
- def initialize(title:, author:, comments:)
7
- @title = title
8
- @author = author
9
- @comments = comments
10
- end
11
- end
12
- }
13
-
14
- let(:author_class) {
15
- Class.new do
16
- attr_reader :name
17
-
18
- def initialize(name:)
19
- @name = name
20
- end
21
- end
22
- }
23
-
24
- let(:comment_class) {
25
- Class.new do
26
- attr_reader :author, :body
27
-
28
- def initialize(author:, body:)
29
- @author = author
30
- @body = body
31
- end
32
- end
33
- }
34
-
35
- let (:author) {
36
- author_class.new(name: 'Jane Doe')
37
- }
38
-
39
- let(:article) {
40
- article_class.new(
41
- title: 'Hello world',
42
- author: author,
43
- comments: [
44
- comment_class.new(author: author_class.new(name: 'Sue Smith'), body: 'Great article')
45
- ]
46
- )
47
- }
48
-
49
- describe 'using default decorator' do
50
- subject(:article_part) {
51
- article_part_class.new(
52
- name: :article,
53
- value: article,
54
- )
55
- }
56
-
57
- describe 'decorating without options' do
58
- describe 'multiple declarations' do
59
- let(:article_part_class) {
60
- Class.new(Dry::View::Part) do
61
- decorate :author
62
- decorate :comments
63
- end
64
- }
65
-
66
- it 'decorates attributes with the standard Dry::View::Part class' do
67
- expect(article_part.author).to be_a Dry::View::Part
68
- expect(article_part.comments[0]).to be_a Dry::View::Part
69
- end
70
-
71
- context 'falsey values' do
72
- let(:author) { nil }
73
-
74
- it 'does not decorate the attributes' do
75
- expect(article_part.author).to be_nil
76
- end
77
- end
78
- end
79
-
80
- describe 'single declaration' do
81
- let(:article_part_class) {
82
- Class.new(Dry::View::Part) do
83
- decorate :author, :comments
84
- end
85
- }
86
-
87
- it 'decorates attributes with the standard Dry::View::Part class' do
88
- expect(article_part.author).to be_a Dry::View::Part
89
- expect(article_part.comments[0]).to be_a Dry::View::Part
90
- end
91
-
92
- context 'falsey values' do
93
- let(:author) { nil }
94
-
95
- it 'does not decorate the attributes' do
96
- expect(article_part.author).to be_nil
97
- end
98
- end
99
- end
100
- end
101
-
102
- describe 'decorating with part class specified' do
103
- before do
104
- module Test
105
- class AuthorPart < Dry::View::Part
106
- end
107
-
108
- class CommentPart < Dry::View::Part
109
- end
110
- end
111
- end
112
-
113
- let(:article_part_class) {
114
- Class.new(Dry::View::Part) do
115
- decorate :author, as: Test::AuthorPart
116
- decorate :comments, as: Test::CommentPart
117
- end
118
- }
119
-
120
- it 'deorates attributes with the specified part class' do
121
- expect(article_part.author).to be_a Test::AuthorPart
122
- expect(article_part.comments[0]).to be_a Test::CommentPart
123
- end
124
-
125
- context 'falsey values' do
126
- let(:author) { nil }
127
-
128
- it 'does not decorate the attributes' do
129
- expect(article_part.author).to be_nil
130
- end
131
- end
132
- end
133
- end
134
-
135
- describe 'using custom decorator' do
136
- let(:article_part_class) {
137
- Class.new(Dry::View::Part) do
138
- decorate :author
139
- decorate :comments
140
- end
141
- }
142
-
143
- subject(:article_part) {
144
- article_part_class.new(
145
- name: :article,
146
- value: article,
147
- decorator: decorator,
148
- )
149
- }
150
-
151
- let(:decorator) {
152
- Class.new(Dry::View::Decorator) do
153
- def part_class(name, value, **options)
154
- if !options.key?(:as)
155
- part_name = Dry::Core::Inflector.camelize(name)
156
- begin
157
- Test.const_get(:"#{part_name}Part")
158
- rescue NameError
159
- super
160
- end
161
- else
162
- super
163
- end
164
- end
165
- end.new
166
- }
167
-
168
- before do
169
- module Test
170
- class AuthorPart < Dry::View::Part
171
- end
172
-
173
- class CommentPart < Dry::View::Part
174
- decorate :author
175
- end
176
- end
177
- end
178
-
179
- it 'deorates attributes using the custom decorator' do
180
- expect(article_part.author).to be_a Test::AuthorPart
181
- expect(article_part.comments[0]).to be_a Test::CommentPart
182
- expect(article_part.comments[0].author).to be_a Test::AuthorPart
183
- end
184
-
185
- context 'falsey values' do
186
- let(:author) { nil }
187
-
188
- it 'does not decorate the attributes' do
189
- expect(article_part.author).to be_nil
190
- end
191
- end
192
- end
193
- end