This commit adds FactoryGirl.lint, which iterates over each defined
factory, builds it, and ensures it is valid. If any factories are
invalid, an exception is raised and includes the names of each invalid
factory.
This introduces a set of methods for each build strategy where only two
records are created. Because the *_list methods can create an arbitrary
number (often too high), this introduces *_pair to ensure only two
records are created (and the number 2 doesn't need to be specified in
the call).
Calling `block.call(self)` means any implied scope of block evaluation
goes out the window when an arity of 1 is used. This change uses
`instance_exec`, passing along self as the argument for the block
variable, meaning that the block is still invoked within the context of
the FactoryGirl::Evaluator while allowing the variable to have access to
the evaluator.
Closes#529
This resolves an issue where users have defined methods inside factory
girl blocks. This hasn't worked in quite a while and without raising,
users would be able to define methods and see an odd error (a
NoMethodError for `singleton_method_added=`). Instead, we raise
immediately so that no issues come up later when they actually try to
use the factories.
Closes#513
This allows callbacks (after :build, :create, etc.) to be defined at the
FactoryGirl level; this means that the callback will be invoked for all
factories. This is primarily to maintain consistency and follow the
principle of least surprise.
As usual, callbacks are applied from the lowest component to the
highest, meaning that global callbacks will be run after factory and
trait callbacks are run.
FactoryGirl.define do
after(:build) {|object| puts "Built #{object}" }
factory :user
# ...
end
Closes#481Closes#486
This fixes weird issues where methods invoked within sequences
(like `sprintf`) fail because these methods are being evaluated within
the context of the DefinitionProxy. With this change, invoking `#next`
on a sequence happens from the evaluator so if the scope is provided (it
usually will be), it'll run in the proper context. This means a couple
of oddities are fixed:
1. Developers can now refer to methods on the object instance, just like
in dynamic attributes:
class User
def company
# company lookup
end
end
FactoryGirl.define do
factory :user do
sequence(:job_title) {|n| "{title} #{n} at #{company.name}" }
end
end
2. Invoke methods typically available because the method is available on
Object (e.g. Kernel methods):
FactoryGirl.define do
factory :user do
sequence(:last_4_ssn) {|n| sprintf '%04d', n }
end
end
[#466]
This allows for binding multiple callbacks (after_stub, before_create,
etc.) to a single block. This is useful if you want a block to be called
across all build strategies (since build_stubbed doesn't share any
callbacks with build/create).
Examples:
factory :user do
callback(:after_stub, :before_create) { do_something }
after(:stub, :create) { do_something_else }
before(:create, :custom) { do_a_third_thing }
end
This includes #callbacks, #constructor, and #to_create. The reasoning
behind this is that we were mimicing an inheritance chain via methods;
now, we actually generate classes, which Factory maintains, who inherit
from their parent's hierarchy. We build the hierarchy during compilation
to conditionally define methods based on whether what we're dealing
with from the definition is actually meaningful. The base class
(DefinitionHierarchy) uses the defaults (an empty array for #callbacks
and the global #to_create and #constructor) so once we hit the top
level, if the definition doesn't set any overrides, we have a list of
sensible values.
This allows defining associations with factories using traits by slicing
up the hash passed to association. Passing an array to `:factory` will
use the first item in the array as the factory, with any subsequent
symbols as traits.
Here's an example:
factory :post do
association :author, factory: [:user, :admin], name: 'John Doe'
end
Closes#395
The previous implementation of trait handling within the Definition
didn't account for when implicit traits were used within other traits.
This is useful if you have two different traits, but one depends on
another; for example, a refunded order and a completed order could both
have the attribute `completed_at` set, but refunded would additionally
have `refunded_at` set:
FactoryGirl.define do
factory :order do
trait :completed do
completed_at { 3.days.ago }
end
trait :refunded do
completed
refunded_at { 1.day.ago }
end
end
end
This also tests to make sure that callbacks, custom constructors, and
creation overrides work correctly when implicit traits are used within
other traits.
Fixes#360
By setting:
FactoryGirl.duplicate_attribute_assignment_from_initialize_with =
false
This turns off duplicate assignment of attributes accessed from
initialize_with. This means any attributes accessed from the factory and
assigned in the initialize_with block won't be subsequently set after
the object has been instantiated.
This will be the default functionality in 4.0.
Closes#345
Evaluator has all methods defined dynamically; this includes standard
attributes AND overrides. Because of this, method_missing never actually
gets used to retrieve a value from previously declared attributes or
overrides.
This branch of logic was never accessed
This introduces an EnumeratorAdapter so everything coming in looks like
an Enumerator. It doesn't use the adapter if the #peek method exists on
the object.
This allows for sequences like:
sequence(:cities, %w[Boston Atlanta Detroit Seattle].cycle)
This also makes it easier to cycle through arrays and ranges, since they
can be converted to Enumerators rather easily.
sequence(:month, (1..12).to_enum)
sequence(:month, %w[foo bar baz].to_enum)
This doesn't handle when calling Range#step out of the box, because
Ruby returns an Enumerator but ActiveSupport 3.x returns an array,
meaning #to_enum still needs to be called.
Closes#339, #378