diff --git a/content/v2.2/introduction/building-a-web-app.md b/content/v2.2/introduction/building-a-web-app.md index 28f73359..899b95f5 100644 --- a/content/v2.2/introduction/building-a-web-app.md +++ b/content/v2.2/introduction/building-a-web-app.md @@ -178,7 +178,7 @@ We can find this view's template in our `app` directory at `app/templates/home/s

Welcome to Bookshelf

``` -At this point we need to compile our frontend assets just once, so they're available to the layout in `app/views/layouts/app.html.erb`: +At this point we need to compile our frontend assets (a favicon, some very simple CSS, and placeholder JS) just once, so they're available to the layout in `app/views/layouts/app.html.erb`: ```shell $ bundle exec hanami assets compile @@ -365,7 +365,7 @@ $ bundle exec hanami generate relation books This creates the following file at `app/relations/books.rb`: ```ruby -# lib/bookshelf/persistence/relations/books.rb +# lib/bookshelf/relations/books.rb module Bookshelf module Relations @@ -376,10 +376,13 @@ module Bookshelf end ``` -Lastly, we need to ensure the database is cleaned between tests. Add the Database Cleaner gem to your `Gemfile`: +This tells the relation to use the `'books'` table we just created, and use that table's schema as its own (instead of manually specifying it). + +Lastly, we need to ensure the database is cleaned between tests. Add the Database Cleaner gem to the `:test` section of your `Gemfile`: ```ruby group :test do + # ... gem "database_cleaner-sequel" end ``` @@ -409,7 +412,7 @@ RSpec.configure do |config| DatabaseCleaner.clean_with(:truncation) end - config.around(:each, type: :db) do |example| + config.around(:each, type: :feature) do |example| DatabaseCleaner.cleaning do example.run end @@ -469,7 +472,9 @@ module Bookshelf end ``` -To access this book repo from the view, we can use Hanami's Deps mixin. Covered in detail in the [container and components](/v2.2/app/container-and-components/) section of the Architecture guide, the Deps mixin gives each of your app's components easy access to the other components it depends on to achieve its work. We'll see this in more detail as these guides progress. +This selects all the attributes from the `books` relation (by default), `order`s them alphabetically by title, and then, finally _materializes_ the query with `#to_a`. This executes the query against the database and then automatically turns each row into a simple object (of class `ROM::Struct`) and puts those into an array. + +To access this book repo from the view, we use Hanami's `Deps` mixin, which is how Hanami files declare their dependencies on other files. Covered in detail in the [container and components](/v2.2/app/container-and-components/) section of the Architecture guide, the `Deps` mixin gives each of your app's components easy access to the other components it depends on to achieve its work. We'll see this in more detail as these guides progress. For now however, it's enough to know that we can use `include Deps["repos.book_repo"]` to make the repo available via a `book_repo` method within our view. @@ -502,7 +507,7 @@ Then we can update our template to include the author: ``` @@ -696,9 +701,11 @@ Failures: We can use Hanami's action generator to create both a route and an action. Run: ```shell -$ bundle exec hanami generate action books.show +$ bundle exec hanami generate action books.show --skip-tests ``` +(We skip creating a spec file for this action because we have the feature spec already. We recommend writing them for your real apps.) + If you inspect `config/routes.rb` you will see the generator has automatically added a new `get "/books/:id", to: "books.show"` route: ```ruby @@ -739,6 +746,8 @@ To fetch a single book from our database, we can add a new method to our book re module Bookshelf module Repos class BookRepo < Bookshelf::Repo + # ... + def get(id) books.by_pk(id).one end @@ -747,6 +756,8 @@ module Bookshelf end ``` +Now, we're calling the `#by_pk` (primary key) method on the books relation, then the `#one` method is the one that _materializes_ the query by executing it and turning into an object. + Then we can edit the view at `app/views/books/show.rb` to get the book via the repo and expose it to the template: ```ruby @@ -772,9 +783,9 @@ Lastly, we can populate the template. ```sql -

<%= book[:title] %>

+

<%= book.title %>

-

By <%= book[:author] %>

+

By <%= book.author %>

