Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repository or Gateway pattern in Ruby

How can I implement the Repository or Gateway pattern in Ruby?

I come from a C# world and I usually abstract away my data access but with ActiveRecord as the default data access mechanism in Ruby, it's not obvious how to accomplish that.

What I usually would do in C# is work with abstract interfaces and then have a concrete implementation for EFCustomerRepository, NHibernateCustomerRepository and InMemoryCustomerRepository and depending on the situation I inject the matching concrete implementation.

So now, what’s the Ruby way?!

As far as I understand it, in dynamic languages you would not need something like DI (dependency injection). And Ruby has powerful language features to allow things like mixins.

But you would define the mixin to use statically on class or module-level?

How do I write my business logic if I want to develop against an in-memory repository and in production I would switch to my ActiveRecord-Repository?

If might be on the wrong path here since I'm used to thinking in a statically typed language. How would someone tackle this task the Ruby way? Basically I want to make my persistence layer abstract and it's implementations interchangeable.

EDIT: I am referring to robert c. martins (unclebob) keynote about architecture

Thanks for any help...

like image 241
server info Avatar asked Feb 20 '12 17:02

server info


People also ask

What is a repository in Ruby?

"A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer.

Should I use Repository pattern?

The Repository pattern makes it easier to test your application logic. The Repository pattern allows you to easily test your application with unit tests. Remember that unit tests only test your code, not infrastructure, so the repository abstractions make it easier to achieve that goal.

Is repository pattern as same as Active Record pattern?

They are different. Active Record Pattern defines An object that wraps a row in a database table or view, encapsulates the data access, and adds domain logic on that data. In the Repository pattern all of the data access is put in a separate class and is accessed via instance methods.

What is the benefit of repository pattern in Entity Framework?

The Repository Pattern allows us to create an abstraction layer between the data access layer and the business logic layer of an application. So, this Data Access Pattern offers a more loosely coupled approach to data access.


1 Answers

I get what you are saying. I come from a .NET background as well. Abstracting away your business logic & persistance logic is imo a good idea. I haven't found a gem that does it for you yet. But you can easily roll something simple yourself. In the end a repository pattern is basically a class that delegates to your persistance layer.

Here is what I do:

require 'active_support/core_ext/module/attribute_accessors'

class GenericRepository

  def initialize(options = {})
    @scope = options[:scope]
    @association_name = options[:association_name]
  end

  def self.set_model(model, options = {})
    cattr_accessor :model
    self.model = model
  end

  def update(record, attributes)
    check_record_matches(record)
    record.update_attributes!(attributes)
  end

  def save(record)
    check_record_matches(record)
    record.save
  end

  def destroy(record)
    check_record_matches(record)
    record.destroy
  end

  def find_by_id(id)
    scoped_model.find(id)
  end

  def all
    scoped_model.all
  end

  def create(attributes)
    scoped_model.create!(attributes)
  end

private

  def check_record_matches(record)
    raise(ArgumentError, "record model doesn't match the model of the repository") if not record.class == self.model
  end

  def scoped_model
    if @scope
      @scope.send(@association_name)
    else
      self.model
    end
  end

end

And then you could for example have a Post repository.

class PostRepository < GenericRepository

  set_model Post

  # override all because we also want to fetch the comments in 1 go.
  def all
    scoped_model.all(:include => :comments)
  end

  def count()
    scoped_model.count
  end

end

Just instantiate it in your controller in a before_filter or initialize or wherever. In this case I'm scoping it to the current_user so that it only fetches those records and automatically create posts only for the current user.

def initialize
  @post_repository = PostRepository.new(:scope => @current_user, :association_name => 'posts')
end

def index
  @posts = @post_repository.all
  respond_with @posts, :status => :ok
end

I came across https://github.com/bkeepers/morphine which is a tiny DI framework. It could work for you :) But DI isn't a heavily used pattern in ruby. Also, I instantiate my repos in order to scope them to a current user or something else.

I'm on a quest to find the right way to do just what you ask and do a little write-up about it if I ever do find it. But for now it's already sufficient to make the clean cut between persistance & my controllers. If this is done properly it won't be a big hassle to switch to a different system later on. Or add caching etc.

like image 180
SpoBo Avatar answered Oct 23 '22 11:10

SpoBo