grape-entity 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 619c4d253ef6fbfde4f5c3bff0a0a309a67ef6ce49d408de9f3912bf12c4f690
4
- data.tar.gz: e4c3f773ba0b6c1751abdbc0642e730c325be409b6d0d55dae1c1b2ce075b7e2
3
+ metadata.gz: 7c0080bab47d3cd3667c29ef580dcca4977ba5572d95c56304dd55cc444456fc
4
+ data.tar.gz: 40b564ee118de1a2a5b36dcff7526ca6bb1c54d738b7ac2191b1a208a583add1
5
5
  SHA512:
6
- metadata.gz: 8937886f91fe82d37eb9a8eca7b6d4542b8dcb12b8ea241d8b08c4116305e351c3acc980722fe60dead828140c6c56425763854287b6e0736134e994da2b1f5e
7
- data.tar.gz: 3073993606e0f950e39ed8417bbc33aaff7d21544bbdb3797b3e0325ee01c0c8f71c9a63295630af21006698fae585b0bd1a5139dd6241221e2aac07442f1ac6
6
+ metadata.gz: 06564de07c6a909a3dbd4c3839902f529c5c44291f1a0669bcbc3dca7ea1e015db71887ca9f46132ef5bf4bb01fd10fced74b09ac392f3678903d39875816b63
7
+ data.tar.gz: 3e22b1382012b543d50a256e1792f9f79e3b02d49e42420353a351d49f549f021cd3dede90c576f657f0e61df862c0d8879cfb9c0a1d4578ef77a5648845ff6e
@@ -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/.rubocop.yml CHANGED
@@ -4,11 +4,18 @@ 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
 
11
+ # Layout stuff
12
+ #
9
13
  Layout/EmptyLinesAroundArguments:
10
14
  Enabled: false
11
15
 
16
+ Layout/EmptyLinesAroundAttributeAccessor:
17
+ Enabled: true
18
+
12
19
  Layout/FirstHashElementIndentation:
13
20
  EnforcedStyle: consistent
14
21
 
@@ -17,15 +24,30 @@ Layout/LineLength:
17
24
  Exclude:
18
25
  - spec/**/*
19
26
 
27
+ Layout/SpaceAroundMethodCallOperator:
28
+ Enabled: true
29
+
30
+ # Lint stuff
31
+ #
32
+ Lint/ConstantDefinitionInBlock:
33
+ Enabled: true
34
+ Exclude:
35
+ - spec/**/*
36
+
37
+ # Metrics stuff
38
+ #
20
39
  Metrics/AbcSize:
21
40
  Max: 25
41
+ IgnoredMethods:
42
+ # from lib/grape_entity/exposure/nesting_exposure.rb
43
+ - 'normalized_exposures'
22
44
 
23
45
  Metrics/BlockLength:
24
46
  Exclude:
25
47
  - spec/**/*
26
48
 
27
49
  Metrics/CyclomaticComplexity:
28
- Max: 10
50
+ Max: 13
29
51
 
30
52
  Metrics/ClassLength:
31
53
  Max: 300
@@ -37,12 +59,28 @@ Metrics/MethodLength:
37
59
 
38
60
  Metrics/PerceivedComplexity:
39
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'
68
+
69
+ # Naming stuff
70
+ #
40
71
 
41
72
  Naming:
42
73
  Enabled: false
43
74
 
75
+ # Style stuff
76
+ #
44
77
  Style/Documentation:
45
78
  Enabled: false
46
79
 
47
- Style/RegexpLiteral:
48
- Enabled: false
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,7 +8,42 @@
8
8
 
9
9
  * Your contribution here.
10
10
 
