grape-entity 0.6.0 → 0.8.1
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/.rspec +1 -1
- data/.rubocop.yml +124 -2
- data/.rubocop_todo.yml +21 -32
- data/.travis.yml +16 -17
- data/CHANGELOG.md +66 -0
- data/Dangerfile +2 -0
- data/Gemfile +8 -8
- data/Guardfile +4 -2
- data/README.md +101 -4
- data/Rakefile +2 -2
- data/bench/serializing.rb +7 -0
- data/grape-entity.gemspec +10 -8
- data/lib/grape-entity.rb +2 -0
- data/lib/grape_entity.rb +2 -0
- data/lib/grape_entity/condition.rb +20 -11
- data/lib/grape_entity/condition/base.rb +2 -0
- 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/entity.rb +112 -38
- data/lib/grape_entity/exposure.rb +64 -41
- data/lib/grape_entity/exposure/base.rb +20 -6
- 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 +35 -30
- data/lib/grape_entity/exposure/nesting_exposure/nested_exposures.rb +25 -15
- data/lib/grape_entity/exposure/nesting_exposure/output_builder.rb +6 -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 +243 -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 +31 -44
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
class Entity
|
3
5
|
module Exposure
|
@@ -21,7 +23,7 @@ module Grape
|
|
21
23
|
end
|
22
24
|
|
23
25
|
def value(entity, options)
|
24
|
-
new_options = options.for_nesting(key)
|
26
|
+
new_options = options.for_nesting(key(entity))
|
25
27
|
using_class.represent(@subexposure.value(entity, options), new_options)
|
26
28
|
end
|
27
29
|
|
data/lib/grape_entity/options.rb
CHANGED
@@ -1,8 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
1
5
|
module Grape
|
2
6
|
class Entity
|
3
7
|
class Options
|
8
|
+
extend Forwardable
|
9
|
+
|
4
10
|
attr_reader :opts_hash
|
5
11
|
|
12
|
+
def_delegators :opts_hash, :dig, :key?, :fetch, :[], :empty
|
13
|
+
|
6
14
|
def initialize(opts_hash = {})
|
7
15
|
@opts_hash = opts_hash
|
8
16
|
@has_only = !opts_hash[:only].nil?
|
@@ -11,54 +19,33 @@ module Grape
|
|
11
19
|
@should_return_key_cache = {}
|
12
20
|
end
|
13
21
|
|
14
|
-
def
|
15
|
-
|
16
|
-
end
|
22
|
+
def merge(new_opts)
|
23
|
+
return self if new_opts.empty?
|
17
24
|
|
18
|
-
|
19
|
-
|
20
|
-
|
25
|
+
merged = if new_opts.instance_of? Options
|
26
|
+
@opts_hash.merge(new_opts.opts_hash)
|
27
|
+
else
|
28
|
+
@opts_hash.merge(new_opts)
|
29
|
+
end
|
21
30
|
|
22
|
-
|
23
|
-
@opts_hash.key? key
|
24
|
-
end
|
25
|
-
|
26
|
-
def merge(new_opts)
|
27
|
-
if new_opts.empty?
|
28
|
-
self
|
29
|
-
else
|
30
|
-
merged = if new_opts.instance_of? Options
|
31
|
-
@opts_hash.merge(new_opts.opts_hash)
|
32
|
-
else
|
33
|
-
@opts_hash.merge(new_opts)
|
34
|
-
end
|
35
|
-
Options.new(merged)
|
36
|
-
end
|
31
|
+
Options.new(merged)
|
37
32
|
end
|
38
33
|
|
39
34
|
def reverse_merge(new_opts)
|
40
|
-
if new_opts.empty?
|
41
|
-
self
|
42
|
-
else
|
43
|
-
merged = if new_opts.instance_of? Options
|
44
|
-
new_opts.opts_hash.merge(@opts_hash)
|
45
|
-
else
|
46
|
-
new_opts.merge(@opts_hash)
|
47
|
-
end
|
48
|
-
Options.new(merged)
|
49
|
-
end
|
50
|
-
end
|
35
|
+
return self if new_opts.empty?
|
51
36
|
|
52
|
-
|
53
|
-
|
37
|
+
merged = if new_opts.instance_of? Options
|
38
|
+
new_opts.opts_hash.merge(@opts_hash)
|
39
|
+
else
|
40
|
+
new_opts.merge(@opts_hash)
|
41
|
+
end
|
42
|
+
|
43
|
+
Options.new(merged)
|
54
44
|
end
|
55
45
|
|
56
46
|
def ==(other)
|
57
|
-
|
58
|
-
|
59
|
-
else
|
60
|
-
other
|
61
|
-
end
|
47
|
+
other_hash = other.is_a?(Options) ? other.opts_hash : other
|
48
|
+
@opts_hash == other_hash
|
62
49
|
end
|
63
50
|
|
64
51
|
def should_return_key?(key)
|
@@ -66,7 +53,7 @@ module Grape
|
|
66
53
|
|
67
54
|
only = only_fields.nil? ||
|
68
55
|
only_fields.key?(key)
|
69
|
-
except = except_fields
|
56
|
+
except = except_fields&.key?(key) &&
|
70
57
|
except_fields[key] == true
|
71
58
|
only && !except
|
72
59
|
end
|
@@ -96,36 +83,35 @@ module Grape
|
|
96
83
|
end
|
97
84
|
|
98
85
|
def with_attr_path(part)
|
86
|
+
return yield unless part
|
87
|
+
|
99
88
|
stack = (opts_hash[:attr_path] ||= [])
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
result
|
105
|
-
else
|
106
|
-
yield
|
107
|
-
end
|
89
|
+
stack.push part
|
90
|
+
result = yield
|
91
|
+
stack.pop
|
92
|
+
result
|
108
93
|
end
|
109
94
|
|
110
95
|
private
|
111
96
|
|
112
97
|
def build_for_nesting(key)
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
98
|
+
Options.new(
|
99
|
+
opts_hash.dup.reject { |current_key| current_key == :collection }.merge(
|
100
|
+
root: nil,
|
101
|
+
only: only_fields(key),
|
102
|
+
except: except_fields(key),
|
103
|
+
attr_path: opts_hash[:attr_path]
|
104
|
+
)
|
105
|
+
)
|
121
106
|
end
|
122
107
|
|
123
108
|
def build_symbolized_hash(attribute, hash)
|
124
|
-
|
109
|
+
case attribute
|
110
|
+
when Hash
|
125
111
|
attribute.each do |attr, nested_attrs|
|
126
112
|
hash[attr.to_sym] = build_symbolized_hash(nested_attrs, {})
|
127
113
|
end
|
128
|
-
|
114
|
+
when Array
|
129
115
|
return attribute.each { |x| build_symbolized_hash(x, {}) }
|
130
116
|
else
|
131
117
|
hash[attribute.to_sym] = true
|
data/lib/grape_entity/version.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'ostruct'
|
3
5
|
|
@@ -27,7 +29,9 @@ describe Grape::Entity do
|
|
27
29
|
end
|
28
30
|
|
29
31
|
it 'makes sure that :format_with as a proc cannot be used with a block' do
|
30
|
-
|
32
|
+
# rubocop:disable Style/BlockDelimiters
|
33
|
+
expect { subject.expose :name, format_with: proc {} do p 'hi' end }.to raise_error ArgumentError
|
34
|
+
# rubocop:enable Style/BlockDelimiters
|
31
35
|
end
|
32
36
|
|
33
37
|
it 'makes sure unknown options are not silently ignored' do
|
@@ -64,6 +68,150 @@ describe Grape::Entity do
|
|
64
68
|
end
|
65
69
|
end
|
66
70
|
|
71
|
+
context 'with :expose_nil option' do
|
72
|
+
let(:a) { nil }
|
73
|
+
let(:b) { nil }
|
74
|
+
let(:c) { 'value' }
|
75
|
+
|
76
|
+
context 'when model is a PORO' do
|
77
|
+
let(:model) { Model.new(a, b, c) }
|
78
|
+
|
79
|
+
before do
|
80
|
+
stub_const 'Model', Class.new
|
81
|
+
Model.class_eval do
|
82
|
+
attr_accessor :a, :b, :c
|
83
|
+
|
84
|
+
def initialize(a, b, c)
|
85
|
+
@a = a
|
86
|
+
@b = b
|
87
|
+
@c = c
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when expose_nil option is not provided' do
|
93
|
+
it 'exposes nil attributes' do
|
94
|
+
subject.expose(:a)
|
95
|
+
subject.expose(:b)
|
96
|
+
subject.expose(:c)
|
97
|
+
expect(subject.represent(model).serializable_hash).to eq(a: nil, b: nil, c: 'value')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'when expose_nil option is true' do
|
102
|
+
it 'exposes nil attributes' do
|
103
|
+
subject.expose(:a, expose_nil: true)
|
104
|
+
subject.expose(:b, expose_nil: true)
|
105
|
+
subject.expose(:c)
|
106
|
+
expect(subject.represent(model).serializable_hash).to eq(a: nil, b: nil, c: 'value')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when expose_nil option is false' do
|
111
|
+
it 'does not expose nil attributes' do
|
112
|
+
subject.expose(:a, expose_nil: false)
|
113
|
+
subject.expose(:b, expose_nil: false)
|
114
|
+
subject.expose(:c)
|
115
|
+
expect(subject.represent(model).serializable_hash).to eq(c: 'value')
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'is only applied per attribute' do
|
119
|
+
subject.expose(:a, expose_nil: false)
|
120
|
+
subject.expose(:b)
|
121
|
+
subject.expose(:c)
|
122
|
+
expect(subject.represent(model).serializable_hash).to eq(b: nil, c: 'value')
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'raises an error when applied to multiple attribute exposures' do
|
126
|
+
expect { subject.expose(:a, :b, :c, expose_nil: false) }.to raise_error ArgumentError
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'when expose_nil option is false and block passed' do
|
131
|
+
it 'does not expose if block returns nil' do
|
132
|
+
subject.expose(:a, expose_nil: false) do |_obj, _options|
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
subject.expose(:b)
|
136
|
+
subject.expose(:c)
|
137
|
+
expect(subject.represent(model).serializable_hash).to eq(b: nil, c: 'value')
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'exposes is block returns a value' do
|
141
|
+
subject.expose(:a, expose_nil: false) do |_obj, _options|
|
142
|
+
100
|
143
|
+
end
|
144
|
+
subject.expose(:b)
|
145
|
+
subject.expose(:c)
|
146
|
+
expect(subject.represent(model).serializable_hash).to eq(a: 100, b: nil, c: 'value')
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'when model is a hash' do
|
152
|
+
let(:model) { { a: a, b: b, c: c } }
|
153
|
+
|
154
|
+
context 'when expose_nil option is not provided' do
|
155
|
+
it 'exposes nil attributes' do
|
156
|
+
subject.expose(:a)
|
157
|
+
subject.expose(:b)
|
158
|
+
subject.expose(:c)
|
159
|
+
expect(subject.represent(model).serializable_hash).to eq(a: nil, b: nil, c: 'value')
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'when expose_nil option is true' do
|
164
|
+
it 'exposes nil attributes' do
|
165
|
+
subject.expose(:a, expose_nil: true)
|
166
|
+
subject.expose(:b, expose_nil: true)
|
167
|
+
subject.expose(:c)
|
168
|
+
expect(subject.represent(model).serializable_hash).to eq(a: nil, b: nil, c: 'value')
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'when expose_nil option is false' do
|
173
|
+
it 'does not expose nil attributes' do
|
174
|
+
subject.expose(:a, expose_nil: false)
|
175
|
+
subject.expose(:b, expose_nil: false)
|
176
|
+
subject.expose(:c)
|
177
|
+
expect(subject.represent(model).serializable_hash).to eq(c: 'value')
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'is only applied per attribute' do
|
181
|
+
subject.expose(:a, expose_nil: false)
|
182
|
+
subject.expose(:b)
|
183
|
+
subject.expose(:c)
|
184
|
+
expect(subject.represent(model).serializable_hash).to eq(b: nil, c: 'value')
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'raises an error when applied to multiple attribute exposures' do
|
188
|
+
expect { subject.expose(:a, :b, :c, expose_nil: false) }.to raise_error ArgumentError
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'with nested structures' do
|
194
|
+
let(:model) { { a: a, b: b, c: { d: nil, e: nil, f: { g: nil, h: nil } } } }
|
195
|
+
|
196
|
+
context 'when expose_nil option is false' do
|
197
|
+
it 'does not expose nil attributes' do
|
198
|
+
subject.expose(:a, expose_nil: false)
|
199
|
+
subject.expose(:b)
|
200
|
+
subject.expose(:c) do
|
201
|
+
subject.expose(:d, expose_nil: false)
|
202
|
+
subject.expose(:e)
|
203
|
+
subject.expose(:f) do
|
204
|
+
subject.expose(:g, expose_nil: false)
|
205
|
+
subject.expose(:h)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
expect(subject.represent(model).serializable_hash).to eq(b: nil, c: { e: nil, f: { h: nil } })
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
67
215
|
context 'with a block' do
|
68
216
|
it 'errors out if called with multiple attributes' do
|
69
217
|
expect { subject.expose(:name, :email) { true } }.to raise_error ArgumentError
|
@@ -112,6 +260,30 @@ describe Grape::Entity do
|
|
112
260
|
end
|
113
261
|
end
|
114
262
|
|
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
|
269
|
+
end
|
270
|
+
|
271
|
+
subject.expose :that_method_without_args do |object|
|
272
|
+
object.method_without_args
|
273
|
+
end
|
274
|
+
|
275
|
+
subject.expose :that_method_without_args_again, &:method_without_args
|
276
|
+
|
277
|
+
object = SomeObject.new
|
278
|
+
|
279
|
+
value = subject.represent(object).value_for(:that_method_without_args)
|
280
|
+
expect(value).to eq('result')
|
281
|
+
|
282
|
+
value2 = subject.represent(object).value_for(:that_method_without_args_again)
|
283
|
+
expect(value2).to eq('result')
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
115
287
|
context 'with no parameters passed to the block' do
|
116
288
|
it 'adds a nested exposure' do
|
117
289
|
subject.expose :awesome do
|
@@ -131,7 +303,7 @@ describe Grape::Entity do
|
|
131
303
|
expect(another_nested).to_not be_nil
|
132
304
|
expect(another_nested.using_class_name).to eq('Awesome')
|
133
305
|
expect(moar_nested).to_not be_nil
|
134
|
-
expect(moar_nested.key).to eq(:weee)
|
306
|
+
expect(moar_nested.key(subject)).to eq(:weee)
|
135
307
|
end
|
136
308
|
|
137
309
|
it 'represents the exposure as a hash of its nested.root_exposures' do
|
@@ -324,6 +496,24 @@ describe Grape::Entity do
|
|
324
496
|
expect(subject.represent({ name: 'bar' }, serializable: true)).to eq(email: nil, name: 'bar')
|
325
497
|
expect(child_class.represent({ name: 'bar' }, serializable: true)).to eq(email: nil, name: 'foo')
|
326
498
|
end
|
499
|
+
|
500
|
+
it 'not overrides exposure by default' do
|
501
|
+
subject.expose :name
|
502
|
+
child_class = Class.new(subject)
|
503
|
+
child_class.expose :name, as: :child_name
|
504
|
+
|
505
|
+
expect(subject.represent({ name: 'bar' }, serializable: true)).to eq(name: 'bar')
|
506
|
+
expect(child_class.represent({ name: 'bar' }, serializable: true)).to eq(name: 'bar', child_name: 'bar')
|
507
|
+
end
|
508
|
+
|
509
|
+
it 'overrides parent class exposure when option is specified' do
|
510
|
+
subject.expose :name
|
511
|
+
child_class = Class.new(subject)
|
512
|
+
child_class.expose :name, as: :child_name, override: true
|
513
|
+
|
514
|
+
expect(subject.represent({ name: 'bar' }, serializable: true)).to eq(name: 'bar')
|
515
|
+
expect(child_class.represent({ name: 'bar' }, serializable: true)).to eq(child_name: 'bar')
|
516
|
+
end
|
327
517
|
end
|
328
518
|
|
329
519
|
context 'register formatters' do
|
@@ -496,7 +686,7 @@ describe Grape::Entity do
|
|
496
686
|
end
|
497
687
|
|
498
688
|
exposure = subject.find_exposure(:awesome_thing)
|
499
|
-
expect(exposure.key).to eq :extra_smooth
|
689
|
+
expect(exposure.key(subject)).to eq :extra_smooth
|
500
690
|
end
|
501
691
|
|
502
692
|
it 'merges nested :if option' do
|
@@ -596,6 +786,34 @@ describe Grape::Entity do
|
|
596
786
|
exposure = subject.find_exposure(:awesome_thing)
|
597
787
|
expect(exposure.documentation).to eq(desc: 'Other description.')
|
598
788
|
end
|
789
|
+
|
790
|
+
it 'propagates expose_nil option' do
|
791
|
+
subject.class_eval do
|
792
|
+
with_options(expose_nil: false) do
|
793
|
+
expose :awesome_thing
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
exposure = subject.find_exposure(:awesome_thing)
|
798
|
+
expect(exposure.conditions[0].inversed?).to be true
|
799
|
+
expect(exposure.conditions[0].block.call(awesome_thing: nil)).to be true
|
800
|
+
end
|
801
|
+
|
802
|
+
it 'overrides nested :expose_nil option' do
|
803
|
+
subject.class_eval do
|
804
|
+
with_options(expose_nil: true) do
|
805
|
+
expose :awesome_thing, expose_nil: false
|
806
|
+
expose :other_awesome_thing
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
exposure = subject.find_exposure(:awesome_thing)
|
811
|
+
expect(exposure.conditions[0].inversed?).to be true
|
812
|
+
expect(exposure.conditions[0].block.call(awesome_thing: nil)).to be true
|
813
|
+
# Conditions are only added for exposures that do not expose nil
|
814
|
+
exposure = subject.find_exposure(:other_awesome_thing)
|
815
|
+
expect(exposure.conditions[0]).to be_nil
|
816
|
+
end
|
599
817
|
end
|
600
818
|
|
601
819
|
describe '.represent' do
|
@@ -653,7 +871,7 @@ describe Grape::Entity do
|
|
653
871
|
context 'with specified fields' do
|
654
872
|
it 'returns only specified fields with only option' do
|
655
873
|
subject.expose(:id, :name, :phone)
|
656
|
-
representation = subject.represent(OpenStruct.new, only: [
|
874
|
+
representation = subject.represent(OpenStruct.new, only: %i[id name], serializable: true)
|
657
875
|
expect(representation).to eq(id: nil, name: nil)
|
658
876
|
end
|
659
877
|
|
@@ -666,7 +884,7 @@ describe Grape::Entity do
|
|
666
884
|
it 'returns only fields specified in the only option and not specified in the except option' do
|
667
885
|
subject.expose(:id, :name, :phone)
|
668
886
|
representation = subject.represent(OpenStruct.new,
|
669
|
-
only: [
|
887
|
+
only: %i[name phone],
|
670
888
|
except: [:phone], serializable: true)
|
671
889
|
expect(representation).to eq(name: nil)
|
672
890
|
end
|
@@ -736,7 +954,7 @@ describe Grape::Entity do
|
|
736
954
|
subject.expose(:id, :name, :phone)
|
737
955
|
subject.expose(:user, using: user_entity)
|
738
956
|
|
739
|
-
representation = subject.represent(OpenStruct.new(user: {}), only: [:id, :name, { user: [
|
957
|
+
representation = subject.represent(OpenStruct.new(user: {}), only: [:id, :name, { user: %i[name email] }], serializable: true)
|
740
958
|
expect(representation).to eq(id: nil, name: nil, user: { name: nil, email: nil })
|
741
959
|
end
|
742
960
|
|
@@ -759,7 +977,7 @@ describe Grape::Entity do
|
|
759
977
|
subject.expose(:user, using: user_entity)
|
760
978
|
|
761
979
|
representation = subject.represent(OpenStruct.new(user: {}),
|
762
|
-
only: [:id, :name, :phone, user: [
|
980
|
+
only: [:id, :name, :phone, { user: %i[id name email] }],
|
763
981
|
except: [:phone, { user: [:id] }], serializable: true)
|
764
982
|
expect(representation).to eq(id: nil, name: nil, user: { name: nil, email: nil })
|
765
983
|
end
|
@@ -771,7 +989,7 @@ describe Grape::Entity do
|
|
771
989
|
subject.expose(:name)
|
772
990
|
end
|
773
991
|
|
774
|
-
representation = subject.represent(OpenStruct.new, condition: true, only: [
|
992
|
+
representation = subject.represent(OpenStruct.new, condition: true, only: %i[id name], serializable: true)
|
775
993
|
expect(representation).to eq(id: nil, name: nil)
|
776
994
|
end
|
777
995
|
|
@@ -781,7 +999,7 @@ describe Grape::Entity do
|
|
781
999
|
subject.expose(:name, :mobile_phone)
|
782
1000
|
end
|
783
1001
|
|
784
|
-
representation = subject.represent(OpenStruct.new, condition: true, except: [
|
1002
|
+
representation = subject.represent(OpenStruct.new, condition: true, except: %i[phone mobile_phone], serializable: true)
|
785
1003
|
expect(representation).to eq(id: nil, name: nil)
|
786
1004
|
end
|
787
1005
|
|
@@ -863,7 +1081,7 @@ describe Grape::Entity do
|
|
863
1081
|
subject.expose(:id)
|
864
1082
|
subject.expose(:name, as: :title)
|
865
1083
|
|
866
|
-
representation = subject.represent(OpenStruct.new, condition: true, only: [
|
1084
|
+
representation = subject.represent(OpenStruct.new, condition: true, only: %i[id title], serializable: true)
|
867
1085
|
expect(representation).to eq(id: nil, title: nil)
|
868
1086
|
end
|
869
1087
|
|
@@ -890,7 +1108,7 @@ describe Grape::Entity do
|
|
890
1108
|
subject.expose(:nephew, using: nephew_entity)
|
891
1109
|
|
892
1110
|
representation = subject.represent(OpenStruct.new(user: {}),
|
893
|
-
only: [
|
1111
|
+
only: %i[id name user], except: [:nephew], serializable: true)
|
894
1112
|
expect(representation).to eq(id: nil, name: nil, user: { id: nil, name: nil, email: nil })
|
895
1113
|
end
|
896
1114
|
end
|
@@ -1178,6 +1396,18 @@ describe Grape::Entity do
|
|
1178
1396
|
expect(res).to have_key :nonexistent_attribute
|
1179
1397
|
end
|
1180
1398
|
|
1399
|
+
it 'exposes attributes defined through module inclusion' do
|
1400
|
+
module SharedAttributes
|
1401
|
+
def a_value
|
1402
|
+
3.14
|
1403
|
+
end
|
1404
|
+
end
|
1405
|
+
fresh_class.include(SharedAttributes)
|
1406
|
+
fresh_class.expose :a_value
|
1407
|
+
res = fresh_class.new(model).serializable_hash
|
1408
|
+
expect(res[:a_value]).to eq(3.14)
|
1409
|
+
end
|
1410
|
+
|
1181
1411
|
it 'does not expose attributes that are generated by a block but have not passed criteria' do
|
1182
1412
|
fresh_class.expose :nonexistent_attribute,
|
1183
1413
|
proc: ->(_, _) { 'I exist, but it is not yet my time to shine' },
|
@@ -1341,8 +1571,8 @@ describe Grape::Entity do
|
|
1341
1571
|
it 'allows to pass different :only and :except params using the same instance' do
|
1342
1572
|
fresh_class.expose :a, :b, :c
|
1343
1573
|
presenter = fresh_class.new(a: 1, b: 2, c: 3)
|
1344
|
-
expect(presenter.serializable_hash(only: [
|
1345
|
-
expect(presenter.serializable_hash(only: [
|
1574
|
+
expect(presenter.serializable_hash(only: %i[a b])).to eq(a: 1, b: 2)
|
1575
|
+
expect(presenter.serializable_hash(only: %i[b c])).to eq(b: 2, c: 3)
|
1346
1576
|
end
|
1347
1577
|
end
|
1348
1578
|
end
|
@@ -1765,39 +1995,5 @@ describe Grape::Entity do
|
|
1765
1995
|
end
|
1766
1996
|
end
|
1767
1997
|
end
|
1768
|
-
|
1769
|
-
describe Grape::Entity::Options do
|
1770
|
-
module EntitySpec
|
1771
|
-
class Crystalline
|
1772
|
-
attr_accessor :prop1, :prop2
|
1773
|
-
|
1774
|
-
def initialize
|
1775
|
-
@prop1 = 'value1'
|
1776
|
-
@prop2 = 'value2'
|
1777
|
-
end
|
1778
|
-
end
|
1779
|
-
|
1780
|
-
class CrystallineEntity < Grape::Entity
|
1781
|
-
expose :prop1, if: ->(_, options) { options.fetch(:signal) }
|
1782
|
-
expose :prop2, if: ->(_, options) { options.fetch(:beam, 'destructive') == 'destructive' }
|
1783
|
-
end
|
1784
|
-
end
|
1785
|
-
|
1786
|
-
context '#fetch' do
|
1787
|
-
it 'without passing in a required option raises KeyError' do
|
1788
|
-
expect { EntitySpec::CrystallineEntity.represent(EntitySpec::Crystalline.new).as_json }.to raise_error KeyError
|
1789
|
-
end
|
1790
|
-
|
1791
|
-
it 'passing in a required option will expose the values' do
|
1792
|
-
crystalline_entity = EntitySpec::CrystallineEntity.represent(EntitySpec::Crystalline.new, signal: true)
|
1793
|
-
expect(crystalline_entity.as_json).to eq(prop1: 'value1', prop2: 'value2')
|
1794
|
-
end
|
1795
|
-
|
1796
|
-
it 'with an option that is not default will not expose that value' do
|
1797
|
-
crystalline_entity = EntitySpec::CrystallineEntity.represent(EntitySpec::Crystalline.new, signal: true, beam: 'intermittent')
|
1798
|
-
expect(crystalline_entity.as_json).to eq(prop1: 'value1')
|
1799
|
-
end
|
1800
|
-
end
|
1801
|
-
end
|
1802
1998
|
end
|
1803
1999
|
end
|