2009-10-21 11 views
9

私はサブドメインを使って独立したアカウントサイトを隔離するRails(現在は2.3.4)アプリを開発中です。明確にするために、foo.mysite.comはfooアカウントのコンテンツを表示し、bar.mysite.comはバーのコンテンツを表示する必要があります。Rails:サブドメインに基づいてクエリをスコープするベストプラクティス?

すべてのモデルクエリが現在のサブドメインにスコープされるようにするにはどうすればよいですか?例えば

は、私のコントローラの一つは次のようになります。

@page = @global_organization.pages.find_by_id(params[:id])   

@global_organization注サブドメイン府を経由してapplication_controllerに設定されている。) 私が希望している場合は、何かのように:

@page = Page.find_by_id(params[:id]) 

Pageモデルの検索対象は、自動的に適切な組織に限定されます。私はこのようなdefault_scopeディレクティブを使用して試してみた:グローバルアクセスのための組織のIDへ:[組織]だけに注目することは、再び

class Page < ActiveRecord::Base 
    default_scope :conditions => "organization_id = #{Thread.current[:organization]}" 
    # yadda yadda 
end 

((ページモデルで)、同じapplication_controllerはThread.currentを設定します。 )このアプローチの問題は、デフォルトのスコープが最初の要求に設定され、後続の要求が異なるサブドメインに変更されないことです。

これまでに三の見かけソリューション:各サブドメインの

1を使用する別のバーチャルホストだけ(mod_railsを使って)、サブドメインごとにアプリの異なるインスタンスを実行します。このアプローチは、このアプリケーションのためにスケーラブルではありません。

2上記の元のコントローラの方法を使用してください。残念ながら、アプリにはかなりの数のモデルがあり、モデルの多くは組織から削除された結合であるため、この表記法はすぐに面倒になります。悪化するのは、開発者が制限を覚えて適用したり、重大なセキュリティ上の問題を引き起こすことを積極的に要求することです。

3 before_filterを使用して、各リクエストのモデルのデフォルトスコープをリセットします。ここでのパフォーマンスのヒットや、reqeustごとに更新するモデルを選択する最良の方法については不明です。

思考?私が行方不明の他の解決策?これは、ベストプラクティスでなければならない共通の問題であるようです。すべての入力は、ありがとう!

答えて

2

default_scopelambdaを定義しましたか?オプションを定義するlambdaビットは、スコープが使用されるたびに評価されます。

class Page < ActiveRecord::Base 
    default_scope lambda do 
    {:conditions => "organization_id = #{Thread.current[:organization]}"} 
    end 
    # yadda yadda 
end 

これは基本的に、前のフィルタマジックと連携して作業することで、3番目の選択肢です。しかし、それよりも少し積極的です。Pag​​eモデルで使用されるすべての検索エンジンに挑戦します。

すべてのモデルでこの動作が必要な場合は、default_scopeをActiveRecord :: Baseに追加することができますが、いくつかの結合は離れていることに言及してください。したがって、このルートを使用する場合は、それらのモデルのデフォルトスコープをオーバーライドして、ジョインに対処する必要があります。

+0

ありがとう!実際にそのコードをテストしましたか?私はアプリで試してみましたが、ラムダが実際に各検索で評価されていることを確認できましたが、default_scopeは戻り値を適用しません。出力SQLを見てみると、:条件は単に無視されるようです。私はまだデバッガを起動することができませんでしたが、それ以外のさまざまな理由で、復旧してすぐにピークに達します。 なぜ機能しないのでしょうか? – qfinder

+0

正直なところ、内部サーバがどのようにスレッドを使用しているのか分かりません。そのため、モデルでは 'Thread.current [:organization]'が働いていると思いました。しかし、条件が無視される理由を説明できるかどうかはわかりません。基本的には、organazation_id = nilを探すためのデフォルトスコープを定義しています。 – EmFi

+0

さらなる調査の結果、これはまだ機能しないようです。パッチはありますが、YMMV:https://rails.lighthouseapp.com/projects/8994/tickets/1812-default_scope-cant-take-procs – EmFi

2

ここでデフォルトのスコープを使用してください。これは、特にレコードを作成するときに、誤ったセキュリティの意識につながります。私はいつもこの明確に保つためにあなたの最初の例を使用しました

@page = @go.pages.find(params[:id]) 

あなたもこの関連付けを確認したいので、最大の理由は、新しいレコードに適用されるので、あなたの新しい作成/アクションが見えます以下のように、彼らはきちんと親会にスコープされることを保証する:

# New 
@page = @go.pages.new 

# Create 
@page = @go.pages.create(params[:page]) 
+0

良い点は、この場合、結合構造のために作成時に強制する必要がある場所がほんのわずかであることですが、読み込み時に強制する必要がある場所が多いので、まだ方法を見つけたいと思いますdefault_scopeを使用します。 – qfinder

+0

さらに、サブドメインスコープのマルチテナントアプリケーションを開始するためのサンプルアプリケーションであるhttp://github.com/devinterface/authlogic_subdomain_fu_startup_appを見てください。初期のユーザー(アカウントの所有者でもある)とのアカウント作成を処理します。 – bensie

+1

コントローラーの余分な作業/コードの混乱のように思えるかもしれませんが、実際はそうではありません。最後に、プログラマーの意図が示されているので、道路を簡単に修正することができます。 開発者が制限事項を覚えて適用する必要がある場合は、とにかくすべてのアクションに対してテストを行う必要があり、その1つのテストでモデルがその親に正しくスコープされるようにする必要があります。 – bensie

0

あなたがサブドメインに基づいてdatabase per account and switching the database connectionを持っていることの方が良いかもしれません。

デフォルトのデータベースを使用するモデル(ケースのアカウント)がモデルにある場合は、モデルにestablish connectionを追加するだけです。

class Account < ActiveRecord::Base 

    # Always use shared database 
    establish_connection "shared_#{RAILS_ENV}".to_sym 
+0

実行可能なソリューションかもしれませんが、アカウント間でレポート(分析)を実行することは大きな苦労です。マイグレーションなどもあります。 –

+0

はい、マイグレーションは私の痛みのポイントでした。私は、データベースのリストに対してマイグレーションを実行できる解決策を見ているのを覚えていますが、どこで呼び出すことはできません。 – Kris

0

我々は最後の2年間のhttps://github.com/penguincoder/acts_as_restricted_subdomainを使用してきたが、それが唯一のRails 2.3で動作します。

私は現在、Rails 3で動作するようにプラグインをアップグレード(そしてgemmify)しようとしています。あなたの問題の仕組みが不思議です。

関連する問題