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.
@@ -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
@@ -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.7.1'
4
+ VERSION = '0.9.0'
5
5
  end
@@ -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
- context 'with block passed via &' do
244
- it 'with does not pass options when block is passed via &' do
245
- class SomeObject
246
- def method_without_args
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
- subject.expose :that_method_without_args do |object|
252
- object.method_without_args
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
- subject.expose :that_method_without_args_again, &:method_without_args
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
- object = SomeObject.new
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
- value = subject.represent(object).value_for(:that_method_without_args)
260
- expect(value).to eq('result')
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
- value2 = subject.represent(object).value_for(:that_method_without_args_again)
263
- expect(value2).to eq('result')
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 { exposure.using_class_name }.to(using_class_name)
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 { exposure.subexposure }.to(subexposure)
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 { exposure.using_class }
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.7.1
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: 2018-01-30 00:00:00.000000000 Z
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: '4.0'
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: '4.0'
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.0'
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.0'
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.3'
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
- rubyforge_project: grape-entity
233
- rubygems_version: 2.7.3
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