initial commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
ENVIRONMENT=dev
|
||||
DATABASE_PASSWORD=P@$$w0rd
|
||||
DATABASE_URL=postgres:P@$$w0rd//full_odds:@db1.whatset.cards:5432/full_odds
|
||||
@@ -0,0 +1 @@
|
||||
.env
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
gem "sequel", "~> 5.109"
|
||||
gem "rack", "~> 3.2"
|
||||
gem "pg", "~> 1.7"
|
||||
|
||||
gem "rack-session", "~> 2.1"
|
||||
|
||||
gem "sequel-seed", "~> 1.1"
|
||||
|
||||
gem "httparty", "~> 0.24.3"
|
||||
@@ -0,0 +1,48 @@
|
||||
GEM
|
||||
specs:
|
||||
base64 (0.3.0)
|
||||
bigdecimal (4.1.3)
|
||||
csv (3.3.6)
|
||||
httparty (0.24.3)
|
||||
csv
|
||||
mini_mime (>= 1.0.0)
|
||||
multi_xml (>= 0.5.2)
|
||||
mini_mime (1.1.5)
|
||||
multi_xml (0.9.1)
|
||||
bigdecimal (>= 3.1, < 5)
|
||||
pg (1.7.0-x86_64-linux)
|
||||
rack (3.2.7)
|
||||
rack-session (2.1.2)
|
||||
base64 (>= 0.1.0)
|
||||
rack (>= 3.0.0)
|
||||
sequel (5.109.0)
|
||||
bigdecimal
|
||||
sequel-seed (1.1.2)
|
||||
sequel (>= 4.49.0)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
httparty (~> 0.24.3)
|
||||
pg (~> 1.7)
|
||||
rack (~> 3.2)
|
||||
rack-session (~> 2.1)
|
||||
sequel (~> 5.109)
|
||||
sequel-seed (~> 1.1)
|
||||
|
||||
CHECKSUMS
|
||||
base64 (0.3.0)
|
||||
bigdecimal (4.1.3)
|
||||
csv (3.3.6)
|
||||
httparty (0.24.3)
|
||||
mini_mime (1.1.5)
|
||||
multi_xml (0.9.1)
|
||||
pg (1.7.0-x86_64-linux)
|
||||
rack (3.2.7)
|
||||
rack-session (2.1.2)
|
||||
sequel (5.109.0)
|
||||
sequel-seed (1.1.2)
|
||||
|
||||
BUNDLED WITH
|
||||
4.0.20
|
||||
@@ -0,0 +1,62 @@
|
||||
require 'dotenv/load'
|
||||
require 'sequel'
|
||||
require 'sequel/extensions/seed'
|
||||
|
||||
require_relative './lib/full_odds'
|
||||
require_relative './lib/full_odds/db'
|
||||
|
||||
def create_migration_or_seed(type, args)
|
||||
name = args[:name] || type
|
||||
timestamp = Time.now.strftime("%Y%m%d%H%M%S")
|
||||
Dir.mkdir("lib/full_odds/db/#{type}s") unless Dir.exist?("lib/full_odds/db/#{type}s")
|
||||
filename = "lib/full_odds/db/#{type}s/#{timestamp}_#{name}.rb"
|
||||
filename
|
||||
end
|
||||
|
||||
namespace :db do
|
||||
desc "Run migrations"
|
||||
task :migrate do
|
||||
Sequel.extension :migration
|
||||
db = FO::DB.connection
|
||||
Sequel::Migrator.run(db, "lib/full_odds/db/migrations")
|
||||
puts "Database migrated successfully!"
|
||||
end
|
||||
|
||||
desc "Create migration"
|
||||
task :new_migration, [:name] do |t, args|
|
||||
filename = create_migration_or_seed('migration', args)
|
||||
File.open(filename, 'w') do |f|
|
||||
f.write <<~RUBY
|
||||
Sequel.migration do
|
||||
change do
|
||||
# Add migration logic here
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
puts "Created migration: #{filename}"
|
||||
end
|
||||
|
||||
desc "Apply seeds"
|
||||
task :seed do
|
||||
Sequel.extension :seed
|
||||
db = FO::DB.connection
|
||||
Sequel::Seeder.apply(db, "lib/full_odds/db/seeds")
|
||||
puts "Seeds run successfully!"
|
||||
end
|
||||
|
||||
desc "Create seed"
|
||||
task :new_seed, [:name] do |t, args|
|
||||
filename = create_migration_or_seed('seed', args)
|
||||
File.open(filename, 'w') do |f|
|
||||
f.write <<~RUBY
|
||||
Sequel.seed do
|
||||
def run
|
||||
# Add seed logic here
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
puts "Created seed: #{filename}"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
require 'rack'
|
||||
require 'rack/session'
|
||||
require 'dotenv/load'
|
||||
require 'sequel'
|
||||
|
||||
module FO
|
||||
end
|
||||
|
||||
require_relative 'lib/full_odds/db'
|
||||
|
||||
DB = FO::DB.connection # Warm connection object
|
||||
|
||||
require_relative 'lib/full_odds'
|
||||
require_relative 'lib/full_odds/environment'
|
||||
require_relative 'lib/full_odds/middleware/error_handler'
|
||||
|
||||
use Rack::Static, urls: ['/static']
|
||||
use Rack::Session::Cookie, secret: FO::Environment.session_secret
|
||||
use FO::Middleware::ErrorHandler
|
||||
|
||||
run FO
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'hanami/router'
|
||||
|
||||
require_relative 'full_odds/endpoint/health'
|
||||
require_relative 'full_odds/endpoint/index'
|
||||
|
||||
module FO
|
||||
def self.router
|
||||
@router ||= Hanami::Router.new do
|
||||
get '/health', to: FO::Endpoint::Health
|
||||
get '/', to: FO::Endpoint::Index
|
||||
end
|
||||
end
|
||||
|
||||
def self.call(env)
|
||||
self.router.call(env)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module FO
|
||||
class Cache
|
||||
def initialize(init = {})
|
||||
@c = init
|
||||
end
|
||||
|
||||
def file!(path)
|
||||
if @c[path].nil?
|
||||
return @c[path] = File.read(path)
|
||||
end
|
||||
@c[path]
|
||||
end
|
||||
|
||||
def get(k)
|
||||
@c.fetch(k)
|
||||
end
|
||||
|
||||
def set!(k, v)
|
||||
@c[k] = v
|
||||
end
|
||||
|
||||
def set(k, v)
|
||||
self.class.new(@c.merge(k => v))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'httparty'
|
||||
|
||||
module FO::Client
|
||||
class PokeAPI
|
||||
include HTTParty
|
||||
base_uri 'pokeapi.co'
|
||||
|
||||
def species(id = nil)
|
||||
if id.nil?
|
||||
self.class.get('/api/v2/pokemon-species', {query: {offset: 0, limit: 100_000}})
|
||||
else
|
||||
self.class.get("/api/v2/pokemon-species/#{id}/")
|
||||
end
|
||||
end
|
||||
|
||||
def pokemon(id)
|
||||
self.class.get("/api/v2/pokemon/#{id}/")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'sequel'
|
||||
require 'sequel/extensions/seed'
|
||||
require 'logger'
|
||||
|
||||
require_relative 'environment'
|
||||
|
||||
module FO::DB
|
||||
def self.connection
|
||||
if @db.nil?
|
||||
@db = Sequel.connect(FO::Environment.fetch('DATABASE_URL'))
|
||||
Sequel::Model.plugin :timestamps, update_on_create: true
|
||||
unless (FO::Environment.is_prod?)
|
||||
@db.loggers << Logger.new($stdout)
|
||||
end
|
||||
@db.extension :pg_enum
|
||||
@db.extension :pg_array
|
||||
else
|
||||
@db
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
require_relative '../../versions'
|
||||
|
||||
Sequel.migration do
|
||||
change do
|
||||
create_table(:users) do
|
||||
primary_key :id
|
||||
String :email, null: false, unique: true
|
||||
String :name
|
||||
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
|
||||
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
|
||||
end
|
||||
|
||||
create_table(:identities) do
|
||||
primary_key :id
|
||||
foreign_key :user_id, :users, null: false, on_delete: :cascade
|
||||
String :provider, null: false
|
||||
String :uid, null: false
|
||||
String :token, text: true
|
||||
String :refresh_token, text: true
|
||||
DateTime :expires_at
|
||||
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
|
||||
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
|
||||
unique [:provider, :uid]
|
||||
end
|
||||
|
||||
create_enum(:project_type, %w[sbq hunt bingo dtq])
|
||||
|
||||
create_table(:pokemon) do
|
||||
primary_key :id
|
||||
String :name, unique: true, null: false, index: true
|
||||
|
||||
FO::Versions::VERSIONS_BY_GENERATION
|
||||
.values
|
||||
.map { |g| g[:version_groups] }
|
||||
.reduce(&:+)
|
||||
.each { |version|
|
||||
TrueClass ("version_#{version}").to_sym, default: false
|
||||
File ("version_#{version}_sprite").to_sym
|
||||
}
|
||||
|
||||
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
|
||||
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
|
||||
end
|
||||
|
||||
create_table(:projects) do
|
||||
primary_key :id
|
||||
foreign_key :user_id, :users, null: false, on_delete: :cascade
|
||||
column :data, :jsonb, null: false
|
||||
column :type, :project_type
|
||||
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
|
||||
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,11 @@
|
||||
Sequel.migration do
|
||||
change do
|
||||
create_table(:versions) do
|
||||
primary_key :id
|
||||
String :name, unique: true, null: false, index: true
|
||||
String :generation, null: false, index: true
|
||||
end
|
||||
|
||||
add_enum_value(:project_type, 'mono')
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
require_relative '../../versions'
|
||||
|
||||
Sequel.migration do
|
||||
change do
|
||||
alter_table(:pokemon) do
|
||||
FO::Versions::VERSIONS_BY_GENERATION
|
||||
.values
|
||||
.map { |g| g[:version_groups] }
|
||||
.reduce(&:+)
|
||||
.each { |version|
|
||||
add_column ("version_#{version}_dex").to_sym, :integer
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
require_relative '../../versions'
|
||||
|
||||
Sequel.migration do
|
||||
change do
|
||||
alter_table(:pokemon) do
|
||||
FO::Versions::VERSIONS_BY_GENERATION
|
||||
.values
|
||||
.map { |g| g[:version_groups] }
|
||||
.reduce(&:+)
|
||||
.each { |version|
|
||||
drop_column ("version_#{version}_dex").to_sym
|
||||
}
|
||||
add_column :dex, :integer, null: false, index: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
Sequel.migration do
|
||||
change do
|
||||
alter_table(:projects) do
|
||||
drop_column :data
|
||||
end
|
||||
|
||||
create_table(:hunts) do
|
||||
primary_key :id
|
||||
foreign_key :project_id, :projects, null: false, on_delete: :cascade
|
||||
foreign_key :pokemon_id, :pokemon, null: false, on_delete: :cascade
|
||||
Integer :encounters
|
||||
DateTime :started_at
|
||||
TrueClass :complete, default: false
|
||||
DateTime :completed_at
|
||||
TrueClass :verified, default: false
|
||||
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
|
||||
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
|
||||
File :proof
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
Sequel.migration do
|
||||
change do
|
||||
alter_table(:pokemon) do
|
||||
add_column :types, "text[2]", null: false, index: true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
require_relative '../../versions'
|
||||
|
||||
Sequel.migration do
|
||||
change do
|
||||
alter_table(:pokemon) do
|
||||
FO::Versions::VERSIONS_BY_GENERATION
|
||||
.values
|
||||
.map { |g| g[:version_groups] }
|
||||
.reduce(&:+)
|
||||
.each { |version|
|
||||
drop_column ("version_#{version}").to_sym
|
||||
drop_column ("version_#{version}_sprite").to_sym
|
||||
}
|
||||
end
|
||||
|
||||
create_table(:sprites) do
|
||||
primary_key :id
|
||||
foreign_key :pokemon_id, :pokemon, null: false, on_delete: :cascade
|
||||
File :sprite, null: false
|
||||
end
|
||||
|
||||
create_table(:sprites_versions) do
|
||||
foreign_key :version_id, :versions, null: false, on_delete: :cascade
|
||||
foreign_key :sprite_id, :versions, null: false, on_delete: :cascade
|
||||
primary_key [:version_id, :sprite_id]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
Sequel.migration do
|
||||
change do
|
||||
alter_table(:sprites_versions) do
|
||||
drop_column :sprite_id
|
||||
add_foreign_key(:sprite_id, :sprites)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
Sequel.migration do
|
||||
change do
|
||||
create_table(:encounters) do
|
||||
primary_key :id
|
||||
|
||||
Integer :percentage
|
||||
TrueClass :static
|
||||
String :location, null: false, index: true
|
||||
|
||||
foreign_key :pokemon_id, :pokemon, null: false, on_delete: :cascade
|
||||
foreign_key :version_id, :versions, null: false, on_delete: :cascade
|
||||
end
|
||||
|
||||
alter_table(:hunts) do
|
||||
drop_column :pokemon_id
|
||||
add_foreign_key :enounter_id, :encounters
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
require_relative '../../versions'
|
||||
require_relative '../../model/version'
|
||||
|
||||
Sequel.seed do
|
||||
def run
|
||||
FO::Versions::VERSIONS_BY_GENERATION
|
||||
.keys
|
||||
.map { |gen| { gen: gen }.merge(FO::Versions::VERSIONS_BY_GENERATION[gen]) }
|
||||
.flatten
|
||||
.each { |generation|
|
||||
gen_name = generation[:gen]
|
||||
generation[:version_groups].each { |v|
|
||||
FO::Model::Version.create(
|
||||
name: v,
|
||||
generation: gen_name
|
||||
)
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,121 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'httparty'
|
||||
require 'json'
|
||||
|
||||
require_relative '../../db'
|
||||
require_relative '../../model/pokemon'
|
||||
require_relative '../../model/sprite'
|
||||
require_relative '../../model/version'
|
||||
require_relative '../../versions'
|
||||
require_relative '../../client/pokeapi'
|
||||
|
||||
INITIAL_GENERATIONS = [
|
||||
'generation-i',
|
||||
'generation-ii',
|
||||
'generation-iii',
|
||||
'generation-iv',
|
||||
'generation-v',
|
||||
'generation-vi',
|
||||
'generation-vii'
|
||||
]
|
||||
|
||||
Sequel.seed do
|
||||
def run
|
||||
client = FO::Client::PokeAPI.new
|
||||
all_species = client.species
|
||||
|
||||
all_species["results"].each_with_index do |s, i|
|
||||
dex = i+1
|
||||
|
||||
unless FO::Model::Pokemon.where(dex: dex).empty?
|
||||
puts "Dex num #{dex} already in database"
|
||||
next
|
||||
end
|
||||
|
||||
puts "Working on #{s["name"]} #{dex}"
|
||||
|
||||
species = client.species(dex)
|
||||
|
||||
varieties = species["varieties"]
|
||||
.map { |v| v["pokemon"]["url"].match(/\/(\d+)\//)[1] }
|
||||
.select { |v| !v.nil? }
|
||||
|
||||
puts "Found varieties: #{varieties}"
|
||||
|
||||
varieties.each do |sub|
|
||||
pokemon = client.pokemon(sub)
|
||||
name = pokemon["name"]
|
||||
types = pokemon["types"].map { |t| t["type"]["name"] }
|
||||
|
||||
if types.size < 2
|
||||
types << nil
|
||||
end
|
||||
|
||||
|
||||
FO::DB.connection.transaction do
|
||||
pkmn = FO::Model::Pokemon.create(
|
||||
name: name,
|
||||
dex: dex,
|
||||
types: pokemon["types"].map { |t| t["type"]["name"] }
|
||||
)
|
||||
|
||||
pokemon["sprites"]["versions"].keys.each do |generation|
|
||||
next unless INITIAL_GENERATIONS.include?(generation)
|
||||
generation_data = pokemon["sprites"]["versions"][generation]
|
||||
generation_data.keys.each do |version_group|
|
||||
puts "Working on generation"
|
||||
actual_version_group = version_group
|
||||
if version_group == 'gold' ||
|
||||
version_group == 'silver'
|
||||
actual_version_group = 'gold-silver'
|
||||
end
|
||||
|
||||
unless FO::Versions::VERSION_GROUPS_TO_VERSIONS.keys.include?(actual_version_group)
|
||||
puts "Skipping #{actual_version_group} because its not a valid version group. Pkmn: #{name}"
|
||||
next
|
||||
end
|
||||
|
||||
versions = FO::Versions::VERSION_GROUPS_TO_VERSIONS[actual_version_group]
|
||||
next if versions.nil?
|
||||
|
||||
puts "Looking at generation:"
|
||||
puts "Gen: #{generation}"
|
||||
puts "Data: #{generation_data}"
|
||||
puts "VG: #{version_group}"
|
||||
puts "AVG: #{actual_version_group}"
|
||||
|
||||
sprite_url = ""
|
||||
if generation == "generation-i"
|
||||
sprite_url = generation_data[version_group]["front_default"]
|
||||
else
|
||||
if !generation_data[version_group]["animated"].nil?
|
||||
sprite_url = generation_data[version_group]["animated"]["front_shiny"]
|
||||
else
|
||||
sprite_url = generation_data[version_group]["front_shiny"]
|
||||
end
|
||||
end
|
||||
|
||||
next if sprite_url.nil?
|
||||
|
||||
puts "Found sprite (#{sprite_url}) for #{name} for versions: #{versions}"
|
||||
|
||||
sprite_blob = HTTParty.get(sprite_url).body
|
||||
sprite = pkmn.add_sprite(sprite: sprite_blob)
|
||||
|
||||
versions.each do |v|
|
||||
version = FO::Model::Version.find(name: v.to_s)
|
||||
next if version.nil?
|
||||
version.add_sprite(sprite)
|
||||
end
|
||||
|
||||
sleep 0.5
|
||||
end
|
||||
end
|
||||
sleep 0.1
|
||||
end
|
||||
sleep 0.5
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'http/response'
|
||||
require_relative 'db'
|
||||
|
||||
module FO::Endpoint
|
||||
def call(env)
|
||||
req = FO::HTTP::Request.new(env)
|
||||
res = req.new_response(200)
|
||||
self.dispatch(req, res)
|
||||
res.finish
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../endpoint'
|
||||
|
||||
module FO::Endpoint::Health
|
||||
extend FO::Endpoint
|
||||
|
||||
def self.dispatch(request, response)
|
||||
response.json({status: 'OK'})
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../endpoint'
|
||||
|
||||
require_relative '../model/pokemon'
|
||||
|
||||
module FO::Endpoint::Index
|
||||
extend FO::Endpoint
|
||||
|
||||
def self.dispatch(request, response)
|
||||
pokemon = FO::Model::Pokemon.all
|
||||
|
||||
response.template(
|
||||
'index',
|
||||
{
|
||||
pokemon: pokemon
|
||||
})
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module FO::Environment
|
||||
def self.fetch(k)
|
||||
ENV.fetch(k)
|
||||
end
|
||||
|
||||
def self.is_prod?
|
||||
env == 'prod' ||
|
||||
env == 'production'
|
||||
end
|
||||
|
||||
def self.session_secret
|
||||
s = ENV['SECRET']
|
||||
if self.is_prod? && s.nil?
|
||||
raise ArgumentError, "Environment variable SECRET missing in production environment"
|
||||
end
|
||||
s || ('x' * 64)
|
||||
end
|
||||
|
||||
def self.env
|
||||
ENV.fetch('ENVIRONMENT', 'prod')
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module FO::HTTP
|
||||
module Error
|
||||
HTTP_ERROR_TYPES = {
|
||||
NotFound: {
|
||||
status: 404,
|
||||
msg: 'We could not find the requested resource...',
|
||||
name: 'Bad Request'
|
||||
},
|
||||
BadRequest: {
|
||||
status: 400,
|
||||
msg: 'We were unable to process your request, check your input and try again later',
|
||||
name: 'Bad Request'
|
||||
},
|
||||
InternalServer: {
|
||||
status: 500,
|
||||
msg: 'Something went very wrong on our end!',
|
||||
name: 'Internal Server Error'
|
||||
}
|
||||
}
|
||||
|
||||
class Base < StandardError
|
||||
attr_reader :status, :msg, :name
|
||||
end
|
||||
|
||||
FO::HTTP::Error::HTTP_ERROR_TYPES.each { |k,v|
|
||||
clazz = Class.new(FO::HTTP::Error::Base) do
|
||||
define_method(:initialize) do
|
||||
super(v[:msg])
|
||||
v.each { |attr, attr_value| self.set }
|
||||
@status = v[:status]
|
||||
@msg = v[:msg]
|
||||
@name = v[:name]
|
||||
end
|
||||
end
|
||||
FO::HTTP::Error.const_set(k, clazz)
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rack'
|
||||
require 'json'
|
||||
|
||||
require_relative 'response'
|
||||
|
||||
module FO::HTTP
|
||||
class Request < Rack::Request
|
||||
def content
|
||||
if get? || options?
|
||||
return nil
|
||||
end
|
||||
|
||||
begin
|
||||
body = body.read
|
||||
if media_type.match?(/^application\/json/i)
|
||||
return json
|
||||
elsif media_type.match?(/^application\/form-data/i) ||
|
||||
media_type.match?(/^application\/x-www-form-urlencoded/i)
|
||||
return params
|
||||
end
|
||||
body
|
||||
ensure
|
||||
body.rewind
|
||||
end
|
||||
end
|
||||
|
||||
def path_params
|
||||
self.env['router.params']
|
||||
end
|
||||
|
||||
def new_response(status)
|
||||
FO::HTTP::Response.new(self).status(status)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,50 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rack'
|
||||
require 'json'
|
||||
|
||||
require_relative '../renderer'
|
||||
|
||||
module FO::HTTP
|
||||
class Response < Rack::Response
|
||||
attr_reader :request
|
||||
|
||||
def initialize(request)
|
||||
super('')
|
||||
@request = request
|
||||
end
|
||||
|
||||
def json(thingy)
|
||||
body(JSON.generate(thingy))
|
||||
content_type('application/json; charset=utf-8')
|
||||
self
|
||||
end
|
||||
|
||||
def template(template_name, locals = {})
|
||||
path = File.expand_path("#{__dir__}/../views/#{template_name}.erb")
|
||||
layout_name = locals['layout'].nil? ? 'default' : locals['layout']
|
||||
layout_path = File.expand_path("#{__dir__}/../views/layouts/#{layout_name}.erb")
|
||||
|
||||
body(FO::Renderer.new(layout_path, path, locals).render)
|
||||
|
||||
content_type('text/html; charset=utf-8')
|
||||
self
|
||||
end
|
||||
|
||||
def status(s)
|
||||
@status = s
|
||||
self
|
||||
end
|
||||
|
||||
def body(body)
|
||||
write(body)
|
||||
self
|
||||
end
|
||||
|
||||
def content_type(type)
|
||||
set_header('Content-Type', type)
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module FO::HTTP
|
||||
class Session
|
||||
def initialize(env)
|
||||
@env = env
|
||||
end
|
||||
|
||||
def hash
|
||||
@env['rack.session']
|
||||
end
|
||||
|
||||
def set!(k,v)
|
||||
hash[k] = v
|
||||
end
|
||||
|
||||
def get(k)
|
||||
hash[k]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'http/request'
|
||||
require_relative 'http/response'
|
||||
|
||||
module FO::Middleware
|
||||
def new(app)
|
||||
->(env) {
|
||||
request = FO::HTTP::Request.new(env)
|
||||
response = request.new_response(200)
|
||||
r = self.call(request, response, app)
|
||||
|
||||
if (r.is_a?(FO::HTTP::Response))
|
||||
r.finish
|
||||
end
|
||||
|
||||
r
|
||||
}
|
||||
end
|
||||
|
||||
def call(...)
|
||||
self.dispatch(...)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../middleware'
|
||||
require_relative '../environment'
|
||||
require_relative '../http/error'
|
||||
|
||||
module FO::Middleware
|
||||
module ErrorHandler
|
||||
extend FO::Middleware
|
||||
|
||||
def self.dispatch(request, response, app)
|
||||
app.call(request.env)
|
||||
rescue FO::HTTP::Error => e
|
||||
response
|
||||
.status(e.status)
|
||||
.template('error', {status: e.status, message: e.message, error: e})
|
||||
rescue => e
|
||||
warn "Uncaught exception: #{e.message}"
|
||||
warn e.backtrace
|
||||
message = FO::Environment.is_prod? ?
|
||||
'Something went very wrong ... please try again later.' :
|
||||
e.message
|
||||
response
|
||||
.status(500)
|
||||
.template('error', {status: 500, message: message, error: e})
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'sequel'
|
||||
|
||||
module FO::Model
|
||||
class Encounter < Sequel::Model
|
||||
many_to_one :pokemon
|
||||
many_to_one :versions
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'sequel'
|
||||
|
||||
module FO::Model
|
||||
class Hunt < Sequel::Model
|
||||
one_to_one :encounter
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'sequel'
|
||||
|
||||
module FO::Model
|
||||
class Pokemon < Sequel::Model(:pokemon)
|
||||
one_to_many :encounters
|
||||
one_to_many :sprites
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'sequel'
|
||||
|
||||
module FO::Model
|
||||
class Project < Sequel::Model
|
||||
many_to_one :user
|
||||
one_to_many :hunts
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'sequel'
|
||||
|
||||
module FO::Model
|
||||
class Sprite < Sequel::Model
|
||||
many_to_one :pokemon
|
||||
many_to_many :versions
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'sequel'
|
||||
|
||||
module FO::Model
|
||||
class User < Sequel::Model
|
||||
one_to_many :projects
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'sequel'
|
||||
|
||||
module FO::Model
|
||||
class Version < Sequel::Model
|
||||
many_to_many :sprites
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'erb'
|
||||
|
||||
require_relative 'cache'
|
||||
|
||||
module FO
|
||||
class Renderer
|
||||
def initialize(layout, template, locals)
|
||||
@layout = layout
|
||||
@template = template
|
||||
@locals = locals
|
||||
@globals = {title: 'fullodds.club'}
|
||||
@cache = FO::Cache.new
|
||||
end
|
||||
|
||||
def render
|
||||
layout_content = @cache.file!(@layout)
|
||||
template_content = @cache.file!(@template)
|
||||
@locals.merge(@globals).each do |k,v|
|
||||
self.instance_variable_set("@#{k.to_s}".to_sym, v)
|
||||
end
|
||||
b = binding
|
||||
b.local_variable_set(:content, ERB.new(template_content).result(b))
|
||||
ERB.new(layout_content).result(b)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def verify_templates!
|
||||
unless File.exist?(@layout)
|
||||
raise ArgumentError, "Layout #{@layout} does not exist!"
|
||||
end
|
||||
|
||||
unless File.exist?(@template)
|
||||
raise ArgumentError, "Template #{@template} does not exist!"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,85 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module FO::Versions
|
||||
VERSIONS_BY_GENERATION = {
|
||||
generation_1: {
|
||||
version_groups: ["red", "blue", "yellow"],
|
||||
versions: ["red", "blue", "yellow"]
|
||||
},
|
||||
generation_2: {
|
||||
version_groups: ["gold", "silver", "crystal"],
|
||||
versions: ["gold", "silver", "crystal"]
|
||||
},
|
||||
generation_3: {
|
||||
version_groups: ["ruby", "sapphire", "emerald", "firered", "leafgreen"],
|
||||
versions: ["ruby", "sapphire", "emerald", "firered", "leafgreen"]
|
||||
},
|
||||
generation_4: {
|
||||
version_groups: ["diamond", "pearl", "platinum", "heartgold", "soulsilver"],
|
||||
versions: ["diamond", "pearl", "platinum", "heartgold", "soulsilver"]
|
||||
},
|
||||
generation_5: {
|
||||
version_groups: ["black", "white", "black-2", "white-2"],
|
||||
versions: ["black", "white", "black-2", "white-2"]
|
||||
},
|
||||
generation_6: {
|
||||
version_groups: ["x", "y", "omega-ruby", "alpha-sapphire"],
|
||||
versions: ["x", "y", "omega-ruby", "alpha-sapphire"]
|
||||
},
|
||||
generation_7: {
|
||||
version_groups: ["sun", "moon", "ultra-sun", "ultra-moon", "lets-go-pikachu", "lets-go-eevee"],
|
||||
versions: ["sun", "moon", "ultra-sun", "ultra-moon", "lets-go-pikachu", "lets-go-eevee"]
|
||||
},
|
||||
generation_8: {
|
||||
version_groups: ["sword", "shield", "brilliant-diamond", "shining-pearl", "legends-arceus"],
|
||||
versions: ["sword", "shield", "the-isle-of-armor", "the-crown-tundra", "brilliant-diamond", "shining-pearl", "legends-arceus"]
|
||||
},
|
||||
generation_9: {
|
||||
version_groups: ["scarlet", "violet"],
|
||||
versions: ["scarlet", "violet", "the-teal-mask", "the-indigo-disk"]
|
||||
}
|
||||
}
|
||||
|
||||
VERSIONS_TO_VERSION_GROUPS = {
|
||||
"yellow": "yellow",
|
||||
"blue": "red-blue",
|
||||
"red": "red-blue",
|
||||
"gold": "gold-silver",
|
||||
"silver": "gold-silver",
|
||||
"crystal": "crystal",
|
||||
"ruby": "ruby-sapphire",
|
||||
"sapphire": "ruby-sapphire",
|
||||
"emerald": "emerald",
|
||||
"firered": "firered-leafgreen",
|
||||
"leafgreen": "firered-leafgreen",
|
||||
"diamond": "diamond-pearl",
|
||||
"pearl": "diamond-pearl",
|
||||
"platinum": "platinum",
|
||||
"heartgold": "heartgold-soulsilver",
|
||||
"soulsilver": "heartgold-soulsilver",
|
||||
"black": "black-white",
|
||||
"white": "black-white",
|
||||
"black-2": "black-white",
|
||||
"white-2": "black-white",
|
||||
"x": "x-y",
|
||||
"y": "x-y",
|
||||
"omega-ruby": "omega-ruby-alpha-sapphire",
|
||||
"alpha-sapphire": "omega-ruby-alpha-sapphire",
|
||||
"sun": "ultra-sun-ultra-moon",
|
||||
"moon": "ultra-sun-ultra-moon",
|
||||
"ultra-sun": "ultra-sun-ultra-moon",
|
||||
"ultra-moon": "ultra-sun-ultra-moon",
|
||||
"lets-go-eevee": "lets-go-pikachu-lets-go-eevee",
|
||||
"lets-go-pikachu": "lets-go-pikachu-lets-go-eevee",
|
||||
"sword": "sword-shield",
|
||||
"shield": "sword-shield",
|
||||
"brilliant-diamond": "brilliant-diamond-shining-pearl",
|
||||
"brilliant-pearl": "brilliant-diamond-shining-pearl",
|
||||
"legends-arceus": "legends-arceus",
|
||||
"scarlet": "scarlet-violet",
|
||||
"violet": "scarlet-violet"
|
||||
}
|
||||
|
||||
VERSION_GROUPS_TO_VERSIONS = VERSIONS_TO_VERSION_GROUPS
|
||||
.each_with_object({}){|(k,v),o|(o[v]||=[])<<k}
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
<ul>
|
||||
<% @pokemon.each do |pkmn| %>
|
||||
<li>
|
||||
<%= pkmn.name %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link rel="stylesheet" href="/static/css/layout.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%= @title || 'fullodds.club' %></title>
|
||||
</head>
|
||||
<body>
|
||||
<header id="header-wrapper">
|
||||
</header>
|
||||
<main class="" id="main-wrapper" tabindex="-1">
|
||||
<%= content %>
|
||||
</main>
|
||||
<footer id="footer-wrapper">
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user