Performed a liposuction to the mods controller, and smeared the fat everywhere. It's beautiful now. 😷

master
Zequez 2015-07-28 20:57:20 -03:00
parent ce778c2778
commit a0d231435e
7 changed files with 310 additions and 195 deletions

View File

@ -5,77 +5,32 @@ class ModsController < ApplicationController
def index
@mods = Mod
@mods = @mods
.includes([:categories, :authors, :owner, :forum_post, versions: :files])
.visible
.sort_by(params[:sort])
.filter_by_names(params[:names])
.filter_by_search_query(params[:q])
.filter_by_game_version(params[:v])
.filter_by_category(params[:category_id])
.page(params[:page]).per(20)
.decorate
if params[:names].present?
@mods = @mods.filter_by_names(params[:names])
end
# This #to_sym is not a DDoS concern since the value is constrained in the router
@sort = params[:sort].to_sym
case @sort
when :alpha
@mods = @mods.sort_by_alpha
when :popular
@mods = @mods.sort_by_popular
when :forum_comments
@mods = @mods.sort_by_forum_comments
when :downloads
@mods = @mods.sort_by_downloads
when :most_recent
@mods = @mods.sort_by_most_recent
else
@mods = @mods.sort_by_alpha
end
if params[:v].present?
@game_version = GameVersion.find_by_number(params[:v])
if @game_version
@mods = @mods.filter_by_game_version @game_version
else
@mods = []
end
end
if params[:q].present?
@query = params[:q][0..30]
@mods = @mods.filter_by_search_query(@query)
end
@uncategorized_mods = @mods
@all_mods = Mod.visible
if params[:category_id].present?
@category = Category.find_by_slug params[:category_id]
if @category
@mods = @mods.filter_by_category @category
else
not_found and return
end
end
@sort = @mods.sorted_by
@category = @mods.category
@game_version = @mods.game_version
@game_versions = GameVersion.sort_by_newer_to_older
@categories = Category.order_by_mods_count.order_by_name
@mods = @mods.decorate
respond_with @mods
end
def show
@mod = Mod.includes(versions: :files).find_by_slug(params[:id])
not_found unless @mod
@mod = @mod.decorate
@mod = Mod.includes(versions: :files).find(params[:id]).decorate
respond_with @mod
end
def new
@mod = Mod.new visible: true, owner: (current_user unless current_user.is_admin?)
fill_with_forum_post_data(@mod, @mod.versions.build, @mod.versions[0].files.build)
@mod = Mod.new_for_form(current_user, params[:forum_post_id])
render_form
end
@ -90,7 +45,6 @@ class ModsController < ApplicationController
def edit
@mod = Mod.find params[:id]
@existing_authors_names = User.pluck(:name)
render_form
end
@ -155,44 +109,4 @@ class ModsController < ApplicationController
permitted_params
end
def fill_with_forum_post_data(mod, mod_version, mod_file)
if params[:forum_post_id]
forum_post = ForumPost.find params[:forum_post_id]
mod.name = forum_post.title
mod.authors_list = forum_post.author_name
mod.forum_url = forum_post.url
if forum_post.published_at
mod_version.released_at = forum_post.published_at
end
if forum_post.subforum and forum_post.subforum.game_version
mod_version.game_versions = [forum_post.subforum.game_version]
end
# scraper = ForumPostScraper.new forum_post
# scraper.scrap
# mod.description = forum_post.markdown_content
end
end
# def category
# @category ||= if params[:category_id]
# begin
# @category = Category.find(params[:category_id])
# rescue ActiveRecord::RecordNotFound
# begin
# @mod = Mod.find(params[:category_id])
# redirect_to category_mod_url(@mod.category, @mod), status: :moved_permanently
# rescue ActiveRecord::RecordNotFound
# not_found
# end
# end
# @mods = @mods.filter_by_category @category
# end
# else
# nil
# end
end

View File

