2

以下では、配列を返す必要があるMovieというモデルにクラスメソッドを作成しました。

def self.all_ratings
  Array['G','PG','PG-13','R','NC-17']
end

そして、私のムービーコントローラーでは、次のインスタンス変数を使用してアクセスします。

@all_ratings = Movie.all_ratings

ただし、インデックスビューで使用すると、次のエラーが発生します。

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each

アレイを適切に作成していると思いますが、間違っている可能性があります。これらのエラーが発生する理由について何か提案はありますか?

以下は、@all_ratingsが使用されているビューです。

%h1 All Movies

= form_tag movies_path, :method => :get do
  Include: 
  - @all_ratings.each do |rating|
    = rating
    = check_box_tag "ratings[#{rating}]"
  = submit_tag 'Refresh'

そして、これが私が@all_ratingsをコントローラーに実装した方法です

class MoviesController < ApplicationController

  @all_ratings = Movie.all_ratings
4

2 に答える 2

5

インスタンス変数を初期化するコードは、インスタンス メソッド内にある必要があります。

(それ以外の場合、スコープはインスタンスではなくクラスです。)

class MoviesController < ApplicationController
  def index # Or wherever
    @all_ratings = Movie.all_ratings
  end
end

いくつかのメソッドでその値が必要な場合は、たとえば a を使用できますbefore_filter

Ruby は、Java などの言語とは異なります。Java では、インスタンス変数はインスタンス メソッドの外部で定義され、初期化された値に関係なく、すべてのインスタンス メソッドで使用できます。

Ruby では、インスタンス変数を処理する (主な) 方法が 2 つありますattr_accessor。上記のように、アクセサー メソッドを作成するようなメソッドを使用するか、インスタンス メソッド内でそれらを初期化します。

インスタンス変数が初期化されると、その値は他のインスタンス メソッドから使​​用できます。たとえば、コメントでは、ratingsメソッドで初期化することに言及しています。ratings明示的に呼び出されない限り、初期化@all_ratingsされません。つまり、 にGETリクエストを行ったindex場合、ratingsメソッドは呼び出され@all_ratings、 のままですnil

から明示的に呼び出すと 、 (メソッドによって) 初期化されます。任意のインスタンス メソッドで初期化されると、オブジェクトのそのインスタンス (この場合はコントローラー) は初期化されたインスタンス変数を持ちます。ratingsindex@all_ratingsratings@all_ratings

def index
  ratings
end

の値が@all_ratingsインデックスのテンプレートで利用できるようになりました。

インスタンスメソッドにインスタンス変数の初期化を入れずに、実際に行っているのはclass Fooにインスタンス変数を作成することです。これは非常に異なるものです:

[1] pry(main)> class Foo
[1] pry(main)*   @wat = self.class
[1] pry(main)* end  
=> Class
[2] pry(main)> f = Foo.new
=> #<Foo:0x007fbfba8efba8>
[5] pry(main)> f.instance_variables
=> []
[6] pry(main)> Foo.instance_variables
=> [:@wat]
[7] pry(main)> Foo.instance_eval "@wat"
=> Class
于 2012-06-06T00:45:49.890 に答える
3

@all_ratings = Movie.all_ratingsコントローラーのインデックスアクション内に移動してください。すべてのアクションに共通する場合は、次のように before フィルターに入れます。

class MoviesController < ApplicationController

before_filter :load_ratings

def index
..
end

<other public methods>

private

def load_ratings
  @all_ratings = Movie.all_ratings
end
于 2012-06-06T00:47:47.590 に答える