dry-view 0.2.0 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/dry/view/controller.rb +1 -1
- data/lib/dry/view/exposure.rb +19 -19
- data/lib/dry/view/version.rb +1 -1
- data/spec/integration/exposures_spec.rb +35 -2
- data/spec/unit/exposure_spec.rb +91 -54
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4a41e09f937605f77be24092d9da6ff653da34d
|
4
|
+
data.tar.gz: 5bde51853d38d892bbbba62f42acacfebcfd71b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5121bec86b8fa020f4e46dd12beae722fee4eb135a000efff37926061e376206ca507aaea6a3334707df618d9368ca5201af041581fe6f4fedc626ed643df0e
|
7
|
+
data.tar.gz: 422541634a6634adfdac9cb2fa8e82e0b8cc35efc8aaf9dfe669a08394020ead0ce776a5564bb5819c701a43959f95c8d638a51d4ba389433c7b1fc09f88a3c7
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# 0.2.1 / 2017-01-30
|
2
|
+
|
3
|
+
### Fixed
|
4
|
+
|
5
|
+
- Exposure blocks now have access to the view controller instance when they're called (timriley)
|
6
|
+
|
1
7
|
# 0.2.0 / 2017-01-30
|
2
8
|
|
3
9
|
This release is a major reorientation for dry-view, and it should allow for more natural, straightforward template authoring.
|
data/lib/dry/view/controller.rb
CHANGED
data/lib/dry/view/exposure.rb
CHANGED
@@ -5,28 +5,30 @@ module Dry
|
|
5
5
|
|
6
6
|
attr_reader :name
|
7
7
|
attr_reader :proc
|
8
|
+
attr_reader :object
|
8
9
|
attr_reader :to_view
|
9
10
|
|
10
|
-
def initialize(name, proc = nil, to_view: true)
|
11
|
-
ensure_proc_parameters(proc) if proc
|
12
|
-
|
11
|
+
def initialize(name, proc = nil, object = nil, to_view: true)
|
13
12
|
@name = name
|
14
|
-
@proc = proc
|
13
|
+
@proc = prepare_proc(proc, object)
|
14
|
+
@object = object
|
15
15
|
@to_view = to_view
|
16
16
|
end
|
17
17
|
|
18
18
|
def bind(obj)
|
19
|
-
proc
|
19
|
+
self.class.new(name, proc, obj, to_view: to_view)
|
20
20
|
end
|
21
21
|
|
22
22
|
def dependencies
|
23
|
-
proc.parameters.map(&:last)
|
23
|
+
proc ? proc.parameters.map(&:last) : []
|
24
24
|
end
|
25
25
|
|
26
26
|
alias_method :to_view?, :to_view
|
27
27
|
|
28
28
|
def call(input, locals = {})
|
29
|
-
|
29
|
+
return input.fetch(name) unless proc
|
30
|
+
|
31
|
+
args = dependencies.map.with_index { |name, position|
|
30
32
|
if position.zero?
|
31
33
|
locals.fetch(name) { input }
|
32
34
|
else
|
@@ -34,26 +36,24 @@ module Dry
|
|
34
36
|
end
|
35
37
|
}
|
36
38
|
|
37
|
-
|
39
|
+
call_proc(*args)
|
38
40
|
end
|
39
41
|
|
40
42
|
private
|
41
43
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
def build_default_proc(obj)
|
47
|
-
if obj.respond_to?(name, _include_private = true)
|
48
|
-
obj.method(name)
|
44
|
+
def call_proc(*args)
|
45
|
+
if proc.is_a?(Method)
|
46
|
+
proc.(*args)
|
49
47
|
else
|
50
|
-
|
48
|
+
object.instance_exec(*args, &proc)
|
51
49
|
end
|
52
50
|
end
|
53
51
|
|
54
|
-
def
|
55
|
-
if proc
|
56
|
-
|
52
|
+
def prepare_proc(proc, object)
|
53
|
+
if proc
|
54
|
+
proc
|
55
|
+
elsif object.respond_to?(name, _include_private = true)
|
56
|
+
object.method(name)
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
data/lib/dry/view/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
RSpec.describe 'exposures' do
|
2
2
|
let(:context) { Struct.new(:title, :assets).new('dry-view rocks!', -> input { "#{input}.jpg" }) }
|
3
3
|
|
4
|
-
it 'uses exposures to build view locals' do
|
4
|
+
it 'uses exposures with blocks to build view locals' do
|
5
5
|
vc = Class.new(Dry::View::Controller) do
|
6
6
|
configure do |config|
|
7
7
|
config.paths = SPEC_ROOT.join('fixtures/templates')
|
@@ -27,7 +27,40 @@ RSpec.describe 'exposures' do
|
|
27
27
|
)
|
28
28
|
end
|
29
29
|
|
30
|
-
it '
|
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 |input|
|
47
|
+
input.fetch(: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
|
31
64
|
vc = Class.new(Dry::View::Controller) do
|
32
65
|
configure do |config|
|
33
66
|
config.paths = SPEC_ROOT.join('fixtures/templates')
|
data/spec/unit/exposure_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
RSpec.describe Dry::View::Exposure do
|
2
|
-
subject(:exposure) { described_class.new(:hello, proc) }
|
2
|
+
subject(:exposure) { described_class.new(:hello, proc, object) }
|
3
3
|
|
4
4
|
let(:proc) { -> input { "hi" } }
|
5
|
+
let(:object) { nil }
|
5
6
|
|
6
7
|
describe "initialization and attributes" do
|
7
8
|
describe "#name" do
|
@@ -18,18 +19,17 @@ RSpec.describe Dry::View::Exposure do
|
|
18
19
|
it "allows a nil proc" do
|
19
20
|
expect(described_class.new(:hello).proc).to be_nil
|
20
21
|
end
|
22
|
+
end
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
expect { described_class.new(:hello, proc) }.not_to raise_error
|
25
|
-
end
|
24
|
+
describe "#object" do
|
25
|
+
let(:object) { Object.new }
|
26
26
|
|
27
|
-
it "
|
28
|
-
|
29
|
-
|
27
|
+
it "accepts an object" do
|
28
|
+
expect(exposure.object).to eq object
|
29
|
+
end
|
30
30
|
|
31
|
-
|
32
|
-
expect
|
31
|
+
it "allows a nil object" do
|
32
|
+
expect(described_class.new(:hello).object).to be_nil
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -45,25 +45,29 @@ RSpec.describe Dry::View::Exposure do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
describe "#bind" do
|
48
|
-
|
49
|
-
subject(:bound_exposure) { exposure.bind(Object.new) }
|
48
|
+
subject(:bound_exposure) { exposure.bind(bind_object) }
|
50
49
|
|
51
|
-
|
52
|
-
expect(bound_exposure).to eql exposure
|
53
|
-
end
|
50
|
+
let(:bind_object) { Object.new }
|
54
51
|
|
55
|
-
|
52
|
+
it "returns a new object" do
|
53
|
+
expect(bound_exposure).not_to eql exposure
|
54
|
+
end
|
55
|
+
|
56
|
+
it "retains the bind object" do
|
57
|
+
expect(bound_exposure.object).to eq bind_object
|
58
|
+
end
|
59
|
+
|
60
|
+
context "proc is set" do
|
61
|
+
it "retains the existing proc" do
|
56
62
|
expect(bound_exposure.proc).to eql proc
|
57
63
|
end
|
58
64
|
end
|
59
65
|
|
60
|
-
context "
|
61
|
-
|
62
|
-
|
63
|
-
let(:exposure) { described_class.new(:hello) }
|
66
|
+
context "proc is nil" do
|
67
|
+
let(:proc) { nil }
|
64
68
|
|
65
69
|
context "matching instance method" do
|
66
|
-
let(:
|
70
|
+
let(:bind_object) do
|
67
71
|
Class.new do
|
68
72
|
def hello(input)
|
69
73
|
"hi there, #{input.fetch(:name)}"
|
@@ -71,75 +75,108 @@ RSpec.describe Dry::View::Exposure do
|
|
71
75
|
end.new
|
72
76
|
end
|
73
77
|
|
74
|
-
it "returns a new object" do
|
75
|
-
expect(bound_exposure).not_to eql exposure
|
76
|
-
end
|
77
|
-
|
78
78
|
it "sets the proc to the method on the object matching the exposure's name" do
|
79
|
-
expect(bound_exposure.proc).to eql
|
79
|
+
expect(bound_exposure.proc).to eql bind_object.method(:hello)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
83
|
context "no matching instance method" do
|
84
84
|
let(:object) { Object.new }
|
85
85
|
|
86
|
-
it "
|
87
|
-
expect(bound_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"
|
86
|
+
it "leaves proc as nil" do
|
87
|
+
expect(bound_exposure.proc).to be_nil
|
92
88
|
end
|
93
89
|
end
|
94
90
|
end
|
95
91
|
end
|
96
92
|
|
97
93
|
describe "#dependencies" do
|
98
|
-
|
94
|
+
context "proc provided" do
|
95
|
+
let(:proc) { -> input, foo, bar { "hi" } }
|
99
96
|
|
100
|
-
|
101
|
-
|
97
|
+
it "returns an array of exposure dependencies derived from the proc's argument names" do
|
98
|
+
expect(exposure.dependencies).to eql [:input, :foo, :bar]
|
99
|
+
end
|
102
100
|
end
|
103
|
-
end
|
104
101
|
|
105
|
-
|
106
|
-
|
102
|
+
context "matching instance method" do
|
103
|
+
let(:proc) { nil }
|
104
|
+
|
105
|
+
let(:object) do
|
106
|
+
Class.new do
|
107
|
+
def hello(input, bar, baz)
|
108
|
+
"hi there, #{input.fetch(:name)}"
|
109
|
+
end
|
110
|
+
end.new
|
111
|
+
end
|
107
112
|
|
108
|
-
|
109
|
-
|
113
|
+
it "returns an array of exposure dependencies derived from the instance method's argument names" do
|
114
|
+
expect(exposure.dependencies).to eql [:input, :bar, :baz]
|
115
|
+
end
|
110
116
|
end
|
111
117
|
|
118
|
+
context "proc is nil" do
|
119
|
+
let(:proc) { nil }
|
120
|
+
|
121
|
+
it "returns no dependencies" do
|
122
|
+
expect(exposure.dependencies).to eql []
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "#call" do
|
128
|
+
let(:input) { "input" }
|
129
|
+
|
112
130
|
context "proc expects input only" do
|
113
|
-
|
114
|
-
exposure.(input)
|
131
|
+
let(:proc) { -> input { input } }
|
115
132
|
|
116
|
-
|
133
|
+
it "sends the input to the proc" do
|
134
|
+
expect(exposure.(input)).to eql "input"
|
117
135
|
end
|
118
136
|
end
|
119
137
|
|
120
138
|
context "proc expects input and dependencies" do
|
121
|
-
let(:proc) { -> input, greeting { "#{greeting}, #{input
|
139
|
+
let(:proc) { -> input, greeting { "#{greeting}, #{input}" } }
|
122
140
|
let(:locals) { {greeting: "Hola"} }
|
123
141
|
|
124
|
-
before do
|
125
|
-
exposure.(input, locals)
|
126
|
-
end
|
127
|
-
|
128
142
|
it "sends the input and dependency values to the proc" do
|
129
|
-
expect(
|
143
|
+
expect(exposure.(input, locals)).to eq "Hola, input"
|
130
144
|
end
|
131
145
|
end
|
132
146
|
|
133
147
|
context "proc expects dependencies only" do
|
134
|
-
let(:proc) { -> greeting, farewell { "#{greeting}, #{
|
148
|
+
let(:proc) { -> greeting, farewell { "#{greeting}, #{farewell}" } }
|
135
149
|
let(:locals) { {greeting: "Hola", farewell: "Adios"} }
|
136
150
|
|
137
|
-
|
138
|
-
exposure.(input, locals)
|
151
|
+
it "sends the dependency values to the proc" do
|
152
|
+
expect(exposure.(input, locals)).to eq "Hola, Adios"
|
139
153
|
end
|
154
|
+
end
|
140
155
|
|
141
|
-
|
142
|
-
|
156
|
+
context "proc accesses object instance" do
|
157
|
+
let(:proc) { -> input { "#{input} from #{name}" } }
|
158
|
+
|
159
|
+
let(:object) do
|
160
|
+
Class.new do
|
161
|
+
attr_reader :name
|
162
|
+
|
163
|
+
def initialize(name)
|
164
|
+
@name = name
|
165
|
+
end
|
166
|
+
end.new("Jane")
|
167
|
+
end
|
168
|
+
|
169
|
+
it "makes the instance available as self" do
|
170
|
+
expect(exposure.(input)).to eq "input from Jane"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context "no proc" do
|
175
|
+
let(:proc) { nil }
|
176
|
+
let(:input) { {hello: "hi there"} }
|
177
|
+
|
178
|
+
it "returns a matching key from the input" do
|
179
|
+
expect(exposure.(input)).to eq "hi there"
|
143
180
|
end
|
144
181
|
end
|
145
182
|
end
|