dry-view 0.5.3 → 0.6.0

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.
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