5

railscastsプロジェクトでは、次のコードを確認できます。

before(:each) do
  login_as Factory(:user, :admin => true)
end

関数の対応する定義は次のとおりです。

Factory.define :user do |f|
  f.sequence(:github_username) { |n| "foo#{n}" }
end

adminパラメーターが関数にどのように渡されているのか理解できませんが、関数にはadminパラメーターについての単語がありません。ありがとう

4

3 に答える 3

9

Factory.defineは関数定義ではなく、シンボルまたは文字列(この場合はユーザー)と、作成するファクトリを定義するブロックを受け取るメソッドです。 admin属性をFactory(:user, :admin => true)持つオブジェクトを作成します。User2番目のスニペットのコードを呼び出すのではなくFactory()、ファクトリを初期化して1つ(この場合は2番目のスニペットで定義されたもの)を選択する呼び出しです。次に、オプションをハッシュ形式でFactoryにも渡します。

Factory:user非常に一般的なファクトリを選択します。このオプションは、Userの管理インスタンス変数をtrueに設定するように:admin=>true指示するだけです。Factory

This is actually what it is calling in factory.rb in factory girl

def initialize(name, options = {}) #:nodoc:
  assert_valid_options(options)
  @name = factory_name_for(name)
  @options = options
  @attributes = []
end

したがって、Factory(name、options)は、このコードのFactory.new(name、options)と同等です。

http://www.ruby-doc.org/core/classes/Kernel.html 配列や文字列などの構造は似ています。私は彼らが今それをどのように行ったかを理解しようとしています。

これは、まともなRubyプログラマーにとってさえすべて混乱を招きます。「MetaprogrammingRuby」という本を強くお勧めします。これはおそらく私がルビーで読んだ中で最高の本であり、この魔法のことについて多くのことを教えてくれます。

于 2011-02-17T06:51:16.307 に答える
3

MichaelPapileの応答は本質的に正しいです。ただし、知っておきたい技術的なニュアンスがいくつかあるので、少し詳しく説明したいと思います。railscastsfactory_girlのコードを調べたところ、パズルには、 :admin => trueargがユーザーファクトリのadmin属性を作成する方法を説明する追加のピースがいくつかあると思います。属性の追加は、実際にはFactoryのinitialize()メソッドを介して行われるわけではありませんが、Michaelが指摘したように、このメソッドは実際に新しいユーザーファクトリオブジェクトを構築するために呼び出されています。

この説明には、同様の質問を調査する方法を確認したい場合に備えて、私が行ったすべての手順を含めます。

元の投稿の日付は2月17日なので、その日付に厳密に一致するバージョンのrailscastを調べました。

私はそのGemfileを調べました:

https://github.com/ryanb/railscasts/blob/d124319f4ca2a2367c1fa705f5c8229cce70921d/Gemfile

18行目:

gem "factory_girl_rails"

次に、2月17日の日付に最も近いfactory_girl_railsのコミットを確認しました。

https://github.com/thoughtbot/factory_girl_rails/blob/544868740c3e26d8a5e8337940f9de4990b1cd0b/factory_girl_rails.gemspec

16行目:

s.add_runtime_dependency('factory_girl', '~> 2.0.0.beta')

factory_girlバージョン2.0.0.betaは、実際にはそれほど簡単に見つけることができませんでした。その名前のgithubタグはないので、コミット日に関して最も近いものをチェックしました。

https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/syntax/vintage.rb

122〜128行目:

# Shortcut for Factory.default_strategy.
#
# Example:
#   Factory(:user, :name => 'Joe')
def Factory(name, attrs = {})
  Factory.default_strategy(name, attrs)
end

したがって、railscastsでのFactory呼び出しは、実際には、同じファイルにある「デフォルト戦略」を呼び出す便利なメソッドを呼び出しています。

39〜52行目:

# Executes the default strategy for the given factory. This is usually create,
# but it can be overridden for each factory.
#
# Arguments:
# * name: +Symbol+ or +String+
#   The name of the factory that should be used.
# * overrides: +Hash+
#   Attributes to overwrite for this instance.
#
# Returns: +Object+
# The result of the default strategy.
def self.default_strategy(name, overrides = {})
  self.send(FactoryGirl.find(name).default_strategy, name, overrides)
end

FactoryGirl.findは、 default_strategyを呼び出すオブジェクトを取得するために呼び出されることに注意してください。findメソッドはここに解決されます:

https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/registry.rb

12〜14行目:

def find(name)
  @items[name.to_sym] or raise ArgumentError.new("Not registered: #{name.to_s}")
end

ここでの名前は:userです。したがって、ユーザーファクトリでdefault_strategyを呼び出したいと思います。Michael Papileが指摘したように、このユーザーファクトリは、ファクトリのクラス定義であると最初に考えていたrailscastsコードによって定義および登録されました。

