Helper Methods
Mary Webby
Posted on March 1, 2024
➜ Cleaning up config/routes.rb
post("/movies", { :controller => "movies", :action => "create" })
anywhere you see the old
Hash
syntax (:symbol
keys with hash rockets=>
): switch to the newHash
syntax. Which would becontroller: "movies", action: "create"
next, it is always good to see how many methods are happening on this line, Raghu's convention is that is there no other methods happening, you can leave out the parentheses. so our output would look something like this
get "/movies", controller: "movies", action: "create"
.we can even go further to just take out the controller and action since it is the only method we are placing into the route. remember, the
#
symbol is used to indicate anObject#method
, which, in this example, isMoviesController#create
. which would finally leave us with something like this
get "/movies" => "movies#create"
- for special circumstances like when you are wanting to display the root page of the website, rails has special syntax that allows you to omit the route being displayed, so instead of
# get("/", { :controller => "movies", :action => "index" })
, you would write it as thisroot "movies#index"
.
➜ Route helper methods
- at the bash prompt, run
rails routes
. the display should look like something similar to this
Prefix Verb URI Pattern Controller#Action
GET / movies#index
movies POST /movies(.:format) movies#create
movies_new GET /movies/new(.:format) movies#new
GET /movies(.:format) movies#index
GET /movies/:id(.:format) movies#show
PATCH /movies/:id(.:format) movies#update
GET /movies/:id/edit(.:format) movies#edit
DELETE /movies/:id(.:format) movies#destroy
Prefix
means that Rails has defined a method that we can call anywhere in our view templates, controller actions, etc., that will return the path as a string.to test out what exactly this code is doing, move over to the
index.html.erb
page, type in<%= movies_path %>
, and see what the page renders. you can see that it is displaying the string"/movies"
. if we were to try others, we can see that<%= movies_new_path %>
refresh the change, and/movies
renders"/movies/new"
. Following this pattern,<%= root_path %>
just renders"/"
.-
movies
andmovies_new
are static routes, without dynamic ID numbers in the path, so Rails just puts the segments together and gives you the method. But, for all the rest of the routes that do contain:id
, there are no methods defined yet. We get to define those methods ourselves!➜ Creating our own route helper methods
get "/movies/:id" => "movies#show", get "/movies/:id" => "movies#show", as: :details
say for the one above,
"/movies/:id"
has:id
within the route, so we have to properly define it after the route, just like we are doing above, theas: :details
part of the route is us defining a specific method, we can then transition back into theindex.html.erb
file and define our route like so<%= details_path %>
.if we see what this provides us with it will be an error
No route matches {:action=>"show", :controller=>"movies"}, missing required keys: {:id}
. this is because it is a dynamic route, so rails cant produce a route unless you tell it what to put in the path!say if we were to put
(42)
within this ruby syntax helper method, it would be able to display the route with the:id
being(42)
.you can also change the embedded ruby method to
details_url(42)
, then you will be able to see the full url of the app, including the path, this allows you to see fully qualified url's. this means you dont need to hardcode anything because it will be able to full run on any persons own personal web browser.now we are going to be placing them into the normal conventions that every ruby developers use. such as
get "/movies/new" => "movies#new", as: :new_movie
get "/movies/:id/edit" => "movies#edit", as: :edit_movie
- we however dont need to define the helper methods for delete or patch
patch "/movies/:id" => "movies#update"
delete "/movies/:id" => "movies#destroy"
➜ implementing helper methods
- Now we are going to be taking those helper methods and placing them into the rest of our code, so these are going to work the same way as routes, where instead of having the routes path and then placing the variable into it with ruby
<%= a_movie.id %>
like this, we will change it to something like below...
<form action="<%= movie_path(@the_movie.id) %>" method="post" data-turbo="false">
<input name="authenticity_token" value="<%= form_authenticity_token %>" type="hidden">
another point, since we are taking the specific movie id for the route, we can use ruby convention and just always assume that that is what it will naturally grab specifically unless we say otherwise
action="<%= movie_path(@the_movie) %>"
say we are implementing them into our controllers for redirection, for this piece of code for example...
if @the_movie.valid?
@the_movie.save
redirect_to("/movies", { :notice => "Movie created successfully." })
else
render template: "movies/new"
end
- we can instead change
"/movies"
tomovies_url
, since we want to specifically route them to a url and not use the path name/movies
➜ Creating forms with helper methods
<%= form_with(url: movies_path) do %>
<div>
<!-- <label for="title_box">
Title
</label>-->
<%= label_tag :title_box, "Title" %>
<!--<input type="text" id="title_box" name="query_title" value="<%#= @the_movie.title %>">-->
<%= text_field_tag :query_title, @the_movie.title, { id: "title_box" } %>
</div>
<div>
<!--<label for="description_box">
Description
</label>-->
<%= label_tag :description_box, "Description" %>
<!-- <textarea id="description_box" name="query_description" rows="3"><%#= @the_movie.description %></textarea>\ -->
<%= text_area_tag :query_description, @the_movie.description, id: "description_box", rows: 3 %>
</div>
<!-- <button>
Create Movie
</button>-->
<%= button_tag "Button"%>
<% end %>
- here you can see the before and after of placing in tags to our forms, and below is a much more organized way to look at it
<%= form_with(url: movies_path) do %>
<div>
<%= label_tag :title_box, "Title" %>
<%= text_field_tag :query_title, @the_movie.title, { id: "title_box" } %>
</div>
<div>
<%= label_tag :description_box, "Description" %>
<%= text_area_tag :query_description, @the_movie.description, id: "description_box", rows: 3 %>
</div>
<%= button_tag "Button"%>
<% end %>
➜ find_by
method
what we have been practicing before has led us to this moment where we will be totally deducing our 3 steps to get an
ActiveRecord
instance (a single row of a table, compared to anActiveRecord:Relation
(which is many rows of a table.in the beginning of learning ruby, we had something very similar to this setup in our code...
def show
the_id = params.fetch(:id) # FIRST we get the ID
matching_movies = Movie.where(id: the_id) # THEN we get the set of matching rows (ActiveRecord:Relation)
@the_movie = matching_movies.first # FINALLY we get one instance of ActiveRecord, or one row
end
- now we are presented with something very similar to this, essentially reducing whatever we started with to a more simplified version, such as this...
def show
@the_movie = Movie.where(id: params.fetch(:id)).first
end
-
now, finally we are going to be stripping that even more to a better more conventional version of this piece of code, something using the
.find_by
method. it is essentially the same thing as where, except that is automatically returns the.first
record given in a collection without having to explicitly say that.
def show
@the_movie = Movie.find_by(id: params.fetch(:id))
end
or shorter
def show
@the_movie = Movie.find(params.fetch(:id))
end
important to note here that the
.find()
method only takes an integer argument and is specifically for finding ID's in the ID column table
- now we can accurately and effectively search for what we need without using lines and lines of code as we were doing before.
➜ Creating a checkbox form
getting an array with checkbox
<form>
<label>Red</label>
<input type="checkbox" name="zebra[]" value="red">
<label>Blue</label>
<input type="checkbox" name="zebra[]" value="blue">
</form>
output looks something like this
zebra["red", "blue"]
getting an array with hashes with checkbox
<form>
<label>Red</label>
<input type="checkbox" name="zebra[giraffe]" value="red">
<label>Blue</label>
<input type="checkbox" name="zebra[elephant]" value="blue">
</form>
output looks something like this
zebra["giraffe" => "red", "elephant" => "blue"]
➜ Mass assignment with forms
<%= form_with(model: @movie, data: { turbo: false }) do |form| %>
<div>
<%= form.label :title %>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :description %>
<%= form.text_area :description, { rows: 3 } %>
</div>
<%= form.button %>
<% end %>
➜ Adding method
- This movie_params is a good tool to understand and takes away the need to write in a new table over and over again when making it cause you would just add it in one place and then it could be added everywhere.
def movie_params
params.require(:movie).permit(:title, :description, :image_url, :director_id)
end
➜ partial view templates
- You can only add a partial to a big, not a big to a big or a big to a partial. this helps up organize our code into more folders and shorter lines so we are not overwhelmed by how many lines could be for each page.
<%= render "shared/flash_messages", locals: { person: "Alice" }%>
could be good for explaining name for each user in the top corner
in the file youre displaying
<%= render partial: "movies/form", locals: { foo: @the_movie} %>
- in the partial
<% foo.errors.full_messages.each do |message| %>
<p style="color: red;"><%= message %></p>
<% end %>
Posted on March 1, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024