grape-entity 0.4.3 → 0.4.4
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 +7 -0
- data/.travis.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/README.md +36 -2
- data/lib/grape_entity/entity.rb +81 -12
- data/lib/grape_entity/version.rb +1 -1
- data/spec/grape_entity/entity_spec.rb +227 -196
- data/spec/spec_helper.rb +4 -0
- metadata +19 -41
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: bca27e644c8d862751cbb573d3da20ec58fa9a1a
|
|
4
|
+
data.tar.gz: 40aa499604d5a7941fc13016c58c9a2fda9de787
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b02bedc0adc756dec9fb93c0ae527eb4251e21882aedefb30f3f973fd867ee9589a61702d0464ae69366ea4fc27f5e42ce382ea9ec247b347ebeab2c3c817713
|
|
7
|
+
data.tar.gz: c175aad11e6c88bfbd10c551a3f1cd0381753b918bc901f9786676f03558dc6e75d369e25ebc3aad45ed780ecf4f78d2a3e3627aa519e0824b29e11ec248794f
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
0.4.4 (2014-08-17)
|
|
2
|
+
==================
|
|
3
|
+
|
|
4
|
+
* [#85](https://github.com/intridea/grape-entity/pull/85): Added `present_collection` to indicate that an `Entity` presents an entire Collection - [@dspaeth-faber](https://github.com/dspaeth-faber).
|
|
5
|
+
* [#85](https://guthub.com/intridea/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).
|
|
6
|
+
|
|
1
7
|
0.4.3 (2014-06-12)
|
|
2
8
|
==================
|
|
3
9
|
|
data/README.md
CHANGED
|
@@ -63,6 +63,13 @@ Define a list of fields that will always be exposed.
|
|
|
63
63
|
expose :user_name, :ip
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
The field lookup takes several steps
|
|
67
|
+
|
|
68
|
+
* first try `entity-instance.exposure`
|
|
69
|
+
* next try `object.exposure`
|
|
70
|
+
* next try `object.fetch(exposure)`
|
|
71
|
+
* last raise an Exception
|
|
72
|
+
|
|
66
73
|
#### Exposing with a Presenter
|
|
67
74
|
|
|
68
75
|
Don't derive your model classes from `Grape::Entity`, expose them using a presenter.
|
|
@@ -119,6 +126,20 @@ root 'users', 'user'
|
|
|
119
126
|
expose :id, :name, ...
|
|
120
127
|
```
|
|
121
128
|
|
|
129
|
+
By default every object of a collection is wrapped into an instance of your `Entity` class.
|
|
130
|
+
You can override this behavior and wrapp the hole collection into one instance of your `Entity`
|
|
131
|
+
class.
|
|
132
|
+
|
|
133
|
+
As example:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
|
|
137
|
+
present_collection true, :collection_name # `collection_name` is optional and defaults to `items`
|
|
138
|
+
expose :collection_name, using: API:Items
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
|
|
122
143
|
#### Runtime Exposure
|
|
123
144
|
|
|
124
145
|
Use a block or a `Proc` to evaluate exposure at runtime. The supplied block or
|
|
@@ -151,6 +172,20 @@ private
|
|
|
151
172
|
end
|
|
152
173
|
```
|
|
153
174
|
|
|
175
|
+
You have always access to the presented instance with `object`
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
class ExampleEntity < Grape::Entity
|
|
179
|
+
expose :formatted_value
|
|
180
|
+
# ...
|
|
181
|
+
private
|
|
182
|
+
|
|
183
|
+
def formatted_value
|
|
184
|
+
"+ X #{object.value}"
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
```
|
|
188
|
+
|
|
154
189
|
#### Aliases
|
|
155
190
|
|
|
156
191
|
Expose under a different name with `:as`.
|
|
@@ -216,8 +251,7 @@ The above will automatically create a `Status::Entity` class and define properti
|
|
|
216
251
|
|
|
217
252
|
### Using Entities
|
|
218
253
|
|
|
219
|
-
With Grape, once an entity is defined, it can be used within endpoints, by calling `present`. The `present` method accepts two arguments, the object to be presented and the options associated with it. The options hash must always include `:with`, which defines the entity to expose.
|
|
220
|
-
|
|
254
|
+
With Grape, once an entity is defined, it can be used within endpoints, by calling `present`. The `present` method accepts two arguments, the `object` to be presented and the `options` associated with it. The options hash must always include `:with`, which defines the entity to expose.
|
|
221
255
|
If the entity includes documentation it can be included in an endpoint's description.
|
|
222
256
|
|
|
223
257
|
```ruby
|
data/lib/grape_entity/entity.rb
CHANGED
|
@@ -329,6 +329,62 @@ module Grape
|
|
|
329
329
|
@root = singular
|
|
330
330
|
end
|
|
331
331
|
|
|
332
|
+
# This allows you to present a collection of objects.
|
|
333
|
+
#
|
|
334
|
+
# @param present_collection [true or false] when true all objects will be available as
|
|
335
|
+
# items in your presenter instead of wrapping each object in an instance of your presenter.
|
|
336
|
+
# When false (default) every object in a collection to present will be wrapped separately
|
|
337
|
+
# into an instance of your presenter.
|
|
338
|
+
# @param collection_name [Symbol] the name of the collection accessor in your entity object.
|
|
339
|
+
# Default :items
|
|
340
|
+
#
|
|
341
|
+
# @example Entity Definition
|
|
342
|
+
#
|
|
343
|
+
# module API
|
|
344
|
+
# module Entities
|
|
345
|
+
# class User < Grape::Entity
|
|
346
|
+
# expose :id
|
|
347
|
+
# end
|
|
348
|
+
#
|
|
349
|
+
# class Users < Grape::Entity
|
|
350
|
+
# present_collection true
|
|
351
|
+
# expose :items, as: 'users', using: API::Entities::Users
|
|
352
|
+
# expose :version, documentation: { type: 'string',
|
|
353
|
+
# desc: 'actual api version',
|
|
354
|
+
# required: true }
|
|
355
|
+
#
|
|
356
|
+
# def version
|
|
357
|
+
# options[:version]
|
|
358
|
+
# end
|
|
359
|
+
# end
|
|
360
|
+
# end
|
|
361
|
+
# end
|
|
362
|
+
#
|
|
363
|
+
# @example Usage in the API Layer
|
|
364
|
+
#
|
|
365
|
+
# module API
|
|
366
|
+
# class Users < Grape::API
|
|
367
|
+
# version 'v2'
|
|
368
|
+
#
|
|
369
|
+
# # this will render { "users" : [ { "id" : "1" }, { "id" : "2" } ], "version" : "v2" }
|
|
370
|
+
# get '/users' do
|
|
371
|
+
# @users = User.all
|
|
372
|
+
# present @users, with: API::Entities::Users
|
|
373
|
+
# end
|
|
374
|
+
#
|
|
375
|
+
# # this will render { "user" : { "id" : "1" } }
|
|
376
|
+
# get '/users/:id' do
|
|
377
|
+
# @user = User.find(params[:id])
|
|
378
|
+
# present @user, with: API::Entities::User
|
|
379
|
+
# end
|
|
380
|
+
# end
|
|
381
|
+
# end
|
|
382
|
+
#
|
|
383
|
+
def self.present_collection(present_collection = false, collection_name = :items)
|
|
384
|
+
@present_collection = present_collection
|
|
385
|
+
@collection_name = collection_name
|
|
386
|
+
end
|
|
387
|
+
|
|
332
388
|
# This convenience method allows you to instantiate one or more entities by
|
|
333
389
|
# passing either a singular or collection of objects. Each object will be
|
|
334
390
|
# initialized with the same options. If an array of objects is passed in,
|
|
@@ -339,27 +395,34 @@ module Grape
|
|
|
339
395
|
# @param options [Hash] Options that will be passed through to each entity
|
|
340
396
|
# representation.
|
|
341
397
|
#
|
|
342
|
-
# @option options :root [String] override the default root name set for the entity.
|
|
398
|
+
# @option options :root [String or false] override the default root name set for the entity.
|
|
343
399
|
# Pass nil or false to represent the object or objects with no root name
|
|
344
400
|
# even if one is defined for the entity.
|
|
401
|
+
# @option options :serializable [true or false] when true a serializable Hash will be returned
|
|
402
|
+
#
|
|
345
403
|
def self.represent(objects, options = {})
|
|
346
|
-
if objects.respond_to?(:to_ary)
|
|
347
|
-
|
|
348
|
-
inner =
|
|
404
|
+
if objects.respond_to?(:to_ary) && ! @present_collection
|
|
405
|
+
root_element = @collection_root
|
|
406
|
+
inner = objects.to_ary.map { |object| new(object, { collection: true }.merge(options)).presented }
|
|
349
407
|
else
|
|
350
|
-
|
|
351
|
-
|
|
408
|
+
objects = { @collection_name => objects } if @present_collection
|
|
409
|
+
root_element = @root
|
|
410
|
+
inner = new(objects, options).presented
|
|
352
411
|
end
|
|
353
412
|
|
|
354
|
-
root_element = if options.key?(:root)
|
|
355
|
-
options[:root]
|
|
356
|
-
else
|
|
357
|
-
objects.respond_to?(:to_ary) ? @collection_root : @root
|
|
358
|
-
end
|
|
413
|
+
root_element = options[:root] if options.key?(:root)
|
|
359
414
|
|
|
360
415
|
root_element ? { root_element => inner } : inner
|
|
361
416
|
end
|
|
362
417
|
|
|
418
|
+
def presented
|
|
419
|
+
if options[:serializable]
|
|
420
|
+
serializable_hash
|
|
421
|
+
else
|
|
422
|
+
self
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
363
426
|
def initialize(object, options = {})
|
|
364
427
|
@object, @options = object, options
|
|
365
428
|
end
|
|
@@ -486,7 +549,13 @@ module Grape
|
|
|
486
549
|
if respond_to?(name, true)
|
|
487
550
|
send(name)
|
|
488
551
|
else
|
|
489
|
-
object.
|
|
552
|
+
if object.respond_to?(name, true)
|
|
553
|
+
object.send(name)
|
|
554
|
+
elsif object.respond_to?(:fetch, true)
|
|
555
|
+
object.fetch(name)
|
|
556
|
+
else
|
|
557
|
+
raise ArgumentError
|
|
558
|
+
end
|
|
490
559
|
end
|
|
491
560
|
end
|
|
492
561
|
|
data/lib/grape_entity/version.rb
CHANGED
|
@@ -11,12 +11,12 @@ describe Grape::Entity do
|
|
|
11
11
|
context 'multiple attributes' do
|
|
12
12
|
it 'is able to add multiple exposed attributes with a single call' do
|
|
13
13
|
subject.expose :name, :email, :location
|
|
14
|
-
subject.exposures.size.
|
|
14
|
+
expect(subject.exposures.size).to eq 3
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
it 'sets the same options for all exposures passed' do
|
|
18
18
|
subject.expose :name, :email, :location, documentation: true
|
|
19
|
-
subject.exposures.values.each { |v| v.
|
|
19
|
+
subject.exposures.values.each { |v| expect(v).to eq(documentation: true) }
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -62,22 +62,22 @@ describe Grape::Entity do
|
|
|
62
62
|
|
|
63
63
|
object = EntitySpec::SomeObject1.new
|
|
64
64
|
value = subject.represent(object).send(:value_for, :bogus)
|
|
65
|
-
value.
|
|
65
|
+
expect(value).to be_instance_of EntitySpec::BogusEntity
|
|
66
66
|
|
|
67
67
|
prop1 = value.send(:value_for, :prop1)
|
|
68
|
-
prop1.
|
|
68
|
+
expect(prop1).to eq 'MODIFIED 2'
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
context 'with parameters passed to the block' do
|
|
72
72
|
it 'sets the :proc option in the exposure options' do
|
|
73
73
|
block = lambda { |_| true }
|
|
74
74
|
subject.expose :name, using: 'Awesome', &block
|
|
75
|
-
subject.exposures[:name].
|
|
75
|
+
expect(subject.exposures[:name]).to eq(proc: block, using: 'Awesome')
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
it 'references an instance of the entity without any options' do
|
|
79
79
|
subject.expose(:size) { |_| self }
|
|
80
|
-
subject.represent(Hash.new).send(:value_for, :size).
|
|
80
|
+
expect(subject.represent(Hash.new).send(:value_for, :size)).to be_an_instance_of fresh_class
|
|
81
81
|
end
|
|
82
82
|
end
|
|
83
83
|
|
|
@@ -90,12 +90,12 @@ describe Grape::Entity do
|
|
|
90
90
|
subject.expose :another_nested, using: 'Awesome'
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
-
subject.exposures.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
expect(subject.exposures).to eq(
|
|
94
|
+
awesome: {},
|
|
95
|
+
awesome__nested: { nested: true },
|
|
96
|
+
awesome__nested__moar_nested: { as: 'weee', nested: true },
|
|
97
|
+
awesome__another_nested: { using: 'Awesome', nested: true }
|
|
98
|
+
)
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
it 'represents the exposure as a hash of its nested exposures' do
|
|
@@ -104,10 +104,10 @@ describe Grape::Entity do
|
|
|
104
104
|
subject.expose(:another_nested) { |_| 'value' }
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
-
subject.represent({}).send(:value_for, :awesome).
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
expect(subject.represent({}).send(:value_for, :awesome)).to eq(
|
|
108
|
+
nested: 'value',
|
|
109
|
+
another_nested: 'value'
|
|
110
|
+
)
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
it 'does not represent attributes, declared inside nested exposure, outside of it' do
|
|
@@ -119,15 +119,15 @@ describe Grape::Entity do
|
|
|
119
119
|
end
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
-
subject.represent({}).serializable_hash.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
122
|
+
expect(subject.represent({}).serializable_hash).to eq(
|
|
123
|
+
awesome: {
|
|
124
|
+
nested: 'value',
|
|
125
|
+
another_nested: 'value',
|
|
126
|
+
second_level_nested: {
|
|
127
|
+
deeply_exposed_attr: 'value'
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
it 'complex nested attributes' do
|
|
@@ -152,24 +152,24 @@ describe Grape::Entity do
|
|
|
152
152
|
expose(:children, using: 'Student') { |_| [{}, {}] }
|
|
153
153
|
end
|
|
154
154
|
|
|
155
|
-
ClassRoom.represent({}).serializable_hash.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
155
|
+
expect(ClassRoom.represent({}).serializable_hash).to eq(
|
|
156
|
+
parents: [
|
|
157
|
+
{
|
|
158
|
+
user: { in_first: 'value' },
|
|
159
|
+
children: [
|
|
160
|
+
{ user: { in_first: 'value', user_id: 'value', display_id: 'value' } },
|
|
161
|
+
{ user: { in_first: 'value', user_id: 'value', display_id: 'value' } }
|
|
162
|
+
]
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
user: { in_first: 'value' },
|
|
166
|
+
children: [
|
|
167
|
+
{ user: { in_first: 'value', user_id: 'value', display_id: 'value' } },
|
|
168
|
+
{ user: { in_first: 'value', user_id: 'value', display_id: 'value' } }
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
)
|
|
173
173
|
end
|
|
174
174
|
|
|
175
175
|
it 'is safe if its nested exposures are safe' do
|
|
@@ -184,8 +184,8 @@ describe Grape::Entity do
|
|
|
184
184
|
|
|
185
185
|
valid_keys = subject.represent({}).valid_exposures.keys
|
|
186
186
|
|
|
187
|
-
valid_keys.include?(:awesome).
|
|
188
|
-
valid_keys.include?(:not_awesome).
|
|
187
|
+
expect(valid_keys.include?(:awesome)).to be true
|
|
188
|
+
expect(valid_keys.include?(:not_awesome)).to be false
|
|
189
189
|
end
|
|
190
190
|
end
|
|
191
191
|
end
|
|
@@ -195,7 +195,7 @@ describe Grape::Entity do
|
|
|
195
195
|
subject.expose :name, :email
|
|
196
196
|
child_class = Class.new(subject)
|
|
197
197
|
|
|
198
|
-
child_class.exposures.
|
|
198
|
+
expect(child_class.exposures).to eq(subject.exposures)
|
|
199
199
|
end
|
|
200
200
|
|
|
201
201
|
it 'returns exposures from multiple ancestor' do
|
|
@@ -203,7 +203,7 @@ describe Grape::Entity do
|
|
|
203
203
|
parent_class = Class.new(subject)
|
|
204
204
|
child_class = Class.new(parent_class)
|
|
205
205
|
|
|
206
|
-
child_class.exposures.
|
|
206
|
+
expect(child_class.exposures).to eq(subject.exposures)
|
|
207
207
|
end
|
|
208
208
|
|
|
209
209
|
it 'returns descendant exposures as a priority' do
|
|
@@ -213,8 +213,8 @@ describe Grape::Entity do
|
|
|
213
213
|
'foo'
|
|
214
214
|
end
|
|
215
215
|
|
|
216
|
-
subject.exposures[:name].
|
|
217
|
-
child_class.exposures[:name].
|
|
216
|
+
expect(subject.exposures[:name]).not_to have_key :proc
|
|
217
|
+
expect(child_class.exposures[:name]).to have_key :proc
|
|
218
218
|
end
|
|
219
219
|
end
|
|
220
220
|
|
|
@@ -224,14 +224,14 @@ describe Grape::Entity do
|
|
|
224
224
|
it 'registers a formatter' do
|
|
225
225
|
subject.format_with :timestamp, &date_formatter
|
|
226
226
|
|
|
227
|
-
subject.formatters[:timestamp].
|
|
227
|
+
expect(subject.formatters[:timestamp]).not_to be_nil
|
|
228
228
|
end
|
|
229
229
|
|
|
230
230
|
it 'inherits formatters from ancestors' do
|
|
231
231
|
subject.format_with :timestamp, &date_formatter
|
|
232
232
|
child_class = Class.new(subject)
|
|
233
233
|
|
|
234
|
-
child_class.formatters.
|
|
234
|
+
expect(child_class.formatters).to eq subject.formatters
|
|
235
235
|
end
|
|
236
236
|
|
|
237
237
|
it 'does not allow registering a formatter without a block' do
|
|
@@ -246,14 +246,14 @@ describe Grape::Entity do
|
|
|
246
246
|
subject.expose :birthday, format_with: :timestamp
|
|
247
247
|
|
|
248
248
|
model = { birthday: Time.gm(2012, 2, 27) }
|
|
249
|
-
subject.new(double(model)).as_json[:birthday].
|
|
249
|
+
expect(subject.new(double(model)).as_json[:birthday]).to eq '02/27/2012'
|
|
250
250
|
end
|
|
251
251
|
|
|
252
252
|
it 'formats an exposure with a :format_with lambda that returns a value from the entity instance' do
|
|
253
253
|
object = Hash.new
|
|
254
254
|
|
|
255
255
|
subject.expose(:size, format_with: lambda { |_value| self.object.class.to_s })
|
|
256
|
-
subject.represent(object).send(:value_for, :size).
|
|
256
|
+
expect(subject.represent(object).send(:value_for, :size)).to eq object.class.to_s
|
|
257
257
|
end
|
|
258
258
|
|
|
259
259
|
it 'formats an exposure with a :format_with symbol that returns a value from the entity instance' do
|
|
@@ -264,7 +264,7 @@ describe Grape::Entity do
|
|
|
264
264
|
object = Hash.new
|
|
265
265
|
|
|
266
266
|
subject.expose(:size, format_with: :size_formatter)
|
|
267
|
-
subject.represent(object).send(:value_for, :size).
|
|
267
|
+
expect(subject.represent(object).send(:value_for, :size)).to eq object.class.to_s
|
|
268
268
|
end
|
|
269
269
|
end
|
|
270
270
|
end
|
|
@@ -287,7 +287,7 @@ describe Grape::Entity do
|
|
|
287
287
|
end
|
|
288
288
|
end
|
|
289
289
|
|
|
290
|
-
subject.exposures[:awesome_thing].
|
|
290
|
+
expect(subject.exposures[:awesome_thing]).to eq(if: { awesome: true }, using: 'Awesome')
|
|
291
291
|
end
|
|
292
292
|
|
|
293
293
|
it 'allows for nested .with_options' do
|
|
@@ -299,7 +299,7 @@ describe Grape::Entity do
|
|
|
299
299
|
end
|
|
300
300
|
end
|
|
301
301
|
|
|
302
|
-
subject.exposures[:awesome_thing].
|
|
302
|
+
expect(subject.exposures[:awesome_thing]).to eq(if: { awesome: true }, using: 'Something')
|
|
303
303
|
end
|
|
304
304
|
|
|
305
305
|
it 'overrides nested :as option' do
|
|
@@ -309,7 +309,7 @@ describe Grape::Entity do
|
|
|
309
309
|
end
|
|
310
310
|
end
|
|
311
311
|
|
|
312
|
-
subject.exposures[:awesome_thing].
|
|
312
|
+
expect(subject.exposures[:awesome_thing]).to eq(as: :extra_smooth)
|
|
313
313
|
end
|
|
314
314
|
|
|
315
315
|
it 'merges nested :if option' do
|
|
@@ -331,10 +331,10 @@ describe Grape::Entity do
|
|
|
331
331
|
end
|
|
332
332
|
end
|
|
333
333
|
|
|
334
|
-
subject.exposures[:awesome_thing].
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
334
|
+
expect(subject.exposures[:awesome_thing]).to eq(
|
|
335
|
+
if: { awesome: false, less_awesome: true },
|
|
336
|
+
if_extras: [:awesome, match_proc]
|
|
337
|
+
)
|
|
338
338
|
end
|
|
339
339
|
|
|
340
340
|
it 'merges nested :unless option' do
|
|
@@ -356,10 +356,10 @@ describe Grape::Entity do
|
|
|
356
356
|
end
|
|
357
357
|
end
|
|
358
358
|
|
|
359
|
-
subject.exposures[:awesome_thing].
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
359
|
+
expect(subject.exposures[:awesome_thing]).to eq(
|
|
360
|
+
unless: { awesome: false, less_awesome: true },
|
|
361
|
+
unless_extras: [:awesome, match_proc]
|
|
362
|
+
)
|
|
363
363
|
end
|
|
364
364
|
|
|
365
365
|
it 'overrides nested :using option' do
|
|
@@ -369,7 +369,7 @@ describe Grape::Entity do
|
|
|
369
369
|
end
|
|
370
370
|
end
|
|
371
371
|
|
|
372
|
-
subject.exposures[:awesome_thing].
|
|
372
|
+
expect(subject.exposures[:awesome_thing]).to eq(using: 'SomethingElse')
|
|
373
373
|
end
|
|
374
374
|
|
|
375
375
|
it 'aliases :with option to :using option' do
|
|
@@ -378,7 +378,7 @@ describe Grape::Entity do
|
|
|
378
378
|
expose :awesome_thing, with: 'SomethingElse'
|
|
379
379
|
end
|
|
380
380
|
end
|
|
381
|
-
subject.exposures[:awesome_thing].
|
|
381
|
+
expect(subject.exposures[:awesome_thing]).to eq(using: 'SomethingElse')
|
|
382
382
|
end
|
|
383
383
|
|
|
384
384
|
it 'overrides nested :proc option' do
|
|
@@ -390,7 +390,7 @@ describe Grape::Entity do
|
|
|
390
390
|
end
|
|
391
391
|
end
|
|
392
392
|
|
|
393
|
-
subject.exposures[:awesome_thing].
|
|
393
|
+
expect(subject.exposures[:awesome_thing]).to eq(proc: match_proc)
|
|
394
394
|
end
|
|
395
395
|
|
|
396
396
|
it 'overrides nested :documentation option' do
|
|
@@ -400,41 +400,72 @@ describe Grape::Entity do
|
|
|
400
400
|
end
|
|
401
401
|
end
|
|
402
402
|
|
|
403
|
-
subject.exposures[:awesome_thing].
|
|
403
|
+
expect(subject.exposures[:awesome_thing]).to eq(documentation: { desc: 'Other description.' })
|
|
404
404
|
end
|
|
405
405
|
end
|
|
406
406
|
|
|
407
407
|
describe '.represent' do
|
|
408
408
|
it 'returns a single entity if called with one object' do
|
|
409
|
-
subject.represent(Object.new).
|
|
409
|
+
expect(subject.represent(Object.new)).to be_kind_of(subject)
|
|
410
410
|
end
|
|
411
411
|
|
|
412
412
|
it 'returns a single entity if called with a hash' do
|
|
413
|
-
subject.represent(Hash.new).
|
|
413
|
+
expect(subject.represent(Hash.new)).to be_kind_of(subject)
|
|
414
414
|
end
|
|
415
415
|
|
|
416
416
|
it 'returns multiple entities if called with a collection' do
|
|
417
417
|
representation = subject.represent(4.times.map { Object.new })
|
|
418
|
-
representation.
|
|
419
|
-
representation.size.
|
|
420
|
-
representation.reject { |r| r.kind_of?(subject) }.
|
|
418
|
+
expect(representation).to be_kind_of Array
|
|
419
|
+
expect(representation.size).to eq(4)
|
|
420
|
+
expect(representation.reject { |r| r.kind_of?(subject) }).to be_empty
|
|
421
421
|
end
|
|
422
422
|
|
|
423
423
|
it 'adds the collection: true option if called with a collection' do
|
|
424
424
|
representation = subject.represent(4.times.map { Object.new })
|
|
425
|
-
representation.each { |r| r.options[:collection].
|
|
425
|
+
representation.each { |r| expect(r.options[:collection]).to be true }
|
|
426
426
|
end
|
|
427
427
|
|
|
428
428
|
it 'returns a serialized hash of a single object if serializable: true' do
|
|
429
429
|
subject.expose(:awesome) { |_| true }
|
|
430
430
|
representation = subject.represent(Object.new, serializable: true)
|
|
431
|
-
representation.
|
|
431
|
+
expect(representation).to eq(awesome: true)
|
|
432
432
|
end
|
|
433
433
|
|
|
434
434
|
it 'returns a serialized array of hashes of multiple objects if serializable: true' do
|
|
435
435
|
subject.expose(:awesome) { |_| true }
|
|
436
436
|
representation = subject.represent(2.times.map { Object.new }, serializable: true)
|
|
437
|
-
representation.
|
|
437
|
+
expect(representation).to eq([{ awesome: true }, { awesome: true }])
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
it 'returns a serialized hash of a hash' do
|
|
441
|
+
subject.expose(:awesome)
|
|
442
|
+
representation = subject.represent({ awesome: true }, serializable: true)
|
|
443
|
+
expect(representation).to eq(awesome: true)
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
describe '.present_collection' do
|
|
448
|
+
it 'make the objects accessible' do
|
|
449
|
+
subject.present_collection true
|
|
450
|
+
subject.expose :items
|
|
451
|
+
|
|
452
|
+
representation = subject.represent(4.times.map { Object.new })
|
|
453
|
+
expect(representation).to be_kind_of(subject)
|
|
454
|
+
expect(representation.object).to be_kind_of(Hash)
|
|
455
|
+
expect(representation.object).to have_key :items
|
|
456
|
+
expect(representation.object[:items]).to be_kind_of Array
|
|
457
|
+
expect(representation.object[:items].size).to be 4
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
it 'serializes items with my root name' do
|
|
461
|
+
subject.present_collection true, :my_items
|
|
462
|
+
subject.expose :my_items
|
|
463
|
+
|
|
464
|
+
representation = subject.represent(4.times.map { Object.new }, serializable: true)
|
|
465
|
+
expect(representation).to be_kind_of(Hash)
|
|
466
|
+
expect(representation).to have_key :my_items
|
|
467
|
+
expect(representation[:my_items]).to be_kind_of Array
|
|
468
|
+
expect(representation[:my_items].size).to be 4
|
|
438
469
|
end
|
|
439
470
|
end
|
|
440
471
|
|
|
@@ -447,37 +478,37 @@ describe Grape::Entity do
|
|
|
447
478
|
context 'with a single object' do
|
|
448
479
|
it 'allows a root element name to be specified' do
|
|
449
480
|
representation = subject.represent(Object.new)
|
|
450
|
-
representation.
|
|
451
|
-
representation.
|
|
452
|
-
representation['thing'].
|
|
481
|
+
expect(representation).to be_kind_of Hash
|
|
482
|
+
expect(representation).to have_key 'thing'
|
|
483
|
+
expect(representation['thing']).to be_kind_of(subject)
|
|
453
484
|
end
|
|
454
485
|
end
|
|
455
486
|
|
|
456
487
|
context 'with an array of objects' do
|
|
457
488
|
it 'allows a root element name to be specified' do
|
|
458
489
|
representation = subject.represent(4.times.map { Object.new })
|
|
459
|
-
representation.
|
|
460
|
-
representation.
|
|
461
|
-
representation['things'].
|
|
462
|
-
representation['things'].size.
|
|
463
|
-
representation['things'].reject { |r| r.kind_of?(subject) }.
|
|
490
|
+
expect(representation).to be_kind_of Hash
|
|
491
|
+
expect(representation).to have_key 'things'
|
|
492
|
+
expect(representation['things']).to be_kind_of Array
|
|
493
|
+
expect(representation['things'].size).to eq 4
|
|
494
|
+
expect(representation['things'].reject { |r| r.kind_of?(subject) }).to be_empty
|
|
464
495
|
end
|
|
465
496
|
end
|
|
466
497
|
|
|
467
498
|
context 'it can be overridden' do
|
|
468
499
|
it 'can be disabled' do
|
|
469
500
|
representation = subject.represent(4.times.map { Object.new }, root: false)
|
|
470
|
-
representation.
|
|
471
|
-
representation.size.
|
|
472
|
-
representation.reject { |r| r.kind_of?(subject) }.
|
|
501
|
+
expect(representation).to be_kind_of Array
|
|
502
|
+
expect(representation.size).to eq 4
|
|
503
|
+
expect(representation.reject { |r| r.kind_of?(subject) }).to be_empty
|
|
473
504
|
end
|
|
474
505
|
it 'can use a different name' do
|
|
475
506
|
representation = subject.represent(4.times.map { Object.new }, root: 'others')
|
|
476
|
-
representation.
|
|
477
|
-
representation.
|
|
478
|
-
representation['others'].
|
|
479
|
-
representation['others'].size.
|
|
480
|
-
representation['others'].reject { |r| r.kind_of?(subject) }.
|
|
507
|
+
expect(representation).to be_kind_of Hash
|
|
508
|
+
expect(representation).to have_key 'others'
|
|
509
|
+
expect(representation['others']).to be_kind_of Array
|
|
510
|
+
expect(representation['others'].size).to eq 4
|
|
511
|
+
expect(representation['others'].reject { |r| r.kind_of?(subject) }).to be_empty
|
|
481
512
|
end
|
|
482
513
|
end
|
|
483
514
|
end
|
|
@@ -490,18 +521,18 @@ describe Grape::Entity do
|
|
|
490
521
|
context 'with a single object' do
|
|
491
522
|
it 'allows a root element name to be specified' do
|
|
492
523
|
representation = subject.represent(Object.new)
|
|
493
|
-
representation.
|
|
494
|
-
representation.
|
|
495
|
-
representation['thing'].
|
|
524
|
+
expect(representation).to be_kind_of Hash
|
|
525
|
+
expect(representation).to have_key 'thing'
|
|
526
|
+
expect(representation['thing']).to be_kind_of(subject)
|
|
496
527
|
end
|
|
497
528
|
end
|
|
498
529
|
|
|
499
530
|
context 'with an array of objects' do
|
|
500
531
|
it 'allows a root element name to be specified' do
|
|
501
532
|
representation = subject.represent(4.times.map { Object.new })
|
|
502
|
-
representation.
|
|
503
|
-
representation.size.
|
|
504
|
-
representation.reject { |r| r.kind_of?(subject) }.
|
|
533
|
+
expect(representation).to be_kind_of Array
|
|
534
|
+
expect(representation.size).to eq 4
|
|
535
|
+
expect(representation.reject { |r| r.kind_of?(subject) }).to be_empty
|
|
505
536
|
end
|
|
506
537
|
end
|
|
507
538
|
end
|
|
@@ -513,18 +544,18 @@ describe Grape::Entity do
|
|
|
513
544
|
|
|
514
545
|
context 'with a single object' do
|
|
515
546
|
it 'allows a root element name to be specified' do
|
|
516
|
-
subject.represent(Object.new).
|
|
547
|
+
expect(subject.represent(Object.new)).to be_kind_of(subject)
|
|
517
548
|
end
|
|
518
549
|
end
|
|
519
550
|
|
|
520
551
|
context 'with an array of objects' do
|
|
521
552
|
it 'allows a root element name to be specified' do
|
|
522
553
|
representation = subject.represent(4.times.map { Object.new })
|
|
523
|
-
representation.
|
|
524
|
-
representation.
|
|
525
|
-
representation['things'].
|
|
526
|
-
representation['things'].size.
|
|
527
|
-
representation['things'].reject { |r| r.kind_of?(subject) }.
|
|
554
|
+
expect(representation).to be_kind_of Hash
|
|
555
|
+
expect(representation).to have_key('things')
|
|
556
|
+
expect(representation['things']).to be_kind_of Array
|
|
557
|
+
expect(representation['things'].size).to eq 4
|
|
558
|
+
expect(representation['things'].reject { |r| r.kind_of?(subject) }).to be_empty
|
|
528
559
|
end
|
|
529
560
|
end
|
|
530
561
|
end
|
|
@@ -539,8 +570,8 @@ describe Grape::Entity do
|
|
|
539
570
|
|
|
540
571
|
it 'has attribute readers for the object and options' do
|
|
541
572
|
entity = subject.new('abc', {})
|
|
542
|
-
entity.object.
|
|
543
|
-
entity.options.
|
|
573
|
+
expect(entity.object).to eq 'abc'
|
|
574
|
+
expect(entity.options).to eq({})
|
|
544
575
|
end
|
|
545
576
|
end
|
|
546
577
|
|
|
@@ -585,9 +616,9 @@ describe Grape::Entity do
|
|
|
585
616
|
fresh_class.expose :email, :nonexistent_attribute, :name, safe: true
|
|
586
617
|
|
|
587
618
|
res = fresh_class.new(model).serializable_hash
|
|
588
|
-
res.
|
|
589
|
-
res.
|
|
590
|
-
res.
|
|
619
|
+
expect(res).to have_key :email
|
|
620
|
+
expect(res).not_to have_key :nonexistent_attribute
|
|
621
|
+
expect(res).to have_key :name
|
|
591
622
|
end
|
|
592
623
|
|
|
593
624
|
it "does not expose attributes that don't exist on the object, even with criteria" do
|
|
@@ -596,9 +627,9 @@ describe Grape::Entity do
|
|
|
596
627
|
fresh_class.expose :nonexistent_attribute2, safe: true, if: lambda { true }
|
|
597
628
|
|
|
598
629
|
res = fresh_class.new(model).serializable_hash
|
|
599
|
-
res.
|
|
600
|
-
res.
|
|
601
|
-
res.
|
|
630
|
+
expect(res).to have_key :email
|
|
631
|
+
expect(res).not_to have_key :nonexistent_attribute
|
|
632
|
+
expect(res).not_to have_key :nonexistent_attribute2
|
|
602
633
|
end
|
|
603
634
|
end
|
|
604
635
|
|
|
@@ -613,7 +644,7 @@ describe Grape::Entity do
|
|
|
613
644
|
'well, I do exist after all'
|
|
614
645
|
end
|
|
615
646
|
res = fresh_class.new(model).serializable_hash
|
|
616
|
-
res.
|
|
647
|
+
expect(res).to have_key :nonexistent_attribute
|
|
617
648
|
end
|
|
618
649
|
|
|
619
650
|
it 'does not expose attributes that are generated by a block but have not passed criteria' do
|
|
@@ -621,7 +652,7 @@ describe Grape::Entity do
|
|
|
621
652
|
'I exist, but it is not yet my time to shine'
|
|
622
653
|
}, if: lambda { |_model, _opts| false }
|
|
623
654
|
res = fresh_class.new(model).serializable_hash
|
|
624
|
-
res.
|
|
655
|
+
expect(res).not_to have_key :nonexistent_attribute
|
|
625
656
|
end
|
|
626
657
|
end
|
|
627
658
|
|
|
@@ -635,7 +666,7 @@ describe Grape::Entity do
|
|
|
635
666
|
'well, I do exist after all'
|
|
636
667
|
end
|
|
637
668
|
res = fresh_class.new(model).serializable_hash
|
|
638
|
-
res.
|
|
669
|
+
expect(res).to have_key :nonexistent_attribute
|
|
639
670
|
end
|
|
640
671
|
|
|
641
672
|
it 'does not expose attributes that are generated by a block but have not passed criteria' do
|
|
@@ -643,7 +674,7 @@ describe Grape::Entity do
|
|
|
643
674
|
'I exist, but it is not yet my time to shine'
|
|
644
675
|
}, if: lambda { |_, _| false }
|
|
645
676
|
res = fresh_class.new(model).serializable_hash
|
|
646
|
-
res.
|
|
677
|
+
expect(res).not_to have_key :nonexistent_attribute
|
|
647
678
|
end
|
|
648
679
|
|
|
649
680
|
context '#serializable_hash' do
|
|
@@ -688,19 +719,19 @@ describe Grape::Entity do
|
|
|
688
719
|
it 'serializes embedded objects which respond to #serializable_hash' do
|
|
689
720
|
fresh_class.expose :name, :embedded
|
|
690
721
|
presenter = fresh_class.new(EntitySpec::EmbeddedExampleWithOne.new)
|
|
691
|
-
presenter.serializable_hash.
|
|
722
|
+
expect(presenter.serializable_hash).to eq(name: 'abc', embedded: { abc: 'def' })
|
|
692
723
|
end
|
|
693
724
|
|
|
694
725
|
it 'serializes embedded arrays of objects which respond to #serializable_hash' do
|
|
695
726
|
fresh_class.expose :name, :embedded
|
|
696
727
|
presenter = fresh_class.new(EntitySpec::EmbeddedExampleWithMany.new)
|
|
697
|
-
presenter.serializable_hash.
|
|
728
|
+
expect(presenter.serializable_hash).to eq(name: 'abc', embedded: [{ abc: 'def' }, { abc: 'def' }])
|
|
698
729
|
end
|
|
699
730
|
|
|
700
731
|
it 'serializes embedded hashes of objects which respond to #serializable_hash' do
|
|
701
732
|
fresh_class.expose :name, :embedded
|
|
702
733
|
presenter = fresh_class.new(EntitySpec::EmbeddedExampleWithHash.new)
|
|
703
|
-
presenter.serializable_hash.
|
|
734
|
+
expect(presenter.serializable_hash).to eq(name: 'abc', embedded: { a: nil, b: { abc: 'def' } })
|
|
704
735
|
end
|
|
705
736
|
end
|
|
706
737
|
end
|
|
@@ -725,14 +756,14 @@ describe Grape::Entity do
|
|
|
725
756
|
end
|
|
726
757
|
|
|
727
758
|
it 'passes through bare expose attributes' do
|
|
728
|
-
subject.send(:value_for, :name).
|
|
759
|
+
expect(subject.send(:value_for, :name)).to eq attributes[:name]
|
|
729
760
|
end
|
|
730
761
|
|
|
731
762
|
it 'instantiates a representation if that is called for' do
|
|
732
763
|
rep = subject.send(:value_for, :friends)
|
|
733
|
-
rep.reject { |r| r.is_a?(fresh_class) }.
|
|
734
|
-
rep.first.serializable_hash[:name].
|
|
735
|
-
rep.last.serializable_hash[:name].
|
|
764
|
+
expect(rep.reject { |r| r.is_a?(fresh_class) }).to be_empty
|
|
765
|
+
expect(rep.first.serializable_hash[:name]).to eq 'Friend 1'
|
|
766
|
+
expect(rep.last.serializable_hash[:name]).to eq 'Friend 2'
|
|
736
767
|
end
|
|
737
768
|
|
|
738
769
|
context 'child representations' do
|
|
@@ -749,10 +780,10 @@ describe Grape::Entity do
|
|
|
749
780
|
end
|
|
750
781
|
|
|
751
782
|
rep = subject.send(:value_for, :friends)
|
|
752
|
-
rep.
|
|
753
|
-
rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }.
|
|
754
|
-
rep.first.serializable_hash[:name].
|
|
755
|
-
rep.last.serializable_hash[:name].
|
|
783
|
+
expect(rep).to be_kind_of Array
|
|
784
|
+
expect(rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }).to be_empty
|
|
785
|
+
expect(rep.first.serializable_hash[:name]).to eq 'Friend 1'
|
|
786
|
+
expect(rep.last.serializable_hash[:name]).to eq 'Friend 2'
|
|
756
787
|
end
|
|
757
788
|
|
|
758
789
|
it 'passes through the proc which returns an array of objects with custom options(:using)' do
|
|
@@ -770,10 +801,10 @@ describe Grape::Entity do
|
|
|
770
801
|
end
|
|
771
802
|
|
|
772
803
|
rep = subject.send(:value_for, :custom_friends)
|
|
773
|
-
rep.
|
|
774
|
-
rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }.
|
|
775
|
-
rep.first.serializable_hash.
|
|
776
|
-
rep.last.serializable_hash.
|
|
804
|
+
expect(rep).to be_kind_of Array
|
|
805
|
+
expect(rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }).to be_empty
|
|
806
|
+
expect(rep.first.serializable_hash).to eq(name: 'Friend 1', email: 'friend1@example.com')
|
|
807
|
+
expect(rep.last.serializable_hash).to eq(name: 'Friend 2', email: 'friend2@example.com')
|
|
777
808
|
end
|
|
778
809
|
|
|
779
810
|
it 'passes through the proc which returns single object with custom options(:using)' do
|
|
@@ -791,8 +822,8 @@ describe Grape::Entity do
|
|
|
791
822
|
end
|
|
792
823
|
|
|
793
824
|
rep = subject.send(:value_for, :first_friend)
|
|
794
|
-
rep.
|
|
795
|
-
rep.serializable_hash.
|
|
825
|
+
expect(rep).to be_kind_of EntitySpec::FriendEntity
|
|
826
|
+
expect(rep.serializable_hash).to eq(name: 'Friend 1', email: 'friend1@example.com')
|
|
796
827
|
end
|
|
797
828
|
|
|
798
829
|
it 'passes through the proc which returns empty with custom options(:using)' do
|
|
@@ -809,8 +840,8 @@ describe Grape::Entity do
|
|
|
809
840
|
end
|
|
810
841
|
|
|
811
842
|
rep = subject.send(:value_for, :first_friend)
|
|
812
|
-
rep.
|
|
813
|
-
rep.serializable_hash.
|
|
843
|
+
expect(rep).to be_kind_of EntitySpec::FriendEntity
|
|
844
|
+
expect(rep.serializable_hash).to be_nil
|
|
814
845
|
end
|
|
815
846
|
|
|
816
847
|
it 'passes through custom options' do
|
|
@@ -827,16 +858,16 @@ describe Grape::Entity do
|
|
|
827
858
|
end
|
|
828
859
|
|
|
829
860
|
rep = subject.send(:value_for, :friends)
|
|
830
|
-
rep.
|
|
831
|
-
rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }.
|
|
832
|
-
rep.first.serializable_hash[:email].
|
|
833
|
-
rep.last.serializable_hash[:email].
|
|
861
|
+
expect(rep).to be_kind_of Array
|
|
862
|
+
expect(rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }).to be_empty
|
|
863
|
+
expect(rep.first.serializable_hash[:email]).to be_nil
|
|
864
|
+
expect(rep.last.serializable_hash[:email]).to be_nil
|
|
834
865
|
|
|
835
866
|
rep = subject.send(:value_for, :friends, user_type: :admin)
|
|
836
|
-
rep.
|
|
837
|
-
rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }.
|
|
838
|
-
rep.first.serializable_hash[:email].
|
|
839
|
-
rep.last.serializable_hash[:email].
|
|
867
|
+
expect(rep).to be_kind_of Array
|
|
868
|
+
expect(rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }).to be_empty
|
|
869
|
+
expect(rep.first.serializable_hash[:email]).to eq 'friend1@example.com'
|
|
870
|
+
expect(rep.last.serializable_hash[:email]).to eq 'friend2@example.com'
|
|
840
871
|
end
|
|
841
872
|
|
|
842
873
|
it 'ignores the :collection parameter in the source options' do
|
|
@@ -853,23 +884,23 @@ describe Grape::Entity do
|
|
|
853
884
|
end
|
|
854
885
|
|
|
855
886
|
rep = subject.send(:value_for, :friends, collection: false)
|
|
856
|
-
rep.
|
|
857
|
-
rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }.
|
|
858
|
-
rep.first.serializable_hash[:email].
|
|
859
|
-
rep.last.serializable_hash[:email].
|
|
887
|
+
expect(rep).to be_kind_of Array
|
|
888
|
+
expect(rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }).to be_empty
|
|
889
|
+
expect(rep.first.serializable_hash[:email]).to eq 'friend1@example.com'
|
|
890
|
+
expect(rep.last.serializable_hash[:email]).to eq 'friend2@example.com'
|
|
860
891
|
end
|
|
861
892
|
end
|
|
862
893
|
|
|
863
894
|
it 'calls through to the proc if there is one' do
|
|
864
|
-
subject.send(:value_for, :computed, awesome: 123).
|
|
895
|
+
expect(subject.send(:value_for, :computed, awesome: 123)).to eq 123
|
|
865
896
|
end
|
|
866
897
|
|
|
867
898
|
it 'returns a formatted value if format_with is passed' do
|
|
868
|
-
subject.send(:value_for, :birthday).
|
|
899
|
+
expect(subject.send(:value_for, :birthday)).to eq '02/27/2012'
|
|
869
900
|
end
|
|
870
901
|
|
|
871
902
|
it 'returns a formatted value if format_with is passed a lambda' do
|
|
872
|
-
subject.send(:value_for, :fantasies).
|
|
903
|
+
expect(subject.send(:value_for, :fantasies)).to eq ['Nessy', 'Double Rainbows', 'Unicorns']
|
|
873
904
|
end
|
|
874
905
|
|
|
875
906
|
it 'tries instance methods on the entity first' do
|
|
@@ -889,8 +920,8 @@ describe Grape::Entity do
|
|
|
889
920
|
|
|
890
921
|
friend = double('Friend', name: 'joe', email: 'joe@example.com')
|
|
891
922
|
rep = EntitySpec::DelegatingEntity.new(friend)
|
|
892
|
-
rep.send(:value_for, :name).
|
|
893
|
-
rep.send(:value_for, :email).
|
|
923
|
+
expect(rep.send(:value_for, :name)).to eq 'cooler name'
|
|
924
|
+
expect(rep.send(:value_for, :email)).to eq 'joe@example.com'
|
|
894
925
|
end
|
|
895
926
|
|
|
896
927
|
context 'using' do
|
|
@@ -907,9 +938,9 @@ describe Grape::Entity do
|
|
|
907
938
|
end
|
|
908
939
|
|
|
909
940
|
rep = subject.send(:value_for, :friends)
|
|
910
|
-
rep.
|
|
911
|
-
rep.size.
|
|
912
|
-
rep.all? { |r| r.is_a?(EntitySpec::UserEntity) }.
|
|
941
|
+
expect(rep).to be_kind_of Array
|
|
942
|
+
expect(rep.size).to eq 2
|
|
943
|
+
expect(rep.all? { |r| r.is_a?(EntitySpec::UserEntity) }).to be true
|
|
913
944
|
end
|
|
914
945
|
|
|
915
946
|
it 'class' do
|
|
@@ -918,9 +949,9 @@ describe Grape::Entity do
|
|
|
918
949
|
end
|
|
919
950
|
|
|
920
951
|
rep = subject.send(:value_for, :friends)
|
|
921
|
-
rep.
|
|
922
|
-
rep.size.
|
|
923
|
-
rep.all? { |r| r.is_a?(EntitySpec::UserEntity) }.
|
|
952
|
+
expect(rep).to be_kind_of Array
|
|
953
|
+
expect(rep.size).to eq 2
|
|
954
|
+
expect(rep.all? { |r| r.is_a?(EntitySpec::UserEntity) }).to be true
|
|
924
955
|
end
|
|
925
956
|
end
|
|
926
957
|
end
|
|
@@ -929,7 +960,7 @@ describe Grape::Entity do
|
|
|
929
960
|
it 'returns an empty hash is no documentation is provided' do
|
|
930
961
|
fresh_class.expose :name
|
|
931
962
|
|
|
932
|
-
subject.documentation.
|
|
963
|
+
expect(subject.documentation).to eq({})
|
|
933
964
|
end
|
|
934
965
|
|
|
935
966
|
it 'returns each defined documentation hash' do
|
|
@@ -938,7 +969,7 @@ describe Grape::Entity do
|
|
|
938
969
|
fresh_class.expose :email, documentation: doc
|
|
939
970
|
fresh_class.expose :birthday
|
|
940
971
|
|
|
941
|
-
subject.documentation.
|
|
972
|
+
expect(subject.documentation).to eq(name: doc, email: doc)
|
|
942
973
|
end
|
|
943
974
|
|
|
944
975
|
it 'returns each defined documentation hash with :as param considering' do
|
|
@@ -947,24 +978,24 @@ describe Grape::Entity do
|
|
|
947
978
|
fresh_class.expose :email, documentation: doc
|
|
948
979
|
fresh_class.expose :birthday
|
|
949
980
|
|
|
950
|
-
subject.documentation.
|
|
981
|
+
expect(subject.documentation).to eq(label: doc, email: doc)
|
|
951
982
|
end
|
|
952
983
|
end
|
|
953
984
|
|
|
954
985
|
describe '#key_for' do
|
|
955
986
|
it 'returns the attribute if no :as is set' do
|
|
956
987
|
fresh_class.expose :name
|
|
957
|
-
subject.class.send(:key_for, :name).
|
|
988
|
+
expect(subject.class.send(:key_for, :name)).to eq :name
|
|
958
989
|
end
|
|
959
990
|
|
|
960
991
|
it 'returns a symbolized version of the attribute' do
|
|
961
992
|
fresh_class.expose :name
|
|
962
|
-
subject.class.send(:key_for, 'name').
|
|
993
|
+
expect(subject.class.send(:key_for, 'name')).to eq :name
|
|
963
994
|
end
|
|
964
995
|
|
|
965
996
|
it 'returns the :as alias if one exists' do
|
|
966
997
|
fresh_class.expose :name, as: :nombre
|
|
967
|
-
subject.class.send(:key_for, 'name').
|
|
998
|
+
expect(subject.class.send(:key_for, 'name')).to eq :nombre
|
|
968
999
|
end
|
|
969
1000
|
end
|
|
970
1001
|
|
|
@@ -972,54 +1003,54 @@ describe Grape::Entity do
|
|
|
972
1003
|
it 'only passes through hash :if exposure if all attributes match' do
|
|
973
1004
|
exposure_options = { if: { condition1: true, condition2: true } }
|
|
974
1005
|
|
|
975
|
-
subject.send(:conditions_met?, exposure_options, {}).
|
|
976
|
-
subject.send(:conditions_met?, exposure_options, condition1: true).
|
|
977
|
-
subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true).
|
|
978
|
-
subject.send(:conditions_met?, exposure_options, condition1: false, condition2: true).
|
|
979
|
-
subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true, other: true).
|
|
1006
|
+
expect(subject.send(:conditions_met?, exposure_options, {})).to be false
|
|
1007
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: true)).to be false
|
|
1008
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true)).to be true
|
|
1009
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: false, condition2: true)).to be false
|
|
1010
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true, other: true)).to be true
|
|
980
1011
|
end
|
|
981
1012
|
|
|
982
1013
|
it 'looks for presence/truthiness if a symbol is passed' do
|
|
983
1014
|
exposure_options = { if: :condition1 }
|
|
984
1015
|
|
|
985
|
-
subject.send(:conditions_met?, exposure_options, {}).
|
|
986
|
-
subject.send(:conditions_met?, exposure_options, condition1: true).
|
|
987
|
-
subject.send(:conditions_met?, exposure_options, condition1: false).
|
|
988
|
-
subject.send(:conditions_met?, exposure_options, condition1: nil).
|
|
1016
|
+
expect(subject.send(:conditions_met?, exposure_options, {})).to be false
|
|
1017
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: true)).to be true
|
|
1018
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: false)).to be false
|
|
1019
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: nil)).to be false
|
|
989
1020
|
end
|
|
990
1021
|
|
|
991
1022
|
it 'looks for absence/falsiness if a symbol is passed' do
|
|
992
1023
|
exposure_options = { unless: :condition1 }
|
|
993
1024
|
|
|
994
|
-
subject.send(:conditions_met?, exposure_options, {}).
|
|
995
|
-
subject.send(:conditions_met?, exposure_options, condition1: true).
|
|
996
|
-
subject.send(:conditions_met?, exposure_options, condition1: false).
|
|
997
|
-
subject.send(:conditions_met?, exposure_options, condition1: nil).
|
|
1025
|
+
expect(subject.send(:conditions_met?, exposure_options, {})).to be true
|
|
1026
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: true)).to be false
|
|
1027
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: false)).to be true
|
|
1028
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: nil)).to be true
|
|
998
1029
|
end
|
|
999
1030
|
|
|
1000
1031
|
it 'only passes through proc :if exposure if it returns truthy value' do
|
|
1001
1032
|
exposure_options = { if: lambda { |_, opts| opts[:true] } }
|
|
1002
1033
|
|
|
1003
|
-
subject.send(:conditions_met?, exposure_options, true: false).
|
|
1004
|
-
subject.send(:conditions_met?, exposure_options, true: true).
|
|
1034
|
+
expect(subject.send(:conditions_met?, exposure_options, true: false)).to be false
|
|
1035
|
+
expect(subject.send(:conditions_met?, exposure_options, true: true)).to be true
|
|
1005
1036
|
end
|
|
1006
1037
|
|
|
1007
1038
|
it 'only passes through hash :unless exposure if any attributes do not match' do
|
|
1008
1039
|
exposure_options = { unless: { condition1: true, condition2: true } }
|
|
1009
1040
|
|
|
1010
|
-
subject.send(:conditions_met?, exposure_options, {}).
|
|
1011
|
-
subject.send(:conditions_met?, exposure_options, condition1: true).
|
|
1012
|
-
subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true).
|
|
1013
|
-
subject.send(:conditions_met?, exposure_options, condition1: false, condition2: true).
|
|
1014
|
-
subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true, other: true).
|
|
1015
|
-
subject.send(:conditions_met?, exposure_options, condition1: false, condition2: false).
|
|
1041
|
+
expect(subject.send(:conditions_met?, exposure_options, {})).to be true
|
|
1042
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: true)).to be false
|
|
1043
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true)).to be false
|
|
1044
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: false, condition2: true)).to be false
|
|
1045
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true, other: true)).to be false
|
|
1046
|
+
expect(subject.send(:conditions_met?, exposure_options, condition1: false, condition2: false)).to be true
|
|
1016
1047
|
end
|
|
1017
1048
|
|
|
1018
1049
|
it 'only passes through proc :unless exposure if it returns falsy value' do
|
|
1019
1050
|
exposure_options = { unless: lambda { |_, options| options[:true] == true } }
|
|
1020
1051
|
|
|
1021
|
-
subject.send(:conditions_met?, exposure_options, true: false).
|
|
1022
|
-
subject.send(:conditions_met?, exposure_options, true: true).
|
|
1052
|
+
expect(subject.send(:conditions_met?, exposure_options, true: false)).to be true
|
|
1053
|
+
expect(subject.send(:conditions_met?, exposure_options, true: true)).to be false
|
|
1023
1054
|
end
|
|
1024
1055
|
end
|
|
1025
1056
|
|
|
@@ -1027,9 +1058,9 @@ describe Grape::Entity do
|
|
|
1027
1058
|
subject { Class.new }
|
|
1028
1059
|
|
|
1029
1060
|
it 'creates an Entity class when called' do
|
|
1030
|
-
subject.
|
|
1061
|
+
expect(subject).not_to be_const_defined :Entity
|
|
1031
1062
|
subject.send(:include, Grape::Entity::DSL)
|
|
1032
|
-
subject.
|
|
1063
|
+
expect(subject).to be_const_defined :Entity
|
|
1033
1064
|
end
|
|
1034
1065
|
|
|
1035
1066
|
context 'pre-mixed' do
|
|
@@ -1040,19 +1071,19 @@ describe Grape::Entity do
|
|
|
1040
1071
|
expose :name
|
|
1041
1072
|
end
|
|
1042
1073
|
|
|
1043
|
-
subject.entity_class.exposures.
|
|
1074
|
+
expect(subject.entity_class.exposures).not_to be_empty
|
|
1044
1075
|
end
|
|
1045
1076
|
|
|
1046
1077
|
it 'is able to expose straight from the class' do
|
|
1047
1078
|
subject.entity :name, :email
|
|
1048
|
-
subject.entity_class.exposures.size.
|
|
1079
|
+
expect(subject.entity_class.exposures.size).to eq 2
|
|
1049
1080
|
end
|
|
1050
1081
|
|
|
1051
1082
|
it 'is able to mix field and advanced exposures' do
|
|
1052
1083
|
subject.entity :name, :email do
|
|
1053
1084
|
expose :third
|
|
1054
1085
|
end
|
|
1055
|
-
subject.entity_class.exposures.size.
|
|
1086
|
+
expect(subject.entity_class.exposures.size).to eq 3
|
|
1056
1087
|
end
|
|
1057
1088
|
|
|
1058
1089
|
context 'instance' do
|
|
@@ -1060,15 +1091,15 @@ describe Grape::Entity do
|
|
|
1060
1091
|
|
|
1061
1092
|
describe '#entity' do
|
|
1062
1093
|
it 'is an instance of the entity class' do
|
|
1063
|
-
instance.entity.
|
|
1094
|
+
expect(instance.entity).to be_kind_of(subject.entity_class)
|
|
1064
1095
|
end
|
|
1065
1096
|
|
|
1066
1097
|
it 'has an object of itself' do
|
|
1067
|
-
instance.entity.object.
|
|
1098
|
+
expect(instance.entity.object).to eq instance
|
|
1068
1099
|
end
|
|
1069
1100
|
|
|
1070
1101
|
it 'instantiates with options if provided' do
|
|
1071
|
-
instance.entity(awesome: true).options.
|
|
1102
|
+
expect(instance.entity(awesome: true).options).to eq(awesome: true)
|
|
1072
1103
|
end
|
|
1073
1104
|
end
|
|
1074
1105
|
end
|