Enhance Inkbunny job processing and database schema

- Marked tasks as complete in TODO.md for the Inkbunny index scan job and log attachment features.
- Refactored `UpdatePostsJob` to improve method naming and enhance error handling for missing posts.
- Introduced associations between `Post`, `Pool`, and `PoolJoin` models to manage relationships effectively.
- Updated database schema to include `ib_pool_id` in pools and modified the `domain_inkbunny_pool_joins` table structure.
- Added tests to ensure correct association of posts with pools and validate left/right post IDs in pool joins.
This commit is contained in:
Dylan Knutson
2025-01-05 19:05:08 +00:00
parent 304b9bd5d0
commit b35d6878dd
18 changed files with 716 additions and 38 deletions

View File

@@ -2,6 +2,7 @@
- [ ] Add bookmarking feature for posts across different domains
- [ ] Add search feature to search FA descriptions, tags, E621 descriptions, tags
- [ ] Get inkbunny index scan job working
- [ ] Attach logs to jobs, page to view jobs and their logs
- [x] Get inkbunny index scan job working
- [x] Attach logs to jobs, page to view jobs and their logs
- [ ] Standardize all the embeddings tables to use the same schema (item_id, embedding)
- [ ] Bluesky scraper

View File

@@ -10,7 +10,7 @@ class Domain::Inkbunny::Job::UpdatePostsJob < Domain::Inkbunny::Job::Base
end
ib_post_ids.each_slice(100) do |ib_post_ids_chunk|
process_ib_post_ids_chunk(ib_post_ids_chunk)
process_ib_post_ids(ib_post_ids_chunk)
end
end
@@ -22,9 +22,9 @@ class Domain::Inkbunny::Job::UpdatePostsJob < Domain::Inkbunny::Job::Base
"&show_description=yes&show_writing=yes&show_pools=yes"
end
sig { params(ib_post_ids_chunk: T::Array[Integer]).void }
def process_ib_post_ids_chunk(ib_post_ids_chunk)
url = build_api_submissions_url(ib_post_ids_chunk)
sig { params(ib_post_ids: T::Array[Integer]).void }
def process_ib_post_ids(ib_post_ids)
url = build_api_submissions_url(ib_post_ids)
response = http_client.get(url, caused_by_entry: causing_log_entry)
log_entry = response.log_entry
self.first_log_entry ||= log_entry
@@ -32,13 +32,29 @@ class Domain::Inkbunny::Job::UpdatePostsJob < Domain::Inkbunny::Job::Base
fatal_error("api_submissions failed: #{response.status_code}")
end
api_submissions_json = JSON.parse(response.body)
submissions = api_submissions_json["submissions"]
logger.info("api_submissions page has #{submissions.size} posts")
submissions.each do |submission_json|
submission_jsons = api_submissions_json["submissions"]
logger.info("api_submissions page has #{submission_jsons.size} posts")
missing_pool_post_ib_ids = T::Set[Integer].new
submission_jsons.each do |submission_json|
Domain::Inkbunny::Post.transaction do
deep_update_post_from_submission_json(submission_json, log_entry)
deep_update_post_from_submission_json(
submission_json,
log_entry,
missing_pool_post_ib_ids,
)
end
end
# Enqueue update jobs for missing posts
unless missing_pool_post_ib_ids.empty?
logger.info "enqueuing update jobs for missing posts: #{missing_pool_post_ib_ids.to_a.join(", ")}"
defer_job(
Domain::Inkbunny::Job::UpdatePostsJob,
{ ib_post_ids: missing_pool_post_ib_ids.to_a },
)
end
logger.prefix = ""
end
@@ -46,13 +62,18 @@ class Domain::Inkbunny::Job::UpdatePostsJob < Domain::Inkbunny::Job::Base
params(
submission_json: T::Hash[String, T.untyped],
log_entry: HttpLogEntry,
missing_pool_post_ib_ids: T::Set[Integer],
).void
end
def deep_update_post_from_submission_json(submission_json, log_entry)
def deep_update_post_from_submission_json(
submission_json,
log_entry,
missing_pool_post_ib_ids
)
logger.prefix = "ib_post_id #{submission_json["submission_id"].to_s.bold}"
post =
Domain::Inkbunny::Post.find_by!(
Domain::Inkbunny::Post.includes(:pools).find_by!(
ib_post_id: submission_json["submission_id"],
)
logger.info "deep update post #{post.ib_post_id.to_s.bold}"
@@ -71,6 +92,10 @@ class Domain::Inkbunny::Job::UpdatePostsJob < Domain::Inkbunny::Job::Base
post.keywords = submission_json["keywords"]
post.deep_update_log_entry = log_entry
if pools_json = submission_json["pools"]
update_submission_pools(post, pools_json, missing_pool_post_ib_ids)
end
if submission_json["user_icon_url_large"]
user = T.must(post.creator)
user.avatar_url_str = submission_json["user_icon_url_large"]
@@ -133,4 +158,50 @@ class Domain::Inkbunny::Job::UpdatePostsJob < Domain::Inkbunny::Job::Base
defer_job(Domain::Inkbunny::Job::FileJob, { file: file }, { priority: 1 })
end
end
sig do
params(
post: Domain::Inkbunny::Post,
pools_json: T::Array[T::Hash[String, T.untyped]],
missing_pool_post_ib_ids: T::Set[Integer],
).void
end
def update_submission_pools(post, pools_json, missing_pool_post_ib_ids)
pools_json.each do |pool_json|
left_post, right_post =
%w[
submission_left_submission_id
submission_right_submission_id
].map do |key|
ib_post_id = pool_json[key]&.to_i
next nil if ib_post_id.blank?
p =
Domain::Inkbunny::Post.find_or_initialize_by(
ib_post_id: ib_post_id,
) do |p|
p.creator = post.creator
p.state_detail = { "created_from" => "pool_mention" }
end
if p.new_record?
missing_pool_post_ib_ids.add(ib_post_id)
p.save!
end
p
end
pool =
Domain::Inkbunny::Pool.find_or_initialize_by(
ib_pool_id: pool_json["pool_id"],
)
pool.count = pool_json["count"]&.to_i
pool.name = pool_json["name"]
pool.description = pool_json["description"]
pool.save!
pool_join = post.pool_joins.find_or_initialize_by(pool: pool)
pool_join.left_post = left_post
pool_join.right_post = right_post
pool_join.save!
end
end
end

3
app/lib/sorbet_schema.rb Normal file
View File

@@ -0,0 +1,3 @@
# typed: strict
module SorbetSchema
end

View File

@@ -1,4 +1,9 @@
# typed: strict
class Domain::Inkbunny::Pool < ReduxApplicationRecord
self.table_name = "domain_inkbunny_pools"
has_many :pool_joins, class_name: "::Domain::Inkbunny::PoolJoin"
has_many :posts, through: :pool_joins, source: :post
validates :ib_pool_id, presence: true, uniqueness: true
end

View File

@@ -1,4 +1,17 @@
# typed: strict
class Domain::Inkbunny::PoolJoin < ReduxApplicationRecord
self.table_name = "domain_inkbunny_pool_joins"
belongs_to :pool
belongs_to :post
belongs_to :left_post,
class_name: "Domain::Inkbunny::Post",
foreign_key: :left_post_id,
optional: true
belongs_to :right_post,
class_name: "Domain::Inkbunny::Post",
foreign_key: :right_post_id,
optional: true
end

View File

@@ -38,6 +38,9 @@ class Domain::Inkbunny::Post < ReduxApplicationRecord
photography
]
has_many :pool_joins, class_name: "::Domain::Inkbunny::PoolJoin"
has_many :pools, through: :pool_joins, source: :pool
after_initialize do
self.state = :ok unless self.state.present?
self.state_detail ||= {}

