Module: Familia::Features::Housekeeping::ModelClassMethods
- Defined in:
- lib/familia/features/housekeeping.rb
Overview
Housekeeping::ModelClassMethods
Instance Method Summary collapse
-
#chore(name, callable = nil) {|obj| ... } ⇒ #call
Register a chore by name.
-
#chores ⇒ Hash{Symbol => #call}
Registered chores in registration order.
-
#run_chores!(chore_name: nil, limit: nil, batch_size: DEFAULT_BATCH_SIZE) ⇒ Hash
Run registered chores against every record reachable via the
instancesclass-level sorted set.
Instance Method Details
#chore(name, callable = nil) {|obj| ... } ⇒ #call
Register a chore by name. Takes either a block or any callable (a class or object responding to +call+) -- both receive the instance. Callables allow shipping reusable chores as classes, e.g. Housekeeping::EnforceCollectionCaps.
75 76 77 78 79 80 81 82 83 84 |
# File 'lib/familia/features/housekeeping.rb', line 75 def chore(name, callable = nil, &block) raise ArgumentError, 'chore name required' if name.nil? || name.to_s.empty? raise ArgumentError, "chore #{name.inspect} takes a callable or a block, not both" if callable && block handler = callable || block raise ArgumentError, "chore #{name.inspect} requires a callable or block" unless handler raise ArgumentError, "chore #{name.inspect} callable must respond to #call" unless handler.respond_to?(:call) chores[name.to_sym] = handler end |
#chores ⇒ Hash{Symbol => #call}
Registered chores in registration order. Subclasses inherit a copy of their parent's chores on first access, so registering a new chore on a subclass does not mutate the parent.
91 92 93 94 95 96 97 |
# File 'lib/familia/features/housekeeping.rb', line 91 def chores @chores ||= if superclass.respond_to?(:chores) superclass.chores.dup else {} end end |
#run_chores!(chore_name: nil, limit: nil, batch_size: DEFAULT_BATCH_SIZE) ⇒ Hash
Run registered chores against every record reachable via the
instances class-level sorted set. Records are loaded in pipelined
batches (load_multi) and each chore is executed per record with
error isolation -- one failing chore call does not abort the run.
Stats shape:
{ model: 'User', scanned: 4200, chores: { downcase_email: { modified: 37, errors: 0 }, default_timezone: { modified: 102, errors: 1 }, }, }
The modified counter increments when the chore block returns a
truthy value (the conventional "modified" signal). The errors
counter increments when the chore block raises; the exception is
logged via Familia.warn and iteration continues.
Requires the class to expose an instances collection (Horreum's
default class-level sorted set) and load_multi (Horreum default).
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/familia/features/housekeeping.rb', line 130 def run_chores!(chore_name: nil, limit: nil, batch_size: DEFAULT_BATCH_SIZE) unless respond_to?(:instances) && respond_to?(:load_multi) raise ArgumentError, "#{name} cannot run_chores! without instances and load_multi" end chore_keys = resolve_chore_keys(chore_name) stats = chore_keys.to_h { |key| [key, { modified: 0, errors: 0 }] } scanned = 0 instances.to_a.each_slice(batch_size) do |batch_ids| break if limit && scanned >= limit batch_ids = batch_ids.take(limit - scanned) if limit records = load_multi(batch_ids).compact records.each do |record| scanned += 1 chore_keys.each do |key| begin stats[key][:modified] += 1 if record.do_chore!(key) rescue StandardError => e stats[key][:errors] += 1 Familia.warn "[run_chores!] #{name}##{record.identifier} chore=#{key} failed: #{e.}" end end end end { model: name, scanned: scanned, chores: stats } end |