Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ gem "jquery-rails"
gem "sass-rails"
gem "sqlite3"
gem "uglifier"

gem "jbuilder"
gem 'will_paginate'
# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", ">= 1.1.0", require: false

Expand All @@ -34,6 +35,7 @@ group :development, :test do
gem "rspec-rails"
gem "rubocop"
gem "simplecov"
gem "rails-controller-testing"
end

group :test do
Expand Down
12 changes: 11 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ GEM
ruby_parser (~> 3.5)
i18n (1.0.0)
concurrent-ruby (~> 1.0)
jbuilder (2.9.1)
activesupport (>= 4.2.0)
jquery-rails (4.3.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
Expand Down Expand Up @@ -182,6 +184,10 @@ GEM
bundler (>= 1.3.0)
railties (= 5.2.0)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.4)
actionpack (>= 5.0.1.x)
actionview (>= 5.0.1.x)
activesupport (>= 5.0.1.x)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
Expand Down Expand Up @@ -283,6 +289,7 @@ GEM
websocket-driver (0.7.0)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
will_paginate (3.1.7)
xpath (3.0.0)
nokogiri (~> 1.8)

Expand All @@ -302,11 +309,13 @@ DEPENDENCIES
factory_bot_rails
faker
haml-rails
jbuilder
jquery-rails
letter_opener
listen
pry-rails
rails (= 5.2)
rails-controller-testing
rails-erd
rspec-rails
rubocop
Expand All @@ -317,6 +326,7 @@ DEPENDENCIES
spring-commands-rspec
sqlite3
uglifier
will_paginate

BUNDLED WITH
1.16.1
1.16.2
6 changes: 6 additions & 0 deletions app/controllers/api/movies_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class Api::MoviesController < ApplicationController
def index
@with_details = params[:details] and params[:details] == 'true';
@movies = Movie.all
end
end
28 changes: 28 additions & 0 deletions app/controllers/comments_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class CommentsController < ApplicationController
def create
@movie = Movie.find(params[:movie_id])
@comment = @movie.comments.new(comment_params) do |c|
c.user = current_user
end

if @comment.save()
redirect_back(fallback_location: root_path, notice: "Comment added")
else
redirect_back(fallback_location: root_path, alert: @comment.errors)
end
end

def destroy
@comment = Comment.find(params[:id])
if @comment.destroy
redirect_back(fallback_location: root_path, notice: "Comment deleted")
else
redirect_back(fallback_location: root_path, alert: @comment.errors)
end
end

private
def comment_params
params.require(:comment).permit(:body)
end
end
12 changes: 8 additions & 4 deletions app/controllers/movies_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@ class MoviesController < ApplicationController
before_action :authenticate_user!, only: [:send_info]

def index
@movies = Movie.all.decorate
@movies = Movie.all.paginate(:page => params[:page], :per_page => 5).decorate
end

def show
@movie = Movie.find(params[:id])
if current_user
@user_comment_exists = @movie.comments.find_by_user_id(current_user.id)
@comment = @movie.comments.build unless @user_comment_exists
end
end

def send_info
@movie = Movie.find(params[:id])
MovieInfoMailer.send_info(current_user, @movie).deliver_now
SendMovieInfoJob.perform_later(current_user, @movie)
redirect_back(fallback_location: root_path, notice: "Email sent with movie info")
end

def export
file_path = "tmp/movies.csv"
MovieExporter.new.call(current_user, file_path)
CsvMoviesExportJob.perform_later(current_user, file_path)
redirect_to root_path, notice: "Movies exported"
end
end
5 changes: 5 additions & 0 deletions app/controllers/rewards_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class RewardsController < ApplicationController
def index
@rewards = Rewards.instance().get_rewards
end
end
5 changes: 4 additions & 1 deletion app/decorators/movie_decorator.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
class MovieDecorator < Draper::Decorator
delegate_all

def cover
"http://lorempixel.com/100/150/" +
%w[abstract nightlife transport].sample +
"?a=" + SecureRandom.uuid
end

def self.collection_decorator_class
PaginatingDecorator
end
end
3 changes: 3 additions & 0 deletions app/decorators/paginating_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class PaginatingDecorator < Draper::CollectionDecorator
delegate :current_page, :total_entries, :total_pages, :per_page, :offset
end
7 changes: 7 additions & 0 deletions app/jobs/application_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class ApplicationJob < ActiveJob::Base
# Automatically retry jobs that encountered a deadlock
# retry_on ActiveRecord::Deadlocked

# Most jobs are safe to ignore if the underlying records are no longer available
# discard_on ActiveJob::DeserializationError
end
8 changes: 8 additions & 0 deletions app/jobs/csv_movies_export_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class CsvMoviesExportJob < ApplicationJob
queue_as :default

def perform(user, file_path)
# Do something later
MovieExporter.new.call(user, file_path)
end
end
7 changes: 7 additions & 0 deletions app/jobs/send_movie_info_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class SendMovieInfoJob < ApplicationJob
queue_as :default

def perform(user, movie)
MovieInfoMailer.send_info(user, movie).deliver_now
end
end
11 changes: 11 additions & 0 deletions app/models/comment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class Comment < ApplicationRecord
belongs_to :user
belongs_to :movie

after_save :update_rewards
after_destroy :update_rewards