View File

@@ -0,0 +1,5 @@
class DropDomainInkbunnyPoolJoinsForRecreate < ActiveRecord::Migration[7.2]
def change
drop_table :domain_inkbunny_pool_joins
end
end

View File

@@ -0,0 +1,51 @@
# typed: strict
class AddIbPoolIdToPools < ActiveRecord::Migration[7.0]
extend T::Sig
sig { void }
def change
add_column :domain_inkbunny_pools, :ib_pool_id, :bigint
add_index :domain_inkbunny_pools, :ib_pool_id, unique: true
add_column :domain_inkbunny_pools, :count, :integer
# ib_post_id should have been unique, but it wasn't. used for foreign key
# constraint.
add_index :domain_inkbunny_posts,
:ib_post_id,
unique: true,
if_not_exists: true
tablespace_name =
(
if ActiveRecord::Base
.connection
.execute("SELECT 1 FROM pg_tablespace WHERE spcname = 'mirai';")
.any?
"mirai"
else
"pg_default"
end
)
create_table :domain_inkbunny_pool_joins, tablespace: tablespace_name do |t|
t.references :post,
to_table: :domain_inkbunny_posts,
validate: true,
index: false
t.references :pool, to_table: :domain_inkbunny_pools, validate: true
t.references :left_post, to_table: :domain_inkbunny_posts, validate: true
t.references :right_post, to_table: :domain_inkbunny_posts, validate: true
t.index %i[post_id pool_id], unique: true
end
up_only { ActiveRecord::Base.connection.execute(<<-SQL) }
ALTER TABLE domain_inkbunny_pool_joins SET TABLESPACE #{tablespace_name};
ALTER INDEX domain_inkbunny_pool_joins_pkey SET TABLESPACE #{tablespace_name};
ALTER INDEX index_domain_inkbunny_pool_joins_on_post_id_and_pool_id SET TABLESPACE #{tablespace_name};
ALTER INDEX index_domain_inkbunny_pool_joins_on_pool_id SET TABLESPACE #{tablespace_name};
ALTER INDEX index_domain_inkbunny_pool_joins_on_left_post_id SET TABLESPACE #{tablespace_name};
ALTER INDEX index_domain_inkbunny_pool_joins_on_right_post_id SET TABLESPACE #{tablespace_name};
SQL
end
end

26
db/schema.rb generated
View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2025_01_02_185501) do
ActiveRecord::Schema[7.2].define(version: 2025_01_04_211454) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_prewarm"
enable_extension "pg_stat_statements"
@@ -1357,6 +1357,13 @@ ActiveRecord::Schema[7.2].define(version: 2025_01_02_185501) do
t.index ["post_id"], name: "index_domain_fa_post_factors_on_post_id", unique: true
end
create_table "domain_fa_post_factors_new", id: false, force: :cascade do |t|
t.bigint "post_id"
t.vector "for_favorite", limit: 16
t.index ["for_favorite"], name: "domain_fa_post_factors_new_for_favorite_idx", opclass: :vector_cosine_ops, using: :ivfflat
t.index ["post_id"], name: "index_domain_fa_post_factors_new_on_post_id", unique: true
end
create_table "domain_fa_posts", force: :cascade do |t|
t.integer "fa_id"
t.bigint "creator_id"
@@ -1484,11 +1491,14 @@ ActiveRecord::Schema[7.2].define(version: 2025_01_02_185501) do
end
create_table "domain_inkbunny_pool_joins", force: :cascade do |t|
t.bigint "post_id", null: false
t.bigint "pool_id", null: false
t.integer "ordinal"
t.bigint "post_id"
t.bigint "pool_id"
t.bigint "left_post_id"
t.bigint "right_post_id"
t.index ["left_post_id"], name: "index_domain_inkbunny_pool_joins_on_left_post_id"
t.index ["pool_id"], name: "index_domain_inkbunny_pool_joins_on_pool_id"
t.index ["post_id"], name: "index_domain_inkbunny_pool_joins_on_post_id"
t.index ["post_id", "pool_id"], name: "index_domain_inkbunny_pool_joins_on_post_id_and_pool_id", unique: true
t.index ["right_post_id"], name: "index_domain_inkbunny_pool_joins_on_right_post_id"
end
create_table "domain_inkbunny_pools", force: :cascade do |t|
@@ -1496,6 +1506,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_01_02_185501) do
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "ib_pool_id"
t.integer "count"
t.index ["ib_pool_id"], name: "index_domain_inkbunny_pools_on_ib_pool_id", unique: true
end
create_table "domain_inkbunny_posts", force: :cascade do |t|
@@ -1524,6 +1537,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_01_02_185501) do
t.jsonb "keywords"
t.index ["creator_id"], name: "index_domain_inkbunny_posts_on_creator_id"
t.index ["deep_update_log_entry_id"], name: "index_domain_inkbunny_posts_on_deep_update_log_entry_id"
t.index ["ib_post_id"], name: "index_domain_inkbunny_posts_on_ib_post_id", unique: true
t.index ["shallow_update_log_entry_id"], name: "index_domain_inkbunny_posts_on_shallow_update_log_entry_id"
end
@@ -1834,8 +1848,6 @@ ActiveRecord::Schema[7.2].define(version: 2025_01_02_185501) do
add_foreign_key "domain_inkbunny_files", "http_log_entries", column: "log_entry_id"
add_foreign_key "domain_inkbunny_follows", "domain_inkbunny_users", column: "followed_id"
add_foreign_key "domain_inkbunny_follows", "domain_inkbunny_users", column: "follower_id"
add_foreign_key "domain_inkbunny_pool_joins", "domain_inkbunny_pools", column: "pool_id"
add_foreign_key "domain_inkbunny_pool_joins", "domain_inkbunny_posts", column: "post_id"
add_foreign_key "domain_inkbunny_posts", "domain_inkbunny_users", column: "creator_id"
add_foreign_key "domain_inkbunny_posts", "http_log_entries", column: "deep_update_log_entry_id"
add_foreign_key "domain_inkbunny_posts", "http_log_entries", column: "shallow_update_log_entry_id"

View File

@@ -6,6 +6,7 @@
class Domain::Inkbunny::Pool
include GeneratedAssociationMethods
include GeneratedAttributeMethods
extend CommonRelationMethods
extend GeneratedRelationMethods
@@ -414,6 +415,36 @@ class Domain::Inkbunny::Pool
def third_to_last!; end
end
module GeneratedAssociationMethods
sig { returns(T::Array[T.untyped]) }
def pool_join_ids; end
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
def pool_join_ids=(ids); end
# This method is created by ActiveRecord on the `Domain::Inkbunny::Pool` class because it declared `has_many :pool_joins`.
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
sig { returns(::Domain::Inkbunny::PoolJoin::PrivateCollectionProxy) }
def pool_joins; end
sig { params(value: T::Enumerable[::Domain::Inkbunny::PoolJoin]).void }
def pool_joins=(value); end
sig { returns(T::Array[T.untyped]) }
def post_ids; end
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
def post_ids=(ids); end
# This method is created by ActiveRecord on the `Domain::Inkbunny::Pool` class because it declared `has_many :posts, through: :pool_joins`.
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
sig { returns(::Domain::Inkbunny::Post::PrivateCollectionProxy) }
def posts; end
sig { params(value: T::Enumerable[::Domain::Inkbunny::Post]).void }
def posts=(value); end
end
module GeneratedAssociationRelationMethods
sig { returns(PrivateAssociationRelation) }
def all; end
@@ -559,6 +590,51 @@ class Domain::Inkbunny::Pool
end
module GeneratedAttributeMethods
sig { returns(T.nilable(::Integer)) }
def count; end
sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) }
def count=(value); end
sig { returns(T::Boolean) }
def count?; end
sig { returns(T.nilable(::Integer)) }
def count_before_last_save; end
sig { returns(T.untyped) }
def count_before_type_cast; end
sig { returns(T::Boolean) }
def count_came_from_user?; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def count_change; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def count_change_to_be_saved; end
sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) }
def count_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::Integer)) }
def count_in_database; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def count_previous_change; end
sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) }
def count_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::Integer)) }
def count_previously_was; end
sig { returns(T.nilable(::Integer)) }
def count_was; end
sig { void }
def count_will_change!; end
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
def created_at; end
@@ -659,6 +735,51 @@ class Domain::Inkbunny::Pool
sig { void }
def description_will_change!; end
sig { returns(T.nilable(::Integer)) }
def ib_pool_id; end
sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) }
def ib_pool_id=(value); end
sig { returns(T::Boolean) }
def ib_pool_id?; end
sig { returns(T.nilable(::Integer)) }
def ib_pool_id_before_last_save; end
sig { returns(T.untyped) }
def ib_pool_id_before_type_cast; end
sig { returns(T::Boolean) }
def ib_pool_id_came_from_user?; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def ib_pool_id_change; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def ib_pool_id_change_to_be_saved; end
sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) }
def ib_pool_id_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::Integer)) }
def ib_pool_id_in_database; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def ib_pool_id_previous_change; end
sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) }
def ib_pool_id_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::Integer)) }
def ib_pool_id_previously_was; end
sig { returns(T.nilable(::Integer)) }
def ib_pool_id_was; end
sig { void }
def ib_pool_id_will_change!; end
sig { returns(T.nilable(::Integer)) }
def id; end
@@ -794,12 +915,18 @@ class Domain::Inkbunny::Pool
sig { void }
def name_will_change!; end
sig { void }
def restore_count!; end
sig { void }
def restore_created_at!; end
sig { void }
def restore_description!; end
sig { void }
def restore_ib_pool_id!; end
sig { void }
def restore_id!; end
@@ -812,6 +939,12 @@ class Domain::Inkbunny::Pool
sig { void }
def restore_updated_at!; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def saved_change_to_count; end
sig { returns(T::Boolean) }
def saved_change_to_count?; end
sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) }
def saved_change_to_created_at; end
@@ -824,6 +957,12 @@ class Domain::Inkbunny::Pool
sig { returns(T::Boolean) }
def saved_change_to_description?; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def saved_change_to_ib_pool_id; end
sig { returns(T::Boolean) }
def saved_change_to_ib_pool_id?; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def saved_change_to_id; end
@@ -903,12 +1042,18 @@ class Domain::Inkbunny::Pool
sig { void }
def updated_at_will_change!; end
sig { returns(T::Boolean) }
def will_save_change_to_count?; end
sig { returns(T::Boolean) }
def will_save_change_to_created_at?; end
sig { returns(T::Boolean) }
def will_save_change_to_description?; end
sig { returns(T::Boolean) }
def will_save_change_to_ib_pool_id?; end
sig { returns(T::Boolean) }
def will_save_change_to_id?; end

