spec/grape_entity/entity_spec.rb in grape-entity-0.3.0 vs spec/grape_entity/entity_spec.rb in grape-entity-0.4.0
- old
+ new
@@ -1,8 +1,9 @@
require 'spec_helper'
describe Grape::Entity do
+
let(:fresh_class) { Class.new(Grape::Entity) }
context 'class methods' do
subject { fresh_class }
@@ -12,38 +13,120 @@
subject.expose :name, :email, :location
subject.exposures.size.should == 3
end
it 'sets the same options for all exposures passed' do
- subject.expose :name, :email, :location, :foo => :bar
- subject.exposures.values.each{|v| v.should == {:foo => :bar}}
+ subject.expose :name, :email, :location, documentation: true
+ subject.exposures.values.each { |v| v.should == { documentation: true } }
end
end
context 'option validation' do
it 'makes sure that :as only works on single attribute calls' do
- expect{ subject.expose :name, :email, :as => :foo }.to raise_error(ArgumentError)
- expect{ subject.expose :name, :as => :foo }.not_to raise_error
+ expect { subject.expose :name, :email, as: :foo }.to raise_error ArgumentError
+ expect { subject.expose :name, as: :foo }.not_to raise_error
end
- it 'makes sure that :format_with as a proc can not be used with a block' do
- expect { subject.expose :name, :format_with => Proc.new {} do |_| end }.to raise_error(ArgumentError)
+ it 'makes sure that :format_with as a proc cannot be used with a block' do
+ expect { subject.expose :name, format_with: proc {} {} }.to raise_error ArgumentError
end
+
+ it 'makes sure unknown options are not silently ignored' do
+ expect { subject.expose :name, unknown: nil }.to raise_error ArgumentError
+ end
end
context 'with a block' do
it 'errors out if called with multiple attributes' do
- expect{ subject.expose(:name, :email) do
- true
- end }.to raise_error(ArgumentError)
+ expect { subject.expose(:name, :email) { true } }.to raise_error ArgumentError
end
- it 'sets the :proc option in the exposure options' do
- block = lambda{|_| true }
- subject.expose :name, &block
- subject.exposures[:name][:proc].should == block
+ it 'references an instance of the entity with :using option' do
+ module EntitySpec
+ class SomeObject1
+ attr_accessor :prop1
+
+ def initialize
+ @prop1 = "value1"
+ end
+ end
+
+ class BogusEntity < Grape::Entity
+ expose :prop1
+ end
+ end
+
+ subject.expose(:bogus, using: EntitySpec::BogusEntity) do |entity|
+ entity.prop1 = "MODIFIED 2"
+ entity
+ end
+
+ object = EntitySpec::SomeObject1.new
+ value = subject.represent(object).send(:value_for, :bogus)
+ value.should be_instance_of EntitySpec::BogusEntity
+
+ prop1 = value.send(:value_for, :prop1)
+ prop1.should == "MODIFIED 2"
end
+
+ context 'with parameters passed to the block' do
+ it 'sets the :proc option in the exposure options' do
+ block = lambda { |_| true }
+ subject.expose :name, using: 'Awesome', &block
+ subject.exposures[:name].should == { proc: block, using: 'Awesome' }
+ end
+
+ it 'references an instance of the entity without any options' do
+ subject.expose(:size) { |_| self }
+ subject.represent(Hash.new).send(:value_for, :size).should be_an_instance_of fresh_class
+ end
+ end
+
+ context 'with no parameters passed to the block' do
+ it 'adds a nested exposure' do
+ subject.expose :awesome do
+ subject.expose :nested do
+ subject.expose :moar_nested, as: 'weee'
+ end
+ subject.expose :another_nested, using: 'Awesome'
+ end
+
+ subject.exposures.should == {
+ awesome: {},
+ awesome__nested: {},
+ awesome__nested__moar_nested: { as: 'weee' },
+ awesome__another_nested: { using: 'Awesome' }
+ }
+ end
+
+ it 'represents the exposure as a hash of its nested exposures' do
+ subject.expose :awesome do
+ subject.expose(:nested) { |_| "value" }
+ subject.expose(:another_nested) { |_| "value" }
+ end
+
+ subject.represent({}).send(:value_for, :awesome).should == {
+ nested: "value",
+ another_nested: "value"
+ }
+ end
+
+ it 'is safe if its nested exposures are safe' do
+ subject.with_options safe: true do
+ subject.expose :awesome do
+ subject.expose(:nested) { |_| "value" }
+ end
+ subject.expose :not_awesome do
+ subject.expose :nested
+ end
+ end
+
+ valid_keys = subject.represent({}).valid_exposures.keys
+ valid_keys.include?(:awesome).should == true && \
+ valid_keys.include?(:not_awesome).should == false
+ end
+ end
end
context 'inherited exposures' do
it 'returns exposures from an ancestor' do
subject.expose :name, :email
@@ -71,11 +154,11 @@
child_class.exposures[:name].should have_key :proc
end
end
context 'register formatters' do
- let(:date_formatter) { lambda {|date| date.strftime('%m/%d/%Y') }}
+ let(:date_formatter) { lambda { |date| date.strftime('%m/%d/%Y') } }
it 'registers a formatter' do
subject.format_with :timestamp, &date_formatter
subject.formatters[:timestamp].should_not be_nil
@@ -87,58 +170,177 @@
child_class.formatters.should == subject.formatters
end
it 'does not allow registering a formatter without a block' do
- expect{ subject.format_with :foo }.to raise_error(ArgumentError)
+ expect { subject.format_with :foo }.to raise_error ArgumentError
end
it 'formats an exposure with a registered formatter' do
subject.format_with :timestamp do |date|
date.strftime('%m/%d/%Y')
end
- subject.expose :birthday, :format_with => :timestamp
+ subject.expose :birthday, format_with: :timestamp
- model = { :birthday => Time.gm(2012, 2, 27) }
- subject.new(mock(model)).as_json[:birthday].should == '02/27/2012'
+ model = { birthday: Time.gm(2012, 2, 27) }
+ subject.new(double(model)).as_json[:birthday].should == '02/27/2012'
end
+
+ it 'formats an exposure with a :format_with lambda that returns a value from the entity instance' do
+ object = Hash.new
+
+ subject.expose(:size, format_with: lambda { |value| self.object.class.to_s })
+ subject.represent(object).send(:value_for, :size).should == object.class.to_s
+ end
+
+ it 'formats an exposure with a :format_with symbol that returns a value from the entity instance' do
+ subject.format_with :size_formatter do |date|
+ self.object.class.to_s
+ end
+
+ object = Hash.new
+
+ subject.expose(:size, format_with: :size_formatter)
+ subject.represent(object).send(:value_for, :size).should == object.class.to_s
+ end
end
end
describe '.with_options' do
- it 'should apply the options to all exposures inside' do
+ it 'raises an error for unknown options' do
+ block = proc do
+ with_options(unknown: true) do
+ expose :awesome_thing
+ end
+ end
+
+ expect { subject.class_eval(&block) }.to raise_error ArgumentError
+ end
+
+ it 'applies the options to all exposures inside' do
subject.class_eval do
- with_options(:if => {:awesome => true}) do
- expose :awesome_thing, :using => 'Awesome'
+ with_options(if: { awesome: true }) do
+ expose :awesome_thing, using: 'Awesome'
end
end
- subject.exposures[:awesome_thing].should == {:if => {:awesome => true}, :using => 'Awesome'}
+ subject.exposures[:awesome_thing].should == { if: { awesome: true }, using: 'Awesome' }
end
- it 'should allow for nested .with_options' do
+ it 'allows for nested .with_options' do
subject.class_eval do
- with_options(:if => {:awesome => true}) do
- with_options(:using => 'Something') do
+ with_options(if: { awesome: true }) do
+ with_options(using: 'Something') do
expose :awesome_thing
end
end
end
- subject.exposures[:awesome_thing].should == {:if => {:awesome => true}, :using => 'Something'}
+ subject.exposures[:awesome_thing].should == { if: { awesome: true }, using: 'Something' }
end
- it 'should allow for overrides' do
+ it 'overrides nested :as option' do
subject.class_eval do
- with_options(:if => {:awesome => true}) do
- expose :less_awesome_thing, :if => {:awesome => false}
+ with_options(as: :sweet) do
+ expose :awesome_thing, as: :extra_smooth
end
end
- subject.exposures[:less_awesome_thing].should == {:if => {:awesome => false}}
+ subject.exposures[:awesome_thing].should == { as: :extra_smooth }
end
+
+ it "merges nested :if option" do
+ match_proc = lambda { |obj, opts| true }
+
+ subject.class_eval do
+ # Symbol
+ with_options(if: :awesome) do
+ # Hash
+ with_options(if: { awesome: true }) do
+ # Proc
+ with_options(if: match_proc) do
+ # Hash (override existing key and merge new key)
+ with_options(if: { awesome: false, less_awesome: true }) do
+ expose :awesome_thing
+ end
+ end
+ end
+ end
+ end
+
+ subject.exposures[:awesome_thing].should == {
+ if: { awesome: false, less_awesome: true },
+ if_extras: [:awesome, match_proc]
+ }
+ end
+
+ it 'merges nested :unless option' do
+ match_proc = lambda { |obj, opts| true }
+
+ subject.class_eval do
+ # Symbol
+ with_options(unless: :awesome) do
+ # Hash
+ with_options(unless: { awesome: true }) do
+ # Proc
+ with_options(unless: match_proc) do
+ # Hash (override existing key and merge new key)
+ with_options(unless: { awesome: false, less_awesome: true }) do
+ expose :awesome_thing
+ end
+ end
+ end
+ end
+ end
+
+ subject.exposures[:awesome_thing].should == {
+ unless: { awesome: false, less_awesome: true },
+ unless_extras: [:awesome, match_proc]
+ }
+ end
+
+ it 'overrides nested :using option' do
+ subject.class_eval do
+ with_options(using: 'Something') do
+ expose :awesome_thing, using: 'SomethingElse'
+ end
+ end
+
+ subject.exposures[:awesome_thing].should == { using: 'SomethingElse' }
+ end
+
+ it 'aliases :with option to :using option' do
+ subject.class_eval do
+ with_options(using: 'Something') do
+ expose :awesome_thing, with: 'SomethingElse'
+ end
+ end
+ subject.exposures[:awesome_thing].should == { using: 'SomethingElse' }
+ end
+
+ it 'overrides nested :proc option' do
+ match_proc = lambda { |obj, opts| 'more awesomer' }
+
+ subject.class_eval do
+ with_options(proc: lambda { |obj, opts| 'awesome' }) do
+ expose :awesome_thing, proc: match_proc
+ end
+ end
+
+ subject.exposures[:awesome_thing].should == { proc: match_proc }
+ end
+
+ it 'overrides nested :documentation option' do
+ subject.class_eval do
+ with_options(documentation: { desc: 'Description.' }) do
+ expose :awesome_thing, documentation: { desc: 'Other description.' }
+ end
+ end
+
+ subject.exposures[:awesome_thing].should == { documentation: { desc: 'Other description.' } }
+ end
end
describe '.represent' do
it 'returns a single entity if called with one object' do
subject.represent(Object.new).should be_kind_of(subject)
@@ -147,20 +349,32 @@
it 'returns a single entity if called with a hash' do
subject.represent(Hash.new).should be_kind_of(subject)
end
it 'returns multiple entities if called with a collection' do
- representation = subject.represent(4.times.map{Object.new})
+ representation = subject.represent(4.times.map { Object.new })
representation.should be_kind_of Array
representation.size.should == 4
- representation.reject{|r| r.kind_of?(subject)}.should be_empty
+ representation.reject { |r| r.kind_of?(subject) }.should 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| r.options[:collection].should be_true}
+ it 'adds the collection: true option if called with a collection' do
+ representation = subject.represent(4.times.map { Object.new })
+ representation.each { |r| r.options[:collection].should be_true }
end
+
+ it 'returns a serialized hash of a single object if serializable: true' do
+ subject.expose(:awesome) { |_| true }
+ representation = subject.represent(Object.new, serializable: true)
+ representation.should == { awesome: true }
+ end
+
+ it 'returns a serialized array of hashes of multiple objects if serializable: true' do
+ subject.expose(:awesome) { |_| true }
+ representation = subject.represent(2.times.map { Object.new }, serializable: true)
+ representation.should == [{ awesome: true }, { awesome: true }]
+ end
end
describe '.root' do
context 'with singular and plural root keys' do
before(:each) do
@@ -176,33 +390,33 @@
end
end
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})
+ representation = subject.represent(4.times.map { Object.new })
representation.should be_kind_of Hash
representation.should have_key 'things'
representation['things'].should be_kind_of Array
representation['things'].size.should == 4
- representation['things'].reject{|r| r.kind_of?(subject)}.should be_empty
+ representation['things'].reject { |r| r.kind_of?(subject) }.should 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)
+ representation = subject.represent(4.times.map { Object.new }, root: false)
representation.should be_kind_of Array
representation.size.should == 4
- representation.reject{|r| r.kind_of?(subject)}.should be_empty
+ representation.reject { |r| r.kind_of?(subject) }.should be_empty
end
it 'can use a different name' do
- representation = subject.represent(4.times.map{Object.new}, :root=>'others')
+ representation = subject.represent(4.times.map { Object.new }, root: 'others')
representation.should be_kind_of Hash
representation.should have_key 'others'
representation['others'].should be_kind_of Array
representation['others'].size.should == 4
- representation['others'].reject{|r| r.kind_of?(subject)}.should be_empty
+ representation['others'].reject { |r| r.kind_of?(subject) }.should be_empty
end
end
end
context 'with singular root key' do
@@ -219,14 +433,14 @@
end
end
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})
+ representation = subject.represent(4.times.map { Object.new })
representation.should be_kind_of Array
representation.size.should == 4
- representation.reject{|r| r.kind_of?(subject)}.should be_empty
+ representation.reject { |r| r.kind_of?(subject) }.should be_empty
end
end
end
context 'with plural root key' do
@@ -240,359 +454,526 @@
end
end
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})
+ representation = subject.represent(4.times.map { Object.new })
representation.should be_kind_of Hash
representation.should have_key('things')
representation['things'].should be_kind_of Array
representation['things'].size.should == 4
- representation['things'].reject{|r| r.kind_of?(subject)}.should be_empty
+ representation['things'].reject { |r| r.kind_of?(subject) }.should be_empty
end
end
end
end
describe '#initialize' do
it 'takes an object and an optional options hash' do
- expect{ subject.new(Object.new) }.not_to raise_error
- expect{ subject.new }.to raise_error(ArgumentError)
- expect{ subject.new(Object.new, {}) }.not_to raise_error
+ expect { subject.new(Object.new) }.not_to raise_error
+ expect { subject.new }.to raise_error ArgumentError
+ expect { subject.new(Object.new, {}) }.not_to raise_error
end
it 'has attribute readers for the object and options' do
entity = subject.new('abc', {})
entity.object.should == 'abc'
entity.options.should == {}
end
end
+
end
context 'instance methods' do
-
- let(:model){ mock(attributes) }
-
- let(:attributes) { {
- :name => 'Bob Bobson',
- :email => 'bob@example.com',
- :birthday => Time.gm(2012, 2, 27),
- :fantasies => ['Unicorns', 'Double Rainbows', 'Nessy'],
- :friends => [
- mock(:name => "Friend 1", :email => 'friend1@example.com', :fantasies => [], :birthday => Time.gm(2012, 2, 27), :friends => []),
- mock(:name => "Friend 2", :email => 'friend2@example.com', :fantasies => [], :birthday => Time.gm(2012, 2, 27), :friends => [])
- ]
- } }
-
- subject{ fresh_class.new(model) }
- describe '#serializable_hash' do
+ let(:model) { double(attributes) }
+ let(:attributes) {
+ {
+ name: 'Bob Bobson',
+ email: 'bob@example.com',
+ birthday: Time.gm(2012, 2, 27),
+ fantasies: ['Unicorns', 'Double Rainbows', 'Nessy'],
+ 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: [])
+ ]
+ }
+ }
+
+ subject { fresh_class.new(model) }
+
+ describe '#serializable_hash' do
it 'does not throw an exception if a nil options object is passed' do
- expect{ fresh_class.new(model).serializable_hash(nil) }.not_to raise_error
+ expect { fresh_class.new(model).serializable_hash(nil) }.not_to raise_error
end
it 'does not blow up when the model is nil' do
fresh_class.expose :name
- expect{ fresh_class.new(nil).serializable_hash }.not_to raise_error
+ expect { fresh_class.new(nil).serializable_hash }.not_to raise_error
end
- it 'does not throw an exception when an attribute is not found on the object' do
- fresh_class.expose :name, :nonexistent_attribute
- expect{ fresh_class.new(model).serializable_hash }.not_to raise_error
- end
+ context "with safe option" do
+ it 'does not throw an exception when an attribute is not found on the object' do
+ fresh_class.expose :name, :nonexistent_attribute, safe: true
+ expect { fresh_class.new(model).serializable_hash }.not_to raise_error
+ end
- it "does not expose attributes that don't exist on the object" do
- fresh_class.expose :email, :nonexistent_attribute, :name
+ it "does not expose attributes that don't exist on the object" do
+ fresh_class.expose :email, :nonexistent_attribute, :name, safe: true
- res = fresh_class.new(model).serializable_hash
- res.should have_key :email
- res.should_not have_key :nonexistent_attribute
- res.should have_key :name
+ res = fresh_class.new(model).serializable_hash
+ res.should have_key :email
+ res.should_not have_key :nonexistent_attribute
+ res.should 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 }
+
+ res = fresh_class.new(model).serializable_hash
+ res.should have_key :email
+ res.should_not have_key :nonexistent_attribute
+ res.should_not have_key :nonexistent_attribute2
+ end
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, :if => lambda { false }
- fresh_class.expose :nonexistent_attribute2, :if => lambda { true }
+ context "without safe option" do
+ it 'throws an exception when an attribute is not found on the object' do
+ fresh_class.expose :name, :nonexistent_attribute
+ expect { fresh_class.new(model).serializable_hash }.to raise_error
+ end
- res = fresh_class.new(model).serializable_hash
- res.should have_key :email
- res.should_not have_key :nonexistent_attribute
- res.should_not have_key :nonexistent_attribute2
+ it "exposes attributes that don't exist on the object only when they are generated by a block" do
+ fresh_class.expose :nonexistent_attribute do |model, _|
+ "well, I do exist after all"
+ end
+ res = fresh_class.new(model).serializable_hash
+ res.should have_key :nonexistent_attribute
+ end
+
+ it "does not expose attributes that are generated by a block but have not passed criteria" do
+ fresh_class.expose :nonexistent_attribute, proc: lambda { |model, _|
+ "I exist, but it is not yet my time to shine"
+ }, if: lambda { |model, _| false }
+ res = fresh_class.new(model).serializable_hash
+ res.should_not have_key :nonexistent_attribute
+ end
end
- it "exposes attributes that don't exist on the object only when they are generated by a block" do
- fresh_class.expose :nonexistent_attribute do |model, _|
+ it "exposes attributes that don't exist on the object only when they are generated by a block with options" do
+ module EntitySpec
+ class TestEntity < Grape::Entity
+ end
+ end
+
+ fresh_class.expose :nonexistent_attribute, using: EntitySpec::TestEntity do |model, _|
"well, I do exist after all"
end
res = fresh_class.new(model).serializable_hash
res.should have_key :nonexistent_attribute
end
it "does not expose attributes that are generated by a block but have not passed criteria" do
- fresh_class.expose :nonexistent_attribute, :proc => lambda {|model, _|
+ fresh_class.expose :nonexistent_attribute, proc: lambda { |model, _|
"I exist, but it is not yet my time to shine"
- }, :if => lambda { |model, _| false }
+ }, if: lambda { |model, _| false }
res = fresh_class.new(model).serializable_hash
res.should_not have_key :nonexistent_attribute
end
context '#serializable_hash' do
-
module EntitySpec
class EmbeddedExample
def serializable_hash(opts = {})
- { :abc => 'def' }
+ { abc: 'def' }
end
end
+
+ class EmbeddedExampleWithHash
+ def name
+ "abc"
+ end
+
+ def embedded
+ { a: nil, b: EmbeddedExample.new }
+ end
+ end
+
class EmbeddedExampleWithMany
def name
"abc"
end
+
def embedded
- [ EmbeddedExample.new, EmbeddedExample.new ]
+ [EmbeddedExample.new, EmbeddedExample.new]
end
end
+
class EmbeddedExampleWithOne
def name
"abc"
end
+
def embedded
EmbeddedExample.new
end
end
end
-
+
it 'serializes embedded objects which respond to #serializable_hash' do
fresh_class.expose :name, :embedded
presenter = fresh_class.new(EntitySpec::EmbeddedExampleWithOne.new)
- presenter.serializable_hash.should == {:name => "abc", :embedded => {:abc => "def"}}
+ presenter.serializable_hash.should == { name: "abc", embedded: { abc: "def" } }
end
it 'serializes embedded arrays of objects which respond to #serializable_hash' do
fresh_class.expose :name, :embedded
presenter = fresh_class.new(EntitySpec::EmbeddedExampleWithMany.new)
- presenter.serializable_hash.should == {:name => "abc", :embedded => [{:abc => "def"}, {:abc => "def"}]}
+ presenter.serializable_hash.should == { name: "abc", embedded: [{ abc: "def" }, { abc: "def" }] }
end
-
+
+ it 'serializes embedded hashes of objects which respond to #serializable_hash' do
+ fresh_class.expose :name, :embedded
+ presenter = fresh_class.new(EntitySpec::EmbeddedExampleWithHash.new)
+ presenter.serializable_hash.should == { name: "abc", embedded: { a: nil, b: { abc: "def" } } }
+ end
end
-
end
describe '#value_for' do
before do
fresh_class.class_eval do
expose :name, :email
- expose :friends, :using => self
+ expose :friends, using: self
expose :computed do |_, options|
options[:awesome]
end
- expose :birthday, :format_with => :timestamp
+ expose :birthday, format_with: :timestamp
def timestamp(date)
date.strftime('%m/%d/%Y')
end
- expose :fantasies, :format_with => lambda {|f| f.reverse }
+ expose :fantasies, format_with: lambda { |f| f.reverse }
end
end
it 'passes through bare expose attributes' do
subject.send(:value_for, :name).should == attributes[:name]
end
it 'instantiates a representation if that is called for' do
rep = subject.send(:value_for, :friends)
- rep.reject{|r| r.is_a?(fresh_class)}.should be_empty
+ rep.reject { |r| r.is_a?(fresh_class) }.should be_empty
rep.first.serializable_hash[:name].should == 'Friend 1'
rep.last.serializable_hash[:name].should == 'Friend 2'
end
context 'child representations' do
it 'disables root key name for child representations' do
-
module EntitySpec
class FriendEntity < Grape::Entity
root 'friends', 'friend'
expose :name, :email
end
end
-
+
fresh_class.class_eval do
- expose :friends, :using => EntitySpec::FriendEntity
+ expose :friends, using: EntitySpec::FriendEntity
end
-
+
rep = subject.send(:value_for, :friends)
rep.should be_kind_of Array
- rep.reject{|r| r.is_a?(EntitySpec::FriendEntity)}.should be_empty
+ rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }.should be_empty
rep.first.serializable_hash[:name].should == 'Friend 1'
rep.last.serializable_hash[:name].should == 'Friend 2'
end
+ it "passes through the proc which returns an array of objects with custom options(:using)" do
+ module EntitySpec
+ class FriendEntity < Grape::Entity
+ root 'friends', 'friend'
+ expose :name, :email
+ end
+ end
+
+ fresh_class.class_eval do
+ expose :custom_friends, using: EntitySpec::FriendEntity do |user, options|
+ user.friends
+ end
+ end
+
+ rep = subject.send(:value_for, :custom_friends)
+ rep.should be_kind_of Array
+ rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }.should be_empty
+ rep.first.serializable_hash.should == { name: 'Friend 1', email: 'friend1@example.com' }
+ rep.last.serializable_hash.should == { name: 'Friend 2', email: 'friend2@example.com' }
+ end
+
+ it "passes through the proc which returns single object with custom options(:using)" do
+ module EntitySpec
+ class FriendEntity < Grape::Entity
+ root 'friends', 'friend'
+ expose :name, :email
+ end
+ end
+
+ fresh_class.class_eval do
+ expose :first_friend, using: EntitySpec::FriendEntity do |user, options|
+ user.friends.first
+ end
+ end
+
+ rep = subject.send(:value_for, :first_friend)
+ rep.should be_kind_of EntitySpec::FriendEntity
+ rep.serializable_hash.should == { name: 'Friend 1', email: 'friend1@example.com' }
+ end
+
+ it "passes through the proc which returns empty with custom options(:using)" do
+ module EntitySpec
+ class FriendEntity < Grape::Entity
+ root 'friends', 'friend'
+ expose :name, :email
+ end
+ end
+
+ fresh_class.class_eval do
+ expose :first_friend, using: EntitySpec::FriendEntity do |user, options|
+
+ end
+ end
+
+ rep = subject.send(:value_for, :first_friend)
+ rep.should be_kind_of EntitySpec::FriendEntity
+ rep.serializable_hash.should be_nil
+ end
+
it 'passes through custom options' do
module EntitySpec
class FriendEntity < Grape::Entity
root 'friends', 'friend'
expose :name
- expose :email, :if => { :user_type => :admin }
+ expose :email, if: { user_type: :admin }
end
end
-
+
fresh_class.class_eval do
- expose :friends, :using => EntitySpec::FriendEntity
+ expose :friends, using: EntitySpec::FriendEntity
end
-
+
rep = subject.send(:value_for, :friends)
rep.should be_kind_of Array
- rep.reject{|r| r.is_a?(EntitySpec::FriendEntity)}.should be_empty
+ rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }.should be_empty
rep.first.serializable_hash[:email].should be_nil
rep.last.serializable_hash[:email].should be_nil
- rep = subject.send(:value_for, :friends, { :user_type => :admin })
+ rep = subject.send(:value_for, :friends, user_type: :admin)
rep.should be_kind_of Array
- rep.reject{|r| r.is_a?(EntitySpec::FriendEntity)}.should be_empty
+ rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }.should be_empty
rep.first.serializable_hash[:email].should == 'friend1@example.com'
rep.last.serializable_hash[:email].should == 'friend2@example.com'
end
it 'ignores the :collection parameter in the source options' do
module EntitySpec
class FriendEntity < Grape::Entity
root 'friends', 'friend'
expose :name
- expose :email, :if => { :collection => true }
+ expose :email, if: { collection: true }
end
end
-
+
fresh_class.class_eval do
- expose :friends, :using => EntitySpec::FriendEntity
+ expose :friends, using: EntitySpec::FriendEntity
end
-
- rep = subject.send(:value_for, :friends, { :collection => false })
+
+ rep = subject.send(:value_for, :friends, collection: false)
rep.should be_kind_of Array
- rep.reject{|r| r.is_a?(EntitySpec::FriendEntity)}.should be_empty
+ rep.reject { |r| r.is_a?(EntitySpec::FriendEntity) }.should be_empty
rep.first.serializable_hash[:email].should == 'friend1@example.com'
rep.last.serializable_hash[:email].should == 'friend2@example.com'
end
-
end
it 'calls through to the proc if there is one' do
- subject.send(:value_for, :computed, :awesome => 123).should == 123
+ subject.send(:value_for, :computed, awesome: 123).should == 123
end
it 'returns a formatted value if format_with is passed' do
subject.send(:value_for, :birthday).should == '02/27/2012'
end
it 'returns a formatted value if format_with is passed a lambda' do
subject.send(:value_for, :fantasies).should == ['Nessy', 'Double Rainbows', 'Unicorns']
end
+
+ it "tries instance methods on the entity first" do
+ module EntitySpec
+ class DelegatingEntity < Grape::Entity
+ root 'friends', 'friend'
+ expose :name
+ expose :email
+
+ private
+
+ def name
+ "cooler name"
+ end
+ end
+ end
+
+ friend = double("Friend", name: "joe", email: "joe@example.com")
+ rep = EntitySpec::DelegatingEntity.new(friend)
+ rep.send(:value_for, :name).should == "cooler name"
+ rep.send(:value_for, :email).should == "joe@example.com"
+ end
+
+ context "using" do
+ before do
+ module EntitySpec
+ class UserEntity < Grape::Entity
+ expose :name, :email
+ end
+ end
+ end
+ it "string" do
+ fresh_class.class_eval do
+ expose :friends, using: "EntitySpec::UserEntity"
+ end
+
+ rep = subject.send(:value_for, :friends)
+ rep.should be_kind_of Array
+ rep.size.should == 2
+ rep.all? { |r| r.is_a?(EntitySpec::UserEntity) }.should be_true
+ end
+
+ it 'class' do
+ fresh_class.class_eval do
+ expose :friends, using: EntitySpec::UserEntity
+ end
+
+ rep = subject.send(:value_for, :friends)
+ rep.should be_kind_of Array
+ rep.size.should == 2
+ rep.all? { |r| r.is_a?(EntitySpec::UserEntity) }.should be_true
+ end
+ end
end
describe '#documentation' do
it 'returns an empty hash is no documentation is provided' do
fresh_class.expose :name
subject.documentation.should == {}
end
it 'returns each defined documentation hash' do
- doc = {:type => "foo", :desc => "bar"}
- fresh_class.expose :name, :documentation => doc
- fresh_class.expose :email, :documentation => doc
+ doc = { type: "foo", desc: "bar" }
+ fresh_class.expose :name, documentation: doc
+ fresh_class.expose :email, documentation: doc
fresh_class.expose :birthday
- subject.documentation.should == {:name => doc, :email => doc}
+ subject.documentation.should == { name: doc, email: doc }
end
+
+ it 'returns each defined documentation hash with :as param considering' do
+ doc = { type: "foo", desc: "bar" }
+ fresh_class.expose :name, documentation: doc, as: :label
+ fresh_class.expose :email, documentation: doc
+ fresh_class.expose :birthday
+
+ subject.documentation.should == { label: doc, email: doc }
+ end
end
describe '#key_for' do
it 'returns the attribute if no :as is set' do
fresh_class.expose :name
- subject.send(:key_for, :name).should == :name
+ subject.class.send(:key_for, :name).should == :name
end
it 'returns a symbolized version of the attribute' do
fresh_class.expose :name
- subject.send(:key_for, 'name').should == :name
+ subject.class.send(:key_for, 'name').should == :name
end
it 'returns the :as alias if one exists' do
- fresh_class.expose :name, :as => :nombre
- subject.send(:key_for, 'name').should == :nombre
+ fresh_class.expose :name, as: :nombre
+ subject.class.send(:key_for, 'name').should == :nombre
end
end
describe '#conditions_met?' do
it 'only passes through hash :if exposure if all attributes match' do
- exposure_options = {:if => {:condition1 => true, :condition2 => true}}
+ exposure_options = { if: { condition1: true, condition2: true } }
subject.send(:conditions_met?, exposure_options, {}).should be_false
- subject.send(:conditions_met?, exposure_options, :condition1 => true).should be_false
- subject.send(:conditions_met?, exposure_options, :condition1 => true, :condition2 => true).should be_true
- subject.send(:conditions_met?, exposure_options, :condition1 => false, :condition2 => true).should be_false
- subject.send(:conditions_met?, exposure_options, :condition1 => true, :condition2 => true, :other => true).should be_true
+ subject.send(:conditions_met?, exposure_options, condition1: true).should be_false
+ subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true).should be_true
+ subject.send(:conditions_met?, exposure_options, condition1: false, condition2: true).should be_false
+ subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true, other: true).should be_true
end
it 'looks for presence/truthiness if a symbol is passed' do
- exposure_options = {:if => :condition1}
+ exposure_options = { if: :condition1 }
subject.send(:conditions_met?, exposure_options, {}).should be_false
- subject.send(:conditions_met?, exposure_options, {:condition1 => true}).should be_true
- subject.send(:conditions_met?, exposure_options, {:condition1 => false}).should be_false
- subject.send(:conditions_met?, exposure_options, {:condition1 => nil}).should be_false
+ subject.send(:conditions_met?, exposure_options, condition1: true).should be_true
+ subject.send(:conditions_met?, exposure_options, condition1: false).should be_false
+ subject.send(:conditions_met?, exposure_options, condition1: nil).should be_false
end
it 'looks for absence/falsiness if a symbol is passed' do
- exposure_options = {:unless => :condition1}
+ exposure_options = { unless: :condition1 }
subject.send(:conditions_met?, exposure_options, {}).should be_true
- subject.send(:conditions_met?, exposure_options, {:condition1 => true}).should be_false
- subject.send(:conditions_met?, exposure_options, {:condition1 => false}).should be_true
- subject.send(:conditions_met?, exposure_options, {:condition1 => nil}).should be_true
+ subject.send(:conditions_met?, exposure_options, condition1: true).should be_false
+ subject.send(:conditions_met?, exposure_options, condition1: false).should be_true
+ subject.send(:conditions_met?, exposure_options, condition1: nil).should be_true
end
it 'only passes through proc :if exposure if it returns truthy value' do
- exposure_options = {:if => lambda{|_,opts| opts[:true]}}
+ exposure_options = { if: lambda { |_, opts| opts[:true] } }
- subject.send(:conditions_met?, exposure_options, :true => false).should be_false
- subject.send(:conditions_met?, exposure_options, :true => true).should be_true
+ subject.send(:conditions_met?, exposure_options, true: false).should be_false
+ subject.send(:conditions_met?, exposure_options, true: true).should be_true
end
it 'only passes through hash :unless exposure if any attributes do not match' do
- exposure_options = {:unless => {:condition1 => true, :condition2 => true}}
+ exposure_options = { unless: { condition1: true, condition2: true } }
subject.send(:conditions_met?, exposure_options, {}).should be_true
- subject.send(:conditions_met?, exposure_options, :condition1 => true).should be_false
- subject.send(:conditions_met?, exposure_options, :condition1 => true, :condition2 => true).should be_false
- subject.send(:conditions_met?, exposure_options, :condition1 => false, :condition2 => true).should be_false
- subject.send(:conditions_met?, exposure_options, :condition1 => true, :condition2 => true, :other => true).should be_false
- subject.send(:conditions_met?, exposure_options, :condition1 => false, :condition2 => false).should be_true
+ subject.send(:conditions_met?, exposure_options, condition1: true).should be_false
+ subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true).should be_false
+ subject.send(:conditions_met?, exposure_options, condition1: false, condition2: true).should be_false
+ subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true, other: true).should be_false
+ subject.send(:conditions_met?, exposure_options, condition1: false, condition2: false).should be_true
end
it 'only passes through proc :unless exposure if it returns falsy value' do
- exposure_options = {:unless => lambda{|_,options| options[:true] == true}}
+ exposure_options = { unless: lambda { |_, options| options[:true] == true } }
- subject.send(:conditions_met?, exposure_options, :true => false).should be_true
- subject.send(:conditions_met?, exposure_options, :true => true).should be_false
+ subject.send(:conditions_met?, exposure_options, true: false).should be_true
+ subject.send(:conditions_met?, exposure_options, true: true).should be_false
end
end
describe '::DSL' do
- subject{ Class.new }
+ subject { Class.new }
it 'creates an Entity class when called' do
subject.should_not be_const_defined :Entity
subject.send(:include, Grape::Entity::DSL)
subject.should be_const_defined :Entity
end
context 'pre-mixed' do
- before{ subject.send(:include, Grape::Entity::DSL) }
+ before { subject.send(:include, Grape::Entity::DSL) }
it 'is able to define entity traits through DSL' do
subject.entity do
expose :name
end
@@ -611,22 +992,22 @@
end
subject.entity_class.exposures.size.should == 3
end
context 'instance' do
- let(:instance){ subject.new }
+ let(:instance) { subject.new }
describe '#entity' do
it 'is an instance of the entity class' do
instance.entity.should be_kind_of(subject.entity_class)
end
it 'has an object of itself' do
instance.entity.object.should == instance
end
- it 'should instantiate with options if provided' do
- instance.entity(:awesome => true).options.should == {:awesome => true}
+ it 'instantiates with options if provided' do
+ instance.entity(awesome: true).options.should == { awesome: true }
end
end
end
end
end