2012-02-20 5 views
8

Rubyでリポジトリまたはゲートウェイパターンを実装するにはどうすればよいですか?Rubyのリポジトリまたはゲートウェイパターン

私はC#の世界から来ています。私は通常、データアクセスを抽象化しますが、RubyのデフォルトのデータアクセスメカニズムとしてActiveRecordを使用しています。私は通常、C#で何をするのか

EFCustomerRepositoryNHibernateCustomerRepositoryInMemoryCustomerRepositoryのための具体的な実装を持っていると私は一致する具体的な実装を注入し、状況に応じて、次に抽象インタフェースを持つ作品であると。

これで、Rubyの方法は何ですか?

私が理解する限り、動的言語ではDI(依存性注入)のようなものは必要ありません。 Rubyには、mixinのようなものを可能にする強力な言語機能があります。

しかし、クラスやモジュールレベルで静的に使用するには、mixinを定義しますか?

私はインメモリリポジトリに対して、そして私は自分のActiveRecord-Repositoryに切り替えるでしょうか?

私は静的型付けされた言語で考えることに慣れているので、ここでは間違った経路にあるかもしれません。誰かがRubyのやり方でどのようにこのタスクに取り組んでいますか?基本的には、私のパーシスタンス層を抽象的にしたいと思っています。

EDIT:が、私は任意の助けrobert c. martins (unclebob) keynote about architecture

感謝を参照しています...

+0

http://lancecarlson.github.com/2012/05/15/dci-and-decoupling-business-logic-from-ruby-on-rails.htmlこれは誰かがBobさんの記事に返信してください。もっと来る。 – SpoBo

+0

私はまた、リポジトリパターンを実装する宝石を探しています。 – Andrew

答えて

2

まあ、ActiveRecordのは、すでに抽象永続層を提供 - それは別のデータベースバックエンドを使用することができseveral different adaptersを持っています。また、オープンソースなので、どのように達成されたかを自由に見てもらうことができます。

最初の目には、AbstractAdapterが他のすべてのアダプタに継承されていることがわかりますが、Rubyは動的なダック型言語であるため、子でオーバーライドされる抽象メソッドを含む必要はありませんどちらも、彼らが尊敬すべき "契約"を定義していない。

編集:

ここであなたは正確にはわからない、抽象化Rubyであなたのストレージである可能性があるパターン方法についての簡単なスケッチです:

# 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

返信ありがとうございました...私はアヒル型タイピング言語がこのようなことをどのようにしているのだろうと思っていました...そして、私はフレームワークから独立した方法をとっていました。ある日、ActiveRecordはもう誇大宣伝ではなく、ブロックすると簡単に切り替えることができます... –

+0

それはあなたが絶対にそれを使用しなければならない 'ブロック上の新しい子供 'ではありません...あなたは'これ以上の誇大広告ではない 'それでも "良い"ソフトウェアを作る。 –

1

@serverinfoが、私は、について多くを知りませんC#。しかし、Java/CのバックグラウンドからRubyに来たときに、この言語がどれほど柔軟であるかを実感したとき、私は吹き飛ばされました。ここでの本当の問題は、「パーシスタンス層を抽象化して交換可能にする」ことです。また、「ビジネスロジックをどのように書くのか」と尋ねました。

私は、あなたの先入観を捨てて、自分のビジネスロジックレイヤー内でデータアクセス/ストレージを表現するためにのようにをどうやったらいいでしょうか?あなたが思う、できないことを心配しないでください。もしあなたがのようにのようなインターフェースがうまくいくと分かっていれば、おそらくRubyでやり遂げることができます。

具体的な実装をどのように指定するかを決める必要があります。異なるモデルオブジェクトに対して異なるデータストアを使用することは可能でしょうか?実行時に切り替えることができますか?構成ファイルまたはコードで使用するバックエンドを指定しますか? が何であるかを決めることができる場合は、には、をどのように把握するのを助けることができる多くの人々がスタックオーバーフローにあります。

+0

返信ありがとうございました...私はこれを理解しようとしていました:http://confreaks.com/videos/759-rubymidwest2011-keynote-architecture-the-lost-years ...そしてあなたはまるで心のシフトです - しかし良い=) –

3

私はあなたが言っていることを得る。私は.NETの背景からも来ています。あなたのビジネスロジックを抽象化して&パーシスタンスロジックは良いアイデアです。私はあなたのためにそれを行う宝石をまだ見つけていない。しかし、あなたは簡単に何かを簡単に転がすことができます。最終的には、リポジトリパターンは基本的にあなたの持続層に委譲するクラスです。ここで

は、私が何をすべきかです:

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 

そして、あなたは、例えばポストリポジトリを持つことができます。

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 

コントローラでbefore_filterでインスタンシエートするか、初期化するか、どこからでも行うことができます。この場合、私はそれを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 

私は小さなDIフレームワークであるhttps://github.com/bkeepers/morphineに出くわしました。それはあなたのために働くことができる:)しかし、DIは、ルビーの頻繁に使用されるパターンではありません。また、私は自分のリポジトリをインスタンス化して、現在のユーザーまたは他の何かにそれらをスコープします。

私はあなたが求めるものだけを行う正しい方法を見つけるために探求しています。もし私がこれを見つけたら少し書き上げます。しかし、今のところ、私のコントローラである持続性&の間でクリーンカットを行うだけで十分です。これが適切に行われれば、後で別のシステムに切り替えるのは面倒ではありません。または、キャッシングなどを追加してください。

+0

私は上記の方法が好きで、これを私のアプリケーションの1つで実装しようとしました。しかし、私はデータベース接続とActiveRecordのものを置く/追加する場所に立ち往生していますか?単純なActiveRecord/sqlite3 db接続とメソッドを使って上記を強化できますか? – Autodidact

関連する問題