@ -1,3 +1,13 @@
class ModsDecorator < Draper::CollectionDecorator
delegate :current_page, :total_pages, :limit_value, :entry_name, :total_count, :offset_value, :last_page?
end
delegate :current_page, :total_pages, :limit_value,
:entry_name, :total_count, :offset_value, :last_page?,
:game_version, :category, :sorted_by
def all_count
@all_count ||= Mod.visible.count
end
def uncategorized_count
@visible_count ||= object.uncategorized.count
end
end

View File

@ -7,17 +7,9 @@ module ApplicationHelper
end
end
def uncategorized_mods_total_count
@uncategorized_mods_total_count ||= @uncategorized_mods.total_count
end
def all_mods_count
@all_mods_count ||= @all_mods.count
end
def category_probabilistic_count(category)
if all_mods_count != uncategorized_mods_total_count
aproximate_count = (category.mods_count.to_f / all_mods_count * uncategorized_mods_total_count).round(1).to_s
if @mods.all_count != @mods.uncategorized_count
aproximate_count = (category.mods_count.to_f / @mods.all_count * @mods.uncategorized_count).round(1).to_s
content_tag('span', "~#{aproximate_count}", title: 'Probabilistic quantity').html_safe
else
category.mods_count

View File

@ -39,6 +39,7 @@ class Mod < ActiveRecord::Base
# has_many :tags
has_many :favorites
has_many :forum_posts
has_many :bookmarks
has_many :mod_game_versions, -> { uniq }, dependent: :destroy
has_many :game_versions, -> { uniq.sort_by_older_to_newer }, through: :mod_game_versions
@ -56,24 +57,54 @@ class Mod < ActiveRecord::Base
### Scopes
#################
scope :filter_by_category, ->(category) { joins(:categories_mods).where(categories_mods: { category_id: category }) }
scope :filter_by_game_version, ->(game_version) do
joins(:mod_game_versions).where(mod_game_versions: { game_version: game_version })
end
scope :visible, ->{ where(visible: true) }
scope :sort_by, ->(sort_type) do
@sorted_by = sort_type.to_sym
case sort_type.to_sym
when :alpha then sort_by_alpha
when :most_recent then sort_by_most_recent
when :popular then sort_by_popular
when :forum_comments then sort_by_forum_comments
when :downloads then sort_by_downloads
else
@sorted_by = :alpha
sort_by_alpha
end
end
scope :sort_by_most_recent, -> { order('mods.last_release_date desc NULLS LAST') }
scope :sort_by_alpha, -> { order('LOWER(mods.name) asc') }
scope :sort_by_forum_comments, -> { order('mods.forum_comments_count desc') }
scope :sort_by_downloads, -> { order('mods.downloads_count desc') }
scope :sort_by_popular, -> { includes(:forum_post).order('forum_posts.views_count desc NULLS LAST') }
scope :filter_by_category, ->(category) do
@uncategorized = all
if category.present?
@category = category.is_a?(String) ? Category.find(category) : category
joins(:categories_mods).where(categories_mods: { category: @category })
end
end
scope :filter_by_game_version, ->(gv) do
if gv.present?
@game_version = gv.is_a?(String) ? GameVersion.find_by_number!(gv) : gv
joins(:mod_game_versions).where(mod_game_versions: { game_version: @game_version })
end
end
scope :filter_by_search_query, ->(query) do
where('mods.name ILIKE ? OR mods.summary ILIKE ? OR mods.description ILIKE ?', "%#{query}%", "%#{query}%", "%#{query}%")
if query.present?
query = query[0..30]
where('mods.name ILIKE ? OR mods.summary ILIKE ? OR mods.description ILIKE ?', "%#{query}%", "%#{query}%", "%#{query}%")
end
end
scope :filter_by_names, ->(names_list) do
names = names_list.split(',').map(&:strip)
where(info_json_name: names)
if names_list.present?
names = names_list.split(',').map(&:strip)
where(info_json_name: names)
end
end
# def self.filter_by_search_query(query)
@ -92,6 +123,41 @@ class Mod < ActiveRecord::Base
# s1.all.concat s2.all.concat s3.all
# end
### Collection attributes
#################
class << self
attr_reader :game_version, :category, :uncategorized, :sorted_by
end
### Builders
#################
def self.new_for_form(user, forum_post_id)
user = nil if user.is_admin?
mod = Mod.new owner: user, visible: true
mod_version = mod.versions.build
mod_version.files.build
if forum_post_id
forum_post = ForumPost.find forum_post_id
mod.name = forum_post.title
mod.authors_list = forum_post.author_name
mod.forum_url = forum_post.url
if forum_post.published_at
mod_version.released_at = forum_post.published_at
end
if forum_post.subforum and forum_post.subforum.game_version
mod_version.game_versions = [forum_post.subforum.game_version]
end
end
mod
end
### Callbacks
#################

