grape-entity 0.8.1 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb3365afa36454c797c3d44adfa51ecc757a105fe6cb6644f3f3896ff06af48d
4
- data.tar.gz: 1e3c91d73fb17727c931443910b2f602e037384eb4f04e055207bbaf471c3962
3
+ metadata.gz: 9e3db71fd839585d80faac0980ec028b1204def600b6ce36101e2986e0be3cbf
4
+ data.tar.gz: 7bfcb78548991ed3ab73bfc1f23a51e1e32c2af7ccba472e87b9a767a9499c79
5
5
  SHA512:
6
- metadata.gz: ba4bd4207a7c5f5f09806017b29723ba0f3ed0fc749cd360d55ebba9a41465165f91a7ae974d6af4baed71109c2ad5b7f19514a6f510953fe3733b46d35f405e
7
- data.tar.gz: 4ac4db65df766dd004c35e926a6a43e08023d86b4477d6e396b8c9ce9b125ee889bbbe86fecf7f6f4a60f847c2ae5d38beb829ce017957737de7a52f0ab802d3
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
@@ -1,2 +1,3 @@
1
- --format documentation
2
1
  --color
2
+ --profile
3
+ --format documentation
data/.rubocop.yml CHANGED
@@ -4,7 +4,9 @@ AllCops:
4
4
  Exclude:
5
5
  - vendor/**/*
6
6
  - example/**/*
7
- TargetRubyVersion: 2.7
7
+ NewCops: enable
8
+ TargetRubyVersion: 3.0
9
+ SuggestExtensions: false
8
10
 
9
11
  # Layout stuff
10
12
  #
@@ -27,25 +29,18 @@ Layout/SpaceAroundMethodCallOperator:
27
29
 
28
30
  # Lint stuff
29
31
  #
30
- Lint/DeprecatedOpenSSLConstant:
31
- Enabled: true
32
-
33
- Lint/DuplicateElsifCondition:
34
- Enabled: true
35
-
36
- Lint/MixedRegexpCaptureTypes:
37
- Enabled: true
38
-
39
- Lint/RaiseException:
40
- Enabled: true
41
-
42
- Lint/StructNewOverride:
32
+ Lint/ConstantDefinitionInBlock:
43
33
  Enabled: true
34
+ Exclude:
35
+ - spec/**/*
44
36
 
45
37
  # Metrics stuff
46
38
  #
47
39
  Metrics/AbcSize:
48
40
  Max: 25
41
+ IgnoredMethods:
42
+ # from lib/grape_entity/exposure/nesting_exposure.rb
43
+ - 'normalized_exposures'
49
44
 
50
45
  Metrics/BlockLength:
51
46
  Exclude:
@@ -64,6 +59,12 @@ Metrics/MethodLength:
64
59
 
65
60
  Metrics/PerceivedComplexity:
66
61
  Max: 11
62
+ IgnoredMethods:
63
+ # from lib/grape_entity/entity.rb
64
+ - 'expose'
65
+ - 'merge_options'
66
+ # from lib/grape_entity/exposure/nesting_exposure.rb
67
+ - 'normalized_exposures'
67
68
 
68
69
  # Naming stuff
69
70
  #
@@ -73,56 +74,13 @@ Naming:
73
74
 
74
75
  # Style stuff
75
76
  #
76
- Style/AccessorGrouping:
77
- Enabled: true
78
-
79
- Style/ArrayCoercion:
80
- Enabled: true
81
-
82
- Style/BisectedAttrAccessor:
83
- Enabled: true
84
-
85
- Style/CaseLikeIf:
86
- Enabled: true
87
-
88
77
  Style/Documentation:
89
78
  Enabled: false
90
79
 
