workshop-app
workshop-app copied to clipboard
Interactive learning app for dry-rb workshops
dry-rb/rom-rb workshop app
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.
Requirements
- git 1.7.0 or newer
- A recent version of Ruby (2.3.x or 2.4.x)
- PostgreSQL
First steps
Clone this repository:
$ git clone https://github.com/dry-rb/workshop-app
Set up the app:
$ ./bin/setup
Run the app:
$ bundle exec shotgun -p 3000 -o 0.0.0.0 config.ru
Then browse the app at localhost:3000.
Exercises
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.
✏️ Types & validation
Merge the starter code:
$ git merge --no-edit -s recursive -X theirs 1-types-validation
Run the specs:
$ bundle exec rspec
There 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
Types
- [ ] Make a strict string enum type called
ArticleStatus
Structs
- [ ] Make an
Article
struct class with the following atributes-
title
(strict string) -
status
(ArticleStatus
) -
published_at
(optional strict time)
-
Validation
- [ ] Update
Admin::Articles::FormSchema
to validate input to match theArticle
struct attributes - [ ] Add a high-level rule to validate that
published_at
is filled only whenstatus
is set to "published"
After completing these exercises, re-run the specs and ensure they're all passing.
In practice
- 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
Further exploration
Types
- [ ] Type with default
- [ ] Constrained type using predicates
- [ ] Type with custom constructor
Validation
- [ ] 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-completed
✏️ Functional objects & systems
Merge the starter code and run the specs:
$ git merge --no-edit -s recursive -X theirs 2-functional-objects-and-systems
$ bundle exec rspec
There should be failures for examples in this file:
./spec/admin/unit/articles/create_spec.rb
Building a functional object
- [ ] Create a functional operation class for creating an article, in
apps/admin/lib/blog/admin/articles/create.rb
- [ ] Define a
#call
method accepting article params - [ ] Use the
FormSchema
we already created to validate these params - [ ] Create a dummy article repository class (with a
#create
method) atapps/admin/lib/blog/admin/article_repo.rb
- [ ] Inject the article_repo into the
Articles::Create
functional 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
Inspecting the system
- Inspect the
Blog::Admin::Container
system container- [ ] Open the console and inspect its
.keys
- [ ] Resolve an
articles.create
object 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::Create
object directly - [ ] Inspect the container's
.keys
again - [ ] 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
In practice
- [ ] 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-completed
️✏️ Persistence with rom-rb
Merge the starter code:
$ git merge --no-edit -s recursive -X theirs 3-persistence
Migrate the database:
$ bundle exec rake db:migrate
$ RACK_ENV=test bundle exec rake db:migrate
Run the specs:
$ bundle exec rspec
There should be failures for examples in this file:
./spec/admin/unit/article_repo_spec.rb
Getting acquainted
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
Reading data
- [ ] Define an "author" association on articles
- [ ] Specify a
belongs_to :author
association in articles relation - [ ] Update
spec/factories/articles.rb
to include this association
- [ ] Specify a
- [ ] Add
#by_pk
to repo for reading individual records - [ ] Add
#listing
to repo for reading lists of articles- [ ] Order articles by created_at time descending
- [ ] Aggregate articles with their author
Writing data
- [ ] Enable
create
command on repo - [ ] Enable
update
command on repo usingby_pk
restriction - [ ] Test writing/reading/updating article records from the console
Refactoring
- [ ] 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
Further exploration
- [ ] 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-completed
️✏️ Views & routes
Merge the starter code and run the specs:
$ git merge --no-edit -s recursive -X theirs 4-routes-views
$ bundle exec rspec
There should be failures for examples in this file:
./spec/main/unit/views/home_spec.rb
- [ ] Set up the
Blog::Main::Views::Home
view controller:- [ ] Inject an
article_repo
dependency - [ ] Add an
articles
exposure returningarticles_repo.listing
- [ ] Inject an
- [ ] Add the
#listing
method toBlog::Main::ArticleRepo
(return published articles only, ordered bypublished_at
descending) - [ ] Fill in
web/templates/home.html.slim
template 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-completed
️✏️ Next steps
This 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/articles
-
admin/articles/new
-
admin/articles/:id/edit
-
- Add user authentication to the admin area