mack-data_factory 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +167 -0
- data/lib/mack-data_factory/content_generator.rb +217 -0
- data/lib/mack-data_factory/core_extensions/kernel.rb +55 -0
- data/lib/mack-data_factory/data_factory.rb +124 -0
- data/lib/mack-data_factory/field.rb +89 -0
- data/lib/mack-data_factory/field_manager.rb +28 -0
- data/lib/mack-data_factory/orm_api_bridge/bridge.rb +46 -0
- data/lib/mack-data_factory/orm_api_bridge/orm/active_record.rb +34 -0
- data/lib/mack-data_factory/orm_api_bridge/orm/data_mapper.rb +34 -0
- data/lib/mack-data_factory/orm_api_bridge/orm/default.rb +34 -0
- data/lib/mack-data_factory.rb +19 -0
- metadata +73 -0
data/README
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
Mack-data_factory
|
2
|
+
-----------------------------------------------------------------------------
|
3
|
+
|
4
|
+
== Summary
|
5
|
+
Utility to create faux data.
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
sudo gem install mack-data_factory
|
9
|
+
|
10
|
+
== Using The Gem
|
11
|
+
|
12
|
+
** Constructing Factory
|
13
|
+
For each model that you want to produce, you will need to define a factory class.
|
14
|
+
Let's say that I have 2 models: Item and User, and Item belongs to user. So the factories will look like the following:
|
15
|
+
|
16
|
+
class ItemFactory
|
17
|
+
include Mack::Data::Factory
|
18
|
+
|
19
|
+
field :title, "MyItem"
|
20
|
+
field :owner_id, {:user => 'id'}
|
21
|
+
end
|
22
|
+
|
23
|
+
class UserFactory
|
24
|
+
include Mack::Data::Factory
|
25
|
+
|
26
|
+
field :username, "planters", :length => 25, :content => :alpha
|
27
|
+
field :password, "roastedPeanuts", :immutable => true
|
28
|
+
end
|
29
|
+
|
30
|
+
So, the 2 classes above defined the factory for item and user. As you can see, each factory will need to explicitly
|
31
|
+
list all the fields that it will populate, and for each field, you can define rules on how the content is generated.
|
32
|
+
Important: For each field defined in the factory, you will need to make sure that there's a corresponding setter
|
33
|
+
method in the model class (e.g. Item class is required to have title= and owner_id=).
|
34
|
+
|
35
|
+
For numeric content, you can specify the start number and end number, and the content generator will generate a random number
|
36
|
+
between that range.
|
37
|
+
|
38
|
+
For all fields, you can specify :immutable to true, which means for all instances created, the content for that field
|
39
|
+
will not change.
|
40
|
+
|
41
|
+
|
42
|
+
Supported content types:
|
43
|
+
# Strings and Numbers
|
44
|
+
- :alpha --> alphabets. rules: [:length, :min_length, :max_length]
|
45
|
+
- :alphanumeric --> alphabets and number. rules: same as :alpha
|
46
|
+
- :numeric --> numbers [optional, because if the field's default value is number, its content type will automatically set to numeric)
|
47
|
+
# Internet related content
|
48
|
+
- :email --> generate random email address
|
49
|
+
- :username --> generate random username
|
50
|
+
- :domain --> generate random domain name
|
51
|
+
# Name related info
|
52
|
+
- :firstname --> generate first name
|
53
|
+
- :lastname --> generate last name
|
54
|
+
- :name --> generate full name
|
55
|
+
# Address related info
|
56
|
+
- :city --> generate city name
|
57
|
+
- :streetname --> generate street name
|
58
|
+
- :state --> generate state. rules: [:country --> :us or :uk, :abbr --> true if you want a abbreviated state name (us only)]
|
59
|
+
- :zipcode --> generate zipcode. rules: [:country --> :us or :uk]
|
60
|
+
- :phone --> generate phone number
|
61
|
+
# Company info
|
62
|
+
- :company --> generate company name. rules: [:include_bs --> include sales tag line]
|
63
|
+
example: field, "", :content => :company, :include_bs => true
|
64
|
+
could generate something like:
|
65
|
+
Fadel-Larkin
|
66
|
+
monetize cross-media experiences
|
67
|
+
|
68
|
+
** Association
|
69
|
+
class ItemFactory
|
70
|
+
include Mack::Data::Factory
|
71
|
+
|
72
|
+
field :title, "MyItem"
|
73
|
+
field :owner_id, {:user => 'id'}, {:assoc => :first}
|
74
|
+
end
|
75
|
+
|
76
|
+
class UserFactory
|
77
|
+
include Mack::Data::Factory
|
78
|
+
|
79
|
+
field :username, "planters", :length => 25, :content => :alpha
|
80
|
+
field :password, "roastedPeanuts", :immutable => true
|
81
|
+
end
|
82
|
+
|
83
|
+
In the above example, the ItemFactory define the owner_id field's value as {:user => 'id'}. When the factory is run,
|
84
|
+
it will generate value for owner_id as 'the id of the user #x'. You can also pass in rules to define which instance
|
85
|
+
of user that the id will be retrieved from. The rules are: :first, :last, :random, and :spread. The default is :spread.
|
86
|
+
Definition of the association rules:
|
87
|
+
:first --> always associate with the first object that this object belongs to. If there are 10 users [id = 0 - 10], then the item will always get associated with user #0 (i.e. item's owner_id always == 0)
|
88
|
+
:last --> always associate with the last object that this object belongs to. If there are 10 users [id = 0 - 10], then the item will always get associated with user #10 (i.e. item's owner_id always == 9)
|
89
|
+
:random --> always associate with the Nth object that this object belongs to (and N is random). If there are 10 users [id = 0 - 10], then the item will get associated with user #n (i.e. item's owner_id == rand(10))
|
90
|
+
:spread --> spread the association. If there are 3 users [id = 0 - 2], then the items' association will be spread out (i.e. 6 items will have id--sequentially--[0, 1, 2, 0, 1, 2])
|
91
|
+
|
92
|
+
** Pumping Data
|
93
|
+
|
94
|
+
To create objects from the factory is very easy, just call create method on that factory and tell it how many items
|
95
|
+
you want to create.
|
96
|
+
|
97
|
+
Note that if your factory has dependencies to other model (like the ItemFactory in the example), then make sure you
|
98
|
+
have created the model that it's depending on first.
|
99
|
+
|
100
|
+
The create method will return constructed objects. If there's only 1 instance constructed, then it will return that object;
|
101
|
+
if more than one then it will return an array of objects.
|
102
|
+
|
103
|
+
** Creating Factories Execution Chain
|
104
|
+
In some instances, you may want to create an execution chain of a bunch of factories. In the above example: the UserFactory
|
105
|
+
has to be run before the ItemFactory.
|
106
|
+
If that's the case, you can create factory chain that you can execute later.
|
107
|
+
|
108
|
+
factories(:my_cool_factory) do
|
109
|
+
UserFactory.create(1)
|
110
|
+
ItemFactory.create(1000)
|
111
|
+
end
|
112
|
+
|
113
|
+
Once you have defined a named factory chain, you can execute that any time, by calling:
|
114
|
+
|
115
|
+
run_factories(:my_cool_factory)
|
116
|
+
|
117
|
+
** Scoping
|
118
|
+
In other instances, you may want different settings in the factory for different test scope. You can achieve this by doing
|
119
|
+
the following:
|
120
|
+
|
121
|
+
class UserFactory
|
122
|
+
include Mack::Data::Factory
|
123
|
+
|
124
|
+
field :username, "planters", :length => 25, :content => :alpha
|
125
|
+
field :password, "roastedPeanuts", :immutable => true
|
126
|
+
|
127
|
+
scope_for(:long_username) do
|
128
|
+
field :username, "planters", :length => 128, :content => :alpha
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
The above example defined a scoping for "long_username", which you can use by calling:
|
133
|
+
UserFactory.create(100, :long_username)
|
134
|
+
|
135
|
+
When a scope is defined and called, the field defined in the block will overwrite the default field listing
|
136
|
+
in that class. Scopes in the factory is independent to each other, and one scope cannot affect the others.
|
137
|
+
|
138
|
+
** Custom content generator
|
139
|
+
In some cases, the default content generators that are provided by this framework don't fit your needs.
|
140
|
+
To accommodate this situation, you can provide a custom content generator for each field you defined by
|
141
|
+
passing in a proc.
|
142
|
+
|
143
|
+
Example:
|
144
|
+
I'm creating a users, but I don't want the username be totally random, instead I want it to use the default
|
145
|
+
name I provide, and append 0-n at the end of the string to represent the Nth user in the list. Here's how to
|
146
|
+
accomplish that:
|
147
|
+
class UserFactory
|
148
|
+
include Mack::Data::Factory
|
149
|
+
|
150
|
+
field :username, "planters" do |def_value, rules, index|
|
151
|
+
"#{def_value}#{index}"
|
152
|
+
end
|
153
|
+
field :password, "roastedPeanuts", :immutable => true
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
== Contact
|
158
|
+
Please mail bugs, suggestions, and patches to darsono.sutedja@gmail.com
|
159
|
+
|
160
|
+
== Copyright Notices
|
161
|
+
Copyright (c) 2008 Darsono Sutedja
|
162
|
+
|
163
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
164
|
+
|
165
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
166
|
+
|
167
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,217 @@
|
|
1
|
+
module Mack
|
2
|
+
module Data
|
3
|
+
module Factory
|
4
|
+
|
5
|
+
class FieldContentGenerator
|
6
|
+
class << self
|
7
|
+
def alpha_generator
|
8
|
+
@alpha_gen = Proc.new do |def_value, rules, index|
|
9
|
+
words = %w(alias consequatur aut perferendis sit voluptatem accusantium doloremque aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo aspernatur aut odit aut fugit sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt neque dolorem ipsum quia dolor sit amet consectetur adipisci velit sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem ut enim ad minima veniam quis nostrum exercitationem ullam corporis nemo enim ipsam voluptatem quia voluptas sit suscipit laboriosam nisi ut aliquid ex ea commodi consequatur quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae et iusto odio dignissimos ducimus qui blanditiis praesentium laudantium totam rem voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident sed ut perspiciatis unde omnis iste natus error similique sunt in culpa qui officia deserunt mollitia animi id est laborum et dolorum fuga et harum quidem rerum facilis est et expedita distinctio nam libero tempore cum soluta nobis est eligendi optio cumque nihil impedit quo porro quisquam est qui minus id quod maxime placeat facere possimus omnis voluptas assumenda est omnis dolor repellendus temporibus autem quibusdam et aut consequatur vel illum qui dolorem eum fugiat quo voluptas nulla pariatur at vero eos et accusamus officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae itaque earum rerum hic tenetur a sapiente delectus ut aut reiciendis voluptatibus maiores doloribus asperiores repellat)
|
10
|
+
|
11
|
+
length = 128
|
12
|
+
min_length = -1
|
13
|
+
max_length = -1
|
14
|
+
|
15
|
+
if rules[:min_length]
|
16
|
+
min_length = rules[:min_length].to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
if rules[:max_length]
|
20
|
+
max_length = rules[:max_length].to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
if rules[:length]
|
24
|
+
length = rules[:length].to_i
|
25
|
+
length = max_length if (max_length != -1) and (max_length <= length)
|
26
|
+
end
|
27
|
+
|
28
|
+
ret_val = ""
|
29
|
+
words_size = words.size
|
30
|
+
until ret_val.size > length do
|
31
|
+
i = (rand * 100).to_i
|
32
|
+
i = words_size if (i-1) > words_size
|
33
|
+
|
34
|
+
ret_val += words[i]
|
35
|
+
ret_val += " " if rules[:add_space]
|
36
|
+
end
|
37
|
+
|
38
|
+
ret_val.strip!
|
39
|
+
|
40
|
+
ret_val = ret_val[0, length] if ret_val.size > length
|
41
|
+
|
42
|
+
ret_val
|
43
|
+
end
|
44
|
+
return @alpha_gen
|
45
|
+
end
|
46
|
+
|
47
|
+
def alpha_numeric_generator
|
48
|
+
@alpha_numeric_gen = Proc.new do |def_value, rules, index|
|
49
|
+
words = %w(alias consequatur aut perferendis sit voluptatem accusantium doloremque aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo aspernatur aut odit aut fugit sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt neque dolorem ipsum quia dolor sit amet consectetur adipisci velit sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem ut enim ad minima veniam quis nostrum exercitationem ullam corporis nemo enim ipsam voluptatem quia voluptas sit suscipit laboriosam nisi ut aliquid ex ea commodi consequatur quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae et iusto odio dignissimos ducimus qui blanditiis praesentium laudantium totam rem voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident sed ut perspiciatis unde omnis iste natus error similique sunt in culpa qui officia deserunt mollitia animi id est laborum et dolorum fuga et harum quidem rerum facilis est et expedita distinctio nam libero tempore cum soluta nobis est eligendi optio cumque nihil impedit quo porro quisquam est qui minus id quod maxime placeat facere possimus omnis voluptas assumenda est omnis dolor repellendus temporibus autem quibusdam et aut consequatur vel illum qui dolorem eum fugiat quo voluptas nulla pariatur at vero eos et accusamus officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae itaque earum rerum hic tenetur a sapiente delectus ut aut reiciendis voluptatibus maiores doloribus asperiores repellat)
|
50
|
+
|
51
|
+
length = 128
|
52
|
+
min_length = -1
|
53
|
+
max_length = -1
|
54
|
+
|
55
|
+
if rules[:min_length]
|
56
|
+
min_length = rules[:min_length].to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
if rules[:max_length]
|
60
|
+
max_length = rules[:max_length].to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
if rules[:length]
|
64
|
+
length = rules[:length].to_i
|
65
|
+
length = max_length if (max_length != -1) and (max_length <= length)
|
66
|
+
end
|
67
|
+
|
68
|
+
ret_val = ""
|
69
|
+
words_size = words.size
|
70
|
+
until ret_val.size > length do
|
71
|
+
i = (rand * 100).to_i
|
72
|
+
i = words_size if (i-1) > words_size
|
73
|
+
|
74
|
+
ret_val += (words[i] + (rand * 1000).to_i.to_s)
|
75
|
+
ret_val += " " if rules[:add_space]
|
76
|
+
end
|
77
|
+
|
78
|
+
ret_val.strip!
|
79
|
+
|
80
|
+
ret_val = ret_val[0, length] if ret_val.size > length
|
81
|
+
|
82
|
+
ret_val
|
83
|
+
end
|
84
|
+
return @alpha_numeric_gen
|
85
|
+
end
|
86
|
+
|
87
|
+
def numeric_generator
|
88
|
+
@numeric_gen = Proc.new do |def_value, rules, index|
|
89
|
+
n_start = rules[:start_num] || 0
|
90
|
+
n_end = rules[:end_num] || 1000
|
91
|
+
|
92
|
+
val = (n_start..n_end).to_a.randomize[0]
|
93
|
+
val
|
94
|
+
end
|
95
|
+
return @numeric_gen
|
96
|
+
end
|
97
|
+
|
98
|
+
def date_generator
|
99
|
+
@date_gen = Proc.new do |def_value, rules, index|
|
100
|
+
Time.now.to_s
|
101
|
+
end
|
102
|
+
return @date_gen
|
103
|
+
end
|
104
|
+
|
105
|
+
def date_time_generator
|
106
|
+
@date_time_gen = Proc.new do |def_value, rules, index|
|
107
|
+
Time.now.to_s
|
108
|
+
end
|
109
|
+
return @date_time_gen
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def email_generator
|
114
|
+
@email_gen = Proc.new do |def_value, rules, index|
|
115
|
+
Faker::Internet.free_email
|
116
|
+
end
|
117
|
+
return @email_gen
|
118
|
+
end
|
119
|
+
|
120
|
+
def username_generator
|
121
|
+
@username_gen = Proc.new do |def_value, rules, index|
|
122
|
+
Faker::Internet.user_name
|
123
|
+
end
|
124
|
+
return @username_gen
|
125
|
+
end
|
126
|
+
|
127
|
+
def domain_generator
|
128
|
+
@domain_gen = Proc.new do |def_value, rules, index|
|
129
|
+
Faker::Internet.domain_name
|
130
|
+
end
|
131
|
+
return @domain_gen
|
132
|
+
end
|
133
|
+
|
134
|
+
def firstname_generator
|
135
|
+
@fn_gen = Proc.new do |def_value, rules, index|
|
136
|
+
Faker::Name.first_name
|
137
|
+
end
|
138
|
+
return @fn_gen
|
139
|
+
end
|
140
|
+
|
141
|
+
def lastname_generator
|
142
|
+
@ln_gen = Proc.new do |def_value, rules, index|
|
143
|
+
Faker::Name.last_name
|
144
|
+
end
|
145
|
+
return @ln_gen
|
146
|
+
end
|
147
|
+
|
148
|
+
def phone_generator
|
149
|
+
@phone_gen = Proc.new do |def_value, rules, index|
|
150
|
+
Faker::PhoneNumber.phone_number
|
151
|
+
end
|
152
|
+
return @phone_gen
|
153
|
+
end
|
154
|
+
|
155
|
+
def company_generator
|
156
|
+
@company_gen = Proc.new do |def_value, rules, index|
|
157
|
+
str = Faker::Company.name
|
158
|
+
|
159
|
+
if rules[:include_bs]
|
160
|
+
str += "\n#{Faker::Company.bs}"
|
161
|
+
end
|
162
|
+
|
163
|
+
str
|
164
|
+
end
|
165
|
+
return @company_gen
|
166
|
+
end
|
167
|
+
|
168
|
+
def name_generator
|
169
|
+
@name_gen = Proc.new do |def_value, rules, index|
|
170
|
+
Faker::Name.name
|
171
|
+
end
|
172
|
+
return @name_gen
|
173
|
+
end
|
174
|
+
|
175
|
+
# Address Generators
|
176
|
+
|
177
|
+
def city_generator
|
178
|
+
@city_gen = Proc.new do |def_value, rules, index|
|
179
|
+
Faker::Address.city
|
180
|
+
end
|
181
|
+
return @city_gen
|
182
|
+
end
|
183
|
+
|
184
|
+
def streetname_generator
|
185
|
+
@sn_gen = Proc.new do |def_value, rules, index|
|
186
|
+
Faker::Address.street_name
|
187
|
+
end
|
188
|
+
return @sn_gen
|
189
|
+
end
|
190
|
+
|
191
|
+
def state_generator
|
192
|
+
@state_gen = Proc.new do |def_value, rules, index|
|
193
|
+
supported_countries = [:us, :uk]
|
194
|
+
us_or_uk = :us
|
195
|
+
us_or_uk = :uk if rules[:country] and rules[:country].to_sym == :uk
|
196
|
+
abbr = (us_or_uk == :us and rules[:abbr]) ? "_abbr" : ""
|
197
|
+
Faker::Address.send("#{us_or_uk.to_s}_state#{abbr}")
|
198
|
+
end
|
199
|
+
return @state_gen
|
200
|
+
end
|
201
|
+
|
202
|
+
def zipcode_generator
|
203
|
+
@zip_gen = Proc.new do |def_value, rules, index|
|
204
|
+
ret_val = Faker::Address.zip_code
|
205
|
+
ret_val = Faker::Address.uk_postcode if rules[:country] and rules[:country].to_sym == :uk
|
206
|
+
ret_val
|
207
|
+
end
|
208
|
+
return @zip_gen
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Mack
|
2
|
+
module Data
|
3
|
+
class FactoryRegistryMap < Mack::Utils::RegistryMap
|
4
|
+
end
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Kernel
|
9
|
+
|
10
|
+
#
|
11
|
+
# Convenient routine to create an execution chain of factories
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
# factories(:foo) do
|
15
|
+
# UserFactory.create(1)
|
16
|
+
# UserFactory.create(2, :diff_firstname)
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# Then to execute the chains, you'll need to call run_factories, and
|
20
|
+
# pass in the name of the chain you want to execute.
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
# run_factories(:foo)
|
24
|
+
#
|
25
|
+
# @tag -- the name of the factory chain
|
26
|
+
# @block -- the proc to be executed later
|
27
|
+
#
|
28
|
+
def factories(tag, &block)
|
29
|
+
raise "factories: block needed" if !block_given?
|
30
|
+
fact_registry.add(tag, block)
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Run defined factory chain
|
35
|
+
#
|
36
|
+
# @see factories
|
37
|
+
# @tag -- the name of the factory chain to be run
|
38
|
+
# @return true if successful, false otherwise
|
39
|
+
def run_factories(tag)
|
40
|
+
runners = fact_registry.registered_items[tag]
|
41
|
+
return false if runners == nil
|
42
|
+
|
43
|
+
runners.each do |r|
|
44
|
+
r.call
|
45
|
+
end
|
46
|
+
|
47
|
+
return true
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def fact_registry
|
52
|
+
Mack::Data::FactoryRegistryMap.instance
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Mack
|
2
|
+
module Data
|
3
|
+
#
|
4
|
+
# Add factory capability to a class.
|
5
|
+
#
|
6
|
+
# A factory is able to define which field it want to generate content for,
|
7
|
+
# define a scope for different situation, and set a custom content generator
|
8
|
+
# for field that doesn't want to use the default content generator.
|
9
|
+
#
|
10
|
+
# For more information and usage, please read README file
|
11
|
+
#
|
12
|
+
# author: Darsono Sutedja
|
13
|
+
# July 2008
|
14
|
+
#
|
15
|
+
module Factory
|
16
|
+
|
17
|
+
# make sure the data factory API is available to the class that includes it
|
18
|
+
def self.included(base)
|
19
|
+
base.extend ClassMethods
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
|
24
|
+
#
|
25
|
+
# Run the factory to produce n number of objects.
|
26
|
+
#
|
27
|
+
# Example:
|
28
|
+
# class CarFactory
|
29
|
+
# include Mack::Data::Factory
|
30
|
+
# field :name, "honda" { |def_value, rules, index| "#{def_value} #{['civic', 'accord', 'pilot'].randomize[0]}"}
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# CarFactory.create(100) #=> will produce 100 cars whose name is "honda xxx" where xxx is a random item from ['civic', 'accord', 'pilot']
|
34
|
+
#
|
35
|
+
# params:
|
36
|
+
# * num - how many objects to produce
|
37
|
+
# * scope - run the factory in a named scope
|
38
|
+
#
|
39
|
+
def create(num, scope = :default)
|
40
|
+
factory_name = self.name.underscore
|
41
|
+
|
42
|
+
# retrieve the model name from the factory class.
|
43
|
+
model_name = factory_name.gsub('_factory', '')
|
44
|
+
|
45
|
+
# if user is running custom scope, then merge the fields
|
46
|
+
# defined for that scope with the default one, before we run the factory
|
47
|
+
scoped_fields = field_manager.scopes[scope]
|
48
|
+
fields = field_manager.scopes[:default].merge(scoped_fields)
|
49
|
+
|
50
|
+
ret_arr = []
|
51
|
+
|
52
|
+
Mack::Data::RegistryMap.reset!
|
53
|
+
num.times do |i|
|
54
|
+
#puts "Creating #{model_name} ##{i+1}"
|
55
|
+
obj = model_name.camelcase.constantize.new
|
56
|
+
|
57
|
+
fields.each_pair do |k, v|
|
58
|
+
field_name = k.to_s
|
59
|
+
field_value = v.get_value(i)
|
60
|
+
assert_method(obj, "#{field_name}=", "#{model_name.camelcase} doesn't have #{field_name}= method!") do
|
61
|
+
obj.send("#{field_name}=", field_value)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
assert_method(obj, "save", "#{model_name.camelcase} doesn't have save method. Data will not be saved!") do
|
66
|
+
obj.save
|
67
|
+
end
|
68
|
+
|
69
|
+
ret_arr << obj
|
70
|
+
end
|
71
|
+
Mack::Data::RegistryMap.reset!
|
72
|
+
|
73
|
+
return ret_arr[0] if ret_arr.size == 1
|
74
|
+
return ret_arr
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Define a field with its default value and rules and an optional content generator
|
79
|
+
# for this factory
|
80
|
+
#
|
81
|
+
def field(model_attrib_sym, default_value, options = {}, &block)
|
82
|
+
field_manager.add(scope, model_attrib_sym, default_value, options, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Define a scope in the factory.
|
87
|
+
# Any field defined in a scope will overwrite its cousin in the default scope.
|
88
|
+
#
|
89
|
+
def scope_for(tag)
|
90
|
+
set_scope(tag)
|
91
|
+
yield
|
92
|
+
set_scope(:default)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def scope
|
98
|
+
@current_scope ||= :default
|
99
|
+
return @current_scope
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_scope(tag)
|
103
|
+
@current_scope = tag
|
104
|
+
end
|
105
|
+
|
106
|
+
def field_manager
|
107
|
+
@fm ||= Mack::Data::FieldMgr.new
|
108
|
+
return @fm
|
109
|
+
end
|
110
|
+
|
111
|
+
def assert_method(obj, meth, message)
|
112
|
+
raise "#{self.name}: assert_method: no block given" if !block_given?
|
113
|
+
if !obj.respond_to?(meth)
|
114
|
+
Mack.logger.warn(message)
|
115
|
+
else
|
116
|
+
yield
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Mack
|
2
|
+
module Data
|
3
|
+
|
4
|
+
class RegistryMap < Mack::Utils::RegistryMap
|
5
|
+
end
|
6
|
+
|
7
|
+
class Field
|
8
|
+
attr_accessor :field_name
|
9
|
+
attr_accessor :field_value
|
10
|
+
attr_accessor :field_value_producer
|
11
|
+
attr_accessor :field_rules
|
12
|
+
|
13
|
+
def initialize(hash = {})
|
14
|
+
#puts "Inititalizing DataFactory's Field object:"
|
15
|
+
|
16
|
+
hash.each_pair do |k, v|
|
17
|
+
#puts "--> Setting #{v} to #{k}"
|
18
|
+
self.send("#{k}=", v)
|
19
|
+
end
|
20
|
+
|
21
|
+
self.field_rules = {
|
22
|
+
:immutable => false,
|
23
|
+
:length => 256,
|
24
|
+
:add_space => true,
|
25
|
+
:content => :alpha_numeric,
|
26
|
+
:null_frequency => nil
|
27
|
+
}.merge!(hash[:field_rules]) if hash[:field_rules] != nil
|
28
|
+
|
29
|
+
# transform the content type based on the given default value
|
30
|
+
if field_value.is_a?(Fixnum) or field_value.is_a?(Integer)
|
31
|
+
field_rules[:content] = :numeric
|
32
|
+
end
|
33
|
+
|
34
|
+
self.field_value_producer = Mack::Data::Factory::FieldContentGenerator.send("#{field_rules[:content]}_generator")
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_value(index = 0)
|
38
|
+
# return the field_value immediately if the rule states that it's immutable
|
39
|
+
return field_value if field_rules[:immutable]
|
40
|
+
|
41
|
+
#
|
42
|
+
# if the value is a hash, then it's a relationship mapping
|
43
|
+
if field_value.is_a?(Hash)
|
44
|
+
owner = field_value.keys[0]
|
45
|
+
key = field_value[owner]
|
46
|
+
begin
|
47
|
+
owner_model = owner.to_s.camelcase.constantize
|
48
|
+
bridge = Mack::Data::Bridge.new
|
49
|
+
|
50
|
+
assoc_rules = field_rules[:assoc] || :spread
|
51
|
+
assoc_rules = :random if !([:first, :last, :random, :spread].include?(assoc_rules))
|
52
|
+
# cache the query once
|
53
|
+
if Mack::Data::RegistryMap.registered_items[self.field_name.to_sym] == nil
|
54
|
+
all_owner_models = bridge.get_all(owner_model)
|
55
|
+
Mack::Data::RegistryMap.add(self.field_name.to_sym, all_owner_models)
|
56
|
+
end
|
57
|
+
|
58
|
+
all_owner_models = Mack::Data::RegistryMap.registered_items[self.field_name.to_sym][0]
|
59
|
+
|
60
|
+
case assoc_rules
|
61
|
+
when :first
|
62
|
+
value = all_owner_models[0].send(key)
|
63
|
+
when :last
|
64
|
+
value = all_owner_models[all_owner_models.size - 1].send(key)
|
65
|
+
when :random
|
66
|
+
my_index = rand((all_owner_models.size - 1))
|
67
|
+
value = all_owner_models[my_index].send(key)
|
68
|
+
when :spread
|
69
|
+
# if index >= all_owner_models.size
|
70
|
+
# my_index = rand((all_owner_models.size - 1))
|
71
|
+
# else
|
72
|
+
# my_index = index
|
73
|
+
# end
|
74
|
+
my_index = index % all_owner_models.size
|
75
|
+
value = all_owner_models[my_index].send(key)
|
76
|
+
end
|
77
|
+
|
78
|
+
return value
|
79
|
+
rescue Exception
|
80
|
+
Mack.logger.warn "WARNING: DataFactory: field_value for #{field_name} is not set properly because data relationship defined is not correct"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# must generate random string and also respect the rules
|
85
|
+
field_value_producer.call(field_value, field_rules, index)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Mack
|
2
|
+
module Data
|
3
|
+
class FieldMgr
|
4
|
+
attr_reader :scopes
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@scopes = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(scope, field_name, default_value, options = {}, &block)
|
11
|
+
field_list = fields(scope)
|
12
|
+
field_list[field_name] = Field.new(:field_name => field_name,
|
13
|
+
:field_value => default_value,
|
14
|
+
:field_rules => options)
|
15
|
+
field_list[field_name].field_value_producer = block if block_given?
|
16
|
+
return field_list
|
17
|
+
end
|
18
|
+
|
19
|
+
def fields(scope = :default)
|
20
|
+
if @scopes[scope].nil?
|
21
|
+
@scopes[scope] = {}
|
22
|
+
end
|
23
|
+
return @scopes[scope]
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Mack
|
2
|
+
module Data
|
3
|
+
|
4
|
+
class OrmRegistry < Mack::Utils::RegistryList
|
5
|
+
end
|
6
|
+
|
7
|
+
class Bridge
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
OrmRegistry.add(Mack::Data::OrmBridge::ActiveRecord.new)
|
11
|
+
OrmRegistry.add(Mack::Data::OrmBridge::DataMapper.new)
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(obj, *args)
|
15
|
+
handler(obj).get(obj, *args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_all(obj, *args)
|
19
|
+
handler(obj).get_all(obj, *args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_first(obj, *args)
|
23
|
+
handler(obj).get_first(obj, *args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def count(obj, *args)
|
27
|
+
handler(obj).count(obj, *args)
|
28
|
+
end
|
29
|
+
|
30
|
+
def save(obj, *args)
|
31
|
+
handler(obj).save(obj, *args)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def handler(obj)
|
36
|
+
OrmRegistry.registered_items.each do |i|
|
37
|
+
if i.can_handle(obj)
|
38
|
+
return i
|
39
|
+
end
|
40
|
+
end
|
41
|
+
return Mack::Data::OrmBridge::Default.new
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Mack
|
2
|
+
module Data
|
3
|
+
module OrmBridge
|
4
|
+
class ActiveRecord
|
5
|
+
|
6
|
+
def can_handle(obj)
|
7
|
+
return false if !Object.const_defined?('ActiveRecord')
|
8
|
+
return obj.ancestors.include?(::ActiveRecord::Base)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(obj, *args)
|
12
|
+
obj.find(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_all(obj, *args)
|
16
|
+
obj.find(:all, *args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def count(obj, *args)
|
20
|
+
obj.count(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def save(obj, *args)
|
24
|
+
obj.save
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_first(obj, *args)
|
28
|
+
obj.find(:first, *args)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Mack
|
2
|
+
module Data
|
3
|
+
module OrmBridge
|
4
|
+
class DataMapper
|
5
|
+
|
6
|
+
def can_handle(obj)
|
7
|
+
return false if !Object.const_defined?('DataMapper')
|
8
|
+
return obj.ancestors.include?(::DataMapper::Resource)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(obj, *args)
|
12
|
+
obj.get(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_all(obj, *args)
|
16
|
+
obj.all(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def count(obj, *args)
|
20
|
+
obj.count(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def save(obj, *args)
|
24
|
+
obj.save
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_first(obj, *args)
|
28
|
+
obj.first(*args)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Mack
|
2
|
+
module Data
|
3
|
+
module OrmBridge
|
4
|
+
class Default
|
5
|
+
|
6
|
+
def can_handle(obj)
|
7
|
+
return true
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(obj, *args)
|
11
|
+
Mack.logger.warn "Mack::Data::OrmBridge: You don't have supported orm api handler installed."
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_all(obj, *args)
|
15
|
+
Mack.logger.warn "Mack::Data::OrmBridge: You don't have supported orm api handler installed."
|
16
|
+
end
|
17
|
+
|
18
|
+
def count(obj, *args)
|
19
|
+
Mack.logger.warn "Mack::Data::OrmBridge: You don't have supported orm api handler installed."
|
20
|
+
end
|
21
|
+
|
22
|
+
def save(obj, *args)
|
23
|
+
Mack.logger.warn "Mack::Data::OrmBridge: You don't have supported orm api handler installed."
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_first(obj, *args)
|
27
|
+
Mack.logger.warn "Mack::Data::OrmBridge: You don't have supported orm api handler installed."
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
fl = File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'faker'
|
4
|
+
|
5
|
+
[:data_factory, :field, :field_manager, :content_generator].each do |f|
|
6
|
+
require File.join(fl, 'mack-data_factory', "#{f}.rb")
|
7
|
+
end
|
8
|
+
|
9
|
+
[:kernel].each do |f|
|
10
|
+
require File.join(fl, 'mack-data_factory', 'core_extensions', "#{f}.rb")
|
11
|
+
end
|
12
|
+
|
13
|
+
[:bridge].each do |f|
|
14
|
+
require File.join(fl, 'mack-data_factory', 'orm_api_bridge', "#{f}.rb")
|
15
|
+
end
|
16
|
+
|
17
|
+
[:active_record, :data_mapper, :default].each do |f|
|
18
|
+
require File.join(fl, 'mack-data_factory', 'orm_api_bridge', 'orm', "#{f}.rb")
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mack-data_factory
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Darsono Sutedja
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-08-04 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: faker
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - "="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.3.1
|
24
|
+
version:
|
25
|
+
description: Data factory for Mack Testing Framework
|
26
|
+
email: darsono.sutedja@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- lib/mack-data_factory/content_generator.rb
|
35
|
+
- lib/mack-data_factory/core_extensions/kernel.rb
|
36
|
+
- lib/mack-data_factory/data_factory.rb
|
37
|
+
- lib/mack-data_factory/field.rb
|
38
|
+
- lib/mack-data_factory/field_manager.rb
|
39
|
+
- lib/mack-data_factory/orm_api_bridge/bridge.rb
|
40
|
+
- lib/mack-data_factory/orm_api_bridge/orm/active_record.rb
|
41
|
+
- lib/mack-data_factory/orm_api_bridge/orm/data_mapper.rb
|
42
|
+
- lib/mack-data_factory/orm_api_bridge/orm/default.rb
|
43
|
+
- lib/mack-data_factory.rb
|
44
|
+
- README
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://www.mackframework.com
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project: magrathea
|
68
|
+
rubygems_version: 1.2.0
|
69
|
+
signing_key:
|
70
|
+
specification_version: 2
|
71
|
+
summary: Data Factory
|
72
|
+
test_files: []
|
73
|
+
|