Skip to content

Commit 5a6d1b6

Browse files
dan-corneanudblock
authored andcommitted
Added except option.
1 parent 11914e8 commit 5a6d1b6

File tree

4 files changed

+102
-7
lines changed

4 files changed

+102
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* [#115](https://github.com/intridea/grape-entity/pull/115): Allowing 'root' to be inherited from parent to child entities - [@guidoprincess](https://github.com/guidoprincess).
66
* [#121](https://github.com/intridea/grape-entity/pull/122): Sublcassed Entity#documentation properly handles unexposed params - [@dan-corneanu](https://github.com/dan-corneanu).
77
* [#134](https://github.com/intridea/grape-entity/pull/134): Subclasses no longer affected in all cases by `unexpose` in parent - [@etehtsea](https://github.com/etehtsea).
8+
* [#135](https://github.com/intridea/grape-entity/pull/135): Added `except` option - [@dan-corneanu](https://github.com/dan-corneanu).
89
* Your contribution here.
910

1011
0.4.5 (2015-03-10)

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ end
222222

223223
#### Returning only the fields you want
224224

225-
After exposing the desired attributes, you can choose which one you need when representing some object or collection, see the example:
225+
After exposing the desired attributes, you can choose which one you need when representing some object or collection by using the only: and except: options. See the example:
226226

227227
```ruby
228228
class UserEntity
@@ -237,7 +237,7 @@ class Entity
237237
expose :user, using: UserEntity
238238
end
239239

240-
data = Entity.represent(model, only: [:name, { user: [:name, :email] }])
240+
data = Entity.represent(model, only: [:title, { user: [:name, :email] }])
241241
data.as_json
242242
```
243243

@@ -256,7 +256,12 @@ This will return something like this:
256256
Instead of returning all the exposed attributes.
257257

258258

259+
The same result can be achieved with the following exposure:
259260

261+
```ruby
262+
data = Entity.represent(model, except: [:id, { user: [:id] }])
263+
data.as_json
264+
```
260265

261266
#### Aliases
262267

lib/grape_entity/entity.rb

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ def self.present_collection(present_collection = false, collection_name = :items
355355
# @option options :serializable [true or false] when true a serializable Hash will be returned
356356
#
357357
# @option options :only [Array] all the fields that should be returned
358+
# @option options :except [Array] all the fields that should not be returned
358359
def self.represent(objects, options = {})
359360
if objects.respond_to?(:to_ary) && ! @present_collection
360361
root_element = root_element(:collection_root)
@@ -444,8 +445,12 @@ def serializable_hash(runtime_options = {})
444445
end
445446

446447
def should_return_attribute?(attribute, options)
447-
return true unless options[:only]
448-
only_fields(options).include?(self.class.key_for(attribute))
448+
key = self.class.key_for(attribute)
449+
only = only_fields(options).nil? ||
450+
only_fields(options).include?(key)
451+
except = except_fields(options) && except_fields(options).include?(key) &&
452+
except_fields(options)[key] == true
453+
only && !except
449454
end
450455

451456
def only_fields(options, for_attribute = nil)
@@ -469,6 +474,27 @@ def only_fields(options, for_attribute = nil)
469474
end
470475
end
471476

477+
def except_fields(options, for_attribute = nil)
478+
return nil unless options[:except]
479+
480+
@except_fields ||= options[:except].each_with_object({}) do |attribute, allowed_fields|
481+
if attribute.is_a?(Hash)
482+
attribute.each do |attr, nested_attrs|
483+
allowed_fields[attr] ||= []
484+
allowed_fields[attr] += nested_attrs
485+
end
486+
else
487+
allowed_fields[attribute] = true
488+
end
489+
end
490+
491+
if for_attribute && @except_fields[for_attribute].is_a?(Array)
492+
@except_fields[for_attribute]
493+
elsif for_attribute.nil?
494+
@except_fields
495+
end
496+
end
497+
472498
alias_method :as_json, :serializable_hash
473499

474500
def to_json(options = {})
@@ -604,6 +630,7 @@ def options_for_using(attribute, options)
604630
using_options.delete(:collection)
605631
using_options[:root] = nil
606632
using_options[:only] = only_fields(using_options, attribute)
633+
using_options[:except] = except_fields(using_options, attribute)
607634

608635
using_options
609636
end

spec/grape_entity/entity_spec.rb

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,21 @@ class Parent < Person
503503
expect(representation).to eq(id: nil, name: nil)
504504
end
505505

506-
it 'can specify children attributes' do
506+
it 'returns all fields except the ones specified in the except option' do
507+
subject.expose(:id, :name, :phone)
508+
representation = subject.represent(OpenStruct.new, except: [:phone], serializable: true)
509+
expect(representation).to eq(id: nil, name: nil)
510+
end
511+
512+
it 'returns only fields specified in the only option and not specified in the except option' do
513+
subject.expose(:id, :name, :phone)
514+
representation = subject.represent(OpenStruct.new,
515+
only: [:name, :phone],
516+
except: [:phone], serializable: true)
517+
expect(representation).to eq(name: nil)
518+
end
519+
520+
it 'can specify children attributes with only' do
507521
user_entity = Class.new(Grape::Entity)
508522
user_entity.expose(:id, :name, :email)
509523

@@ -514,16 +528,50 @@ class Parent < Person
514528
expect(representation).to eq(id: nil, name: nil, user: { name: nil, email: nil })
515529
end
516530

531+
it 'can specify children attributes with except' do
532+
user_entity = Class.new(Grape::Entity)
533+
user_entity.expose(:id, :name, :email)
534+
535+
subject.expose(:id, :name, :phone)
536+
subject.expose(:user, using: user_entity)
537+
538+
representation = subject.represent(OpenStruct.new(user: {}), except: [:phone, { user: [:id] }], serializable: true)
539+
expect(representation).to eq(id: nil, name: nil, user: { name: nil, email: nil })
540+
end
541+
542+
it 'can specify children attributes with mixed only and except' do
543+
user_entity = Class.new(Grape::Entity)
544+
user_entity.expose(:id, :name, :email, :address)
545+
546+
subject.expose(:id, :name, :phone, :mobile_phone)
547+
subject.expose(:user, using: user_entity)
548+
549+
representation = subject.represent(OpenStruct.new(user: {}),
550+
only: [:id, :name, :phone, user: [:id, :name, :email]],
551+
except: [:phone, { user: [:id] }], serializable: true)
552+
expect(representation).to eq(id: nil, name: nil, user: { name: nil, email: nil })
553+
end
554+
517555
context 'specify attribute with exposure condition' do
518556
it 'returns only specified fields' do
519-
subject.expose(:id, :name)
557+
subject.expose(:id)
520558
subject.with_options(if: { condition: true }) do
521559
subject.expose(:name)
522560
end
523561

524562
representation = subject.represent(OpenStruct.new, condition: true, only: [:id, :name], serializable: true)
525563
expect(representation).to eq(id: nil, name: nil)
526564
end
565+
566+
it 'does not return fields specified in the except option' do
567+
subject.expose(:id, :phone)
568+
subject.with_options(if: { condition: true }) do
569+
subject.expose(:name, :mobile_phone)
570+
end
571+
572+
representation = subject.represent(OpenStruct.new, condition: true, except: [:phone, :mobile_phone], serializable: true)
573+
expect(representation).to eq(id: nil, name: nil)
574+
end
527575
end
528576

529577
context 'attribute with alias' do
@@ -534,17 +582,31 @@ class Parent < Person
534582
representation = subject.represent(OpenStruct.new, condition: true, only: [:id, :title], serializable: true)
535583
expect(representation).to eq(id: nil, title: nil)
536584
end
585+
586+
it 'does not return fields specified in the except option' do
587+
subject.expose(:id)
588+
subject.expose(:name, as: :title)
589+
subject.expose(:phone, as: :phone_number)
590+
591+
representation = subject.represent(OpenStruct.new, condition: true, except: [:phone_number], serializable: true)
592+
expect(representation).to eq(id: nil, title: nil)
593+
end
537594
end
538595

539596
context 'attribute that is an entity itself' do
540597
it 'returns correctly the children entity attributes' do
541598
user_entity = Class.new(Grape::Entity)
542599
user_entity.expose(:id, :name, :email)
543600

601+
nephew_entity = Class.new(Grape::Entity)
602+
nephew_entity.expose(:id, :name, :email)
603+
544604
subject.expose(:id, :name, :phone)
545605
subject.expose(:user, using: user_entity)
606+
subject.expose(:nephew, using: nephew_entity)
546607

547-
representation = subject.represent(OpenStruct.new(user: {}), only: [:id, :name, :user], serializable: true)
608+
representation = subject.represent(OpenStruct.new(user: {}),
609+
only: [:id, :name, :user], except: [:nephew], serializable: true)
548610
expect(representation).to eq(id: nil, name: nil, user: { id: nil, name: nil, email: nil })
549611
end
550612
end

0 commit comments

Comments
 (0)