I am using the JQuery Datatables gem and I have successfully gotten it to work for one of my Controllers. SteamGames.
SteamGames Controller
def index
@steam_games = SteamGame.all
respond_to do |format|
format.html
format.json { render json: SteamGamesDatatable.new(view_context) }
end
end
The Datatable itself is pretty simple, from what I got from the git.
class SteamGamesDatatable
delegate :params, :link_to, :number_to_currency, to: :@view
def initialize(view)
@view = view
end
def as_json(options = {})
{
sEcho: params[:sEcho].to_i,
iTotalRecords: SteamGame.count,
iTotalDisplayRecords: games.total_entries,
aaData: data
}
end
private
def data
games.map do |game|
[
link_to(game.game_name, game),
"<img src='#{game.img_icon_url}'/>",
game.created_at.strftime("%B %e, %Y"),
game.updated_at.strftime("%B %e, %Y"),
]
end
end
def games
@games ||= fetch_games
end
def fetch_games
games = SteamGame.order("#{sort_column} #{sort_direction}")
games = games.page(page).per_page(per_page)
if params[:sSearch].present?
games = games.where("game_name like :search", search: "%#{params[:sSearch]}%")
end
games
end
def page
params[:iDisplayStart].to_i/per_page + 1
end
def per_page
params[:iDisplayLength].to_i > 0 ? params[:iDisplayLength].to_i : 10
end
def sort_column
columns = %w[game_name img_icon_url created_at]
columns[params[:iSortCol_0].to_i]
end
def sort_direction
params[:sSortDir_0] == "desc" ? "desc" : "asc"
end
end
Now I'm setting it up for another controller Collections but I realize I'm being extremely redundant but I don't know how to resolve it.
"Collections" is the middleware link between Users and SteamGames.
So I thought perhaps I could just duplicate the ENTIRE Datatables code and replace SteamGame with Collection.steam_game as I would in the Rails Console, but it informs me
NoMethodError (undefined method 'steam_game' for #<Class:0x00000007159670>):
The purpose of this is if I go to /collection/:id I will only see games THAT collection owns. /steamgames shows me every game in my database.
How could I leverage the previous logic easily within the new controller?
If I can't, then how do I properly reference a relational link within a controller?
FYI This is the Datatable I tried making for Collections out of curiousity
class CollectionsDatatable
delegate :params, :link_to, :number_to_currency, to: :@view
def initialize(view)
@view = view
end
def as_json(options = {})
{
sEcho: params[:sEcho].to_i,
iTotalRecords: Collection.count,
iTotalDisplayRecords: games.total_entries,
aaData: data
}
end
private
def data
games.map do |game|
[
link_to(game.game_name, game),
"<img src='#{game.img_icon_url}'/>",
game.created_at.strftime("%B %e, %Y"),
game.updated_at.strftime("%B %e, %Y"),
]
end
end
def games
@games ||= fetch_games
end
def fetch_games
games = Collection.steam_game.order("#{sort_column} #{sort_direction}") ##<--- This is where the error comes from
games = games.page(page).per_page(per_page)
if params[:sSearch].present?
games = games.where("game_name like :search", search: "%#{params[:sSearch]}%")
end
games
end
def page
params[:iDisplayStart].to_i/per_page + 1
end
def per_page
params[:iDisplayLength].to_i > 0 ? params[:iDisplayLength].to_i : 10
end
def sort_column
columns = %w[game_name img_icon_url created_at]
columns[params[:iSortCol_0].to_i]
end
def sort_direction
params[:sSortDir_0] == "desc" ? "desc" : "asc"
end
end
I was thinking maybe an additional function within SteamGamesController might suffice, so I can over-write the def fetch_games function but I don't fully understand what SteamGamesDatatable.new(view_context) is calling within the controller. I ~assume~ the initialize(view) function?
Collection Model
class Collection < ApplicationRecord
belongs_to :user
belongs_to :steam_game
end
SteamGames is actually very similar
Schema for Collection/SteamGames
create_table "collections", force: :cascade do |t|
t.string "platform"
t.string "name"
t.bigint "user_id"
t.bigint "steam_game_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["steam_game_id"], name: "index_collections_on_steam_game_id"
t.index ["user_id"], name: "index_collections_on_user_id"
end
create_table "steam_games", force: :cascade do |t|
t.integer "appid", null: false
t.string "game_name", default: "", null: false
t.string "img_icon_url", default: "assets/32x32-no.png"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Update 2 - Passing Additional Initialization
class CollectionsDatatable
delegate :params, :link_to, :number_to_currency, to: :@view
def initialize(view, steam_games_resource)
@view = view
@steam_games_resource = steam_games_resource
end
def as_json(options = {})
{
sEcho: params[:sEcho].to_i,
iTotalRecords: @steam_games_resource.count,
iTotalDisplayRecords: games.total_entries,
aaData: data
}
end
private
def data
games.map do |game|
[
link_to(game.game_name, game),
"<img src='#{game.img_icon_url}'/>",
game.created_at.strftime("%B %e, %Y"),
game.updated_at.strftime("%B %e, %Y"),
]
end
end
def games
@games ||= fetch_games
end
def fetch_games
games = @steam_games_resource.order("#{sort_column} #{sort_direction}")
games = games.page(page).per_page(per_page)
if params[:sSearch].present?
games = games.where("game_name like :search", search: "%#{params[:sSearch]}%")
end
games
end
def page
params[:iDisplayStart].to_i/per_page + 1
end
def per_page
params[:iDisplayLength].to_i > 0 ? params[:iDisplayLength].to_i : 10
end
def sort_column
columns = %w[game_name img_icon_url created_at]
columns[params[:iSortCol_0].to_i]
end
def sort_direction
params[:sSortDir_0] == "desc" ? "desc" : "asc"
end
end
Controller
def show
@collection = Collection.find(params[:id])
respond_to do |format|
format.html
format.json { render json: CollectionsDatatable.new(view_context, @collection.steam_game) }
end
end
I have modified the initialization to accept a new parameter, which I might be complicating. I also went through the datatable to remove the instance of Collection.steam_game.
Presently I am getting a
undefined methodcount' for #` response, which makes me believe that it is trying to .count on a singular game. I think this is because every record is inserted into the Collection table - So even though it outputs a 'steam_game', there is no count.
After getting this far, I think my models might not be set up properly.
A Member should have a "Collection" - Collection has a platform and a name. The Collection should have games. In theory this is proper, but I'm noticing every game is creating a new Collections row.
Should I instead have a User Collection GameCollections Game
system where the GameCollection is nothing but the 'union'? And User has Collections?
Final Update
Thanks to @Yaro's answer below, it helped guide me on the proper solution.
I went with a 4 Step Sync. User -> Collection <-> Game Collections <- Steam_Games
This allows me to find all users who have X steam_game and find all Collections that have X steam_game
After fixing the logic, I was able to use the same Datatable with the provided recommendation.
My CollectionsController
def show
@collection = Collection.find(params[:id])
respond_to do |format|
format.html
format.json { render json: SteamGamesDatatable.new(view_context, @collection.steam_games) }
end
end
This now shows only the games applicable to this specific collection. I now need to re-visit the naming convention, but this is exactly what I needed.
(Side-note, it also worked with created an exact duplicate CollectionsDatatable but that felt very repetitive)
Collection.rbmodel