Module: ConcernsOnRails::Support::ColumnGuard
- Included in:
- Models::Addressable::ClassMethods, Models::Aliasable::ClassMethods, Models::Anonymizable::ClassMethods, Models::Auditable::ClassMethods, Models::CounterCacheable::ClassMethods, Models::Duplicable::ClassMethods, Models::Encryptable::ClassMethods, Models::Lockable::ClassMethods, Models::SoftDeletable::ClassMethods, Models::Sortable::ClassMethods, Models::Stateable::ClassMethods, Models::Storable::ClassMethods, Models::Taggable::ClassMethods
- Defined in:
- lib/concerns_on_rails/support/column_guard.rb
Overview
Shared schema-validation helper mixed into a concern's ClassMethods.
Runs in class context, so column_names / table_name resolve against
the including model. Centralizes the column-existence check that every
model concern used to re-implement, and keeps the error wording uniform.
class_methods do
include ConcernsOnRails::Support::ColumnGuard
def activatable_by(field = :active)
self.activatable_field = field.to_sym
ensure_columns!("ConcernsOnRails::Models::Activatable", activatable_field)
end
end
When the schema is unreachable — no database yet (db:create, a fresh
db:migrate, assets:precompile, CI bootstrap) or the table not yet
migrated — the check is skipped and false is returned instead of
raising, so model classes stay loadable. A missing column with a
reachable schema still raises: the rescue is scoped to
ActiveRecord::ActiveRecordError precisely so real bugs (NameError from a
typo etc.) keep surfacing. Skipping is self-healing: once the migration
runs and classes reload, validation happens for real — and a genuinely
missing column still fails loudly on first query.
The phrase "does not exist" is preserved so existing specs that match /does not exist/ keep passing.
Instance Method Summary collapse
-
#column_migration_hint(klass, field, types) ⇒ Object
" Add it with: bin/rails generate migration AddDeletedAtToArticles deleted_at:datetime" — every missing-column failure becomes a copy-paste fix.
-
#ensure_columns!(concern, *fields, types: nil) ⇒ Object
types:teaches the error message: a Symbol/String applies to every listed field, a Hash maps field => type. -
#ensure_columns_on!(concern, klass, *fields, types: nil) ⇒ Object
Same contract, validated against another class (e.g. CounterCacheable checks the counter column on the parent model).
-
#schema_reachable?(klass = self) ⇒ Boolean
True when the class's table can actually be inspected.
Instance Method Details
#column_migration_hint(klass, field, types) ⇒ Object
" Add it with: bin/rails generate migration AddDeletedAtToArticles deleted_at:datetime" — every missing-column failure becomes a copy-paste fix. Without a known type the column name goes out bare (the generator defaults to string).
60 61 62 63 64 65 |
# File 'lib/concerns_on_rails/support/column_guard.rb', line 60 def column_migration_hint(klass, field, types) type = types.is_a?(Hash) ? types[field.to_sym] : types column = [field, type].compact.join(":") " Add it with: bin/rails generate migration " \ "Add#{field.to_s.camelize}To#{klass.table_name.to_s.camelize} #{column}" end |
#ensure_columns!(concern, *fields, types: nil) ⇒ Object
types: teaches the error message: a Symbol/String applies to every
listed field, a Hash maps field => type. When present, the raised
ArgumentError appends a ready-to-paste migration command. Generator
column-modifier syntax is welcome ("string:uniq" — tokens/slugs want a
unique index anyway).
37 38 39 |
# File 'lib/concerns_on_rails/support/column_guard.rb', line 37 def ensure_columns!(concern, *fields, types: nil) ensure_columns_on!(concern, self, *fields, types: types) end |
#ensure_columns_on!(concern, klass, *fields, types: nil) ⇒ Object
Same contract, validated against another class (e.g. CounterCacheable checks the counter column on the parent model).
43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/concerns_on_rails/support/column_guard.rb', line 43 def ensure_columns_on!(concern, klass, *fields, types: nil) return false unless schema_reachable?(klass) fields.flatten.compact.each do |field| next if klass.column_names.include?(field.to_s) raise ArgumentError, "#{concern}: '#{field}' does not exist in the database (table: #{klass.table_name})." \ "#{column_migration_hint(klass, field, types)}" end true end |
#schema_reachable?(klass = self) ⇒ Boolean
True when the class's table can actually be inspected. Connection errors (ConnectionNotEstablished, NoDatabaseError, adapter errors) all inherit from ActiveRecord::ActiveRecordError.
70 71 72 73 74 |
# File 'lib/concerns_on_rails/support/column_guard.rb', line 70 def schema_reachable?(klass = self) klass.table_exists? rescue ActiveRecord::ActiveRecordError false end |