View File

@ -12,7 +12,7 @@
= category_link nil do
%i.fa.fa-cog
= t('.all')
(#{uncategorized_mods_total_count})
(#{@mods.uncategorized_count})
- @categories.each do |category|
%li.category-filter
= category_link category do

View File

@ -10,49 +10,49 @@ feature 'Display an index of mods in certain order' do
c2 = create :category
c3 = create :category
c4 = create :category
c5 = create :category
m1 = create :mod, categories: [c1, c2]
m2 = create :mod, categories: [c2, c3]
m3 = create :mod, categories: [c4]
create :category
create :mod, categories: [c1, c2]
create :mod, categories: [c2, c3]
create :mod, categories: [c4]
visit '/mods'
# All C2 C1 C3 C4 C5
# 2 2 1 1 1 0
expect(categories_counts).to eq [3.0, 2.0, 1.0, 1.0, 1.0, 0.0]
end
scenario 'the user visits the index filtered by game version' do
c1 = create :category
c2 = create :category
c3 = create :category
c4 = create :category
c5 = create :category
create :category
gv1 = create :game_version
m1 = create :mod, categories: [c1, c2], game_versions: [gv1]
m2 = create :mod, categories: [c2, c3], game_versions: [gv1]
m3 = create :mod, categories: [c4]
create :mod, categories: [c1, c2], game_versions: [gv1]
create :mod, categories: [c2, c3], game_versions: [gv1]
create :mod, categories: [c4]
visit "/mods?v=#{gv1.number}"
# All C2 C1 C3 C4 C5
# 2 2/3*2 1/3*2 1/3*2 1/3*2 0/3*2
expect(categories_counts).to eq [2.0, 1.3, 0.7, 0.7, 0.7, 0.0]
end
scenario 'the user visits the index filtered by game version and by category' do
c1 = create :category
c2 = create :category
c3 = create :category
c4 = create :category, name: 'potato'
c5 = create :category
create :category
gv1 = create :game_version
m1 = create :mod, categories: [c1, c2], game_versions: [gv1]
m2 = create :mod, categories: [c2, c3], game_versions: [gv1]
m3 = create :mod, categories: [c4]
create :mod, categories: [c1, c2], game_versions: [gv1]
create :mod, categories: [c2, c3], game_versions: [gv1]
create :mod, categories: [c4]
visit "/category/potato?v=#{gv1.number}"
# All C2 C1 C3 C4 C5
# 2 2/3*2 1/3*2 1/3*2 1/3*2 0/3*2
expect(categories_counts).to eq [2.0, 1.3, 0.7, 0.7, 0.7, 0.0]
end
def categories_counts
page.all('.category-filter a').map{|n| n.text.match(/\(~?([\d\.]+)\)/)[1].to_f}
end
end
end

View File

@ -23,12 +23,11 @@ RSpec.describe Mod, :type => :model do
it { is_expected.to respond_to :imgur_normal }
it { is_expected.to respond_to :last_release_date }
it { is_expected.to respond_to :visible? }
it { expect(mod.visible).to eq true }
it { is_expected.to respond_to :contact }
it { is_expected.to respond_to :info_json_name }
it { is_expected.to respond_to :bookmarks_count }
# URLs
it { is_expected.to respond_to :license_url }
@ -55,6 +54,8 @@ RSpec.describe Mod, :type => :model do
it { is_expected.to respond_to :files }
it { is_expected.to respond_to :versions }
it { expect(mod.versions.build).to be_kind_of ModVersion }
it { is_expected.to respond_to :bookmarks }
it { expect(mod.bookmarks.build).to be_kind_of Bookmark }
# it { is_expected.to respond_to :tags }
it { is_expected.to respond_to :favorites }
it { is_expected.to respond_to :favorites_count }
@ -480,6 +481,32 @@ RSpec.describe Mod, :type => :model do
end
end
describe 'builders' do
describe '.new_for_form' do
it 'should build the required associations and read the #forum_post' do
gv = create :game_version
subforum = create :subforum, game_version: gv
forum_post = create :forum_post,
title: 'rsarsarsarsa',
author_name: 'GuyGuy',
url: 'http://potatopotato.com.potato',
published_at: 1.day.ago,
subforum: subforum
current_user = create :user
mod = Mod.new_for_form(current_user, forum_post.id)
expect(mod.versions[0]).to be_kind_of ModVersion
expect(mod.versions[0].files[0]).to be_kind_of ModFile
expect(mod.name).to eq 'rsarsarsarsa'
expect(mod.authors_list).to eq 'GuyGuy'
expect(mod.forum_url).to eq 'http://potatopotato.com.potato'
expect(mod.versions[0].released_at).to eq forum_post.published_at
expect(mod.versions[0].game_versions).to eq [gv]
expect(mod.visible).to eq true
expect(mod.owner).to eq current_user
end
end
end
describe 'scopes' do
describe '.filter_by_names' do
it 'should return a list of mods by #info_json_name' do
@ -505,16 +532,47 @@ RSpec.describe Mod, :type => :model do
create :mod, info_json_name: 'banana'
expect(Mod.filter_by_names('potato')).to match_array [mod1, mod2]
end
it "should return the whole scope with an empty argument" do
create :mod
create :mod
expect(Mod.filter_by_names('')).to eq Mod.all
end
end
describe '.filter_by_category' do
before :each do
@cat = create :category, name: 'Potato'
@mod1 = create :mod, categories: [@cat]
@mod2 = create :mod, categories: [@cat]
@mod3 = create :mod
end
it 'should filter results by category' do
mod1 = create(:mod)
mod2 = create(:mod)
mod2.categories = [mod1.categories[0]]
mod2.save!
create(:mod)
Mod.filter_by_category(mod1.categories[0]).all.should eq [mod1, mod2]
expect(Mod.filter_by_category(@cat)).to eq [@mod1, @mod2]
end
it 'should work with the category slug' do
expect(Mod.filter_by_category('potato')).to eq [@mod1, @mod2]
end
it 'should raise an exception for a non-existant category' do
expect{ Mod.filter_by_category('rsarsasrasrt') }.to raise_error ActiveRecord::RecordNotFound
end
it 'should make the category available as a collection attribute' do
mods = Mod.filter_by_category(@cat)
expect(mods.category).to eq @cat
end
it 'should return the whole scope with an empty argument' do
expect(Mod.filter_by_category('')).to eq [@mod1, @mod2, @mod3]
end
it 'should make #uncategorized available with the previous scope' do
prev_scope = Mod.where(name: 'something')
mods = prev_scope.filter_by_category('potato')
expect(mods.uncategorized).to eq prev_scope
end
end
@ -559,77 +617,147 @@ RSpec.describe Mod, :type => :model do
expect(Mod.filter_by_game_version(gv2)).to match [m1, m2]
expect(Mod.filter_by_game_version(gv3)).to match [m2, m3]
end
it 'should work by poviding the game version number' do
m1 = create :mod
m2 = create :mod
m3 = create :mod
gv1 = create :game_version, number: '1.1.x'
gv2 = create :game_version, number: '1.2.x'
gv3 = create :game_version, number: '1.3.x'
create :mod_version, game_versions: [gv1, gv2], mod: m1
create :mod_version, game_versions: [gv2, gv3], mod: m2
create :mod_version, game_versions: [gv3], mod: m3
expect(Mod.filter_by_game_version('1.1.x')).to match [m1]
expect(Mod.filter_by_game_version('1.2.x')).to match [m1, m2]
expect(Mod.filter_by_game_version('1.3.x')).to match [m2, m3]
end
it 'returns the whole scope when empty' do
expect(Mod.filter_by_game_version('')).to eq Mod.all
end
it 'should raise an exception for a non-existant game version' do
expect{ Mod.filter_by_game_version('1.1.1.2.3') }.to raise_error ActiveRecord::RecordNotFound
end
it 'should make the game version available on the collection' do
m1 = create :mod
gv1 = create :game_version, number: '1.1.x'
create :mod_version, game_versions: [gv1], mod: m1
mods = Mod.filter_by_game_version('1.1.x')
expect(mods.game_version).to eq gv1
end
end
describe '.sort_by_most_recent' do
context 'there are no mods' do
it 'should return empty' do
Mod.sort_by_most_recent.all.should be_empty
describe 'sorting' do
describe '.sort_by' do
it 'should work with :alpha' do
expect(Mod).to receive(:sort_by_alpha)
Mod.sort_by(:alpha)
end
it 'should work with :most_recent' do
expect(Mod).to receive(:sort_by_most_recent)
Mod.sort_by(:most_recent)
end
it 'should work with :popular' do
expect(Mod).to receive(:sort_by_popular)
Mod.sort_by(:popular)
end
it 'should work with :forum_comments' do
expect(Mod).to receive(:sort_by_forum_comments)
Mod.sort_by(:forum_comments)
end
it 'should work with :downloads' do
expect(Mod).to receive(:sort_by_downloads)
Mod.sort_by(:downloads)
end
it 'should add #sorted_by to the scope methods' do
expect(Mod.sort_by(:alpha).sorted_by).to eq :alpha
expect(Mod.sort_by(:most_recent).sorted_by).to eq :most_recent
expect(Mod.sort_by(:popular).sorted_by).to eq :popular
expect(Mod.sort_by(:forum_comments).sorted_by).to eq :forum_comments
expect(Mod.sort_by(:downloads).sorted_by).to eq :downloads
expect(Mod.sort_by('').sorted_by).to eq :alpha
end
end
context 'there are some mods' do
it 'should return them by versions#released_at date' do
mod1 = create(:mod)
mod2 = create(:mod)
mod3 = create(:mod)
create(:mod_version, released_at: 2.day.ago, mod: mod1)
create(:mod_version, released_at: 1.day.ago, mod: mod2)
create(:mod_version, released_at: 3.day.ago, mod: mod3)
describe '.sort_by_most_recent' do
context 'there are no mods' do
it 'should return empty' do
Mod.sort_by_most_recent.all.should be_empty
end
end
Mod.sort_by_most_recent.all.should eq [mod2, mod1, mod3]
context 'there are some mods' do
it 'should return them by versions#released_at date' do
mod1 = create(:mod)
mod2 = create(:mod)
mod3 = create(:mod)
create(:mod_version, released_at: 2.day.ago, mod: mod1)
create(:mod_version, released_at: 1.day.ago, mod: mod2)
create(:mod_version, released_at: 3.day.ago, mod: mod3)
Mod.sort_by_most_recent.all.should eq [mod2, mod1, mod3]
end
end
context 'multiple versions of the same mod' do
it 'shuold return only one copy of each mod in the correct order' do
mod1 = create(:mod)
mod2 = create(:mod)
mod3 = create(:mod)
create :mod_version, released_at: 3.days.ago, mod: mod1
create :mod_version, released_at: 1.days.ago, mod: mod1
create :mod_version, released_at: 2.days.ago, mod: mod2
create :mod_version, released_at: 4.days.ago, mod: mod3
Mod.sort_by_most_recent.all.should eq [mod1, mod2, mod3]
end
end
end
context 'multiple versions of the same mod' do
it 'shuold return only one copy of each mod in the correct order' do
mod1 = create(:mod)
mod2 = create(:mod)
mod3 = create(:mod)
create :mod_version, released_at: 3.days.ago, mod: mod1
create :mod_version, released_at: 1.days.ago, mod: mod1
create :mod_version, released_at: 2.days.ago, mod: mod2
create :mod_version, released_at: 4.days.ago, mod: mod3
describe '.sort_by_alpha' do
it 'should sort the results by #name' do
mods = []
mods << create(:mod, name: 'Banana')
mods << create(:mod, name: 'Avocado')
mods << create(:mod, name: 'Caca')
Mod.sort_by_most_recent.all.should eq [mod1, mod2, mod3]
expect(Mod.sort_by_alpha).to match [mods[1], mods[0], mods[2]]
end
end
end
describe '.sort_by_alpha' do
it 'should sort the results by #name' do
mods = []
mods << create(:mod, name: 'Banana')
mods << create(:mod, name: 'Avocado')
mods << create(:mod, name: 'Caca')
describe '.sort_by_forum_comments' do
it 'should sort the results by #forum_comments_count' do
mods = []
mods << create(:mod, forum_comments_count: 8)
mods << create(:mod, forum_comments_count: 1)
mods << create(:mod, forum_comments_count: 3)
mods << create(:mod, forum_comments_count: 5)
mods << create(:mod, forum_comments_count: 4)
expect(Mod.sort_by_alpha).to match [mods[1], mods[0], mods[2]]
expect(Mod.sort_by_forum_comments).to match [mods[0], mods[3], mods[4], mods[2], mods[1]]
end
end
end
describe '.sort_by_forum_comments' do
it 'should sort the results by #forum_comments_count' do
mods = []
mods << create(:mod, forum_comments_count: 8)
mods << create(:mod, forum_comments_count: 1)
mods << create(:mod, forum_comments_count: 3)
mods << create(:mod, forum_comments_count: 5)
mods << create(:mod, forum_comments_count: 4)
describe '.sort_by_downloads' do
it 'should sort the results by #forum_comments_count' do
mods = []
mods << create(:mod, downloads_count: 2)
mods << create(:mod, downloads_count: 1)
mods << create(:mod, downloads_count: 3)
mods << create(:mod, downloads_count: 5)
mods << create(:mod, downloads_count: 4)
expect(Mod.sort_by_forum_comments).to match [mods[0], mods[3], mods[4], mods[2], mods[1]]
end
end
describe '.sort_by_downloads' do
it 'should sort the results by #forum_comments_count' do
mods = []
mods << create(:mod, downloads_count: 2)
mods << create(:mod, downloads_count: 1)
mods << create(:mod, downloads_count: 3)
mods << create(:mod, downloads_count: 5)
mods << create(:mod, downloads_count: 4)
expect(Mod.sort_by_downloads).to match [mods[3], mods[4], mods[2], mods[0], mods[1]]
expect(Mod.sort_by_downloads).to match [mods[3], mods[4], mods[2], mods[0], mods[1]]
end
end
end
@ -666,6 +794,11 @@ RSpec.describe Mod, :type => :model do
expect(Mod.filter_by_search_query('banana')).to eq [m2]
end
it 'should return the whole scope with an empty query' do
create(:mod, description: 'This is a potato simulator')
expect(Mod.filter_by_search_query('')).to eq Mod.all
end
# context 'find on name, summary and description' do
# it 'should return them with name > summary > description precedence' do
# m1 = create(:mod, summary: 'This is a bananaFace! simulator')