https://ryanb/railscasts/blob/d124319f4ca2a2367c1fa705f5c8229cce70921d/spec/factories.rb

23〜25行目:

Factory.define :user do |f|
  f.sequence(:github_username) { |n| "foo#{n}" }
end

そのため、ユーザーファクトリのデフォルトの戦略を調査する際に、railscastsプロジェクトを調べて、次のことを見つけました。

https://ryanb/railscasts/blob/d124319f4ca2a2367c1fa705f5c8229cce70921d/spec/factories.rb

43〜45行目:

def default_strategy #:nodoc:
  @options[:default_strategy] || :create
end

:createがデフォルトの戦略です。factory_girlに戻って、 createのdefを見つけます。

https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/syntax/methods.rb

37〜55行目:

# Generates, saves, and returns an instance from this factory. Attributes can
# be individually overridden by passing in a Hash of attribute => value
# pairs.
#
# Instances are saved using the +save!+ method, so ActiveRecord models will
# raise ActiveRecord::RecordInvalid exceptions for invalid attribute sets.
#
# Arguments:
# * name: +Symbol+ or +String+
#   The name of the factory that should be used.
# * overrides: +Hash+
#   Attributes to overwrite for this instance.
#
# Returns: +Object+
# A saved instance of the class this factory generates, with generated
# attributes assigned.
def create(name, overrides = {})
  FactoryGirl.find(name).run(Proxy::Create, overrides)
end 

createストラテジーは、ここで定義されているrunメソッドを呼び出します。

https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/factory.rb

86〜97行目:

def run(proxy_class, overrides) #:nodoc:
  proxy = proxy_class.new(build_class)
  overrides = symbolize_keys(overrides)
  overrides.each {|attr, val| proxy.set(attr, val) }
  passed_keys = overrides.keys.collect {|k| FactoryGirl.aliases_for(k) }.flatten
  @attributes.each do |attribute|
    unless passed_keys.include?(attribute.name)
      attribute.add_to(proxy)
    end
  end
  proxy.result(@to_create_block)
end

このコードが実行していることの翻訳/要約:

まず、プロキシオブジェクトは、proxy_class(この場合はProxy :: Create )でnewを呼び出すことによって構築されます。これは、次のように定義されています。

https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/proxy/create.rb

基本的に知っておく必要があるのは、プロキシが新しいユーザーファクトリオブジェクトを構築し、ファクトリオブジェクトが作成される前後にコールバックを呼び出すことだけです。

runメソッドに戻ると、元々 Factoryコンビニエンスメソッド(この場合は:admin => true )に渡されたすべての追加の引数がオーバーライドとしてラベル付けされていることがわかります。次に、プロキシオブジェクトはsetメソッドを呼び出し、各属性名と値のペアを引数として渡します。

set()メソッドは、 Proxyの親クラスであるBuildクラスの一部です。

https://thoughtbot/factory_girl/blob/9fb8a3b40f24f0c8477776133a2f9cd654ca1c8c/lib/factory_girl/proxy/build.rb

12〜14行目:

def set(attribute, value)
  @instance.send(:"#{attribute}=", value)
end

ここで、@ instanceは、プロキシされたオブジェクトであるユーザーファクトリオブジェクトを指します。

これは、:admin=>trueがrailscastsスペックコードが作成するユーザーファクトリの属性として設定される方法です。

必要に応じて、「プログラミングデザインパターン」をグーグルで検索し、ファクトリー、プロキシ、ビルダー、ストラテジーのパターンについて読むことができます。

MichaelPapileは次のように書いています。

http://www.ruby-doc.org/core/classes/Kernel.html配列や文字列などの構造は似ています。私は彼らが今それをどのように行ったかを理解しようとしています。

それでも興味がある場合は、カーネルドキュメントに表示される配列と文字列は、実際には、これらのタイプの新しいオブジェクトを作成するために使用されるファクトリメソッドにすぎません。そのため、新しいメソッドの呼び出しは必要ありません。これらは実際にはコンストラクター呼び出しではありませんが、ArrayオブジェクトとStringオブジェクトを割り当てて初期化するため、内部では、これらのタイプのオブジェクトに対してinitialize()を呼び出すのと同じことを行います。(ただし、CではもちろんRubyではありません)

于 2012-01-20T18:18:38.457 に答える
-1

2番目のスニペットが関数の定義だとは思いません。関数定義にはとがdefありendます。:user2番目のスニペットは、の引数とパラメーターを受け取るブロックを使用して呼び出される関数またはメソッドのように見えると思いfます。

もちろん、メタプログラミングでは、が起こっているのかを実際に確認することはできません。

于 2011-02-17T04:37:51.520 に答える