def update_rewards
Rewards.instance().update_rewards
end
end
25 changes: 25 additions & 0 deletions app/models/movie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,29 @@

class Movie < ApplicationRecord
belongs_to :genre
has_many :comments

validates_with TitleBracketsValidator

def api_data
api_request = ApiRequest.new()
api_request.movie_details(self.title)
end

def poster
(@api_data ||= api_data)
@api_data ? URI.join(ApiRequest::API_SERVER, @api_data['poster']) : nil
end

def plot
(@api_data ||= api_data)
@api_data ? @api_data['plot'] : nil
end

def rating
(@api_data ||= api_data)
@api_data ? @api_data['rating'] : nil
end
end


33 changes: 33 additions & 0 deletions app/models/title_brackets_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class TitleBracketsValidator < ActiveModel::Validator
def validate(record)
validate_brackets(record, "(", ")")
validate_brackets(record, "{", "}")
validate_brackets(record, "[", "]")
end

private
def validate_brackets(record, left_bracket, right_bracket)
count = 0
if record.title.include?("#{left_bracket}#{right_bracket}")
record.errors.add :base, 'has invalid title'
return false
end
record.title.split("").each do |ch|
if ch == left_bracket
count += 1
elsif ch == right_bracket
if (count == 0)
record.errors.add :base, 'has invalid title'
return false
else
count -= 1
end
end
end
if (count == 0)
return true
end
record.errors.add :base, 'has invalid title'
return false
end
end
1 change: 1 addition & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class User < ApplicationRecord
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :comments

validates :phone_number, format: { with: /\A[+]?\d+(?>[- .]\d+)*\z/, allow_nil: true }
end
13 changes: 13 additions & 0 deletions app/services/api_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class ApiRequest
API_SERVER = "https://pairguru-api.herokuapp.com"

def movie_details(movie_title)
uri = URI.parse(URI.escape("#{API_SERVER}/api/v1/movies/#{movie_title}"))
Net::HTTP.start(uri.host) do |http|
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request request
json_body = JSON.parse(response.body)
Net::HTTPSuccess and json_body.key?('data') ? json_body['data']['attributes'] : nil
end
end
end
22 changes: 22 additions & 0 deletions app/services/rewards.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Rewards
include Singleton
attr_accessor :rewards

def initialize
update_rewards
end

def update_rewards
summary = Comment
.where(:created_at => (Date.today-7.days).beginning_of_day..Date.today.end_of_day)
.select(:user_id, "count(id) as count")
.group(:user_id)
.order("count desc")
.limit(10)
@rewards = summary.map{|it| {email: User.find(it["user_id"]).email, count: it["count"]}}
end

def get_rewards
@rewards
end
end
11 changes: 11 additions & 0 deletions app/views/api/movies/index.json.jbuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
json.array! @movies do |movie|
json.id movie.id
json.title movie.title
if @with_details
json.genre do
json.id movie.genre.id
json.name movie.genre.name
json.count_movies movie.genre.movies.count
end
end
end
1 change: 1 addition & 0 deletions app/views/layouts/_navigation_links.html.haml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
%li= link_to 'Movies', movies_path
%li= link_to 'Genres', genres_path
%li= link_to 'Rewards', rewards_path
- if user_signed_in?
%li= link_to 'Export', export_movies_path
17 changes: 17 additions & 0 deletions app/views/movies/_comments_table.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
%table.table.table-striped
%caption Comments
%thead
%tr
%td created_at
%td email
%td comment
%td
%tbody
- @movie.comments.order(id: :desc).each_with_index do |comment, i|
- if comment.id #prevent to show empty comment from build (when comment for current_user doesn't exists)
%tr
%td= comment.created_at.strftime("%Y-%m-%d %H:%M:%S")
%td= comment.user.email
%td= comment.body
%td= link_to 'delete', [@movie, comment], :method => :delete, data: {confirm: "Are you sure?"} if current_user == comment.user

5 changes: 5 additions & 0 deletions app/views/movies/_form.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
= form_for [@movie, @comment], method: :post do |f|
= f.label 'Comment'
%div.fields
= f.text_area :body, class: 'form-control'
= f.submit "Add comment"
8 changes: 8 additions & 0 deletions app/views/movies/_movies_table.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,11 @@
= link_to movie.genre.name, movies_genre_path(movie.genre)
= ' (' + movie.released_at.to_s + ')'
%p= movie.description

%img{ src: movie.poster }

%h4 Plot
%p= movie.plot

%h4 Rating
%p= movie.rating
1 change: 1 addition & 0 deletions app/views/movies/index.html.haml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.row
.col-md-8.col-md-offset-2.col-lg-8.col-lg-offset-2
%h1.text-center Movies
= will_paginate @movies
= render 'movies_table', movies: @movies
16 changes: 16 additions & 0 deletions app/views/movies/show.html.haml
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
%h1= @movie.title
.jumbotron
= @movie.description

- if user_signed_in? and !@user_comment_exists
= render "form"
%hr
- if @movie.comments.count > 0
= render "comments_table"

%hr
%div.text-center
%img{ src: @movie.poster }
%hr
%h4 Plot
%p= @movie.plot
%h4 Rating
%p= @movie.rating

- if user_signed_in?
%p= link_to 'Email me details about this movie', send_info_movie_path(@movie), class: 'btn btn-sm btn-default'
Loading