dry-view 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5cf179c08c3deb29086bc87197420a301595a70f
4
- data.tar.gz: 1c6eccdfb39502b559748c8c75d4c4b127b16019
3
+ metadata.gz: f4a41e09f937605f77be24092d9da6ff653da34d
4
+ data.tar.gz: 5bde51853d38d892bbbba62f42acacfebcfd71b4
5
5
  SHA512:
6
- metadata.gz: 390c86f0b801d44bfe8a5958a982da88a52030dbfbf4403380af90c82c1279d3603a0f4376d1b2f349f3e42f36628c3a551b37e3fe8d2d75874b77d1e4d1efb3
7
- data.tar.gz: cfdc7df2e7d8b63cda8611d203d9ec39da0a31d9237e742a00e861244556b2cfc5854b09a312abb358f77c572d4102b56d707d74ee27a61154fb7d6c44952756
6
+ metadata.gz: b5121bec86b8fa020f4e46dd12beae722fee4eb135a000efff37926061e376206ca507aaea6a3334707df618d9368ca5201af041581fe6f4fedc626ed643df0e
7
+ data.tar.gz: 422541634a6634adfdac9cb2fa8e82e0b8cc35efc8aaf9dfe669a08394020ead0ce776a5564bb5819c701a43959f95c8d638a51d4ba389433c7b1fc09f88a3c7
@@ -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.
@@ -48,7 +48,7 @@ module Dry
48
48
  exposures.add(names.first, block, **options)
49
49
  else
50
50
  names.each do |name|
51
- exposures.add(name, nil, **options)
51
+ exposures.add(name, **options)
52
52
  end
53
53
  end
54
54
  end
@@ -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 ? self : with_default_proc(obj)
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
- params = dependencies.map.with_index { |name, position|
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
- proc.(*params)
39
+ call_proc(*args)
38
40
  end
39
41
 
40
42
  private
41
43
 
42
- def with_default_proc(obj)
43
- self.class.new(name, build_default_proc(obj), to_view: to_view)
44
- end
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
- -> input { input.fetch(name) }
48
+ object.instance_exec(*args, &proc)
51
49
  end
52
50
  end
53
51
 
54
- def ensure_proc_parameters(proc)
55
- if proc.parameters.any? { |type, _| !SUPPORTED_PARAMETER_TYPES.include?(type) }
56
- raise ArgumentError, "+proc+ must take positional arugments only"
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
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module View
3
- VERSION = '0.2.0'.freeze
3
+ VERSION = '0.2.1'.freeze
4
4
  end
5
5
  end
@@ -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 'supports both blocks and instance methods as exposures' do
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')
@@ -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
- it "allows proc to take no arguments" do
23
- proc = -> { "hi" }
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 "requires proc to take positional arguments only" do
28
- proc = -> a: "a" { "hi" }
29
- expect { described_class.new(:hello, proc) }.to raise_error ArgumentError
27
+ it "accepts an object" do
28
+ expect(exposure.object).to eq object
29
+ end
30
30
 
31
- proc = -> input, a: "a" { "hi" }
32
- expect { described_class.new(:hello, proc) }.to raise_error ArgumentError
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
- context "proc provided" do
49
- subject(:bound_exposure) { exposure.bind(Object.new) }
48
+ subject(:bound_exposure) { exposure.bind(bind_object) }
50
49
 
51
- it "returns itself" do
52
- expect(bound_exposure).to eql exposure
53
- end
50
+ let(:bind_object) { Object.new }
54
51
 
55
- it "retains the same proc" do
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 "no proc provided" do
61
- subject(:bound_exposure) { exposure.bind(object) }
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(:object) do
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 object.method(:hello)
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 "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"
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
- let(:proc) { -> input, foo, bar { "hi" } }
94
+ context "proc provided" do
95
+ let(:proc) { -> input, foo, bar { "hi" } }
99
96
 
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]
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
- describe "#call" do
106
- let(:input) { double("input") }
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
- before do
109
- allow(proc).to receive(:call)
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
- it "sends the input to the proc" do
114
- exposure.(input)
131
+ let(:proc) { -> input { input } }
115
132
 
116
- expect(proc).to have_received(:call).with(input)
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.fetch(:name)}" } }
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(proc).to have_received(:call).with(input, "Hola")
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}, #{input.fetch(:name)}" } }
148
+ let(:proc) { -> greeting, farewell { "#{greeting}, #{farewell}" } }
135
149
  let(:locals) { {greeting: "Hola", farewell: "Adios"} }
136
150
 
137
- before do
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
- it "sends the dependency values to the proc" do
142
- expect(proc).to have_received(:call).with "Hola", "Adios"
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-view
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica