diff --git a/app/models/domain/post_file.rb b/app/models/domain/post_file.rb index 8b8562c1..0bfb1ac3 100644 --- a/app/models/domain/post_file.rb +++ b/app/models/domain/post_file.rb @@ -13,7 +13,8 @@ class Domain::PostFile < ReduxApplicationRecord has_many :thumbnails, class_name: "::Domain::PostFileThumbnail", foreign_key: :post_file_id, - dependent: :destroy + dependent: :destroy, + inverse_of: :post_file attr_json :state, :string attr_json :url_str, :string diff --git a/app/models/domain/post_file_thumbnail.rb b/app/models/domain/post_file_thumbnail.rb index d9a9fb87..a6e78671 100644 --- a/app/models/domain/post_file_thumbnail.rb +++ b/app/models/domain/post_file_thumbnail.rb @@ -5,7 +5,8 @@ class Domain::PostFileThumbnail < ReduxApplicationRecord belongs_to :post_file, foreign_key: :post_file_id, - class_name: "::Domain::PostFile" + class_name: "::Domain::PostFile", + inverse_of: :thumbnails has_many :perceptual_hashes, class_name: "::Domain::PerceptualHash", @@ -18,30 +19,84 @@ class Domain::PostFileThumbnail < ReduxApplicationRecord scope: :post_file_id, }, inclusion: { - in: THUMBNAIL_TYPES.keys, + in: Domain::ThumbnailType.values.map(&:name), } - # Thumbnail types for different uses - THUMBNAIL_TYPES = + TMP_DIR = T.let(File.join(BlobFile::ROOT_DIR, "tmp-files"), String) + + THUMBNAIL_ROOT_DIR = + T.let(File.join(BlobFile::ROOT_DIR, "post_file_thumbnails"), String) + + THUMBNAIL_CONTENT_TYPES = T.let( - { - small: { - width: 128, - height: 128, - }, - medium: { - width: 256, - height: 256, - }, - large: { - width: 512, - height: 512, - }, - phash: { - width: 64, - height: 64, - }, # Special size for perceptual hashing - }, - T::Hash[Symbol, T::Hash[Symbol, Integer]], + [ + %r{image/jpeg}, + %r{image/jpg}, + %r{image/png}, + %r{image/gif}, + %r{image/webp}, + ], + T::Array[Regexp], ) + + sig { returns(T.nilable(String)) } + def absolute_file_path + return nil unless thumbnail_type = self.thumbnail_type + return nil unless post_file_id = self.post_file&.id + return nil unless sha256 = self.post_file&.blob_sha256 + sha256_hex = HexUtil.bin2hex(sha256) + path_segments = [ + THUMBNAIL_ROOT_DIR, + thumbnail_type, + *BlobFile.path_segments([2, 2, 1], sha256_hex), + ] + path_segments[-1] = "#{path_segments[-1]}.jpeg" + path_segments.join("/") + end + + sig do + params( + post_file: Domain::PostFile, + thumbnail_type: Domain::ThumbnailType, + ).returns(T.nilable(Domain::PostFileThumbnail)) + end + def self.find_or_create_from_post_file(post_file, thumbnail_type) + if t = find_by(post_file: post_file, thumbnail_type: thumbnail_type.name) + return t + end + return nil unless post_file.state_ok? + return nil unless log_entry = post_file.log_entry + unless THUMBNAIL_CONTENT_TYPES.any? { |regex| + regex.match?(log_entry.content_type) + } + return nil + end + + file_path = post_file.blob&.absolute_file_path + return nil unless file_path + + thumbnail = + Domain::PostFileThumbnail.new( + post_file: post_file, + thumbnail_type: thumbnail_type.name, + ) + + thumbnail_path = thumbnail.absolute_file_path.to_s + unless File.exist?(thumbnail_path) + FileUtils.mkdir_p(File.dirname(thumbnail_path)) + tmp_file_path = File.join(TMP_DIR, "thumbnail-#{SecureRandom.uuid}.jpeg") + image_data = + Vips::Image.thumbnail( + file_path, + thumbnail_type.width, + height: thumbnail_type.height, + size: :force, + ) + image_data.jpegsave(tmp_file_path, Q: thumbnail_type.quality, strip: true) + FileUtils.mv(tmp_file_path, thumbnail_path) + end + + thumbnail.save! + thumbnail + end end diff --git a/app/models/domain/thumbnail_type.rb b/app/models/domain/thumbnail_type.rb new file mode 100644 index 00000000..df1aadba --- /dev/null +++ b/app/models/domain/thumbnail_type.rb @@ -0,0 +1,50 @@ +# typed: strict +# Thumbnail types for different uses +class Domain::ThumbnailType < T::Enum + extend T::Sig + + enums do + Small = new + Medium = new + Large = new + PHash = new + end + + sig { returns(String) } + def name + case self + when Small + "small" + when Medium + "medium" + when Large + "large" + when PHash + "phash" + end + end + + sig { returns(Integer) } + def width + case self + when Small + 128 + when Medium + 256 + when Large + 512 + when PHash + 64 + end + end + + sig { returns(Integer) } + def height + width + end + + sig { returns(Integer) } + def quality + 70 + end +end diff --git a/app/models/ip_address_role.rb b/app/models/ip_address_role.rb index b7b725a2..2f8389e7 100644 --- a/app/models/ip_address_role.rb +++ b/app/models/ip_address_role.rb @@ -24,6 +24,16 @@ class IpAddressRole < ReduxApplicationRecord end end + sig { returns(T::Boolean) } + def admin? + false + end + + sig { returns(T::Boolean) } + def moderator? + false + end + private # Custom validation to prevent overlapping IP ranges diff --git a/app/policies/state/ip_address_role_policy.rb b/app/policies/state/ip_address_role_policy.rb index b4dc104b..da4da1bd 100644 --- a/app/policies/state/ip_address_role_policy.rb +++ b/app/policies/state/ip_address_role_policy.rb @@ -57,7 +57,7 @@ class State::IpAddressRolePolicy < ApplicationPolicy sig { returns(T.untyped) } def resolve - if @user&.admin? || @controller.current_ip_address_role&.admin? + if @user&.admin? @scope else @scope.where(id: nil) # Returns empty relation diff --git a/config/blob_file_location.yml b/config/blob_file_location.yml index f4c22528..f2370779 100644 --- a/config/blob_file_location.yml +++ b/config/blob_file_location.yml @@ -1,14 +1,9 @@ -test: - tmp/blob_files_test +test: tmp/blob_files_test -development: - /mnt/blob_files_development +development: /mnt/blob_files_development -staging: - /mnt/blob_files_production +staging: /mnt/blob_files_production -production: - /mnt/blob_files_production +production: /mnt/blob_files_production -worker: - /mnt/blob_files_production +worker: /mnt/blob_files_production diff --git a/db/structure.sql b/db/structure.sql index 9a4579cd..7a86a831 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -2696,6 +2696,71 @@ CREATE SEQUENCE public.domain_inkbunny_users_id_seq ALTER SEQUENCE public.domain_inkbunny_users_id_seq OWNED BY public.domain_inkbunny_users.id; +-- +-- Name: domain_perceptual_hashes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.domain_perceptual_hashes ( + id bigint NOT NULL, + thumbnail_id bigint NOT NULL, + algorithm character varying NOT NULL, + hash_value public.vector, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: domain_perceptual_hashes_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.domain_perceptual_hashes_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: domain_perceptual_hashes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.domain_perceptual_hashes_id_seq OWNED BY public.domain_perceptual_hashes.id; + + +-- +-- Name: domain_post_file_thumbnails; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.domain_post_file_thumbnails ( + id bigint NOT NULL, + post_file_id bigint NOT NULL, + thumbnail_type character varying NOT NULL, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: domain_post_file_thumbnails_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.domain_post_file_thumbnails_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: domain_post_file_thumbnails_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.domain_post_file_thumbnails_id_seq OWNED BY public.domain_post_file_thumbnails.id; + + SET default_tablespace = mirai; -- @@ -4588,6 +4653,20 @@ ALTER TABLE ONLY public.domain_inkbunny_tags ALTER COLUMN id SET DEFAULT nextval ALTER TABLE ONLY public.domain_inkbunny_users ALTER COLUMN id SET DEFAULT nextval('public.domain_inkbunny_users_id_seq'::regclass); +-- +-- Name: domain_perceptual_hashes id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.domain_perceptual_hashes ALTER COLUMN id SET DEFAULT nextval('public.domain_perceptual_hashes_id_seq'::regclass); + + +-- +-- Name: domain_post_file_thumbnails id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.domain_post_file_thumbnails ALTER COLUMN id SET DEFAULT nextval('public.domain_post_file_thumbnails_id_seq'::regclass); + + -- -- Name: domain_post_files id; Type: DEFAULT; Schema: public; Owner: - -- @@ -5370,6 +5449,22 @@ ALTER TABLE ONLY public.domain_inkbunny_users ADD CONSTRAINT domain_inkbunny_users_pkey PRIMARY KEY (id); +-- +-- Name: domain_perceptual_hashes domain_perceptual_hashes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.domain_perceptual_hashes + ADD CONSTRAINT domain_perceptual_hashes_pkey PRIMARY KEY (id); + + +-- +-- Name: domain_post_file_thumbnails domain_post_file_thumbnails_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.domain_post_file_thumbnails + ADD CONSTRAINT domain_post_file_thumbnails_pkey PRIMARY KEY (id); + + SET default_tablespace = mirai; -- @@ -6971,6 +7066,34 @@ CREATE UNIQUE INDEX index_domain_inkbunny_users_on_ib_user_id ON public.domain_i CREATE INDEX index_domain_inkbunny_users_on_shallow_update_log_entry_id ON public.domain_inkbunny_users USING btree (shallow_update_log_entry_id); +-- +-- Name: index_domain_perceptual_hashes_on_algorithm; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_domain_perceptual_hashes_on_algorithm ON public.domain_perceptual_hashes USING btree (algorithm); + + +-- +-- Name: index_domain_perceptual_hashes_on_thumbnail_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_domain_perceptual_hashes_on_thumbnail_id ON public.domain_perceptual_hashes USING btree (thumbnail_id); + + +-- +-- Name: index_domain_post_file_thumbnails_on_post_file_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_domain_post_file_thumbnails_on_post_file_id ON public.domain_post_file_thumbnails USING btree (post_file_id); + + +-- +-- Name: index_domain_post_file_thumbnails_on_thumbnail_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_domain_post_file_thumbnails_on_thumbnail_type ON public.domain_post_file_thumbnails USING btree (thumbnail_type); + + SET default_tablespace = mirai; -- @@ -8434,6 +8557,14 @@ ALTER TABLE ONLY public.domain_fa_follows ADD CONSTRAINT fk_rails_175679b7a2 FOREIGN KEY (followed_id) REFERENCES public.domain_fa_users(id); +-- +-- Name: domain_perceptual_hashes fk_rails_1ae1a89060; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.domain_perceptual_hashes + ADD CONSTRAINT fk_rails_1ae1a89060 FOREIGN KEY (thumbnail_id) REFERENCES public.domain_post_file_thumbnails(id); + + -- -- Name: domain_post_group_joins fk_rails_22154fb920; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -8722,6 +8853,14 @@ ALTER TABLE ONLY public.domain_e621_taggings ADD CONSTRAINT fk_rails_da3a488297 FOREIGN KEY (post_id) REFERENCES public.domain_e621_posts(id); +-- +-- Name: domain_post_file_thumbnails fk_rails_dde88b4af5; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.domain_post_file_thumbnails + ADD CONSTRAINT fk_rails_dde88b4af5 FOREIGN KEY (post_file_id) REFERENCES public.domain_post_files(id); + + -- -- Name: domain_inkbunny_follows fk_rails_dffb743e89; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -8786,6 +8925,8 @@ SET search_path TO "$user", public; INSERT INTO "schema_migrations" (version) VALUES ('20250302074924'), +('20250301000002'), +('20250301000001'), ('20250226003653'), ('20250222035939'), ('20250206224121'), diff --git a/sorbet/rbi/dsl/application_controller.rbi b/sorbet/rbi/dsl/application_controller.rbi index 5d7bb2f3..51bb2820 100644 --- a/sorbet/rbi/dsl/application_controller.rbi +++ b/sorbet/rbi/dsl/application_controller.rbi @@ -65,7 +65,7 @@ class ApplicationController sig { params(scope: T.untyped).returns(T.untyped) } def pundit_policy_scope(scope); end - sig { returns(T.untyped) } + sig { returns(T.nilable(T.any(::IpAddressRole, ::User))) } def pundit_user; end end diff --git a/sorbet/rbi/dsl/domain/perceptual_hash.rbi b/sorbet/rbi/dsl/domain/perceptual_hash.rbi new file mode 100644 index 00000000..32accdcd --- /dev/null +++ b/sorbet/rbi/dsl/domain/perceptual_hash.rbi @@ -0,0 +1,1407 @@ +# typed: true + +# DO NOT EDIT MANUALLY +# This is an autogenerated file for dynamic methods in `Domain::PerceptualHash`. +# Please instead update this file by running `bin/tapioca dsl Domain::PerceptualHash`. + + +class Domain::PerceptualHash + include GeneratedAssociationMethods + include GeneratedAttributeMethods + extend CommonRelationMethods + extend GeneratedRelationMethods + + sig { returns(ColorLogger) } + def logger; end + + private + + sig { returns(NilClass) } + def to_ary; end + + class << self + sig do + params( + name: Symbol, + type: T.any(Symbol, ActiveModel::Type::Value), + options: T.nilable(T::Hash[Symbol, T.untyped]) + ).void + end + def attr_json(name, type, options = nil); end + + sig do + params( + default_container_attribute: T.nilable(Symbol), + bad_cast: T.nilable(Symbol), + unknown_key: T.nilable(Symbol) + ).void + end + def attr_json_config(default_container_attribute: nil, bad_cast: nil, unknown_key: nil); end + + sig { returns(T::Array[Symbol]) } + def attr_json_registry; end + + sig { returns(ColorLogger) } + def logger; end + + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + def new(attributes = nil, &block); end + end + + module CommonRelationMethods + sig do + params( + block: T.nilable(T.proc.params(record: ::Domain::PerceptualHash).returns(T.untyped)) + ).returns(T::Boolean) + end + def any?(&block); end + + sig { params(column_name: T.any(String, Symbol)).returns(T.any(Integer, Float, BigDecimal)) } + def average(column_name); end + + sig do + params( + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(T::Array[::Domain::PerceptualHash]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + def build(attributes = nil, &block); end + + sig { params(operation: Symbol, column_name: T.any(String, Symbol)).returns(T.any(Integer, Float, BigDecimal)) } + def calculate(operation, column_name); end + + sig { params(column_name: T.nilable(T.any(String, Symbol))).returns(Integer) } + sig { params(column_name: NilClass, block: T.proc.params(object: ::Domain::PerceptualHash).void).returns(Integer) } + def count(column_name = nil, &block); end + + sig do + params( + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(T::Array[::Domain::PerceptualHash]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + def create(attributes = nil, &block); end + + sig do + params( + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(T::Array[::Domain::PerceptualHash]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + def create!(attributes = nil, &block); end + + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(T::Array[::Domain::PerceptualHash]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + def create_or_find_by(attributes, &block); end + + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(T::Array[::Domain::PerceptualHash]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + def create_or_find_by!(attributes, &block); end + + sig { returns(T::Array[::Domain::PerceptualHash]) } + def destroy_all; end + + sig { params(conditions: T.untyped).returns(T::Boolean) } + def exists?(conditions = :none); end + + sig { returns(T.nilable(::Domain::PerceptualHash)) } + def fifth; end + + sig { returns(::Domain::PerceptualHash) } + def fifth!; end + + sig do + params( + args: T.any(String, Symbol, ::ActiveSupport::Multibyte::Chars, T::Boolean, BigDecimal, Numeric, ::ActiveRecord::Type::Binary::Data, ::ActiveRecord::Type::Time::Value, Date, Time, ::ActiveSupport::Duration, T::Class[T.anything]) + ).returns(::Domain::PerceptualHash) + end + sig do + params( + args: T::Array[T.any(String, Symbol, ::ActiveSupport::Multibyte::Chars, T::Boolean, BigDecimal, Numeric, ::ActiveRecord::Type::Binary::Data, ::ActiveRecord::Type::Time::Value, Date, Time, ::ActiveSupport::Duration, T::Class[T.anything])] + ).returns(T::Enumerable[::Domain::PerceptualHash]) + end + sig do + params( + args: NilClass, + block: T.proc.params(object: ::Domain::PerceptualHash).void + ).returns(T.nilable(::Domain::PerceptualHash)) + end + def find(args = nil, &block); end + + sig { params(args: T.untyped).returns(T.nilable(::Domain::PerceptualHash)) } + def find_by(*args); end + + sig { params(args: T.untyped).returns(::Domain::PerceptualHash) } + def find_by!(*args); end + + sig do + params( + start: T.untyped, + finish: T.untyped, + batch_size: Integer, + error_on_ignore: T.untyped, + order: Symbol, + block: T.proc.params(object: ::Domain::PerceptualHash).void + ).void + end + sig do + params( + start: T.untyped, + finish: T.untyped, + batch_size: Integer, + error_on_ignore: T.untyped, + order: Symbol + ).returns(T::Enumerator[::Domain::PerceptualHash]) + end + def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc, &block); end + + sig do + params( + start: T.untyped, + finish: T.untyped, + batch_size: Integer, + error_on_ignore: T.untyped, + order: Symbol, + block: T.proc.params(object: T::Array[::Domain::PerceptualHash]).void + ).void + end + sig do + params( + start: T.untyped, + finish: T.untyped, + batch_size: Integer, + error_on_ignore: T.untyped, + order: Symbol + ).returns(T::Enumerator[T::Enumerator[::Domain::PerceptualHash]]) + end + def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc, &block); end + + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(T::Array[::Domain::PerceptualHash]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + def find_or_create_by(attributes, &block); end + + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(T::Array[::Domain::PerceptualHash]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + def find_or_create_by!(attributes, &block); end + + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(T::Array[::Domain::PerceptualHash]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + def find_or_initialize_by(attributes, &block); end + + sig { params(signed_id: T.untyped, purpose: T.untyped).returns(T.nilable(::Domain::PerceptualHash)) } + def find_signed(signed_id, purpose: nil); end + + sig { params(signed_id: T.untyped, purpose: T.untyped).returns(::Domain::PerceptualHash) } + def find_signed!(signed_id, purpose: nil); end + + sig { params(arg: T.untyped, args: T.untyped).returns(::Domain::PerceptualHash) } + def find_sole_by(arg, *args); end + + sig { returns(T.nilable(::Domain::PerceptualHash)) } + sig { params(limit: Integer).returns(T::Array[::Domain::PerceptualHash]) } + def first(limit = nil); end + + sig { returns(::Domain::PerceptualHash) } + def first!; end + + sig { returns(T.nilable(::Domain::PerceptualHash)) } + def forty_two; end + + sig { returns(::Domain::PerceptualHash) } + def forty_two!; end + + sig { returns(T.nilable(::Domain::PerceptualHash)) } + def fourth; end + + sig { returns(::Domain::PerceptualHash) } + def fourth!; end + + sig { returns(Array) } + def ids; end + + sig do + params( + of: Integer, + start: T.untyped, + finish: T.untyped, + load: T.untyped, + error_on_ignore: T.untyped, + order: Symbol, + use_ranges: T.untyped, + block: T.proc.params(object: PrivateRelation).void + ).void + end + sig do + params( + of: Integer, + start: T.untyped, + finish: T.untyped, + load: T.untyped, + error_on_ignore: T.untyped, + order: Symbol, + use_ranges: T.untyped + ).returns(::ActiveRecord::Batches::BatchEnumerator) + end + def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: :asc, use_ranges: nil, &block); end + + sig { params(record: T.untyped).returns(T::Boolean) } + def include?(record); end + + sig { returns(T.nilable(::Domain::PerceptualHash)) } + sig { params(limit: Integer).returns(T::Array[::Domain::PerceptualHash]) } + def last(limit = nil); end + + sig { returns(::Domain::PerceptualHash) } + def last!; end + + sig do + params( + block: T.nilable(T.proc.params(record: ::Domain::PerceptualHash).returns(T.untyped)) + ).returns(T::Boolean) + end + def many?(&block); end + + sig { params(column_name: T.any(String, Symbol)).returns(T.untyped) } + def maximum(column_name); end + + sig { params(record: T.untyped).returns(T::Boolean) } + def member?(record); end + + sig { params(column_name: T.any(String, Symbol)).returns(T.untyped) } + def minimum(column_name); end + + sig do + params( + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(T::Array[::Domain::PerceptualHash]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PerceptualHash).void) + ).returns(::Domain::PerceptualHash) + end + def new(attributes = nil, &block); end + + sig do + params( + block: T.nilable(T.proc.params(record: ::Domain::PerceptualHash).returns(T.untyped)) + ).returns(T::Boolean) + end + def none?(&block); end + + sig do + params( + block: T.nilable(T.proc.params(record: ::Domain::PerceptualHash).returns(T.untyped)) + ).returns(T::Boolean) + end + def one?(&block); end + + sig { params(column_names: T.untyped).returns(T.untyped) } + def pick(*column_names); end + + sig { params(column_names: T.untyped).returns(T.untyped) } + def pluck(*column_names); end + + sig { returns(T.nilable(::Domain::PerceptualHash)) } + def second; end + + sig { returns(::Domain::PerceptualHash) } + def second!; end + + sig { returns(T.nilable(::Domain::PerceptualHash)) } + def second_to_last; end + + sig { returns(::Domain::PerceptualHash) } + def second_to_last!; end + + sig { returns(::Domain::PerceptualHash) } + def sole; end + + sig { params(initial_value_or_column: T.untyped).returns(T.any(Integer, Float, BigDecimal)) } + sig do + type_parameters(:U) + .params( + initial_value_or_column: T.nilable(T.type_parameter(:U)), + block: T.proc.params(object: ::Domain::PerceptualHash).returns(T.type_parameter(:U)) + ).returns(T.type_parameter(:U)) + end + def sum(initial_value_or_column = nil, &block); end + + sig { returns(T.nilable(::Domain::PerceptualHash)) } + sig { params(limit: Integer).returns(T::Array[::Domain::PerceptualHash]) } + def take(limit = nil); end + + sig { returns(::Domain::PerceptualHash) } + def take!; end + + sig { returns(T.nilable(::Domain::PerceptualHash)) } + def third; end + + sig { returns(::Domain::PerceptualHash) } + def third!; end + + sig { returns(T.nilable(::Domain::PerceptualHash)) } + def third_to_last; end + + sig { returns(::Domain::PerceptualHash) } + def third_to_last!; end + end + + module GeneratedAssociationMethods + sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::PostFileThumbnail) } + def build_thumbnail(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::PostFileThumbnail) } + def create_thumbnail(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::PostFileThumbnail) } + def create_thumbnail!(*args, &blk); end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + def reload_thumbnail; end + + sig { void } + def reset_thumbnail; end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + def thumbnail; end + + sig { params(value: T.nilable(::Domain::PostFileThumbnail)).void } + def thumbnail=(value); end + + sig { returns(T::Boolean) } + def thumbnail_changed?; end + + sig { returns(T::Boolean) } + def thumbnail_previously_changed?; end + end + + module GeneratedAssociationRelationMethods + sig { returns(PrivateAssociationRelation) } + def all; end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def and(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def annotate(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def arel_columns(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def create_with(*args, &blk); end + + sig { params(value: T::Boolean).returns(PrivateAssociationRelation) } + def distinct(value = true); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def eager_load(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def except(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def excluding(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def extending(*args, &blk); end + + sig { params(association: Symbol).returns(T::Array[T.untyped]) } + def extract_associated(association); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def from(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelationGroupChain) } + def group(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def having(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def in_order_of(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def includes(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def invert_where(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def left_joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def left_outer_joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def limit(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def lock(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def merge(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def none(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def null_relation?(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def offset(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def only(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def optimizer_hints(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def or(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def order(*args, &blk); end + + sig do + params( + num: T.any(Integer, String) + ).returns(T.all(PrivateAssociationRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) + end + def page(num = nil); end + + sig do + params( + num: Integer + ).returns(T.all(PrivateAssociationRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) + end + def per(num); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def preload(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def readonly(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def references(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def regroup(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def reorder(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def reselect(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def reverse_order(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def rewhere(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def select(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def strict_loading(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def structurally_compatible?(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def uniq!(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def unscope(*args, &blk); end + + sig { returns(PrivateAssociationRelationWhereChain) } + sig { params(args: T.untyped).returns(PrivateAssociationRelation) } + def where(*args); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def with(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def with_recursive(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def without(*args, &blk); end + + sig do + returns(T.all(PrivateAssociationRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) + end + def without_count; end + end + + module GeneratedAttributeMethods + sig { returns(T.nilable(::String)) } + def algorithm; end + + sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } + def algorithm=(value); end + + sig { returns(T::Boolean) } + def algorithm?; end + + sig { returns(T.nilable(::String)) } + def algorithm_before_last_save; end + + sig { returns(T.untyped) } + def algorithm_before_type_cast; end + + sig { returns(T::Boolean) } + def algorithm_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) } + def algorithm_change; end + + sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) } + def algorithm_change_to_be_saved; end + + sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) } + def algorithm_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::String)) } + def algorithm_in_database; end + + sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) } + def algorithm_previous_change; end + + sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) } + def algorithm_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::String)) } + def algorithm_previously_was; end + + sig { returns(T.nilable(::String)) } + def algorithm_was; end + + sig { void } + def algorithm_will_change!; end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at; end + + sig { params(value: T.nilable(::ActiveSupport::TimeWithZone)).returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at=(value); end + + sig { returns(T::Boolean) } + def created_at?; end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at_before_last_save; end + + sig { returns(T.untyped) } + def created_at_before_type_cast; end + + sig { returns(T::Boolean) } + def created_at_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def created_at_change; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def created_at_change_to_be_saved; end + + sig do + params( + from: T.nilable(::ActiveSupport::TimeWithZone), + to: T.nilable(::ActiveSupport::TimeWithZone) + ).returns(T::Boolean) + end + def created_at_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at_in_database; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def created_at_previous_change; end + + sig do + params( + from: T.nilable(::ActiveSupport::TimeWithZone), + to: T.nilable(::ActiveSupport::TimeWithZone) + ).returns(T::Boolean) + end + def created_at_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at_previously_was; end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at_was; end + + sig { void } + def created_at_will_change!; end + + sig { returns(T.untyped) } + def hash_value; end + + sig { params(value: T.untyped).returns(T.untyped) } + def hash_value=(value); end + + sig { returns(T::Boolean) } + def hash_value?; end + + sig { returns(T.untyped) } + def hash_value_before_last_save; end + + sig { returns(T.untyped) } + def hash_value_before_type_cast; end + + sig { returns(T::Boolean) } + def hash_value_came_from_user?; end + + sig { returns(T.nilable([T.untyped, T.untyped])) } + def hash_value_change; end + + sig { returns(T.nilable([T.untyped, T.untyped])) } + def hash_value_change_to_be_saved; end + + sig { params(from: T.untyped, to: T.untyped).returns(T::Boolean) } + def hash_value_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.untyped) } + def hash_value_in_database; end + + sig { returns(T.nilable([T.untyped, T.untyped])) } + def hash_value_previous_change; end + + sig { params(from: T.untyped, to: T.untyped).returns(T::Boolean) } + def hash_value_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.untyped) } + def hash_value_previously_was; end + + sig { returns(T.untyped) } + def hash_value_was; end + + sig { void } + def hash_value_will_change!; end + + sig { returns(T.nilable(::Integer)) } + def id; end + + sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) } + def id=(value); end + + sig { returns(T::Boolean) } + def id?; end + + sig { returns(T.nilable(::Integer)) } + def id_before_last_save; end + + sig { returns(T.untyped) } + def id_before_type_cast; end + + sig { returns(T::Boolean) } + def id_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_change; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_change_to_be_saved; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def id_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def id_in_database; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_previous_change; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def id_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def id_previously_was; end + + sig { returns(T.nilable(::Integer)) } + def id_value; end + + sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) } + def id_value=(value); end + + sig { returns(T::Boolean) } + def id_value?; end + + sig { returns(T.nilable(::Integer)) } + def id_value_before_last_save; end + + sig { returns(T.untyped) } + def id_value_before_type_cast; end + + sig { returns(T::Boolean) } + def id_value_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_value_change; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_value_change_to_be_saved; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def id_value_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def id_value_in_database; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_value_previous_change; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def id_value_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def id_value_previously_was; end + + sig { returns(T.nilable(::Integer)) } + def id_value_was; end + + sig { void } + def id_value_will_change!; end + + sig { returns(T.nilable(::Integer)) } + def id_was; end + + sig { void } + def id_will_change!; end + + sig { void } + def restore_algorithm!; end + + sig { void } + def restore_created_at!; end + + sig { void } + def restore_hash_value!; end + + sig { void } + def restore_id!; end + + sig { void } + def restore_id_value!; end + + sig { void } + def restore_thumbnail_id!; end + + sig { void } + def restore_updated_at!; end + + sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) } + def saved_change_to_algorithm; end + + sig { returns(T::Boolean) } + def saved_change_to_algorithm?; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def saved_change_to_created_at; end + + sig { returns(T::Boolean) } + def saved_change_to_created_at?; end + + sig { returns(T.nilable([T.untyped, T.untyped])) } + def saved_change_to_hash_value; end + + sig { returns(T::Boolean) } + def saved_change_to_hash_value?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def saved_change_to_id; end + + sig { returns(T::Boolean) } + def saved_change_to_id?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def saved_change_to_id_value; end + + sig { returns(T::Boolean) } + def saved_change_to_id_value?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def saved_change_to_thumbnail_id; end + + sig { returns(T::Boolean) } + def saved_change_to_thumbnail_id?; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def saved_change_to_updated_at; end + + sig { returns(T::Boolean) } + def saved_change_to_updated_at?; end + + sig { returns(T.nilable(::Integer)) } + def thumbnail_id; end + + sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) } + def thumbnail_id=(value); end + + sig { returns(T::Boolean) } + def thumbnail_id?; end + + sig { returns(T.nilable(::Integer)) } + def thumbnail_id_before_last_save; end + + sig { returns(T.untyped) } + def thumbnail_id_before_type_cast; end + + sig { returns(T::Boolean) } + def thumbnail_id_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def thumbnail_id_change; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def thumbnail_id_change_to_be_saved; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def thumbnail_id_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def thumbnail_id_in_database; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def thumbnail_id_previous_change; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def thumbnail_id_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def thumbnail_id_previously_was; end + + sig { returns(T.nilable(::Integer)) } + def thumbnail_id_was; end + + sig { void } + def thumbnail_id_will_change!; end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at; end + + sig { params(value: T.nilable(::ActiveSupport::TimeWithZone)).returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at=(value); end + + sig { returns(T::Boolean) } + def updated_at?; end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at_before_last_save; end + + sig { returns(T.untyped) } + def updated_at_before_type_cast; end + + sig { returns(T::Boolean) } + def updated_at_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def updated_at_change; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def updated_at_change_to_be_saved; end + + sig do + params( + from: T.nilable(::ActiveSupport::TimeWithZone), + to: T.nilable(::ActiveSupport::TimeWithZone) + ).returns(T::Boolean) + end + def updated_at_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at_in_database; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def updated_at_previous_change; end + + sig do + params( + from: T.nilable(::ActiveSupport::TimeWithZone), + to: T.nilable(::ActiveSupport::TimeWithZone) + ).returns(T::Boolean) + end + def updated_at_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at_previously_was; end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at_was; end + + sig { void } + def updated_at_will_change!; end + + sig { returns(T::Boolean) } + def will_save_change_to_algorithm?; end + + sig { returns(T::Boolean) } + def will_save_change_to_created_at?; end + + sig { returns(T::Boolean) } + def will_save_change_to_hash_value?; end + + sig { returns(T::Boolean) } + def will_save_change_to_id?; end + + sig { returns(T::Boolean) } + def will_save_change_to_id_value?; end + + sig { returns(T::Boolean) } + def will_save_change_to_thumbnail_id?; end + + sig { returns(T::Boolean) } + def will_save_change_to_updated_at?; end + end + + module GeneratedRelationMethods + sig { returns(PrivateRelation) } + def all; end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def and(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def annotate(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def arel_columns(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def create_with(*args, &blk); end + + sig { params(value: T::Boolean).returns(PrivateRelation) } + def distinct(value = true); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def eager_load(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def except(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def excluding(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def extending(*args, &blk); end + + sig { params(association: Symbol).returns(T::Array[T.untyped]) } + def extract_associated(association); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def from(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelationGroupChain) } + def group(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def having(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def in_order_of(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def includes(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def invert_where(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def left_joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def left_outer_joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def limit(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def lock(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def merge(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def none(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def null_relation?(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def offset(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def only(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def optimizer_hints(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def or(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def order(*args, &blk); end + + sig do + params( + num: T.any(Integer, String) + ).returns(T.all(PrivateRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) + end + def page(num = nil); end + + sig do + params( + num: Integer + ).returns(T.all(PrivateRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) + end + def per(num); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def preload(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def readonly(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def references(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def regroup(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def reorder(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def reselect(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def reverse_order(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def rewhere(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def select(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def strict_loading(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def structurally_compatible?(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def uniq!(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def unscope(*args, &blk); end + + sig { returns(PrivateRelationWhereChain) } + sig { params(args: T.untyped).returns(PrivateRelation) } + def where(*args); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def with(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def with_recursive(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def without(*args, &blk); end + + sig { returns(T.all(PrivateRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) } + def without_count; end + end + + class PrivateAssociationRelation < ::ActiveRecord::AssociationRelation + include CommonRelationMethods + include GeneratedAssociationRelationMethods + + Elem = type_member { { fixed: ::Domain::PerceptualHash } } + + sig { returns(T::Array[::Domain::PerceptualHash]) } + def to_a; end + + sig { returns(T::Array[::Domain::PerceptualHash]) } + def to_ary; end + end + + class PrivateAssociationRelationGroupChain < PrivateAssociationRelation + Elem = type_member { { fixed: ::Domain::PerceptualHash } } + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) } + def average(column_name); end + + sig do + params( + operation: Symbol, + column_name: T.any(String, Symbol) + ).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) + end + def calculate(operation, column_name); end + + sig { params(column_name: T.untyped).returns(T::Hash[T.untyped, Integer]) } + def count(column_name = nil); end + + sig { params(args: T.untyped, blk: T.untyped).returns(T.self_type) } + def having(*args, &blk); end + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.untyped]) } + def maximum(column_name); end + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.untyped]) } + def minimum(column_name); end + + sig do + params( + column_name: T.nilable(T.any(String, Symbol)), + block: T.nilable(T.proc.params(record: T.untyped).returns(T.untyped)) + ).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) + end + def sum(column_name = nil, &block); end + end + + class PrivateAssociationRelationWhereChain + Elem = type_member { { fixed: ::Domain::PerceptualHash } } + + sig { params(args: T.untyped).returns(PrivateAssociationRelation) } + def associated(*args); end + + sig { params(args: T.untyped).returns(PrivateAssociationRelation) } + def missing(*args); end + + sig { params(opts: T.untyped, rest: T.untyped).returns(PrivateAssociationRelation) } + def not(opts, *rest); end + end + + class PrivateCollectionProxy < ::ActiveRecord::Associations::CollectionProxy + include CommonRelationMethods + include GeneratedAssociationRelationMethods + + Elem = type_member { { fixed: ::Domain::PerceptualHash } } + + sig do + params( + records: T.any(::Domain::PerceptualHash, T::Enumerable[T.any(::Domain::PerceptualHash, T::Enumerable[::Domain::PerceptualHash])]) + ).returns(PrivateCollectionProxy) + end + def <<(*records); end + + sig do + params( + records: T.any(::Domain::PerceptualHash, T::Enumerable[T.any(::Domain::PerceptualHash, T::Enumerable[::Domain::PerceptualHash])]) + ).returns(PrivateCollectionProxy) + end + def append(*records); end + + sig { returns(PrivateCollectionProxy) } + def clear; end + + sig do + params( + records: T.any(::Domain::PerceptualHash, T::Enumerable[T.any(::Domain::PerceptualHash, T::Enumerable[::Domain::PerceptualHash])]) + ).returns(PrivateCollectionProxy) + end + def concat(*records); end + + sig { returns(T::Array[::Domain::PerceptualHash]) } + def load_target; end + + sig do + params( + records: T.any(::Domain::PerceptualHash, T::Enumerable[T.any(::Domain::PerceptualHash, T::Enumerable[::Domain::PerceptualHash])]) + ).returns(PrivateCollectionProxy) + end + def prepend(*records); end + + sig do + params( + records: T.any(::Domain::PerceptualHash, T::Enumerable[T.any(::Domain::PerceptualHash, T::Enumerable[::Domain::PerceptualHash])]) + ).returns(PrivateCollectionProxy) + end + def push(*records); end + + sig do + params( + other_array: T.any(::Domain::PerceptualHash, T::Enumerable[T.any(::Domain::PerceptualHash, T::Enumerable[::Domain::PerceptualHash])]) + ).returns(T::Array[::Domain::PerceptualHash]) + end + def replace(other_array); end + + sig { returns(PrivateAssociationRelation) } + def scope; end + + sig { returns(T::Array[::Domain::PerceptualHash]) } + def target; end + + sig { returns(T::Array[::Domain::PerceptualHash]) } + def to_a; end + + sig { returns(T::Array[::Domain::PerceptualHash]) } + def to_ary; end + end + + class PrivateRelation < ::ActiveRecord::Relation + include CommonRelationMethods + include GeneratedRelationMethods + + Elem = type_member { { fixed: ::Domain::PerceptualHash } } + + sig { returns(T::Array[::Domain::PerceptualHash]) } + def to_a; end + + sig { returns(T::Array[::Domain::PerceptualHash]) } + def to_ary; end + end + + class PrivateRelationGroupChain < PrivateRelation + Elem = type_member { { fixed: ::Domain::PerceptualHash } } + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) } + def average(column_name); end + + sig do + params( + operation: Symbol, + column_name: T.any(String, Symbol) + ).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) + end + def calculate(operation, column_name); end + + sig { params(column_name: T.untyped).returns(T::Hash[T.untyped, Integer]) } + def count(column_name = nil); end + + sig { params(args: T.untyped, blk: T.untyped).returns(T.self_type) } + def having(*args, &blk); end + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.untyped]) } + def maximum(column_name); end + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.untyped]) } + def minimum(column_name); end + + sig do + params( + column_name: T.nilable(T.any(String, Symbol)), + block: T.nilable(T.proc.params(record: T.untyped).returns(T.untyped)) + ).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) + end + def sum(column_name = nil, &block); end + end + + class PrivateRelationWhereChain + Elem = type_member { { fixed: ::Domain::PerceptualHash } } + + sig { params(args: T.untyped).returns(PrivateRelation) } + def associated(*args); end + + sig { params(args: T.untyped).returns(PrivateRelation) } + def missing(*args); end + + sig { params(opts: T.untyped, rest: T.untyped).returns(PrivateRelation) } + def not(opts, *rest); end + end +end diff --git a/sorbet/rbi/dsl/domain/post_file.rbi b/sorbet/rbi/dsl/domain/post_file.rbi index fc848950..1e8c4ccf 100644 --- a/sorbet/rbi/dsl/domain/post_file.rbi +++ b/sorbet/rbi/dsl/domain/post_file.rbi @@ -533,6 +533,20 @@ class Domain::PostFile sig { void } def reset_post; end + + sig { returns(T::Array[T.untyped]) } + def thumbnail_ids; end + + sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) } + def thumbnail_ids=(ids); end + + # This method is created by ActiveRecord on the `Domain::PostFile` class because it declared `has_many :thumbnails`. + # 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association) + sig { returns(::Domain::PostFileThumbnail::PrivateCollectionProxy) } + def thumbnails; end + + sig { params(value: T::Enumerable[::Domain::PostFileThumbnail]).void } + def thumbnails=(value); end end module GeneratedAssociationRelationMethods diff --git a/sorbet/rbi/dsl/domain/post_file/inkbunny_post_file.rbi b/sorbet/rbi/dsl/domain/post_file/inkbunny_post_file.rbi index 1b11fa45..1bfa51db 100644 --- a/sorbet/rbi/dsl/domain/post_file/inkbunny_post_file.rbi +++ b/sorbet/rbi/dsl/domain/post_file/inkbunny_post_file.rbi @@ -516,6 +516,20 @@ class Domain::PostFile::InkbunnyPostFile sig { void } def reset_post; end + + sig { returns(T::Array[T.untyped]) } + def thumbnail_ids; end + + sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) } + def thumbnail_ids=(ids); end + + # This method is created by ActiveRecord on the `Domain::PostFile` class because it declared `has_many :thumbnails`. + # 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association) + sig { returns(::Domain::PostFileThumbnail::PrivateCollectionProxy) } + def thumbnails; end + + sig { params(value: T::Enumerable[::Domain::PostFileThumbnail]).void } + def thumbnails=(value); end end module GeneratedAssociationRelationMethods diff --git a/sorbet/rbi/dsl/domain/post_file_thumbnail.rbi b/sorbet/rbi/dsl/domain/post_file_thumbnail.rbi new file mode 100644 index 00000000..e1bc50c7 --- /dev/null +++ b/sorbet/rbi/dsl/domain/post_file_thumbnail.rbi @@ -0,0 +1,1369 @@ +# typed: true + +# DO NOT EDIT MANUALLY +# This is an autogenerated file for dynamic methods in `Domain::PostFileThumbnail`. +# Please instead update this file by running `bin/tapioca dsl Domain::PostFileThumbnail`. + + +class Domain::PostFileThumbnail + include GeneratedAssociationMethods + include GeneratedAttributeMethods + extend CommonRelationMethods + extend GeneratedRelationMethods + + sig { returns(ColorLogger) } + def logger; end + + private + + sig { returns(NilClass) } + def to_ary; end + + class << self + sig do + params( + name: Symbol, + type: T.any(Symbol, ActiveModel::Type::Value), + options: T.nilable(T::Hash[Symbol, T.untyped]) + ).void + end + def attr_json(name, type, options = nil); end + + sig do + params( + default_container_attribute: T.nilable(Symbol), + bad_cast: T.nilable(Symbol), + unknown_key: T.nilable(Symbol) + ).void + end + def attr_json_config(default_container_attribute: nil, bad_cast: nil, unknown_key: nil); end + + sig { returns(T::Array[Symbol]) } + def attr_json_registry; end + + sig { returns(ColorLogger) } + def logger; end + + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + def new(attributes = nil, &block); end + end + + module CommonRelationMethods + sig do + params( + block: T.nilable(T.proc.params(record: ::Domain::PostFileThumbnail).returns(T.untyped)) + ).returns(T::Boolean) + end + def any?(&block); end + + sig { params(column_name: T.any(String, Symbol)).returns(T.any(Integer, Float, BigDecimal)) } + def average(column_name); end + + sig do + params( + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(T::Array[::Domain::PostFileThumbnail]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + def build(attributes = nil, &block); end + + sig { params(operation: Symbol, column_name: T.any(String, Symbol)).returns(T.any(Integer, Float, BigDecimal)) } + def calculate(operation, column_name); end + + sig { params(column_name: T.nilable(T.any(String, Symbol))).returns(Integer) } + sig do + params( + column_name: NilClass, + block: T.proc.params(object: ::Domain::PostFileThumbnail).void + ).returns(Integer) + end + def count(column_name = nil, &block); end + + sig do + params( + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(T::Array[::Domain::PostFileThumbnail]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + def create(attributes = nil, &block); end + + sig do + params( + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(T::Array[::Domain::PostFileThumbnail]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + def create!(attributes = nil, &block); end + + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(T::Array[::Domain::PostFileThumbnail]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + def create_or_find_by(attributes, &block); end + + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(T::Array[::Domain::PostFileThumbnail]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + def create_or_find_by!(attributes, &block); end + + sig { returns(T::Array[::Domain::PostFileThumbnail]) } + def destroy_all; end + + sig { params(conditions: T.untyped).returns(T::Boolean) } + def exists?(conditions = :none); end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + def fifth; end + + sig { returns(::Domain::PostFileThumbnail) } + def fifth!; end + + sig do + params( + args: T.any(String, Symbol, ::ActiveSupport::Multibyte::Chars, T::Boolean, BigDecimal, Numeric, ::ActiveRecord::Type::Binary::Data, ::ActiveRecord::Type::Time::Value, Date, Time, ::ActiveSupport::Duration, T::Class[T.anything]) + ).returns(::Domain::PostFileThumbnail) + end + sig do + params( + args: T::Array[T.any(String, Symbol, ::ActiveSupport::Multibyte::Chars, T::Boolean, BigDecimal, Numeric, ::ActiveRecord::Type::Binary::Data, ::ActiveRecord::Type::Time::Value, Date, Time, ::ActiveSupport::Duration, T::Class[T.anything])] + ).returns(T::Enumerable[::Domain::PostFileThumbnail]) + end + sig do + params( + args: NilClass, + block: T.proc.params(object: ::Domain::PostFileThumbnail).void + ).returns(T.nilable(::Domain::PostFileThumbnail)) + end + def find(args = nil, &block); end + + sig { params(args: T.untyped).returns(T.nilable(::Domain::PostFileThumbnail)) } + def find_by(*args); end + + sig { params(args: T.untyped).returns(::Domain::PostFileThumbnail) } + def find_by!(*args); end + + sig do + params( + start: T.untyped, + finish: T.untyped, + batch_size: Integer, + error_on_ignore: T.untyped, + order: Symbol, + block: T.proc.params(object: ::Domain::PostFileThumbnail).void + ).void + end + sig do + params( + start: T.untyped, + finish: T.untyped, + batch_size: Integer, + error_on_ignore: T.untyped, + order: Symbol + ).returns(T::Enumerator[::Domain::PostFileThumbnail]) + end + def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc, &block); end + + sig do + params( + start: T.untyped, + finish: T.untyped, + batch_size: Integer, + error_on_ignore: T.untyped, + order: Symbol, + block: T.proc.params(object: T::Array[::Domain::PostFileThumbnail]).void + ).void + end + sig do + params( + start: T.untyped, + finish: T.untyped, + batch_size: Integer, + error_on_ignore: T.untyped, + order: Symbol + ).returns(T::Enumerator[T::Enumerator[::Domain::PostFileThumbnail]]) + end + def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc, &block); end + + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(T::Array[::Domain::PostFileThumbnail]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + def find_or_create_by(attributes, &block); end + + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(T::Array[::Domain::PostFileThumbnail]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + def find_or_create_by!(attributes, &block); end + + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(T::Array[::Domain::PostFileThumbnail]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + def find_or_initialize_by(attributes, &block); end + + sig { params(signed_id: T.untyped, purpose: T.untyped).returns(T.nilable(::Domain::PostFileThumbnail)) } + def find_signed(signed_id, purpose: nil); end + + sig { params(signed_id: T.untyped, purpose: T.untyped).returns(::Domain::PostFileThumbnail) } + def find_signed!(signed_id, purpose: nil); end + + sig { params(arg: T.untyped, args: T.untyped).returns(::Domain::PostFileThumbnail) } + def find_sole_by(arg, *args); end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + sig { params(limit: Integer).returns(T::Array[::Domain::PostFileThumbnail]) } + def first(limit = nil); end + + sig { returns(::Domain::PostFileThumbnail) } + def first!; end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + def forty_two; end + + sig { returns(::Domain::PostFileThumbnail) } + def forty_two!; end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + def fourth; end + + sig { returns(::Domain::PostFileThumbnail) } + def fourth!; end + + sig { returns(Array) } + def ids; end + + sig do + params( + of: Integer, + start: T.untyped, + finish: T.untyped, + load: T.untyped, + error_on_ignore: T.untyped, + order: Symbol, + use_ranges: T.untyped, + block: T.proc.params(object: PrivateRelation).void + ).void + end + sig do + params( + of: Integer, + start: T.untyped, + finish: T.untyped, + load: T.untyped, + error_on_ignore: T.untyped, + order: Symbol, + use_ranges: T.untyped + ).returns(::ActiveRecord::Batches::BatchEnumerator) + end + def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: :asc, use_ranges: nil, &block); end + + sig { params(record: T.untyped).returns(T::Boolean) } + def include?(record); end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + sig { params(limit: Integer).returns(T::Array[::Domain::PostFileThumbnail]) } + def last(limit = nil); end + + sig { returns(::Domain::PostFileThumbnail) } + def last!; end + + sig do + params( + block: T.nilable(T.proc.params(record: ::Domain::PostFileThumbnail).returns(T.untyped)) + ).returns(T::Boolean) + end + def many?(&block); end + + sig { params(column_name: T.any(String, Symbol)).returns(T.untyped) } + def maximum(column_name); end + + sig { params(record: T.untyped).returns(T::Boolean) } + def member?(record); end + + sig { params(column_name: T.any(String, Symbol)).returns(T.untyped) } + def minimum(column_name); end + + sig do + params( + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + sig do + params( + attributes: T::Array[T.untyped], + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(T::Array[::Domain::PostFileThumbnail]) + end + sig do + params( + attributes: T.untyped, + block: T.nilable(T.proc.params(object: ::Domain::PostFileThumbnail).void) + ).returns(::Domain::PostFileThumbnail) + end + def new(attributes = nil, &block); end + + sig do + params( + block: T.nilable(T.proc.params(record: ::Domain::PostFileThumbnail).returns(T.untyped)) + ).returns(T::Boolean) + end + def none?(&block); end + + sig do + params( + block: T.nilable(T.proc.params(record: ::Domain::PostFileThumbnail).returns(T.untyped)) + ).returns(T::Boolean) + end + def one?(&block); end + + sig { params(column_names: T.untyped).returns(T.untyped) } + def pick(*column_names); end + + sig { params(column_names: T.untyped).returns(T.untyped) } + def pluck(*column_names); end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + def second; end + + sig { returns(::Domain::PostFileThumbnail) } + def second!; end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + def second_to_last; end + + sig { returns(::Domain::PostFileThumbnail) } + def second_to_last!; end + + sig { returns(::Domain::PostFileThumbnail) } + def sole; end + + sig { params(initial_value_or_column: T.untyped).returns(T.any(Integer, Float, BigDecimal)) } + sig do + type_parameters(:U) + .params( + initial_value_or_column: T.nilable(T.type_parameter(:U)), + block: T.proc.params(object: ::Domain::PostFileThumbnail).returns(T.type_parameter(:U)) + ).returns(T.type_parameter(:U)) + end + def sum(initial_value_or_column = nil, &block); end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + sig { params(limit: Integer).returns(T::Array[::Domain::PostFileThumbnail]) } + def take(limit = nil); end + + sig { returns(::Domain::PostFileThumbnail) } + def take!; end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + def third; end + + sig { returns(::Domain::PostFileThumbnail) } + def third!; end + + sig { returns(T.nilable(::Domain::PostFileThumbnail)) } + def third_to_last; end + + sig { returns(::Domain::PostFileThumbnail) } + def third_to_last!; end + end + + module GeneratedAssociationMethods + sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::PostFile) } + def build_post_file(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::PostFile) } + def create_post_file(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::PostFile) } + def create_post_file!(*args, &blk); end + + sig { returns(T::Array[T.untyped]) } + def perceptual_hash_ids; end + + sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) } + def perceptual_hash_ids=(ids); end + + # This method is created by ActiveRecord on the `Domain::PostFileThumbnail` class because it declared `has_many :perceptual_hashes`. + # 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association) + sig { returns(::Domain::PerceptualHash::PrivateCollectionProxy) } + def perceptual_hashes; end + + sig { params(value: T::Enumerable[::Domain::PerceptualHash]).void } + def perceptual_hashes=(value); end + + sig { returns(T.nilable(::Domain::PostFile)) } + def post_file; end + + sig { params(value: T.nilable(::Domain::PostFile)).void } + def post_file=(value); end + + sig { returns(T::Boolean) } + def post_file_changed?; end + + sig { returns(T::Boolean) } + def post_file_previously_changed?; end + + sig { returns(T.nilable(::Domain::PostFile)) } + def reload_post_file; end + + sig { void } + def reset_post_file; end + end + + module GeneratedAssociationRelationMethods + sig { returns(PrivateAssociationRelation) } + def all; end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def and(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def annotate(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def arel_columns(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def create_with(*args, &blk); end + + sig { params(value: T::Boolean).returns(PrivateAssociationRelation) } + def distinct(value = true); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def eager_load(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def except(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def excluding(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def extending(*args, &blk); end + + sig { params(association: Symbol).returns(T::Array[T.untyped]) } + def extract_associated(association); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def from(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelationGroupChain) } + def group(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def having(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def in_order_of(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def includes(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def invert_where(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def left_joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def left_outer_joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def limit(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def lock(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def merge(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def none(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def null_relation?(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def offset(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def only(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def optimizer_hints(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def or(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def order(*args, &blk); end + + sig do + params( + num: T.any(Integer, String) + ).returns(T.all(PrivateAssociationRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) + end + def page(num = nil); end + + sig do + params( + num: Integer + ).returns(T.all(PrivateAssociationRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) + end + def per(num); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def preload(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def readonly(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def references(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def regroup(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def reorder(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def reselect(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def reverse_order(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def rewhere(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def select(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def strict_loading(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def structurally_compatible?(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def uniq!(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def unscope(*args, &blk); end + + sig { returns(PrivateAssociationRelationWhereChain) } + sig { params(args: T.untyped).returns(PrivateAssociationRelation) } + def where(*args); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def with(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def with_recursive(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } + def without(*args, &blk); end + + sig do + returns(T.all(PrivateAssociationRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) + end + def without_count; end + end + + module GeneratedAttributeMethods + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at; end + + sig { params(value: T.nilable(::ActiveSupport::TimeWithZone)).returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at=(value); end + + sig { returns(T::Boolean) } + def created_at?; end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at_before_last_save; end + + sig { returns(T.untyped) } + def created_at_before_type_cast; end + + sig { returns(T::Boolean) } + def created_at_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def created_at_change; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def created_at_change_to_be_saved; end + + sig do + params( + from: T.nilable(::ActiveSupport::TimeWithZone), + to: T.nilable(::ActiveSupport::TimeWithZone) + ).returns(T::Boolean) + end + def created_at_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at_in_database; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def created_at_previous_change; end + + sig do + params( + from: T.nilable(::ActiveSupport::TimeWithZone), + to: T.nilable(::ActiveSupport::TimeWithZone) + ).returns(T::Boolean) + end + def created_at_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at_previously_was; end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def created_at_was; end + + sig { void } + def created_at_will_change!; end + + sig { returns(T.nilable(::Integer)) } + def id; end + + sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) } + def id=(value); end + + sig { returns(T::Boolean) } + def id?; end + + sig { returns(T.nilable(::Integer)) } + def id_before_last_save; end + + sig { returns(T.untyped) } + def id_before_type_cast; end + + sig { returns(T::Boolean) } + def id_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_change; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_change_to_be_saved; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def id_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def id_in_database; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_previous_change; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def id_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def id_previously_was; end + + sig { returns(T.nilable(::Integer)) } + def id_value; end + + sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) } + def id_value=(value); end + + sig { returns(T::Boolean) } + def id_value?; end + + sig { returns(T.nilable(::Integer)) } + def id_value_before_last_save; end + + sig { returns(T.untyped) } + def id_value_before_type_cast; end + + sig { returns(T::Boolean) } + def id_value_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_value_change; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_value_change_to_be_saved; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def id_value_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def id_value_in_database; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def id_value_previous_change; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def id_value_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def id_value_previously_was; end + + sig { returns(T.nilable(::Integer)) } + def id_value_was; end + + sig { void } + def id_value_will_change!; end + + sig { returns(T.nilable(::Integer)) } + def id_was; end + + sig { void } + def id_will_change!; end + + sig { returns(T.nilable(::Integer)) } + def post_file_id; end + + sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) } + def post_file_id=(value); end + + sig { returns(T::Boolean) } + def post_file_id?; end + + sig { returns(T.nilable(::Integer)) } + def post_file_id_before_last_save; end + + sig { returns(T.untyped) } + def post_file_id_before_type_cast; end + + sig { returns(T::Boolean) } + def post_file_id_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def post_file_id_change; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def post_file_id_change_to_be_saved; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def post_file_id_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def post_file_id_in_database; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def post_file_id_previous_change; end + + sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) } + def post_file_id_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::Integer)) } + def post_file_id_previously_was; end + + sig { returns(T.nilable(::Integer)) } + def post_file_id_was; end + + sig { void } + def post_file_id_will_change!; end + + sig { void } + def restore_created_at!; end + + sig { void } + def restore_id!; end + + sig { void } + def restore_id_value!; end + + sig { void } + def restore_post_file_id!; end + + sig { void } + def restore_thumbnail_type!; end + + sig { void } + def restore_updated_at!; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def saved_change_to_created_at; end + + sig { returns(T::Boolean) } + def saved_change_to_created_at?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def saved_change_to_id; end + + sig { returns(T::Boolean) } + def saved_change_to_id?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def saved_change_to_id_value; end + + sig { returns(T::Boolean) } + def saved_change_to_id_value?; end + + sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) } + def saved_change_to_post_file_id; end + + sig { returns(T::Boolean) } + def saved_change_to_post_file_id?; end + + sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) } + def saved_change_to_thumbnail_type; end + + sig { returns(T::Boolean) } + def saved_change_to_thumbnail_type?; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def saved_change_to_updated_at; end + + sig { returns(T::Boolean) } + def saved_change_to_updated_at?; end + + sig { returns(T.nilable(::String)) } + def thumbnail_type; end + + sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) } + def thumbnail_type=(value); end + + sig { returns(T::Boolean) } + def thumbnail_type?; end + + sig { returns(T.nilable(::String)) } + def thumbnail_type_before_last_save; end + + sig { returns(T.untyped) } + def thumbnail_type_before_type_cast; end + + sig { returns(T::Boolean) } + def thumbnail_type_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) } + def thumbnail_type_change; end + + sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) } + def thumbnail_type_change_to_be_saved; end + + sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) } + def thumbnail_type_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::String)) } + def thumbnail_type_in_database; end + + sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) } + def thumbnail_type_previous_change; end + + sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) } + def thumbnail_type_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::String)) } + def thumbnail_type_previously_was; end + + sig { returns(T.nilable(::String)) } + def thumbnail_type_was; end + + sig { void } + def thumbnail_type_will_change!; end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at; end + + sig { params(value: T.nilable(::ActiveSupport::TimeWithZone)).returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at=(value); end + + sig { returns(T::Boolean) } + def updated_at?; end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at_before_last_save; end + + sig { returns(T.untyped) } + def updated_at_before_type_cast; end + + sig { returns(T::Boolean) } + def updated_at_came_from_user?; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def updated_at_change; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def updated_at_change_to_be_saved; end + + sig do + params( + from: T.nilable(::ActiveSupport::TimeWithZone), + to: T.nilable(::ActiveSupport::TimeWithZone) + ).returns(T::Boolean) + end + def updated_at_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at_in_database; end + + sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) } + def updated_at_previous_change; end + + sig do + params( + from: T.nilable(::ActiveSupport::TimeWithZone), + to: T.nilable(::ActiveSupport::TimeWithZone) + ).returns(T::Boolean) + end + def updated_at_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at_previously_was; end + + sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) } + def updated_at_was; end + + sig { void } + def updated_at_will_change!; end + + sig { returns(T::Boolean) } + def will_save_change_to_created_at?; end + + sig { returns(T::Boolean) } + def will_save_change_to_id?; end + + sig { returns(T::Boolean) } + def will_save_change_to_id_value?; end + + sig { returns(T::Boolean) } + def will_save_change_to_post_file_id?; end + + sig { returns(T::Boolean) } + def will_save_change_to_thumbnail_type?; end + + sig { returns(T::Boolean) } + def will_save_change_to_updated_at?; end + end + + module GeneratedRelationMethods + sig { returns(PrivateRelation) } + def all; end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def and(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def annotate(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def arel_columns(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def create_with(*args, &blk); end + + sig { params(value: T::Boolean).returns(PrivateRelation) } + def distinct(value = true); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def eager_load(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def except(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def excluding(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def extending(*args, &blk); end + + sig { params(association: Symbol).returns(T::Array[T.untyped]) } + def extract_associated(association); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def from(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelationGroupChain) } + def group(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def having(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def in_order_of(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def includes(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def invert_where(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def left_joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def left_outer_joins(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def limit(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def lock(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def merge(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def none(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def null_relation?(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def offset(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def only(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def optimizer_hints(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def or(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def order(*args, &blk); end + + sig do + params( + num: T.any(Integer, String) + ).returns(T.all(PrivateRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) + end + def page(num = nil); end + + sig do + params( + num: Integer + ).returns(T.all(PrivateRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) + end + def per(num); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def preload(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def readonly(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def references(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def regroup(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def reorder(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def reselect(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def reverse_order(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def rewhere(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def select(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def strict_loading(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def structurally_compatible?(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def uniq!(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def unscope(*args, &blk); end + + sig { returns(PrivateRelationWhereChain) } + sig { params(args: T.untyped).returns(PrivateRelation) } + def where(*args); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def with(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def with_recursive(*args, &blk); end + + sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } + def without(*args, &blk); end + + sig { returns(T.all(PrivateRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) } + def without_count; end + end + + class PrivateAssociationRelation < ::ActiveRecord::AssociationRelation + include CommonRelationMethods + include GeneratedAssociationRelationMethods + + Elem = type_member { { fixed: ::Domain::PostFileThumbnail } } + + sig { returns(T::Array[::Domain::PostFileThumbnail]) } + def to_a; end + + sig { returns(T::Array[::Domain::PostFileThumbnail]) } + def to_ary; end + end + + class PrivateAssociationRelationGroupChain < PrivateAssociationRelation + Elem = type_member { { fixed: ::Domain::PostFileThumbnail } } + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) } + def average(column_name); end + + sig do + params( + operation: Symbol, + column_name: T.any(String, Symbol) + ).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) + end + def calculate(operation, column_name); end + + sig { params(column_name: T.untyped).returns(T::Hash[T.untyped, Integer]) } + def count(column_name = nil); end + + sig { params(args: T.untyped, blk: T.untyped).returns(T.self_type) } + def having(*args, &blk); end + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.untyped]) } + def maximum(column_name); end + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.untyped]) } + def minimum(column_name); end + + sig do + params( + column_name: T.nilable(T.any(String, Symbol)), + block: T.nilable(T.proc.params(record: T.untyped).returns(T.untyped)) + ).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) + end + def sum(column_name = nil, &block); end + end + + class PrivateAssociationRelationWhereChain + Elem = type_member { { fixed: ::Domain::PostFileThumbnail } } + + sig { params(args: T.untyped).returns(PrivateAssociationRelation) } + def associated(*args); end + + sig { params(args: T.untyped).returns(PrivateAssociationRelation) } + def missing(*args); end + + sig { params(opts: T.untyped, rest: T.untyped).returns(PrivateAssociationRelation) } + def not(opts, *rest); end + end + + class PrivateCollectionProxy < ::ActiveRecord::Associations::CollectionProxy + include CommonRelationMethods + include GeneratedAssociationRelationMethods + + Elem = type_member { { fixed: ::Domain::PostFileThumbnail } } + + sig do + params( + records: T.any(::Domain::PostFileThumbnail, T::Enumerable[T.any(::Domain::PostFileThumbnail, T::Enumerable[::Domain::PostFileThumbnail])]) + ).returns(PrivateCollectionProxy) + end + def <<(*records); end + + sig do + params( + records: T.any(::Domain::PostFileThumbnail, T::Enumerable[T.any(::Domain::PostFileThumbnail, T::Enumerable[::Domain::PostFileThumbnail])]) + ).returns(PrivateCollectionProxy) + end + def append(*records); end + + sig { returns(PrivateCollectionProxy) } + def clear; end + + sig do + params( + records: T.any(::Domain::PostFileThumbnail, T::Enumerable[T.any(::Domain::PostFileThumbnail, T::Enumerable[::Domain::PostFileThumbnail])]) + ).returns(PrivateCollectionProxy) + end + def concat(*records); end + + sig { returns(T::Array[::Domain::PostFileThumbnail]) } + def load_target; end + + sig do + params( + records: T.any(::Domain::PostFileThumbnail, T::Enumerable[T.any(::Domain::PostFileThumbnail, T::Enumerable[::Domain::PostFileThumbnail])]) + ).returns(PrivateCollectionProxy) + end + def prepend(*records); end + + sig do + params( + records: T.any(::Domain::PostFileThumbnail, T::Enumerable[T.any(::Domain::PostFileThumbnail, T::Enumerable[::Domain::PostFileThumbnail])]) + ).returns(PrivateCollectionProxy) + end + def push(*records); end + + sig do + params( + other_array: T.any(::Domain::PostFileThumbnail, T::Enumerable[T.any(::Domain::PostFileThumbnail, T::Enumerable[::Domain::PostFileThumbnail])]) + ).returns(T::Array[::Domain::PostFileThumbnail]) + end + def replace(other_array); end + + sig { returns(PrivateAssociationRelation) } + def scope; end + + sig { returns(T::Array[::Domain::PostFileThumbnail]) } + def target; end + + sig { returns(T::Array[::Domain::PostFileThumbnail]) } + def to_a; end + + sig { returns(T::Array[::Domain::PostFileThumbnail]) } + def to_ary; end + end + + class PrivateRelation < ::ActiveRecord::Relation + include CommonRelationMethods + include GeneratedRelationMethods + + Elem = type_member { { fixed: ::Domain::PostFileThumbnail } } + + sig { returns(T::Array[::Domain::PostFileThumbnail]) } + def to_a; end + + sig { returns(T::Array[::Domain::PostFileThumbnail]) } + def to_ary; end + end + + class PrivateRelationGroupChain < PrivateRelation + Elem = type_member { { fixed: ::Domain::PostFileThumbnail } } + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) } + def average(column_name); end + + sig do + params( + operation: Symbol, + column_name: T.any(String, Symbol) + ).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) + end + def calculate(operation, column_name); end + + sig { params(column_name: T.untyped).returns(T::Hash[T.untyped, Integer]) } + def count(column_name = nil); end + + sig { params(args: T.untyped, blk: T.untyped).returns(T.self_type) } + def having(*args, &blk); end + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.untyped]) } + def maximum(column_name); end + + sig { params(column_name: T.any(String, Symbol)).returns(T::Hash[T.untyped, T.untyped]) } + def minimum(column_name); end + + sig do + params( + column_name: T.nilable(T.any(String, Symbol)), + block: T.nilable(T.proc.params(record: T.untyped).returns(T.untyped)) + ).returns(T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]) + end + def sum(column_name = nil, &block); end + end + + class PrivateRelationWhereChain + Elem = type_member { { fixed: ::Domain::PostFileThumbnail } } + + sig { params(args: T.untyped).returns(PrivateRelation) } + def associated(*args); end + + sig { params(args: T.untyped).returns(PrivateRelation) } + def missing(*args); end + + sig { params(opts: T.untyped, rest: T.untyped).returns(PrivateRelation) } + def not(opts, *rest); end + end +end diff --git a/sorbet/rbi/dsl/generated_path_helpers_module.rbi b/sorbet/rbi/dsl/generated_path_helpers_module.rbi index 62f52603..93135b08 100644 --- a/sorbet/rbi/dsl/generated_path_helpers_module.rbi +++ b/sorbet/rbi/dsl/generated_path_helpers_module.rbi @@ -213,6 +213,9 @@ module GeneratedPathHelpersModule sig { params(args: T.untyped).returns(String) } def stats_log_entries_path(*args); end + sig { params(args: T.untyped).returns(String) } + def toggle_state_ip_address_role_path(*args); end + sig { params(args: T.untyped).returns(String) } def turbo_recede_historical_location_path(*args); end diff --git a/sorbet/rbi/dsl/generated_url_helpers_module.rbi b/sorbet/rbi/dsl/generated_url_helpers_module.rbi index 208c5e00..af0717ea 100644 --- a/sorbet/rbi/dsl/generated_url_helpers_module.rbi +++ b/sorbet/rbi/dsl/generated_url_helpers_module.rbi @@ -213,6 +213,9 @@ module GeneratedUrlHelpersModule sig { params(args: T.untyped).returns(String) } def stats_log_entries_url(*args); end + sig { params(args: T.untyped).returns(String) } + def toggle_state_ip_address_role_url(*args); end + sig { params(args: T.untyped).returns(String) } def turbo_recede_historical_location_url(*args); end diff --git a/sorbet/rbi/dsl/ip_address_role.rbi b/sorbet/rbi/dsl/ip_address_role.rbi index 5ce3d6d8..131be8a4 100644 --- a/sorbet/rbi/dsl/ip_address_role.rbi +++ b/sorbet/rbi/dsl/ip_address_role.rbi @@ -393,18 +393,6 @@ class IpAddressRole end module EnumMethodsModule - sig { void } - def admin!; end - - sig { returns(T::Boolean) } - def admin?; end - - sig { void } - def moderator!; end - - sig { returns(T::Boolean) } - def moderator?; end - sig { void } def user!; end @@ -413,9 +401,6 @@ class IpAddressRole end module GeneratedAssociationRelationMethods - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } - def admin(*args, &blk); end - sig { returns(PrivateAssociationRelation) } def all; end @@ -485,18 +470,9 @@ class IpAddressRole sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } def merge(*args, &blk); end - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } - def moderator(*args, &blk); end - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } def none(*args, &blk); end - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } - def not_admin(*args, &blk); end - - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } - def not_moderator(*args, &blk); end - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } def not_user(*args, &blk); end @@ -1082,9 +1058,6 @@ class IpAddressRole end module GeneratedRelationMethods - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } - def admin(*args, &blk); end - sig { returns(PrivateRelation) } def all; end @@ -1154,18 +1127,9 @@ class IpAddressRole sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } def merge(*args, &blk); end - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } - def moderator(*args, &blk); end - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } def none(*args, &blk); end - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } - def not_admin(*args, &blk); end - - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } - def not_moderator(*args, &blk); end - sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } def not_user(*args, &blk); end diff --git a/sorbet/rbi/shims/vips.rbi b/sorbet/rbi/shims/vips.rbi index 287afecf..a82d5855 100644 --- a/sorbet/rbi/shims/vips.rbi +++ b/sorbet/rbi/shims/vips.rbi @@ -7,6 +7,27 @@ class Vips::Image sig { params(name: String, n: T.nilable(Integer)).returns(Vips::Image) } def self.gifload(name, n: nil) end + sig do + params( + filename: String, + width: Integer, + height: T.nilable(Integer), + # :up, :down, :both, :force + size: T.nilable(Symbol), + ).returns(Vips::Image) + end + def self.thumbnail(filename, width, height: nil, size: nil) + end + + sig do + params( + width: Integer, + height: T.nilable(Integer), + size: T.nilable(Symbol), + ).returns(Vips::Image) + end + def thumbnail_image(width, height: nil, size: nil) + end sig { params(scale: T.untyped).returns(Vips::Image) } def resize(scale) @@ -16,9 +37,7 @@ class Vips::Image def gifsave_buffer(**opts) end - sig do - params(width: Integer, height: T.nilable(Integer)).returns(Vips::Image) - end - def thumbnail_image(width, height: nil) + sig { params(filename: String, opts: T.untyped).returns(String) } + def jpegsave(filename, **opts) end end diff --git a/spec/factories/domain/post_file_thumbnail.rb b/spec/factories/domain/post_file_thumbnail.rb new file mode 100644 index 00000000..56094af9 --- /dev/null +++ b/spec/factories/domain/post_file_thumbnail.rb @@ -0,0 +1,7 @@ +# typed: false +FactoryBot.define do + factory :domain_post_file_thumbnail, class: "Domain::PostFileThumbnail" do + association :post_file, factory: :domain_post_file + thumbnail_type { Domain::ThumbnailType::Small.name } + end +end diff --git a/spec/models/domain/post_file_thumbnail_spec.rb b/spec/models/domain/post_file_thumbnail_spec.rb new file mode 100644 index 00000000..236091f5 --- /dev/null +++ b/spec/models/domain/post_file_thumbnail_spec.rb @@ -0,0 +1,171 @@ +# typed: false +require "rails_helper" + +RSpec.describe Domain::PostFileThumbnail do + describe ".find_or_create_from_post_file" do + let(:post) { create(:domain_post_fa_post) } + let(:blob_file) do + fixture_path = + Rails.root.join( + "test/fixtures/files/images/thumb-036aaab6-content-container.jpeg", + ) + create( + :blob_file, + contents: File.binread(fixture_path), + content_type: "image/jpeg", + ) + end + let(:log_entry) do + create(:http_log_entry, content_type: "image/jpeg", response: blob_file) + end + let(:post_file) do + create(:domain_post_file, post: post, state: "ok", log_entry: log_entry) + end + + before do + # Ensure directories exist + FileUtils.mkdir_p(Domain::PostFileThumbnail::TMP_DIR) + end + + it "creates a thumbnail for a valid post file" do + thumbnail = + described_class.find_or_create_from_post_file( + post_file, + Domain::ThumbnailType::Small, + ) + + expect(thumbnail).to be_a(Domain::PostFileThumbnail) + expect(thumbnail.post_file).to eq(post_file) + expect(thumbnail.thumbnail_type).to eq("small") + + # Verify the thumbnail file was created on disk + thumbnail_path = thumbnail.absolute_file_path + expect(thumbnail_path).not_to be_nil + expect(File.exist?(thumbnail_path)).to be true + end + + it "returns existing thumbnail if one already exists" do + # Create a thumbnail first + existing = + described_class.find_or_create_from_post_file( + post_file, + Domain::ThumbnailType::Small, + ) + expect(existing).not_to be_nil + + # Try to find or create again + thumbnail = + described_class.find_or_create_from_post_file( + post_file, + Domain::ThumbnailType::Small, + ) + + # Should return the existing one without creating a new one + expect(thumbnail).to eq(existing) + end + + it "returns nil for a post file that's not in 'ok' state" do + post_file.update!(state: "pending") + + thumbnail = + described_class.find_or_create_from_post_file( + post_file, + Domain::ThumbnailType::Small, + ) + + expect(thumbnail).to be_nil + end + + it "returns nil for a post file without a log entry" do + post_file.update!(log_entry: nil) + + thumbnail = + described_class.find_or_create_from_post_file( + post_file, + Domain::ThumbnailType::Small, + ) + + expect(thumbnail).to be_nil + end + + it "returns nil for a post file with an unsupported content type" do + # Create a new log entry with PDF content type since HttpLogEntry is immutable + pdf_blob = + create( + :blob_file, + content_type: "application/pdf", + contents: "fake pdf data", + ) + pdf_log_entry = + create( + :http_log_entry, + content_type: "application/pdf", + response: pdf_blob, + ) + pdf_post_file = + create( + :domain_post_file, + post: post, + state: "ok", + log_entry: pdf_log_entry, + ) + + thumbnail = + described_class.find_or_create_from_post_file( + pdf_post_file, + Domain::ThumbnailType::Small, + ) + + expect(thumbnail).to be_nil + end + + it "returns nil if the post file has no blob" do + allow(post_file).to receive(:blob).and_return(nil) + + thumbnail = + described_class.find_or_create_from_post_file( + post_file, + Domain::ThumbnailType::Small, + ) + + expect(thumbnail).to be_nil + end + end + + describe "#absolute_file_path" do + let(:post) { create(:domain_post_fa_post) } + let(:blob_file) do + create( + :blob_file, + content_type: "image/jpeg", + contents: "fake image data", + ) + end + let(:post_file) do + create(:domain_post_file, post: post, state: "ok", blob: blob_file) + end + let(:thumbnail) do + create( + :domain_post_file_thumbnail, + post_file: post_file, + thumbnail_type: "small", + ) + end + + it "constructs the correct file path" do + expect(thumbnail.absolute_file_path).to start_with( + Domain::PostFileThumbnail::THUMBNAIL_ROOT_DIR, + ) + end + + it "returns nil if thumbnail_type is nil" do + thumbnail.thumbnail_type = nil + expect(thumbnail.absolute_file_path).to be_nil + end + + it "returns nil if post_file is nil" do + thumbnail.post_file = nil + expect(thumbnail.absolute_file_path).to be_nil + end + end +end diff --git a/test/fixtures/files/images/thumb-036aaab6-content-container.jpeg b/test/fixtures/files/images/thumb-036aaab6-content-container.jpeg new file mode 100644 index 00000000..d2550108 Binary files /dev/null and b/test/fixtures/files/images/thumb-036aaab6-content-container.jpeg differ