dry-view 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/.travis.yml +15 -11
- data/CHANGELOG.md +23 -0
- data/Gemfile +6 -5
- data/README.md +8 -1
- data/benchmarks/templates/{button.erb → button.html.erb} +0 -0
- data/benchmarks/view.rb +3 -4
- data/bin/console +7 -0
- data/dry-view.gemspec +7 -6
- data/lib/dry/view/controller.rb +107 -0
- data/lib/dry/view/exposure.rb +61 -0
- data/lib/dry/view/exposures.rb +50 -0
- data/lib/dry/view/path.rb +40 -0
- data/lib/dry/view/renderer.rb +20 -28
- data/lib/dry/view/scope.rb +55 -0
- data/lib/dry/view/version.rb +1 -1
- data/lib/dry/view.rb +1 -1
- data/spec/fixtures/templates/empty.html.slim +1 -0
- data/spec/fixtures/templates/layouts/app.html.slim +1 -1
- data/spec/fixtures/templates/layouts/app.txt.erb +1 -1
- data/spec/fixtures/templates/parts_with_args/_box.html.slim +3 -0
- data/spec/fixtures/templates/parts_with_args.html.slim +3 -0
- data/spec/fixtures/templates/users/_tbody.html.slim +1 -1
- data/spec/fixtures/templates/users.html.slim +4 -4
- data/spec/fixtures/templates/users.txt.erb +0 -2
- data/spec/fixtures/templates/users_with_count.html.slim +5 -0
- data/spec/fixtures/templates_override/users.html.slim +5 -0
- data/spec/integration/exposures_spec.rb +178 -0
- data/spec/integration/view_spec.rb +83 -20
- data/spec/spec_helper.rb +13 -3
- data/spec/unit/controller_spec.rb +36 -0
- data/spec/unit/exposure_spec.rb +146 -0
- data/spec/unit/exposures_spec.rb +63 -0
- data/spec/unit/renderer_spec.rb +2 -1
- data/spec/unit/scope_spec.rb +98 -0
- metadata +36 -46
- data/lib/dry/view/layout.rb +0 -126
- data/lib/dry/view/null_part.rb +0 -30
- data/lib/dry/view/part.rb +0 -39
- data/lib/dry/view/value_part.rb +0 -50
- data/spec/unit/layout_spec.rb +0 -55
- data/spec/unit/null_part_spec.rb +0 -39
- data/spec/unit/value_part_spec.rb +0 -55
@@ -0,0 +1,178 @@
|
|
1
|
+
RSpec.describe 'exposures' do
|
2
|
+
let(:context) { Struct.new(:title, :assets).new('dry-view rocks!', -> input { "#{input}.jpg" }) }
|
3
|
+
|
4
|
+
it 'uses exposures 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 |input|
|
14
|
+
input.fetch(: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 'supports both blocks and instance methods as exposures' 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
|
+
expose :users
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def users(input)
|
44
|
+
input.fetch(:users).map { |user|
|
45
|
+
user.merge(name: user[:name].upcase)
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end.new
|
49
|
+
|
50
|
+
users = [
|
51
|
+
{ name: 'Jane', email: 'jane@doe.org' },
|
52
|
+
{ name: 'Joe', email: 'joe@doe.org' }
|
53
|
+
]
|
54
|
+
|
55
|
+
expect(vc.(users: users, context: context)).to eql(
|
56
|
+
'<!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>'
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'passes matching input data if no proc or instance method is available' do
|
61
|
+
vc = Class.new(Dry::View::Controller) do
|
62
|
+
configure do |config|
|
63
|
+
config.paths = SPEC_ROOT.join('fixtures/templates')
|
64
|
+
config.layout = 'app'
|
65
|
+
config.template = 'users'
|
66
|
+
config.default_format = :html
|
67
|
+
end
|
68
|
+
|
69
|
+
expose :users
|
70
|
+
end.new
|
71
|
+
|
72
|
+
users = [
|
73
|
+
{ name: 'Jane', email: 'jane@doe.org' },
|
74
|
+
{ name: 'Joe', email: 'joe@doe.org' }
|
75
|
+
]
|
76
|
+
|
77
|
+
expect(vc.(users: users, context: context)).to eql(
|
78
|
+
'<!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>'
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'allows exposures to depend on each other' do
|
83
|
+
vc = Class.new(Dry::View::Controller) do
|
84
|
+
configure do |config|
|
85
|
+
config.paths = SPEC_ROOT.join('fixtures/templates')
|
86
|
+
config.layout = 'app'
|
87
|
+
config.template = 'users_with_count'
|
88
|
+
config.default_format = :html
|
89
|
+
end
|
90
|
+
|
91
|
+
expose :users do |input|
|
92
|
+
input.fetch(:users)
|
93
|
+
end
|
94
|
+
|
95
|
+
expose :users_count do |users|
|
96
|
+
"#{users.length} users"
|
97
|
+
end
|
98
|
+
end.new
|
99
|
+
|
100
|
+
users = [
|
101
|
+
{name: 'Jane', email: 'jane@doe.org'},
|
102
|
+
{name: 'Joe', email: 'joe@doe.org'}
|
103
|
+
]
|
104
|
+
|
105
|
+
expect(vc.(users: users, context: context)).to eql(
|
106
|
+
'<!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>'
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'supports defining multiple exposures at once' do
|
111
|
+
vc = Class.new(Dry::View::Controller) do
|
112
|
+
configure do |config|
|
113
|
+
config.paths = SPEC_ROOT.join('fixtures/templates')
|
114
|
+
config.layout = 'app'
|
115
|
+
config.template = 'users_with_count'
|
116
|
+
config.default_format = :html
|
117
|
+
end
|
118
|
+
|
119
|
+
expose :users, :users_count
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def users(input)
|
124
|
+
input.fetch(:users)
|
125
|
+
end
|
126
|
+
|
127
|
+
def users_count(users)
|
128
|
+
"#{users.length} users"
|
129
|
+
end
|
130
|
+
end.new
|
131
|
+
|
132
|
+
users = [
|
133
|
+
{name: 'Jane', email: 'jane@doe.org'},
|
134
|
+
{name: 'Joe', email: 'joe@doe.org'}
|
135
|
+
]
|
136
|
+
|
137
|
+
expect(vc.(users: users, context: context)).to eql(
|
138
|
+
'<!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>'
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'allows exposures to be hidden from the view' do
|
143
|
+
vc = Class.new(Dry::View::Controller) do
|
144
|
+
configure do |config|
|
145
|
+
config.paths = SPEC_ROOT.join('fixtures/templates')
|
146
|
+
config.layout = 'app'
|
147
|
+
config.template = 'users_with_count'
|
148
|
+
config.default_format = :html
|
149
|
+
end
|
150
|
+
|
151
|
+
private_expose :prefix do
|
152
|
+
"COUNT: "
|
153
|
+
end
|
154
|
+
|
155
|
+
expose :users do |input|
|
156
|
+
input.fetch(:users)
|
157
|
+
end
|
158
|
+
|
159
|
+
expose :users_count do |prefix, users|
|
160
|
+
"#{prefix}#{users.length} users"
|
161
|
+
end
|
162
|
+
end.new
|
163
|
+
|
164
|
+
users = [
|
165
|
+
{name: 'Jane', email: 'jane@doe.org'},
|
166
|
+
{name: 'Joe', email: 'joe@doe.org'}
|
167
|
+
]
|
168
|
+
|
169
|
+
input = {users: users, context: context}
|
170
|
+
|
171
|
+
expect(vc.(input)).to eql(
|
172
|
+
'<!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>'
|
173
|
+
)
|
174
|
+
|
175
|
+
expect(vc.locals(input)).to include(:users, :users_count)
|
176
|
+
expect(vc.locals(input)).not_to include(:prefix)
|
177
|
+
end
|
178
|
+
end
|
@@ -1,51 +1,114 @@
|
|
1
1
|
RSpec.describe 'dry-view' do
|
2
|
-
let(:
|
3
|
-
Class.new(Dry::View::
|
2
|
+
let(:vc_class) do
|
3
|
+
Class.new(Dry::View::Controller) do
|
4
4
|
configure do |config|
|
5
|
-
config.
|
6
|
-
config.
|
5
|
+
config.paths = SPEC_ROOT.join('fixtures/templates')
|
6
|
+
config.layout = 'app'
|
7
7
|
config.template = 'users'
|
8
|
-
config.
|
8
|
+
config.default_format = :html
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
let(:
|
14
|
-
Struct.new(:title).new('dry-view rocks!')
|
13
|
+
let(:context) do
|
14
|
+
Struct.new(:title, :assets).new('dry-view rocks!', -> input { "#{input}.jpg" })
|
15
15
|
end
|
16
16
|
|
17
|
-
it 'renders within a layout
|
18
|
-
|
17
|
+
it 'renders within a layout and makes the provided context available everywhere' do
|
18
|
+
vc = vc_class.new
|
19
19
|
|
20
20
|
users = [
|
21
21
|
{ name: 'Jane', email: 'jane@doe.org' },
|
22
22
|
{ name: 'Joe', email: 'joe@doe.org' }
|
23
23
|
]
|
24
24
|
|
25
|
-
expect(
|
26
|
-
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><
|
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>'
|
27
56
|
)
|
28
57
|
end
|
29
58
|
|
30
59
|
it 'renders a view with an alternative format and engine' do
|
31
|
-
|
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
|
32
95
|
|
33
96
|
users = [
|
34
97
|
{ name: 'Jane', email: 'jane@doe.org' },
|
35
98
|
{ name: 'Joe', email: 'joe@doe.org' }
|
36
99
|
]
|
37
100
|
|
38
|
-
expect(
|
39
|
-
|
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>'
|
40
103
|
)
|
41
104
|
end
|
42
105
|
|
43
106
|
describe 'inheritance' do
|
44
107
|
let(:parent_view) do
|
45
|
-
klass = Class.new(Dry::View::
|
108
|
+
klass = Class.new(Dry::View::Controller)
|
46
109
|
|
47
|
-
klass.setting :
|
48
|
-
klass.setting :
|
110
|
+
klass.setting :paths, SPEC_ROOT.join('fixtures/templates')
|
111
|
+
klass.setting :layout, 'app'
|
49
112
|
klass.setting :formats, {html: :slim}
|
50
113
|
|
51
114
|
klass
|
@@ -59,10 +122,10 @@ RSpec.describe 'dry-view' do
|
|
59
122
|
end
|
60
123
|
end
|
61
124
|
|
62
|
-
it 'renders within a parent class layout using provided
|
63
|
-
|
125
|
+
it 'renders within a parent class layout using provided context' do
|
126
|
+
vc = child_view.new
|
64
127
|
|
65
|
-
expect(
|
128
|
+
expect(vc.(context: context, locals: { tasks: [{ title: 'one' }, { title: 'two' }] })).to eql(
|
66
129
|
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ol><li>one</li><li>two</li></ol></body></html>'
|
67
130
|
)
|
68
131
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
if RUBY_ENGINE ==
|
2
|
-
require
|
3
|
-
|
1
|
+
if RUBY_ENGINE == 'ruby'
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start do
|
4
|
+
add_filter "/spec/"
|
5
|
+
end
|
4
6
|
end
|
5
7
|
|
6
8
|
begin
|
@@ -24,3 +26,11 @@ RSpec.configure do |config|
|
|
24
26
|
config.order = :random
|
25
27
|
Kernel.srand config.seed
|
26
28
|
end
|
29
|
+
|
30
|
+
RSpec::Matchers.define :part_including do |data|
|
31
|
+
match { |actual|
|
32
|
+
data.all? { |(key, val)|
|
33
|
+
actual._data[key] == val
|
34
|
+
}
|
35
|
+
}
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
RSpec.describe Dry::View::Controller do
|
2
|
+
subject(:layout) { layout_class.new }
|
3
|
+
|
4
|
+
let(:layout_class) do
|
5
|
+
klass = Class.new(Dry::View::Controller)
|
6
|
+
|
7
|
+
klass.configure do |config|
|
8
|
+
config.paths = SPEC_ROOT.join('fixtures/templates')
|
9
|
+
config.layout = 'app'
|
10
|
+
config.template = 'user'
|
11
|
+
config.default_format = :html
|
12
|
+
end
|
13
|
+
|
14
|
+
klass
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:page) do
|
18
|
+
double(:page, title: 'Test')
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:options) do
|
22
|
+
{ context: page, locals: { user: { name: 'Jane' }, header: { title: 'User' } } }
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:renderer) do
|
26
|
+
layout.class.renderers[:html]
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#call' do
|
30
|
+
it 'renders template within the layout' do
|
31
|
+
expect(layout.(options)).to eql(
|
32
|
+
'<!DOCTYPE html><html><head><title>Test</title></head><body><h1>User</h1><p>Jane</p></body></html>'
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
RSpec.describe Dry::View::Exposure do
|
2
|
+
subject(:exposure) { described_class.new(:hello, proc) }
|
3
|
+
|
4
|
+
let(:proc) { -> input { "hi" } }
|
5
|
+
|
6
|
+
describe "initialization and attributes" do
|
7
|
+
describe "#name" do
|
8
|
+
it "accepts a name" do
|
9
|
+
expect(exposure.name).to eql :hello
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#proc" do
|
14
|
+
it "accepts a proc" do
|
15
|
+
expect(exposure.proc).to eql proc
|
16
|
+
end
|
17
|
+
|
18
|
+
it "allows a nil proc" do
|
19
|
+
expect(described_class.new(:hello).proc).to be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it "allows proc to take no arguments" do
|
23
|
+
proc = -> { "hi" }
|
24
|
+
expect { described_class.new(:hello, proc) }.not_to raise_error
|
25
|
+
end
|
26
|
+
|
27
|
+
it "requires proc to take positional arguments only" do
|
28
|
+
proc = -> a: "a" { "hi" }
|
29
|
+
expect { described_class.new(:hello, proc) }.to raise_error ArgumentError
|
30
|
+
|
31
|
+
proc = -> input, a: "a" { "hi" }
|
32
|
+
expect { described_class.new(:hello, proc) }.to raise_error ArgumentError
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#to_view" do
|
37
|
+
it "is true by default" do
|
38
|
+
expect(exposure.to_view).to be true
|
39
|
+
end
|
40
|
+
|
41
|
+
it "can be set to false on initialization" do
|
42
|
+
expect(described_class.new(:hello, to_view: false).to_view).to be false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#bind" do
|
48
|
+
context "proc provided" do
|
49
|
+
subject(:bound_exposure) { exposure.bind(Object.new) }
|
50
|
+
|
51
|
+
it "returns itself" do
|
52
|
+
expect(bound_exposure).to eql exposure
|
53
|
+
end
|
54
|
+
|
55
|
+
it "retains the same proc" do
|
56
|
+
expect(bound_exposure.proc).to eql proc
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "no proc provided" do
|
61
|
+
subject(:bound_exposure) { exposure.bind(object) }
|
62
|
+
|
63
|
+
let(:exposure) { described_class.new(:hello) }
|
64
|
+
|
65
|
+
context "matching instance method" do
|
66
|
+
let(:object) do
|
67
|
+
Class.new do
|
68
|
+
def hello(input)
|
69
|
+
"hi there, #{input.fetch(:name)}"
|
70
|
+
end
|
71
|
+
end.new
|
72
|
+
end
|
73
|
+
|
74
|
+
it "returns a new object" do
|
75
|
+
expect(bound_exposure).not_to eql exposure
|
76
|
+
end
|
77
|
+
|
78
|
+
it "sets the proc to the method on the object matching the exposure's name" do
|
79
|
+
expect(bound_exposure.proc).to eql object.method(:hello)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "no matching instance method" do
|
84
|
+
let(:object) { Object.new }
|
85
|
+
|
86
|
+
it "returns a new object" do
|
87
|
+
expect(bound_exposure).not_to eql exposure
|
88
|
+
end
|
89
|
+
|
90
|
+
it "builds a proc that passes through data from a matching key in the input" do
|
91
|
+
expect(bound_exposure.proc.(hello: "hello in input")).to eq "hello in input"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "#dependencies" do
|
98
|
+
let(:proc) { -> input, foo, bar { "hi" } }
|
99
|
+
|
100
|
+
it "returns an array of exposure dependencies derived from the proc's argument names" do
|
101
|
+
expect(exposure.dependencies).to eql [:input, :foo, :bar]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#call" do
|
106
|
+
let(:input) { double("input") }
|
107
|
+
|
108
|
+
before do
|
109
|
+
allow(proc).to receive(:call)
|
110
|
+
end
|
111
|
+
|
112
|
+
context "proc expects input only" do
|
113
|
+
it "sends the input to the proc" do
|
114
|
+
exposure.(input)
|
115
|
+
|
116
|
+
expect(proc).to have_received(:call).with(input)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "proc expects input and dependencies" do
|
121
|
+
let(:proc) { -> input, greeting { "#{greeting}, #{input.fetch(:name)}" } }
|
122
|
+
let(:locals) { {greeting: "Hola"} }
|
123
|
+
|
124
|
+
before do
|
125
|
+
exposure.(input, locals)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "sends the input and dependency values to the proc" do
|
129
|
+
expect(proc).to have_received(:call).with(input, "Hola")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "proc expects dependencies only" do
|
134
|
+
let(:proc) { -> greeting, farewell { "#{greeting}, #{input.fetch(:name)}" } }
|
135
|
+
let(:locals) { {greeting: "Hola", farewell: "Adios"} }
|
136
|
+
|
137
|
+
before do
|
138
|
+
exposure.(input, locals)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "sends the dependency values to the proc" do
|
142
|
+
expect(proc).to have_received(:call).with "Hola", "Adios"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
RSpec.describe Dry::View::Exposures do
|
2
|
+
subject(:exposures) { described_class.new }
|
3
|
+
|
4
|
+
describe "#exposures" do
|
5
|
+
it "is empty by defalut" do
|
6
|
+
expect(exposures.exposures).to be_empty
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#add" do
|
11
|
+
it "creates and adds an exposure" do
|
12
|
+
proc = -> input { "hi" }
|
13
|
+
exposures.add :hello, proc
|
14
|
+
|
15
|
+
expect(exposures[:hello].name).to eq :hello
|
16
|
+
expect(exposures[:hello].proc).to eq proc
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#bind" do
|
21
|
+
subject(:bound_exposures) { exposures.bind(object) }
|
22
|
+
|
23
|
+
let(:object) do
|
24
|
+
Class.new do
|
25
|
+
def hello(input)
|
26
|
+
"hi"
|
27
|
+
end
|
28
|
+
end.new
|
29
|
+
end
|
30
|
+
|
31
|
+
before do
|
32
|
+
exposures.add(:hello)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "binds each of the exposures" do
|
36
|
+
expect(bound_exposures[:hello].proc).to eq object.method(:hello)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns a new copy of the exposures" do
|
40
|
+
expect(exposures.exposures).not_to eql(bound_exposures.exposures)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#locals" do
|
45
|
+
before do
|
46
|
+
exposures.add(:greeting, -> input { input.fetch(:greeting).upcase })
|
47
|
+
exposures.add(:farewell, -> greeting { "#{greeting} and goodbye" })
|
48
|
+
end
|
49
|
+
|
50
|
+
subject(:locals) { exposures.locals(greeting: "hello") }
|
51
|
+
|
52
|
+
it "returns the values from the exposures' procs" do
|
53
|
+
expect(locals).to eq(greeting: "HELLO", farewell: "HELLO and goodbye")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "does not return any values from private exposures" do
|
57
|
+
exposures.add(:hidden, -> input { "shh" }, to_view: false)
|
58
|
+
|
59
|
+
expect(locals).to include(:greeting, :farewell)
|
60
|
+
expect(locals).not_to include(:hidden)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/spec/unit/renderer_spec.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
require 'dry/view/path'
|
1
2
|
require 'dry/view/renderer'
|
2
3
|
|
3
4
|
RSpec.describe Dry::View::Renderer do
|
4
5
|
subject(:renderer) do
|
5
|
-
Dry::View::Renderer.new(SPEC_ROOT.join('fixtures/templates'), format: 'html'
|
6
|
+
Dry::View::Renderer.new([Dry::View::Path.new(SPEC_ROOT.join('fixtures/templates'))], format: 'html')
|
6
7
|
end
|
7
8
|
|
8
9
|
let(:scope) { double(:scope) }
|