11
- ### 0.9.0 (2020-02-18)
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
+
23
+ ### 0.9.0 (2021-03-20)
24
+
25
+ #### Features
26
+
27
+ * [#346](https://github.com/ruby-grape/grape-entity/pull/346): Ruby 3 support - [@LeFnord](https://github.com/LeFnord).
28
+
29
+
30
+ ### 0.8.2 (2020-11-08)
31
+
32
+ #### Fixes
33
+
34
+ * [#340](https://github.com/ruby-grape/grape-entity/pull/340): Preparations for 3.0 - [@LeFnord](https://github.com/LeFnord).
35
+ * [#338](https://github.com/ruby-grape/grape-entity/pull/338): Fix ruby 2.7 deprecation warning - [@begotten63](https://github.com/begotten63).
36
+
37
+
38
+ ### 0.8.1 (2020-07-15)
39
+
40
+ #### Fixes
41
+
42
+ * [#336](https://github.com/ruby-grape/grape-entity/pull/336): Pass options to delegators when they accept it - [@dnesteryuk](https://github.com/dnesteryuk).
43
+ * [#333](https://github.com/ruby-grape/grape-entity/pull/333): Fix typo in CHANGELOG.md - [@eitoball](https://github.com/eitoball).
44
+
45
+
46
+ ### 0.8.0 (2020-02-18)
12
47
 
13
48
  #### Features
14
49
 
@@ -23,12 +58,14 @@
23
58
  * [#320](https://github.com/ruby-grape/grape-entity/pull/320): Gemspec: drop eol'd property rubyforge_project - [@olleolleolle](https://github.com/olleolleolle).
24
59
  * [#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).
25
60
 
61
+
26
62
  ### 0.7.1 (2018-01-30)
27
63
 
28
64
  #### Features
29
65
 
30
66
  * [#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).
31
67
 
68
+
32
69
  ### 0.7.0 (2018-01-25)
33
70
 
34
71
  #### Features
@@ -47,6 +84,7 @@
47
84
  * [#291](https://github.com/ruby-grape/grape-entity/pull/291): Refactor and simplify various classes and modules - [@DmitryTsepelev](https://github.com/DmitryTsepelev).
48
85
  * [#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).
49
86
 
87
+
50
88
  ### 0.6.1 (2017-01-09)
51
89
 
52
90
  #### Features
@@ -57,6 +95,7 @@
57
95
 
58
96
  * [#251](https://github.com/ruby-grape/grape-entity/pull/251): Avoid noise when code runs with Ruby warnings - [@cpetschnig](https://github.com/cpetschnig).
59
97
 
98
+
60
99
  ### 0.6.0 (2016-11-20)
61
100
 
62
101
  #### Features
@@ -68,6 +107,7 @@
68
107
  * [#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).
69
108
  * [#248](https://github.com/ruby-grape/grape-entity/pull/248): Fix `nil` values causing errors when `merge` option passed - [@arempe93](https://github.com/arempe93).
70
109
 
110
+
71
111
  ### 0.5.2 (2016-11-14)
72
112
 
73
113
  #### Features
@@ -83,6 +123,7 @@
83
123
  * [#219](https://github.com/ruby-grape/grape-entity/pull/219): Double pass options in `serializable_hash` - [@sbatykov](https://github.com/sbatykov).
84
124
  * [#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).
85
125
 
126
+
86
127
  ### 0.5.1 (2016-4-4)
87
128
 
88
129
  #### Features
@@ -94,6 +135,7 @@
94
135
 
95
136
  * [#202](https://github.com/ruby-grape/grape-entity/pull/202): Reset `@using_class` memoization on `.setup` - [@rngtng](https://github.com/rngtng).
96
137
 
138
+
97
139
  ### 0.5.0 (2015-12-07)
98
140
 
99
141
  #### Features
@@ -114,18 +156,21 @@
114
156
  * [#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).
115
157
  * [#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).
116
158
 
159
+
117
160
  ### 0.4.8 (2015-08-10)
118
161
 
119
162
  #### Features
120
163
 
121
164
  * [#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).
122
165
 
166
+
123
167
  ### 0.4.7 (2015-08-03)
124
168
 
125
169
  #### Features
126
170
 
127
171
  * [#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).
128
172
 
173
+
129
174
  ### 0.4.6 (2015-07-27)
130
175
 
131
176
  #### Features
@@ -144,6 +189,7 @@
144
189
 
145
190
  * [#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).
146
191
 
192
+
147
193
  ### 0.4.5 (2015-03-10)
148
194
 
149
195
  #### Features
@@ -158,6 +204,7 @@
158
204
  * [#110](https://github.com/ruby-grape/grape-entity/pull/110): Safe exposure when using `Hash` models - [@croeck](https://github.com/croeck).
159
205
  * [#91](https://github.com/ruby-grape/grape-entity/pull/91): OpenStruct serializing - [@etehtsea](https://github.com/etehtsea).
160
206
 
207
+
161
208
  ### 0.4.4 (2014-08-17)
162
209
 
163
210
  #### Features
@@ -165,6 +212,7 @@
165
212
  * [#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).
166
213
  * [#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).
167
214
 
215
+
168
216
  ### 0.4.3 (2014-06-12)
169
217
 
170
218
  #### Features
@@ -175,6 +223,7 @@
175
223
 
176
224
  * [#77](https://github.com/ruby-grape/grape-entity/pull/77): Compatibility with Rspec 3 - [@justfalter](https://github.com/justfalter).
177
225
 
226
+
178
227
  ### 0.4.2 (2014-04-03)
179
228
 
180
229
  #### Features
@@ -182,12 +231,14 @@
182
231
  * [#60](https://github.com/ruby-grape/grape-entity/issues/59): Performance issues introduced by nested exposures - [@AlexYankee](https://github.com/AlexYankee).
183
232
  * [#60](https://github.com/ruby-grape/grape-entity/issues/57): Nested exposure double-exposes a field - [@AlexYankee](https://github.com/AlexYankee).
184
233
 
234
+
185
235
  ### 0.4.1 (2014-02-13)
186
236
 
187
237
  #### Fixes
188
238
 
189
239
  * [#54](https://github.com/ruby-grape/grape-entity/issues/54): Fix: undefined method `to_set` - [@aj0strow](https://github.com/aj0strow).
190
240
 
241
+
191
242
  ### 0.4.0 (2014-01-27)
192
243
 
193
244
  #### Features
@@ -207,6 +258,7 @@
207
258
  * [#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).
208
259
  * [#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).
209
260
 
261
+
210
262
  ### 0.3.0 (2013-03-29)
211
263
 
212
264
  #### Features
@@ -215,12 +267,14 @@
215
267
  * The `instance.entity` method now optionally accepts `options` - [@mbleigh](https://github.com/mbleigh).
216
268
  * You can pass symbols to `:if` and `:unless` to simply check for truthiness/falsiness of the specified options key - [@mbleigh](https://github.com/mbleigh).
217
269
 
270
+
218
271
  ### 0.2.0 (2013-01-11)
219
272
 
220
273
  #### Features
221
274
 
222
275
  * Moved the namespace back to `Grape::Entity` to preserve compatibility with Grape - [@dblock](https://github.com/dblock).
223
276
 
277
+
224
278
  ### 0.1.0 (2013-01-11)
225
279
 
226
280
  * 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.79.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.
@@ -446,6 +483,19 @@ module Entities
446
483
  end
447
484
  ```
448
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
+
449
499
  #### Documentation
450
500
 
451
501
  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/bench/serializing.rb CHANGED
@@ -7,6 +7,7 @@ require 'benchmark'
7
7
  module Models
8
8
  class School
9
9
  attr_reader :classrooms
10
+
10
11
  def initialize
11
12
  @classrooms = []
12
13
  end
@@ -15,6 +16,7 @@ module Models
15
16
  class ClassRoom
16
17
  attr_reader :students
17
18
  attr_accessor :teacher
19
+
18
20
  def initialize(opts = {})
19
21
  @teacher = opts[:teacher]
20
22
  @students = []
@@ -23,6 +25,7 @@ module Models
23
25
 
24
26
  class Person
25
27
  attr_accessor :name
28
+
26
29
  def initialize(opts = {})
27
30
  @name = opts[:name]
28
31
  end
@@ -30,6 +33,7 @@ module Models
30
33
 
31
34
  class Teacher < Models::Person
32
35
  attr_accessor :tenure
36
+
33
37
  def initialize(opts = {})
34
38
  super(opts)
35
39
  @tenure = opts[:tenure]
@@ -38,6 +42,7 @@ module Models
38
42
 
39
43
  class Student < Models::Person
40
44
  attr_reader :grade
45
+
41
46
  def initialize(opts = {})
42
47
  super(opts)
43
48
  @grade = opts[:grade]
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)
@@ -4,7 +4,7 @@ module Grape
4
4
  class Entity
5
5
  module Delegator
6
6
  class FetchableObject < Base
7
- def delegate(attribute, **)
7
+ def delegate(attribute)
8
8
  object.fetch attribute
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  class Entity
5
5
  module Delegator
6
6
  class OpenStructObject < Base
7
- def delegate(attribute, **)
7
+ def delegate(attribute)
8
8
  object.send attribute
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  class Entity
5
5
  module Delegator
6
6
  class PlainObject < Base
7
- def delegate(attribute, **)
7
+ def delegate(attribute)
8
8
  object.send attribute
9
9
  end
10
10
 
@@ -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
@@ -105,7 +105,7 @@ module Grape
105
105
  @root_exposure ||= Exposure.new(nil, nesting: true)
106
106
  end
107
107
 
108
- attr_writer :root_exposure
108
+ attr_writer :root_exposure, :formatters
109
109
 
110
110
  # Returns all formatters that are registered for this and it's ancestors
111
111
  # @return [Hash] of formatters
@@ -113,8 +113,6 @@ module Grape
113
113
  @formatters ||= {}
114
114
  end
115
115
 
116
- attr_writer :formatters
117
-
118
116
  def hash_access
119
117
  @hash_access ||= :to_sym
120
118
  end
@@ -124,12 +122,14 @@ module Grape
124
122
  case value
125
123
  when :to_s, :str, :string
126
124
  :to_s
127
- when :to_sym, :sym, :symbol
128
- :to_sym
129
125
  else
130
126
  :to_sym
131
127
  end
132
128
  end
129
+
130
+ def delegation_opts
131
+ @delegation_opts ||= { hash_access: hash_access }
132
+ end
133
133
  end
134
134
 
135
135
  @formatters = {}
@@ -137,6 +137,8 @@ module Grape
137
137
  def self.inherited(subclass)
138
138
  subclass.root_exposure = root_exposure.dup
139
139
  subclass.formatters = formatters.dup
140
+
141
+ super
140
142
  end
141
143
 
142
144
  # This method is the primary means by which you will declare what attributes
@@ -151,7 +153,7 @@ module Grape
151
153
  #
152
154
  # @example as: a proc or lambda
153
155
  #
154
- # 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' )
155
157
  #
156
158
  # class MyEntity < Grape::Entity
157
159
  # expose :awesome, as: proc { object.awesomeness }
@@ -479,6 +481,9 @@ module Grape
479
481
  @object = object
480
482
  @options = options.is_a?(Options) ? options : Options.new(options)
481
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
482
487
  end
483
488
 
484
489
  def root_exposures
@@ -518,6 +523,11 @@ module Grape
518
523
  else
519
524
  instance_exec(object, options, &block)
520
525
  end
526
+ rescue StandardError => e
527
+ # it handles: https://github.com/ruby/ruby/blob/v3_0_0_preview1/NEWS.md#language-changes point 3, Proc
528
+ raise Grape::Entity::Deprecated.new e.message, 'in ruby 3.0' if e.is_a?(ArgumentError)
529
+
530
+ raise e
521
531
  end
522
532
 
523
533
  def exec_with_attribute(attribute, &block)
@@ -531,8 +541,10 @@ module Grape
531
541
  def delegate_attribute(attribute)
532
542
  if is_defined_in_entity?(attribute)
533
543
  send(attribute)
544
+ elsif @delegator_accepts_opts
545
+ delegator.delegate(attribute, **self.class.delegation_opts)
534
546
  else
535
- delegator.delegate(attribute, hash_access: self.class.hash_access)
547
+ delegator.delegate(attribute)
536
548
  end
537
549
  end
538
550
 
@@ -573,6 +585,7 @@ module Grape
573
585
  merge
574
586
  expose_nil
575
587
  override
588
+ default
576
589
  ].to_set.freeze
577
590
 
578
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
- 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
 
@@ -106,11 +106,12 @@ module Grape
106
106
  end
107
107
 
108
108
  def build_symbolized_hash(attribute, hash)
109
- if attribute.is_a?(Hash)
109
+ case attribute
110
+ when Hash
110
111
  attribute.each do |attr, nested_attrs|
111
112
  hash[attr.to_sym] = build_symbolized_hash(nested_attrs, {})
112
113
  end
113
- elsif attribute.is_a?(Array)
114
+ when Array
114
115
  return attribute.each { |x| build_symbolized_hash(x, {}) }
115
116
  else
116
117
  hash[attribute.to_sym] = true
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrapeEntity
4
- VERSION = '0.8.0'
4
+ VERSION = '0.10.0'
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
 
@@ -977,7 +1124,7 @@ describe Grape::Entity do
977
1124
  subject.expose(:user, using: user_entity)
978
1125
 
979
1126
  representation = subject.represent(OpenStruct.new(user: {}),
980
- only: [:id, :name, :phone, user: %i[id name email]],
1127
+ only: [:id, :name, :phone, { user: %i[id name email] }],
981
1128
  except: [:phone, { user: [:id] }], serializable: true)
982
1129
  expect(representation).to eq(id: nil, name: nil, user: { name: nil, email: nil })
983
1130
  end
@@ -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
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.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-18 00:00:00.000000000 Z
11
+ date: 2021-09-15 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
@@ -214,7 +217,7 @@ homepage: https://github.com/ruby-grape/grape-entity
214
217
  licenses:
215
218
  - MIT
216
219
  metadata: {}
217
- post_install_message:
220
+ post_install_message:
218
221
  rdoc_options: []
219
222
  require_paths:
220
223
  - lib
@@ -222,15 +225,15 @@ 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.2
233
- signing_key:
235
+ rubygems_version: 3.2.22
236
+ signing_key:
234
237
  specification_version: 4
235
238
  summary: A simple facade for managing the relationship between your model and API.
236
239
  test_files:
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.7
11
- - 2.6.5
12
- - 2.7.0
13
- - ruby-head
14
- - jruby-head
15
-
16
- matrix:
17
- fast_finish: true
18
-
19
- include:
20
- - rvm: 2.4.9
21
-
22
- allow_failures:
23
- - rvm: 2.4.9
24
- - rvm: ruby-head
25
- - rvm: jruby-head