DEV Community

Cover image for Rails Movie Browser Project
Chuck
Chuck

Posted on • Updated on

Rails Movie Browser Project

Introduction

Requirements

  • Use the Ruby on Rails framework.
  • Design models with at least one has_many, at least one belongs_to, and at least two has_many :through relationships. Also, include a 1many-to-many1 relationship with has_many :through associations. The join table must include a user-submittable attribute.
  • The models must include reasonable validations for the simple attributes which defend against invalid data.
  • You must include at least one class level ActiveRecord scope method, which is chainable.
  • Provide standard user authentication, including signup, login, logout, and passwords.
  • Provide an authentication system which allows login from some other service. Facebook, Twitter, Foursquare, Github, etc...
  • You must include and make use of a nested resource with the appropriate RESTful URLs.

TLTR: Feel free to get the source code.

App Design

The overall design and idea of the App was to allow the user to see a list of current movies, read details about the movie and save a secure WatchList that the user could add personal comments. The overall structure includes three models: User, Movies, and WatchList. The Movie model is where the logic for retrieving and storing data resides, and the WatchList model concerns the management of the users watch lists.

Security

To handle the security of the user model, this app utilizes Devise. The default Devise validation has been enhanced:

# Additional validations
  EMAIL_REGEX = /\A[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}\z/i.freeze

  validates :email, presence: true, format: EMAIL_REGEX
  validates :password, length: { in: 6..20 }
Enter fullscreen mode Exit fullscreen mode

The API keys for MovieDb have been secured via DOTENV locally and of course the .env file is excluded from the repository via .gitignore.

Alt Text

Photo by K. Mitch Hodge on Unsplash

Fetching API

For accessing the data I used the MovieDB API, which is a well documented API and was a joy to work with. These is a level of complexity of the JSON data from the API, and required a multiple tiered approach.

Popular Movies Data

The beginning of the app starts with downloading a series of twenty movies from the API, using the Rest Client gem. Also, to meet the requires of this project for Flatiron, I decided to write the specific data to a databse table in SQlite3. The method accesses the JSON data from the API, checks the DB for each movie, and then writes the required movie data to the DB table:

def movie_popular(page)
    data = JSON.parse(RestClient.get("https://api.themoviedb.org/3/movie/popular?api_key=#{ENV['MOVIE_API']}&page=#{page.to_s}"))
    data['results'].each do |movie|
      existing_movies = Movie.find_by(movie_id: movie['id'])
      next if existing_movies

      new_movie = Movie.new do |m|
        m.movie_id = movie['id']
        m.title = movie['title']
        m.release_date = movie['release_date']
        m.poster_path = movie['poster_path']
        m.backdrop_path = movie['backdrop_path']
        m.overview = movie['overview']
        m.average_vote = movie['vote_average']
      end
      new_movie.save
    end
end
Enter fullscreen mode Exit fullscreen mode

Since, the first access to the API reads and writes twenty movies, I change to include and seed the DB at the project start, vie seed.rb like so:

# Seed 20 movies
movie_seed = JSON.parse(RestClient.get("https://api.themoviedb.org/3/movie/popular?api_key=#{ENV['MOVIE_API']}&page=1"))
movie_seed['results'].each do |movie|
  Movie.create(
    movie_id: movie['id'],
    title: movie['title'],
    release_date: movie['release_date'],
    poster_path: movie['poster_path'],
    backdrop_path: movie['backdrop_path'],
    overview: movie['overview'],
    average_vote: movie['vote_average']
  )
end
Enter fullscreen mode Exit fullscreen mode

Movie Details

The first pass at the API does not yield all the data I needed for the Movie Detail page. The movie detail call to the API is structured differently, with some data the same, but some structured differently. So, in this case, I called the API based on a movie_id, write specific data to an existing table entry, and used the DB table to populate the detail page:

def movie_detail(loc_movie)
    detail_data = JSON.parse(RestClient.get("https://api.themoviedb.org/3/movie/#{loc_movie}?api_key=#{ENV['MOVIE_API']}"))
    edit_movie = Movie.find_by(movie_id: loc_movie)
    edit_movie.budget = detail_data['budget']
    edit_movie.tagline = detail_data['tagline']
    edit_movie.runtime = detail_data['runtime']
    edit_movie.save
  end
Enter fullscreen mode Exit fullscreen mode

Future Plans

Again because of the API complexity to provide the specific info I need, I am planning to migrate to Postgres. For instance, I need to add Genre tags to the movies. The Genre names are available in the Movie Detail but only as a hash, which Postgres supports.

The next article in this series will explore what I learned Styling this project (i.e. CSS/SCSS/ TailwindCSS).

Top comments (0)