2024-03-17 - Ruby on Rails
Today, we're going to be briefly dipping our toes into Ruby on Rails.
Ruby on Rails is a great way to build a website as a solo developer.
Ruby on Rails was initially released in 2004, and is widely used across many big companies.
Today, we're going to demo of the power of Ruby on Rails. Then, we'll start with the basics of Ruby.
This is what we will be using today:
- This demo on Ruby on Rails
- https://www.jdoodle.com/execute-ruby-online
Demo
You don't need to follow along with this. This is to serve as a motivation as to why to learn Ruby/Ruby on Rails.
Installation
You can install Ruby on Rails here: https://guides.rubyonrails.org/install_ruby_on_rails.html
Creating a project
Let's create a new project!
This project is going to be themed around Sam's Rat Tickling certification.
We can run:
To create a new project called "ratcert".
This follows the "Convention over Configuration" part of Rail's doctrine. They know what you'll need, so they add it for you.
In this basic project, you get your Model, View, and Controller (MVC), as well as your mailers (for email), jobs (for background jobs), and Hotwire.
So, now we have a basic project, ready to go!
Let's start modelling!
Our project is going to represent a few basic parts of a website built to manager rat tickling certifications.
Let's create the ability to keep track of rats.
One of the superpowers of Ruby on Rails is its code "scaffolds".
We can run:
To generate a rat that has a string name, and a birthday date.
You can see all the files this created in the output of the command.
Now, we can run:
You should be greeted with something like this:
We can navigate to /rats
to see a list of our rats.
We can click "new rat" to create a rat.
I'll demo some more features of the webui here.
But, let's take a second to see where this comes from.
The scaffold has created some views, which we can interact with:
The scaffold has also created a database migration:
class CreateRats < ActiveRecord::Migration[8.0]
def change
create_table :rats do |t|
t.string :name
t.date :birthday
t.timestamps
end
end
end
This is another power of ruby: to abstract away the database you're using.
Read more about this here: https://guides.rubyonrails.org/active_record_migrations.html#migration-overview
Note that this is bi-directional, so that this can both create and destroy these fields in our database.
We also created a Rat, which just inherits everything from the database:
It's also created a controller in ./app/controllers/rats_controller.rb
that we should look through and explain.
Creating a Tickler!
So, rats can be tickled by one person, and each person may tickle many rats.
We can create a Tickler like this:
This means each tickler has a name, which it uses as its index (read more here), and an email which must be unique.
For more info on associations, read this: https://guides.rubyonrails.org/association_basics.html
Improving our Tickler
At this point, we can just put anything into our form.
That's not great, let's add some validations!
We can add these lines to the model, to ensure any new data is valid:
class Tickler < ApplicationRecord
# validates that the name is present
validates_presence_of :name
# validate that the email exists, and is in the right format
validates_uniqueness_of :email
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
end
(Here's the bit where Zack demonstrates this at http://127.0.0.1:3000/ticklers
)
Creating a relationship
So, let's define a relationship between our rats and rat ticklers.
The documentation at https://guides.rubyonrails.org/association_basics.html#types-of-associations is great, so read that first.
Then, let's create a one-to-many relationship:
(I think IRL this should be a many-to-many relationship, but that's a little complex for right now.)
class Tickler < ApplicationRecord
# validates that the name is present
validates_presence_of :name
# validate that the email exists, and is in the right format
validates_uniqueness_of :email
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
has_many :rats
end
Now let's update the views to show this relationship:
Rats
# app/views/rats/_form.html.erb
<div>
<%= form.label :tickler_id, style: "display: block" %>
<%= form.collection_select :tickler_id, Tickler.all, :id, :name, prompt: true %>
</div>
Ticklers
# app/views/ticklers/show.html.erb
<h2>Rats</h2>
<ul>
<% @tickler.rats.each do |rat| %>
<li><%= link_to "Rat ##{rat.id}", rat %></li>
<% end %>
</ul>
<%= link_to 'New Rat', new_rat_path(tickler_id: @tickler.id) %>
Also, we need to create a field on our Rat that associates it with a Tickler. We can do so here:
And we need to update the controller's expected parameters:
# app/controllers/rats_controller.rb
private
# Use callbacks to share common setup or constraints between actions.
def set_rat
@rat = Rat.find(params.expect(:id))
end
# Only allow a list of trusted parameters through.
def rat_params
params.expect(rat: [ :name, :birthday, :tickler_id ])
end
More Power: Like LLMs?
I found this recently, and thought it highlighted the beauty of both Ruby and Rails:
https://github.com/crmne/ruby_llm?tab=readme-ov-file#rails-integration-that-makes-sense
I thought the tool feature was very nice.
Ruby
Okay, now let's review the features of Ruby that made all of that so powerful.
Let's check out:
- https://learnxinyminutes.com/ruby/
- https://blog.appsignal.com/2021/08/24/responsible-monkeypatching-in-ruby.html
- https://www.theodinproject.com/paths/full-stack-ruby-on-rails/courses/ruby#basic-ruby
- https://medium.com/@camfeg/dynamic-method-definition-with-rubys-define-method-b3ffbbee8197
Further reading:
- https://learnxinyminutes.com/ruby/
- The creator of Ruby on Rails giving a demo of its features
- https://www.theodinproject.com/paths/full-stack-ruby-on-rails
- https://guides.rubyonrails.org/getting_started.html
- https://www.destroyallsoftware.com/talks/boundaries
- https://github.com/fpsvogel/learn-ruby
- https://poignant.guide/book/chapter-3.html
- ...the past 20 years of blog posts from various developers...