View File

@@ -6,6 +6,7 @@
class Domain::Inkbunny::PoolJoin
include GeneratedAssociationMethods
include GeneratedAttributeMethods
extend CommonRelationMethods
extend GeneratedRelationMethods
@@ -419,6 +420,116 @@ class Domain::Inkbunny::PoolJoin
def third_to_last!; end
end
module GeneratedAssociationMethods
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Post) }
def build_left_post(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Pool) }
def build_pool(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Post) }
def build_post(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Post) }
def build_right_post(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Post) }
def create_left_post(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Post) }
def create_left_post!(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Pool) }
def create_pool(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Pool) }
def create_pool!(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Post) }
def create_post(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Post) }
def create_post!(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Post) }
def create_right_post(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::Inkbunny::Post) }
def create_right_post!(*args, &blk); end
sig { returns(T.nilable(::Domain::Inkbunny::Post)) }
def left_post; end
sig { params(value: T.nilable(::Domain::Inkbunny::Post)).void }
def left_post=(value); end
sig { returns(T::Boolean) }
def left_post_changed?; end
sig { returns(T::Boolean) }
def left_post_previously_changed?; end
sig { returns(T.nilable(::Domain::Inkbunny::Pool)) }
def pool; end
sig { params(value: T.nilable(::Domain::Inkbunny::Pool)).void }
def pool=(value); end
sig { returns(T::Boolean) }
def pool_changed?; end
sig { returns(T::Boolean) }
def pool_previously_changed?; end
sig { returns(T.nilable(::Domain::Inkbunny::Post)) }
def post; end
sig { params(value: T.nilable(::Domain::Inkbunny::Post)).void }
def post=(value); end
sig { returns(T::Boolean) }
def post_changed?; end
sig { returns(T::Boolean) }
def post_previously_changed?; end
sig { returns(T.nilable(::Domain::Inkbunny::Post)) }
def reload_left_post; end
sig { returns(T.nilable(::Domain::Inkbunny::Pool)) }
def reload_pool; end
sig { returns(T.nilable(::Domain::Inkbunny::Post)) }
def reload_post; end
sig { returns(T.nilable(::Domain::Inkbunny::Post)) }
def reload_right_post; end
sig { void }
def reset_left_post; end
sig { void }
def reset_pool; end
sig { void }
def reset_post; end
sig { void }
def reset_right_post; end
sig { returns(T.nilable(::Domain::Inkbunny::Post)) }
def right_post; end
sig { params(value: T.nilable(::Domain::Inkbunny::Post)).void }
def right_post=(value); end
sig { returns(T::Boolean) }
def right_post_changed?; end
sig { returns(T::Boolean) }
def right_post_previously_changed?; end
end
module GeneratedAssociationRelationMethods
sig { returns(PrivateAssociationRelation) }
def all; end
@@ -655,49 +766,49 @@ class Domain::Inkbunny::PoolJoin
def id_will_change!; end
sig { returns(T.nilable(::Integer)) }
def ordinal; end
def left_post_id; end
sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) }
def ordinal=(value); end
def left_post_id=(value); end
sig { returns(T::Boolean) }
def ordinal?; end
def left_post_id?; end
sig { returns(T.nilable(::Integer)) }
def ordinal_before_last_save; end
def left_post_id_before_last_save; end
sig { returns(T.untyped) }
def ordinal_before_type_cast; end
def left_post_id_before_type_cast; end
sig { returns(T::Boolean) }
def ordinal_came_from_user?; end
def left_post_id_came_from_user?; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def ordinal_change; end
def left_post_id_change; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def ordinal_change_to_be_saved; end
def left_post_id_change_to_be_saved; end
sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) }
def ordinal_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
def left_post_id_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::Integer)) }
def ordinal_in_database; end
def left_post_id_in_database; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def ordinal_previous_change; end
def left_post_id_previous_change; end
sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) }
def ordinal_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
def left_post_id_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::Integer)) }
def ordinal_previously_was; end
def left_post_id_previously_was; end
sig { returns(T.nilable(::Integer)) }
def ordinal_was; end
def left_post_id_was; end
sig { void }
def ordinal_will_change!; end
def left_post_id_will_change!; end
sig { returns(T.nilable(::Integer)) }
def pool_id; end
@@ -796,7 +907,7 @@ class Domain::Inkbunny::PoolJoin
def restore_id_value!; end
sig { void }
def restore_ordinal!; end
def restore_left_post_id!; end
sig { void }
def restore_pool_id!; end
@@ -804,6 +915,54 @@ class Domain::Inkbunny::PoolJoin
sig { void }
def restore_post_id!; end
sig { void }
def restore_right_post_id!; end
sig { returns(T.nilable(::Integer)) }
def right_post_id; end
sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) }
def right_post_id=(value); end
sig { returns(T::Boolean) }
def right_post_id?; end
sig { returns(T.nilable(::Integer)) }
def right_post_id_before_last_save; end
sig { returns(T.untyped) }
def right_post_id_before_type_cast; end
sig { returns(T::Boolean) }
def right_post_id_came_from_user?; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def right_post_id_change; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def right_post_id_change_to_be_saved; end
sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) }
def right_post_id_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::Integer)) }
def right_post_id_in_database; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def right_post_id_previous_change; end
sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) }
def right_post_id_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::Integer)) }
def right_post_id_previously_was; end
sig { returns(T.nilable(::Integer)) }
def right_post_id_was; end
sig { void }
def right_post_id_will_change!; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def saved_change_to_id; end
@@ -817,10 +976,10 @@ class Domain::Inkbunny::PoolJoin
def saved_change_to_id_value?; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def saved_change_to_ordinal; end
def saved_change_to_left_post_id; end
sig { returns(T::Boolean) }
def saved_change_to_ordinal?; end
def saved_change_to_left_post_id?; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def saved_change_to_pool_id; end
@@ -834,6 +993,12 @@ class Domain::Inkbunny::PoolJoin
sig { returns(T::Boolean) }
def saved_change_to_post_id?; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def saved_change_to_right_post_id; end
sig { returns(T::Boolean) }
def saved_change_to_right_post_id?; end
sig { returns(T::Boolean) }
def will_save_change_to_id?; end
@@ -841,13 +1006,16 @@ class Domain::Inkbunny::PoolJoin
def will_save_change_to_id_value?; end
sig { returns(T::Boolean) }
def will_save_change_to_ordinal?; end
def will_save_change_to_left_post_id?; end
sig { returns(T::Boolean) }
def will_save_change_to_pool_id?; end
sig { returns(T::Boolean) }
def will_save_change_to_post_id?; end
sig { returns(T::Boolean) }
def will_save_change_to_right_post_id?; end
end
module GeneratedRelationMethods

