Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion lib/permittable/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,15 @@ def columns_for(model)
# top (draft_column) must not share this rescue: an error there would
# otherwise discard EVERY column — the draft silently falls back to a
# scan alone, or to nothing — for what is one column's problem.
#
# The rescue is scoped to ActiveRecord::ActiveRecordError, same as
# ColumnGuard.schema_reachable? and for the same reason: a genuinely
# unreachable schema (no database yet, table not migrated) degrades to
# no columns, but a real bug — a broken custom type adapter, a NameError
# from a typo — must keep surfacing instead of quietly emitting an empty
# draft. The defined? guard keeps this gem loadable without
# activerecord, same as schema_reachable? (a host without it duck-types
# `model:` and cannot raise an ActiveRecordError in the first place).
def schema_columns(model)
return nil unless model.respond_to?(:columns)
return nil unless model.table_exists?
Expand All @@ -408,7 +417,9 @@ def schema_columns(model)
# into its column names; a nil primary key becomes [].
skipped = SKIPPED_COLUMNS + Array(model.primary_key).map(&:to_s)
model.columns.reject { |c| skipped.include?(c.name) }
rescue StandardError
rescue StandardError => e
raise unless defined?(ActiveRecord::ActiveRecordError) && e.is_a?(ActiveRecord::ActiveRecordError)

nil
end

Expand Down
12 changes: 12 additions & 0 deletions spec/generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,18 @@ def deserialize(_value) = raise("a type deserialize ran while drafting")
allow(GenUnreachable).to receive(:columns).and_raise(ActiveRecord::StatementInvalid, "no such table")
expect(described_class.draft(model: GenUnreachable)).to be_nil
end

it "lets a real bug from schema access propagate instead of drafting an empty contract" do
# Same rescue scope as ColumnGuard.schema_reachable?: only
# ActiveRecord::ActiveRecordError is swallowed (a genuinely
# unreachable schema). Anything else — a broken custom type
# adapter, a real app bug — must surface loudly rather than
# quietly falling back to "no columns".
stub_const("GenMisconfigured", Class.new(TestModel) { self.table_name = "gen_vehicles" })
allow(GenMisconfigured).to receive(:columns).and_raise(NoMethodError, "undefined method `type' for nil")
expect { described_class.draft(model: GenMisconfigured) }
.to raise_error(NoMethodError, /undefined method `type'/)
end
end

context "with STI and optimistic locking" do
Expand Down
Loading