iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
💎

Trying out roda-sequel-stack

に公開

https://twitter.com/tmtms/status/1662470086379307016

I wonder what web framework would be good if I want to use Sequel instead of ActiveRecord in Ruby. Maybe Hanami. Well, using Sequel with Rails is also an option.

When I tweeted this, the author himself replied:

https://twitter.com/jeremyevans0/status/1662871626663460864

I recommend Roda+Sequel https://roda.jeremyevans.net

He introduced me to roda-sequel-stack, so I'm going to try it out.

Initial Setup

Copy to local using git clone.

% git clone https://github.com/jeremyevans/roda-sequel-stack.git hoge
% cd hoge

You might want to delete the Git history of roda-sequel-stack itself and create a new repository.

% rm -rf .git
% git init
% git commit --allow-empty -m 'Initial commit'

Set up as the "Hoge" app.

% rake 'setup[Hoge]'
% bundle install

It uses PostgreSQL by default, but I want to use MySQL, so I'll change it.

-gem 'sequel_pg', '>= 1.8', require: 'sequel'
+gem 'ruby-mysql', '>= 4.0', require: 'mysql'

For simplicity, I'll use Docker Compose for MySQL.

docker-compose.yml
services:
  db:
    image: mysql:8.0.33
    ports:
      - "127.0.0.1:13306:3306"
    volumes:
      - db:/var/lib/mysql
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
volumes:
  db:

Create the database and user.

% docker-compose up -d
% docker-compose exec db mysql
mysql> create database hoge_production;
mysql> create database hoge_development;
mysql> create database hoge_test;
mysql> create user hoge;
mysql> grant all on `hoge\_%`.* to hoge;

Configure the database connection information.

.env.rb
...
-  ENV['HOGE_DATABASE_URL'] ||= "postgres:///hoge_test?user=hoge"
+  ENV['HOGE_DATABASE_URL'] ||= "mysql://hoge@127.0.0.1:13306/hoge_test"
...
-  ENV['HOGE_DATABASE_URL'] ||= "postgres:///hoge_production?user=hoge"
+  ENV['HOGE_DATABASE_URL'] ||= "mysql://hoge@127.0.0.1:13306/hoge_production"
...
-  ENV['HOGE_DATABASE_URL'] ||= "postgres:///hoge_development?user=hoge"
+  ENV['HOGE_DATABASE_URL'] ||= "mysql://hoge@127.0.0.1:13306/hoge_development"
...

Install rackup and start the service.

% gem install rackup
% rackup -p 8080

If you access http://localhost:8080 in your browser, the following page will be displayed.

Building a typical sample service

Migration

Create the Article model. Delete the sample migration file.

% rm migrate/001_tables.rb

Create a new migration file. For the syntax of Sequel migration files, refer to Sequel's schema_modification.rdoc.

migrate/20230611001_articles.rb
Sequel.migration do
  change do
    create_table :articles do
      primary_key :id
      String :title
      String :body, size: 1024
    end
  end
end

Run the migration.

% rake dev_up

Model

Create the Article model.

models/article.rb
class Article < Sequel::Model
end

Routing (Controller)

Create the route file. This is like a controller in Rails.
For information on how to write it, see Roda's README.rdoc.

routes/articles.rb
class Hoge
  hash_branch('articles') do |r|
    r.is do
      @articles = Article.all
      view 'index'
    end
  end
end

View

Create the view file. Since it's an erb file, it's mostly the same as a Rails view file.

views/articles/index.erb
<table>
    <tr>
        <th>タイトル</th>
        <th>作成日時</th>
    </tr>
    <% @articles.each do |article| %>
        <tr>
            <td><%= article.title %></td>
            <td><%= article.created_at %></td>
        </tr>
    <% end %>
</table>

Now, if you access http://localhost:8080/articles in your browser, the list of articles should be displayed, but since there is no data yet, nothing will be shown.

IRB (Console)

Launch irb with rake dev_irb and try creating an Article object. It's similar to Rails' rails console.

% rake dev_irb
irb(main):001:0> Article.create(title: 'ほげほげほげほげ', body: "1行目\n2行目", created_at: Time.now)

It was displayed.

CSS

Let's tweak the CSS to add borders to the table.

assets/css/app.scss
th {
    border: solid 1px
}
td {
    border: solid 1px
}

Let's make it so that you can see the body text by clicking on the title.

views/articles/index.erb
-            <td><%= article.title %></td>
+            <td><a href="/articles/<%=article.id%>"><%= article.title %></a></td>

Now the title is a link. However, since it's a bit clunky, let's use the link_to plugin.

Configure the path for the Article object.

app.rb
  plugin :link_to
  path Article do |article|
    "/articles/#{article.id}"
  end

Rewrite the view to call the link_to method.

views/articles/index.erb
-            <td><a href="/articles/<%=article.id%>"><%= article.title %></a></td>
+            <td><%== link_to(article.title, article) %></a></td>

It's become a bit simpler.

To display the body when the link is clicked, modify the route file and create a view file.

routes/articles.rb
class Hoge
  hash_branch('articles') do |r|
    r.is do
      @articles = Article.all
      view 'index'
    end
    r.is Integer do |id|
      @article = Article.with_pk!(id)
      view "show"
    end
  end
end
views/articles/show.erb
<dl>
    <dt>タイトル</dt>
    <dd><%= @article.title %></dd>
    <dt>作成日時</dt>
    <dd><%= @article.created_at %></dd>
    <dt>本文</dt>
    <textarea readonly><%= @article.body %></textarea>
</dl>

New Registration

Earlier, we created an article record from irb, but now let's create a page where you can register articles from the browser.

views/articles/new.erb
<form method="post" action="/articles">
<dl>
    <dt>タイトル</dt>
    <dd><input type="text" name="title"></dd>
    <dt>本文</dt>
    <dd><textarea name="body"></textarea></dd>
</dl>
<input type="submit">
</form>

The route file looks like this:

routes/articles.rb
class Hoge
  hash_branch('articles') do |r|
    r.is do
      @articles = Article.all
      view "index"
    end
    r.is Integer do |id|
      @article = Article.with_pk!(id)
      view "show"
    end
    r.is 'new' do
      view "new"
    end
  end
end

Now, if you access http://localhost:8080/articles/new, the new registration screen will be displayed.

Modify the route file to accept POST requests.

routes/articles.rb
class Hoge
  hash_branch('articles') do |r|
    r.is do
      r.post do
        Article.create(title: r.params['title'], body: r.params['body'], created_at: Time.now)
        r.redirect
      end
      r.get do
        @articles = Article.all
        view "index"
      end
    end
    r.is Integer do |id|
      @article = Article.with_pk!(id)
      view "show"
    end
    r.is 'new' do
      view "new"
    end
  end
end

However, when you actually try to POST, you'll get an error saying "Invalid Security Token".

Add csrf_tag to the view file.

views/articles/new.erb
<form method="post" action="/articles">
<dl>
    <dt>タイトル</dt>
    <dd><input type="text" name="title"></dd>
    <dt>本文</dt>
    <dd><textarea name="body"></textarea></dd>
</dl>
<%== csrf_tag('/articles') %>
<input type="submit">
</form>

Now you can create new articles.

Conclusion

roda-sequel-stack is not a gem; it's more like a sample for building a full-stack framework using Roda, Sequel, and other libraries, rather than a full-stack framework like Rails.

I've been using Sequel for over 10 years, and Roda seems quite good, so I might even like it more than Rails.

Discussion