@@ -114,36 +114,55 @@ def define_columns_hash(klass, config)
114114
115115 columns_hash =
116116 columns . each_with_object ( { } ) do |column , acc |
117- acc [ column [ :name ] ] =
118- if Rails . version . to_f >= 7.2
119- base_column . class . new (
120- column [ :name ] ,
121- nil ,
122- base_column . sql_type_metadata . class . new (
123- ActiveRecord ::ConnectionAdapters ::SqlTypeMetadata . new ( sql_type : column [ :column_type ] ,
124- type : column [ :column_type ] . to_sym ) )
125- )
126- else
127- ActiveRecord ::ConnectionAdapters ::Column . new (
128- column [ :name ] ,
129- nil ,
130- ActiveRecord ::ConnectionAdapters ::SqlTypeMetadata . new ( sql_type : column [ :column_type ] ,
131- type : column [ :column_type ] . to_sym )
132- )
133- end
117+ acc [ column [ :name ] ] = synthesize_column ( base_column , column [ :name ] , column [ :column_type ] )
134118 end
135119
136120 klass . instance_variable_set ( :@__motor_custom_sql_columns_hash , columns_hash )
137121
138122 # rubocop:disable Naming/MemoizedInstanceVariableName
139123 klass . instance_eval do
124+ # Expose custom_sql columns for reading/type-casting, but keep persistence
125+ # behavior based only on real DB columns.
140126 def columns_hash
141127 @__motor__columns_hash ||= @__motor_custom_sql_columns_hash . merge ( super )
142128 end
129+
130+ # Only real DB columns should be considered for persistence and strong params.
131+ def column_names
132+ connection . schema_cache . columns_hash ( table_name ) . keys
133+ end
134+
135+ # Same for the columns collection used by AR internals.
136+ def columns
137+ connection . schema_cache . columns ( table_name )
138+ end
143139 end
144140 # rubocop:enable Naming/MemoizedInstanceVariableName
145141 end
146142
143+ def synthesize_column ( template_column , name , sql_type )
144+ column = template_column . dup
145+
146+ generic_meta = ActiveRecord ::ConnectionAdapters ::SqlTypeMetadata . new (
147+ sql_type : sql_type ,
148+ type : sql_type . to_sym
149+ )
150+
151+ adapter_meta = template_column . sql_type_metadata . class . new ( generic_meta )
152+
153+ begin
154+ cast_type = ActiveRecord ::Type . lookup ( sql_type . to_sym )
155+ rescue StandardError
156+ cast_type = ActiveRecord ::Type . lookup ( :string )
157+ end
158+
159+ column . instance_variable_set ( :@name , name )
160+ column . instance_variable_set ( :@sql_type_metadata , adapter_meta )
161+ column . instance_variable_set ( :@cast_type , cast_type )
162+
163+ column
164+ end
165+
147166 def define_column_reflections ( klass , config )
148167 config . fetch ( :columns , [ ] ) . each do |column |
149168 reference = column [ :reference ]
0 commit comments