# frozen_string_literal: true require 'spec_helper' require 'grape_entity' describe Grape::Entity do subject { Class.new(Grape::API) } def app subject end describe '#present' do it 'sets the object as the body if no options are provided' do inner_body = nil subject.get '/example' do present(abc: 'def') inner_body = body end get '/example' expect(inner_body).to eql(abc: 'def') end it 'calls through to the provided entity class if one is given' do entity_mock = Object.new allow(entity_mock).to receive(:represent) subject.get '/example' do present Object.new, with: entity_mock end get '/example' end it 'pulls a representation from the class options if it exists' do entity = Class.new(Grape::Entity) allow(entity).to receive(:represent).and_return('Hiya') subject.represent Object, with: entity subject.get '/example' do present Object.new end get '/example' expect(last_response.body).to eq('Hiya') end it 'pulls a representation from the class options if the presented object is a collection of objects' do entity = Class.new(Grape::Entity) allow(entity).to receive(:represent).and_return('Hiya') module EntitySpec class TestObject end class FakeCollection def first TestObject.new end end end subject.represent EntitySpec::TestObject, with: entity subject.get '/example' do present [EntitySpec::TestObject.new] end subject.get '/example2' do present EntitySpec::FakeCollection.new end get '/example' expect(last_response.body).to eq('Hiya') get '/example2' expect(last_response.body).to eq('Hiya') end it 'pulls a representation from the class ancestor if it exists' do entity = Class.new(Grape::Entity) allow(entity).to receive(:represent).and_return('Hiya') subclass = Class.new(Object) subject.represent Object, with: entity subject.get '/example' do present subclass.new end get '/example' expect(last_response.body).to eq('Hiya') end it 'automatically uses Klass::Entity if that exists' do some_model = Class.new entity = Class.new(Grape::Entity) allow(entity).to receive(:represent).and_return('Auto-detect!') some_model.const_set :Entity, entity subject.get '/example' do present some_model.new end get '/example' expect(last_response.body).to eq('Auto-detect!') end it 'automatically uses Klass::Entity based on the first object in the collection being presented' do some_model = Class.new entity = Class.new(Grape::Entity) allow(entity).to receive(:represent).and_return('Auto-detect!') some_model.const_set :Entity, entity subject.get '/example' do present [some_model.new] end get '/example' expect(last_response.body).to eq('Auto-detect!') end it 'does not run autodetection for Entity when explicitely provided' do entity = Class.new(Grape::Entity) some_array = [] subject.get '/example' do present some_array, with: entity end expect(some_array).not_to receive(:first) get '/example' end it 'does not use #first method on ActiveRecord::Relation to prevent needless sql query' do entity = Class.new(Grape::Entity) some_relation = Class.new some_model = Class.new allow(entity).to receive(:represent).and_return('Auto-detect!') allow(some_relation).to receive(:first) allow(some_relation).to receive(:klass).and_return(some_model) some_model.const_set :Entity, entity subject.get '/example' do present some_relation end expect(some_relation).not_to receive(:first) get '/example' expect(last_response.body).to eq('Auto-detect!') end it 'autodetection does not use Entity if it is not a presenter' do some_model = Class.new entity = Class.new some_model.class.const_set :Entity, entity subject.get '/example' do present some_model end get '/example' expect(entity).not_to receive(:represent) end it 'adds a root key to the output if one is given' do inner_body = nil subject.get '/example' do present({ abc: 'def' }, root: :root) inner_body = body end get '/example' expect(inner_body).to eql(root: { abc: 'def' }) end %i[json serializable_hash].each do |format| it "presents with #{format}" do entity = Class.new(Grape::Entity) entity.root 'examples', 'example' entity.expose :id subject.format format subject.get '/example' do c = Class.new do attr_reader :id def initialize(id) @id = id end end present c.new(1), with: entity end get '/example' expect(last_response.status).to eq(200) expect(last_response.body).to eq('{"example":{"id":1}}') end it "presents with #{format} collection" do entity = Class.new(Grape::Entity) entity.root 'examples', 'example' entity.expose :id subject.format format subject.get '/examples' do c = Class.new do attr_reader :id def initialize(id) @id = id end end examples = [c.new(1), c.new(2)] present examples, with: entity end get '/examples' expect(last_response.status).to eq(200) expect(last_response.body).to eq('{"examples":[{"id":1},{"id":2}]}') end end it 'presents with xml' do entity = Class.new(Grape::Entity) entity.root 'examples', 'example' entity.expose :name subject.format :xml subject.get '/example' do c = Class.new do attr_reader :name def initialize(args) @name = args[:name] || 'no name set' end end present c.new(name: 'johnnyiller'), with: entity end get '/example' expect(last_response.status).to eq(200) expect(last_response.headers['Content-type']).to eq('application/xml') expect(last_response.body).to eq <<-XML johnnyiller XML end it 'presents with json' do entity = Class.new(Grape::Entity) entity.root 'examples', 'example' entity.expose :name subject.format :json subject.get '/example' do c = Class.new do attr_reader :name def initialize(args) @name = args[:name] || 'no name set' end end present c.new(name: 'johnnyiller'), with: entity end get '/example' expect(last_response.status).to eq(200) expect(last_response.headers['Content-type']).to eq('application/json') expect(last_response.body).to eq('{"example":{"name":"johnnyiller"}}') end it 'presents with jsonp utilising Rack::JSONP' do # Include JSONP middleware subject.use Rack::JSONP entity = Class.new(Grape::Entity) entity.root 'examples', 'example' entity.expose :name # Rack::JSONP expects a standard JSON response in UTF-8 format subject.format :json subject.formatter :json, lambda { |object, _| object.to_json.encode('utf-8') } subject.get '/example' do c = Class.new do attr_reader :name def initialize(args) @name = args[:name] || 'no name set' end end present c.new(name: 'johnnyiller'), with: entity end get '/example?callback=abcDef' expect(last_response.status).to eq(200) expect(last_response.headers['Content-type']).to eq('application/javascript') expect(last_response.body).to include 'abcDef({"example":{"name":"johnnyiller"}})' end context 'present with multiple entities' do it 'present with multiple entities using optional symbol' do user = Class.new do attr_reader :name def initialize(args) @name = args[:name] || 'no name set' end end user1 = user.new(name: 'user1') user2 = user.new(name: 'user2') entity = Class.new(Grape::Entity) entity.expose :name subject.format :json subject.get '/example' do present :page, 1 present :user1, user1, with: entity present :user2, user2, with: entity end get '/example' expect_response_json = { 'page' => 1, 'user1' => { 'name' => 'user1' }, 'user2' => { 'name' => 'user2' } } expect(JSON(last_response.body)).to eq(expect_response_json) end end end end