This is the companion app for 2017's series of dry-rb and rom-rb workshops conducted by Tim Riley.
Follow the instructions and complete the exercises below to get to know dry-rb and rom-rb, and eventually put together a complete, working app.
- git 1.7.0 or newer
- A recent version of Ruby (2.3.x or 2.4.x)
- PostgreSQL
Clone this repository:
$ git clone https://github.com/dry-rb/workshop-appSet up the app:
$ ./bin/setupRun the app:
$ bundle exec shotgun -p 3000 -o 0.0.0.0 config.ru
Then browse the app at localhost:3000.
Each exercise topic focuses on a specific area of the dry-rb/rom-rb stack, and is intended to follow an introduction to the topic as part of the workshop.
Merge the starter code:
$ git merge --no-edit -s recursive -X theirs 1-types-validationRun the specs:
$ bundle exec rspecThere should be failures for examples in the following files:
./spec/unit/types/article_status_spec.rb
./spec/unit/entities/article_spec.rb
./spec/admin/unit/articles/form_schema_spec.rb
- Make a strict string enum type called
ArticleStatus
- Make an
Articlestruct class with the following atributestitle(strict string)status(ArticleStatus)published_at(optional strict time)
- Update
Admin::Articles::FormSchemato validate input to match theArticlestruct attributes - Add a high-level rule to validate that
published_atis filled only whenstatusis set to "published"
After completing these exercises, re-run the specs and ensure they're all passing.
- Think of some examples of where and how these would have helped in your own applications:
- Types
- Typed structs or value objects
- Standalone validation schemas
- Type with default
- Constrained type using predicates
- Type with custom constructor
- Use a custom predicate (with custom error message)
- Write a schema for nested data
- Write a high-level validation block
If you need to catch up, merge the completed work:
$ git merge --no-edit -s recursive -X theirs 1-types-validation-completedMerge the starter code and run the specs:
$ git merge --no-edit -s recursive -X theirs 2-functional-objects-and-systems
$ bundle exec rspecThere should be failures for examples in this file:
./spec/admin/unit/articles/create_spec.rb
- Create a functional operation class for creating an article, in
apps/admin/lib/blog/admin/articles/create.rb - Define a
#callmethod accepting article params - Use the
FormSchemawe already created to validate these params - Create a dummy article repository class (with a
#createmethod) atapps/admin/lib/blog/admin/article_repo.rb - Inject the article_repo into the
Articles::Createfunctional object - When article params are valid, create an article using the repo and return it wrapped in a
Right - When article params are invalid, return the validation result wrapped in a
Left
- Inspect the
Blog::Admin::Containersystem container- Open the console and inspect its
.keys - Resolve an
articles.createobject from the container - Call the object with valid/invalid attributes to inspect its output
- Open the console and inspect its
- Inspect the behavior of a non-finalized container
- Comment out the code that finalizes the container (in
apps/admin/system/boot.rb) - Open the console and inspect the container's
.keys - Count the number of loaded Ruby source files (via
$LOADED_FEATURES.grep(/workshop-app/).count) - Initialize an
Admin::Articles::Createobject directly - Inspect the container's
.keysagain - Count the number of loaded Ruby source files again (via
$LOADED_FEATURES.grep(/workshop-app/).count)
- Comment out the code that finalizes the container (in
- Think of how something you've written before could be modelled as functional objects
- Think of something you've written that would have been better broken up into smaller units of responsibility
If you need to catch up, merge the completed work:
$ git merge --no-edit -s recursive -X theirs 2-functional-objects-and-systems-completedMerge the starter code:
$ git merge --no-edit -s recursive -X theirs 3-persistenceMigrate the database:
$ bundle exec rake db:migrate
$ RACK_ENV=test bundle exec rake db:migrateRun the specs:
$ bundle exec rspecThere should be failures for examples in this file:
./spec/admin/unit/article_repo_spec.rb
Inspect the basic setup:
- Bootable component in
system/boot/persistence.rb - Migrations in
db/migrate - Relations in
lib/persistence/relations - Test factories in
spec/factories - Articles repo at
apps/admin/lib/admin/persistence/articles_repo.rb
- Define an "author" association on articles
- Specify a
belongs_to :authorassociation in articles relation - Update
spec/factories/articles.rbto include this association
- Specify a
- Add
#by_pkto repo for reading individual records - Add
#listingto repo for reading lists of articles- Order articles by created_at time descending
- Aggregate articles with their author
- Enable
createcommand on repo - Enable
updatecommand on repo usingby_pkrestriction - Test writing/reading/updating article records from the console
- Return results as custom structs via a custom struct namespace
- Move lower-level query methods into relation
- Create shared method in repository to ensure all results return
- Return results as wrapped in custom classes via
.as - Investigate using dry-struct to build custom struct classes with strict attribute types
- Build and use a custom changeset to transform data before writing
If you need to catch up, merge the completed work:
$ git merge --no-edit -s recursive -X theirs 3-persistence-completedMerge the starter code and run the specs:
$ git merge --no-edit -s recursive -X theirs 4-routes-views
$ bundle exec rspecThere should be failures for examples in this file:
./spec/main/unit/views/home_spec.rb
- Set up the
Blog::Main::Views::Homeview controller:- Inject an
article_repodependency - Add an
articlesexposure returningarticles_repo.listing
- Inject an
- Add the
#listingmethod toBlog::Main::ArticleRepo(return published articles only, ordered bypublished_atdescending) - Fill in
web/templates/home.html.slimtemplate so it displays each article - Test your work by running the app and viewing it in the browser
If you need to catch up, merge the completed work:
$ git merge --no-edit -s recursive -X theirs 4-routes-views-completedThis is just the beginning of working app. We can do more!
- Add individual article pages to the public area
articles/:id
- Add article management to the admin area:
admin/articlesadmin/articles/newadmin/articles/:id/edit
- Add user authentication to the admin area