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