Learn Enough Society
Certificate of Course CompletionThis page certifies that mhartl has completed Ruby on Rails Tutorial! 🎉
0..16
, print out the first 17 powers of 2.
yeller
that takes in an array of characters and returns a string with an ALLCAPS version of the input. Verify that yeller(['o', 'l', 'd'])
returns "OLD"
. Hint: Combine map
, upcase
, and join
.
signup_path
instead of users_new_url
.
ruby -v
at the command line.
hello
action in Listing 1.13 to read “hola, mundo!” instead of “hello, world!”.
hello
action in Listing 1.13, add a second action called goodbye
that renders the text “goodbye, world!”. Edit the routes file from Listing 1.15 so that the root route goes to goodbye
instead of to hello
(Figure 1.22).
goodbye
action. When deploying, confirm that you can omit main
in the Git push, as in git push heroku
.
heroku help
to see a list of Heroku commands. (If the output of heroku help
doesn’t fit in your terminal window, either scroll up or use heroku help | less
to pipe to the less
command.) What is the command to display logs for an app?
set_user
.
FILL_IN
with the appropriate code to validate the presence of name and email attributes in the User model (Figure 2.19).
ApplicationController
to inherit from ActionController::Base
.
ApplicationRecord
inherits from ActiveRecord::Base
? Hint: It would probably be a file called something like application_record.rb
in the app/models
directory.
Foo
with actions bar
and baz
.
Foo
controller and its associated actions.
setup
, which is automatically run before every test, verify that the tests in Listing 3.32 are still green. (Listing 3.32 uses an instance variable, seen briefly in Section 2.2.2 and covered further in Section 4.4.5, combined with string interpolation, which is covered further in Section 4.2.1.)
root_url
(in analogy with helpers like static_pages_home_url
). By filling in the code marked FILL_IN
in Listing 3.44, write a test for the root route.
city
and state
to your current city and state of residence. (If residing outside the U.S., substitute the analogous quantities.)
puts
) a string consisting of the city and state separated by a comma and a space, as in “Los Angeles, CA”.
reverse
method that the string in the previous exercise is the same when its letters are reversed.
s
. Confirm using the comparison operator ==
that s
and s.reverse
are equal.
s
to the string “onomatopoeia”? Hint: Use up-arrow to retrieve and edit previous commands
FILL_IN
with the appropriate comparison test shown in Listing 4.10, define a method for testing palindromes. Hint: Use the comparison shown in Listing 4.9.
nil?
method on palindrome_tester("racecar")
, confirm that its return value is nil
(i.e., calling nil?
on the result of the method should return true
). This is because the code in Listing 4.10 prints its responses instead of returning them.
a
to be to the result of splitting the string “A man, a plan, a canal, Panama” on comma-space.
s
to the string resulting from joining a
on nothing.
s
on whitespace and rejoin on nothing. Use the palindrome test from Listing 4.10 to confirm that the resulting string s
is not a palindrome by the current definition. Using the downcase
method, show that s.downcase
is a palindrome.
a
through z
? What about the same range reversed? Hint: In both cases you will have to convert the range to an array.
shuffled_subdomain
that returns a string of eight letters from a fully shuffled alphabet.
split
, shuffle
, and join
to write a function that shuffles the letters in a given string.
'one'
, 'two'
, and 'three'
, and the values 'uno'
, 'dos'
, and 'tres'
. Iterate over the hash, and for each key–value pair print out "'#{key}' in Spanish is '#{value}'"
.
person1
, person2
, and person3
, with first and last names under the keys :first
and :last
. Then create a params
hash so that params[:father]
is person1
, params[:mother]
is person2
, and params[:child]
is person3
. Verify that, for example, params[:father][:first]
has the right value.
merge
. What is the value of the following expression?
{ "a" => 100, "b" => 200 }.merge({ "b" => 300 })
Range
class and the new
method? Hint: new
takes two arguments in this context.
==
operator that the literal and named constructors from the previous two exercises are identical.
self.reverse
with just reverse
.
shuffle
method to the String
class. Hint: Refer to Listing 4.12.
self.
.
user
object using User.new
.
user
object.
name
to separate first and last name attributes, and then add a method called full_name
that returns the first and last names separated by a space. Use it to replace the use of name
in the formatted email method.
full_name.split
is the same as alphabetical_name.split(', ').reverse
.
mv
command, move kitten.jpg
to the correct asset directory for images (Section 5.2.1).
image_tag
, add kitten.jpg
to the Home page, as shown in Figure 5.4.
custom.scss
, hide all images in the application—currently just the Rails logo on the Home page). Verify with a web inspector that, although the image doesn’t appear, the HTML source is still present. (Be sure to undo this exercise when you’re done so that future images display correctly.)
render
shown in Listing 5.18. Hint: For convenience, cut the default header rather than just deleting it.
layouts
directory, paste in the contents, and verify that the tests are now green again.
alphabetical_name
that returns the last name and first name separated by comma-space.
as:
option. Drawing inspiration from this famous Far Side comic strip, change the route for the Help page to use helf
(Listing 5.29).
helf
route from Listing 5.29.
about_path
to contact_path
and verify that the tests catch the error.
full_title
helper in the tests by including the Application helper into the test helper, as shown in Listing 5.35. We can then test for the right title using code like Listing 5.36. This is brittle, though, because now any typo in the base title (such as “Ruby on Rails Tutoial”) won’t be caught by the test suite. Fix this problem by writing a direct test of the full_title
helper, which involves creating a file to test the application helper and then filling in the code indicated with FILL_IN
in Listing 5.37. (Listing 5.37 uses assert_equal <expected>, <actual>
, which verifies that the expected result matches the actual value when compared with the ==
operator.)
signup_path
. Because of the route defined in Listing 5.43, this test should initially be green.
signup
route to get to red, then uncomment to get to green.
get
method and verify that the resulting page title is correct. Hint: Use the full_title
helper as in Listing 5.36.
schema.rb
in the db/
directory to keep track of the structure of the database (called the schema, hence the filename). Examine your local copy of db/schema.rb
and compare its contents to the migration code in Listing 6.2.
db:rollback
:
$ rails db:rollback
db/schema.rb
to confirm that the rollback was successful. (See Box 3.1 for another technique useful for reversing migrations.) Under the hood, this command executes the drop_table
command to remove the user
table from the database. The reason this works is that the change
method knows that drop_table
is the inverse of create_table
, which means that the rollback migration can be easily inferred. In the case of an irreversible migration, such as one to remove a database column, it is necessary to define separate up
and down
methods in place of the single change
method. Read about migrations in the Rails Guides for more information.
rails db:migrate
again. Confirm that the contents of db/schema.rb
have been restored.
User.new
is of class User
and inherits from ApplicationRecord
.
ApplicationRecord
inherits from ActiveRecord::Base
.
user.name
and user.email
are of class String
.
created_at
and updated_at
attributes?
name
. Confirm that find_by_name
works as well. (You will often encounter this older style of find_by
in legacy Rails applications.)
User.all
acts like an array, but confirm that in fact it’s of class User::ActiveRecord_Relation
.
User.all
by passing it the length
method (Section 4.2.2). Ruby’s ability to manipulate objects based on how they act rather than on their formal class type is called duck typing, based on the aphorism that “If it looks like a duck, and it quacks like a duck, it’s probably a duck.”
save
.
update
.
created_at
column using assignment and a save. Use the value 1.year.ago
, which is a Rails way to create a timestamp one year before the present time.
u
and confirm that it’s initially invalid. What are the full error messages?
u.errors.messages
is a hash of errors. How would you access just the email errors?
reload
method for reloading a value from the database and the assert_equal
method for testing equality. To verify that Listing 6.34 tests the right thing, comment out the before_save
line to get to red, then uncomment it to get to green.
before_save
callback can be written using the “bang” method email.downcase!
to modify the email
attribute directly, as shown in Listing 6.35.
save
. Why didn’t it work?
user
’s name to use your name. Hint: The necessary technique is covered in Section 6.1.5.
params
hash.
user
. What is the output of puts user.attributes.to_yaml
? Compare this to using the y
method via y user.attributes
.
created_at
and updated_at
“magic column” attributes to the user show page from Listing 7.4.
Time.now
to the user show page. What happens when you refresh the browser?
debugger
in the show
action as in Listing 7.6, hit /users/1. Use puts
to display the value of the YAML form of the params
hash. Hint: Refer to the relevant exercise in Section 7.1.1.1. How does it compare to the debug information shown by the debug
method in the site template?
debugger
in the User new
action and hit /users/new. What is the value of @user
?
gravatar_for
helper defined in Section 7.1.4 to take an optional size
parameter, allowing code like gravatar_for user, size: 50
in the view. (We’ll put this improved helper to use in Section 10.3.1.)
f
with foobar
that the name of the block variable is irrelevant as far as the result is concerned. Why might foobar
nevertheless be a bad choice?
form
tag. Why not?
admin
attribute appears in the params
debug information.
redirect_to user_url(@user)
has the same effect as redirect_to @user
.
"#{:success}"
?
FILL_IN
with the appropriate code. (Even testing for the right key, much less the text, is likely to be brittle, so I prefer to test only that the flash isn’t empty.)
content_tag
helper, also works.
@user.save
to false
in Listing 7.26. How does this change verify that the assert_difference
block is testing the right thing?
https
appear.
GET login_path
and POST login_path
?
rails routes
to grep
, list all the routes associated with the Users resource. Do the same for Sessions. How many routes does each resource have? Hint: Refer to the section on grep in Learn Enough Command Line to Be Dangerous.
create
action. How does Rails know to do this? Hint: Refer to Table 8.1 and the first line of Listing 8.5.
user = nil
, and then use user = User.first
. Hint: To coerce the result to a boolean value, use the bang-bang trick from Section 4.2.2, as in !!(user && user.authenticate('foobar'))
.
Expires
attribute from the previous exercise?
User.find_by(id: ...)
returns nil
when the corresponding user doesn’t exist.
session
hash with key :user_id
. By following the steps in Listing 8.17, confirm that the ||=
operator works as required.
log_in
line in Listing 8.32?
user
to the first user in the database, and verify by calling it directly that the remember
method works. How do remember_token
and remember_digest
compare?
User
. This works fine and, because they are actually called using User.new_token
and User.digest
, it is probably the clearest way to define them. But there are two perhaps more idiomatically correct ways to define class methods, one slightly confusing and one extremely confusing. By running the test suite, verify that the implementations in Listing 9.4 (slightly confusing) and Listing 9.5 (extremely confusing) are correct. (Note that, in the context of Listing 9.4 and Listing 9.5, self
is the User
class, whereas the other uses of self
in the User model refer to a user object instance. This is part of what makes them confusing.)
authenticated?
method defined in Listing 9.6 works correctly.
remember_token
attribute in the integration test in Listing 9.25. It is possible, though, using a special test method called assigns
. Inside a test, you can access instance variables defined in the controller by using assigns
with the corresponding symbol. For example, if the create
action defines an @user
variable, we can access it in the test using assigns(:user)
. Right now, the Sessions controller create
action defines a normal (non-instance) variable called user
, but if we change it to an instance variable we can test that cookies
correctly contains the user’s remember token. By filling in the missing elements in Listing 9.27 and Listing 9.28 (indicated with question marks ?
and FILL_IN
), complete this improved test of the “remember me” checkbox.
authenticated?
expression in Listing 9.33 that the second test in Listing 9.31 fails, thereby confirming that it tests the right thing.
target="_blank"
to open URLs, which is that the target site gains control of what’s known as the “window
object” associated with the HTML document. The result is that the target site could potentially introduce malicious content, such as a phishing page. This is extremely unlikely to happen when linking to a reputable site like Gravatar, but it turns out that we can eliminate the risk entirely by setting the rel
attribute (“relationship”) to "noopener"
in the origin link. Add this attribute to the Gravatar edit link in Listing 10.2.
new.html.erb
and edit.html.erb
views to use the partial in Listing 10.5, as shown in Listing 10.6 and Listing 10.7. Note the use of the provide
method, which we used before in Section 3.4.3 to eliminate duplication in the layout.3
assert_select
(Table 5.2) that tests for a div
with class alert
containing the text “The form contains 4 errors.”
only:
hash in Listing 10.15, confirm that the test suite catches this error.
edit
and update
actions?
session[:forwarding_url]
.
debugger
(Section 7.1.3) in the Sessions controller’s new
action, then log out and try to visit /users/1/edit. Confirm in the debugger that the value of session[:forwarding_url]
is correct. What is the value of request.get?
for the new
action? (Sometimes the terminal can freeze up or act strangely when you’re using the debugger; use your technical sophistication (Box 1.2) to resolve any issues.)
log_in_as
helper and add to the steps shown in Listing 5.32.
nil
pulls out the first page of users.
User.all
?
render
line in Listing 10.52 and confirm that the resulting tests are red.
PATCH
request directly to the user path as shown in Listing 10.56, verify that the admin
attribute isn’t editable through the web. To be sure your test is covering the right thing, your first step should be to add admin
to the list of permitted parameters in user_params
so that the initial test is red. For the final line, make sure to load the updated user information from the database (Section 6.1.5).
_url
form of the named route instead of the _path
form? Hint: We’re going to use it in an email.
create_activation_digest
method raises a NoMethodError
due to its being a private method. What is the value of the user’s activation digest?
email.downcase!
(without any assignment). Make this change to the downcase_email
method in Listing 11.3 and verify by running the test suite that it works.
escape
method in the CGI
module escapes out the email address as shown in Listing 11.15. What is the escaped value of the string "Don't panic!"
?
CGI.escape
in Listing 11.20.
authenticated?
method from Listing 11.26, verify that the user is authenticated according to both the remember token and the activation token.
activate
method makes two calls to the update_attribute
, each of which requires a separate database transaction. By filling in the template shown in Listing 11.39, replace the two update_attribute
calls with a single call to update_columns
, which hits the database only once. (Note that, like update_attribute
, update_columns
doesn’t run the model callbacks or validations.) After making the changes, verify that the test suite is still green.
where
method, which we’ll learn more about in Section 13.3.3.)
heroku logs
at the command line.
rails generate integration_test user_show
and then add an inactive fixture user as shown in Listing 11.42. A template for the corresponding test is shown in Listing 11.43.
_url
form of the edit
named route instead of the _path
form? Hint: The answer is the same as for the similar account activations exercise (Section 11.1.1.1).
form_with
in Listing 12.4 use :password_reset
instead of @password_reset
?
reset_digest
and reset_sent_at
attributes, despite the error. What are the attribute values?
reset_digest
and reset_sent_at
attributes.
CGI.escape
in Listing 12.12.
password_digest
attribute. Now submit valid matching passwords to the form shown in Figure 12.12. Did the submission appear to work? How did it affect the value of password_digest
? Hint: Use user.reload
to retrieve the new value.
heroku logs
at the command line.
create_reset_digest
method makes two calls to update_attribute
, each of which requires a separate database operation. By filling in the template shown in Listing 12.21, replace the two update_attribute
calls with a single call to update_columns
, which hits the database only once. After making the changes, verify that the test suite is still green. (For convenience, Listing 12.21 includes the results of solving the exercise in Listing 11.39.)
response.body
, which returns the full HTML body of the page.) There are many ways to test for the result of an expiration, but the method suggested by Listing 12.22 is to (case-insensitively) check that the response body includes the word “expired”.
assert_nil
(first seen in Listing 9.25) with user.reload
(Listing 11.33) to test the reset_digest
attribute directly.
Micropost.new
in the console, instantiate a new Micropost object called micropost
with content “Lorem ipsum” and user id equal to the id of the first user in the database. What are the values of the magic columns created_at
and updated_at
?
micropost.user
for the micropost in the previous exercise? What about micropost.user.name
?
user
to the first user in the database. What happens when you execute the command micropost = user.microposts.create(content: "Lorem ipsum")
?
user.microposts.find(micropost.id)
. What if you write micropost
instead of micropost.id
?
user == micropost.user
? How about user.microposts.first == micropost
?
Micropost.first.created_at
compare to Micropost.last.created_at
?
user
be the first user in the database. What is the id of its first micropost? Destroy the first user in the database using the destroy
method, then confirm using Micropost.find
that the user’s first micropost was also destroyed.
helper.time_ago_in_words(1.year.ago)
?
class
method on paginate
with the argument page: nil
.
(1..10).to_a.take(6)
. Check at the console to see if your guess is right.
to_a
method in the previous exercise necessary?
'h1'
lines in Listing 13.28 from green to red.
will_paginate
appears only once. Hint: Refer to Table 5.2.
logged_in_user
in the Users controller?
if
-else
statement.
INSERT
command in the server log?
user
to the first user in the database. Confirm that the values of Micropost.where("user_id = ?", user.id)
, user.microposts
, and user.feed
are all the same. Hint: It’s probably easiest to compare directly using ==
.
DELETE
command in the server log?
redirect_to request.referrer || root_url
can be replaced with the line redirect_back(fallback_location: root_url)
. (This method was added in Rails 5.)
\
in Listing 13.65, which is a line continuation character, but not the right angle bracket >
, which should automatically be added by your terminal’s shell program.) The additional assertions in Listing 13.66 check both for a file upload field on the Home page and for a valid image attribute on the micropost resulting from valid submission. Note the use of the special fixture_file_upload
method for uploading files as fixtures inside tests.20 Hint: To check for a valid image
attribute, use the assigns
method mentioned in Section 11.3.3 to access the micropost in the create
action after valid submission.
1
from Figure 14.7, what would the value of user.following.map(&:id)
be? (Recall the map(&:method_name)
pattern from Section 4.3.2; user.following.map(&:id)
just returns the array of ids.)
user.following
for the user with id equal to 2
. What would the value of user.following.map(&:id)
be for this user?
create
method from Table 14.1 in the console, create an active relationship for the first user in the database where the followed id is the second user.
active_relationship.followed
and active_relationship.follower
are correct.
user
). What is the value of user.followers.map(&:id)
?
user.followers.count
matches the number of followers you created in the previous exercise.
user.followers.count
? How is this different from user.followers.to_a.count
? Hint: Suppose that the user had a million followers.
User.first.followers.count
matches the value expected from Listing 14.14.
User.first.following.count
is correct as well.
assert_select
tests in Listing 14.29 red to confirm they’re testing the right thing.
respond_to
blocks (Listing 14.36), verify that the tests are testing the right things. Which test fails in each case?
xhr: true
in Listing 14.40? Explain why this is a problem, and why the procedure in the previous exercise would catch it.
user.feed.map(&:id)
return for the feed shown in Figure 14.22? Hint: Recall the default scope from Section 13.1.4.
CGI.escapeHTML
(which is closely related to the CGI.escape
method we used in Section 11.2.3 to escape URLs). Why is escaping the HTML necessary in this case? Hint: Try removing the escaping and carefully inspect the page source for the micropost content that doesn’t match. Using the search feature of your terminal shell (Cmd-F on Ctrl-F on most systems) to find the word “sorry” may prove particularly helpful.
left_outer_joins
method. By running the tests, show that the code in Listing 14.52 returns a passing feed.12 Unfortunately, the actual feed contains multiple copies of the user’s own microposts (Figure 14.25),13 so use the test in Listing 14.53 to catch this error (using the distinct
method, which returns the distinct elements in a collection). Then show that appending the distinct
method to the query (Listing 14.54) results in a green test. By inspecting the generated SQL directly, confirm that the word DISTINCT
appears in the query itself, indicating that the distinct elements are efficiently selected in the database rather than in our application’s memory. (Hint: To get the SQL, run User.first.feed
in the Rails console.)
if user
in Line 8 of Listing 8.15 that the tests still pass even if we don’t authenticate the user by email and password, as shown in Listing 8.29. This is because Listing 8.9 doesn’t test the case of a correct user email but incorrect password. Fix this serious omission in our test suite by adding a valid email to the Users login test (Listing 8.30). Verify that the tests are red, then remove the Line 8 comment to get back to green. (Because it’s so important, we’ll add this test to the main code in Section 8.3.)
&.
to simplify the boolean test in Line 8 of Listing 8.15, as shown in Line 8 of Listing 8.31.16 This Ruby feature allows us to condense the common pattern of obj &&
obj.method
into obj&.method
. Confirm that the tests in Listing 8.30 still pass after the change.
FILL_IN
the code needed to accomplish this. Hint: Recall from Listing 9.36 that we reused the remember digest as a session token, and the User model already has a method designed to delete this (Listing 9.11).
user_id
in our application’s session is currently vulnerable to session replay attacks. Remedy this situation by using the code in Listing 9.35 and Listing 9.36 to FILL_IN
the missing code in Listing 9.37. Hint: Set @current_user
only if the token in the session
hash is equal to the user’s session token. Note that, since the remember_digest
is already a unique value tied to each user, we simply reuse it as a session token, generating one if it doesn’t already exist. (This involves a minor modification to the remember
method to return the digest rather than returning the result of update_attribute
, as seen in Listing 9.36. It’s safe to reuse the remember digest for this purpose both because bcrypt digests are secure by design and because session values are encrypted in any case (Section 8.2).) With this implementation, all that is needed to expire a hijacked session is to call user.forget
. We’ll apply this method in Section 12.3.2.1 to ensure that resetting user passwords expires any existing sessions. Finally, because this code is security-related, it will be incorporated into future code listings and the sample app, unlike the solutions to other exercises in the tutorial.
Get free access to all 10 Learn Enough courses (including the Ruby on Rails Tutorial) for 7 days!
We require a credit card for security purposes, but it will not be charged during the trial period. After 7 days, you will be enrolled automatically in the monthly All Access subscription.
BUT you can cancel any time and still get the rest of the 7 days for free!
All Learn Enough tutorials come with a 60-day 100% money-back guarantee.