Kickflip is a web app built for for skateboarders to use to track and find local skateparks, show their friends when they get a new trick, and encourage return use with a “checkin” feature. Kickflip is built using Sinatra and AciveRecord.
Project Architecture
Kickflip is primarily built with the sinatra ruby gem and uses the “MVC” app architecture (Model, View, Controller) inside the app directory. Kickflip also makes use of the activerecord gem to persist data in sqlite3.
Most models are connected to their corresponding table using ActiveRecord::Base, with the exception of the Search class. Parks and Users have a many-to-many relationship through SkateSessions, while Users relate to Tricks with a has-many relationship through UserTricks

Gems
Kickflip also takes advantage of the geocoder gem, which extends a Class and returns latitude and longitude from street addresses. With the geocoder gem, a user can find a Park near a location without the instance containing the specific string in it’s address.
This gem becomes especially useful when searching for parks in a geographical area that would otherwise not return any results. For example, there are no Parks that are located in “Bountiful, UT”. Instead of returning no results, the query “Bountiful, UT” is transformed into latitude and longitude coordinates (69.0933, -111.8805) and returns results of “Fairmont Skate Park” located in “Salt Lake City, UT”

The application also uses sinatra-flash to display error and success messages when the user modifies data or attempts to access pages they are not authorized to use.

RESTful Conventions
All controllers adhere to RESTful conventions (Representational state transfer) with a few exceptions:
- The
get '/signup'route is used instead ofget '/user/new' - SkateSessions use the
get '/users/:id/skate-sessions'route route as aSkateSessionalways belongs-to aUser. Tricksare not represented by a route because they can not be modified by aUser.UserTricksdo not have a route because they are displayed on on aUserpage.
Models
Because Parks and Users have a many-to-many relationship, both models have similar instance methods to return either the top users or top parks related to each other. An additional method is included in each class, top_x_users(x) and top_x_parks(x) where the argument of x can be changed to return the desired number of results.
This method is especially useful to front end users in displaying “King of the Park” where the top User at a Park gains a special badge displayed on their profile and the Park page.
The User class contains additional instance methods: most_recent_skate_session, minutes_since_most_recent_skate_session, and can_record_skate_session. These methods are used together with the SkateSession.timeout to determine if enough time has passed for a User to record a new SkateSession.

If not enough time has passed, the checkin button is removed and replaced with a message saying “Wait 5 minutes before you can checkin again”.
Sessions
Kickflip allows users to log in and out by enabling sessions in the Application Controller. Together with several helper methods, buttons and content are dynamically generated on a number of variables.
When not logged in or viewing another User’s page, there are no edit buttons present.

However, when viewing a user is viewing their own page and the user is logged in, they are presented with both “Edit My Info”, and “Edit My Checkins” buttons.

Further, if a user attempts to access a route they are not authorized to, they are rerouted and presented with an error message using sinatra-flash.
The redirect_if_user_not_authorized method checks if the passed in user.id has the same session[:user_id].
def redirect_if_user_not_authorized(user)
if user.id != current_user.id
flash[:error] = "You can not edit someone else's data."
redirect "/"
end
end

##Security and Password Encryption
Passwords are salted and hashed through has_secure_password and stored in the database in their encrypted form in the password_digest column. When logging in, the password param is encrypted with the .authenticate method and compared against the encrypted password stored in the database. This way, the user’s password is never exposed to the browser.
post '/login' do
user = User.find_by(email: params["email"])&.authenticate(params['password'])
if user != nil
session[:user_id] = user.id
flash[:success] = "Welcome back to Kickflip, #{user.username}!"
redirect "/users/#{user.id}"
else
flash[:error] = "Invalid login."
redirect "login"
end
end

Resources
Feel free to check out Kickflip on my Github or give me a follow on Twitter to continue following my coding journey.
Kickflip is licensed with a BSD 2-Clause License.