ardm-sweatshop 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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