4

私の質問

Rails 4 は、ネストされたスコープ外のブロックを無視しているようです (Rails 3 では問題ありませんでした)。私は狂ったようにグーグルしてきましたが、ここで変化を示すものは何も見つかりません. Rails 4でこれを機能させる方法はありますか?

私がしていること

#388 Multitenancy with Scopesに示されているように、マルチテナンシーに default_scope を使用しています。一部の管理者は複数のテナントの管理者になり、レポートに集計データを表示したいと考えています。スコープ外のブロックを使用してこれを行っています。1) より効率的であり、2) 関連するすべてのオブジェクトを 1 か所で取得する必要があるため、ビューでスコープ外のブロックを使用し続ける必要がないため、関連するオブジェクトもプリロードしています。関連するオブジェクトをプリロードするために、ネストされたスコープなしブロックを使用しています。

Rails 3.2.17 のアプリでこれが機能していましたが、Rails 4.0.1.rc1 にアップグレードしたため、機能しなくなりました。

違いを説明する簡単な例

以下に、コンソールに表示される内容を示します。この例は、私が実際にやりたいことよりもはるかに単純ですが、何が起こっているかを示す最も簡単な方法だと思います。

Loading development environment (Rails 3.2.17)
1.9.3-p374 :001 > s = nil
   => nil 
1.9.3-p374 :002 > Submission.unscoped { Checklist.unscoped { s = Submission.preload(:checklist).find(3269) }}
  Submission Load (27.8ms)  SELECT "submissions".* FROM "submissions" WHERE "submissions"."id" = $1 LIMIT 1  [["id", 3269]]
  Checklist Load (0.6ms)  SELECT "checklists".* FROM "checklists" WHERE "checklists"."id" IN (17)
 => #<Submission id: 3269, user_id: nil, workday_id: 17, checklist_id: 17, note: nil, submitted: false, submitted_at: nil, created_at: "2014-03-12 01:06:03", updated_at: "2014-03-12 01:06:03", for_date: "2014-03-11", tenant_id: 2, position: 1, department_id: nil, status: "blank"> 
1.9.3-p374 :003 > s
 => #<Submission id: 3269, user_id: nil, workday_id: 17, checklist_id: 17, note: nil, submitted: false, submitted_at: nil, created_at: "2014-03-12 01:06:03", updated_at: "2014-03-12 01:06:03", for_date: "2014-03-11", tenant_id: 2, position: 1, department_id: nil, status: "blank"> 
1.9.3-p374 :004 > s.checklist
 => #<Checklist id: 17, name: "Open", description: "Chores Required To Open Store Front", creator_id: 2, created_at: "2013-09-23 21:55:23", updated_at: "2013-09-23 21:55:23", archived: false, archived_at: nil, ancestry: nil, tenant_id: 2>

そのため、提出物をロードし、関連するチェックリストをプリロードしています。次に、提出物とそれに関連するチェックリストの両方が利用可能であることを確認できます。

Rails 4 環境 (どちらも同じデータベースで動作) に切り替えると、次のように表示されます。

 Loading development environment (Rails 4.1.0.rc1)
 1.9.3-p374 :001 > s = nil
  => nil 
 1.9.3-p374 :002 > Submission.unscoped { Checklist.unscoped { s = Submission.preload(:checklist).find(3269) }}
   Submission Load (0.4ms)  SELECT  "submissions".* FROM "submissions"  WHERE "submissions"."id" = $1 LIMIT 1  [["id", 3269]]
   Checklist Load (0.6ms)  SELECT "checklists".* FROM "checklists"  WHERE "checklists"."tenant_id" IS NULL AND "checklists"."id" IN (17)
  => #<Submission id: 3269, user_id: nil, workday_id: 17, checklist_id: 17, note: nil, submitted: false, submitted_at: nil, created_at: "2014-03-12 01:06:03", updated_at: "2014-03-12 01:06:03", for_date: "2014-03-11", tenant_id: 2, position: 1, department_id: nil, status: "blank"> 
 1.9.3-p374 :003 > s
  => #<Submission id: 3269, user_id: nil, workday_id: 17, checklist_id: 17, note: nil, submitted: false, submitted_at: nil, created_at: "2014-03-12 01:06:03", updated_at: "2014-03-12 01:06:03", for_date: "2014-03-11", tenant_id: 2, position: 1, department_id: nil, status: "blank"> 
 1.9.3-p374 :004 > s.checklist
  => nil

私はまったく同じコードを実行していますが、提出のみが利用可能です - 関連するチェックリストはありません。

FWIW、これをコンソールで実行すると、Rails 3.2.17でこれを複製できます。注: ここでの違いは、Submission.unscoped {} ブロックのみを使用しており、Checklist.unscoped {} ブロックをネストしていないことです。

  Loading development environment (Rails 3.2.17)
  1.9.3-p374 :001 > s = nil
   => nil 
  1.9.3-p374 :002 > Submission.unscoped { s = Submission.preload(:checklist).find(3269) }
    Submission Load (3.6ms)  SELECT "submissions".* FROM "submissions" WHERE "submissions"."id" = $1 LIMIT 1  [["id", 3269]]
    Checklist Load (0.4ms)  SELECT "checklists".* FROM "checklists" WHERE "checklists"."tenant_id" IS NULL AND "checklists"."id" IN (17)
   => #<Submission id: 3269, user_id: nil, workday_id: 17, checklist_id: 17, note: nil, submitted: false, submitted_at: nil, created_at: "2014-03-12 01:06:03", updated_at: "2014-03-12 01:06:03", for_date: "2014-03-11", tenant_id: 2, position: 1, department_id: nil, status: "blank"> 
  1.9.3-p374 :003 > s
   => #<Submission id: 3269, user_id: nil, workday_id: 17, checklist_id: 17, note: nil, submitted: false, submitted_at: nil, created_at: "2014-03-12 01:06:03", updated_at: "2014-03-12 01:06:03", for_date: "2014-03-11", tenant_id: 2, position: 1, department_id: nil, status: "blank"> 
  1.9.3-p374 :004 > s.checklist
   => nil

アップデート

Rails のソースを掘り下げてみたところ、「unscoped」メソッドは現在のマスターと 3.2-stable で同じですが、そのメソッドは「relation.scoping」を呼び出し、そのメソッドは 2 つのブランチ間で大幅に異なっていることがわかります。バグが導入されたのか、それとも予想される動作が変更されたのかはわかりません。

参考のため:

現在のマスター

def unscoped
  block_given? ? relation.scoping { yield } : relation
end

関係のスコープ

def scoping
  previous, klass.current_scope = klass.current_scope, self
  yield
ensure
  klass.current_scope = previous
end

current_scope

def current_scope #:nodoc:
  ScopeRegistry.value_for(:current_scope, base_class.to_s)
end

3.2安定

def unscoped #:nodoc:
  block_given? ? relation.scoping { yield } : relation
end

関係のスコープ

def scoping
  @klass.with_scope(self, :overwrite) { yield }
end

with_scopeメソッドへのリンク...

def with_scope
  # Pretty long and involved method
  # See Line 60 in the linked doc
end
4

2 に答える 2