grape-entity 0.8.2 → 0.10.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 +4 -4
- data/.github/dependabot.yml +14 -0
- data/.github/workflows/rubocop.yml +26 -0
- data/.github/workflows/ruby.yml +26 -0
- data/.rspec +2 -1
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +25 -0
- data/Gemfile +1 -1
- data/README.md +67 -3
- data/UPGRADING.md +2 -14
- data/grape-entity.gemspec +1 -1
- data/lib/grape_entity/delegator/base.rb +5 -0
- data/lib/grape_entity/delegator.rb +8 -4
- data/lib/grape_entity/entity.rb +4 -8
- data/lib/grape_entity/exposure/base.rb +5 -1
- data/lib/grape_entity/exposure/nesting_exposure/output_builder.rb +0 -2
- data/lib/grape_entity/exposure.rb +6 -1
- data/lib/grape_entity/version.rb +1 -1
- data/spec/grape_entity/entity_spec.rb +137 -3
- data/spec/grape_entity/hash_spec.rb +15 -1
- metadata +7 -5
- data/.travis.yml +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e3db71fd839585d80faac0980ec028b1204def600b6ce36101e2986e0be3cbf
|
4
|
+
data.tar.gz: 7bfcb78548991ed3ab73bfc1f23a51e1e32c2af7ccba472e87b9a767a9499c79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ba1bf0a642f56275e8279b34d433591a21c7849ede46d0718fd6496831d4f5b5bd676bda0af6bf435beebe5c717ace6c0b53c1c90db92fdecccb8d88cdbe044
|
7
|
+
data.tar.gz: c06a650f7c29dd918fa8b83ab62dd04b91448bcddc440a43e48d055f5499982b6c4532f18abc7429f71f464895b083f8e551748c9d9fdf0f8072c2cd1af0e438
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
3
|
+
# Please see the documentation for all configuration options:
|
4
|
+
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
5
|
+
|
6
|
+
version: 2
|
7
|
+
updates:
|
8
|
+
- package-ecosystem: "bundler" # See documentation for possible values
|
9
|
+
directory: "/" # Location of package manifests
|
10
|
+
schedule:
|
11
|
+
interval: "weekly"
|
12
|
+
day: "friday"
|
13
|
+
assignees:
|
14
|
+
- "LeFnord"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
name: Rubocop
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- '*'
|
7
|
+
pull_request:
|
8
|
+
branches:
|
9
|
+
- '*'
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
rubocop:
|
13
|
+
name: Rubocop
|
14
|
+
runs-on: ubuntu-latest
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v2
|
17
|
+
- uses: actions/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: '3.0'
|
20
|
+
- run: gem install rubocop --no-doc
|
21
|
+
- run: rubocop --format progress --format json --out rubocop.json
|
22
|
+
id: rubocop
|
23
|
+
- uses: duderman/rubocop-annotate-action@v0.1.0
|
24
|
+
with:
|
25
|
+
path: rubocop.json
|
26
|
+
if: ${{ failure() }}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- '*'
|
7
|
+
pull_request:
|
8
|
+
branches:
|
9
|
+
- '*'
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
spec:
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
strategy:
|
15
|
+
matrix:
|
16
|
+
ruby-version: ['2.6', '2.7', '3.0', head, jruby, truffleruby]
|
17
|
+
|
18
|
+
steps:
|
19
|
+
- uses: actions/checkout@v2
|
20
|
+
- name: Set up Ruby
|
21
|
+
uses: ruby/setup-ruby@v1
|
22
|
+
with:
|
23
|
+
ruby-version: ${{ matrix.ruby-version }}
|
24
|
+
bundler-cache: true
|
25
|
+
- name: Run rspec
|
26
|
+
run: bundle exec rspec
|
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -9,6 +9,31 @@
|
|
9
9
|
* Your contribution here.
|
10
10
|
|
11
11
|
|
12
|
+
### 0.10.1 (2021-10-22)
|
13
|
+
|
14
|
+
#### Fixes
|
15
|
+
|
16
|
+
* [#359](https://github.com/ruby-grape/grape-entity/pull/359): Respect `hash_access` setting when using `expose_nil: false` option - [@magni-](https://github.com/magni-).
|
17
|
+
|
18
|
+
|
19
|
+
### 0.10.0 (2021-09-15)
|
20
|
+
|
21
|
+
#### Features
|
22
|
+
|
23
|
+
* [#352](https://github.com/ruby-grape/grape-entity/pull/352): Add Default value option - [@ahmednaguib](https://github.com/ahmednaguib).
|
24
|
+
|
25
|
+
#### Fixes
|
26
|
+
|
27
|
+
* [#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).
|
28
|
+
|
29
|
+
|
30
|
+
### 0.9.0 (2021-03-20)
|
31
|
+
|
32
|
+
#### Features
|
33
|
+
|
34
|
+
* [#346](https://github.com/ruby-grape/grape-entity/pull/346): Ruby 3 support - [@LeFnord](https://github.com/LeFnord).
|
35
|
+
|
36
|
+
|
12
37
|
### 0.8.2 (2020-11-08)
|
13
38
|
|
14
39
|
#### Fixes
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,10 +1,47 @@
|
|
1
|
-
# Grape::Entity
|
2
|
-
|
3
1
|
[](http://badge.fury.io/rb/grape-entity)
|
4
|
-
|
2
|
+

|
5
3
|
[](https://coveralls.io/github/ruby-grape/grape-entity?branch=master)
|
6
4
|
[](https://codeclimate.com/github/ruby-grape/grape-entity)
|
7
5
|
|
6
|
+
# Table of Contents
|
7
|
+
|
8
|
+
- [Grape::Entity](#grapeentity)
|
9
|
+
- [Introduction](#introduction)
|
10
|
+
- [Example](#example)
|
11
|
+
- [Reusable Responses with Entities](#reusable-responses-with-entities)
|
12
|
+
- [Defining Entities](#defining-entities)
|
13
|
+
- [Basic Exposure](#basic-exposure)
|
14
|
+
- [Exposing with a Presenter](#exposing-with-a-presenter)
|
15
|
+
- [Conditional Exposure](#conditional-exposure)
|
16
|
+
- [Safe Exposure](#safe-exposure)
|
17
|
+
- [Nested Exposure](#nested-exposure)
|
18
|
+
- [Collection Exposure](#collection-exposure)
|
19
|
+
- [Merge Fields](#merge-fields)
|
20
|
+
- [Runtime Exposure](#runtime-exposure)
|
21
|
+
- [Unexpose](#unexpose)
|
22
|
+
- [Overriding exposures](#overriding-exposures)
|
23
|
+
- [Returning only the fields you want](#returning-only-the-fields-you-want)
|
24
|
+
- [Aliases](#aliases)
|
25
|
+
- [Format Before Exposing](#format-before-exposing)
|
26
|
+
- [Expose Nil](#expose-nil)
|
27
|
+
- [Default Value](#default-value)
|
28
|
+
- [Documentation](#documentation)
|
29
|
+
- [Options Hash](#options-hash)
|
30
|
+
- [Passing Additional Option To Nested Exposure](#passing-additional-option-to-nested-exposure)
|
31
|
+
- [Attribute Path Tracking](#attribute-path-tracking)
|
32
|
+
- [Using the Exposure DSL](#using-the-exposure-dsl)
|
33
|
+
- [Using Entities](#using-entities)
|
34
|
+
- [Entity Organization](#entity-organization)
|
35
|
+
- [Caveats](#caveats)
|
36
|
+
- [Installation](#installation)
|
37
|
+
- [Testing with Entities](#testing-with-entities)
|
38
|
+
- [Project Resources](#project-resources)
|
39
|
+
- [Contributing](#contributing)
|
40
|
+
- [License](#license)
|
41
|
+
- [Copyright](#copyright)
|
42
|
+
|
43
|
+
# Grape::Entity
|
44
|
+
|
8
45
|
## Introduction
|
9
46
|
|
10
47
|
This gem adds Entity support to API frameworks, such as [Grape](https://github.com/ruby-grape/grape). Grape's Entity is an API focused facade that sits on top of an object model.
|
@@ -74,6 +111,20 @@ The field lookup takes several steps
|
|
74
111
|
* next try `object.fetch(exposure)`
|
75
112
|
* last raise an Exception
|
76
113
|
|
114
|
+
`exposure` is a Symbol by default. If `object` is a Hash with stringified keys, you can set the hash accessor at the entity-class level to properly expose its members:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
class Status < GrapeEntity
|
118
|
+
self.hash_access = :to_s
|
119
|
+
|
120
|
+
expose :code
|
121
|
+
expose :message
|
122
|
+
end
|
123
|
+
|
124
|
+
Status.represent({ 'code' => 418, 'message' => "I'm a teapot" }).as_json
|
125
|
+
#=> { code: 418, message: "I'm a teapot" }
|
126
|
+
```
|
127
|
+
|
77
128
|
#### Exposing with a Presenter
|
78
129
|
|
79
130
|
Don't derive your model classes from `Grape::Entity`, expose them using a presenter.
|
@@ -446,6 +497,19 @@ module Entities
|
|
446
497
|
end
|
447
498
|
```
|
448
499
|
|
500
|
+
#### Default Value
|
501
|
+
|
502
|
+
This option can be used to provide a default value in case the return value is nil or empty.
|
503
|
+
|
504
|
+
```ruby
|
505
|
+
module Entities
|
506
|
+
class MyModel < Grape::Entity
|
507
|
+
expose :name, default: ''
|
508
|
+
expose :age, default: 60
|
509
|
+
end
|
510
|
+
end
|
511
|
+
```
|
512
|
+
|
449
513
|
#### Documentation
|
450
514
|
|
451
515
|
Expose documentation with the field. Gets bubbled up when used with Grape and various API documentation systems.
|
data/UPGRADING.md
CHANGED
@@ -1,22 +1,10 @@
|
|
1
1
|
# Upgrading Grape Entity
|
2
2
|
|
3
|
-
### Upgrading to >= 0.8.2
|
4
|
-
|
5
|
-
In Ruby 3.0: the block handling will be changed
|
6
|
-
[language-changes point 3, Proc](https://github.com/ruby/ruby/blob/v3_0_0_preview1/NEWS.md#language-changes).
|
7
|
-
This:
|
8
|
-
```ruby
|
9
|
-
expose :that_method_without_args, &:method_without_args
|
10
|
-
```
|
11
|
-
will be deprecated.
|
12
|
-
|
13
|
-
Prefer to use this pattern for simple setting a value
|
14
|
-
```ruby
|
15
|
-
expose :method_without_args, as: :that_method_without_args
|
16
|
-
```
|
17
3
|
|
18
4
|
### Upgrading to >= 0.8.2
|
19
5
|
|
6
|
+
Official support for ruby < 2.5 removed, ruby 2.5 only in testing mode, but no support.
|
7
|
+
|
20
8
|
In Ruby 3.0: the block handling will be changed
|
21
9
|
[language-changes point 3, Proc](https://github.com/ruby/ruby/blob/v3_0_0_preview1/NEWS.md#language-changes).
|
22
10
|
This:
|
data/grape-entity.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.description = 'Extracted from Grape, A Ruby framework for rapid API development with great conventions.'
|
15
15
|
s.license = 'MIT'
|
16
16
|
|
17
|
-
s.required_ruby_version = '>= 2.
|
17
|
+
s.required_ruby_version = '>= 2.5'
|
18
18
|
|
19
19
|
s.add_runtime_dependency 'activesupport', '>= 3.0.0'
|
20
20
|
# FIXME: remove dependecy
|
@@ -11,10 +11,14 @@ module Grape
|
|
11
11
|
module Delegator
|
12
12
|
def self.new(object)
|
13
13
|
delegator_klass =
|
14
|
-
if object.is_a?(Hash)
|
15
|
-
|
16
|
-
elsif object.
|
17
|
-
|
14
|
+
if object.is_a?(Hash)
|
15
|
+
HashObject
|
16
|
+
elsif defined?(OpenStruct) && object.is_a?(OpenStruct)
|
17
|
+
OpenStructObject
|
18
|
+
elsif object.respond_to?(:fetch, true)
|
19
|
+
FetchableObject
|
20
|
+
else
|
21
|
+
PlainObject
|
18
22
|
end
|
19
23
|
|
20
24
|
delegator_klass.new(object)
|
data/lib/grape_entity/entity.rb
CHANGED
@@ -122,8 +122,6 @@ module Grape
|
|
122
122
|
case value
|
123
123
|
when :to_s, :str, :string
|
124
124
|
:to_s
|
125
|
-
when :to_sym, :sym, :symbol
|
126
|
-
:to_sym
|
127
125
|
else
|
128
126
|
:to_sym
|
129
127
|
end
|
@@ -155,7 +153,7 @@ module Grape
|
|
155
153
|
#
|
156
154
|
# @example as: a proc or lambda
|
157
155
|
#
|
158
|
-
# object = OpenStruct(
|
156
|
+
# object = OpenStruct(awesomeness: 'awesome_key', awesome: 'not-my-key', other: 'other-key' )
|
159
157
|
#
|
160
158
|
# class MyEntity < Grape::Entity
|
161
159
|
# expose :awesome, as: proc { object.awesomeness }
|
@@ -483,9 +481,6 @@ module Grape
|
|
483
481
|
@object = object
|
484
482
|
@options = options.is_a?(Options) ? options : Options.new(options)
|
485
483
|
@delegator = Delegator.new(object)
|
486
|
-
|
487
|
-
# Why not `arity > 1`? It might be negative https://ruby-doc.org/core-2.6.6/Method.html#method-i-arity
|
488
|
-
@delegator_accepts_opts = @delegator.method(:delegate).arity != 1
|
489
484
|
end
|
490
485
|
|
491
486
|
def root_exposures
|
@@ -529,7 +524,7 @@ module Grape
|
|
529
524
|
# it handles: https://github.com/ruby/ruby/blob/v3_0_0_preview1/NEWS.md#language-changes point 3, Proc
|
530
525
|
raise Grape::Entity::Deprecated.new e.message, 'in ruby 3.0' if e.is_a?(ArgumentError)
|
531
526
|
|
532
|
-
raise e
|
527
|
+
raise e
|
533
528
|
end
|
534
529
|
|
535
530
|
def exec_with_attribute(attribute, &block)
|
@@ -543,7 +538,7 @@ module Grape
|
|
543
538
|
def delegate_attribute(attribute)
|
544
539
|
if is_defined_in_entity?(attribute)
|
545
540
|
send(attribute)
|
546
|
-
elsif
|
541
|
+
elsif delegator.accepts_options?
|
547
542
|
delegator.delegate(attribute, **self.class.delegation_opts)
|
548
543
|
else
|
549
544
|
delegator.delegate(attribute)
|
@@ -587,6 +582,7 @@ module Grape
|
|
587
582
|
merge
|
588
583
|
expose_nil
|
589
584
|
override
|
585
|
+
default
|
590
586
|
].to_set.freeze
|
591
587
|
|
592
588
|
# 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)
|
@@ -51,7 +51,6 @@ module Grape
|
|
51
51
|
output
|
52
52
|
end
|
53
53
|
|
54
|
-
# rubocop:disable Lint/EmptyBlock
|
55
54
|
# In case if we want to solve collisions providing lambda to :merge option
|
56
55
|
def merge_strategy(for_merge)
|
57
56
|
if for_merge.respond_to? :call
|
@@ -60,7 +59,6 @@ module Grape
|
|
60
59
|
-> {}
|
61
60
|
end
|
62
61
|
end
|
63
|
-
# rubocop:enable Lint/EmptyBlock
|
64
62
|
end
|
65
63
|
end
|
66
64
|
end
|
@@ -56,7 +56,12 @@ module Grape
|
|
56
56
|
Condition.new_unless(
|
57
57
|
proc do |object, _options|
|
58
58
|
if options[:proc].nil?
|
59
|
-
Delegator.new(object)
|
59
|
+
delegator = Delegator.new(object)
|
60
|
+
if is_a?(Grape::Entity) && delegator.accepts_options?
|
61
|
+
delegator.delegate(attribute, **self.class.delegation_opts).nil?
|
62
|
+
else
|
63
|
+
delegator.delegate(attribute).nil?
|
64
|
+
end
|
60
65
|
else
|
61
66
|
exec_with_object(options, &options[:proc]).nil?
|
62
67
|
end
|
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(
|
@@ -17,7 +17,7 @@ describe Grape::Entity do
|
|
17
17
|
expose :post, if: :full
|
18
18
|
expose :city
|
19
19
|
expose :street
|
20
|
-
expose :house
|
20
|
+
expose :house, expose_nil: false
|
21
21
|
end
|
22
22
|
|
23
23
|
class Company < Grape::Entity
|
@@ -62,9 +62,23 @@ describe Grape::Entity do
|
|
62
62
|
}
|
63
63
|
}
|
64
64
|
|
65
|
+
company_without_house_with_string = {
|
66
|
+
'full_name' => 'full_name',
|
67
|
+
'name' => 'name',
|
68
|
+
'address' => {
|
69
|
+
'post' => '123456',
|
70
|
+
'city' => 'city',
|
71
|
+
'street' => 'street',
|
72
|
+
'something_else' => 'something_else'
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
65
76
|
expect(EntitySpec::CompanyWithString.represent(company_with_string).serializable_hash).to eq \
|
66
77
|
company.slice(:name).merge(address: company[:address].slice(:city, :street, :house))
|
67
78
|
|
79
|
+
expect(EntitySpec::CompanyWithString.represent(company_without_house_with_string).serializable_hash).to eq \
|
80
|
+
company.slice(:name).merge(address: company[:address].slice(:city, :street))
|
81
|
+
|
68
82
|
expect(EntitySpec::CompanyWithString.represent(company_with_string, full: true).serializable_hash).to eq \
|
69
83
|
company.slice(:full_name, :name).merge(address: company[:address].slice(:city, :street, :house))
|
70
84
|
|
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.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Bleigh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-10-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -159,11 +159,13 @@ extensions: []
|
|
159
159
|
extra_rdoc_files: []
|
160
160
|
files:
|
161
161
|
- ".coveralls.yml"
|
162
|
+
- ".github/dependabot.yml"
|
163
|
+
- ".github/workflows/rubocop.yml"
|
164
|
+
- ".github/workflows/ruby.yml"
|
162
165
|
- ".gitignore"
|
163
166
|
- ".rspec"
|
164
167
|
- ".rubocop.yml"
|
165
168
|
- ".rubocop_todo.yml"
|
166
|
-
- ".travis.yml"
|
167
169
|
- ".yardopts"
|
168
170
|
- CHANGELOG.md
|
169
171
|
- CONTRIBUTING.md
|
@@ -223,14 +225,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
223
225
|
requirements:
|
224
226
|
- - ">="
|
225
227
|
- !ruby/object:Gem::Version
|
226
|
-
version: '2.
|
228
|
+
version: '2.5'
|
227
229
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
228
230
|
requirements:
|
229
231
|
- - ">="
|
230
232
|
- !ruby/object:Gem::Version
|
231
233
|
version: '0'
|
232
234
|
requirements: []
|
233
|
-
rubygems_version: 3.2.
|
235
|
+
rubygems_version: 3.2.22
|
234
236
|
signing_key:
|
235
237
|
specification_version: 4
|
236
238
|
summary: A simple facade for managing the relationship between your model and API.
|
data/.travis.yml
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
|
3
|
-
before_install:
|
4
|
-
- gem install bundler
|
5
|
-
|
6
|
-
after_success:
|
7
|
-
- bundle exec danger
|
8
|
-
|
9
|
-
rvm:
|
10
|
-
- 2.5.8
|
11
|
-
- 2.6.6
|
12
|
-
- 2.7.2
|
13
|
-
- ruby-head
|
14
|
-
- jruby-head
|
15
|
-
- truffleruby-head
|
16
|
-
- 2.4.10
|
17
|
-
|
18
|
-
|
19
|
-
matrix:
|
20
|
-
fast_finish: true
|
21
|
-
|
22
|
-
allow_failures:
|
23
|
-
- rvm: 2.4.10
|
24
|
-
- rvm: ruby-head
|
25
|
-
- rvm: jruby-head
|
26
|
-
- rvm: truffleruby-head
|