Skip to content
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

Adds Entity DSL #234

Merged
merged 2 commits into from
Aug 29, 2012
Merged
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
21 changes: 21 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,27 @@ module API
end
```

#### Using the Exposure DSL

Grape ships with a DSL to easily define entities within the context
of an existing class:

```ruby
class User
include Grape::Entity::DSL

entity :name, :email do
expose :advanced, if: :conditional
end
end
```

The above will automatically create a `User::Entity` class and
define properties on it according to the same rules as above. If
you only want to define simple exposures you don't have to supply
a block and can instead simply supply a list of comma-separated
symbols.

### Using Entities

Once an entity is defined, it can be used within endpoints, by calling #present. The #present
Expand Down
54 changes: 54 additions & 0 deletions lib/grape/entity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,60 @@ module Grape
class Entity
attr_reader :object, :options

# The Entity DSL allows you to mix entity functionality into
# your existing classes.
module DSL
def self.included(base)
base.extend ClassMethods
ancestor_entity_class = base.ancestors.detect{|a| a.entity_class if a.respond_to?(:entity_class)}
base.const_set(:Entity, Class.new(ancestor_entity_class || Grape::Entity)) unless const_defined?(:Entity)
end

module ClassMethods
# Returns the automatically-created entity class for this
# Class.
def entity_class(search_ancestors=true)
klass = const_get(:Entity) if const_defined?(:Entity)
klass ||= ancestors.detect{|a| a.entity_class(false) if a.respond_to?(:entity_class) } if search_ancestors
klass
end

# Call this to make exposures to the entity for this Class.
# Can be called with symbols for the attributes to expose,
# a block that yields the full Entity DSL (See Grape::Entity),
# or both.
#
# @example Symbols only.
#
# class User
# include Grape::Entity::DSL
#
# entity :name, :email
# end
#
# @example Mixed.
#
# class User
# include Grape::Entity::DSL
#
# entity :name, :email do
# expose :latest_status, using: Status::Entity, if: :include_status
# expose :new_attribute, :if => {:version => 'v2'}
# end
# end
def entity(*exposures, &block)
entity_class.expose *exposures if exposures.any?
entity_class.class_eval(&block) if block_given?
entity_class
end
end

# Instantiates an entity version of this object.
def entity
self.class.entity_class.new(self)
end
end

# This method is the primary means by which you will declare what attributes
# should be exposed by the entity.
#
Expand Down
48 changes: 48 additions & 0 deletions spec/grape/entity_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -478,5 +478,53 @@ class FriendEntity < Grape::Entity
subject.send(:conditions_met?, exposure_options, :true => true).should be_false
end
end

describe "::DSL" do
subject{ Class.new }

it 'should create 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) }

it 'should be able to define entity traits through DSL' do
subject.entity do
expose :name
end

subject.entity_class.exposures.should_not be_empty
end

it 'should be able to expose straight from the class' do
subject.entity :name, :email
subject.entity_class.exposures.size.should == 2
end

it 'should be able to mix field and advanced exposures' do
subject.entity :name, :email do
expose :third
end
subject.entity_class.exposures.size.should == 3
end

context 'instance' do
let(:instance){ subject.new }

describe '#entity' do
it 'should be an instance of the entity class' do
instance.entity.should be_kind_of(subject.entity_class)
end

it 'should have an object of itself' do
instance.entity.object.should == instance
end
end
end
end
end
end
end