Repositor
Installation & Setup
In gemfile:
gem 'repositor'
In console:
bundle
Description
This gem is an implementation of Repository Pattern described in book Fearless Refactoring Rails Controllers by Andrzej Krzywda 2014 (c). Awesome book, recommend read for all who are scary open own controller files ;)
The main reason to user RepoObject is that your controller don't communicate with ORM layer (ActiveRecord or Mongoid). It must communicate with Repo layer so you are not stricted about your database adapter. If in future you will want to change it, you will need just to reconfigure your Repository layer. Sounds nice. Let's try it..
With RepoObject you controller must look something like this:
class ProductsController < ApplicationController
# you are free from action callbacks...
def index
@products = repo.all # you communicate with repo object, not with model!
end
def show
@product = repo.find(params[:id]) # with repo... only...
end
def new
@product = repo.new # it's awesome! no?
end
def edit
@product = repo.find(params[:id]) # some dry?
end
def create
respond_to do |format|
@product = repo.create(product_params) # send params to repo
if @product.valid? # just check for valid, not for create action
format.html { redirect_to @product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: @product }
else
format.html { render :new }
format.json { render json: @product.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
@product = repo.update(params[:id], product_params) # give repo id of record and params for update
if @product.valid? # only validations
format.html { redirect_to @product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: @product }
else
format.html { render :edit }
format.json { render json: @product.errors, status: :unprocessable_entity }
end
end
end
def destroy
repo.destroy(params[:id]) # destroy also throw repo object
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def product_params
params.require(:product).permit(:name, :price, :dscription)
end
# Declaration if repo object:
def repo
@products_repo ||= ProductsRepo.new
end
# By default repositor will try to find `Product` model and communicate with it
# if you need specify other model, pass in params
# ProductsRepo.new(model: SaleProduct)
end
How to use
In app directory you need to create new repos directory . Recomended to create application_repo.rb and inherit from it all repos, so you could keep all your repos under single point of inheritance.
class ApplicationRepo
# include ORM submodule
# now supported only ActiveRecord
# more will added soon
include Repositor::ActiveRecord
end
Than you need to create products_repo.rb:
class ProductsRepo < ApplicationRepo;
# here you will have default methods for repo actions
end
and that's all... magic already happened (no)
If check what exactly was done, including Repository module in base ApplicationRepo will add default CRUD methods to all repos that will be inherited from it. That's all. No magic.
Repositor did for you a lot of dry work. In other case for each repo you must make identical methods, like this:
class ProductsRepo
def find(product_id)
Product.find(product_id)
end
def all
Product.all
end
def new
Product.new
end
def update(product_id, params)
find(product_id).tap do |product|
product.update(params)
end
end
def destroy(product_id)
find(product_id).destroy
end
def create(product_params)
Product.create(product_params)
end
end
If you need to add new method to repo, just define it in repo file.
TODO
- Specs on the way...
- Add mongoid support
- Some improvements ? =)