ardm-sweatshop 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0cbc2ccff67e6065da63c6d4169132747c519e61
4
+ data.tar.gz: b0e512069137acea48d45c7162286cdbb39559eb
5
+ SHA512:
6
+ metadata.gz: 38670fa7331b6f1704b3bba60f05938aba6a30fbb1573dd62d6307551e99fadec6176b1a11f7e16e63ab8fbca8b7214418bb6af30714f0330d9d9824e9cd5bc8
7
+ data.tar.gz: 1edddb1819873d2b01186496d374d6c89bdc6f105e8f9dc5e6d78e9ee970da46dd612b3c661e8506b10fb37a72dff249ce699d5e34351feb2494c8ba9d290362
@@ -0,0 +1,35 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## Rubinius
17
+ *.rbc
18
+
19
+ ## PROJECT::GENERAL
20
+ *.gem
21
+ coverage
22
+ rdoc
23
+ pkg
24
+ tmp
25
+ doc
26
+ log
27
+ .yardoc
28
+ measurements
29
+
30
+ ## BUNDLER
31
+ .bundle
32
+ Gemfile.*
33
+
34
+ ## PROJECT::SPECIFIC
35
+ spec/db/
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.5
7
+ - 2.2.0
8
+ matrix:
9
+ allow_failures:
10
+ - rvm: 2.1.5
11
+ - rvm: 2.2.0
data/Gemfile ADDED
@@ -0,0 +1,52 @@
1
+ require 'pathname'
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ SOURCE = ENV.fetch('SOURCE', :git).to_sym
8
+ REPO_POSTFIX = SOURCE == :path ? '' : '.git'
9
+ DATAMAPPER = SOURCE == :path ? Pathname(__FILE__).dirname.parent : 'http://github.com/ar-dm'
10
+ DM_VERSION = '~> 1.2'
11
+ DO_VERSION = '~> 0.10.6'
12
+ DM_DO_ADAPTERS = %w[ sqlite postgres mysql oracle sqlserver ]
13
+ CURRENT_BRANCH = ENV.fetch('GIT_BRANCH', 'master')
14
+
15
+ gem 'ardm-core', DM_VERSION, SOURCE => "#{DATAMAPPER}/ardm-core#{REPO_POSTFIX}", :branch => CURRENT_BRANCH
16
+ gem 'ParseTree', '~> 3.0.7', :platforms => :mri_18
17
+
18
+ group :development do
19
+ gem 'ardm-validations', DM_VERSION, SOURCE => "#{DATAMAPPER}/ardm-validations#{REPO_POSTFIX}", :branch => CURRENT_BRANCH
20
+ end
21
+
22
+ group :datamapper do
23
+
24
+ adapters = ENV['ADAPTER'] || ENV['ADAPTERS']
25
+ adapters = adapters.to_s.tr(',', ' ').split.uniq - %w[ in_memory ]
26
+
27
+ if (do_adapters = DM_DO_ADAPTERS & adapters).any?
28
+ do_options = {}
29
+ do_options[:git] = "#{DATAMAPPER}/do#{REPO_POSTFIX}" if ENV['DO_GIT'] == 'true'
30
+
31
+ gem 'data_objects', DO_VERSION, do_options.dup
32
+
33
+ do_adapters.each do |adapter|
34
+ adapter = 'sqlite3' if adapter == 'sqlite'
35
+ gem "do_#{adapter}", DO_VERSION, do_options.dup
36
+ end
37
+
38
+ gem 'ardm-do-adapter', DM_VERSION, SOURCE => "#{DATAMAPPER}/ardm-do-adapter#{REPO_POSTFIX}", :branch => CURRENT_BRANCH
39
+ end
40
+
41
+ adapters.each do |adapter|
42
+ gem "ardm-#{adapter}-adapter", DM_VERSION, SOURCE => "#{DATAMAPPER}/ardm-#{adapter}-adapter#{REPO_POSTFIX}", :branch => CURRENT_BRANCH
43
+ end
44
+
45
+ plugins = ENV['PLUGINS'] || ENV['PLUGIN']
46
+ plugins = plugins.to_s.tr(',', ' ').split.push('ardm-migrations').uniq
47
+
48
+ plugins.each do |plugin|
49
+ gem plugin, DM_VERSION, SOURCE => "#{DATAMAPPER}/#{plugin}#{REPO_POSTFIX}", :branch => CURRENT_BRANCH
50
+ end
51
+
52
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Ben Burkert
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,259 @@
1
+ = dm-sweatshop
2
+
3
+ == Overview
4
+
5
+ dm-sweatshop is a model factory for DataMapper. It makes it easy & painless to crank out complex pseudo random models -- useful for tests and seed data. Production Goals:
6
+
7
+ * Easy generation of random models with data that fits the application domain.
8
+ * Simple syntax for declaring and generating model patterns.
9
+ * Add context to model patterns, allowing grouping and
10
+ * Effortlessly generate or fill in associations for creating complex models with few lines of code.
11
+
12
+ == How it works
13
+
14
+ DataMapper Sweatshop is built around idea of storing attribute hashes associated
15
+ with a particular class. For instance, you can store two attribute hashes named
16
+ :without_password and :without_email, associated with a Person class. Later, when in the test you need a Person instance without password or email, you
17
+ use DataMapper Sweatship helper methods to pick an object that has attributes
18
+ set you need.
19
+
20
+ So the workflow is usually the following:
21
+
22
+ * Figure out what sets of attributes you need for a good test coverage.
23
+ * Name those sets.
24
+ * Store them associated with a particular class.
25
+ * Use them or objects with those attributes in your tests.
26
+
27
+ But there's more. Two hard parts of working with Ruby code fixtures are associations and generation of test data. Dummy data like "foo" and "bar" not just
28
+ very readable and becomes a mess after a while, it's really annoying to generate
29
+ a few objects that have, for instance, a title of 20+ characters.
30
+
31
+ DataMapper Sweatshop to the rescue. It uses RandExp gem to generate you strings
32
+ from regular expressions. When you need an email that is 60 characters long,
33
+ you can relax and use something like "#{/\w{58}/.gen}@somedomain.info" instead of typing 58 characters long foobar string.
34
+
35
+ Another nice thing is associations. Say we want to have say 20 tags for a
36
+ document or 10 orders for account in tests. DataMapper Sweatshop lets us
37
+ use associations list in attributes hashes described earlier.
38
+
39
+ == Examples
40
+
41
+ Starting off with a simple user model.
42
+
43
+ class User
44
+ include DataMapper::Resource
45
+
46
+ property :id, Serial
47
+ property :username, String
48
+ property :email, String
49
+ property :password, String
50
+ end
51
+
52
+ A fixture for the user model can be defined using the @fixture@ method.
53
+
54
+ User.fixture {{
55
+ :username => (username = /\w+/.gen),
56
+ :email => "#{username}@example.com",
57
+ :password => (password = /\w+/.gen),
58
+ :password_confirmation => password
59
+
60
+ # The /\w+/.gen notation is part of the randexp gem:
61
+ # http://github.com/benburkert/randexp/
62
+ }}
63
+
64
+ Notice the double curly brace (@{{@), a quick little way to pass a block that returns a hash to the fixture method. This is important because it ensures the data is random when we generate a new instance of the model, by calling the block every time.
65
+
66
+ Code snippet above stores a Proc that returns attributes hash in model map for
67
+ class User. Since you did not explicitly specify fixture name, default name is
68
+ used (99+% of the cases it is :default).
69
+
70
+ You can access that attributes hash later in your tests, make objects with those
71
+ attributes, and use and abuse it any way you want. It's just a way to memoize
72
+ attributes set associated with a particular class.
73
+
74
+ And here's how you generate said model.
75
+
76
+ User.generate
77
+
78
+ That's it. In fact, it can even be shortened.
79
+
80
+ User.gen
81
+
82
+ But what if we want to use some name for that attributes set? Just pass an
83
+ argument to @fixture@ method like this:
84
+
85
+ Person.fixture(:valid) {{
86
+ :first_name => %w(Michael Adam Guiseppe)[rand(3)],
87
+ :last_name => %w(Smith Black White)[rand(3)],
88
+ :email => "#{/\w{10}/.gen}@somedomain.info",
89
+ :password_salt => (salt = /\w{20}/.gen),
90
+ :password_hash => Digest::SHA1.hexdigest("#{salt}@--,-`--secret")
91
+ }}
92
+
93
+ Now to a model that has given attributes, use
94
+
95
+ Person.gen(:valid)
96
+
97
+ @generate@ (or @gen@) method uses @create@ method of DataMapper models. This means that validations are run on the model. There are two other methods you can use to create data - @make@ to build a model that has not been saved, and @generate!@ to force saving of the model even if it is invalid (it uses @create!@ internally).
98
+
99
+ Person.make(:valid)
100
+ Person.generate!(:invalid) # You can also use #gen!
101
+
102
+
103
+ === Associations
104
+
105
+ The real power of sweatshop is generating working associations.
106
+
107
+ DataMapper.setup(:default, "sqlite3::memory:")
108
+
109
+ class Tweet
110
+ include DataMapper::Resource
111
+
112
+ property :id, Serial
113
+ property :message, String, :length => 140
114
+ property :user_id, Integer
115
+
116
+ belongs_to :user
117
+ has n, :tags, :through => Resource
118
+ end
119
+
120
+ class Tag
121
+ include DataMapper::Resource
122
+
123
+ property :id, Serial
124
+ property :name, String
125
+
126
+ has n, :tweets, :through => Resource
127
+ end
128
+
129
+ class User
130
+ include DataMapper::Resource
131
+
132
+ property :id, Serial
133
+ property :username, String
134
+
135
+ has n, :tweets
136
+ end
137
+
138
+ DataMapper.auto_migrate!
139
+
140
+ User.fix {{
141
+ :username => /\w+/.gen,
142
+ :tweets => 500.of {Tweet.make}
143
+ }}
144
+
145
+ Tweet.fix {{
146
+ :message => /[:sentence:]/.gen[0..140],
147
+ :tags => (0..10).of {Tag.make}
148
+ }}
149
+
150
+ Tag.fix {{
151
+ :name => /\w+/.gen
152
+ }}
153
+
154
+ # now lets generate 100 users, each with 500 tweets. Also, the tweet's have 0 to 10 tags!
155
+ users = 10.of {User.gen}
156
+
157
+
158
+ That's going to generate alot of tags, way more than you would see in the production app. Let's recycle some already generated tags instead.
159
+
160
+ User.fix {{
161
+ :username => /\w+/.gen,
162
+ :tweets => 500.of {Tweet.make}
163
+ }}
164
+
165
+ Tweet.fix {{
166
+ :message => /[:sentence:]/.gen[0..140],
167
+ :tags => (0..10).of {Tag.pick} #lets pick, not make this time
168
+ }}
169
+
170
+ Tag.fix {{
171
+ :name => /\w+/.gen
172
+ }}
173
+
174
+ 50.times {Tag.gen}
175
+
176
+ users = 10.of {User.gen}
177
+
178
+
179
+ === Contexts
180
+
181
+ You can add multiple fixtures to a mode, dm-sweatshop will randomly pick between the available fixtures when it generates a new model.
182
+
183
+ Tweet.fix {{
184
+ # a @reply for some user
185
+ :message => /\@#{User.pick.name} [:sentence:]/.gen[0..140],
186
+ :tags => (0..10).of {Tag.pick}
187
+ }}
188
+
189
+ To keep track of all of our new fixtures, we can even give them a context.
190
+
191
+ Tweet.fix(:at_reply) {{
192
+ :message => /\@#{User.pick.name} [:sentence:]/.gen[0..140],
193
+ :tags => (0..10).of {Tag.pick}
194
+ }}
195
+
196
+ Tweet.fix(:conversation) {{
197
+ :message => /\@#{(tweet = Tweet.pick(:at_reply)).user.name} [:sentence:]/.gen[0..140],
198
+ :tags => tweet.tags
199
+ }}
200
+
201
+ === Overriding a fixture
202
+
203
+ Sometimes you will want to change one of your fixtures a little bit. You can create a new fixture with a whole new context, but this can be overkill. The other option is to specify attributes in the call to <tt>generate</tt>.
204
+
205
+ User.gen(:username => 'datamapper') #uses 'datamapper' as the user name instead of the randomly generated word
206
+
207
+ This works with contexts too.
208
+
209
+ User.gen(:conversation, :tags => Tag.all) #a very, very broad conversation
210
+
211
+ == Unique values
212
+
213
+ Data for fields with a uniqueness constraint (for example, e-mail addresses) can be generated using the @unique@ method. The simplest usage is to guarantee that random data is unique - wrap your generator in a @unique@ block with no parameters, and the block will be repeatedly executed until it generates a unique value (don't worry, it raises after a few tries).
214
+
215
+ For repeatable data, provide a block with one parameter. An incrementing value will be passed in on each invocation of that block. You can also name a unique block to override the block's identity (yeah that sentence is dense, just see the examples).
216
+
217
+ include DataMapper::Sweatshop::Unique # Use DataMapper::Sweatshop.unique if you don't want to pollute your namespace
218
+
219
+ User.fix {{
220
+ :name => unique { /\w+/.gen }
221
+ :email => unique {|x| "person-#{x}@example.com" }
222
+ }}
223
+
224
+ [User.gen.email, User.gen.email]
225
+ # => ["person-0@example.com", "person-1@example.com"]
226
+
227
+ names = ['bob', 'tom', 'bob']
228
+ Person.fix {{
229
+ :name => (name = names.shift)
230
+ :email => unique(name) {|x| "#{name}-#{x}@example.com" }
231
+ }}
232
+
233
+ [Person.gen.email, Person.gen.email, Person.gen.email]
234
+ # => ["bob-0@example.com", "tom-0@example.com", "bob-1@example.com"]
235
+
236
+ == Best Practices
237
+
238
+ === Specs
239
+
240
+ The suggested way to use <tt>dm-sweatshop</tt> with test specs is to create a <tt>spec/spec_fixtures.rb</tt> file, then declare your fixtures in there. Next, @require@ it in your @spec/spec_helper.rb@ file, after your models have loaded.
241
+
242
+ Merb.start_environment(:testing => true, :adapter => 'runner', :environment => ENV['MERB_ENV'] || 'test')
243
+
244
+ require 'dm-sweatshop'
245
+ require File.join(File.dirname(__FILE__), 'spec_fixtures')
246
+
247
+ Add the <tt>.generate</tt> calls in your <tt>before</tt> setup. Make sure to clear your tables or <tt>auto_migrate</tt> your models after each spec!
248
+
249
+ == Possible Improvements
250
+
251
+ === Enforcing Validations
252
+
253
+ Enforce validations at generation time, before the call to @new@/@create@.
254
+
255
+ === Better Exception Handling
256
+
257
+ === Smarter <tt>pick</tt>
258
+
259
+ Add multiple contexts to pick, or an ability to _fall back_ if one context has no generated models.
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ FileList['tasks/**/*.rake'].each { |task| import task }
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/dm-sweatshop/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = 'ardm-sweatshop'
6
+ gem.version = DataMapper::Sweatshop::VERSION
7
+
8
+ gem.authors = [ 'Martin Emde', 'Ben Burkert' ]
9
+ gem.email = [ 'me@martinemde.com', 'ben [a] benburkert [d] com' ]
10
+ gem.summary = 'Ardm fork of dm-sweatshop'
11
+ gem.description = 'DataMapper plugin for building pseudo random models'
12
+ gem.homepage = "https://github.com/ar-dm/ardm-sweatshop"
13
+ gem.license = 'MIT'
14
+
15
+ gem.files = `git ls-files`.split("\n")
16
+ gem.test_files = `git ls-files -- {spec}/*`.split("\n")
17
+ gem.extra_rdoc_files = %w[LICENSE README.rdoc]
18
+ gem.require_paths = [ "lib" ]
19
+
20
+ gem.add_runtime_dependency 'ardm-core', '~> 1.2'
21
+ gem.add_runtime_dependency 'randexp', '~> 0.1', '>= 0.1.5'
22
+
23
+ gem.add_development_dependency 'rake', '~> 0.9'
24
+ gem.add_development_dependency 'rspec', '~> 1.3'
25
+ end
@@ -0,0 +1 @@
1
+ require 'dm-sweatshop'
@@ -0,0 +1,6 @@
1
+ require 'dm-core'
2
+ require 'randexp'
3
+
4
+ require 'dm-sweatshop/sweatshop'
5
+ require 'dm-sweatshop/model'
6
+ require 'dm-sweatshop/unique'
@@ -0,0 +1,107 @@
1
+ module DataMapper
2
+ # Methods added to this module are available on classes
3
+ # that include DataMapper::Resource.
4
+ #
5
+ # This lets you use Person.pick(:michael) instead of
6
+ # DataMapper::Sweatshop.pick(Person, :michael)
7
+ module Model
8
+ # Adds a fixture to record map.
9
+ # Block is supposed to return a hash of attributes.
10
+ #
11
+ # @param name [Symbol, String] Name of the fixture
12
+ # @param blk [Proc] A proc that returns fixture attributes
13
+ #
14
+ # @return nil
15
+ #
16
+ # @api public
17
+ def fixture(name = default_fauxture_name, &blk)
18
+ Sweatshop.add(self, name, &blk)
19
+ end
20
+
21
+ alias_method :fix, :fixture
22
+
23
+ # Creates an instance from hash of attributes, saves it
24
+ # and adds it to the record map. Attributes given as the
25
+ # second argument are merged into attributes from fixture.
26
+ #
27
+ # If record is valid because of duplicated property value,
28
+ # this method does a retry.
29
+ #
30
+ # @param name [Symbol]
31
+ # @param attributes [Hash]
32
+ #
33
+ # @api public
34
+ #
35
+ # @return [DataMapper::Resource] added instance
36
+ def generate(name = default_fauxture_name, attributes = {})
37
+ name, attributes = default_fauxture_name, name if name.is_a? Hash
38
+ Sweatshop.create(self, name, attributes)
39
+ end
40
+
41
+ alias_method :gen, :generate
42
+
43
+ # Same as generate except that it uses Model#create!. It
44
+ # forces invalid objects to be saved in the repository.
45
+ #
46
+ # @param name [Symbol]
47
+ # @param attributes [Hash]
48
+ #
49
+ # @api public
50
+ #
51
+ # @return [DataMapper::Resource] added instance
52
+ def generate!(name = default_fauxture_name, attributes = {})
53
+ name, attributes = default_fauxture_name, name if name.is_a? Hash
54
+ Sweatshop.create!(self, name, attributes)
55
+ end
56
+
57
+ alias_method :gen!, :generate!
58
+
59
+ # Returns a Hash of attributes from the model map.
60
+ #
61
+ # @param name [Symbol] name of the fauxture to use
62
+ #
63
+ # @return [Hash] existing instance of a model from the model map
64
+ # @raise NoFixtureExist when requested fixture does not exist in the model map
65
+ #
66
+ # @api public
67
+ def generate_attributes(name = default_fauxture_name)
68
+ Sweatshop.attributes(self, name)
69
+ end
70
+
71
+ alias_method :gen_attrs, :generate_attributes
72
+
73
+ # Creates an instance from given hash of attributes
74
+ # and adds it to records map without saving.
75
+ #
76
+ # @param name [Symbol] name of the fauxture to use
77
+ # @param attributes [Hash]
78
+ #
79
+ # @api private
80
+ #
81
+ # @return [DataMapper::Resource] added instance
82
+ def make(name = default_fauxture_name, attributes = {})
83
+ name, attributes = default_fauxture_name, name if name.is_a? Hash
84
+ Sweatshop.make(self, name, attributes)
85
+ end
86
+
87
+ # Returns a pre existing instance of a model from the record map
88
+ #
89
+ # @param name [Symbol] name of the fauxture to pick
90
+ #
91
+ # @return [DataMapper::Resource] existing instance of a model from the record map
92
+ # @raise DataMapper::Sweatshop::NoFixtureExist when requested fixture does not exist in the record map
93
+ #
94
+ # @api public
95
+ def pick(name = default_fauxture_name)
96
+ Sweatshop.pick(self, name)
97
+ end
98
+
99
+ # Default fauxture name. Usually :default.
100
+ #
101
+ # @return [Symbol] default fauxture name
102
+ # @api public
103
+ def default_fauxture_name
104
+ :default
105
+ end
106
+ end
107
+ end