91
- Style/ExponentialNotation:
92
- Enabled: true
93
-
94
- Style/HashAsLastArrayItem:
95
- Enabled: true
96
-
97
- Style/HashEachMethods:
98
- Enabled: true
99
-
100
- Style/HashLikeCase:
101
- Enabled: true
102
-
103
- Style/HashTransformKeys:
104
- Enabled: true
105
-
106
- Style/HashTransformValues:
107
- Enabled: true
108
-
109
- Style/RedundantAssignment:
110
- Enabled: true
111
-
112
- Style/RedundantFetchBlock:
113
- Enabled: true
114
-
115
- Style/RedundantFileExtensionInRequire:
116
- Enabled: true
117
-
118
- Style/RedundantRegexpCharacterClass:
119
- Enabled: true
120
-
121
- Style/RedundantRegexpEscape:
122
- Enabled: true
123
-
124
- Style/RegexpLiteral:
125
- Enabled: false
126
-
127
- Style/SlicingWithRange:
128
- Enabled: true
80
+ Style/OptionalBooleanParameter:
81
+ AllowedMethods:
82
+ # from lib/grape_entity/condition/base.rb
83
+ - 'initialize'
84
+ # form lib/grape_entity/entity.rb
85
+ - 'entity_class'
86
+ - 'present_collection'
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2020-02-18 16:38:42 +0100 using RuboCop version 0.79.0.
3
+ # on 2020-11-07 00:01:40 UTC using RuboCop version 1.2.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -14,17 +14,11 @@ Gemspec/RequiredRubyVersion:
14
14
  - 'grape-entity.gemspec'
15
15
 
16
16
  # Offense count: 6
17
+ # Cop supports --auto-correct.
17
18
  Lint/BooleanSymbol:
18
19
  Exclude:
19
20
  - 'spec/grape_entity/exposure_spec.rb'
20
21
 
21
- # Offense count: 1
22
- # Configuration parameters: EnforcedStyle.
23
- # SupportedStyles: inline, group
24
- Style/AccessModifierDeclarations:
25
- Exclude:
26
- - 'spec/grape_entity/entity_spec.rb'
27
-
28
22
  # Offense count: 1
29
23
  # Cop supports --auto-correct.
30
24
  # Configuration parameters: IgnoredMethods.
data/CHANGELOG.md CHANGED
@@ -8,6 +8,40 @@
8
8
 
9
9
  * Your contribution here.
