Module: ConcernsOnRails::Models::Lockable::ClassMethods
- Includes:
- Support::ColumnGuard
- Defined in:
- lib/concerns_on_rails/models/lockable.rb
Instance Method Summary collapse
-
#lockable_by(attempts: DEFAULT_ATTEMPTS_FIELD, locked_at: DEFAULT_LOCKED_AT_FIELD, max_attempts: DEFAULT_MAX_ATTEMPTS, unlock_in: nil, prefix: nil, suffix: nil) ⇒ Object
Configure the lockout columns and policy.
-
#unlock_expired ⇒ Object
Unlock every row whose lock window has fully elapsed, clearing locked_at and zeroing the attempts counter exactly as unlock_access! does.
Methods included from Support::ColumnGuard
#column_migration_hint, #ensure_columns!, #ensure_columns_on!, #schema_reachable?
Instance Method Details
#lockable_by(attempts: DEFAULT_ATTEMPTS_FIELD, locked_at: DEFAULT_LOCKED_AT_FIELD, max_attempts: DEFAULT_MAX_ATTEMPTS, unlock_in: nil, prefix: nil, suffix: nil) ⇒ Object
Configure the lockout columns and policy. See the module docs.
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/concerns_on_rails/models/lockable.rb', line 72 def lockable_by(attempts: DEFAULT_ATTEMPTS_FIELD, locked_at: DEFAULT_LOCKED_AT_FIELD, max_attempts: DEFAULT_MAX_ATTEMPTS, unlock_in: nil, prefix: nil, suffix: nil) attempts = attempts.to_sym locked_at = locked_at.to_sym validate_lockable!(attempts, locked_at, max_attempts: max_attempts, unlock_in: unlock_in) self.lockable_attempts_field = attempts self.lockable_locked_at_field = locked_at self.lockable_max_attempts = max_attempts self.lockable_unlock_in = unlock_in ensure_columns!(LABEL, attempts, locked_at, types: { attempts => :integer, locked_at => :datetime }) validate_lockable_attempts_column!(attempts) define_lockable_scopes(prefix, suffix) end |
#unlock_expired ⇒ Object
Unlock every row whose lock window has fully elapsed, clearing locked_at and zeroing the attempts counter exactly as unlock_access! does. Returns the Integer count.
Nothing expires when unlock_in is nil (manual unlock only), so that case returns 0 without touching the database. The boundary instant counts as expired, matching lock_expired? and the scopes.
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/concerns_on_rails/models/lockable.rb', line 95 def unlock_expired unlock_in = lockable_unlock_in return 0 unless unlock_in locked_field = lockable_locked_at_field attempts_field = lockable_attempts_field # `lteq` on a NULL locked_at is NULL, so never-locked rows are # excluded without an extra predicate. expired = all.where(arel_table[locked_field].lteq(Time.zone.now - unlock_in)) # Ownership-only check (no validations gate, and no updated_at on the # bulk write): `unlock_access!` writes via update_columns, which # already skips validations and timestamps, so the two paths agree. if ConcernsOnRails::Support::BatchOps.unoverridden?(self, ConcernsOnRails::Models::Lockable, :before_unlock, :after_unlock, :unlock_access!) return expired.update_all(locked_field => nil, attempts_field => 0) end ConcernsOnRails::Support::BatchOps.run( expired, label: LABEL, message: "failed to unlock record", &:unlock_access! ) end |