``` With this, our happy path test passes, but the test for our 404 now fails: @@ -1104,14 +1115,16 @@ To complete our create action, we can add a method to our book repo to create ne module Bookshelf module Repos class BookRepo < Bookshelf::Repo - def create(attributes) - books.changeset(:create, attributes).commit - end + commands :create + + # ... end end end ``` +Commands are built-in features for repos that you can opt-into. In this case, it defines a `def create(attributes)` method that we can call. + In the action, we can then create this book if the posted params are valid, then setting flash messages and redirecting as required: ```ruby diff --git a/content/v2.2/introduction/getting-started.md b/content/v2.2/introduction/getting-started.md index 4a30bbad..9bea82c0 100644 --- a/content/v2.2/introduction/getting-started.md +++ b/content/v2.2/introduction/getting-started.md @@ -31,7 +31,7 @@ Ideally, you already have some familiarity with web apps and the [Ruby language] ### Prerequisites -To create a Hanami app, you will need Ruby 3.0 or greater. Check your ruby version: +To create a Hanami app, you will need Ruby 3.1 or greater. Check your ruby version: ```shell $ ruby --version @@ -104,6 +104,7 @@ $ tree --gitignore . │   ├── routes.rb │   └── settings.rb ├── config.ru +├── db ├── lib │   ├── bookshelf │   │   └── types.rb @@ -116,27 +117,29 @@ $ tree --gitignore . ├── spec_helper.rb └── support ├── features.rb + ├── operations.rb ├── requests.rb └── rspec.rb -23 directories, 32 files +25 directories, 33 files ``` Here's how these files and directories are used: -| Location | Purpose | -|---------------------------------|--------------------------------------------| -| Gemfile | The app's gem dependencies, installed using bundler. | -| Guardfile | Supports code reloading in development. | -| Procfile.dev | For running Hanami dev server processes: both the server and assets watcher. | -| package.json | The app's Node.js package dependencies, for assets management, installed using npm. | -| README.md | The app's README document. | -| Rakefile | Support for running Rake tasks. | -| app/ | This is the directory where you'll put the majority of your app's code. | -| config/ | A directory for your app and assets configuration, also including things like routes, settings and Puma configuration. | -| config.ru | The Rack config file. | -| lib/ | A directory for supporting code. | -| spec/ | The app's RSpec test suite. | +| Location | Purpose | +|---------------|----------| +| Gemfile | The app's gem dependencies, installed using bundler. | +| Guardfile | Supports code reloading in development. | +| Procfile.dev | For running Hanami dev server processes: both the server and assets watcher. | +| package.json | The app's Node.js package dependencies, for assets management, installed using npm. | +| README.md | The app's README document. | +| Rakefile | Support for running Rake tasks. | +| app/ | This is the directory where you'll put the majority of your app's code. | +| config/ | A directory for configurations of all kinds. | +| config.ru | The Rack config file. | +| db/ | Where our sqlite database files lives. | +| lib/ | A directory for supporting code. | +| spec/ | The app's RSpec test suite. | We'll see this structure in more detail as this guide progresses. @@ -152,9 +155,9 @@ If all has gone well, you should see output similar to: 08:14:33 web.1 | started with pid 56242 08:14:33 assets.1 | started with pid 56243 08:14:34 assets.1 | [gsg_app] [watch] build finished, watching for changes... -08:14:34 web.1 | 08:14:34 - INFO - Using Guardfile at /Users/tim/Source/scratch/gsg_app/Guardfile. +08:14:34 web.1 | 08:14:34 - INFO - Using Guardfile at ~/bookshelf/Guardfile. 08:14:34 web.1 | 08:14:34 - INFO - Puma starting on port 2300 in development environment. -08:14:34 web.1 | 08:14:34 - INFO - Guard is now watching at '/Users/tim/Source/scratch/gsg_app' +08:14:34 web.1 | 08:14:34 - INFO - Guard is now watching at '~/bookshelf' 08:14:35 web.1 | Puma starting in single mode... 08:14:35 web.1 | * Puma version: 6.4.2 (ruby 3.3.0-p0) ("The Eagle of Durango") 08:14:35 web.1 | * Min threads: 5 diff --git a/content/v2.2/introduction/hanami-welcome.png b/content/v2.2/introduction/hanami-welcome.png index 74053ed9..45704259 100644 Binary files a/content/v2.2/introduction/hanami-welcome.png and b/content/v2.2/introduction/hanami-welcome.png differ