Skip to content

Commit 337f32d

Browse files
StephenVNelsonshioyama
authored andcommitted
handling for string columns in Rails 8
1 parent f68470d commit 337f32d

File tree

2 files changed

+60
-11
lines changed

2 files changed

+60
-11
lines changed

lib/mobility/plugins/active_record/query.rb

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -187,21 +187,25 @@ def order(opts, *rest)
187187

188188
if ::ActiveRecord::VERSION::MAJOR >= 8
189189
# Fix for https://github.com/shioyama/mobility/pull/654#issuecomment-2503479112
190-
# TODO: Make this better
190+
# When counting with select values that include mobility-translated attributes,
191+
# we need to filter out the aliased columns that Mobility adds (e.g. __mobility_title_en__)
192+
# to prevent errors in the COUNT SQL generation.
191193
def select_for_count
192194
return super unless klass.respond_to?(:mobility_attribute?)
193195

194-
if select_values.any? { |value| value.right.start_with?(ATTRIBUTE_ALIAS_PREFIX) }
195-
filtered_select_values = select_values.map do |value|
196-
value.right.start_with?(ATTRIBUTE_ALIAS_PREFIX) ? value.left : value
197-
end
196+
# Check if any select values are Mobility attribute aliases
197+
has_mobility_aliases = select_values.any? { |value| value.respond_to?(:right) && value.right.start_with?(ATTRIBUTE_ALIAS_PREFIX) }
198198

199-
# Copied from lib/active_record/relation/calculations.rb
200-
with_connection do |conn|
201-
arel_columns(filtered_select_values).map { |column| conn.visitor.compile(column) }.join(", ")
202-
end
203-
else
204-
super
199+
return super unless has_mobility_aliases
200+
201+
# Filter out Mobility aliases, replacing them with the underlying columns
202+
filtered_select_values = select_values.map do |value|
203+
value.respond_to?(:right) && value.right.start_with?(ATTRIBUTE_ALIAS_PREFIX) ? value.left : value
204+
end
205+
206+
# Copied from lib/active_record/relation/calculations.rb
207+
with_connection do |conn|
208+
arel_columns(filtered_select_values).map { |column| conn.visitor.compile(column) }.join(", ")
205209
end
206210
end
207211
end

spec/mobility/plugins/active_record/query_spec.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,4 +218,49 @@
218218
expect(Article.i18n.where(title: "Title", content: "Content")).to eq([article2])
219219
end
220220
end
221+
222+
describe "regression: select_for_count with mixed select values" do
223+
# Test for bug where select_for_count called .right on string values
224+
# causing NoMethodError when select_values contained non-Arel nodes
225+
if ::ActiveRecord::VERSION::MAJOR >= 8
226+
before do
227+
stub_const 'Article', Class.new(ActiveRecord::Base)
228+
translates Article, :title, backend: :table
229+
end
230+
231+
it "handles count with regular column in select" do
232+
Article.create(title: "Article 1")
233+
Article.create(title: "Article 2")
234+
235+
# The original bug: calling .right on string values like "id"
236+
# This should not raise NoMethodError: undefined method `right' for "id":String
237+
expect { Article.select(:id).count }.not_to raise_error
238+
expect(Article.select(:id).count).to eq(2)
239+
end
240+
241+
it "handles count with translated column in select" do
242+
Article.create(title: "Article 1")
243+
Article.create(title: "Article 2")
244+
245+
# When selecting translated attributes, Mobility creates Arel::Nodes::As with aliases
246+
# This should properly filter out the aliased columns for counting
247+
relation = Article.i18n.select(:title)
248+
249+
expect { relation.count }.not_to raise_error
250+
expect(relation.count).to eq(2)
251+
end
252+
253+
it "handles count without select when mobility is present" do
254+
Article.create(title: "Article 1")
255+
Article.create(title: "Article 2")
256+
257+
# Ensure normal counting still works on models with mobility
258+
expect { Article.count }.not_to raise_error
259+
expect(Article.count).to eq(2)
260+
261+
expect { Article.i18n.count }.not_to raise_error
262+
expect(Article.i18n.count).to eq(2)
263+
end
264+
end
265+
end
221266
end

0 commit comments

Comments
 (0)