Skip to content

Commit 7a2761d

Browse files
committed
Merge pull request #1025 from dblock/merge-respond-to
Only merge representations onto an existing body.
2 parents 0507b95 + 6260ee8 commit 7a2761d

File tree

4 files changed

+57
-24
lines changed

4 files changed

+57
-24
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
* [#1001](https://github.com/intridea/grape/pull/1001): Fixed calling endpoint with specified format with format in its path - [@hodak](https://github.com/hodak).
2222
* [#1005](https://github.com/intridea/grape/pull/1005): Fixed the Grape::Middleware::Globals - [@urkle](https://github.com/urkle).
2323
* [#1012](https://github.com/intridea/grape/pull/1012): Fixed `allow_blank: false` with a Boolean value of `false` - [@mfunaro](https://github.com/mfunaro).
24+
* [#1023](https://github.com/intridea/grape/issues/1023): Fixes unexpected beahvior with `present` and an object that responds to `merge` but isn't a Hash - [@dblock](https://github.com/dblock).
2425
* Your contribution here.
2526

2627
0.11.0 (2/23/2015)

UPGRADING.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ Upgrading Grape
33

44
### Upgrading to >= 0.12.0
55

6+
#### Changes in present
7+
8+
Using `present` with objects that responded to `merge` would cause early evaluation of the represented object, with unexpected side-effects, such as missing parameters or environment within rendering code. Grape now only merges represented objects with a previously rendered body, usually when multiple `present` calls are made in the same route.
9+
10+
See [grape-with-roar#5](https://github.com/dblock/grape-with-roar/issues/5) and [#1023](https://github.com/intridea/grape/issues/1023).
11+
612
#### Changes to regexp validator
713

814
Parameters with `nil` value will now pass `regexp` validation. To disallow `nil` value for an endpoint, add `allow_blank: false`.

lib/grape/dsl/inside_route.rb

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,18 +196,17 @@ def present(*args)
196196
root = options.delete(:root)
197197

198198
representation = if entity_class
199-
embeds = { env: env }
200-
embeds[:version] = env['api.version'] if env['api.version']
201-
entity_class.represent(object, embeds.merge(options))
199+
entity_representation_for(entity_class, object, options)
202200
else
203201
object
204202
end
205203

206204
representation = { root => representation } if root
207205
if key
208206
representation = (@body || {}).merge(key => representation)
209-
elsif entity_class.present? && representation.respond_to?('merge')
210-
representation = (@body || {}).merge(representation)
207+
elsif entity_class.present? && @body
208+
fail ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?('merge')
209+
representation = @body.merge(representation)
211210
end
212211

213212
body representation
@@ -245,6 +244,12 @@ def entity_class_for_obj(object, options)
245244

246245
entity_class
247246
end
247+
248+
def entity_representation_for(entity_class, object, options)
249+
embeds = { env: env }
250+
embeds[:version] = env['api.version'] if env['api.version']
251+
entity_class.represent(object, embeds.merge(options))
252+
end
248253
end
249254
end
250255
end

spec/grape/dsl/inside_route_spec.rb

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -239,30 +239,51 @@ def initialize
239239
end
240240
end
241241

242-
describe 'with' do
243-
describe 'multiple entities' do
244-
let(:entity_mock1) do
245-
entity_mock1 = Object.new
246-
allow(entity_mock1).to receive(:represent).and_return(dummy1: 'dummy1')
247-
entity_mock1
242+
describe 'multiple entities' do
243+
let(:entity_mock1) do
244+
entity_mock1 = Object.new
245+
allow(entity_mock1).to receive(:represent).and_return(dummy1: 'dummy1')
246+
entity_mock1
247+
end
248+
249+
let(:entity_mock2) do
250+
entity_mock2 = Object.new
251+
allow(entity_mock2).to receive(:represent).and_return(dummy2: 'dummy2')
252+
entity_mock2
253+
end
254+
255+
describe 'instance' do
256+
before do
257+
subject.present 'dummy1', with: entity_mock1
258+
subject.present 'dummy2', with: entity_mock2
248259
end
249260

250-
let(:entity_mock2) do
251-
entity_mock2 = Object.new
252-
allow(entity_mock2).to receive(:represent).and_return(dummy2: 'dummy2')
253-
entity_mock2
261+
it 'presents both dummy objects' do
262+
expect(subject.body[:dummy1]).to eq 'dummy1'
263+
expect(subject.body[:dummy2]).to eq 'dummy2'
254264
end
265+
end
266+
end
255267

256-
describe 'instance' do
257-
before do
258-
subject.present 'dummy1', with: entity_mock1
259-
subject.present 'dummy2', with: entity_mock2
260-
end
268+
describe 'non mergeable entity' do
269+
let(:entity_mock1) do
270+
entity_mock1 = Object.new
271+
allow(entity_mock1).to receive(:represent).and_return(dummy1: 'dummy1')
272+
entity_mock1
273+
end
261274

262-
it 'presents both dummy objects' do
263-
expect(subject.body[:dummy1]).to eq 'dummy1'
264-
expect(subject.body[:dummy2]).to eq 'dummy2'
265-
end
275+
let(:entity_mock2) do
276+
entity_mock2 = Object.new
277+
allow(entity_mock2).to receive(:represent).and_return('not a hash')
278+
entity_mock2
279+
end
280+
281+
describe 'instance' do
282+
it 'fails' do
283+
subject.present 'dummy1', with: entity_mock1
284+
expect {
285+
subject.present 'dummy2', with: entity_mock2
286+
}.to raise_error ArgumentError, 'Representation of type String cannot be merged.'
266287
end
267288
end
268289
end

0 commit comments

Comments
 (0)