-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
definition_proxy.rb
174 lines (156 loc) · 5.12 KB
/
definition_proxy.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
module FactoryGirl
class DefinitionProxy
UNPROXIED_METHODS = %w(__send__ __id__ nil? send object_id extend instance_eval initialize block_given? raise caller method)
(instance_methods + private_instance_methods).each do |method|
undef_method(method) unless UNPROXIED_METHODS.include?(method.to_s)
end
delegate :before, :after, :callback, to: :@definition
attr_reader :child_factories
def initialize(definition, ignore = false)
@definition = definition
@ignore = ignore
@child_factories = []
end
def singleton_method_added(name)
message = "Defining methods in blocks (trait or factory) is not supported (#{name})"
raise FactoryGirl::MethodDefinitionError, message
end
# Adds an attribute that should be assigned on generated instances for this
# factory.
#
# This method should be called with either a value or block, but not both. If
# called with a block, the attribute will be generated "lazily," whenever an
# instance is generated. Lazy attribute blocks will not be called if that
# attribute is overridden for a specific instance.
#
# When defining lazy attributes, an instance of FactoryGirl::Strategy will
# be yielded, allowing associations to be built using the correct build
# strategy.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of this attribute. This will be assigned using "name=" for
# generated instances.
# * value: +Object+
# If no block is given, this value will be used for this attribute.
def add_attribute(name, value = nil, &block)
raise AttributeDefinitionError, 'Both value and block given' if value && block_given?
declaration = if block_given?
Declaration::Dynamic.new(name, @ignore, block)
else
Declaration::Static.new(name, value, @ignore)
end
@definition.declare_attribute(declaration)
end
def ignore(&block)
ActiveSupport::Deprecation.warn "`#ignore` is deprecated and will be "\
"removed in 5.0. Please use `#transient` instead."
transient &block
end
def transient(&block)
proxy = DefinitionProxy.new(@definition, true)
proxy.instance_eval(&block)
end
# Calls add_attribute using the missing method name as the name of the
# attribute, so that:
#
# factory :user do
# name 'Billy Idol'
# end
#
# and:
#
# factory :user do
# add_attribute :name, 'Billy Idol'
# end
#
# are equivalent.
#
# If no argument or block is given, factory_girl will look for a sequence
# or association with the same name. This means that:
#
# factory :user do
# email { create(:email) }
# association :account
# end
#
# and:
#
# factory :user do
# email
# account
# end
#
# are equivalent.
def method_missing(name, *args, &block)
if args.empty? && block.nil?
@definition.declare_attribute(Declaration::Implicit.new(name, @definition, @ignore))
elsif args.first.respond_to?(:has_key?) && args.first.has_key?(:factory)
association(name, *args)
else
add_attribute(name, *args, &block)
end
end
# Adds an attribute that will have unique values generated by a sequence with
# a specified format.
#
# The result of:
# factory :user do
# sequence(:email) { |n| "person#{n}@example.com" }
# end
#
# Is equal to:
# sequence(:email) { |n| "person#{n}@example.com" }
#
# factory :user do
# email { FactoryGirl.create(:email) }
# end
#
# Except that no globally available sequence will be defined.
def sequence(name, *args, &block)
sequence = Sequence.new(name, *args, &block)
add_attribute(name) { increment_sequence(sequence) }
end
# Adds an attribute that builds an association. The associated instance will
# be built using the same build strategy as the parent instance.
#
# Example:
# factory :user do
# name 'Joey'
# end
#
# factory :post do
# association :author, factory: :user
# end
#
# Arguments:
# * name: +Symbol+
# The name of this attribute.
# * options: +Hash+
#
# Options:
# * factory: +Symbol+ or +String+
# The name of the factory to use when building the associated instance.
# If no name is given, the name of the attribute is assumed to be the
# name of the factory. For example, a "user" association will by
# default use the "user" factory.
def association(name, *options)
@definition.declare_attribute(Declaration::Association.new(name, *options))
end
def to_create(&block)
@definition.to_create(&block)
end
def skip_create
@definition.skip_create
end
def factory(name, options = {}, &block)
@child_factories << [name, options, block]
end
def trait(name, &block)
@definition.define_trait(Trait.new(name, &block))
end
def initialize_with(&block)
@definition.define_constructor(&block)
end
end
end