grape-entity 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.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
|
-
[](http://badge.fury.io/rb/grape-entity)
|
|
4
|
+
[](https://travis-ci.org/intridea/grape-entity)
|
|
5
|
+
[](https://gemnasium.com/intridea/grape-entity)
|
|
6
|
+
[](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.
|