View File

@@ -628,6 +628,34 @@ class Domain::Inkbunny::Post
sig { params(value: T.nilable(::IndexedPost)).void }
def indexed_post=(value); end
sig { returns(T::Array[T.untyped]) }
def pool_ids; end
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
def pool_ids=(ids); end
sig { returns(T::Array[T.untyped]) }
def pool_join_ids; end
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
def pool_join_ids=(ids); end
# This method is created by ActiveRecord on the `Domain::Inkbunny::Post` class because it declared `has_many :pool_joins`.
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
sig { returns(::Domain::Inkbunny::PoolJoin::PrivateCollectionProxy) }
def pool_joins; end
sig { params(value: T::Enumerable[::Domain::Inkbunny::PoolJoin]).void }
def pool_joins=(value); end
# This method is created by ActiveRecord on the `Domain::Inkbunny::Post` class because it declared `has_many :pools, through: :pool_joins`.
# 🔗 [Rails guide for `has_many_through` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association)
sig { returns(::Domain::Inkbunny::Pool::PrivateCollectionProxy) }
def pools; end
sig { params(value: T::Enumerable[::Domain::Inkbunny::Pool]).void }
def pools=(value); end
sig { returns(T.nilable(::Domain::Inkbunny::User)) }
def reload_creator; end

