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 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