Encore
Encore is a Ruby framework to help dealing with entities.


Installation

Add this line to your application's Gemfile:

gem 'encore'

And then execute:

$ bundle

Or install it yourself as:

$ gem install encore

Usage

Let’s say we have the UserEntity entity class bound to the User model:

class User < ActiveRecord::Base
end

class UserEntity
  include Encore::Entity

  expose :name
  expose :email
  expose :created_at, readonly: true
  expose :updated_at, readonly: true
end

We can create a new User resource with attributes:

@entity = UserEntity.new
@entity.assign_attributes email: '[email protected]', name: 'Rémi Prévost'
@entity.save

When assigning attributes to an entity, we can pass either an :update or a :partial_update context.

With the (default) :partial_update context, Encore will assign new attributes and ignore the other exposed attributes. This makes sense in a PATCH HTTP request context.

new_attributes = { email: '[email protected]' }
@entity.assign_attributes new_attributes
@entity.object.email # => "[email protected]"
@entity.object.name # => "Rémi Prévost"

However, with the :update context, Encore will assign new attributes and set all non-provided exposed attributes to nil. This makes sense in a PUT HTTP request context.

new_attributes = { email: '[email protected]' }
@entity.assign_attributes new_attributes, context: :update
@entity.object.email # => "[email protected]"
@entity.object.name # => nil

If we try to assign a value to a non-exposed or readonly attribute, Encore will raise an error.

@entity.assign_attributes email: '[email protected]', created_at: Time.now
# => raises #<Encore::Entity::Input::InvalidAttributeError: The #<Encore::Attribute UserEntity@created_at> attribute is not exposed.>

Associations

You can also expose associations.

class User < ActiveRecord::Base
  belongs_to :organization
end

class Organization < ActiveRecord::Base
  has_many :users
end

class UserEntity
  include Encore::Entity

  expose :name
  expose_one :organization
end

Assigning new value for associations doesn’t save them right away.

@user = User.first # => #<User id=1 organization_id=1>
@entity = UserEntity.new(@user)
@entity.assign_attributes organization: 2
@entity.object.organization_id # => 1

Calling save on the entity saves them.

@entity.save
@entity.object.organization_id # => 2

Typical setup with Ruby on Rails

This is work-in-progress. There’s still missing stuff.

Model

# app/models/user.rb
class User < ActiveRecord::Base
end

Entity

# app/entities/user_entity.rb
class UserEntity
  include Encore::Entity

  expose :name
  expose :email
  expose :created_at, readonly: true
  expose :updated_at, readonly: true
end

Routes

# config/routes.rb
Rails::Application.routes.draw do
  resources :users do
    # This makes Rails route PUT and PATCH requests to two separate actions
    patch on: :member, action: :partial_update
  end
end

Controller

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :fetch_user, only: [:update, :partial_update]

  # POST /users
  def create
    @entity = UserEntity.new
    @entity.assign_attributes(params[:user], context: :create)

    process! @entity

    # Here, `process!` is a shortcut for:
    #
    # if @entity.save
    #   render json: @entity, status: 201
    # else
    #   render json: { errors: @entity.errors }, status: 422
    # end
  end

  # PUT /users/:id
  def update
    @entity = UserEntity.new(@user)
    @entity.assign_attributes(params[:user], context: :update)

    process! @entity
  end

  # PATCH /users/:id
  def partial_update
    @entity = UserEntity.new(@user)
    @entity.assign_attributes(params[:user], context: :partial_update)

    process! @entity
  end

protected

  def fetch_user
    @user = User.find(params[:id])
  end
end

Todo

Please keep in mind that this gem is far from finished and totally not ready to use in production. This is something we’ve been wanting to build for a long time and now we’re finally taking the time do it right.

License

Encore is © 2013 Mirego and may be freely distributed under the New BSD license. See the LICENSE.md file.

The nut logo is based on this lovely icon by Alessandro Suraci, from The Noun Project. Used under a Creative Commons BY 3.0 license.

About Mirego

Mirego is a team of passionate people who believe that work is a place where you can innovate and have fun. We proudly build mobile applications for iPhone, iPad, Android, Blackberry, Windows Phone and Windows 8 in beautiful Quebec City.

We also love open-source software and we try to extract as much code as possible from our projects to give back to the community.