Rails Movie Browser Project
Tags: rails, api, ruby, webdev
Published: 2019-12-09 00:26:39

---
title: Rails Movie Browser Project
published: true
description: A project in Rail using the Movie Db API to retrieve movie details from a database. 
tags: Rails, API, ruby, webdev
series: Rails API Project Series
cover_image: https://thepracticaldev.s3.amazonaws.com/i/65rrgznxv81095qipvc5.jpg
---

## 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](https://guides.rubyonrails.org/active_record_querying.html#scopes) 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](https://github.com/eclectic-coding/rails-movie-browser).

## 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](https://github.com/plataformatec/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 }
```

The API keys for [MovieDb](https://www.themoviedb.org/?language=en-US) have been secured via [DOTENV](https://rubygems.org/gems/dotenv-rails/versions/2.1.1) locally and of course the `.env` file is excluded from the repository via `.gitignore.`
{% link https://dev.to/eclecticcoding/gitignore-revisited-20i5 %} 

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/vdrz6hlgx7tkad6iun8y.jpg)

<figcaption>Photo by <a href="https://unsplash.com/@kmitchhodge?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">K. Mitch Hodge</a> on <a href="https://unsplash.com/s/photos/fetch?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></figcaption>

## Fetching API
For accessing the data I used the [MovieDB](https://www.themoviedb.org/?language=en-US) 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](https://github.com/rest-client/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:  
```ruby
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
```

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:
```ruby
# 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
```

### 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:
```ruby
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
```

## 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).