spec/grape_entity/entity_spec.rb in grape-entity-0.4.4 vs spec/grape_entity/entity_spec.rb in grape-entity-0.4.5
- old
+ new
@@ -1,9 +1,9 @@
require 'spec_helper'
+require 'ostruct'
describe Grape::Entity do
-
let(:fresh_class) { Class.new(Grape::Entity) }
context 'class methods' do
subject { fresh_class }
@@ -108,10 +108,19 @@
nested: 'value',
another_nested: 'value'
)
end
+ it 'does not represent nested exposures whose conditions are not met' do
+ subject.expose :awesome do
+ subject.expose(:condition_met, if: lambda { |_, _| true }) { |_| 'value' }
+ subject.expose(:condition_not_met, if: lambda { |_, _| false }) { |_| 'value' }
+ end
+
+ expect(subject.represent({}).send(:value_for, :awesome)).to eq(condition_met: 'value')
+ end
+
it 'does not represent attributes, declared inside nested exposure, outside of it' do
subject.expose :awesome do
subject.expose(:nested) { |_| 'value' }
subject.expose(:another_nested) { |_| 'value' }
subject.expose :second_level_nested do
@@ -267,10 +276,52 @@
expect(subject.represent(object).send(:value_for, :size)).to eq object.class.to_s
end
end
end
+ describe '.unexpose' do
+ it 'is able to remove exposed attributes' do
+ subject.expose :name, :email
+ subject.unexpose :email
+
+ expect(subject.exposures).to eq(name: {})
+ end
+
+ context 'inherited exposures' do
+ it 'when called from child class, only removes from the attribute from child' do
+ subject.expose :name, :email
+ child_class = Class.new(subject)
+ child_class.unexpose :email
+
+ expect(child_class.exposures).to eq(name: {})
+ expect(subject.exposures).to eq(name: {}, email: {})
+ end
+
+ # the following 2 behaviors are testing because it is not most intuitive and could be confusing
+ context 'when called from the parent class' do
+ it 'remove from parent and all child classes that have not locked down their attributes with an .exposures call' do
+ subject.expose :name, :email
+ child_class = Class.new(subject)
+ subject.unexpose :email
+
+ expect(subject.exposures).to eq(name: {})
+ expect(child_class.exposures).to eq(name: {})
+ end
+
+ it 'remove from parent and do not remove from child classes that have locked down their attributes with an .exposures call' do
+ subject.expose :name, :email
+ child_class = Class.new(subject)
+ child_class.exposures
+ subject.unexpose :email
+
+ expect(subject.exposures).to eq(name: {})
+ expect(child_class.exposures).to eq(name: {}, email: {})
+ end
+ end
+ end
+ end
+
describe '.with_options' do
it 'raises an error for unknown options' do
block = proc do
with_options(unknown: true) do
expose :awesome_thing
@@ -415,11 +466,11 @@
it 'returns multiple entities if called with a collection' do
representation = subject.represent(4.times.map { Object.new })
expect(representation).to be_kind_of Array
expect(representation.size).to eq(4)
- expect(representation.reject { |r| r.kind_of?(subject) }).to be_empty
+ expect(representation.reject { |r| r.is_a?(subject) }).to be_empty
end
it 'adds the collection: true option if called with a collection' do
representation = subject.represent(4.times.map { Object.new })
representation.each { |r| expect(r.options[:collection]).to be true }
@@ -440,10 +491,23 @@
it 'returns a serialized hash of a hash' do
subject.expose(:awesome)
representation = subject.represent({ awesome: true }, serializable: true)
expect(representation).to eq(awesome: true)
end
+
+ it 'returns a serialized hash of an OpenStruct' do
+ subject.expose(:awesome)
+ representation = subject.represent(OpenStruct.new, serializable: true)
+ expect(representation).to eq(awesome: nil)
+ end
+
+ it 'raises error if field not found' do
+ subject.expose(:awesome)
+ expect do
+ subject.represent(Object.new, serializable: true)
+ end.to raise_error(NoMethodError, /missing attribute `awesome'/)
+ end
end
describe '.present_collection' do
it 'make the objects accessible' do
subject.present_collection true
@@ -489,28 +553,28 @@
representation = subject.represent(4.times.map { Object.new })
expect(representation).to be_kind_of Hash
expect(representation).to have_key 'things'
expect(representation['things']).to be_kind_of Array
expect(representation['things'].size).to eq 4
- expect(representation['things'].reject { |r| r.kind_of?(subject) }).to be_empty
+ expect(representation['things'].reject { |r| r.is_a?(subject) }).to be_empty
end
end
context 'it can be overridden' do
it 'can be disabled' do
representation = subject.represent(4.times.map { Object.new }, root: false)
expect(representation).to be_kind_of Array
expect(representation.size).to eq 4
- expect(representation.reject { |r| r.kind_of?(subject) }).to be_empty
+ expect(representation.reject { |r| r.is_a?(subject) }).to be_empty
end
it 'can use a different name' do
representation = subject.represent(4.times.map { Object.new }, root: 'others')
expect(representation).to be_kind_of Hash
expect(representation).to have_key 'others'
expect(representation['others']).to be_kind_of Array
expect(representation['others'].size).to eq 4
- expect(representation['others'].reject { |r| r.kind_of?(subject) }).to be_empty
+ expect(representation['others'].reject { |r| r.is_a?(subject) }).to be_empty
end
end
end
context 'with singular root key' do
@@ -530,11 +594,11 @@
context 'with an array of objects' do
it 'allows a root element name to be specified' do
representation = subject.represent(4.times.map { Object.new })
expect(representation).to be_kind_of Array
expect(representation.size).to eq 4
- expect(representation.reject { |r| r.kind_of?(subject) }).to be_empty
+ expect(representation.reject { |r| r.is_a?(subject) }).to be_empty
end
end
end
context 'with plural root key' do
@@ -553,11 +617,11 @@
representation = subject.represent(4.times.map { Object.new })
expect(representation).to be_kind_of Hash
expect(representation).to have_key('things')
expect(representation['things']).to be_kind_of Array
expect(representation['things'].size).to eq 4
- expect(representation['things'].reject { |r| r.kind_of?(subject) }).to be_empty
+ expect(representation['things'].reject { |r| r.is_a?(subject) }).to be_empty
end
end
end
end
@@ -572,29 +636,30 @@
entity = subject.new('abc', {})
expect(entity.object).to eq 'abc'
expect(entity.options).to eq({})
end
end
-
end
context 'instance methods' do
-
let(:model) { double(attributes) }
- let(:attributes) {
+ let(:attributes) do
{
name: 'Bob Bobson',
email: 'bob@example.com',
birthday: Time.gm(2012, 2, 27),
fantasies: ['Unicorns', 'Double Rainbows', 'Nessy'],
+ characteristics: [
+ { key: 'hair_color', value: 'brown' }
+ ],
friends: [
- double(name: 'Friend 1', email: 'friend1@example.com', fantasies: [], birthday: Time.gm(2012, 2, 27), friends: []),
- double(name: 'Friend 2', email: 'friend2@example.com', fantasies: [], birthday: Time.gm(2012, 2, 27), friends: [])
+ double(name: 'Friend 1', email: 'friend1@example.com', characteristics: [], fantasies: [], birthday: Time.gm(2012, 2, 27), friends: []),
+ double(name: 'Friend 2', email: 'friend2@example.com', characteristics: [], fantasies: [], birthday: Time.gm(2012, 2, 27), friends: [])
]
}
- }
+ end
subject { fresh_class.new(model) }
describe '#serializable_hash' do
it 'does not throw an exception if a nil options object is passed' do
@@ -619,10 +684,17 @@
expect(res).to have_key :email
expect(res).not_to have_key :nonexistent_attribute
expect(res).to have_key :name
end
+ it 'does expose attributes marked as safe if model is a hash object' do
+ fresh_class.expose :name, safe: true
+
+ res = fresh_class.new(name: 'myname').serializable_hash
+ expect(res).to have_key :name
+ end
+
it "does not expose attributes that don't exist on the object, even with criteria" do
fresh_class.expose :email
fresh_class.expose :nonexistent_attribute, safe: true, if: lambda { false }
fresh_class.expose :nonexistent_attribute2, safe: true, if: lambda { true }
@@ -840,9 +912,28 @@
end
rep = subject.send(:value_for, :first_friend)
expect(rep).to be_kind_of EntitySpec::FriendEntity
expect(rep.serializable_hash).to be_nil
+ end
+
+ it 'passes through exposed entity with key and value attributes' do
+ module EntitySpec
+ class CharacteristicsEntity < Grape::Entity
+ root 'characteristics', 'characteristic'
+ expose :key, :value
+ end
+ end
+
+ fresh_class.class_eval do
+ expose :characteristics, using: EntitySpec::CharacteristicsEntity
+ end
+
+ rep = subject.send(:value_for, :characteristics)
+ expect(rep).to be_kind_of Array
+ expect(rep.reject { |r| r.is_a?(EntitySpec::CharacteristicsEntity) }).to be_empty
+ expect(rep.first.serializable_hash[:key]).to eq 'hair_color'
+ expect(rep.first.serializable_hash[:value]).to eq 'brown'
end
it 'passes through custom options' do
module EntitySpec
class FriendEntity < Grape::Entity