README.md in oaken-0.5.0 vs README.md in oaken-0.7.0
- old
+ new
@@ -1,9 +1,20 @@
# Oaken
-Oaken is an alternative to fixtures and/or factories to manage your development, test and some production data using data scripts.
+Oaken is a new take on development and test data management for your Rails app. It blends the stability and storytelling from Fixtures with the dynamicness of FactoryBot/Fabricator.
+Fixtures are stable & help you build a story of how your app and its object graph exists along with edge cases, but the UX is unfortunately a nightmare.
+To trace N associations, you have to open and read N different files — there's no way to group by scenario.
+
+FactoryBot is spray & pray. You basically say “screw it, just give me the bare minimum I need to run this test”, which slows everything down because there’s no cohesion; and the Factories are always suspect in terms of completeness. Sure, I got the test to pass by wiring these 5 Factories together but did I miss something?
+
+Oaken instead upgrades seeds in `db/seeds.rb`, so that you can put together scenarios & also reuse the development data in tests. That way the data you see in your development browser, is the same data you work with in tests to tie it more together — especially for people who are new to your codebase.
+
+So you get the stability of named keys, a cohesive dataset, and a story like Fixtures. But the dynamics of FactoryBot as well. And unlike FactoryBot, you’re not making tons of one-off records to handle each case.
+
+While Fixtures and FactoryBot both load data & truncate in tests, the end result is you end up writing less data back & forth to the database because you aren’t cobbling stuff together.
+
## Setup
### Starting in development
You can set it up in `db/seeds.rb`, like this:
@@ -14,11 +25,11 @@
end
```
This will look for deeply nested files to load in `db/seeds` and `db/seeds/#{Rails.env}` within the `accounts` and `data` directories.
-Here's what they could look like.
+Here's what they could look like:
```ruby
# db/seeds/accounts/kaspers_donuts.rb
donuts = accounts.create :kaspers_donuts, name: "Kasper's Donuts"
@@ -36,65 +47,77 @@
10.times.map { { user_id: users.create(name: "Customer #{_1}").id, item_id: menu.items.sample.id } }
```
```ruby
# db/seeds/data/plans.rb
-plans.insert :basic, title: "Basic", price_cents: 10_00
+plans.upsert :basic, title: "Basic", price_cents: 10_00
```
Seed files will generally use `create` and/or `insert`. Passing a symbol to name the record is useful when reusing the data in tests.
-Now you can run `bin/rails db:seed` — plus Oaken skips executing a seed file if it knows the file hasn't been changed since the last seeding. Speedy!
+Now you can run `bin/rails db:seed` and `bin/rails db:seed:replant`.
### Interlude: Directory Naming Conventions
Oaken has some chosen directory conventions to help strengthen your understanding of your object graph:
- Have a directory for your top-level model, like `Account`, `Team`, `Organization`, that's why we have `db/seeds/accounts` above.
- `db/seeds/data` for any data tables, like the plans a SaaS app has.
- `db/seeds/tests/cases` for any specific cases that are only used in some tests, like `pagination.rb`.
+### Using default attributes
+
+You can set up default attributes that's applied to created/inserted records at different levels, like this:
+
+```ruby
+Oaken.prepare do
+ # Assign broad global defaults for every type.
+ defaults name: -> { Faker::Name.name }, public_key: -> { SecureRandom.hex }
+
+ # Assign a more specific default on one type, which overrides the global default above.
+ accounts.defaults name: -> { Faker::Business.name }
+end
+```
+
+> [!TIP]
+> `defaults` are particularly well suited for assigning generated data with [Faker](https://github.com/faker-ruby/faker).
+
### Reusing data in tests
-With the setup above, Oaken can reuse the same data in tests like so:
+With the setup above, Oaken can reuse the same data in tests like this:
```ruby
# test/test_helper.rb
class ActiveSupport::TestCase
- include Oaken.seeds
-
- # Override Minitest::Test#run to wrap each test in a transaction.
- def run
- result = nil
- ActiveRecord::Base.transaction(requires_new: true) do
- result = super
- raise ActiveRecord::Rollback
- end
- result
- end
+ include Oaken::TestSetup
end
```
Now tests have access to `accounts.kaspers_donuts` and `users.kasper` etc. that were setup in the data scripts.
+> [!NOTE]
+> For RSpec, you can put this in `spec/rails_helper.rb`:
+> ```ruby
+> require "oaken/rspec_setup"
+> ```
+
You can also load a specific seed, like this:
```ruby
class PaginationTest < ActionDispatch::IntegrationTest
- seed "cases/pagination"
+ setup { seed "cases/pagination" }
end
```
-### Resetting cache
+> [!NOTE]
+> We're recommending having one-off seeds on an individual unit of work to help reinforce test isolation. Having some seed files be isolated also helps:
+>
+> - Reduce amount of junk data generated for unrelated tests
+> - Make it easier to debug a particular test
+> - Reduce test flakiness
+> - Encourage writing seed files for specific edge-case scenarios
-Oaken is still early days, so you may need to reset the cache that skips seed files. Pass `OAKEN_RESET` to clear it:
-
-```sh
-OAKEN_RESET=1 bin/rails db:seed
-OAKEN_RESET=1 bin/rails test
-```
-
### Fixtures Converter
You can convert your Rails fixtures to Oaken's seeds by running:
$ bin/rails generate oaken:convert:fixtures
@@ -111,10 +134,10 @@
$ gem install oaken
## Development
-After checking out the repo, run `bin/setup` to install dependencies. Then, run `cd test/dummy` and `bin/rails test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
+After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rails test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing