dry-view 0.5.1 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
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