Exploring how a new model and UI is created in Rails using `rails generate scaffold`
As a newbie to Rails and Ruby, I found the code generated by the rails generate scaffold
command initially very hard to understand. A lot of Googling later, I think I have a better understanding of what things are doing, and so I decided to write a post about it!
Running the generate scaffold command
If we were to create a to-do list app, we would need a way of storing our tasks in a database. With Rails, we can do this using something called a model - which acts like a wrapper aorund a database table.
In our case, we are going to need a table that stores tasks, and so we need to create a Task
model that stores the task’s name, due date, and whether it has been completed or not.
We can call rails generate scaffold
with the name of our model, and any number of arguments in the shape fieldName:dataType
:
Note that you don’t need to specify an ID - Rails will handle this one for you.
If you’re not sure what data types to use for your fields, I found this StackOverflow answer on data types really useful.
One of the new files created by this command will be a new file in db/migrate
that will look something like this:
To create a table of tasks in our database, we will need to run that file. We can do that with the following:
Now that your database can handle storing tasks you can start up your app:
You will see that at http://localhost:3000/tasks
, Rails has generated a basic UI for you that lets you create, delete and edit tasks - all for free! (I was actually pretty amazed by this). I recommend having a little play around with it and creating a few tasks.
In the next section, we’ll be exploring how all this UI has been generated for us.
How tasks routing works
If we take a look at config/routes.rb
we will see a new line has been added:
This is a shorthand for 7 separate routes that allow you to view, modify and create tasks.
e.g. two of them would be:
Each line maps a URL to a controller and action, so if we make a GET call to /tasks
, we will execute the code inside of the index
action in the tasks
controller.
What’s happening in the tasks controller?
The tasks
controller is another file that has been generated for us at app/controllers/tasks_controller.rb
. I’m going to break down some of what is happening.
before_action
Right up the top of the file, you’ll see this:
Here we’re saying that the set_task
method (which is defined near the bottom of the file) should be called before the given list of actions (show, edit, update and destroy).
The colon (
:
) beforeset_task
indicates that it is a Symbol, which is like a reference to the method. If we didn’t use the colon,before_action
would use the results of calling theset_task
method.
set_task
That set_task
method does the following:
@task
- In Ruby, the@
defines a variable as an instance variable, which means it can be accessed outside of where it has been defined (even in the view file!)Task
- Refers to the Task class that lives inmodels/task.rb
.Task.find()
- we can callfind(id)
on the model, which will find the task that matches the ID we passed inparams
- this object contains any parameters defined in the routes file. In our case one of our routes isget '/tasks/:id'
so if a user lands onlocalhost:3000/tasks/2
, then in our params object there will be an:id
with a value of2
.
In short, before we do certain things like viewing a specific task or deleting one, we will store in @task
the relevant task that we are looking for. This means each action that access it immediately without having to add in that extra line of code themselves.
Getting all tasks using the index action
Now the first action is our index action. This maps to the following route:
The action itself is very short:
What we’re doing here is storing a list of all the tasks in @tasks
.
And then with Rails magic, this action will map to the view file defined at tasks/index.html.erb
. This view file will have access to all the tasks, and can loop through them and render them all on the page:
Updating a task using the update action
The update action maps to the following route:
Let’s break this down!
respond_to
- a method available to us in controller classes, which will give access to aformat
method@task
- we got this from theset_task
method (described above)@task.update()
- we can callupdate()
on the specific task, and pass in new valuestask_params
- gives us the values in the body of the call that was made (described in more detail below)format.html
andformat.json
- we’re defining what is returned, depending on if the request was an HTML or JSON request.if
andelse
- if@task.update
fails, we will enter theelse
block.
task_params
The other method defined at the bottom of the file is task_params
:
When this method is called, we will require that a :task
symbol exists inside of the params object, and filter out any values from task that aren’t the values for name, done and due.
What this means in practice is that if you called the endpoint with the data to create a new task, this would be accessible inside of params[:task]
. And if you passed in an extra field like “description”, this would get filtered out by this method.
And that’s it for this post! The other actions are fairly similar to the code I’ve gone through above, so hopefully shouldn’t be too hard to understand.
Thanks for reading!