10
10
 
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
+
37
+ ### 0.8.2 (2020-11-08)
38
+
39
+ #### Fixes
40
+
41
+ * [#340](https://github.com/ruby-grape/grape-entity/pull/340): Preparations for 3.0 - [@LeFnord](https://github.com/LeFnord).
42
+ * [#338](https://github.com/ruby-grape/grape-entity/pull/338): Fix ruby 2.7 deprecation warning - [@begotten63](https://github.com/begotten63).
43
+
44
+
11
45
  ### 0.8.1 (2020-07-15)
12
46
 
13
47
  #### Fixes
@@ -15,6 +49,7 @@
15
49
  * [#336](https://github.com/ruby-grape/grape-entity/pull/336): Pass options to delegators when they accept it - [@dnesteryuk](https://github.com/dnesteryuk).
16
50
  * [#333](https://github.com/ruby-grape/grape-entity/pull/333): Fix typo in CHANGELOG.md - [@eitoball](https://github.com/eitoball).
17
51
 
52
+
18
53
  ### 0.8.0 (2020-02-18)
19
54
 
20
55
  #### Features
@@ -30,12 +65,14 @@
30
65
  * [#320](https://github.com/ruby-grape/grape-entity/pull/320): Gemspec: drop eol'd property rubyforge_project - [@olleolleolle](https://github.com/olleolleolle).
31
66
  * [#307](https://github.com/ruby-grape/grape-entity/pull/307): Allow exposures to call methods defined in modules included in an entity - [@robertoz-01](https://github.com/robertoz-01).
32
67
 
68
+
33
69
  ### 0.7.1 (2018-01-30)
34
70
 
35
71
  #### Features
36
72
 
37
73
  * [#297](https://github.com/ruby-grape/grape-entity/pull/297): Introduce `override` option for expose (fixes [#286](https://github.com/ruby-grape/grape-entity/issues/296)) - [@DmitryTsepelev](https://github.com/DmitryTsepelev).
38
74
 
75
+
39
76
  ### 0.7.0 (2018-01-25)
40
77
 
41
78
  #### Features
@@ -54,6 +91,7 @@
54
91
  * [#291](https://github.com/ruby-grape/grape-entity/pull/291): Refactor and simplify various classes and modules - [@DmitryTsepelev](https://github.com/DmitryTsepelev).
55
92
  * [#292](https://github.com/ruby-grape/grape-entity/pull/292): Allow replace non-conditional non-nesting exposures in child classes (fixes [#286](https://github.com/ruby-grape/grape-entity/issues/286)) - [@DmitryTsepelev](https://github.com/DmitryTsepelev).
56
93
 
94
+
57
95
  ### 0.6.1 (2017-01-09)
58
96
 
59
97
  #### Features
@@ -64,6 +102,7 @@
64
102
 
65
103
  * [#251](https://github.com/ruby-grape/grape-entity/pull/251): Avoid noise when code runs with Ruby warnings - [@cpetschnig](https://github.com/cpetschnig).
66
104
 
105
+
67
106
  ### 0.6.0 (2016-11-20)
68
107
 
69
108
  #### Features
@@ -75,6 +114,7 @@
75
114
  * [#249](https://github.com/ruby-grape/grape-entity/issues/249): Fix leaking of options and internals in default serialization - [@dblock](https://github.com/dblock), [@KingsleyKelly](https://github.com/KingsleyKelly).
76
115
  * [#248](https://github.com/ruby-grape/grape-entity/pull/248): Fix `nil` values causing errors when `merge` option passed - [@arempe93](https://github.com/arempe93).
77
116
 
117
+
78
118
  ### 0.5.2 (2016-11-14)
79
119
 
80
120
  #### Features
@@ -90,6 +130,7 @@
90
130
  * [#219](https://github.com/ruby-grape/grape-entity/pull/219): Double pass options in `serializable_hash` - [@sbatykov](https://github.com/sbatykov).
91
131
  * [#231](https://github.com/ruby-grape/grape-entity/pull/231), [#215](https://github.com/ruby-grape/grape-entity/issues/215): Allow `delegate_attribute` for derived entity - [@sbatykov](https://github.com/sbatykov).
92
132
 
133
+
93
134
  ### 0.5.1 (2016-4-4)
94
135
 
95
136
  #### Features
@@ -101,6 +142,7 @@
101
142
 
102
143
  * [#202](https://github.com/ruby-grape/grape-entity/pull/202): Reset `@using_class` memoization on `.setup` - [@rngtng](https://github.com/rngtng).
103
144
 
145
+
104
146
  ### 0.5.0 (2015-12-07)
105
147
 
106
148
  #### Features
@@ -121,18 +163,21 @@
121
163
  * [#151](https://github.com/ruby-grape/grape-entity/pull/151): Serializing of deeply nested presenter exposures: [#155](https://github.com/ruby-grape/grape-entity/issues/155) - [@marshall-lee](https://github.com/marshall-lee).
122
164
  * [#151](https://github.com/ruby-grape/grape-entity/pull/151): Deep projections (`:only`, `:except`) were unaware of nesting: [#156](https://github.com/ruby-grape/grape-entity/issues/156) - [@marshall-lee](https://github.com/marshall-lee).
123
165
 
166
+
124
167
  ### 0.4.8 (2015-08-10)
125
168
 
126
169
  #### Features
127
170
 
128
171
  * [#167](https://github.com/ruby-grape/grape-entity/pull/167), [#166](https://github.com/ruby-grape/grape-entity/issues/166): Regression with global settings (exposures, formatters) on `Grape::Entity` - [@marshall-lee](https://github.com/marshall-lee).
129
172
 
173
+
130
174
  ### 0.4.7 (2015-08-03)
131
175
 
132
176
  #### Features
133
177
 
134
178
  * [#164](https://github.com/ruby-grape/grape-entity/pull/164): Regression: entity instance methods were exposed with `NoMethodError`: [#163](https://github.com/ruby-grape/grape-entity/issues/163) - [@marshall-lee](https://github.com/marshall-lee).
135
179
 
180
+
136
181
  ### 0.4.6 (2015-07-27)
137
182
 
138
183
  #### Features
@@ -151,6 +196,7 @@
151
196
 
152
197
  * [#147](https://github.com/ruby-grape/grape-entity/pull/147), [#142](https://github.com/ruby-grape/grape-entity/pull/142): Private method values were not exposed with `safe` option - [@marshall-lee](https://github.com/marshall-lee).
153
198
 
199
+
154
200
  ### 0.4.5 (2015-03-10)
155
201
 
156
202
  #### Features
@@ -165,6 +211,7 @@
165
211
  * [#110](https://github.com/ruby-grape/grape-entity/pull/110): Safe exposure when using `Hash` models - [@croeck](https://github.com/croeck).
166
212
  * [#91](https://github.com/ruby-grape/grape-entity/pull/91): OpenStruct serializing - [@etehtsea](https://github.com/etehtsea).
167
213
 
214
+
168
215
  ### 0.4.4 (2014-08-17)
169
216
 
170
217
  #### Features
@@ -172,6 +219,7 @@
172
219
  * [#85](https://github.com/ruby-grape/grape-entity/pull/85): Added `present_collection` to indicate that an `Entity` presents an entire Collection - [@dspaeth-faber](https://github.com/dspaeth-faber).
173
220
  * [#85](https://github.com/ruby-grape/grape-entity/pull/85): Hashes can now be passed as object to be presented and the `Hash` keys can be referenced by expose - [@dspaeth-faber](https://github.com/dspaeth-faber).
174
221
 
222
+
175
223
  ### 0.4.3 (2014-06-12)
176
224
 
177
225
  #### Features
@@ -182,6 +230,7 @@
182
230
 
183
231
  * [#77](https://github.com/ruby-grape/grape-entity/pull/77): Compatibility with Rspec 3 - [@justfalter](https://github.com/justfalter).
184
232
 
233
+
185
234
  ### 0.4.2 (2014-04-03)
186
235
 
187
236
  #### Features
@@ -189,12 +238,14 @@
189
238
  * [#60](https://github.com/ruby-grape/grape-entity/issues/59): Performance issues introduced by nested exposures - [@AlexYankee](https://github.com/AlexYankee).
190
239
  * [#60](https://github.com/ruby-grape/grape-entity/issues/57): Nested exposure double-exposes a field - [@AlexYankee](https://github.com/AlexYankee).
191
240
 
241
+
192
242
  ### 0.4.1 (2014-02-13)
193
243
 
194
244
  #### Fixes
195
245
 
196
246
  * [#54](https://github.com/ruby-grape/grape-entity/issues/54): Fix: undefined method `to_set` - [@aj0strow](https://github.com/aj0strow).
197
247
 
248
+
198
249
  ### 0.4.0 (2014-01-27)
199
250
 
200
251
  #### Features
@@ -214,6 +265,7 @@
214
265
  * [#51](https://github.com/ruby-grape/grape-entity/pull/51): Raise `ArgumentError` if an unknown option is used with `expose` - [@aj0strow](https://github.com/aj0strow).
215
266
  * [#51](https://github.com/ruby-grape/grape-entity/pull/51): Alias `:with` to `:using`, consistently with the Grape api endpoints - [@aj0strow](https://github.com/aj0strow).
216
267
 
268
+
217
269
  ### 0.3.0 (2013-03-29)
218
270
 
219
271
  #### Features
@@ -222,12 +274,14 @@
222
274
  * The `instance.entity` method now optionally accepts `options` - [@mbleigh](https://github.com/mbleigh).
223
275
  * You can pass symbols to `:if` and `:unless` to simply check for truthiness/falsiness of the specified options key - [@mbleigh](https://github.com/mbleigh).
224
276
 
277
+
225
278
  ### 0.2.0 (2013-01-11)
226
279
 
227
280
  #### Features
228
281
 
229
282
  * Moved the namespace back to `Grape::Entity` to preserve compatibility with Grape - [@dblock](https://github.com/dblock).
230
283
 
284
+
231
285
  ### 0.1.0 (2013-01-11)
232
286
 
233
287
  * Initial public release - [@agileanimal](https://github.com/agileanimal).
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ source 'http://rubygems.org'
5
5
  gemspec
6
6
 
7
7
  group :development, :test do
8
- gem 'rubocop', '~> 0.88.0', require: false
8
+ gem 'rubocop', '~> 1.0', require: false
9
9
  end
10
10
 
11
11
  group :test do
@@ -15,6 +15,6 @@ group :test do
15
15
  gem 'guard-bundler'
16
16
  gem 'guard-rspec'
17
17
  gem 'rb-fsevent'
18
- gem 'ruby-grape-danger', '~> 0.1.1', require: false
18
+ gem 'ruby-grape-danger', '~> 0.2', require: false
19
19
  gem 'simplecov', require: false
20
20
  end
data/README.md CHANGED
@@ -1,10 +1,47 @@
1
- # Grape::Entity
2
-
3
1
  [![Gem Version](http://img.shields.io/gem/v/grape-entity.svg)](http://badge.fury.io/rb/grape-entity)
4
- [![Build Status](http://img.shields.io/travis/ruby-grape/grape-entity.svg)](https://travis-ci.org/ruby-grape/grape-entity)
2
+ ![Ruby](https://github.com/ruby-grape/grape-entity/workflows/Ruby/badge.svg)
5
3
  [![Coverage Status](https://coveralls.io/repos/github/ruby-grape/grape-entity/badge.svg?branch=master)](https://coveralls.io/github/ruby-grape/grape-entity?branch=master)
6
4
  [![Code Climate](https://codeclimate.com/github/ruby-grape/grape-entity.svg)](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,5 +1,22 @@
1
- Upgrading Grape Entity
2
- ===============
1
+ # Upgrading Grape Entity
2
+
3
+
4
+ ### Upgrading to >= 0.8.2
5
+
6
+ Official support for ruby < 2.5 removed, ruby 2.5 only in testing mode, but no support.
7
+
8
+ In Ruby 3.0: the block handling will be changed
9
+ [language-changes point 3, Proc](https://github.com/ruby/ruby/blob/v3_0_0_preview1/NEWS.md#language-changes).
10
+ This:
11
+ ```ruby
12
+ expose :that_method_without_args, &:method_without_args
13
+ ```
14
+ will be deprecated.
15
+
16
+ Prefer to use this pattern for simple setting a value
17
+ ```ruby
18
+ expose :method_without_args, as: :that_method_without_args
19
+ ```
3
20
 
4
21
  ### Upgrading to >= 0.6.0
5
22
 
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.4'
17
+ s.required_ruby_version = '>= 2.5'
18
18
 
19
19
  s.add_runtime_dependency 'activesupport', '>= 3.0.0'
20
20
  # FIXME: remove dependecy
@@ -21,7 +21,7 @@ module Grape
21
21
  end
22
22
 
23
23
  def met?(entity, options)
24
- !@inverse ? if_value(entity, options) : unless_value(entity, options)
24
+ @inverse ? unless_value(entity, options) : if_value(entity, options)
25
25
  end
26
26
 
27
27
  def if_value(_entity, _options)
@@ -13,6 +13,11 @@ module Grape
13
13
  def delegatable?(_attribute)
14
14
  true
15
15
  end
16
+
17
+ def accepts_options?
18
+ # Why not `arity > 1`? It might be negative https://ruby-doc.org/core-2.6.6/Method.html#method-i-arity
19
+ method(:delegate).arity != 1
20
+ end
16
21
  end
17
22
  end
18
23
  end
@@ -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) then HashObject
15
- elsif defined?(OpenStruct) && object.is_a?(OpenStruct) then OpenStructObject
16
- elsif object.respond_to?(:fetch, true) then FetchableObject
17
- else PlainObject
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)
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ class Entity
5
+ class Deprecated < StandardError
6
+ def initialize(msg, spec)
7
+ message = "DEPRECATED #{spec}: #{msg}"
8
+
9
+ super(message)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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
@@ -139,6 +137,8 @@ module Grape
139
137
  def self.inherited(subclass)
140
138
  subclass.root_exposure = root_exposure.dup
141
139
  subclass.formatters = formatters.dup
140
+
141
+ super
142
142
  end
143
143
 
144
144
  # This method is the primary means by which you will declare what attributes
@@ -153,7 +153,7 @@ module Grape
153
153
  #
154
154
  # @example as: a proc or lambda
155
155
  #
156
- # object = OpenStruct(awesomness: 'awesome_key', awesome: 'not-my-key', other: 'other-key' )
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 }
@@ -481,9 +481,6 @@ module Grape
481
481
  @object = object
482
482
  @options = options.is_a?(Options) ? options : Options.new(options)
483
483
  @delegator = Delegator.new(object)
484
-
485
- # Why not `arity > 1`? It might be negative https://ruby-doc.org/core-2.6.6/Method.html#method-i-arity
486
- @delegator_accepts_opts = @delegator.method(:delegate).arity != 1
487
484
  end
488
485
 
489
486
  def root_exposures
@@ -523,6 +520,11 @@ module Grape
523
520
  else
524
521
  instance_exec(object, options, &block)
525
522
  end
523
+ rescue StandardError => e
524
+ # it handles: https://github.com/ruby/ruby/blob/v3_0_0_preview1/NEWS.md#language-changes point 3, Proc
525
+ raise Grape::Entity::Deprecated.new e.message, 'in ruby 3.0' if e.is_a?(ArgumentError)
526
+
527
+ raise e
526
528
  end
527
529
 
528
530
  def exec_with_attribute(attribute, &block)
@@ -536,8 +538,8 @@ module Grape
536
538
  def delegate_attribute(attribute)
537
539
  if is_defined_in_entity?(attribute)
538
540
  send(attribute)
539
- elsif @delegator_accepts_opts
540
- delegator.delegate(attribute, self.class.delegation_opts)
541
+ elsif delegator.accepts_options?
542
+ delegator.delegate(attribute, **self.class.delegation_opts)
541
543
  else
542
544
  delegator.delegate(attribute)
543
545
  end
@@ -580,6 +582,7 @@ module Grape
580
582
  merge
581
583
  expose_nil
582
584
  override
585
+ default
583
586
  ].to_set.freeze
584
587
 
585
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
- 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
86
90
  end
87
91
 
88
92
  def should_return_key?(options)
@@ -113,11 +117,9 @@ module Grape
113
117
  @key.respond_to?(:call) ? entity.exec_with_object(@options, &@key) : @key
114
118
  end
115
119
 
116
- def with_attr_path(entity, options)
120
+ def with_attr_path(entity, options, &block)
117
121
  path_part = attr_path(entity, options)
118
- options.with_attr_path(path_part) do
119
- yield
120
- end
122
+ options.with_attr_path(path_part, &block)
121
123
  end
122
124
 
123
125
  def override?
@@ -36,6 +36,7 @@ module Grape
36
36
  @exposures.clear
37
37
  end
38
38
 
39
+ # rubocop:disable Style/DocumentDynamicEvalDefinition
39
40
  %i[
40
41
  each
41
42
  to_ary to_a
@@ -55,6 +56,7 @@ module Grape
55
56
  end
56
57
  RUBY
57
58
  end
59
+ # rubocop:enable Style/DocumentDynamicEvalDefinition
58
60
 
59
61
  # Determine if we have any nesting exposures with the same name.
60
62
  def deep_complex_nesting?(entity)
@@ -9,6 +9,8 @@ module Grape
9
9
  @entity = entity
10
10
  @output_hash = {}
11
11
  @output_collection = []
12
+
13
+ super
12
14
  end
13
15
 
14
16
  def add(exposure, result)
@@ -91,6 +91,7 @@ module Grape
91
91
  output[exposure.key(entity)] ||= []
92
92
  output[exposure.key(entity)] << exposure
93
93
  end
94
+
94
95
  table.map do |key, exposures|
95
96
  last_exposure = exposures.last
96
97
 
@@ -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).delegate(attribute).nil?
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrapeEntity
4
- VERSION = '0.8.1'
4
+ VERSION = '0.10.1'
5
5
  end
data/lib/grape_entity.rb CHANGED
@@ -9,3 +9,4 @@ require 'grape_entity/entity'
9
9
  require 'grape_entity/delegator'
10
10
  require 'grape_entity/exposure'
11
11
  require 'grape_entity/options'
12
+ require 'grape_entity/deprecated'
@@ -212,6 +212,130 @@ describe Grape::Entity do
212
212
  end
213
213
  end
214
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
+
215
339
  context 'with a block' do
216
340
  it 'errors out if called with multiple attributes' do
217
341
  expect { subject.expose(:name, :email) { true } }.to raise_error ArgumentError
@@ -260,27 +384,50 @@ describe Grape::Entity do
260
384
  end
261
385
  end
262
386
 
263
- context 'with block passed via &' do
264
- it 'with does not pass options when block is passed via &' do
265
- class SomeObject
266
- def method_without_args
267
- 'result'
268
- end
387
+ describe 'blocks' do
388
+ class SomeObject
389
+ def method_without_args
390
+ 'result'
269
391
  end
392
+ end
270
393
 
271
- subject.expose :that_method_without_args do |object|
272
- object.method_without_args
394
+ describe 'with block passed in' do
395
+ specify do
396
+ subject.expose :that_method_without_args do |object|
397
+ object.method_without_args
398
+ end
399
+
400
+ object = SomeObject.new
401
+
402
+ value = subject.represent(object).value_for(:that_method_without_args)
403
+ expect(value).to eq('result')
273
404
  end
405
+ end
274
406
 
275
- subject.expose :that_method_without_args_again, &:method_without_args
407
+ context 'with block passed in via &' do
408
+ if RUBY_VERSION.start_with?('3')
409
+ specify do
410
+ subject.expose :that_method_without_args, &:method_without_args
411
+ subject.expose :method_without_args, as: :that_method_without_args_again
276
412
 
277
- object = SomeObject.new
413
+ object = SomeObject.new
414
+ expect do
415
+ subject.represent(object).value_for(:that_method_without_args)
416
+ end.to raise_error Grape::Entity::Deprecated
278
417
 
279
- value = subject.represent(object).value_for(:that_method_without_args)
280
- expect(value).to eq('result')
418
+ value2 = subject.represent(object).value_for(:that_method_without_args_again)
419
+ expect(value2).to eq('result')
420
+ end
421
+ else
422
+ specify do
423
+ subject.expose :that_method_without_args_again, &:method_without_args
424
+
425
+ object = SomeObject.new
281
426
 
282
- value2 = subject.represent(object).value_for(:that_method_without_args_again)
283
- expect(value2).to eq('result')
427
+ value2 = subject.represent(object).value_for(:that_method_without_args_again)
428
+ expect(value2).to eq('result')
429
+ end
430
+ end
284
431
  end
285
432
  end
286
433
 
@@ -1112,6 +1259,18 @@ describe Grape::Entity do
1112
1259
  expect(representation).to eq(id: nil, name: nil, user: { id: nil, name: nil, email: nil })
1113
1260
  end
1114
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
1115
1274
  end
1116
1275
  end
1117
1276
 
@@ -1556,7 +1715,7 @@ describe Grape::Entity do
1556
1715
  end
1557
1716
 
1558
1717
  fresh_class.class_eval do
1559
- expose :characteristics, using: EntitySpec::NoPathCharacterEntity, attr_path: proc { nil }
1718
+ expose :characteristics, using: EntitySpec::NoPathCharacterEntity, attr_path: proc {}
1560
1719
  end
1561
1720
 
1562
1721
  expect(subject.serializable_hash).to eq(
@@ -1693,10 +1852,12 @@ describe Grape::Entity do
1693
1852
  end
1694
1853
  end
1695
1854
 
1855
+ # rubocop:disable Lint/EmptyBlock
1696
1856
  fresh_class.class_eval do
1697
1857
  expose :first_friend, using: EntitySpec::FriendEntity do |_user, _opts|
1698
1858
  end
1699
1859
  end
1860
+ # rubocop:enable Lint/EmptyBlock
1700
1861
 
1701
1862
  rep = subject.value_for(:first_friend)
1702
1863
  expect(rep).to be_kind_of EntitySpec::FriendEntity
@@ -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
 
data/spec/spec_helper.rb CHANGED
@@ -13,7 +13,7 @@ SimpleCov.start do
13
13
  add_filter 'spec/'
14
14
  end
15
15
 
16
- Coveralls.wear!
16
+ Coveralls.wear! unless RUBY_PLATFORM.eql? 'java'
17
17
 
18
18
  $LOAD_PATH.unshift(File.dirname(__FILE__))
19
19
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
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.8.1
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: 2020-07-15 00:00:00.000000000 Z
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
@@ -190,6 +192,7 @@ files:
190
192
  - lib/grape_entity/delegator/hash_object.rb
191
193
  - lib/grape_entity/delegator/openstruct_object.rb
192
194
  - lib/grape_entity/delegator/plain_object.rb
195
+ - lib/grape_entity/deprecated.rb
193
196
  - lib/grape_entity/entity.rb
194
197
  - lib/grape_entity/exposure.rb
195
198
  - lib/grape_entity/exposure/base.rb
@@ -222,14 +225,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
222
225
  requirements:
223
226
  - - ">="
224
227
  - !ruby/object:Gem::Version
225
- version: '2.4'
228
+ version: '2.5'
226
229
  required_rubygems_version: !ruby/object:Gem::Requirement
227
230
  requirements:
228
231
  - - ">="
229
232
  - !ruby/object:Gem::Version
230
233
  version: '0'
231
234
  requirements: []
232
- rubygems_version: 3.1.4
235
+ rubygems_version: 3.2.22
233
236
  signing_key:
234
237
  specification_version: 4
235
238
  summary: A simple facade for managing the relationship between your model and API.
data/.travis.yml DELETED
@@ -1,25 +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.1
13
- - ruby-head
14
- - jruby-head
15
-
16
- matrix:
17
- fast_finish: true
18
-
19
- include:
20
- - rvm: 2.4.10
21
-
22
- allow_failures:
23
- - rvm: 2.4.10
24
- - rvm: ruby-head
25
- - rvm: jruby-head