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,133 +0,0 @@
1
- RSpec.describe 'dry-view' do
2
- let(:vc_class) do
3
- Class.new(Dry::View::Controller) do
4
- configure do |config|
5
- config.paths = SPEC_ROOT.join('fixtures/templates')
6
- config.layout = 'app'
7
- config.template = 'users'
8
- config.default_format = :html
9
- end
10
- end
11
- end
12
-
13
- let(:context) do
14
- Struct.new(:title, :assets).new('dry-view rocks!', -> input { "#{input}.jpg" })
15
- end
16
-
17
- it 'renders within a layout and makes the provided context available everywhere' do
18
- vc = vc_class.new
19
-
20
- users = [
21
- { name: 'Jane', email: 'jane@doe.org' },
22
- { name: 'Joe', email: 'joe@doe.org' }
23
- ]
24
-
25
- expect(vc.(context: context, locals: {users: users})).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 'renders without a layout' do
31
- vc = Class.new(vc_class) do
32
- configure do |config|
33
- config.layout = false
34
- end
35
- end.new
36
-
37
- users = [
38
- { name: 'Jane', email: 'jane@doe.org' },
39
- { name: 'Joe', email: 'joe@doe.org' }
40
- ]
41
-
42
- expect(vc.(context: context, locals: {users: users})).to eql(
43
- '<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" />'
44
- )
45
- end
46
-
47
- it 'renders a view without locals' do
48
- vc = Class.new(vc_class) do
49
- configure do |config|
50
- config.template = 'empty'
51
- end
52
- end.new
53
-
54
- expect(vc.(context: context, locals: {})).to eq(
55
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><p>This is a view with no locals.</p></body></html>'
56
- )
57
- end
58
-
59
- it 'renders a view with an alternative format and engine' do
60
- vc = vc_class.new
61
-
62
- users = [
63
- { name: 'Jane', email: 'jane@doe.org' },
64
- { name: 'Joe', email: 'joe@doe.org' }
65
- ]
66
-
67
- expect(vc.(context: context, locals: {users: users}, format: 'txt').strip).to eql(
68
- "# dry-view rocks!\n\n* Jane (jane@doe.org)\n* Joe (joe@doe.org)"
69
- )
70
- end
71
-
72
- it 'renders a view with a template on another view path' do
73
- vc = Class.new(vc_class) do
74
- configure do |config|
75
- config.paths = [SPEC_ROOT.join('fixtures/templates_override')] + Array(config.paths)
76
- end
77
- end.new
78
-
79
- users = [
80
- { name: 'Jane', email: 'jane@doe.org' },
81
- { name: 'Joe', email: 'joe@doe.org' }
82
- ]
83
-
84
- expect(vc.(context: context, locals: {users: users})).to eq(
85
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><h1>OVERRIDE</h1><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></body></html>'
86
- )
87
- end
88
-
89
- it 'renders a view that passes arguments to partials' do
90
- vc = Class.new(vc_class) do
91
- configure do |config|
92
- config.template = 'parts_with_args'
93
- end
94
- end.new
95
-
96
- users = [
97
- { name: 'Jane', email: 'jane@doe.org' },
98
- { name: 'Joe', email: 'joe@doe.org' }
99
- ]
100
-
101
- expect(vc.(context: context, locals: {users: users})).to eq(
102
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><div class="users"><div class="box"><h2>Nombre</h2>Jane</div><div class="box"><h2>Nombre</h2>Joe</div></div></body></html>'
103
- )
104
- end
105
-
106
- describe 'inheritance' do
107
- let(:parent_view) do
108
- klass = Class.new(Dry::View::Controller)
109
-
110
- klass.setting :paths, SPEC_ROOT.join('fixtures/templates')
111
- klass.setting :layout, 'app'
112
- klass.setting :formats, {html: :slim}
113
-
114
- klass
115
- end
116
-
117
- let(:child_view) do
118
- Class.new(parent_view) do
119
- configure do |config|
120
- config.template = 'tasks'
121
- end
122
- end
123
- end
124
-
125
- it 'renders within a parent class layout using provided context' do
126
- vc = child_view.new
127
-
128
- expect(vc.(context: context, locals: { tasks: [{ title: 'one' }, { title: 'two' }] })).to eql(
129
- '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ol><li>one</li><li>two</li></ol></body></html>'
130
- )
131
- end
132
- end
133
- end
data/spec/spec_helper.rb DELETED
@@ -1,46 +0,0 @@
1
- if RUBY_ENGINE == 'ruby'
2
- require 'simplecov'
3
- SimpleCov.start do
4
- add_filter "/spec/"
5
- end
6
- end
7
-
8
- begin
9
- require 'pry-byebug'
10
- rescue LoadError; end
11
-
12
- SPEC_ROOT = Pathname(__FILE__).dirname
13
-
14
- require 'erb'
15
- require 'slim'
16
-
17
- # Prefer plain ERB processor rather than erubis (which has problems on JRuby)
18
- require 'tilt'
19
- Tilt.register 'erb', Tilt::ERBTemplate
20
-
21
- require 'dry-view'
22
-
23
- module Test
24
- def self.remove_constants
25
- constants.each(&method(:remove_const))
26
- end
27
- end
28
-
29
- RSpec.configure do |config|
30
- config.disable_monkey_patching!
31
-
32
- config.order = :random
33
- Kernel.srand config.seed
34
-
35
- config.after do
36
- Test.remove_constants
37
- end
38
- end
39
-
40
- RSpec::Matchers.define :part_including do |data|
41
- match { |actual|
42
- data.all? { |(key, val)|
43
- actual._data[key] == val
44
- }
45
- }
46
- end
@@ -1,83 +0,0 @@
1
- RSpec.describe Dry::View::Controller do
2
- subject(:controller) {
3
- Class.new(Dry::View::Controller) do
4
- configure do |config|
5
- config.paths = SPEC_ROOT.join('fixtures/templates')
6
- config.layout = 'app'
7
- config.template = 'user'
8
- end
9
- end.new
10
- }
11
-
12
- let(:page) do
13
- double(:page, title: 'Test')
14
- end
15
-
16
- let(:options) do
17
- { context: page, locals: { user: { name: 'Jane' }, header: { title: 'User' } } }
18
- end
19
-
20
- describe '#call' do
21
- it 'renders template within the layout' do
22
- expect(controller.(options)).to eql(
23
- '<!DOCTYPE html><html><head><title>Test</title></head><body><h1>User</h1><p>Jane</p></body></html>'
24
- )
25
- end
26
-
27
- it 'provides a meaningful error if the template name is missing' do
28
- controller = Class.new(Dry::View::Controller) do
29
- configure do |config|
30
- config.paths = SPEC_ROOT.join('fixtures/templates')
31
- end
32
- end.new
33
-
34
- expect { controller.(options) }.to raise_error Dry::View::Controller::UndefinedTemplateError
35
- end
36
- end
37
-
38
- describe 'renderer options' do
39
- subject(:controller) {
40
- Class.new(Dry::View::Controller) do
41
- configure do |config|
42
- config.paths = SPEC_ROOT.join('fixtures/templates')
43
- config.template = 'controller_renderer_options'
44
- config.renderer_options = {
45
- outvar: '@__buf__'
46
- }
47
- end
48
- end.new
49
- }
50
-
51
- subject(:context) {
52
- Class.new do
53
- def self.form(action:, &blk)
54
- new(action, &blk)
55
- end
56
-
57
- def initialize(action, &blk)
58
- @buf = eval('@__buf__', blk.binding)
59
-
60
- @buf << "<form action=\"#{action}\" method=\"post\">"
61
- blk.(self)
62
- @buf << '</form>'
63
- end
64
-
65
- def text(name)
66
- "<input type=\"text\" name=\"#{name}\" />"
67
- end
68
- end
69
- }
70
-
71
- it 'uses default encoding' do
72
- klass = Class.new(Dry::View::Controller)
73
- expect(klass.config.renderer_options).to be_a Hash
74
- expect(klass.config.renderer_options[:default_encoding]).to eql 'utf-8'
75
- end
76
-
77
- it 'are passed to renderer' do
78
- expect(controller.(context: context)).to eql(
79
- '<form action="/people" method="post"><input type="text" name="name" /></form>'
80
- )
81
- end
82
- end
83
- end
@@ -1,61 +0,0 @@
1
- RSpec.describe Dry::View::Decorator do
2
- subject(:decorator) { described_class.new }
3
-
4
- describe '#call' do
5
- let(:value) { double('value') }
6
- let(:renderer) { double('renderer') }
7
- let(:context) { double('context') }
8
- let(:options) { {} }
9
-
10
- describe 'returning a part value' do
11
- subject(:part) { decorator.('user', value, renderer: renderer, context: context, **options) }
12
-
13
- context 'no options provided' do
14
- it 'returns a Part' do
15
- expect(part).to be_a Dry::View::Part
16
- end
17
-
18
- it 'wraps the value' do
19
- expect(part._value).to eq value
20
- end
21
- end
22
-
23
- context 'part class provided via `:as` option' do
24
- let(:options) { {as: Test::CustomPart} }
25
-
26
- before do
27
- module Test
28
- CustomPart = Class.new(Dry::View::Part)
29
- end
30
- end
31
-
32
- it 'returns an instance of the provided class' do
33
- expect(part).to be_a Test::CustomPart
34
- end
35
-
36
- it 'wraps the value' do
37
- expect(part._value).to eq value
38
- end
39
- end
40
-
41
- context 'value is an array' do
42
- let(:child_a) { double('child a') }
43
- let(:child_b) { double('child a') }
44
- let(:value) { [child_a, child_b] }
45
-
46
- it 'returns a part wrapping the array' do
47
- expect(part).to be_a Dry::View::Part
48
- expect(part._value).to be_an Array
49
- end
50
-
51
- it 'wraps the elements within the array' do
52
- expect(part[0]).to be_a Dry::View::Part
53
- expect(part[0]._value).to eq child_a
54
-
55
- expect(part[1]).to be_a Dry::View::Part
56
- expect(part[1]._value).to eq child_b
57
- end
58
- end
59
- end
60
- end
61
- end
@@ -1,227 +0,0 @@
1
- RSpec.describe Dry::View::Exposure do
2
- subject(:exposure) { described_class.new(:hello, proc, object, **options) }
3
-
4
- let(:proc) { -> input { "hi" } }
5
- let(:object) { nil }
6
- let(:options) { {} }
7
-
8
- describe "initialization and attributes" do
9
- describe "#name" do
10
- it "accepts a name" do
11
- expect(exposure.name).to eql :hello
12
- end
13
- end
14
-
15
- describe "#proc" do
16
- it "accepts a proc" do
17
- expect(exposure.proc).to eql proc
18
- end
19
-
20
- it "allows a nil proc" do
21
- expect(described_class.new(:hello).proc).to be_nil
22
- end
23
- end
24
-
25
- describe "#object" do
26
- let(:object) { Object.new }
27
-
28
- it "accepts an object" do
29
- expect(exposure.object).to eq object
30
- end
31
-
32
- it "allows a nil object" do
33
- expect(described_class.new(:hello).object).to be_nil
34
- end
35
- end
36
-
37
- describe "#private?" do
38
- it "is false by default" do
39
- expect(exposure).not_to be_private
40
- end
41
-
42
- it "can be set on initialization" do
43
- expect(described_class.new(:hello, private: true)).to be_private
44
- end
45
- end
46
-
47
- describe "#default_value" do
48
- it "is nil by default" do
49
- expect(exposure.default_value).to be_nil
50
- end
51
-
52
- it "can be set on initialization" do
53
- exposuse = described_class.new(:hello, default: 'Hi !')
54
- expect(exposuse.default_value).to eq('Hi !')
55
- end
56
- end
57
- end
58
-
59
- describe "#bind" do
60
- subject(:bound_exposure) { exposure.bind(bind_object) }
61
-
62
- let(:bind_object) { Object.new }
63
-
64
- it "returns a new object" do
65
- expect(bound_exposure).not_to eql exposure
66
- end
67
-
68
- it "retains the bind object" do
69
- expect(bound_exposure.object).to eq bind_object
70
- end
71
-
72
- context "proc is set" do
73
- it "retains the existing proc" do
74
- expect(bound_exposure.proc).to eql proc
75
- end
76
- end
77
-
78
- context "proc is nil" do
79
- let(:proc) { nil }
80
-
81
- context "matching instance method" do
82
- let(:bind_object) do
83
- Class.new do
84
- def hello(input)
85
- "hi there, #{input.fetch(:name)}"
86
- end
87
- end.new
88
- end
89
-
90
- it "sets the proc to the method on the object matching the exposure's name" do
91
- expect(bound_exposure.proc).to eql bind_object.method(:hello)
92
- end
93
- end
94
-
95
- context "no matching instance method" do
96
- let(:object) { Object.new }
97
-
98
- it "leaves proc as nil" do
99
- expect(bound_exposure.proc).to be_nil
100
- end
101
- end
102
- end
103
- end
104
-
105
- describe "#dependency_names" do
106
- context "proc provided" do
107
- let(:proc) { -> input, foo, bar { "hi" } }
108
-
109
- it "returns an array of exposure dependencies derived from the proc's argument names" do
110
- expect(exposure.dependency_names).to eql [:input, :foo, :bar]
111
- end
112
- end
113
-
114
- context "matching instance method" do
115
- let(:proc) { nil }
116
-
117
- let(:object) do
118
- Class.new do
119
- def hello(input, bar, baz)
120
- "hi there, #{input.fetch(:name)}"
121
- end
122
- end.new
123
- end
124
-
125
- it "returns an array of exposure dependencies derived from the instance method's argument names" do
126
- expect(exposure.dependency_names).to eql [:input, :bar, :baz]
127
- end
128
- end
129
-
130
- context "proc is nil" do
131
- let(:proc) { nil }
132
-
133
- it "returns no dependencies" do
134
- expect(exposure.dependency_names).to eql []
135
- end
136
- end
137
- end
138
-
139
- describe "#call" do
140
- let(:input) { {name: "Jane"} }
141
-
142
- context "proc expects input only" do
143
- let(:proc) { -> name: { name } }
144
-
145
- it "sends the input to the proc" do
146
- expect(exposure.(input)).to eql "Jane"
147
- end
148
- end
149
-
150
- context "proc expects input and dependencies" do
151
- let(:proc) { -> greeting, name: { "#{greeting}, #{name}" } }
152
- let(:locals) { {greeting: "Hola"} }
153
-
154
- it "sends the input and dependency values to the proc" do
155
- expect(exposure.(input, locals)).to eq "Hola, Jane"
156
- end
157
- end
158
-
159
- context "Default value" do
160
- let(:options) { { default: "John" } }
161
-
162
- context "use default value" do
163
- let(:proc) { nil }
164
-
165
- it "use the default value" do
166
- expect(exposure.({})).to eq "John"
167
- end
168
- end
169
-
170
- context "use input value instead of default" do
171
- let(:proc) { nil }
172
-
173
- it "use the default value" do
174
- expect(exposure.({hello: "Jane"})).to eq "Jane"
175
- end
176
- end
177
-
178
- context "use input value over default even when input is nil" do
179
- let(:proc) { nil }
180
-
181
- it "use the default value" do
182
- expect(exposure.({hello: nil})).to eq nil
183
- end
184
- end
185
- end
186
-
187
- context "proc expects dependencies only" do
188
- let(:proc) { -> greeting, farewell { "#{greeting}, #{farewell}" } }
189
- let(:locals) { {greeting: "Hola", farewell: "Adios"} }
190
-
191
- it "sends the dependency values to the proc" do
192
- expect(exposure.(input, locals)).to eq "Hola, Adios"
193
- end
194
- end
195
-
196
- context "proc accesses object instance" do
197
- let(:proc) { -> name: { "My name is #{name} but call me #{title} #{name}" } }
198
-
199
- let(:object) do
200
- Class.new do
201
- attr_reader :title
202
-
203
- def initialize(title)
204
- @title = title
205
- end
206
- end.new("Dr")
207
- end
208
-
209
- it "makes the instance available as self" do
210
- expect(exposure.(input)).to eq "My name is Jane but call me Dr Jane"
211
- end
212
- end
213
-
214
- context "no proc" do
215
- let(:proc) { nil }
216
- let(:input) { {hello: "hi there"} }
217
-
218
- it "returns a matching key from the input" do
219
- expect(exposure.(input)).to eq "hi there"
220
- end
221
-
222
- it "returns nil when no input key matches" do
223
- expect(exposure.(nothing_matches_here: true)).to be_nil
224
- end
225
- end
226
- end
227
- end