grape-entity 0.7.1 → 0.9.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 +4 -4
- data/.github/dependabot.yml +14 -0
- data/.github/workflows/rubocop.yml +26 -0
- data/.github/workflows/ruby.yml +26 -0
- data/.rubocop.yml +72 -23
- data/.rubocop_todo.yml +8 -38
- data/CHANGELOG.md +59 -1
- data/Gemfile +3 -3
- data/Guardfile +4 -2
- data/README.md +43 -7
- data/UPGRADING.md +19 -2
- data/bench/serializing.rb +5 -0
- data/grape-entity.gemspec +4 -6
- data/lib/grape_entity.rb +1 -0
- data/lib/grape_entity/condition/base.rb +1 -1
- data/lib/grape_entity/delegator/hash_object.rb +2 -2
- data/lib/grape_entity/deprecated.rb +13 -0
- data/lib/grape_entity/entity.rb +66 -9
- data/lib/grape_entity/exposure.rb +9 -3
- data/lib/grape_entity/exposure/base.rb +6 -5
- data/lib/grape_entity/exposure/nesting_exposure.rb +2 -0
- data/lib/grape_entity/exposure/nesting_exposure/nested_exposures.rb +3 -1
- data/lib/grape_entity/exposure/nesting_exposure/output_builder.rb +3 -0
- data/lib/grape_entity/options.rb +3 -2
- data/lib/grape_entity/version.rb +1 -1
- data/spec/grape_entity/entity_spec.rb +74 -15
- data/spec/grape_entity/exposure/represent_exposure_spec.rb +3 -3
- data/spec/grape_entity/hash_spec.rb +36 -1
- data/spec/spec_helper.rb +7 -1
- metadata +15 -13
- data/.travis.yml +0 -30
@@ -87,9 +87,11 @@ module Grape
|
|
87
87
|
exposure.should_expose?(entity, options)
|
88
88
|
end
|
89
89
|
next unless should_expose
|
90
|
+
|
90
91
|
output[exposure.key(entity)] ||= []
|
91
92
|
output[exposure.key(entity)] << exposure
|
92
93
|
end
|
94
|
+
|
93
95
|
table.map do |key, exposures|
|
94
96
|
last_exposure = exposures.last
|
95
97
|
|
@@ -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
|
@@ -49,12 +50,13 @@ module Grape
|
|
49
50
|
length
|
50
51
|
empty?
|
51
52
|
].each do |name|
|
52
|
-
class_eval <<-RUBY, __FILE__, __LINE__
|
53
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
53
54
|
def #{name}(*args, &block)
|
54
55
|
@exposures.#{name}(*args, &block)
|
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)
|
@@ -19,6 +21,7 @@ module Grape
|
|
19
21
|
# If we have an array which should not be merged - save it with a key as a hash
|
20
22
|
# If we have hash which should be merged - save it without a key (merge)
|
21
23
|
return unless result
|
24
|
+
|
22
25
|
@output_hash.merge! result, &merge_strategy(exposure.for_merge)
|
23
26
|
else
|
24
27
|
@output_hash[exposure.key(@entity)] = result
|
data/lib/grape_entity/options.rb
CHANGED
@@ -106,11 +106,12 @@ module Grape
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def build_symbolized_hash(attribute, hash)
|
109
|
-
|
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
|
-
|
114
|
+
when Array
|
114
115
|
return attribute.each { |x| build_symbolized_hash(x, {}) }
|
115
116
|
else
|
116
117
|
hash[attribute.to_sym] = true
|
data/lib/grape_entity/version.rb
CHANGED
@@ -30,7 +30,9 @@ describe Grape::Entity do
|
|
30
30
|
|
31
31
|
it 'makes sure that :format_with as a proc cannot be used with a block' do
|
32
32
|
# rubocop:disable Style/BlockDelimiters
|
33
|
+
# rubocop:disable Lint/EmptyBlock
|
33
34
|
expect { subject.expose :name, format_with: proc {} do p 'hi' end }.to raise_error ArgumentError
|
35
|
+
# rubocop:enable Lint/EmptyBlock
|
34
36
|
# rubocop:enable Style/BlockDelimiters
|
35
37
|
end
|
36
38
|
|
@@ -126,6 +128,26 @@ describe Grape::Entity do
|
|
126
128
|
expect { subject.expose(:a, :b, :c, expose_nil: false) }.to raise_error ArgumentError
|
127
129
|
end
|
128
130
|
end
|
131
|
+
|
132
|
+
context 'when expose_nil option is false and block passed' do
|
133
|
+
it 'does not expose if block returns nil' do
|
134
|
+
subject.expose(:a, expose_nil: false) do |_obj, _options|
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
subject.expose(:b)
|
138
|
+
subject.expose(:c)
|
139
|
+
expect(subject.represent(model).serializable_hash).to eq(b: nil, c: 'value')
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'exposes is block returns a value' do
|
143
|
+
subject.expose(:a, expose_nil: false) do |_obj, _options|
|
144
|
+
100
|
145
|
+
end
|
146
|
+
subject.expose(:b)
|
147
|
+
subject.expose(:c)
|
148
|
+
expect(subject.represent(model).serializable_hash).to eq(a: 100, b: nil, c: 'value')
|
149
|
+
end
|
150
|
+
end
|
129
151
|
end
|
130
152
|
|
131
153
|
context 'when model is a hash' do
|
@@ -240,27 +262,50 @@ describe Grape::Entity do
|
|
240
262
|
end
|
241
263
|
end
|
242
264
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
'result'
|
248
|
-
end
|
265
|
+
describe 'blocks' do
|
266
|
+
class SomeObject
|
267
|
+
def method_without_args
|
268
|
+
'result'
|
249
269
|
end
|
270
|
+
end
|
250
271
|
|
251
|
-
|
252
|
-
|
272
|
+
describe 'with block passed in' do
|
273
|
+
specify do
|
274
|
+
subject.expose :that_method_without_args do |object|
|
275
|
+
object.method_without_args
|
276
|
+
end
|
277
|
+
|
278
|
+
object = SomeObject.new
|
279
|
+
|
280
|
+
value = subject.represent(object).value_for(:that_method_without_args)
|
281
|
+
expect(value).to eq('result')
|
253
282
|
end
|
283
|
+
end
|
254
284
|
|
255
|
-
|
285
|
+
context 'with block passed in via &' do
|
286
|
+
if RUBY_VERSION.start_with?('3')
|
287
|
+
specify do
|
288
|
+
subject.expose :that_method_without_args, &:method_without_args
|
289
|
+
subject.expose :method_without_args, as: :that_method_without_args_again
|
256
290
|
|
257
|
-
|
291
|
+
object = SomeObject.new
|
292
|
+
expect do
|
293
|
+
subject.represent(object).value_for(:that_method_without_args)
|
294
|
+
end.to raise_error Grape::Entity::Deprecated
|
258
295
|
|
259
|
-
|
260
|
-
|
296
|
+
value2 = subject.represent(object).value_for(:that_method_without_args_again)
|
297
|
+
expect(value2).to eq('result')
|
298
|
+
end
|
299
|
+
else
|
300
|
+
specify do
|
301
|
+
subject.expose :that_method_without_args_again, &:method_without_args
|
302
|
+
|
303
|
+
object = SomeObject.new
|
261
304
|
|
262
|
-
|
263
|
-
|
305
|
+
value2 = subject.represent(object).value_for(:that_method_without_args_again)
|
306
|
+
expect(value2).to eq('result')
|
307
|
+
end
|
308
|
+
end
|
264
309
|
end
|
265
310
|
end
|
266
311
|
|
@@ -957,7 +1002,7 @@ describe Grape::Entity do
|
|
957
1002
|
subject.expose(:user, using: user_entity)
|
958
1003
|
|
959
1004
|
representation = subject.represent(OpenStruct.new(user: {}),
|
960
|
-
only: [:id, :name, :phone, user: %i[id name email]],
|
1005
|
+
only: [:id, :name, :phone, { user: %i[id name email] }],
|
961
1006
|
except: [:phone, { user: [:id] }], serializable: true)
|
962
1007
|
expect(representation).to eq(id: nil, name: nil, user: { name: nil, email: nil })
|
963
1008
|
end
|
@@ -1376,6 +1421,18 @@ describe Grape::Entity do
|
|
1376
1421
|
expect(res).to have_key :nonexistent_attribute
|
1377
1422
|
end
|
1378
1423
|
|
1424
|
+
it 'exposes attributes defined through module inclusion' do
|
1425
|
+
module SharedAttributes
|
1426
|
+
def a_value
|
1427
|
+
3.14
|
1428
|
+
end
|
1429
|
+
end
|
1430
|
+
fresh_class.include(SharedAttributes)
|
1431
|
+
fresh_class.expose :a_value
|
1432
|
+
res = fresh_class.new(model).serializable_hash
|
1433
|
+
expect(res[:a_value]).to eq(3.14)
|
1434
|
+
end
|
1435
|
+
|
1379
1436
|
it 'does not expose attributes that are generated by a block but have not passed criteria' do
|
1380
1437
|
fresh_class.expose :nonexistent_attribute,
|
1381
1438
|
proc: ->(_, _) { 'I exist, but it is not yet my time to shine' },
|
@@ -1661,10 +1718,12 @@ describe Grape::Entity do
|
|
1661
1718
|
end
|
1662
1719
|
end
|
1663
1720
|
|
1721
|
+
# rubocop:disable Lint/EmptyBlock
|
1664
1722
|
fresh_class.class_eval do
|
1665
1723
|
expose :first_friend, using: EntitySpec::FriendEntity do |_user, _opts|
|
1666
1724
|
end
|
1667
1725
|
end
|
1726
|
+
# rubocop:enable Lint/EmptyBlock
|
1668
1727
|
|
1669
1728
|
rep = subject.value_for(:first_friend)
|
1670
1729
|
expect(rep).to be_kind_of EntitySpec::FriendEntity
|
@@ -12,11 +12,11 @@ describe Grape::Entity::Exposure::RepresentExposure do
|
|
12
12
|
let(:subexposure) { double(:subexposure) }
|
13
13
|
|
14
14
|
it 'sets using_class_name' do
|
15
|
-
expect { subject }.to change
|
15
|
+
expect { subject }.to change(exposure, :using_class_name).to(using_class_name)
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'sets subexposure' do
|
19
|
-
expect { subject }.to change
|
19
|
+
expect { subject }.to change(exposure, :subexposure).to(subexposure)
|
20
20
|
end
|
21
21
|
|
22
22
|
context 'when using_class is set' do
|
@@ -25,7 +25,7 @@ describe Grape::Entity::Exposure::RepresentExposure do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'resets using_class' do
|
28
|
-
expect { subject }.to change
|
28
|
+
expect { subject }.to change(exposure, :using_class)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Grape::Entity do
|
6
|
-
it 'except option for nested entity' do
|
6
|
+
it 'except option for nested entity', :aggregate_failures do
|
7
7
|
module EntitySpec
|
8
8
|
class Address < Grape::Entity
|
9
9
|
expose :post, if: :full
|
@@ -12,6 +12,14 @@ describe Grape::Entity do
|
|
12
12
|
expose :house
|
13
13
|
end
|
14
14
|
|
15
|
+
class AddressWithString < Grape::Entity
|
16
|
+
self.hash_access = :string
|
17
|
+
expose :post, if: :full
|
18
|
+
expose :city
|
19
|
+
expose :street
|
20
|
+
expose :house
|
21
|
+
end
|
22
|
+
|
15
23
|
class Company < Grape::Entity
|
16
24
|
expose :full_name, if: :full
|
17
25
|
expose :name
|
@@ -19,6 +27,15 @@ describe Grape::Entity do
|
|
19
27
|
Address.represent c[:address], Grape::Entity::Options.new(o.opts_hash.except(:full))
|
20
28
|
end
|
21
29
|
end
|
30
|
+
|
31
|
+
class CompanyWithString < Grape::Entity
|
32
|
+
self.hash_access = :string
|
33
|
+
expose :full_name, if: :full
|
34
|
+
expose :name
|
35
|
+
expose :address do |c, o|
|
36
|
+
AddressWithString.represent c['address'], Grape::Entity::Options.new(o.opts_hash.except(:full))
|
37
|
+
end
|
38
|
+
end
|
22
39
|
end
|
23
40
|
|
24
41
|
company = {
|
@@ -33,6 +50,24 @@ describe Grape::Entity do
|
|
33
50
|
}
|
34
51
|
}
|
35
52
|
|
53
|
+
company_with_string = {
|
54
|
+
'full_name' => 'full_name',
|
55
|
+
'name' => 'name',
|
56
|
+
'address' => {
|
57
|
+
'post' => '123456',
|
58
|
+
'city' => 'city',
|
59
|
+
'street' => 'street',
|
60
|
+
'house' => 'house',
|
61
|
+
'something_else' => 'something_else'
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
expect(EntitySpec::CompanyWithString.represent(company_with_string).serializable_hash).to eq \
|
66
|
+
company.slice(:name).merge(address: company[:address].slice(:city, :street, :house))
|
67
|
+
|
68
|
+
expect(EntitySpec::CompanyWithString.represent(company_with_string, full: true).serializable_hash).to eq \
|
69
|
+
company.slice(:full_name, :name).merge(address: company[:address].slice(:city, :street, :house))
|
70
|
+
|
36
71
|
expect(EntitySpec::Company.represent(company).serializable_hash).to eq \
|
37
72
|
company.slice(:name).merge(address: company[:address].slice(:city, :street, :house))
|
38
73
|
|
data/spec/spec_helper.rb
CHANGED
@@ -3,11 +3,17 @@
|
|
3
3
|
require 'simplecov'
|
4
4
|
require 'coveralls'
|
5
5
|
|
6
|
+
# This works around the hash extensions not being automatically included in ActiveSupport < 4
|
7
|
+
require 'active_support/version'
|
8
|
+
require 'active_support/core_ext/hash' if ActiveSupport::VERSION &&
|
9
|
+
ActiveSupport::VERSION::MAJOR &&
|
10
|
+
ActiveSupport::VERSION::MAJOR < 4
|
11
|
+
|
6
12
|
SimpleCov.start do
|
7
13
|
add_filter 'spec/'
|
8
14
|
end
|
9
15
|
|
10
|
-
Coveralls.wear!
|
16
|
+
Coveralls.wear! unless RUBY_PLATFORM.eql? 'java'
|
11
17
|
|
12
18
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
13
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.
|
4
|
+
version: 0.9.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:
|
11
|
+
date: 2021-03-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 3.0.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 3.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: multi_json
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,14 +128,14 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '3.
|
131
|
+
version: '3.9'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '3.
|
138
|
+
version: '3.9'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: yard
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -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,16 +225,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
222
225
|
requirements:
|
223
226
|
- - ">="
|
224
227
|
- !ruby/object:Gem::Version
|
225
|
-
version: '2.
|
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
|
-
|
233
|
-
|
234
|
-
signing_key:
|
235
|
+
rubygems_version: 3.2.3
|
236
|
+
signing_key:
|
235
237
|
specification_version: 4
|
236
238
|
summary: A simple facade for managing the relationship between your model and API.
|
237
239
|
test_files:
|
data/.travis.yml
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
sudo: false
|
2
|
-
|
3
|
-
language: ruby
|
4
|
-
|
5
|
-
before_install:
|
6
|
-
- gem update --system
|
7
|
-
- gem install bundler
|
8
|
-
|
9
|
-
after_success:
|
10
|
-
- coveralls
|
11
|
-
- bundle exec danger
|
12
|
-
|
13
|
-
rvm:
|
14
|
-
- 2.5.0
|
15
|
-
- 2.4.3
|
16
|
-
|
17
|
-
matrix:
|
18
|
-
fast_finish: true
|
19
|
-
|
20
|
-
include:
|
21
|
-
- rvm: 2.3.6
|
22
|
-
- rvm: ruby-head
|
23
|
-
- rvm: jruby-head
|
24
|
-
- rvm: rbx-2
|
25
|
-
|
26
|
-
allow_failures:
|
27
|
-
- rvm: 2.3.6
|
28
|
-
- rvm: ruby-head
|
29
|
-
- rvm: jruby-head
|
30
|
-
- rvm: rbx-2
|