このコミットが含まれているのは:
@@ -0,0 +1,17 @@
|
||||
class GekanatorGamesController < ApplicationController
|
||||
def create
|
||||
return head :unauthorized unless current_user
|
||||
|
||||
answers = params.require(:answers).as_json
|
||||
|
||||
game = GekanatorGame.create!(
|
||||
user: current_user,
|
||||
guessed_post_id: params.require(:guessed_post_id),
|
||||
correct_post_id: params[:correct_post_id].presence,
|
||||
won: bool?(:won),
|
||||
question_count: params.require(:question_count),
|
||||
answers:)
|
||||
|
||||
render json: { id: game.id }, status: :created
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
class GekanatorGame < ApplicationRecord
|
||||
belongs_to :user, optional: true
|
||||
belongs_to :guessed_post, class_name: 'Post'
|
||||
belongs_to :correct_post, class_name: 'Post', optional: true
|
||||
|
||||
validates :answers, presence: true
|
||||
validates :question_count, numericality: { greater_than_or_equal_to: 0 }
|
||||
validates :won, inclusion: { in: [true, false] }
|
||||
validate :correct_post_required_when_lost
|
||||
|
||||
private
|
||||
|
||||
def correct_post_required_when_lost
|
||||
return if won || correct_post_id.present?
|
||||
|
||||
errors.add(:correct_post_id, '外れた時は正解の投稿を指定してくださぃ.')
|
||||
end
|
||||
end
|
||||
@@ -11,6 +11,16 @@ class Post < ApplicationRecord
|
||||
has_many :user_post_views, dependent: :delete_all
|
||||
has_many :post_similarities, dependent: :delete_all
|
||||
has_many :post_versions
|
||||
has_many :gekanator_guessed_games,
|
||||
class_name: 'GekanatorGame',
|
||||
foreign_key: :guessed_post_id,
|
||||
dependent: :delete_all,
|
||||
inverse_of: :guessed_post
|
||||
has_many :gekanator_correct_games,
|
||||
class_name: 'GekanatorGame',
|
||||
foreign_key: :correct_post_id,
|
||||
dependent: :nullify,
|
||||
inverse_of: :correct_post
|
||||
|
||||
has_many :parent_post_implications,
|
||||
class_name: 'PostImplication',
|
||||
|
||||
@@ -63,6 +63,10 @@ Rails.application.routes.draw do
|
||||
end
|
||||
end
|
||||
|
||||
namespace :gekanator do
|
||||
resources :games, only: [:create], controller: '/gekanator_games'
|
||||
end
|
||||
|
||||
resources :users, only: [:create, :update] do
|
||||
collection do
|
||||
post :verify
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
class CreateGekanatorGames < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
create_table :gekanator_games do |t|
|
||||
t.references :user, foreign_key: true
|
||||
t.references :guessed_post, null: false, foreign_key: { to_table: :posts }
|
||||
t.references :correct_post, foreign_key: { to_table: :posts }
|
||||
t.boolean :won, null: false
|
||||
t.integer :question_count, null: false
|
||||
t.json :answers, null: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_check_constraint :gekanator_games,
|
||||
'question_count >= 0',
|
||||
name: 'chk_gekanator_games_question_count_nonnegative'
|
||||
end
|
||||
end
|
||||
生成ファイル
+38
-1
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2026_06_06_000000) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2026_06_07_000000) do
|
||||
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
t.string "record_type", null: false
|
||||
@@ -48,6 +48,21 @@ ActiveRecord::Schema[8.0].define(version: 2026_06_06_000000) do
|
||||
t.index ["tag_id"], name: "index_deerjikists_on_tag_id"
|
||||
end
|
||||
|
||||
create_table "gekanator_games", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.bigint "user_id"
|
||||
t.bigint "guessed_post_id", null: false
|
||||
t.bigint "correct_post_id"
|
||||
t.boolean "won", null: false
|
||||
t.integer "question_count", null: false
|
||||
t.json "answers", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["correct_post_id"], name: "index_gekanator_games_on_correct_post_id"
|
||||
t.index ["guessed_post_id"], name: "index_gekanator_games_on_guessed_post_id"
|
||||
t.index ["user_id"], name: "index_gekanator_games_on_user_id"
|
||||
t.check_constraint "`question_count` >= 0", name: "chk_gekanator_games_question_count_nonnegative"
|
||||
end
|
||||
|
||||
create_table "ip_addresses", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.binary "ip_address", limit: 16, null: false
|
||||
t.datetime "banned_at"
|
||||
@@ -137,6 +152,19 @@ ActiveRecord::Schema[8.0].define(version: 2026_06_06_000000) do
|
||||
t.index ["target_post_id"], name: "index_post_similarities_on_target_post_id"
|
||||
end
|
||||
|
||||
create_table "post_tag_sections", primary_key: ["post_id", "tag_id", "begin_ms"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.bigint "post_id", null: false
|
||||
t.bigint "tag_id", null: false
|
||||
t.integer "begin_ms", null: false
|
||||
t.integer "end_ms", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["post_id", "begin_ms"], name: "idx_post_tag_sections_post_id_begin_ms"
|
||||
t.index ["tag_id"], name: "fk_rails_8be3847903"
|
||||
t.check_constraint "`begin_ms` < `end_ms`", name: "chk_post_tag_sections_end_ms_after_begin_ms"
|
||||
t.check_constraint "`begin_ms` >= 0", name: "chk_post_tag_sections_begin_ms_natural"
|
||||
end
|
||||
|
||||
create_table "post_tags", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.bigint "post_id", null: false
|
||||
t.bigint "tag_id", null: false
|
||||
@@ -187,8 +215,11 @@ ActiveRecord::Schema[8.0].define(version: 2026_06_06_000000) do
|
||||
t.datetime "original_created_before"
|
||||
t.datetime "updated_at", null: false
|
||||
t.integer "version_no", null: false
|
||||
t.integer "video_ms"
|
||||
t.index ["uploaded_user_id"], name: "index_posts_on_uploaded_user_id"
|
||||
t.index ["url"], name: "index_posts_on_url", unique: true
|
||||
t.index ["video_ms", "id"], name: "idx_posts_video_ms_id"
|
||||
t.check_constraint "(`video_ms` is null) or (`video_ms` > 0)", name: "chk_posts_video_ms_positive"
|
||||
t.check_constraint "`version_no` > 0", name: "chk_posts_version_no_positive"
|
||||
end
|
||||
|
||||
@@ -339,6 +370,7 @@ ActiveRecord::Schema[8.0].define(version: 2026_06_06_000000) do
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["expires_at"], name: "index_theatre_watching_users_on_expires_at"
|
||||
t.index ["theatre_id", "expires_at"], name: "idx_on_theatre_id_skip_expires_at_4c8de1dd42"
|
||||
t.index ["theatre_id", "expires_at"], name: "index_theatre_watching_users_on_theatre_id_and_expires_at"
|
||||
t.index ["theatre_id"], name: "index_theatre_watching_users_on_theatre_id"
|
||||
t.index ["user_id"], name: "index_theatre_watching_users_on_user_id"
|
||||
@@ -478,6 +510,9 @@ ActiveRecord::Schema[8.0].define(version: 2026_06_06_000000) do
|
||||
|
||||
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "gekanator_games", "posts", column: "correct_post_id"
|
||||
add_foreign_key "gekanator_games", "posts", column: "guessed_post_id"
|
||||
add_foreign_key "gekanator_games", "users"
|
||||
add_foreign_key "material_versions", "materials"
|
||||
add_foreign_key "material_versions", "materials", column: "parent_id"
|
||||
add_foreign_key "material_versions", "tags"
|
||||
@@ -495,6 +530,8 @@ ActiveRecord::Schema[8.0].define(version: 2026_06_06_000000) do
|
||||
add_foreign_key "post_implications", "posts", column: "parent_post_id"
|
||||
add_foreign_key "post_similarities", "posts"
|
||||
add_foreign_key "post_similarities", "posts", column: "target_post_id"
|
||||
add_foreign_key "post_tag_sections", "posts"
|
||||
add_foreign_key "post_tag_sections", "tags"
|
||||
add_foreign_key "post_tags", "posts"
|
||||
add_foreign_key "post_tags", "tags"
|
||||
add_foreign_key "post_tags", "users", column: "created_user_id"
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Gekanator games API', type: :request do
|
||||
let!(:user) { create_member_user! }
|
||||
let!(:guessed_post) { Post.create!(title: 'guess', url: 'https://example.com/guess') }
|
||||
let!(:correct_post) { Post.create!(title: 'correct', url: 'https://example.com/correct') }
|
||||
|
||||
describe 'POST /gekanator/games' do
|
||||
it 'stores a won game' do
|
||||
sign_in_as user
|
||||
|
||||
post '/gekanator/games', params: {
|
||||
guessed_post_id: guessed_post.id,
|
||||
won: true,
|
||||
question_count: 3,
|
||||
answers: [{ question_id: 'tag:1', answer: 'yes' }] }
|
||||
|
||||
expect(response).to have_http_status(:created)
|
||||
game = GekanatorGame.find(json['id'])
|
||||
expect(game.user).to eq(user)
|
||||
expect(game.guessed_post).to eq(guessed_post)
|
||||
expect(game.correct_post).to be_nil
|
||||
expect(game.won).to eq(true)
|
||||
expect(game.answers).to eq([{ 'question_id' => 'tag:1', 'answer' => 'yes' }])
|
||||
end
|
||||
|
||||
it 'stores a lost game with the correct post' do
|
||||
sign_in_as user
|
||||
|
||||
post '/gekanator/games', params: {
|
||||
guessed_post_id: guessed_post.id,
|
||||
correct_post_id: correct_post.id,
|
||||
won: false,
|
||||
question_count: 4,
|
||||
answers: [{ question_id: 'tag:1', answer: 'no' }] }
|
||||
|
||||
expect(response).to have_http_status(:created)
|
||||
expect(GekanatorGame.find(json['id']).correct_post).to eq(correct_post)
|
||||
end
|
||||
|
||||
it 'rejects a lost game without the correct post' do
|
||||
sign_in_as user
|
||||
|
||||
post '/gekanator/games', params: {
|
||||
guessed_post_id: guessed_post.id,
|
||||
won: false,
|
||||
question_count: 4,
|
||||
answers: [{ question_id: 'tag:1', answer: 'no' }] }
|
||||
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
end
|
||||
|
||||
it 'requires a user' do
|
||||
post '/gekanator/games', params: {
|
||||
guessed_post_id: guessed_post.id,
|
||||
won: true,
|
||||
question_count: 1,
|
||||
answers: [] }
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
end
|
||||
新しい課題から参照
ユーザをブロックする