DSL for scans with intervals

This commit is contained in:
Dylan Knutson
2025-02-25 04:35:58 +00:00
parent 41324f019f
commit 4dbdb68514
31 changed files with 372 additions and 148 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1 @@
**/*.rbi linguist-generated=true

View File

@@ -0,0 +1,23 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for dynamic methods in `ActiveSupport::Callbacks`.
# Please instead update this file by running `bin/tapioca dsl ActiveSupport::Callbacks`.
module ActiveSupport::Callbacks
include GeneratedInstanceMethods
mixes_in_class_methods GeneratedClassMethods
module GeneratedClassMethods
def __callbacks; end
def __callbacks=(value); end
def __callbacks?; end
end
module GeneratedInstanceMethods
def __callbacks; end
def __callbacks?; end
end
end

View File

@@ -83,15 +83,15 @@ class Domain::Fa::ApiController < ApplicationController
object_url: request.base_url + helpers.domain_user_path(user),
page_scan: {
last_at: time_ago_or_never(user.scanned_page_at),
due_for_scan: user.due_for_page_scan?,
due_for_scan: user.page_scan.due?,
},
gallery_scan: {
last_at: time_ago_or_never(user.scanned_gallery_at),
due_for_scan: user.due_for_gallery_scan?,
last_at: time_ago_or_never(user.gallery_scan.at),
due_for_scan: user.gallery_scan.due?,
},
favs_scan: {
last_at: time_ago_or_never(user.scanned_favs_at),
due_for_scan: user.due_for_favs_scan?,
last_at: time_ago_or_never(user.favs_scan.at),
due_for_scan: user.favs_scan.due?,
},
}
else
@@ -348,12 +348,12 @@ class Domain::Fa::ApiController < ApplicationController
)
profile_thumb_url = parser.user_page.profile_thumb_url
else
if user.due_for_follows_scan?
if user.follows_scan.due?
Domain::Fa::Job::UserFollowsJob.set(
{ priority: -20 },
).perform_later({ user: user })
end
if user.due_for_page_scan?
if user.page_scan.due?
Domain::Fa::Job::UserPageJob.set({ priority: -20 }).perform_later(
{ user: user },
)

View File

@@ -33,6 +33,8 @@ module Domain::DomainsHelper
gumroad.com
bigcartel.com
furaffinity.net
boosty.to
hipolink.me
].freeze
DOMAIN_TO_ICON_PATH =
@@ -56,6 +58,7 @@ module Domain::DomainsHelper
"redbubble.com" => "redbubble.png",
"spreadshirt.de" => "spreadshirt.png",
"spreadshirt.com" => "spreadshirt.png",
"boosty.to" => "boosty.png",
}.freeze,
T::Hash[String, String],
)

View File

@@ -90,44 +90,93 @@ module Domain::UsersHelper
"#{domain_user_path(user)}/favorites"
end
sig { params(user: Domain::User).returns(T::Array[[String, Integer]]) }
class StatRow < T::ImmutableStruct
const :name, String
const :value,
T.nilable(
T.any(String, Integer, HasTimestampsWithDueAt::TimestampScanInfo),
)
const :link_to, T.nilable(String)
const :fa_icon_class, T.nilable(String)
const :hover_title, T.nilable(String)
end
sig { params(user: Domain::User).returns(T::Array[StatRow]) }
def stat_rows_for_user(user)
rows = []
rows << ["Favorites", user.user_post_favs.count] if user.has_faved_posts?
rows = T.let([], T::Array[StatRow])
if user.has_faved_posts?
rows << StatRow.new(name: "Favorites", value: user.user_post_favs.count)
end
if user.has_followed_users?
rows << ["Following", user.user_user_follows_from.count]
rows << StatRow.new(
name: "Following",
value: user.user_user_follows_from.count,
)
end
if user.has_followed_by_users?
rows << ["Followed by", user.user_user_follows_to.count]
rows << StatRow.new(
name: "Followed by",
value: user.user_user_follows_to.count,
)
end
can_view_timestamps = policy(user).view_page_scanned_at_timestamps?
can_view_log_entries = policy(user).view_log_entries?
icon_for =
Kernel.proc do |due_for_scan|
due_for_scan ? "fa-hourglass-half" : "fa-check"
end
if user.is_a?(Domain::User::FaUser) && can_view_timestamps
rows << ["Favorites", time_ago_or_never(user.scanned_favs_at)]
rows << ["Gallery scanned", time_ago_or_never(user.scanned_gallery_at)]
rows << StatRow.new(
name: "Favs scanned",
value: user.favs_scan,
fa_icon_class: icon_for.call(user.favs_scan.due?),
hover_title: user.favs_scan.interval.inspect,
)
rows << StatRow.new(
name: "Gallery scanned",
value: user.gallery_scan,
fa_icon_class: icon_for.call(user.gallery_scan.due?),
hover_title: user.gallery_scan.interval.inspect,
)
rows << StatRow.new(
name: "Follows scanned",
value: user.follows_scan,
fa_icon_class: icon_for.call(user.follows_scan.due?),
hover_title: user.follows_scan.interval.inspect,
)
if can_view_log_entries && hle = user.guess_last_user_page_log_entry
rows << [
"Page scanned",
raw(
link_to(
time_ago_or_never(user.scanned_page_at),
log_entry_path(hle),
class: "blue-link",
),
),
]
rows << StatRow.new(
name: "Page scanned",
value: user.page_scan,
link_to: log_entry_path(hle),
fa_icon_class: icon_for.call(user.page_scan.due?),
hover_title: user.page_scan.interval.inspect,
)
else
rows << ["Page scanned", time_ago_or_never(user.scanned_page_at)]
rows << StatRow.new(
name: "Page scanned",
value: user.page_scan,
fa_icon_class: icon_for.call(user.page_scan.due?),
hover_title: user.page_scan.interval.inspect,
)
end
elsif user.is_a?(Domain::User::E621User) && can_view_timestamps
if user.favs_are_hidden
rows << ["Favorites hidden", "yes"]
rows << StatRow.new(name: "Favorites hidden", value: "yes")
else
rows << ["Server favorites", user.num_other_favs_cached]
rows << StatRow.new(
name: "Server favorites",
value: user.num_other_favs_cached,
)
end
rows << ["Favorites scanned", time_ago_or_never(user.scanned_favs_at)]
rows << StatRow.new(
name: "Favorites scanned",
value: user.favs_scan,
fa_icon_class: icon_for.call(user.favs_scan.due?),
hover_title: user.favs_scan.interval.inspect,
)
end
rows

View File

@@ -86,15 +86,15 @@ class Domain::Fa::Job::Base < Scraper::JobBase
sig { params(user: Domain::User::FaUser).returns(T::Boolean) }
def user_due_for_favs_scan?(user)
unless user.due_for_favs_scan?
unless user.favs_scan.due?
if force_scan?
logger.warn(
"scanned favs #{DateHelper.time_ago_in_words(user.scanned_favs_at).bold} ago - force scanning",
"scanned favs #{user.favs_scan.ago_in_words.bold} ago - force scanning",
)
return true
else
logger.warn(
"scanned favs #{DateHelper.time_ago_in_words(user.scanned_favs_at).bold} ago - skipping",
"scanned favs #{user.favs_scan.ago_in_words.bold} ago - skipping",
)
return false
end
@@ -232,47 +232,40 @@ class Domain::Fa::Job::Base < Scraper::JobBase
{ url_name: user.url_name }
end
if user.due_for_page_scan? &&
defer_job(Domain::Fa::Job::UserPageJob, args)
if user.page_scan.due? && defer_job(Domain::Fa::Job::UserPageJob, args)
logger.info(
format_tags(
"enqueue user page job",
make_tag("last scanned", time_ago_in_words(user.scanned_page_at)),
make_tag("last page scan", user.page_scan.ago_in_words),
),
)
end
if user.due_for_gallery_scan? &&
if user.gallery_scan.due? &&
defer_job(Domain::Fa::Job::UserGalleryJob, args)
logger.info(
format_tags(
"enqueue user gallery job",
make_tag(
"last scanned",
time_ago_in_words(user.scanned_gallery_at),
),
make_tag("last gallery scan", user.gallery_scan.ago_in_words),
),
)
end
if user.due_for_follows_scan? &&
if user.follows_scan.due? &&
defer_job(Domain::Fa::Job::UserFollowsJob, args)
logger.info(
format_tags(
"enqueue user follows job",
make_tag(
"last scanned",
time_ago_in_words(user.scanned_follows_at),
),
make_tag("last follows scan", user.follows_scan.ago_in_words),
),
)
end
if user.due_for_favs_scan? && defer_job(Domain::Fa::Job::FavsJob, args)
if user.favs_scan.due? && defer_job(Domain::Fa::Job::FavsJob, args)
logger.info(
format_tags(
"enqueue user favs job",
make_tag("last scanned", time_ago_in_words(user.scanned_favs_at)),
make_tag("last favs scan", user.favs_scan.ago_in_words),
),
)
end

View File

@@ -27,10 +27,8 @@ class Domain::Fa::Job::UserFollowsJob < Domain::Fa::Job::Base
def perform(args)
user = user_from_args!
if !user.due_for_follows_scan? && !force_scan?
logger.warn(
"scanned #{time_ago_in_words(user.scanned_follows_at)}, skipping",
)
if !user.follows_scan.due? && !force_scan?
logger.warn("scanned #{user.follows_scan.ago_in_words}, skipping")
return
end

View File

@@ -37,17 +37,10 @@ class Domain::Fa::Job::UserGalleryJob < Domain::Fa::Job::Base
# buggy (sentinal) user
return if user.id == 117_552 && user.url_name == "click here"
@go_until_end = user.scanned_gallery_at.nil?
if (num_submissions = user.num_submissions) &&
(scanned_page_at = user.scanned_page_at) &&
(scanned_page_at > 3.days.ago)
@max_page_number = [@max_page_number, (num_submissions * 72) + 3].max
end
@go_until_end = user.gallery_scan.at.nil?
if !user.due_for_gallery_scan? && !force_scan?
logger.warn(
"gallery scanned #{time_ago_in_words(user.scanned_page_at)}, skipping",
)
if !user.gallery_scan.due? && !force_scan?
logger.warn("gallery scanned #{user.gallery_scan.ago_in_words}, skipping")
return
end

View File

@@ -19,13 +19,10 @@ module Domain::Fa::Job
# - follows / following: look at the 'watchers' / 'watching' section,
# and add new follows.
if !user.due_for_incremental_scan? && !force_scan?
if !user.incremental_scan.due? && !force_scan?
logger.warn(
format_tags(
make_tag(
"incremental scanned",
time_ago_in_words(user.scanned_incremental_at),
),
make_tag("incremental scanned", user.incremental_scan.ago_in_words),
"scanned recently, skipping",
),
)

View File

@@ -14,10 +14,8 @@ class Domain::Fa::Job::UserPageJob < Domain::Fa::Job::Base
# buggy (sentinal) user
return if user.id == 117_552 && user.url_name == "click here"
if !user.due_for_page_scan? && !force_scan?
logger.warn(
"scanned #{time_ago_in_words(user.scanned_page_at)}, skipping",
)
if !user.page_scan.due? && !force_scan?
logger.warn("scanned #{user.page_scan.ago_in_words}, skipping")
return
end

View File

@@ -44,26 +44,26 @@ class Domain::Fa::UserEnqueuer
rows.each do |user|
types = []
if user.state == "ok"
if user.due_for_favs_scan? || user.due_for_page_scan? ||
user.due_for_follows_scan?
if user.favs_scan.due? || user.page_scan.due? ||
user.follows_scan.due?
Domain::Fa::Job::UserIncrementalJob.perform_later({ user: user })
types << "incremental"
end
if user.due_for_favs_scan?
if user.favs_scan.due?
Domain::Fa::Job::FavsJob.perform_later({ user: user })
types << "favs"
end
if user.due_for_page_scan?
if user.page_scan.due?
Domain::Fa::Job::UserPageJob.perform_later({ user: user })
types << "page"
end
if user.due_for_gallery_scan?
if user.gallery_scan.due?
Domain::Fa::Job::UserGalleryJob.perform_later({ user: user })
types << "gallery"
end
if user.due_for_follows_scan?
if user.follows_scan.due?
Domain::Fa::Job::UserFollowsJob.perform_later({ user: user })
types << "follows"
end

View File

@@ -0,0 +1,62 @@
# typed: strict
# frozen_string_literal: true
module HasTimestampsWithDueAt
extend T::Sig
extend T::Helpers
extend ActiveSupport::Concern
requires_ancestor { ReduxApplicationRecord }
class TimeHelper
extend ActionView::Helpers::DateHelper
end
class TimestampScanInfo < T::ImmutableStruct
extend T::Sig
const :at, T.nilable(ActiveSupport::TimeWithZone)
const :interval, ActiveSupport::Duration
sig { returns(String) }
def ago_in_words
at = self.at
at ? TimeHelper.time_ago_in_words(at) + " ago" : "never"
end
sig { returns(T::Boolean) }
def due?
at = self.at
at ? at < interval.ago : true
end
end
protected
included do
T.unsafe(self).class_attribute :due_at_timestamp_fields, default: []
sig(:final) do
params(field: Symbol, interval: ActiveSupport::Duration).void
end
def self.attr_json_due_timestamp(field, interval)
field_pattern = /scanned_(.*)_at/
field_name = field_pattern.match(field.to_s)&.[](1)&.to_sym
raise "Invalid field name: #{field}" if field_name.nil?
# define the attribute
T.unsafe(self).attr_json field, :datetime
T
.unsafe(self)
.define_method(:"#{field_name}_scan") do
T.bind(self, HasTimestampsWithDueAt)
TimestampScanInfo.new(at: read_attribute(field), interval:)
end
T.unsafe(self).due_at_timestamp_fields =
(T.unsafe(self).due_at_timestamp_fields || {}).merge(
{ field_name => interval },
)
end
end
end

View File

@@ -5,6 +5,7 @@ class Domain::User < ReduxApplicationRecord
include HasCompositeToParam
include HasViewPrefix
include HasDescriptionHtmlForView
include HasTimestampsWithDueAt
self.table_name = "domain_users"
abstract!
@@ -12,7 +13,8 @@ class Domain::User < ReduxApplicationRecord
class_attribute :class_has_created_posts,
:class_has_faved_posts,
:class_has_followed_users,
:class_has_followed_by_users
:class_has_followed_by_users,
:due_at_timestamp_fields
sig(:final) { returns(T::Boolean) }
def has_created_posts?

View File

@@ -5,7 +5,7 @@ class Domain::User::E621User < Domain::User
attr_json :favs_are_hidden, :boolean
attr_json :num_other_favs_cached, :integer
attr_json :scanned_favs_status, :string
attr_json :scanned_favs_at, :datetime
attr_json_due_timestamp :scanned_favs_at, 1.month
attr_json :registered_at, :datetime
enum :scanned_favs_status, { ok: "ok", error: "error" }, prefix: :scanned_favs

View File

@@ -14,11 +14,11 @@ class Domain::User::FaUser < Domain::User
attr_json :num_comments_given, :integer
attr_json :num_journals, :integer
attr_json :num_favorites, :integer
attr_json :scanned_gallery_at, :datetime
attr_json :scanned_page_at, :datetime
attr_json :scanned_follows_at, :datetime
attr_json :scanned_favs_at, :datetime
attr_json :scanned_incremental_at, :datetime
attr_json_due_timestamp :scanned_gallery_at, 1.year
attr_json_due_timestamp :scanned_page_at, 1.month
attr_json_due_timestamp :scanned_follows_at, 1.month
attr_json_due_timestamp :scanned_favs_at, 1.month
attr_json_due_timestamp :scanned_incremental_at, 1.month
attr_json :registered_at, :datetime
attr_json :migrated_followed_users_at, :datetime
attr_json :last_user_page_id, :integer
@@ -87,31 +87,6 @@ class Domain::User::FaUser < Domain::User
[name, url_name, full_name].compact
end
sig { returns(T::Boolean) }
def due_for_favs_scan?
due_for_scan?(scanned_favs_at, 1.month.ago)
end
sig { returns(T::Boolean) }
def due_for_follows_scan?
due_for_scan?(scanned_follows_at, 1.month.ago)
end
sig { returns(T::Boolean) }
def due_for_page_scan?
due_for_scan?(scanned_page_at, 1.month.ago)
end
sig { returns(T::Boolean) }
def due_for_gallery_scan?
due_for_scan?(scanned_gallery_at, 1.year.ago)
end
sig { returns(T::Boolean) }
def due_for_incremental_scan?
due_for_scan?(scanned_incremental_at, 1.month.ago)
end
sig do
params(
submission_parser:
@@ -182,16 +157,4 @@ class Domain::User::FaUser < Domain::User
def name_for_view
name || url_name
end
private
sig do
params(
at: T.nilable(ActiveSupport::TimeWithZone),
ago: ActiveSupport::TimeWithZone,
).returns(T::Boolean)
end
def due_for_scan?(at, ago)
at ? at < ago : true
end
end

View File

@@ -1,11 +1,34 @@
<section class="sky-section animated-shadow-sky divide-y">
<h2 class="section-header">User Stats</h2>
<% stat_rows_for_user(user).each do |value_label, value| %>
<% stat_rows_for_user(user).each do |stat_row| %>
<% label = stat_row.name %>
<% value = stat_row.value %>
<% fa_icon_class = stat_row.fa_icon_class %>
<div class="flex items-center px-4 py-2">
<span class="grow text-slate-900"><%= value_label %></span>
<span class="text-slate-500">
<%= value.is_a?(Integer) ? number_with_delimiter(value, delimiter: ",") : value %>
</span>
</div>
<% end %>
<span class="grow text-slate-900"><%= label %></span>
<span class="text-slate-500 relative group">
<% value_str = case value %>
<% when Integer %>
<% number_with_delimiter(value, delimiter: ",") %>
<% when HasTimestampsWithDueAt::TimestampScanInfo %>
<% value.ago_in_words %>
<% else %>
<% value %>
<% end %>
<% if stat_row.link_to %>
<%= link_to value_str, stat_row.link_to, class: "blue-link" %>
<% else %>
<%= value_str %>
<% end %>
<% if fa_icon_class %>
<i class="fa-solid <%= fa_icon_class %>"></i>
<% end %>
<% if stat_row.hover_title %>
<div class="absolute hidden group-hover:block bg-slate-800 text-white text-sm rounded px-2 py-1 top-1/2 -translate-y-1/2 right-full mr-2 whitespace-nowrap">
<%= stat_row.hover_title %>
</div>
<% end %>
</span>
</div>
<% end %>
</section>

View File

@@ -29,16 +29,19 @@ class ApplicationController
include ::Webpacker::Helper
include ::ActionController::Base::HelperMethods
include ::ApplicationHelper
include ::HelpersInterface
include ::LogEntriesHelper
include ::Domain::UsersHelper
include ::PathsHelper
include ::Domain::DomainsHelper
include ::Domain::PostsHelper
include ::Domain::DescriptionsHelper
include ::Domain::E621::PostsHelper
include ::Domain::Fa::PostsHelper
include ::Domain::Fa::UsersHelper
include ::HelpersInterface
include ::Domain::ModelHelper
include ::Domain::PaginationHelper
include ::Domain::PostGroupsHelper
include ::LogEntriesHelper
include ::Domain::UsersHelper
include ::Domain::PostsHelper
include ::DomainSourceHelper
include ::GoodJobHelper
include ::IndexablePostsHelper

View File

@@ -26,16 +26,19 @@ class DeviseController
include ::Webpacker::Helper
include ::ActionController::Base::HelperMethods
include ::ApplicationHelper
include ::HelpersInterface
include ::LogEntriesHelper
include ::Domain::UsersHelper
include ::PathsHelper
include ::Domain::DomainsHelper
include ::Domain::PostsHelper
include ::Domain::DescriptionsHelper
include ::Domain::E621::PostsHelper
include ::Domain::Fa::PostsHelper
include ::Domain::Fa::UsersHelper
include ::HelpersInterface
include ::Domain::ModelHelper
include ::Domain::PaginationHelper
include ::Domain::PostGroupsHelper
include ::LogEntriesHelper
include ::Domain::UsersHelper
include ::Domain::PostsHelper
include ::DomainSourceHelper
include ::GoodJobHelper
include ::IndexablePostsHelper

View File

@@ -38,6 +38,9 @@ class Domain::User
end
def attr_json_config(default_container_attribute: nil, bad_cast: nil, unknown_key: nil); end
sig { params(field: Symbol, interval: ActiveSupport::Duration).void }
def attr_json_due_timestamp(field, interval); end
sig { returns(T::Array[Symbol]) }
def attr_json_registry; end

View File

@@ -12,6 +12,9 @@ class Domain::User::E621User
extend CommonRelationMethods
extend GeneratedRelationMethods
sig { returns(HasTimestampsWithDueAt::TimestampScanInfo) }
def favs_scan; end
sig { returns(ColorLogger) }
def logger; end
@@ -39,6 +42,9 @@ class Domain::User::E621User
end
def attr_json_config(default_container_attribute: nil, bad_cast: nil, unknown_key: nil); end
sig { params(field: Symbol, interval: ActiveSupport::Duration).void }
def attr_json_due_timestamp(field, interval); end
sig { returns(T::Array[Symbol]) }
def attr_json_registry; end

View File

@@ -12,9 +12,24 @@ class Domain::User::FaUser
extend CommonRelationMethods
extend GeneratedRelationMethods
sig { returns(HasTimestampsWithDueAt::TimestampScanInfo) }
def favs_scan; end
sig { returns(HasTimestampsWithDueAt::TimestampScanInfo) }
def follows_scan; end
sig { returns(HasTimestampsWithDueAt::TimestampScanInfo) }
def gallery_scan; end
sig { returns(HasTimestampsWithDueAt::TimestampScanInfo) }
def incremental_scan; end
sig { returns(ColorLogger) }
def logger; end
sig { returns(HasTimestampsWithDueAt::TimestampScanInfo) }
def page_scan; end
private
sig { returns(NilClass) }
@@ -39,6 +54,9 @@ class Domain::User::FaUser
end
def attr_json_config(default_container_attribute: nil, bad_cast: nil, unknown_key: nil); end
sig { params(field: Symbol, interval: ActiveSupport::Duration).void }
def attr_json_due_timestamp(field, interval); end
sig { returns(T::Array[Symbol]) }
def attr_json_registry; end

View File

@@ -38,6 +38,9 @@ class Domain::User::InkbunnyUser
end
def attr_json_config(default_container_attribute: nil, bad_cast: nil, unknown_key: nil); end
sig { params(field: Symbol, interval: ActiveSupport::Duration).void }
def attr_json_due_timestamp(field, interval); end
sig { returns(T::Array[Symbol]) }
def attr_json_registry; end

View File

@@ -84,6 +84,9 @@ module GeneratedPathHelpersModule
sig { params(args: T.untyped).returns(String) }
def favorites_user_posts_path(*args); end
sig { params(args: T.untyped).returns(String) }
def furecs_user_script_path(*args); end
sig { params(args: T.untyped).returns(String) }
def global_state_path(*args); end
@@ -133,7 +136,7 @@ module GeneratedPathHelpersModule
def pg_hero_path(*args); end
sig { params(args: T.untyped).returns(String) }
def pool_path(*args); end
def post_group_posts_path(*args); end
sig { params(args: T.untyped).returns(String) }
def post_path(*args); end

View File

@@ -84,6 +84,9 @@ module GeneratedUrlHelpersModule
sig { params(args: T.untyped).returns(String) }
def favorites_user_posts_url(*args); end
sig { params(args: T.untyped).returns(String) }
def furecs_user_script_url(*args); end
sig { params(args: T.untyped).returns(String) }
def global_state_url(*args); end
@@ -133,7 +136,7 @@ module GeneratedUrlHelpersModule
def pg_hero_url(*args); end
sig { params(args: T.untyped).returns(String) }
def pool_url(*args); end
def post_group_posts_url(*args); end
sig { params(args: T.untyped).returns(String) }
def post_url(*args); end

View File

@@ -28,6 +28,8 @@ class GoodJob::JobsController
include ::HelpersInterface
include ::LogEntriesHelper
include ::Domain::UsersHelper
include ::PathsHelper
include ::Domain::DomainsHelper
include ::Domain::PostsHelper
include ::Domain::PostGroupsHelper
include ::GoodJobHelper

View File

@@ -0,0 +1,24 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for dynamic methods in `HasTimestampsWithDueAt`.
# Please instead update this file by running `bin/tapioca dsl HasTimestampsWithDueAt`.
module HasTimestampsWithDueAt
include GeneratedInstanceMethods
mixes_in_class_methods GeneratedClassMethods
module GeneratedClassMethods
def due_at_timestamp_fields; end
def due_at_timestamp_fields=(value); end
def due_at_timestamp_fields?; end
end
module GeneratedInstanceMethods
def due_at_timestamp_fields; end
def due_at_timestamp_fields=(value); end
def due_at_timestamp_fields?; end
end
end

View File

@@ -29,16 +29,19 @@ class Rails::ApplicationController
include ::Webpacker::Helper
include ::ActionController::Base::HelperMethods
include ::ApplicationHelper
include ::HelpersInterface
include ::LogEntriesHelper
include ::Domain::UsersHelper
include ::PathsHelper
include ::Domain::DomainsHelper
include ::Domain::PostsHelper
include ::Domain::DescriptionsHelper
include ::Domain::E621::PostsHelper
include ::Domain::Fa::PostsHelper
include ::Domain::Fa::UsersHelper
include ::HelpersInterface
include ::Domain::ModelHelper
include ::Domain::PaginationHelper
include ::Domain::PostGroupsHelper
include ::LogEntriesHelper
include ::Domain::UsersHelper
include ::Domain::PostsHelper
include ::DomainSourceHelper
include ::GoodJobHelper
include ::IndexablePostsHelper

View File

@@ -29,16 +29,19 @@ class Rails::Conductor::BaseController
include ::Webpacker::Helper
include ::ActionController::Base::HelperMethods
include ::ApplicationHelper
include ::HelpersInterface
include ::LogEntriesHelper
include ::Domain::UsersHelper
include ::PathsHelper
include ::Domain::DomainsHelper
include ::Domain::PostsHelper
include ::Domain::DescriptionsHelper
include ::Domain::E621::PostsHelper
include ::Domain::Fa::PostsHelper
include ::Domain::Fa::UsersHelper
include ::HelpersInterface
include ::Domain::ModelHelper
include ::Domain::PaginationHelper
include ::Domain::PostGroupsHelper
include ::LogEntriesHelper
include ::Domain::UsersHelper
include ::Domain::PostsHelper
include ::DomainSourceHelper
include ::GoodJobHelper
include ::IndexablePostsHelper

View File

@@ -29,16 +29,19 @@ class Rails::HealthController
include ::Webpacker::Helper
include ::ActionController::Base::HelperMethods
include ::ApplicationHelper
include ::HelpersInterface
include ::LogEntriesHelper
include ::Domain::UsersHelper
include ::PathsHelper
include ::Domain::DomainsHelper
include ::Domain::PostsHelper
include ::Domain::DescriptionsHelper
include ::Domain::E621::PostsHelper
include ::Domain::Fa::PostsHelper
include ::Domain::Fa::UsersHelper
include ::HelpersInterface
include ::Domain::ModelHelper
include ::Domain::PaginationHelper
include ::Domain::PostGroupsHelper
include ::LogEntriesHelper
include ::Domain::UsersHelper
include ::Domain::PostsHelper
include ::DomainSourceHelper
include ::GoodJobHelper
include ::IndexablePostsHelper

View File

@@ -0,0 +1,42 @@
# typed: strict
module Tapioca::Compilers
class HasTimestampsWithDueAtDsl < Tapioca::Dsl::Compiler
extend T::Sig
ConstantType =
type_member { { fixed: T.class_of(::HasTimestampsWithDueAt) } }
sig { override.returns(T::Enumerable[Module]) }
def self.gather_constants
all_classes.select { |c| c < ::HasTimestampsWithDueAt }
end
sig { override.void }
def decorate
root.create_path(constant) do |klass|
klass.create_method(
"attr_json_due_timestamp",
parameters: [
create_param("field", type: "Symbol"),
create_param("interval", type: "ActiveSupport::Duration"),
],
class_method: true,
return_type: "void",
)
fields =
T.cast(
T.unsafe(constant).due_at_timestamp_fields || {},
T::Hash[Symbol, ActiveSupport::Duration],
)
fields.each do |field, interval|
klass.create_method(
"#{field}_scan",
return_type: "HasTimestampsWithDueAt::TimestampScanInfo",
)
end
end
end
end
end