This commit is contained in:
2026-01-18 17:32:41 +09:00
parent 16ecbd1fc9
commit 8d7e4e4fdd
6 changed files with 83 additions and 6 deletions
+34
View File
@@ -17,7 +17,12 @@ class Post < ApplicationRecord
foreign_key: :target_post_id foreign_key: :target_post_id
has_one_attached :thumbnail has_one_attached :thumbnail
before_validation :normalise_url
validates :url, presence: true, uniqueness: true
validate :validate_original_created_range validate :validate_original_created_range
validate :url_must_be_http_url
def as_json options = { } def as_json options = { }
super(options).merge({ thumbnail: thumbnail.attached? ? super(options).merge({ thumbnail: thumbnail.attached? ?
@@ -69,4 +74,33 @@ class Post < ApplicationRecord
errors.add :original_created_before, 'オリジナルの作成日時の順番がをかしぃです.' errors.add :original_created_before, 'オリジナルの作成日時の順番がをかしぃです.'
end end
end end
def url_must_be_http_url
begin
u = URI.parse(url)
rescue URI::InvalidURIError
errors.add(:url, 'URL が不正です.')
return
end
if !(u in URI::HTTP) || u.host.blank?
errors.add(:url, 'URL が不正です.')
return
end
end
def normalise_url
return if url.blank?
self.url = url.strip
u = URI.parse(url)
return unless u in URI::HTTP
u.host = u.host.downcase if u.host
u.path = u.path.sub(/\/\Z/, '') if u.path.present?
self.url = u.to_s
rescue URI::InvalidURIError
;
end
end end
+5 -1
View File
@@ -28,4 +28,8 @@
# enabled: "ON" # enabled: "ON"
en: en:
hello: "Hello world" activerecord:
errors:
messages:
record_invalid: "Validation failed: %{errors}"
taken: 'イキスギ!'
@@ -0,0 +1,6 @@
class AddUniqueIndexToUrlInPosts < ActiveRecord::Migration[7.1]
def change
change_column :posts, :url, :string, limit: 768
add_index :posts, :url, unique: true, name: 'index_posts_on_url'
end
end
+3 -2
View File
@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2026_01_12_111800) do ActiveRecord::Schema[8.0].define(version: 2026_01_18_144400) do
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name", null: false t.string "name", null: false
t.string "record_type", null: false t.string "record_type", null: false
@@ -85,7 +85,7 @@ ActiveRecord::Schema[8.0].define(version: 2026_01_12_111800) do
create_table "posts", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| create_table "posts", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "title" t.string "title"
t.string "url", limit: 2000, null: false t.string "url", limit: 768, null: false
t.string "thumbnail_base", limit: 2000 t.string "thumbnail_base", limit: 2000
t.bigint "parent_id" t.bigint "parent_id"
t.bigint "uploaded_user_id" t.bigint "uploaded_user_id"
@@ -95,6 +95,7 @@ ActiveRecord::Schema[8.0].define(version: 2026_01_12_111800) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.index ["parent_id"], name: "index_posts_on_parent_id" t.index ["parent_id"], name: "index_posts_on_parent_id"
t.index ["uploaded_user_id"], name: "index_posts_on_uploaded_user_id" t.index ["uploaded_user_id"], name: "index_posts_on_uploaded_user_id"
t.index ["url"], name: "index_posts_on_url", unique: true
end end
create_table "settings", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| create_table "settings", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
+32 -2
View File
@@ -33,14 +33,14 @@ RSpec.describe 'Posts API', type: :request do
let!(:hit_post) do let!(:hit_post) do
Post.create!(uploaded_user: user, title: "hello spec world", Post.create!(uploaded_user: user, title: "hello spec world",
url: 'https://example.com/spec').tap do |p| url: 'https://example.com/spec2').tap do |p|
PostTag.create!(post: p, tag:) PostTag.create!(post: p, tag:)
end end
end end
let!(:miss_post) do let!(:miss_post) do
Post.create!(uploaded_user: user, title: "unrelated title", Post.create!(uploaded_user: user, title: "unrelated title",
url: 'https://example.com/spec2').tap do |p| url: 'https://example.com/spec3').tap do |p|
PostTag.create!(post: p, tag: tag2) PostTag.create!(post: p, tag: tag2)
end end
end end
@@ -158,6 +158,36 @@ RSpec.describe 'Posts API', type: :request do
expect(json['tags']).to be_an(Array) expect(json['tags']).to be_an(Array)
expect(json['tags'][0]).to have_key('name') expect(json['tags'][0]).to have_key('name')
end end
context 'when url is blank' do
it 'returns 422' do
sign_in_as(member)
post '/posts', params: {
title: 'new post',
url: ' ',
tags: 'spec_tag', # 既存タグ名を投げる
thumbnail: dummy_upload
}
expect(response).to have_http_status(:unprocessable_entity)
end
end
context 'when url is invalid' do
it 'returns 422' do
sign_in_as(member)
post '/posts', params: {
title: 'new post',
url: 'ぼざクリタグ広場',
tags: 'spec_tag', # 既存タグ名を投げる
thumbnail: dummy_upload
}
expect(response).to have_http_status(:unprocessable_entity)
end
end
end end
describe 'PUT /posts/:id' do describe 'PUT /posts/:id' do
+3 -1
View File
@@ -8,7 +8,9 @@ RSpec.describe 'Wiki API', type: :request do
let!(:tn) { TagName.create!(name: 'spec_wiki_title') } let!(:tn) { TagName.create!(name: 'spec_wiki_title') }
let!(:page) do let!(:page) do
WikiPage.create!(tag_name: tn, created_user: user, updated_user: user) WikiPage.create!(tag_name: tn, created_user: user, updated_user: user).tap do |p|
Wiki::Commit.content!(page: p, body: 'init', created_user: user, message: 'init')
end
end end
describe 'GET /wiki' do describe 'GET /wiki' do