Skip to content

Commit 2c37a87

Browse files
ahmednaguibLeFnord
andauthored
Add default value option (#352)
Co-authored-by: peter scholz <[email protected]>
1 parent 1ddf61d commit 2c37a87

File tree

5 files changed

+146
-1
lines changed

5 files changed

+146
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
* Your contribution here.
66

7+
* [#352](https://github.com/ruby-grape/grape-entity/pull/352): Add Default value option - [@ahmednaguib](https://github.com/ahmednaguib).
8+
79
#### Fixes
810

911
* Your contribution here.

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
- [Aliases](#aliases)
2525
- [Format Before Exposing](#format-before-exposing)
2626
- [Expose Nil](#expose-nil)
27+
- [Default Value](#default-value)
2728
- [Documentation](#documentation)
2829
- [Options Hash](#options-hash)
2930
- [Passing Additional Option To Nested Exposure](#passing-additional-option-to-nested-exposure)
@@ -482,6 +483,19 @@ module Entities
482483
end
483484
```
484485

486+
#### Default Value
487+
488+
This option can be used to provide a default value in case the return value is nil or empty.
489+
490+
```ruby
491+
module Entities
492+
class MyModel < Grape::Entity
493+
expose :name, default: ''
494+
expose :age, default: 60
495+
end
496+
end
497+
```
498+
485499
#### Documentation
486500

487501
Expose documentation with the field. Gets bubbled up when used with Grape and various API documentation systems.

lib/grape_entity/entity.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ def to_xml(options = {})
585585
merge
586586
expose_nil
587587
override
588+
default
588589
].to_set.freeze
589590

590591
# Merges the given options with current block options.

lib/grape_entity/exposure/base.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def initialize(attribute, options, conditions)
1616
key = options[:as] || attribute
1717
@key = key.respond_to?(:to_sym) ? key.to_sym : key
1818
@is_safe = options[:safe]
19+
@default_value = options[:default]
1920
@for_merge = options[:merge]
2021
@attr_path_proc = options[:attr_path]
2122
@documentation = options[:documentation]
@@ -82,7 +83,10 @@ def serializable_value(entity, options)
8283
end
8384

8485
def valid_value(entity, options)
85-
value(entity, options) if valid?(entity)
86+
return unless valid?(entity)
87+
88+
output = value(entity, options)
89+
output.blank? && @default_value.present? ? @default_value : output
8690
end
8791

8892
def should_return_key?(options)

spec/grape_entity/entity_spec.rb

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,130 @@ def initialize(a, b, c)
212212
end
213213
end
214214

215+
context 'with :default option' do
216+
let(:a) { nil }
217+
let(:b) { nil }
218+
let(:c) { 'value' }
219+
220+
context 'when model is a PORO' do
221+
let(:model) { Model.new(a, b, c) }
222+
223+
before do
224+
stub_const 'Model', Class.new
225+
Model.class_eval do
226+
attr_accessor :a, :b, :c
227+
228+
def initialize(a, b, c)
229+
@a = a
230+
@b = b
231+
@c = c
232+
end
233+
end
234+
end
235+
236+
context 'when default option is not provided' do
237+
it 'exposes attributes values' do
238+
subject.expose(:a)
239+
subject.expose(:b)
240+
subject.expose(:c)
241+
expect(subject.represent(model).serializable_hash).to eq(a: nil, b: nil, c: 'value')
242+
end
243+
end
244+
245+
context 'when default option is set' do
246+
it 'exposes default values for attributes' do
247+
subject.expose(:a, default: 'a')
248+
subject.expose(:b, default: 'b')
249+
subject.expose(:c, default: 'c')
250+
expect(subject.represent(model).serializable_hash).to eq(a: 'a', b: 'b', c: 'value')
251+
end
252+
end
253+
254+
context 'when default option is set and block passed' do
255+
it 'return default value if block returns nil' do
256+
subject.expose(:a, default: 'a') do |_obj, _options|
257+
nil
258+
end
259+
subject.expose(:b)
260+
subject.expose(:c)
261+
expect(subject.represent(model).serializable_hash).to eq(a: 'a', b: nil, c: 'value')
262+
end
263+
264+
it 'return value from block if block returns a value' do
265+
subject.expose(:a, default: 'a') do |_obj, _options|
266+
100
267+
end
268+
subject.expose(:b)
269+
subject.expose(:c)
270+
expect(subject.represent(model).serializable_hash).to eq(a: 100, b: nil, c: 'value')
271+
end
272+
end
273+
end
274+
275+
context 'when model is a hash' do
276+
let(:model) { { a: a, b: b, c: c } }
277+
278+
context 'when expose_nil option is not provided' do
279+
it 'exposes nil attributes' do
280+
subject.expose(:a)
281+
subject.expose(:b)
282+
subject.expose(:c)
283+
expect(subject.represent(model).serializable_hash).to eq(a: nil, b: nil, c: 'value')
284+
end
285+
end
286+
287+
context 'when expose_nil option is true' do
288+
it 'exposes nil attributes' do
289+
subject.expose(:a, expose_nil: true)
290+
subject.expose(:b, expose_nil: true)
291+
subject.expose(:c)
292+
expect(subject.represent(model).serializable_hash).to eq(a: nil, b: nil, c: 'value')
293+
end
294+
end
295+
296+
context 'when expose_nil option is false' do
297+
it 'does not expose nil attributes' do
298+
subject.expose(:a, expose_nil: false)
299+
subject.expose(:b, expose_nil: false)
300+
subject.expose(:c)
301+
expect(subject.represent(model).serializable_hash).to eq(c: 'value')
302+
end
303+
304+
it 'is only applied per attribute' do
305+
subject.expose(:a, expose_nil: false)
306+
subject.expose(:b)
307+
subject.expose(:c)
308+
expect(subject.represent(model).serializable_hash).to eq(b: nil, c: 'value')
309+
end
310+
311+
it 'raises an error when applied to multiple attribute exposures' do
312+
expect { subject.expose(:a, :b, :c, expose_nil: false) }.to raise_error ArgumentError
313+
end
314+
end
315+
end
316+
317+
context 'with nested structures' do
318+
let(:model) { { a: a, b: b, c: { d: nil, e: nil, f: { g: nil, h: nil } } } }
319+
320+
context 'when expose_nil option is false' do
321+
it 'does not expose nil attributes' do
322+
subject.expose(:a, expose_nil: false)
323+
subject.expose(:b)
324+
subject.expose(:c) do
325+
subject.expose(:d, expose_nil: false)
326+
subject.expose(:e)
327+
subject.expose(:f) do
328+
subject.expose(:g, expose_nil: false)
329+
subject.expose(:h)
330+
end
331+
end
332+
333+
expect(subject.represent(model).serializable_hash).to eq(b: nil, c: { e: nil, f: { h: nil } })
334+
end
335+
end
336+
end
337+
end
338+
215339
context 'with a block' do
216340
it 'errors out if called with multiple attributes' do
217341
expect { subject.expose(:name, :email) { true } }.to raise_error ArgumentError

0 commit comments

Comments
 (0)