View File

@@ -87,6 +87,9 @@ module GeneratedPathHelpersModule
sig { params(args: T.untyped).returns(String) }
def good_job_path(*args); end
sig { params(args: T.untyped).returns(String) }
def grafana_path(*args); end
sig { params(args: T.untyped).returns(String) }
def ib_cookies_edit_global_states_path(*args); end
@@ -123,6 +126,9 @@ module GeneratedPathHelpersModule
sig { params(args: T.untyped).returns(String) }
def pg_hero_path(*args); end
sig { params(args: T.untyped).returns(String) }
def prometheus_path(*args); end
sig { params(args: T.untyped).returns(String) }
def rails_blob_path(*args); end

View File

@@ -87,6 +87,9 @@ module GeneratedUrlHelpersModule
sig { params(args: T.untyped).returns(String) }
def good_job_url(*args); end
sig { params(args: T.untyped).returns(String) }
def grafana_url(*args); end
sig { params(args: T.untyped).returns(String) }
def ib_cookies_edit_global_states_url(*args); end
@@ -123,6 +126,9 @@ module GeneratedUrlHelpersModule
sig { params(args: T.untyped).returns(String) }
def pg_hero_url(*args); end
sig { params(args: T.untyped).returns(String) }
def prometheus_url(*args); end
sig { params(args: T.untyped).returns(String) }
def rails_blob_representation_proxy_url(*args); end

View File

