grape-entity 0.8.0 → 0.10.0

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: 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