README.textile in dm-sweatshop-0.9.6 vs README.textile in dm-sweatshop-0.9.7
- old
+ new
@@ -7,10 +7,37 @@
* Easy generation of random models with data that fits the application domain.
* Simple syntax for declaring and generating model patterns.
* Add context to model patterns, allowing grouping and
* Effortlessly generate or fill in associations for creating complex models with few lines of code.
+h2. How it works
+
+DataMapper Sweatshop is built around idea of storing attribute hashes associated
+with a particular class. For instance, you can store two attribute hashes named
+: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
+use DataMapper Sweatship helper methods to pick an object that has attributes
+set you need.
+
+So the workflow is usually the following:
+
+* Figure out what sets of attributes you need for a good test coverage.
+* Name those sets.
+* Store them associated with a particular class.
+* Use them or objects with those attributes in your tests.
+
+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
+very readable and becomes a mess after a while, it's really annoying to generate
+a few objects that have, for instance, a title of 20+ characters.
+
+DataMapper Sweatshop to the rescue. It uses RandExp gem to generate you strings
+from regular expressions. When you need an email that is 60 characters long,
+you can relax and use something like "#{/\w{58}/.gen}@somedomain.info" instead of typing 58 characters long foobar string.
+
+Another nice thing is associations. Say we want to have say 20 tags for a
+document or 10 orders for account in tests. DataMapper Sweatshop lets us
+use associations list in attributes hashes described earlier.
+
h2. Examples
Starting off with a simple user model.
<pre>
<code>
@@ -39,11 +66,19 @@
}}
</code>
</pre>
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.
+
+Code snippet above stores a Proc that returns attributes hash in model map for
+class User. Since you did not explicitly specify fixture name, default name is
+used (99+% of the cases it is :default).
+You can access that attributes hash later in your tests, make objects with those
+attributes, and use and abuse it any way you want. It's just a way to memoize
+attributes set associated with a particular class.
+
And here's how you generate said model.
<pre>
<code>
User.generate
</code>
@@ -54,10 +89,43 @@
<code>
User.gen
</code>
</pre>
+But what if we want to use some name for that attributes set? Just pass an
+argument to @fixture@ method like this:
+
+<pre>
+<code>
+Person.fixture(:valid) {{
+ :first_name => %w(Michael Adam Guiseppe)[rand(3)],
+ :last_name => %w(Smith Black White)[rand(3)],
+ :email => "#{/\w{10}/.gen}@somedomain.info",
+ :password_salt => (salt = /\w{20}/.gen),
+ :password_hash => Digest::SHA1.hexdigest("#{salt}@--,-`--secret")
+}}
+</code>
+</pre>
+
+Now to a model that has given attributes, use
+
+<pre>
+<code>
+Person.gen(:valid)
+</code>
+</pre>
+
+@generate@ (or @gen@) method uses @create@ method of DataMapper models. What if
+we want just a new record? Use @make@ method instead:
+
+<pre>
+<code>
+Person.make(:valid)
+</code>
+</pre>
+
+
h3. Associations
The real power of sweatshop is generating working associations.
<pre>
<code>
@@ -111,11 +179,11 @@
# now lets generate 100 users, each with 500 tweets. Also, the tweet's have 0 to 10 tags!
users = 10.of {User.gen}
</code>
</pre>
-That's going to generate alot of tags, way more than you would see in the production app. Let's recylce some already generated tags instead.
+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.
<pre>
<code>
User.fix {{
:username => /\w+/.gen,
@@ -142,11 +210,12 @@
You can add multiple fixtures to a mode, dm-sweatshop will randomly pick between the available fixtures when it generates a new model.
<pre>
<code>
Tweet.fix {{
- :message => /\@#{User.pick.name} [:sentence:]/.gen[0..140], #an @reply for some user
+ # a @reply for some user
+ :message => /\@#{User.pick.name} [:sentence:]/.gen[0..140],
:tags => (0..10).of {Tag.pick}
}}
</code>
</pre>
@@ -166,11 +235,11 @@
</code>
</pre>
h3. Overriding a fixture
-Sometimes you will want to change one of your fixtures a little bit. You 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 @generate@.
+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 @generate@.
<pre>
<code>
User.gen(:username => 'datamapper') #uses 'datamapper' as the user name instead of the randomly generated word
</code>
@@ -182,12 +251,35 @@
<code>
User.gen(:conversation, :tags => Tag.all) #a very, very broad conversation
</code>
</pre>
-Go forth, and populate your data.
+h2. Unique values
+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).
+
+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).
+
+<pre><code>include DataMapper::Sweatshop::Unique # Use DataMapper::Sweatshop.unique if you don't want to pollute your namespace
+
+User.fix {{
+ :name => unique { /\w+/.gen }
+ :email => unique {|x| "person-#{x}@example.com" }
+}}
+
+[User.gen.email, User.gen.email]
+# => ["person-0@example.com", "person-1@example.com"]
+
+names = ['bob', 'tom', 'bob']
+Person.fix {{
+ :name => (name = names.shift)
+ :email => unique(name) {|x| "#{name}-#{x}@example.com" }
+}}
+
+[Person.gen.email, Person.gen.email, Person.gen.email]
+# => ["bob-0@example.com", "tom-0@example.com", "bob-1@example.com"]</code></pre>
+
h2. Best Practices
h3. Specs
The suggested way to use dm-sweatshop with test specs is to create a @spec/spec_fixtures.rb@ file, then declare your fixtures in there. Next, @require@ it in your @spec/spec_helper.rb@ file, after your models have loaded.
@@ -206,19 +298,10 @@
h3. Enforcing Validations
Enforce validations at generation time, before the call to @new@/@create@.
-<pre>
-<code>
- User.fix {{
- :username.unique => /\w+/.gen,
- :tweets => 500.of {Tweet.make}
- }}
-</code>
-</pre>
-
h3. Better Exception Handling
h3. Smarter @pick@
-Add multiple contexts to pick, or an ability to _fall back_ if one context has no generated models.
\ No newline at end of file
+Add multiple contexts to pick, or an ability to _fall back_ if one context has no generated models.