@@ -526,6 +526,9 @@ class GoodJob::Job
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
def active_job_id(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
def adapter_class(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
def advisory_lock(*args, &blk); end
@@ -2154,6 +2157,9 @@ class GoodJob::Job
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
def active_job_id(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
def adapter_class(*args, &blk); end
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
def advisory_lock(*args, &blk); end

View File

@@ -0,0 +1,45 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for dynamic methods in `Rails::ApplicationController`.
# Please instead update this file by running `bin/tapioca dsl Rails::ApplicationController`.
class Rails::ApplicationController
include GeneratedUrlHelpersModule
include GeneratedPathHelpersModule
sig { returns(HelperProxy) }
def helpers; end
module HelperMethods
include ::Turbo::DriveHelper
include ::Turbo::FramesHelper
include ::Turbo::IncludesHelper
include ::Turbo::StreamsHelper
include ::ActionView::Helpers::CaptureHelper
include ::ActionView::Helpers::OutputSafetyHelper
include ::ActionView::Helpers::TagHelper
include ::Turbo::Streams::ActionHelper
include ::ActionText::ContentHelper
include ::ActionText::TagHelper
include ::Webpacker::Helper
include ::ActionController::Base::HelperMethods
include ::ApplicationHelper
include ::Domain::E621::PostsHelper
include ::Domain::Fa::PostsHelper
include ::Domain::Fa::UsersHelper
include ::GoodJobHelper
include ::IndexablePostsHelper
include ::LogEntriesHelper
include ::SourceHelper
include ::DeviseHelper
include ::ReactOnRails::Utils::Required
include ::ReactOnRails::Helper
include ::ReactOnRailsHelper
end
class HelperProxy < ::ActionView::Base
include HelperMethods
end
end

View File

@@ -0,0 +1,45 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for dynamic methods in `Rails::HealthController`.
# Please instead update this file by running `bin/tapioca dsl Rails::HealthController`.
class Rails::HealthController
include GeneratedUrlHelpersModule
include GeneratedPathHelpersModule
sig { returns(HelperProxy) }
def helpers; end
module HelperMethods
include ::Turbo::DriveHelper
include ::Turbo::FramesHelper
include ::Turbo::IncludesHelper
include ::Turbo::StreamsHelper
include ::ActionView::Helpers::CaptureHelper
include ::ActionView::Helpers::OutputSafetyHelper
include ::ActionView::Helpers::TagHelper
include ::Turbo::Streams::ActionHelper
include ::ActionText::ContentHelper
include ::ActionText::TagHelper
include ::Webpacker::Helper
include ::ActionController::Base::HelperMethods
include ::ApplicationHelper
include ::Domain::E621::PostsHelper
include ::Domain::Fa::PostsHelper
include ::Domain::Fa::UsersHelper
include ::GoodJobHelper
include ::IndexablePostsHelper
include ::LogEntriesHelper
include ::SourceHelper
include ::DeviseHelper
include ::ReactOnRails::Utils::Required
include ::ReactOnRails::Helper
include ::ReactOnRailsHelper
end
class HelperProxy < ::ActionView::Base
include HelperMethods
end
end

View File

@@ -156,6 +156,71 @@ describe Domain::Inkbunny::Job::UpdatePostsJob do
],
)
end
it "associates posts with the correct pools" do
perform_now({ ib_post_ids: ib_post_ids, caused_by_entry: nil })
# Post 3104202 should be in "Phantom Touch" pool
post_3104202.reload
expect(post_3104202.pool_joins.count).to eq(1)
phantom_touch_pool = post_3104202.pools.first
expect(phantom_touch_pool.ib_pool_id).to eq(83_746)
expect(phantom_touch_pool.name).to eq("Phantom Touch | Ongoing")
expect(phantom_touch_pool.description).to eq(
"18+ M/F Adult Comic\n\n(New Page Every Sunday)",
)
expect(phantom_touch_pool.count).to eq(26)
# Post 3104197 should be in 3 pools
post_3104197.reload
expect(post_3104197.pool_joins.count).to eq(3)
pool_ids = post_3104197.pools.pluck(:ib_pool_id).sort
expect(pool_ids).to eq([35_045, 35_628, 71_061])
animation_pool = post_3104197.pools.find_by(ib_pool_id: 71_061)
expect(animation_pool.name).to eq("Animation")
expect(animation_pool.description).to eq("It moves!")
# Post 3104200 should have no pools
post_3104200.reload
expect(post_3104200.pool_joins.count).to eq(0)
expect(post_3104200.pools).to be_empty
end
it "sets correct left/right post IDs in pool joins" do
perform_now({ ib_post_ids: ib_post_ids, caused_by_entry: nil })
# Check post 3104197's pool joins
post_3104197.reload
animation_pool_join =
post_3104197.pool_joins.find_by(
pool: Domain::Inkbunny::Pool.find_by!(ib_pool_id: 71_061),
)
expect(animation_pool_join.left_post.ib_post_id).to eq(3_082_162)
expect(animation_pool_join.right_post).to be_nil
# Check post 3104202's pool joins
post_3104202.reload
phantom_touch_pool_join = post_3104202.pool_joins.first
expect(phantom_touch_pool_join.left_post.ib_post_id).to eq(3_098_688)
expect(phantom_touch_pool_join.right_post).to be_nil
end
it "enqueues update jobs for missing left/right posts" do
perform_now({ ib_post_ids: ib_post_ids, caused_by_entry: nil })
# Should enqueue jobs for posts 3082162 and 3095240
update_jobs =
SpecUtil.enqueued_jobs(Domain::Inkbunny::Job::UpdatePostsJob)
update_job_post_ids =
update_jobs.map { |job| job[:args][0][:ib_post_ids] }.flatten.sort
expect(update_job_post_ids).to match_array(
[3_082_162, 3_095_240, 3_101_532, 3_098_688],
)
update_jobs.each do |job|
expect(job[:args][0][:caused_by_entry]).to eq(log_entries[0])
end
end
end
context "when a post's files change" do