dry-view 0.5.1 → 0.7.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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +143 -18
  3. data/LICENSE +20 -0
  4. data/README.md +22 -14
  5. data/dry-view.gemspec +29 -21
  6. data/lib/dry-view.rb +3 -1
  7. data/lib/dry/view.rb +514 -2
  8. data/lib/dry/view/context.rb +80 -0
  9. data/lib/dry/view/decorated_attributes.rb +82 -0
  10. data/lib/dry/view/errors.rb +29 -0
  11. data/lib/dry/view/exposure.rb +35 -14
  12. data/lib/dry/view/exposures.rb +18 -6
  13. data/lib/dry/view/part.rb +166 -53
  14. data/lib/dry/view/part_builder.rb +140 -0
  15. data/lib/dry/view/path.rb +35 -7
  16. data/lib/dry/view/render_environment.rb +62 -0
  17. data/lib/dry/view/render_environment_missing.rb +44 -0
  18. data/lib/dry/view/rendered.rb +55 -0
  19. data/lib/dry/view/renderer.rb +36 -29
  20. data/lib/dry/view/scope.rb +160 -14
  21. data/lib/dry/view/scope_builder.rb +98 -0
  22. data/lib/dry/view/tilt.rb +78 -0
  23. data/lib/dry/view/tilt/erb.rb +26 -0
  24. data/lib/dry/view/tilt/erbse.rb +21 -0
  25. data/lib/dry/view/tilt/haml.rb +26 -0
  26. data/lib/dry/view/version.rb +5 -2
  27. metadata +78 -115
  28. data/.gitignore +0 -26
  29. data/.rspec +0 -2
  30. data/.travis.yml +0 -23
  31. data/CONTRIBUTING.md +0 -29
  32. data/Gemfile +0 -22
  33. data/LICENSE.md +0 -10
  34. data/Rakefile +0 -6
  35. data/benchmarks/templates/button.html.erb +0 -1
  36. data/benchmarks/view.rb +0 -24
  37. data/bin/console +0 -7
  38. data/lib/dry/view/controller.rb +0 -155
  39. data/lib/dry/view/decorator.rb +0 -45
  40. data/lib/dry/view/missing_renderer.rb +0 -15
  41. data/spec/fixtures/templates/_hello.html.slim +0 -1
  42. data/spec/fixtures/templates/decorated_parts.html.slim +0 -4
  43. data/spec/fixtures/templates/edit.html.slim +0 -11
  44. data/spec/fixtures/templates/empty.html.slim +0 -1
  45. data/spec/fixtures/templates/greeting.html.slim +0 -2
  46. data/spec/fixtures/templates/hello.html.slim +0 -1
  47. data/spec/fixtures/templates/layouts/app.html.slim +0 -6
  48. data/spec/fixtures/templates/layouts/app.txt.erb +0 -3
  49. data/spec/fixtures/templates/parts_with_args.html.slim +0 -3
  50. data/spec/fixtures/templates/parts_with_args/_box.html.slim +0 -3
  51. data/spec/fixtures/templates/shared/_index_table.html.slim +0 -2
  52. data/spec/fixtures/templates/shared/_shared_hello.html.slim +0 -1
  53. data/spec/fixtures/templates/tasks.html.slim +0 -3
  54. data/spec/fixtures/templates/user.html.slim +0 -2
  55. data/spec/fixtures/templates/users.html.slim +0 -5
  56. data/spec/fixtures/templates/users.txt.erb +0 -3
  57. data/spec/fixtures/templates/users/_row.html.slim +0 -2
  58. data/spec/fixtures/templates/users/_tbody.html.slim +0 -5
  59. data/spec/fixtures/templates/users_with_count.html.slim +0 -5
  60. data/spec/fixtures/templates/users_with_count_inherit.html.slim +0 -6
  61. data/spec/fixtures/templates_override/_hello.html.slim +0 -1
  62. data/spec/fixtures/templates_override/users.html.slim +0 -5
  63. data/spec/integration/decorator_spec.rb +0 -80
  64. data/spec/integration/exposures_spec.rb +0 -392
  65. data/spec/integration/part/decorated_attributes_spec.rb +0 -157
  66. data/spec/integration/view_spec.rb +0 -133
  67. data/spec/spec_helper.rb +0 -46
  68. data/spec/unit/controller_spec.rb +0 -37
  69. data/spec/unit/decorator_spec.rb +0 -61
  70. data/spec/unit/exposure_spec.rb +0 -227
  71. data/spec/unit/exposures_spec.rb +0 -103
  72. data/spec/unit/part_spec.rb +0 -90
  73. data/spec/unit/renderer_spec.rb +0 -57
  74. 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,157 +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(:article) {
36
- article_class.new(
37
- title: 'Hello world',
38
- author: author_class.new(name: 'Jane Doe'),
39
- comments: [
40
- comment_class.new(author: author_class.new(name: 'Sue Smith'), body: 'Great article')
41
- ]
42
- )
43
- }
44
-
45
- describe 'using default decorator' do
46
- subject(:article_part) {
47
- article_part_class.new(
48
- name: :article,
49
- value: article,
50
- )
51
- }
52
-
53
- describe 'decorating without options' do
54
- describe 'multiple declarations' do
55
- let(:article_part_class) {
56
- Class.new(Dry::View::Part) do
57
- decorate :author
58
- decorate :comments
59
- end
60
- }
61
-
62
- it 'decorates exposures with the standard Dry::View::Part class' do
63
- expect(article_part.author).to be_a Dry::View::Part
64
- expect(article_part.comments[0]).to be_a Dry::View::Part
65
- end
66
- end
67
-
68
- describe 'single declaration' do
69
- let(:article_part_class) {
70
- Class.new(Dry::View::Part) do
71
- decorate :author, :comments
72
- end
73
- }
74
-
75
- it 'decorates exposures with the standard Dry::View::Part class' do
76
- expect(article_part.author).to be_a Dry::View::Part
77
- expect(article_part.comments[0]).to be_a Dry::View::Part
78
- end
79
- end
80
- end
81
-
82
- describe 'decorating with part class specified' do
83
- before do
84
- module Test
85
- class AuthorPart < Dry::View::Part
86
- end
87
-
88
- class CommentPart < Dry::View::Part
89
- end
90
- end
91
- end
92
-
93
- let(:article_part_class) {
94
- Class.new(Dry::View::Part) do
95
- decorate :author, as: Test::AuthorPart
96
- decorate :comments, as: Test::CommentPart
97
- end
98
- }
99
-
100
- it 'deorates exposures with the specified part class' do
101
- expect(article_part.author).to be_a Test::AuthorPart
102
- expect(article_part.comments[0]).to be_a Test::CommentPart
103
- end
104
- end
105
- end
106
-
107
- describe 'using custom decorator' do
108
- let(:article_part_class) {
109
- Class.new(Dry::View::Part) do
110
- decorate :author
111
- decorate :comments
112
- end
113
- }
114
-
115
- subject(:article_part) {
116
- article_part_class.new(
117
- name: :article,
118
- value: article,
119
- decorator: decorator,
120
- )
121
- }
122
-
123
- let(:decorator) {
124
- Class.new(Dry::View::Decorator) do
125
- def part_class(name, value, **options)
126
- if !options.key?(:as)
127
- part_name = Dry::Core::Inflector.camelize(name)
128
- begin
129
- Test.const_get(:"#{part_name}Part")
130
- rescue NameError
131
- super
132
- end
133
- else
134
- super
135
- end
136
- end
137
- end.new
138
- }
139
-
140
- before do
141
- module Test
142
- class AuthorPart < Dry::View::Part
143
- end
144
-
145
- class CommentPart < Dry::View::Part
146
- decorate :author
147
- end
148
- end
149
- end
150
-
151
- it 'deorates exposures using the custom decorator' do
152
- expect(article_part.author).to be_a Test::AuthorPart
153
- expect(article_part.comments[0]).to be_a Test::CommentPart
154
- expect(article_part.comments[0].author).to be_a Test::AuthorPart
155
- end
156
- end
157
- end