grape-entity 0.6.1 → 0.8.2
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 +5 -5
- data/.coveralls.yml +1 -0
- data/.gitignore +5 -1
- data/.rubocop.yml +82 -2
- data/.rubocop_todo.yml +16 -33
- data/.travis.yml +18 -17
- data/CHANGELOG.md +75 -0
- data/Dangerfile +2 -0
- data/Gemfile +6 -1
- data/Guardfile +4 -2
- data/README.md +101 -4
- data/Rakefile +2 -2
- data/UPGRADING.md +31 -2
- data/bench/serializing.rb +7 -0
- data/grape-entity.gemspec +10 -10
- data/lib/grape-entity.rb +2 -0
- data/lib/grape_entity.rb +3 -0
- data/lib/grape_entity/condition.rb +20 -11
- data/lib/grape_entity/condition/base.rb +3 -1
- data/lib/grape_entity/condition/block_condition.rb +3 -1
- data/lib/grape_entity/condition/hash_condition.rb +2 -0
- data/lib/grape_entity/condition/symbol_condition.rb +2 -0
- data/lib/grape_entity/delegator.rb +10 -9
- data/lib/grape_entity/delegator/base.rb +2 -0
- data/lib/grape_entity/delegator/fetchable_object.rb +2 -0
- data/lib/grape_entity/delegator/hash_object.rb +4 -2
- data/lib/grape_entity/delegator/openstruct_object.rb +2 -0
- data/lib/grape_entity/delegator/plain_object.rb +2 -0
- data/lib/grape_entity/deprecated.rb +13 -0
- data/lib/grape_entity/entity.rb +115 -36
- data/lib/grape_entity/exposure.rb +64 -41
- data/lib/grape_entity/exposure/base.rb +21 -8
- data/lib/grape_entity/exposure/block_exposure.rb +2 -0
- data/lib/grape_entity/exposure/delegator_exposure.rb +2 -0
- data/lib/grape_entity/exposure/formatter_block_exposure.rb +2 -0
- data/lib/grape_entity/exposure/formatter_exposure.rb +2 -0
- data/lib/grape_entity/exposure/nesting_exposure.rb +36 -30
- data/lib/grape_entity/exposure/nesting_exposure/nested_exposures.rb +26 -15
- data/lib/grape_entity/exposure/nesting_exposure/output_builder.rb +10 -2
- data/lib/grape_entity/exposure/represent_exposure.rb +3 -1
- data/lib/grape_entity/options.rb +44 -58
- data/lib/grape_entity/version.rb +3 -1
- data/spec/grape_entity/entity_spec.rb +270 -47
- data/spec/grape_entity/exposure/nesting_exposure/nested_exposures_spec.rb +6 -4
- data/spec/grape_entity/exposure/represent_exposure_spec.rb +5 -3
- data/spec/grape_entity/exposure_spec.rb +14 -2
- data/spec/grape_entity/hash_spec.rb +38 -1
- data/spec/grape_entity/options_spec.rb +66 -0
- data/spec/spec_helper.rb +17 -0
- metadata +32 -43
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/grape-entity)
|
4
4
|
[](https://travis-ci.org/ruby-grape/grape-entity)
|
5
|
-
[](https://coveralls.io/github/ruby-grape/grape-entity?branch=master)
|
6
6
|
[](https://codeclimate.com/github/ruby-grape/grape-entity)
|
7
7
|
|
8
8
|
## Introduction
|
@@ -220,7 +220,8 @@ class ExampleEntity < Grape::Entity
|
|
220
220
|
end
|
221
221
|
```
|
222
222
|
|
223
|
-
You have
|
223
|
+
You always have access to the presented instance (`object`) and the top-level
|
224
|
+
entity options (`options`).
|
224
225
|
|
225
226
|
```ruby
|
226
227
|
class ExampleEntity < Grape::Entity
|
@@ -229,7 +230,7 @@ class ExampleEntity < Grape::Entity
|
|
229
230
|
private
|
230
231
|
|
231
232
|
def formatted_value
|
232
|
-
"+ X #{object.value}"
|
233
|
+
"+ X #{object.value} #{options[:y]}"
|
233
234
|
end
|
234
235
|
end
|
235
236
|
```
|
@@ -255,6 +256,22 @@ class MailingAddress < UserData
|
|
255
256
|
end
|
256
257
|
```
|
257
258
|
|
259
|
+
#### Overriding exposures
|
260
|
+
|
261
|
+
If you want to add one more exposure for the field but don't want the first one to be fired (for instance, when using inheritance), you can use the `override` flag. For instance:
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
class User < Grape::Entity
|
265
|
+
expose :name
|
266
|
+
end
|
267
|
+
|
268
|
+
class Employee < User
|
269
|
+
expose :name, as: :employee_name, override: true
|
270
|
+
end
|
271
|
+
```
|
272
|
+
|
273
|
+
`User` will return something like this `{ "name" : "John" }` while `Employee` will present the same data as `{ "employee_name" : "John" }` instead of `{ "name" : "John", "employee_name" : "John" }`.
|
274
|
+
|
258
275
|
#### Returning only the fields you want
|
259
276
|
|
260
277
|
After exposing the desired attributes, you can choose which one you need when representing some object or collection by using the only: and except: options. See the example:
|
@@ -320,7 +337,7 @@ module Entities
|
|
320
337
|
with_options(format_with: :iso_timestamp) do
|
321
338
|
expose :created_at
|
322
339
|
expose :updated_at
|
323
|
-
end
|
340
|
+
end
|
324
341
|
end
|
325
342
|
end
|
326
343
|
```
|
@@ -349,6 +366,86 @@ module Entities
|
|
349
366
|
end
|
350
367
|
```
|
351
368
|
|
369
|
+
#### Expose Nil
|
370
|
+
|
371
|
+
By default, exposures that contain `nil` values will be represented in the resulting JSON as `null`.
|
372
|
+
|
373
|
+
As an example, a hash with the following values:
|
374
|
+
|
375
|
+
```ruby
|
376
|
+
{
|
377
|
+
name: nil,
|
378
|
+
age: 100
|
379
|
+
}
|
380
|
+
```
|
381
|
+
|
382
|
+
will result in a JSON object that looks like:
|
383
|
+
|
384
|
+
```javascript
|
385
|
+
{
|
386
|
+
"name": null,
|
387
|
+
"age": 100
|
388
|
+
}
|
389
|
+
```
|
390
|
+
|
391
|
+
There are also times when, rather than displaying an attribute with a `null` value, it is more desirable to not display the attribute at all. Using the hash from above the desired JSON would look like:
|
392
|
+
|
393
|
+
```javascript
|
394
|
+
{
|
395
|
+
"age": 100
|
396
|
+
}
|
397
|
+
```
|
398
|
+
|
399
|
+
In order to turn on this behavior for an as-exposure basis, the option `expose_nil` can be used. By default, `expose_nil` is considered to be `true`, meaning that `nil` values will be represented in JSON as `null`. If `false` is provided, then attributes with `nil` values will be omitted from the resulting JSON completely.
|
400
|
+
|
401
|
+
```ruby
|
402
|
+
module Entities
|
403
|
+
class MyModel < Grape::Entity
|
404
|
+
expose :name, expose_nil: false
|
405
|
+
expose :age, expose_nil: false
|
406
|
+
end
|
407
|
+
end
|
408
|
+
```
|
409
|
+
|
410
|
+
`expose_nil` is per exposure, so you can suppress exposures from resulting in `null` or express `null` values on a per exposure basis as you need:
|
411
|
+
|
412
|
+
```ruby
|
413
|
+
module Entities
|
414
|
+
class MyModel < Grape::Entity
|
415
|
+
expose :name, expose_nil: false
|
416
|
+
expose :age # since expose_nil is omitted nil values will be rendered as null
|
417
|
+
end
|
418
|
+
end
|
419
|
+
```
|
420
|
+
|
421
|
+
It is also possible to use `expose_nil` with `with_options` if you want to add the configuration to multiple exposures at once.
|
422
|
+
|
423
|
+
```ruby
|
424
|
+
module Entities
|
425
|
+
class MyModel < Grape::Entity
|
426
|
+
# None of the exposures in the with_options block will render nil values as null
|
427
|
+
with_options(expose_nil: false) do
|
428
|
+
expose :name
|
429
|
+
expose :age
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
```
|
434
|
+
|
435
|
+
When using `with_options`, it is possible to again override which exposures will render `nil` as `null` by adding the option on a specific exposure.
|
436
|
+
|
437
|
+
```ruby
|
438
|
+
module Entities
|
439
|
+
class MyModel < Grape::Entity
|
440
|
+
# None of the exposures in the with_options block will render nil values as null
|
441
|
+
with_options(expose_nil: false) do
|
442
|
+
expose :name
|
443
|
+
expose :age, expose_nil: true # nil values would be rendered as null in the JSON
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
```
|
448
|
+
|
352
449
|
#### Documentation
|
353
450
|
|
354
451
|
Expose documentation with the field. Gets bubbled up when used with Grape and various API documentation systems.
|
data/Rakefile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'bundler'
|
@@ -17,4 +17,4 @@ RSpec::Core::RakeTask.new(:spec)
|
|
17
17
|
require 'rubocop/rake_task'
|
18
18
|
RuboCop::RakeTask.new(:rubocop)
|
19
19
|
|
20
|
-
task default: [
|
20
|
+
task default: %i[spec rubocop]
|
data/UPGRADING.md
CHANGED
@@ -1,5 +1,34 @@
|
|
1
|
-
Upgrading Grape Entity
|
2
|
-
|
1
|
+
# Upgrading Grape Entity
|
2
|
+
|
3
|
+
### Upgrading to >= 0.8.2
|
4
|
+
|
5
|
+
In Ruby 3.0: the block handling will be changed
|
6
|
+
[language-changes point 3, Proc](https://github.com/ruby/ruby/blob/v3_0_0_preview1/NEWS.md#language-changes).
|
7
|
+
This:
|
8
|
+
```ruby
|
9
|
+
expose :that_method_without_args, &:method_without_args
|
10
|
+
```
|
11
|
+
will be deprecated.
|
12
|
+
|
13
|
+
Prefer to use this pattern for simple setting a value
|
14
|
+
```ruby
|
15
|
+
expose :method_without_args, as: :that_method_without_args
|
16
|
+
```
|
17
|
+
|
18
|
+
### Upgrading to >= 0.8.2
|
19
|
+
|
20
|
+
In Ruby 3.0: the block handling will be changed
|
21
|
+
[language-changes point 3, Proc](https://github.com/ruby/ruby/blob/v3_0_0_preview1/NEWS.md#language-changes).
|
22
|
+
This:
|
23
|
+
```ruby
|
24
|
+
expose :that_method_without_args, &:method_without_args
|
25
|
+
```
|
26
|
+
will be deprecated.
|
27
|
+
|
28
|
+
Prefer to use this pattern for simple setting a value
|
29
|
+
```ruby
|
30
|
+
expose :method_without_args, as: :that_method_without_args
|
31
|
+
```
|
3
32
|
|
4
33
|
### Upgrading to >= 0.6.0
|
5
34
|
|
data/bench/serializing.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
4
|
require 'grape-entity'
|
3
5
|
require 'benchmark'
|
@@ -5,6 +7,7 @@ require 'benchmark'
|
|
5
7
|
module Models
|
6
8
|
class School
|
7
9
|
attr_reader :classrooms
|
10
|
+
|
8
11
|
def initialize
|
9
12
|
@classrooms = []
|
10
13
|
end
|
@@ -13,6 +16,7 @@ module Models
|
|
13
16
|
class ClassRoom
|
14
17
|
attr_reader :students
|
15
18
|
attr_accessor :teacher
|
19
|
+
|
16
20
|
def initialize(opts = {})
|
17
21
|
@teacher = opts[:teacher]
|
18
22
|
@students = []
|
@@ -21,6 +25,7 @@ module Models
|
|
21
25
|
|
22
26
|
class Person
|
23
27
|
attr_accessor :name
|
28
|
+
|
24
29
|
def initialize(opts = {})
|
25
30
|
@name = opts[:name]
|
26
31
|
end
|
@@ -28,6 +33,7 @@ module Models
|
|
28
33
|
|
29
34
|
class Teacher < Models::Person
|
30
35
|
attr_accessor :tenure
|
36
|
+
|
31
37
|
def initialize(opts = {})
|
32
38
|
super(opts)
|
33
39
|
@tenure = opts[:tenure]
|
@@ -36,6 +42,7 @@ module Models
|
|
36
42
|
|
37
43
|
class Student < Models::Person
|
38
44
|
attr_reader :grade
|
45
|
+
|
39
46
|
def initialize(opts = {})
|
40
47
|
super(opts)
|
41
48
|
@grade = opts[:grade]
|
data/grape-entity.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
2
4
|
require 'grape_entity/version'
|
3
5
|
|
4
6
|
Gem::Specification.new do |s|
|
@@ -12,22 +14,20 @@ Gem::Specification.new do |s|
|
|
12
14
|
s.description = 'Extracted from Grape, A Ruby framework for rapid API development with great conventions.'
|
13
15
|
s.license = 'MIT'
|
14
16
|
|
15
|
-
s.required_ruby_version = '>= 2.
|
16
|
-
|
17
|
-
s.rubyforge_project = 'grape-entity'
|
17
|
+
s.required_ruby_version = '>= 2.4'
|
18
18
|
|
19
|
+
s.add_runtime_dependency 'activesupport', '>= 3.0.0'
|
20
|
+
# FIXME: remove dependecy
|
19
21
|
s.add_runtime_dependency 'multi_json', '>= 1.3.2'
|
20
|
-
s.add_runtime_dependency 'activesupport', '>= 5.0.0'
|
21
22
|
|
22
23
|
s.add_development_dependency 'bundler'
|
23
|
-
s.add_development_dependency 'rake'
|
24
|
-
s.add_development_dependency 'rubocop', '~> 0.40'
|
25
|
-
s.add_development_dependency 'rspec', '~> 3.0'
|
26
|
-
s.add_development_dependency 'rack-test'
|
27
24
|
s.add_development_dependency 'maruku'
|
28
|
-
s.add_development_dependency 'yard'
|
29
25
|
s.add_development_dependency 'pry' unless RUBY_PLATFORM.eql?('java') || RUBY_ENGINE.eql?('rbx')
|
30
26
|
s.add_development_dependency 'pry-byebug' unless RUBY_PLATFORM.eql?('java') || RUBY_ENGINE.eql?('rbx')
|
27
|
+
s.add_development_dependency 'rack-test'
|
28
|
+
s.add_development_dependency 'rake'
|
29
|
+
s.add_development_dependency 'rspec', '~> 3.9'
|
30
|
+
s.add_development_dependency 'yard'
|
31
31
|
|
32
32
|
s.files = `git ls-files`.split("\n")
|
33
33
|
s.test_files = `git ls-files -- {test,spec}/*`.split("\n")
|
data/lib/grape-entity.rb
CHANGED
data/lib/grape_entity.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_support/version'
|
2
4
|
require 'active_support/core_ext/string/inflections'
|
3
5
|
require 'active_support/core_ext/hash/reverse_merge'
|
@@ -7,3 +9,4 @@ require 'grape_entity/entity'
|
|
7
9
|
require 'grape_entity/delegator'
|
8
10
|
require 'grape_entity/exposure'
|
9
11
|
require 'grape_entity/options'
|
12
|
+
require 'grape_entity/deprecated'
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'grape_entity/condition/base'
|
2
4
|
require 'grape_entity/condition/block_condition'
|
3
5
|
require 'grape_entity/condition/hash_condition'
|
@@ -6,19 +8,26 @@ require 'grape_entity/condition/symbol_condition'
|
|
6
8
|
module Grape
|
7
9
|
class Entity
|
8
10
|
module Condition
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
when Proc then BlockCondition.new false, &arg
|
13
|
-
when Symbol then SymbolCondition.new false, arg
|
11
|
+
class << self
|
12
|
+
def new_if(arg)
|
13
|
+
condition(false, arg)
|
14
14
|
end
|
15
|
-
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
def new_unless(arg)
|
17
|
+
condition(true, arg)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def condition(inverse, arg)
|
23
|
+
condition_klass =
|
24
|
+
case arg
|
25
|
+
when Hash then HashCondition
|
26
|
+
when Proc then BlockCondition
|
27
|
+
when Symbol then SymbolCondition
|
28
|
+
end
|
29
|
+
|
30
|
+
condition_klass.new(inverse, arg)
|
22
31
|
end
|
23
32
|
end
|
24
33
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
class Entity
|
3
5
|
module Condition
|
@@ -19,7 +21,7 @@ module Grape
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def met?(entity, options)
|
22
|
-
|
24
|
+
@inverse ? unless_value(entity, options) : if_value(entity, options)
|
23
25
|
end
|
24
26
|
|
25
27
|
def if_value(_entity, _options)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'grape_entity/delegator/base'
|
2
4
|
require 'grape_entity/delegator/hash_object'
|
3
5
|
require 'grape_entity/delegator/openstruct_object'
|
@@ -8,15 +10,14 @@ module Grape
|
|
8
10
|
class Entity
|
9
11
|
module Delegator
|
10
12
|
def self.new(object)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
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
|
18
|
+
end
|
19
|
+
|
20
|
+
delegator_klass.new(object)
|
20
21
|
end
|
21
22
|
end
|
22
23
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
class Entity
|
3
5
|
module Delegator
|
4
6
|
class HashObject < Base
|
5
|
-
def delegate(attribute)
|
6
|
-
object[attribute]
|
7
|
+
def delegate(attribute, hash_access: :to_sym)
|
8
|
+
object[attribute.send(hash_access)]
|
7
9
|
end
|
8
10
|
end
|
9
11
|
end
|