RentKittyCats
This project is a rental website based on 99dresses, but oriented around cats! It allows users to rent out their cats to other users, and the renters can accept / deny requests. Once a request has been accepted, overlapping requests are automatically denied, preventing new requests for that duration of time.
Learning Goals
-
Be able to build a model with validations and default values
-
Know how to build Rails views for new and edit forms
- Know how to use a hidden field to set the form's method
- Be able to separate the form out into a partial that both forms use
- Be able to show data and actions based on the form's type
- Know how to use select and input HTML elements
-
Be able to add methods to a Rails model
-
Be able to create a user authentication system
- Know the user model's methods that are required for authentication
reset_session_token
,password=
,is_password?
,find_by_credentials
- Know the user model's methods that are required for authentication
-
Know what it means to create and destroy a session
-
Know how cookies and sessions interact in a
current_user
method -
Know how to access the current user from within a view
Phase 1 - Cat
Model
birth_date
color
name
sex
description
text
column describes memories the user has withCat
- Timestamps
- database-level NOT NULL constraints
null: false
and model-level presence validationspresence: true
Index/Show Pages
class CatsController < ApplicationController
index
page of allCat
s- Lists the cats and links to the show pages.
show
page for one cat- Shows the cat's attributes
- A
table
element withtr
,td
,th
tags will format the cat's information.
_form
form partial utilized inindex
andshow
cat
local variable takes value of@cat
edit
makes a PATCH request
Phase 2 - CatRentalRequest
Model
CatRentalRequest
schema and modelcat_id
(integer)start_date
(date)end_date
(date)status
(string),default: 'PENDING'
, can switch to'APPROVED'
or'DENIED'
- inclusion validates
status
null: false
constraints,presence: true
validations- Associations between
CatRentalRequest
andCat
dependent: :destroy
prevents widowedCatRentalRequest
upon destroyingCat
Custom Validation
CatRentalRequests
should not be valid if they overlap with an approved CatRentalRequest
for the same cat. A single cat can't be rented out to two people at once!
CatRentalRequest#overlapping_requests
gets allCatRentalRequest
s that overlap with the one we are trying to validate.CatRentalRequest
we are validating should not appear in list of#overlapping_requests
- The method returns the requests for the current
Cat
- This method runs on saved and unsaved
CatRentalRequest
s - Test Cases:
- A cat rental request starting on 02/25/17 and ending on 02/27/17.
- There is a overlap if another cat rental also starts on the same day (02/25/17).
- There is a overlap if another cat rental request starts on the return day (02/27/17).
- There is a overlap if another cat rental request starts between the start and end dates (02/26/17).
def overlapping_requests
CatRentalRequest,
where.not(id: self.id),
where(cat_id: self.cat_id),
where.not('start_date > :end_date OR end_date < :start_date',
start_date: self.start_date, end_date: self.end_date)
end
Controller and New View
CatRentalRequests
Controller, resources inconfig/routes.rb
new.html.erb
to create new requests- A dropdown will allow user to select
Cat
, with the form uploading a cat id. date
input type selects start and end dates for requestcreate
action- Cat show page will show existing requests
Phase 3: Approving/Denying Requests
approve!
deny!
methods
- Helper method CatRentalRequest#overlapping_pending_requests
approve!
changesCatRentalRequest
instance status from"PENDING"
to"APPROVED"
save!
instance into database, deny conflicting requestsoverlapping_pending_requests
in single Railstransaction
.
def approve!
CatRentalRequest.transaction do
self.status = "APPROVED"
self.save!
# deny conflicting requests
overlapping_pending_requests.each do |request|
request.status = "DENIED"
request.save!
end
end
end
deny!
method changesCatRentalRequest
instance status from"PENDING"
toDENIED
def deny!
self.status = "DENIED"
self.save!
end
Add Buttons
Cat
Show page has buttons to approve/deny rental requests.approve
deny
member routes forcat_rental_requests
- Conditional logic will show/hide
approve
deny
buttons CatRentalRequest#pending?
boolean method- CSS can be added to the currently existing view templates!
Phase 4 - Users
User
model
Add a User
user_name
andpassword_digest
- database constraints and model validations
session_token
columnpresence: true
andnull: false
.after_iniitalize
callback function will set token that is not already set.
User#reset_session_token!
method will useSecureRandom
to generate a token.User#password=(password)
setter method that writes thepassword_digest
attribute with the hash of the given password.#is_password?(password)
method verifies a password.::find_by_credentials(user_name, password)
method returns the user with the given name if the password is correct.
UsersController
, SessionsController
We need a UsersController
with new
/create
actions and routes.
Then we can build a SessionsController
for authorization.
:session
resource inroutes.rb
new
,create
,destroy
actions/resources
SessionsController#new
needsnew.html.erb
SessionsController#create
- Verify
user_name/password
- Reset
User
'ssession_token
session
hash updated withsession_token
- Redirect user to
CatsController#index
- Verify
- Create all remaining views for these controllers.
Using the session
ApplicationController#current_user
looks up user with current session tokenhelper_method :current_user
makes it available in views
SessionsController#destroy
#reset_session_token
called oncurrent_user
invalidates the old token if there is acurrent_user
, making sure that no one can steal the old token and login with it.:session_token
is blanked out in thesession
hash
application.html.erb
has a header at the top- Shows username if signed in
- Shows login or logout button depending if a user is signed in
- A new user logs in as soon as they register.
SessionsController#create
factored out intoApplicationController#login_user!
method to be used inUsersController
- In
UsersController
andSessionsController
,before_action
callback redirects user to Cats index if the user tries to visit login/signup pages when already signed in.
current_user
with Cat
and CatRentalRequest
Phase 5: Use Cats have an owner
user_id
column on cats, index onuser_id
User
has manycats
(Owner has cat)owner
presence validation (not null)CatsController#create
user_id
is set tocurrent_user.id
The form submitter must be logged in or they couldn't view the form to begin with, so we can set cat.owner to the current_user without relying on any form inputs.
- In the
CatsController#edit
CatsController#update
actions,before_action :user_owns_cat, only: [:edit, :update]
- Instead of using Cat.find, search for the cat among only the
current_user
's cats with theUser#cats
association. - Do a
redirect_to
in the filter if the user is not authorized. - Redirection from inside a
before_action
cancels further processing of the request. The action will never be called.
- Instead of using Cat.find, search for the cat among only the
- On
CatRentalRequestsController
only the cat owner should be able to approve/deny. - On the Cat Show page, does not show the approve/deny buttons unless the user owns the cat.
CatRentalRequests have a requester
user_id
column onCatRentalRequest
records ID of requester- index on foreign_key
user_id
CatRentalRequest
belongs_to
User
has_many
validates
:requester
onCatRentalRequest
- index on foreign_key
current_user
isrequester
onCatRentalRequestController
requester
is displayed to each rental request on Cat Show Page
Some things to add later
- Make sure
cat_rental_requests
query isN
notN + 1
There may need to be some changes to the sessions in order to handle logins from multiple browsers at the same time.