Skip to content

Add support for except option. #135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* [#115](https://github.com/intridea/grape-entity/pull/115): Allowing 'root' to be inherited from parent to child entities - [@guidoprincess](https://github.com/guidoprincess).
* [#121](https://github.com/intridea/grape-entity/pull/122): Sublcassed Entity#documentation properly handles unexposed params - [@dan-corneanu](https://github.com/dan-corneanu).
* [#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).
* [#135](https://github.com/intridea/grape-entity/pull/135): Add support for except `options` - [@dan-corneanu](https://github.com/dan-corneanu).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be Added ...

* Your contribution here.

0.4.5 (2015-03-10)
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ end

#### Returning only the fields you want

After exposing the desired attributes, you can choose which one you need when representing some object or collection, see the example:
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:

```ruby
class UserEntity
Expand All @@ -237,7 +237,7 @@ class Entity
expose :user, using: UserEntity
end

data = Entity.represent(model, only: [:name, { user: [:name, :email] }])
data = Entity.represent(model, only: [:title, { user: [:name, :email] }])
data.as_json
```

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


The same result can be achieved with the following exposure:

```ruby
data = Entity.represent(model, except: [:id, { user: [:id] }])
data.as_json
```

#### Aliases

Expand Down
31 changes: 29 additions & 2 deletions lib/grape_entity/entity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ def self.present_collection(present_collection = false, collection_name = :items
# @option options :serializable [true or false] when true a serializable Hash will be returned
#
# @option options :only [Array] all the fields that should be returned
# @option options :except [Array] all the fields that should not be returned
def self.represent(objects, options = {})
if objects.respond_to?(:to_ary) && ! @present_collection
root_element = root_element(:collection_root)
Expand Down Expand Up @@ -444,8 +445,12 @@ def serializable_hash(runtime_options = {})
end

def should_return_attribute?(attribute, options)
return true unless options[:only]
only_fields(options).include?(self.class.key_for(attribute))
key = self.class.key_for(attribute)
only = only_fields(options).nil? ||
only_fields(options).include?(key)
except = except_fields(options) && except_fields(options).include?(key) &&
except_fields(options)[key] == true
only && !except
end

def only_fields(options, for_attribute = nil)
Expand All @@ -469,6 +474,27 @@ def only_fields(options, for_attribute = nil)
end
end

def except_fields(options, for_attribute = nil)
return nil unless options[:except]

@except_fields ||= options[:except].each_with_object({}) do |attribute, allowed_fields|
if attribute.is_a?(Hash)
attribute.each do |attr, nested_attrs|
allowed_fields[attr] ||= []
allowed_fields[attr] += nested_attrs
end
else
allowed_fields[attribute] = true
end
end

if for_attribute && @except_fields[for_attribute].is_a?(Array)
@except_fields[for_attribute]
elsif for_attribute.nil?
@except_fields
end
end

alias_method :as_json, :serializable_hash

def to_json(options = {})
Expand Down Expand Up @@ -604,6 +630,7 @@ def options_for_using(attribute, options)
using_options.delete(:collection)
using_options[:root] = nil
using_options[:only] = only_fields(using_options, attribute)
using_options[:except] = except_fields(using_options, attribute)

using_options
end
Expand Down
68 changes: 65 additions & 3 deletions spec/grape_entity/entity_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,21 @@ class Parent < Person
expect(representation).to eq(id: nil, name: nil)
end

it 'can specify children attributes' do
it 'returns all fields except the ones specified in the except option' do
subject.expose(:id, :name, :phone)
representation = subject.represent(OpenStruct.new, except: [:phone], serializable: true)
expect(representation).to eq(id: nil, name: nil)
end

it 'returns only fields specified in the only option and not specified in the except option' do
subject.expose(:id, :name, :phone)
representation = subject.represent(OpenStruct.new,
only: [:name, :phone],
except: [:phone], serializable: true)
expect(representation).to eq(name: nil)
end

it 'can specify children attributes with only' do
user_entity = Class.new(Grape::Entity)
user_entity.expose(:id, :name, :email)

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

it 'can specify children attributes with except' do
user_entity = Class.new(Grape::Entity)
user_entity.expose(:id, :name, :email)

subject.expose(:id, :name, :phone)
subject.expose(:user, using: user_entity)

representation = subject.represent(OpenStruct.new(user: {}), except: [:phone, { user: [:id] }], serializable: true)
expect(representation).to eq(id: nil, name: nil, user: { name: nil, email: nil })
end

it 'can specify children attributes with mixed only and except' do
user_entity = Class.new(Grape::Entity)
user_entity.expose(:id, :name, :email, :address)

subject.expose(:id, :name, :phone, :mobile_phone)
subject.expose(:user, using: user_entity)

representation = subject.represent(OpenStruct.new(user: {}),
only: [:id, :name, :phone, user: [:id, :name, :email]],
except: [:phone, { user: [:id] }], serializable: true)
expect(representation).to eq(id: nil, name: nil, user: { name: nil, email: nil })
end

context 'specify attribute with exposure condition' do
it 'returns only specified fields' do
subject.expose(:id, :name)
subject.expose(:id)
subject.with_options(if: { condition: true }) do
subject.expose(:name)
end

representation = subject.represent(OpenStruct.new, condition: true, only: [:id, :name], serializable: true)
expect(representation).to eq(id: nil, name: nil)
end

it 'does not return fields specified in the except option' do
subject.expose(:id, :phone)
subject.with_options(if: { condition: true }) do
subject.expose(:name, :mobile_phone)
end

representation = subject.represent(OpenStruct.new, condition: true, except: [:phone, :mobile_phone], serializable: true)
expect(representation).to eq(id: nil, name: nil)
end
end

context 'attribute with alias' do
Expand All @@ -534,17 +582,31 @@ class Parent < Person
representation = subject.represent(OpenStruct.new, condition: true, only: [:id, :title], serializable: true)
expect(representation).to eq(id: nil, title: nil)
end

it 'does not return fields specified in the except option' do
subject.expose(:id)
subject.expose(:name, as: :title)
subject.expose(:phone, as: :phone_number)

representation = subject.represent(OpenStruct.new, condition: true, except: [:phone_number], serializable: true)
expect(representation).to eq(id: nil, title: nil)
end
end

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

nephew_entity = Class.new(Grape::Entity)
nephew_entity.expose(:id, :name, :email)

subject.expose(:id, :name, :phone)
subject.expose(:user, using: user_entity)
subject.expose(:nephew, using: nephew_entity)

representation = subject.represent(OpenStruct.new(user: {}), only: [:id, :name, :user], serializable: true)
representation = subject.represent(OpenStruct.new(user: {}),
only: [:id, :name, :user], except: [:nephew], serializable: true)
expect(representation).to eq(id: nil, name: nil, user: { id: nil, name: nil, email: nil })
end
end
Expand Down