2012-02-20 30 views
8

¿Cómo puedo implementar el patrón Repository o Gateway en Ruby?Repositorio o patrón de puerta de enlace en Ruby

Vengo de un mundo C# y generalmente abstraigo mi acceso a los datos, pero con ActiveRecord como el mecanismo de acceso a datos predeterminado en Ruby, no es obvio cómo lograr eso.

Lo que normalmente haría en C# es el trabajo con los interfaces abstractas y luego tener una aplicación concreta para EFCustomerRepository, NHibernateCustomerRepository y InMemoryCustomerRepository y dependiendo de la situación me inyecto la aplicación concreta a juego.

Así que ahora, ¿cuál es la forma de Ruby?

Por lo que yo entiendo, en idiomas dinámicos no necesitaría algo como DI (inyección de dependencia). Y Ruby tiene poderosas funciones de lenguaje para permitir cosas como mixins.

¿Pero definiría la mezcla para usar estáticamente en clase o nivel de módulo?

¿Cómo escribo mi lógica de negocio si deseo desarrollarme en un repositorio en memoria y en producción cambiaría a mi ActiveRecord-Repository?

Si puede estar en el camino equivocado aquí ya que estoy acostumbrado a pensar en un lenguaje estáticamente tipado. ¿Cómo alguien abordaría esta tarea al estilo Ruby? Básicamente quiero hacer que mi capa de persistencia sea abstracta y sus implementaciones intercambiables.

EDIT: Me refiero a robert c. martins (unclebob) keynote about architecture

Gracias por cualquier ayuda ...

+0

http://lancecarlson.github.com/2012/05/15/dci-and-decoupling-business-logic-from-ruby-on-rails.html esto es algo que alguien hizo como respuesta al artículo del Tío Bob. Más por venir. – SpoBo

+0

También estoy buscando una gema que implemente el patrón de repositorio. – Andrew

Respuesta

2

Bueno, ActiveRecord ya proporciona la capa de persistencia abstracto - se ha several different adapters lo que le permite utilizar diferentes backends de bases de datos. Además, es de código abierto, por lo que puede ver cómo se ha logrado.

A primera vista, puede ver que también tiene un AbstractAdapter que todos los demás adaptadores heredan, sin embargo, como Ruby es dinámico, el lenguaje de pato, AbstractAdapter no tiene que contener métodos abstractos que se anularán en los niños clases, ninguno define un "contrato" que deben honrar.

Editar:

He aquí un bosquejo simple de cómo se puede abstraer su almacenamiento en Ruby, no está seguro de qué patrón es exactamente lo que es:

# say you have an AR model of a person 
class Person < ActiveRecord::Base 
end 

# and in-memory store of persons (simply, a hash) 
IN_MEMORY_STORE = { 
    :Person => ['Tim', 'Tom', 'Tumb'] 
} 

# this will abstract access 
class MyAbstractModel 
    def initialize item, adapter 
    @item = item 
    @adapter = adapter 
    end 

    # get all elements from the store 
    def all 
    case @adapter 
    when :active_record 
     # pull from database: 
     Object.const_get(@item).all 
    when :in_memory_store 
     # get from in-memory store 
     IN_MEMORY_STORE[@item] 
    else 
     raise "Unknown adapter" 
    end 
    end 
end 

# get all Persons from in-memory storage... 
p MyAbstractModel.new(:Person, :in_memory_store).all 
# ...and from a database 
p MyAbstractModel.new(:Person, :active_record).all 
+0

Gracias por la respuesta ... Me preguntaba cómo el lenguaje de pato maneja cosas como esta ... Y cómo me mantendría independiente de los marcos, así que si un día ActiveRecord ya no es una exageración y hay un chico nuevo en el mundo bloquearé, podré cambiar fácilmente ... –

+0

No es porque sea el "chico nuevo de la cuadra" que tienes que usarlo absolutamente ... Puedes seguir usando tecnologías que ya no son "la exageración" y todavía hacer un software "bueno". –

1

@serverinfo, no sé mucho acerca de DO#. Pero cuando vine a Ruby con experiencia en Java/C, quedé impresionado cuando me di cuenta de lo flexible que es este lenguaje. Usted dice que su verdadero problema aquí es "abstraer su capa de persistencia y hacerla intercambiable". También preguntó "cómo escribiré la lógica comercial".

Te sugiero que deseches tus ideas preconcebidas y te preguntes: "¿cómo puedo como para expresar el acceso/almacenamiento de datos dentro de mi capa de lógica de negocios"? No se preocupe por lo que cree que puede o no puede hacer; Si puede averiguar cómo funcionaría la interfaz como, probablemente haya una manera de hacerlo en Ruby.

También tendrá que decidir cómo quiere especificar la implementación concreta que se utilizará. ¿Es posible que desee utilizar un almacén de datos diferente para diferentes objetos modelo? ¿Podría querer cambiar en tiempo de ejecución? ¿Le gustaría especificar el back-end para ser utilizado en un archivo de configuración o en un código? Si puede decidir qué quiere , hay muchas personas en Stack Overflow que pueden ayudarlo a averiguar cómo hacerlo.

+0

Sí, gracias por la respuesta ... Estaba tratando de entender esto: http://confreaks.com/videos/759-rubymidwest2011-keynote-architecture-the-lost-years ... y tienes una gran preocupación mental, pero bueno =) –

3

Entiendo lo que dices. Vengo de un fondo .NET también. Resumiendo su lógica de negocio & lógica de persistencia es una buena idea. No he encontrado una joya que lo haga por ti todavía. Pero puedes enrollar fácilmente algo simple tú mismo. Al final, un patrón de repositorio es básicamente una clase que delega a su capa de persistencia.

Aquí es lo que hago:

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 

Y entonces se podría por ejemplo tener un repositorio de Correos.

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 

Simplemente ejecútelo en su controlador en un before_filter o inicialícelo o donde sea. En este caso, lo estoy buscando en el usuario_actual para que solo obtenga esos registros y cree automáticamente publicaciones solo para el usuario actual.

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 

me encontré con https://github.com/bkeepers/morphine que es un pequeño marco DI. Podría funcionar para usted :) Pero DI no es un patrón muy usado en ruby. Además, ejemplifico mis repos para ubicarlos en un usuario actual u otra cosa.

Estoy en una búsqueda para encontrar la manera correcta de hacer justo lo que me pides y hacer un pequeño comentario al respecto si alguna vez lo encuentro. Pero por ahora ya es suficiente para hacer el corte limpio entre persistence & mis controladores. Si esto se hace correctamente, no será una gran molestia cambiar a un sistema diferente más adelante. O agregue el almacenamiento en caché, etc.

+0

Me gustó la forma en que describió anteriormente e intenté implementar esto en una de mis aplicaciones. Pero me quedé atascado en dónde poner/agregar las conexiones de la base de datos y los productos ActiveRecord? ¿Se puede mejorar lo anterior con una conexión y métodos simples de ActiveRecord/sqlite3 db? – Autodidact

Cuestiones relacionadas