Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Column aliases #27

Closed
wants to merge 3 commits into from
Closed
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
9 changes: 6 additions & 3 deletions lib/active_record/virtual_attributes/virtual_fields.rb
Original file line number Diff line number Diff line change
Expand Up @@ -200,17 +200,20 @@ def find_with_associations(&block)
end

# From ActiveRecord::QueryMethods
# would be nice to get this monkey patch into arel_column
def select(*fields)
return super if block_given? || fields.empty?
# support virtual attributes by adding an alias to the sql phrase for the column
# it does not add an as() if the column already has an as
# this code is based upon _select()
fields.flatten!
fields.map! do |field|
if virtual_attribute?(field) && (arel = klass.arel_attribute(field)) && arel.respond_to?(:as)
arel.as(field.to_s)
else
if !virtual_attribute?(field) || !(arel = klass.arel_attribute(field))
field
elsif arel.respond_to?(:as) && !arel.try(:alias)
arel.as(connection.quote_column_name(field.to_s))
else
arel
end
end
# end support virtual attributes
Expand Down
60 changes: 60 additions & 0 deletions spec/virtual_attributes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,66 @@ def col2
end
end

it "supports non valid sql column names", :with_test_class do
TestClass.create(:str => "ABC")
TestClass.virtual_attribute :"lower column", :string, :arel => ->(t) { t[:str].lower }
class TestClass
define_method("lower column") { has_attribute?(:"lower column") ? self[:"lower column"] : str.downcase }
end

# testing both the select and the where
tc = TestClass.select("lower column").order(:"lower column").find_by(:"lower column" => "abc")
expect(tc.send("lower column")).to eq("abc")
end

context "arel", "aliases" do
it "supports aliased virtual attribute arel with functions", :with_test_class do
class TestClass
# using an alias is not suggested. it will fail if used in an `order` or `where` clauses
virtual_attribute :lc, :string, :arel => ->(t) { t[:str].lower.as("downcased") }
def lc
has_attribute?(:downcased) ? self[:downcased] : str.downcase
end
end

obj = TestClass.create(:str => "ABC")

tc = TestClass.select(:lc).find_by(:id => obj.id)
expect(tc.lc).to eq("abc")
end

# grouping is the most common way to define arel
it "supports aliased virtual attribute arel with grouping", :with_test_class do
class TestClass
# using an alias is not suggested. it will fail if used in an `order` or `where` clauses
virtual_attribute :lc, :string, :arel => ->(t) { Arel.sql("(#{t[:str].lower.to_sql})") }
def lc
has_attribute?(:lc) ? self[:lc] : str.downcase
end
end

obj = TestClass.create(:str => "ABC")

tc = TestClass.select(:lc).find_by(:id => obj.id)
expect(tc.lc).to eq("abc")
end

it "supports aliased virtual attribute arel with nodes", :with_test_class do
class TestClass
# using an alias is not suggested. it will fail if used in an `order` or `where` clauses
virtual_attribute :lc, :string, :arel => ->(t) { Arel::Nodes::As.new(t[:str].lower, Arel.sql("downcased")) }
def lc
has_attribute?(:downcased) ? self[:downcased] : str.downcase
end
end

obj = TestClass.create(:str => "ABC")

tc = TestClass.select(:lc).find_by(:id => obj.id)
expect(tc.lc).to eq("abc")
end
end

it "doesn't botch up the attributes", :with_test_class do
tc = TestClass.select(:id, :str).find(TestClass.create(:str => "abc", :col1 => 55).id)
expect(tc.attributes.size).to eq(2)
Expand Down