grape-entity 0.4.4 → 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -60
- data/.rubocop_todo.yml +64 -0
- data/.travis.yml +3 -2
- data/CHANGELOG.md +11 -1
- data/Gemfile +2 -8
- data/Guardfile +3 -3
- data/README.md +47 -14
- data/RELEASING.md +5 -3
- data/Rakefile +1 -1
- data/grape-entity.gemspec +2 -2
- data/lib/grape_entity/entity.rb +32 -19
- data/lib/grape_entity/version.rb +1 -1
- data/spec/grape_entity/entity_spec.rb +104 -13
- data/spec/spec_helper.rb +1 -3
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77619537334dd75647477a452a937b672ca248cb
|
4
|
+
data.tar.gz: f7c7659bd6f13e2f65efb8091b2ca00a37dfcd74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c73b5b28f9cb7ebcc7ba71e264029e725720cfa79593bcb891aeb5628ca3b50a26699043d38e79f1842a0336f1a66e881d636befa6f20d74e469131f86b1ac04
|
7
|
+
data.tar.gz: 2d086dee3c598f34bedc613fe1da601e7eff2afb6f05d9cd8944c9ffac21e7f16f5e4cea2a686ecdbf1f60c0c6d9f08e918b0f247ed4b23a54df1ec13a6b9f45
|
data/.rubocop.yml
CHANGED
@@ -1,63 +1,5 @@
|
|
1
1
|
AllCops:
|
2
2
|
Exclude:
|
3
|
-
- vendor
|
3
|
+
- vendor/**/*
|
4
4
|
|
5
|
-
|
6
|
-
Enabled: false
|
7
|
-
|
8
|
-
MethodLength:
|
9
|
-
Enabled: false
|
10
|
-
|
11
|
-
ClassLength:
|
12
|
-
Enabled: false
|
13
|
-
|
14
|
-
Documentation:
|
15
|
-
# don't require classes to be documented
|
16
|
-
Enabled: false
|
17
|
-
|
18
|
-
CollectionMethods:
|
19
|
-
# don't prefer map to collect, recuce to inject
|
20
|
-
Enabled: false
|
21
|
-
|
22
|
-
Encoding:
|
23
|
-
# no need to always specify encoding
|
24
|
-
Enabled: false
|
25
|
-
|
26
|
-
Void:
|
27
|
-
# == operator used in void context in specs
|
28
|
-
Enabled: false
|
29
|
-
|
30
|
-
SignalException:
|
31
|
-
# prefer raise to fail
|
32
|
-
EnforcedStyle: only_raise
|
33
|
-
|
34
|
-
RaiseArgs:
|
35
|
-
# don't care for what kind of raise
|
36
|
-
Enabled: false
|
37
|
-
|
38
|
-
PerlBackrefs:
|
39
|
-
# TODO: regular expression matching with $1, $2, etc.
|
40
|
-
Enabled: false
|
41
|
-
|
42
|
-
BlockNesting:
|
43
|
-
# TODO: fix too much nesting
|
44
|
-
Max: 4
|
45
|
-
|
46
|
-
Lambda:
|
47
|
-
# TODO: replace all lambda with -> or Proc
|
48
|
-
Enabled: false
|
49
|
-
|
50
|
-
Blocks:
|
51
|
-
# allow multi-line blocks like expect { }
|
52
|
-
Enabled: false
|
53
|
-
|
54
|
-
WordArray:
|
55
|
-
# %w vs. [ '', ... ]
|
56
|
-
Enabled: false
|
57
|
-
|
58
|
-
CyclomaticComplexity:
|
59
|
-
Enabled: false
|
60
|
-
|
61
|
-
FileName:
|
62
|
-
# allow grape-entity.rb for a require
|
63
|
-
Enabled: false
|
5
|
+
inherit_from: .rubocop_todo.yml
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# This configuration was generated by `rubocop --auto-gen-config`
|
2
|
+
# on 2014-12-11 15:27:22 -0500 using RuboCop version 0.28.0.
|
3
|
+
# The point is for the user to remove these configuration records
|
4
|
+
# one by one as the offenses are removed from the code base.
|
5
|
+
# Note that changes in the inspected code, or installation of new
|
6
|
+
# versions of RuboCop, may require this file to be generated again.
|
7
|
+
|
8
|
+
# Offense count: 5
|
9
|
+
Metrics/AbcSize:
|
10
|
+
Max: 53
|
11
|
+
|
12
|
+
# Offense count: 1
|
13
|
+
# Configuration parameters: CountComments.
|
14
|
+
Metrics/ClassLength:
|
15
|
+
Max: 301
|
16
|
+
|
17
|
+
# Offense count: 4
|
18
|
+
Metrics/CyclomaticComplexity:
|
19
|
+
Max: 17
|
20
|
+
|
21
|
+
# Offense count: 175
|
22
|
+
# Configuration parameters: AllowURI, URISchemes.
|
23
|
+
Metrics/LineLength:
|
24
|
+
Max: 147
|
25
|
+
|
26
|
+
# Offense count: 6
|
27
|
+
# Configuration parameters: CountComments.
|
28
|
+
Metrics/MethodLength:
|
29
|
+
Max: 34
|
30
|
+
|
31
|
+
# Offense count: 4
|
32
|
+
Metrics/PerceivedComplexity:
|
33
|
+
Max: 15
|
34
|
+
|
35
|
+
# Offense count: 2
|
36
|
+
# Cop supports --auto-correct.
|
37
|
+
Style/Blocks:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
# Offense count: 30
|
41
|
+
Style/Documentation:
|
42
|
+
Enabled: false
|
43
|
+
|
44
|
+
# Offense count: 2
|
45
|
+
Style/EachWithObject:
|
46
|
+
Enabled: false
|
47
|
+
|
48
|
+
# Offense count: 1
|
49
|
+
# Configuration parameters: Exclude.
|
50
|
+
Style/FileName:
|
51
|
+
Enabled: false
|
52
|
+
|
53
|
+
# Offense count: 16
|
54
|
+
Style/Lambda:
|
55
|
+
Enabled: false
|
56
|
+
|
57
|
+
# Offense count: 1
|
58
|
+
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
|
59
|
+
Style/Next:
|
60
|
+
Enabled: false
|
61
|
+
|
62
|
+
# Offense count: 2
|
63
|
+
Style/RegexpLiteral:
|
64
|
+
MaxSlashes: 0
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
0.4.5 (2015-03-10)
|
2
|
+
==================
|
3
|
+
|
4
|
+
* [#109](https://github.com/intridea/grape-entity/pull/109): Added `unexpose` method - [@jonmchan](https://github.com/jonmchan).
|
5
|
+
* [#98](https://github.com/intridea/grape-entity/pull/98): Added nested conditionals - [@zbelzer](https://github.com/zbelzer).
|
6
|
+
* [#105](https://github.com/intridea/grape-entity/pull/105): Specify which attribute is missing in which Entity - [@jhollinger](https://github.com/jhollinger).
|
7
|
+
* [#111](https://github.com/intridea/grape-entity/pull/111): Fix: allow usage of attributes with name 'key' if `Hash` objects are used - [@croeck](https://github.com/croeck).
|
8
|
+
* [#110](https://github.com/intridea/grape-entity/pull/110): Fix: safe exposure when using `Hash` models - [@croeck](https://github.com/croeck).
|
9
|
+
* [#91](https://github.com/intridea/grape-entity/pull/91): Fix: OpenStruct serializing - [@etehtsea](https://github.com/etehtsea).
|
10
|
+
|
1
11
|
0.4.4 (2014-08-17)
|
2
12
|
==================
|
3
13
|
|
@@ -7,7 +17,7 @@
|
|
7
17
|
0.4.3 (2014-06-12)
|
8
18
|
==================
|
9
19
|
|
10
|
-
* [#77](https://github.com/intridea/grape-entity/pull/77): Fix compatibility with Rspec 3 - [@justfalter](https://github.com/justfalter).
|
20
|
+
* [#77](https://github.com/intridea/grape-entity/pull/77): Fix: compatibility with Rspec 3 - [@justfalter](https://github.com/justfalter).
|
11
21
|
* [#76](https://github.com/intridea/grape-entity/pull/76): Improve performance of entity serialization - [@justfalter](https://github.com/justfalter)
|
12
22
|
|
13
23
|
0.4.2 (2014-04-03)
|
data/Gemfile
CHANGED
@@ -11,12 +11,6 @@ group :development, :test do
|
|
11
11
|
gem 'growl'
|
12
12
|
gem 'json'
|
13
13
|
gem 'rspec'
|
14
|
-
gem 'rack-test',
|
15
|
-
gem 'rubocop', '0.
|
16
|
-
end
|
17
|
-
|
18
|
-
platforms :rbx do
|
19
|
-
gem 'rubysl', '~> 2.0'
|
20
|
-
gem 'parser', '~> 2.1'
|
21
|
-
gem 'racc', '~> 1.4'
|
14
|
+
gem 'rack-test', '~> 0.6.2', require: 'rack/test'
|
15
|
+
gem 'rubocop', '0.28.0'
|
22
16
|
end
|
data/Guardfile
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# A sample Guardfile
|
2
2
|
# More info at https://github.com/guard/guard#readme
|
3
3
|
|
4
|
-
guard 'rspec', :
|
4
|
+
guard 'rspec', version: 2 do
|
5
5
|
watch(%r{^spec/.+_spec\.rb$})
|
6
6
|
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
|
-
watch(%r{^spec/support/shared_versioning_examples.rb$}) { |
|
8
|
-
watch('spec/spec_helper.rb') {
|
7
|
+
watch(%r{^spec/support/shared_versioning_examples.rb$}) { |_m| 'spec/' }
|
8
|
+
watch('spec/spec_helper.rb') { 'spec/' }
|
9
9
|
end
|
10
10
|
|
11
11
|
guard 'bundler' do
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# Grape::Entity
|
2
2
|
|
3
|
-
[![
|
3
|
+
[![Gem Version](http://img.shields.io/gem/v/grape-entity.svg)](http://badge.fury.io/rb/grape-entity)
|
4
|
+
[![Build Status](http://img.shields.io/travis/intridea/grape-entity.svg)](https://travis-ci.org/intridea/grape-entity)
|
5
|
+
[![Dependency Status](https://gemnasium.com/intridea/grape-entity.svg)](https://gemnasium.com/intridea/grape-entity)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/intridea/grape-entity.svg)](https://codeclimate.com/github/intridea/grape-entity)
|
4
7
|
|
5
8
|
## Introduction
|
6
9
|
|
@@ -66,7 +69,7 @@ expose :user_name, :ip
|
|
66
69
|
The field lookup takes several steps
|
67
70
|
|
68
71
|
* first try `entity-instance.exposure`
|
69
|
-
* next try `object.exposure`
|
72
|
+
* next try `object.exposure`
|
70
73
|
* next try `object.fetch(exposure)`
|
71
74
|
* last raise an Exception
|
72
75
|
|
@@ -81,7 +84,7 @@ expose :replies, using: API::Status, as: :replies
|
|
81
84
|
Presenter classes can also be specified in string format, which helps with circular dependencies.
|
82
85
|
|
83
86
|
```ruby
|
84
|
-
expose :replies, using:
|
87
|
+
expose :replies, using: "API::Status", as: :replies
|
85
88
|
```
|
86
89
|
|
87
90
|
#### Conditional Exposure
|
@@ -117,6 +120,16 @@ expose :contact_info do
|
|
117
120
|
end
|
118
121
|
```
|
119
122
|
|
123
|
+
You can also conditionally expose attributes in nested exposures:
|
124
|
+
```ruby
|
125
|
+
expose :contact_info do
|
126
|
+
expose :phone
|
127
|
+
expose :address, using: API::Address
|
128
|
+
expose :email, if: lambda { |instance, options| options[:type] == :full }
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
|
120
133
|
#### Collection Exposure
|
121
134
|
|
122
135
|
Use `root(plural, singular = nil)` to expose an object or a collection of objects with a root key.
|
@@ -127,16 +140,16 @@ expose :id, :name, ...
|
|
127
140
|
```
|
128
141
|
|
129
142
|
By default every object of a collection is wrapped into an instance of your `Entity` class.
|
130
|
-
You can override this behavior and
|
143
|
+
You can override this behavior and wrap the whole collection into one instance of your `Entity`
|
131
144
|
class.
|
132
145
|
|
133
146
|
As example:
|
134
147
|
|
135
148
|
```ruby
|
136
|
-
|
149
|
+
|
137
150
|
present_collection true, :collection_name # `collection_name` is optional and defaults to `items`
|
138
151
|
expose :collection_name, using: API:Items
|
139
|
-
|
152
|
+
|
140
153
|
|
141
154
|
```
|
142
155
|
|
@@ -186,6 +199,31 @@ private
|
|
186
199
|
end
|
187
200
|
```
|
188
201
|
|
202
|
+
#### Unexpose
|
203
|
+
|
204
|
+
To undefine an exposed field, use the ```.unexpose``` method. Useful for modifying inherited entities.
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
class UserData < Grape::Entity
|
208
|
+
expose :name
|
209
|
+
expose :address1
|
210
|
+
expose :address2
|
211
|
+
expose :address_state
|
212
|
+
expose :address_city
|
213
|
+
expose :email
|
214
|
+
expose :phone
|
215
|
+
end
|
216
|
+
|
217
|
+
class MailingAddress < UserData
|
218
|
+
unexpose :email
|
219
|
+
unexpose :phone
|
220
|
+
end
|
221
|
+
```
|
222
|
+
|
223
|
+
|
224
|
+
|
225
|
+
|
226
|
+
|
189
227
|
#### Aliases
|
190
228
|
|
191
229
|
Expose under a different name with `:as`.
|
@@ -346,18 +384,13 @@ Also see [Grape Entity Matchers](https://github.com/agileanimal/grape-entity-mat
|
|
346
384
|
|
347
385
|
## Contributing
|
348
386
|
|
349
|
-
|
350
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
351
|
-
3. Write tests. Make changes. Run `rubocop`.
|
352
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
353
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
354
|
-
5. Create a new pull request
|
387
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
355
388
|
|
356
389
|
## License
|
357
390
|
|
358
|
-
MIT License. See LICENSE for details.
|
391
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
359
392
|
|
360
393
|
## Copyright
|
361
394
|
|
362
|
-
Copyright (c) 2010-
|
395
|
+
Copyright (c) 2010-2014 Michael Bleigh, Intridea, Inc., and contributors.
|
363
396
|
|
data/RELEASING.md
CHANGED
@@ -31,7 +31,7 @@ Remove the line with "Your contribution here.", since there will be no more cont
|
|
31
31
|
Commit your changes.
|
32
32
|
|
33
33
|
```
|
34
|
-
git add
|
34
|
+
git add CHANGELOG.md lib/grape-entity/version.rb
|
35
35
|
git commit -m "Preparing for release, 0.4.0."
|
36
36
|
git push origin master
|
37
37
|
```
|
@@ -58,11 +58,13 @@ Next Release
|
|
58
58
|
* Your contribution here.
|
59
59
|
```
|
60
60
|
|
61
|
+
Increment the minor version, modify [lib/grape-entity/version.rb](lib/grape-entity/version.rb).
|
62
|
+
|
61
63
|
Comit your changes.
|
62
64
|
|
63
65
|
```
|
64
|
-
git add CHANGELOG.md
|
65
|
-
git commit -m "Preparing for next release."
|
66
|
+
git add CHANGELOG.md lib/grape-entity/version.rb
|
67
|
+
git commit -m "Preparing for next release, 0.4.1."
|
66
68
|
git push origin master
|
67
69
|
```
|
68
70
|
|
data/Rakefile
CHANGED
data/grape-entity.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ['Michael Bleigh']
|
9
9
|
s.email = ['michael@intridea.com']
|
10
10
|
s.homepage = 'https://github.com/intridea/grape-entity'
|
11
|
-
s.summary =
|
12
|
-
s.description =
|
11
|
+
s.summary = 'A simple facade for managing the relationship between your model and API.'
|
12
|
+
s.description = 'Extracted from Grape, A Ruby framework for rapid API development with great conventions.'
|
13
13
|
s.license = 'MIT'
|
14
14
|
|
15
15
|
s.rubyforge_project = 'grape-entity'
|
data/lib/grape_entity/entity.rb
CHANGED
@@ -127,11 +127,11 @@ module Grape
|
|
127
127
|
options = merge_options(args.last.is_a?(Hash) ? args.pop : {})
|
128
128
|
|
129
129
|
if args.size > 1
|
130
|
-
|
131
|
-
|
130
|
+
fail ArgumentError, 'You may not use the :as option on multi-attribute exposures.' if options[:as]
|
131
|
+
fail ArgumentError, 'You may not use block-setting on multi-attribute exposures.' if block_given?
|
132
132
|
end
|
133
133
|
|
134
|
-
|
134
|
+
fail ArgumentError, 'You may not use block-setting when also using format_with' if block_given? && options[:format_with].respond_to?(:call)
|
135
135
|
|
136
136
|
options[:proc] = block if block_given? && block.parameters.any?
|
137
137
|
|
@@ -158,6 +158,10 @@ module Grape
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
161
|
+
def self.unexpose(attribute)
|
162
|
+
exposures.delete(attribute)
|
163
|
+
end
|
164
|
+
|
161
165
|
# Set options that will be applied to any exposures declared inside the block.
|
162
166
|
#
|
163
167
|
# @example Multi-exposure if
|
@@ -270,7 +274,7 @@ module Grape
|
|
270
274
|
# end
|
271
275
|
#
|
272
276
|
def self.format_with(name, &block)
|
273
|
-
|
277
|
+
fail ArgumentError, 'You must pass a block for formatters' unless block_given?
|
274
278
|
formatters[name.to_sym] = block
|
275
279
|
end
|
276
280
|
|
@@ -461,9 +465,9 @@ module Grape
|
|
461
465
|
output[self.class.key_for(attribute)] =
|
462
466
|
if partial_output.respond_to? :serializable_hash
|
463
467
|
partial_output.serializable_hash(runtime_options)
|
464
|
-
elsif partial_output.
|
465
|
-
partial_output.map
|
466
|
-
elsif partial_output.
|
468
|
+
elsif partial_output.is_a?(Array) && !partial_output.map { |o| o.respond_to? :serializable_hash }.include?(false)
|
469
|
+
partial_output.map(&:serializable_hash)
|
470
|
+
elsif partial_output.is_a?(Hash)
|
467
471
|
partial_output.each do |key, value|
|
468
472
|
partial_output[key] = value.serializable_hash if value.respond_to? :serializable_hash
|
469
473
|
end
|
@@ -535,10 +539,14 @@ module Grape
|
|
535
539
|
end
|
536
540
|
|
537
541
|
elsif nested_exposures.any?
|
538
|
-
|
539
|
-
|
540
|
-
|
542
|
+
nested_attributes =
|
543
|
+
nested_exposures.map do |nested_attribute, nested_exposure_options|
|
544
|
+
if conditions_met?(nested_exposure_options, options)
|
545
|
+
[self.class.key_for(nested_attribute), value_for(nested_attribute, options)]
|
546
|
+
end
|
547
|
+
end
|
541
548
|
|
549
|
+
Hash[nested_attributes.compact]
|
542
550
|
else
|
543
551
|
delegate_attribute(attribute)
|
544
552
|
end
|
@@ -548,13 +556,17 @@ module Grape
|
|
548
556
|
name = self.class.name_for(attribute)
|
549
557
|
if respond_to?(name, true)
|
550
558
|
send(name)
|
559
|
+
elsif object.is_a?(Hash)
|
560
|
+
object[name]
|
561
|
+
elsif object.respond_to?(name, true)
|
562
|
+
object.send(name)
|
563
|
+
elsif object.respond_to?(:fetch, true)
|
564
|
+
object.fetch(name)
|
551
565
|
else
|
552
|
-
|
566
|
+
begin
|
553
567
|
object.send(name)
|
554
|
-
|
555
|
-
|
556
|
-
else
|
557
|
-
raise ArgumentError
|
568
|
+
rescue NoMethodError
|
569
|
+
raise NoMethodError, "#{self.class.name} missing attribute `#{name}' on #{object}"
|
558
570
|
end
|
559
571
|
end
|
560
572
|
end
|
@@ -562,9 +574,10 @@ module Grape
|
|
562
574
|
def valid_exposure?(attribute, exposure_options)
|
563
575
|
nested_exposures = self.class.nested_exposures_for(attribute)
|
564
576
|
(nested_exposures.any? && nested_exposures.all? { |a, o| valid_exposure?(a, o) }) || \
|
565
|
-
|
566
|
-
|
567
|
-
|
577
|
+
exposure_options.key?(:proc) || \
|
578
|
+
!exposure_options[:safe] || \
|
579
|
+
object.respond_to?(self.class.name_for(attribute)) || \
|
580
|
+
object.is_a?(Hash) && object.key?(self.class.name_for(attribute))
|
568
581
|
end
|
569
582
|
|
570
583
|
def conditions_met?(exposure_options, options)
|
@@ -638,7 +651,7 @@ module Grape
|
|
638
651
|
# @param options [Hash] Exposure options.
|
639
652
|
def self.valid_options(options)
|
640
653
|
options.keys.each do |key|
|
641
|
-
|
654
|
+
fail ArgumentError, "#{key.inspect} is not a valid option." unless OPTIONS.include?(key)
|
642
655
|
end
|
643
656
|
|
644
657
|
options[:using] = options.delete(:with) if options.key?(:with)
|
data/lib/grape_entity/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
2
3
|
|
3
4
|
describe Grape::Entity do
|
4
|
-
|
5
5
|
let(:fresh_class) { Class.new(Grape::Entity) }
|
6
6
|
|
7
7
|
context 'class methods' do
|
@@ -110,6 +110,15 @@ describe Grape::Entity do
|
|
110
110
|
)
|
111
111
|
end
|
112
112
|
|
113
|
+
it 'does not represent nested exposures whose conditions are not met' do
|
114
|
+
subject.expose :awesome do
|
115
|
+
subject.expose(:condition_met, if: lambda { |_, _| true }) { |_| 'value' }
|
116
|
+
subject.expose(:condition_not_met, if: lambda { |_, _| false }) { |_| 'value' }
|
117
|
+
end
|
118
|
+
|
119
|
+
expect(subject.represent({}).send(:value_for, :awesome)).to eq(condition_met: 'value')
|
120
|
+
end
|
121
|
+
|
113
122
|
it 'does not represent attributes, declared inside nested exposure, outside of it' do
|
114
123
|
subject.expose :awesome do
|
115
124
|
subject.expose(:nested) { |_| 'value' }
|
@@ -269,6 +278,48 @@ describe Grape::Entity do
|
|
269
278
|
end
|
270
279
|
end
|
271
280
|
|
281
|
+
describe '.unexpose' do
|
282
|
+
it 'is able to remove exposed attributes' do
|
283
|
+
subject.expose :name, :email
|
284
|
+
subject.unexpose :email
|
285
|
+
|
286
|
+
expect(subject.exposures).to eq(name: {})
|
287
|
+
end
|
288
|
+
|
289
|
+
context 'inherited exposures' do
|
290
|
+
it 'when called from child class, only removes from the attribute from child' do
|
291
|
+
subject.expose :name, :email
|
292
|
+
child_class = Class.new(subject)
|
293
|
+
child_class.unexpose :email
|
294
|
+
|
295
|
+
expect(child_class.exposures).to eq(name: {})
|
296
|
+
expect(subject.exposures).to eq(name: {}, email: {})
|
297
|
+
end
|
298
|
+
|
299
|
+
# the following 2 behaviors are testing because it is not most intuitive and could be confusing
|
300
|
+
context 'when called from the parent class' do
|
301
|
+
it 'remove from parent and all child classes that have not locked down their attributes with an .exposures call' do
|
302
|
+
subject.expose :name, :email
|
303
|
+
child_class = Class.new(subject)
|
304
|
+
subject.unexpose :email
|
305
|
+
|
306
|
+
expect(subject.exposures).to eq(name: {})
|
307
|
+
expect(child_class.exposures).to eq(name: {})
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'remove from parent and do not remove from child classes that have locked down their attributes with an .exposures call' do
|
311
|
+
subject.expose :name, :email
|
312
|
+
child_class = Class.new(subject)
|
313
|
+
child_class.exposures
|
314
|
+
subject.unexpose :email
|
315
|
+
|
316
|
+
expect(subject.exposures).to eq(name: {})
|
317
|
+
expect(child_class.exposures).to eq(name: {}, email: {})
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
272
323
|
describe '.with_options' do
|
273
324
|
it 'raises an error for unknown options' do
|
274
325
|
block = proc do
|
@@ -417,7 +468,7 @@ describe Grape::Entity do
|
|
417
468
|
representation = subject.represent(4.times.map { Object.new })
|
418
469
|
expect(representation).to be_kind_of Array
|
419
470
|
expect(representation.size).to eq(4)
|
420
|
-
expect(representation.reject { |r| r.
|
471
|
+
expect(representation.reject { |r| r.is_a?(subject) }).to be_empty
|
421
472
|
end
|
422
473
|
|
423
474
|
it 'adds the collection: true option if called with a collection' do
|
@@ -442,6 +493,19 @@ describe Grape::Entity do
|
|
442
493
|
representation = subject.represent({ awesome: true }, serializable: true)
|
443
494
|
expect(representation).to eq(awesome: true)
|
444
495
|
end
|
496
|
+
|
497
|
+
it 'returns a serialized hash of an OpenStruct' do
|
498
|
+
subject.expose(:awesome)
|
499
|
+
representation = subject.represent(OpenStruct.new, serializable: true)
|
500
|
+
expect(representation).to eq(awesome: nil)
|
501
|
+
end
|
502
|
+
|
503
|
+
it 'raises error if field not found' do
|
504
|
+
subject.expose(:awesome)
|
505
|
+
expect do
|
506
|
+
subject.represent(Object.new, serializable: true)
|
507
|
+
end.to raise_error(NoMethodError, /missing attribute `awesome'/)
|
508
|
+
end
|
445
509
|
end
|
446
510
|
|
447
511
|
describe '.present_collection' do
|
@@ -491,7 +555,7 @@ describe Grape::Entity do
|
|
491
555
|
expect(representation).to have_key 'things'
|
492
556
|
expect(representation['things']).to be_kind_of Array
|
493
557
|
expect(representation['things'].size).to eq 4
|
494
|
-
expect(representation['things'].reject { |r| r.
|
558
|
+
expect(representation['things'].reject { |r| r.is_a?(subject) }).to be_empty
|
495
559
|
end
|
496
560
|
end
|
497
561
|
|
@@ -500,7 +564,7 @@ describe Grape::Entity do
|
|
500
564
|
representation = subject.represent(4.times.map { Object.new }, root: false)
|
501
565
|
expect(representation).to be_kind_of Array
|
502
566
|
expect(representation.size).to eq 4
|
503
|
-
expect(representation.reject { |r| r.
|
567
|
+
expect(representation.reject { |r| r.is_a?(subject) }).to be_empty
|
504
568
|
end
|
505
569
|
it 'can use a different name' do
|
506
570
|
representation = subject.represent(4.times.map { Object.new }, root: 'others')
|
@@ -508,7 +572,7 @@ describe Grape::Entity do
|
|
508
572
|
expect(representation).to have_key 'others'
|
509
573
|
expect(representation['others']).to be_kind_of Array
|
510
574
|
expect(representation['others'].size).to eq 4
|
511
|
-
expect(representation['others'].reject { |r| r.
|
575
|
+
expect(representation['others'].reject { |r| r.is_a?(subject) }).to be_empty
|
512
576
|
end
|
513
577
|
end
|
514
578
|
end
|
@@ -532,7 +596,7 @@ describe Grape::Entity do
|
|
532
596
|
representation = subject.represent(4.times.map { Object.new })
|
533
597
|
expect(representation).to be_kind_of Array
|
534
598
|
expect(representation.size).to eq 4
|
535
|
-
expect(representation.reject { |r| r.
|
599
|
+
expect(representation.reject { |r| r.is_a?(subject) }).to be_empty
|
536
600
|
end
|
537
601
|
end
|
538
602
|
end
|
@@ -555,7 +619,7 @@ describe Grape::Entity do
|
|
555
619
|
expect(representation).to have_key('things')
|
556
620
|
expect(representation['things']).to be_kind_of Array
|
557
621
|
expect(representation['things'].size).to eq 4
|
558
|
-
expect(representation['things'].reject { |r| r.
|
622
|
+
expect(representation['things'].reject { |r| r.is_a?(subject) }).to be_empty
|
559
623
|
end
|
560
624
|
end
|
561
625
|
end
|
@@ -574,25 +638,26 @@ describe Grape::Entity do
|
|
574
638
|
expect(entity.options).to eq({})
|
575
639
|
end
|
576
640
|
end
|
577
|
-
|
578
641
|
end
|
579
642
|
|
580
643
|
context 'instance methods' do
|
581
|
-
|
582
644
|
let(:model) { double(attributes) }
|
583
645
|
|
584
|
-
let(:attributes)
|
646
|
+
let(:attributes) do
|
585
647
|
{
|
586
648
|
name: 'Bob Bobson',
|
587
649
|
email: 'bob@example.com',
|
588
650
|
birthday: Time.gm(2012, 2, 27),
|
589
651
|
fantasies: ['Unicorns', 'Double Rainbows', 'Nessy'],
|
652
|
+
characteristics: [
|
653
|
+
{ key: 'hair_color', value: 'brown' }
|
654
|
+
],
|
590
655
|
friends: [
|
591
|
-
double(name: 'Friend 1', email: 'friend1@example.com', fantasies: [], birthday: Time.gm(2012, 2, 27), friends: []),
|
592
|
-
double(name: 'Friend 2', email: 'friend2@example.com', fantasies: [], birthday: Time.gm(2012, 2, 27), friends: [])
|
656
|
+
double(name: 'Friend 1', email: 'friend1@example.com', characteristics: [], fantasies: [], birthday: Time.gm(2012, 2, 27), friends: []),
|
657
|
+
double(name: 'Friend 2', email: 'friend2@example.com', characteristics: [], fantasies: [], birthday: Time.gm(2012, 2, 27), friends: [])
|
593
658
|
]
|
594
659
|
}
|
595
|
-
|
660
|
+
end
|
596
661
|
|
597
662
|
subject { fresh_class.new(model) }
|
598
663
|
|
@@ -621,6 +686,13 @@ describe Grape::Entity do
|
|
621
686
|
expect(res).to have_key :name
|
622
687
|
end
|
623
688
|
|
689
|
+
it 'does expose attributes marked as safe if model is a hash object' do
|
690
|
+
fresh_class.expose :name, safe: true
|
691
|
+
|
692
|
+
res = fresh_class.new(name: 'myname').serializable_hash
|
693
|
+
expect(res).to have_key :name
|
694
|
+
end
|
695
|
+
|
624
696
|
it "does not expose attributes that don't exist on the object, even with criteria" do
|
625
697
|
fresh_class.expose :email
|
626
698
|
fresh_class.expose :nonexistent_attribute, safe: true, if: lambda { false }
|
@@ -844,6 +916,25 @@ describe Grape::Entity do
|
|
844
916
|
expect(rep.serializable_hash).to be_nil
|
845
917
|
end
|
846
918
|
|
919
|
+
it 'passes through exposed entity with key and value attributes' do
|
920
|
+
module EntitySpec
|
921
|
+
class CharacteristicsEntity < Grape::Entity
|
922
|
+
root 'characteristics', 'characteristic'
|
923
|
+
expose :key, :value
|
924
|
+
end
|
925
|
+
end
|
926
|
+
|
927
|
+
fresh_class.class_eval do
|
928
|
+
expose :characteristics, using: EntitySpec::CharacteristicsEntity
|
929
|
+
end
|
930
|
+
|
931
|
+
rep = subject.send(:value_for, :characteristics)
|
932
|
+
expect(rep).to be_kind_of Array
|
933
|
+
expect(rep.reject { |r| r.is_a?(EntitySpec::CharacteristicsEntity) }).to be_empty
|
934
|
+
expect(rep.first.serializable_hash[:key]).to eq 'hair_color'
|
935
|
+
expect(rep.first.serializable_hash[:value]).to eq 'brown'
|
936
|
+
end
|
937
|
+
|
847
938
|
it 'passes through custom options' do
|
848
939
|
module EntitySpec
|
849
940
|
class FriendEntity < Grape::Entity
|
data/spec/spec_helper.rb
CHANGED
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.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Bleigh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -119,6 +119,7 @@ files:
|
|
119
119
|
- .gitignore
|
120
120
|
- .rspec
|
121
121
|
- .rubocop.yml
|
122
|
+
- .rubocop_todo.yml
|
122
123
|
- .travis.yml
|
123
124
|
- .yardopts
|
124
125
|
- CHANGELOG.md
|
@@ -157,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
158
|
version: '0'
|
158
159
|
requirements: []
|
159
160
|
rubyforge_project: grape-entity
|
160
|
-
rubygems_version: 2.
|
161
|
+
rubygems_version: 2.4.5
|
161
162
|
signing_key:
|
162
163
|
specification_version: 4
|
163
164
|
summary: A simple facade for managing the relationship between your model and API.
|