grape-entity 0.9.0 → 0.10.0
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 +11 -0
- data/README.md +14 -0
- data/lib/grape_entity/entity.rb +3 -2
- data/lib/grape_entity/exposure/base.rb +5 -1
- data/lib/grape_entity/version.rb +1 -1
- data/spec/grape_entity/entity_spec.rb +137 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c0080bab47d3cd3667c29ef580dcca4977ba5572d95c56304dd55cc444456fc
|
4
|
+
data.tar.gz: 40b564ee118de1a2a5b36dcff7526ca6bb1c54d738b7ac2191b1a208a583add1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06564de07c6a909a3dbd4c3839902f529c5c44291f1a0669bcbc3dca7ea1e015db71887ca9f46132ef5bf4bb01fd10fced74b09ac392f3678903d39875816b63
|
7
|
+
data.tar.gz: 3e22b1382012b543d50a256e1792f9f79e3b02d49e42420353a351d49f549f021cd3dede90c576f657f0e61df862c0d8879cfb9c0a1d4578ef77a5648845ff6e
|
data/CHANGELOG.md
CHANGED
@@ -9,6 +9,17 @@
|
|
9
9
|
* Your contribution here.
|
10
10
|
|
11
11
|
|
12
|
+
### 0.10.0 (2021-09-15)
|
13
|
+
|
14
|
+
#### Features
|
15
|
+
|
16
|
+
* [#352](https://github.com/ruby-grape/grape-entity/pull/352): Add Default value option - [@ahmednaguib](https://github.com/ahmednaguib).
|
17
|
+
|
18
|
+
#### Fixes
|
19
|
+
|
20
|
+
* [#355](https://github.com/ruby-grape/grape-entity/pull/355): Fix infinite loop problem with the `NameErrors` in block exposures - [@meinac](https://github.com/meinac).
|
21
|
+
|
22
|
+
|
12
23
|
### 0.9.0 (2021-03-20)
|
13
24
|
|
14
25
|
#### Features
|
data/README.md
CHANGED
@@ -24,6 +24,7 @@
|
|
24
24
|
- [Aliases](#aliases)
|
25
25
|
- [Format Before Exposing](#format-before-exposing)
|
26
26
|
- [Expose Nil](#expose-nil)
|
27
|
+
- [Default Value](#default-value)
|
27
28
|
- [Documentation](#documentation)
|
28
29
|
- [Options Hash](#options-hash)
|
29
30
|
- [Passing Additional Option To Nested Exposure](#passing-additional-option-to-nested-exposure)
|
@@ -482,6 +483,19 @@ module Entities
|
|
482
483
|
end
|
483
484
|
```
|
484
485
|
|
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
|
+
|
485
499
|
#### Documentation
|
486
500
|
|
487
501
|
Expose documentation with the field. Gets bubbled up when used with Grape and various API documentation systems.
|
data/lib/grape_entity/entity.rb
CHANGED
@@ -153,7 +153,7 @@ module Grape
|
|
153
153
|
#
|
154
154
|
# @example as: a proc or lambda
|
155
155
|
#
|
156
|
-
# object = OpenStruct(
|
156
|
+
# object = OpenStruct(awesomeness: 'awesome_key', awesome: 'not-my-key', other: 'other-key' )
|
157
157
|
#
|
158
158
|
# class MyEntity < Grape::Entity
|
159
159
|
# expose :awesome, as: proc { object.awesomeness }
|
@@ -527,7 +527,7 @@ module Grape
|
|
527
527
|
# it handles: https://github.com/ruby/ruby/blob/v3_0_0_preview1/NEWS.md#language-changes point 3, Proc
|
528
528
|
raise Grape::Entity::Deprecated.new e.message, 'in ruby 3.0' if e.is_a?(ArgumentError)
|
529
529
|
|
530
|
-
raise e
|
530
|
+
raise e
|
531
531
|
end
|
532
532
|
|
533
533
|
def exec_with_attribute(attribute, &block)
|
@@ -585,6 +585,7 @@ module Grape
|
|
585
585
|
merge
|
586
586
|
expose_nil
|
587
587
|
override
|
588
|
+
default
|
588
589
|
].to_set.freeze
|
589
590
|
|
590
591
|
# Merges the given options with current block options.
|
@@ -16,6 +16,7 @@ module Grape
|
|
16
16
|
key = options[:as] || attribute
|
17
17
|
@key = key.respond_to?(:to_sym) ? key.to_sym : key
|
18
18
|
@is_safe = options[:safe]
|
19
|
+
@default_value = options[:default]
|
19
20
|
@for_merge = options[:merge]
|
20
21
|
@attr_path_proc = options[:attr_path]
|
21
22
|
@documentation = options[:documentation]
|
@@ -82,7 +83,10 @@ module Grape
|
|
82
83
|
end
|
83
84
|
|
84
85
|
def valid_value(entity, options)
|
85
|
-
|
86
|
+
return unless valid?(entity)
|
87
|
+
|
88
|
+
output = value(entity, options)
|
89
|
+
output.blank? && @default_value.present? ? @default_value : output
|
86
90
|
end
|
87
91
|
|
88
92
|
def should_return_key?(options)
|
data/lib/grape_entity/version.rb
CHANGED
@@ -30,9 +30,7 @@ describe Grape::Entity do
|
|
30
30
|
|
31
31
|
it 'makes sure that :format_with as a proc cannot be used with a block' do
|
32
32
|
# rubocop:disable Style/BlockDelimiters
|
33
|
-
# rubocop:disable Lint/EmptyBlock
|
34
33
|
expect { subject.expose :name, format_with: proc {} do p 'hi' end }.to raise_error ArgumentError
|
35
|
-
# rubocop:enable Lint/EmptyBlock
|
36
34
|
# rubocop:enable Style/BlockDelimiters
|
37
35
|
end
|
38
36
|
|
@@ -214,6 +212,130 @@ describe Grape::Entity do
|
|
214
212
|
end
|
215
213
|
end
|
216
214
|
|
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
|
+
|
217
339
|
context 'with a block' do
|
218
340
|
it 'errors out if called with multiple attributes' do
|
219
341
|
expect { subject.expose(:name, :email) { true } }.to raise_error ArgumentError
|
@@ -1137,6 +1259,18 @@ describe Grape::Entity do
|
|
1137
1259
|
expect(representation).to eq(id: nil, name: nil, user: { id: nil, name: nil, email: nil })
|
1138
1260
|
end
|
1139
1261
|
end
|
1262
|
+
|
1263
|
+
context 'when NameError happens in a parameterized block_exposure' do
|
1264
|
+
before do
|
1265
|
+
subject.expose :raise_no_method_error do |_|
|
1266
|
+
foo
|
1267
|
+
end
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
it 'does not cause infinite loop' do
|
1271
|
+
expect { subject.represent({}, serializable: true) }.to raise_error(NameError)
|
1272
|
+
end
|
1273
|
+
end
|
1140
1274
|
end
|
1141
1275
|
end
|
1142
1276
|
|
@@ -1581,7 +1715,7 @@ describe Grape::Entity do
|
|
1581
1715
|
end
|
1582
1716
|
|
1583
1717
|
fresh_class.class_eval do
|
1584
|
-
expose :characteristics, using: EntitySpec::NoPathCharacterEntity, attr_path: proc {
|
1718
|
+
expose :characteristics, using: EntitySpec::NoPathCharacterEntity, attr_path: proc {}
|
1585
1719
|
end
|
1586
1720
|
|
1587
1721
|
expect(subject.serializable_hash).to eq(
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape-entity
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Bleigh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -232,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
232
232
|
- !ruby/object:Gem::Version
|
233
233
|
version: '0'
|
234
234
|
requirements: []
|
235
|
-
rubygems_version: 3.2.
|
235
|
+
rubygems_version: 3.2.22
|
236
236
|
signing_key:
|
237
237
|
specification_version: 4
|
238
238
|
summary: A simple facade for managing the relationship between your model and API.
|