Question

I've got an AREL query that I generated with much pain. For reference (sorry):

def self.summarize_user(user)
  c   = Arel::Table.new(:categories)
  s   = Arel::Table.new(:skills)
  cp  = Arel::Table.new(:completions)

  query = c.project(c[:id], c[:name], c[:handle])
    .project(s[:id].count.as("total_skills"))
    .project(cp[:id].count.as("total_completed"))
    .project(cp[:verified_on].count.as("total_verified"))
    .join(s).on(s[:category_id].eq c[:id])
    .join(cp, Arel::Nodes::OuterJoin).on(cp[:skill_id].eq s[:id])
    .where(cp[:user_id].eq(user.id).or(cp[:user_id].eq nil))
    .group(c[:id], c[:name], c[:handle])

  # this is the relevant bit
  connection.execute(query.to_sql)
end

This executes and gives me proper results from the DB that look like this:

{ "id" => "13",
  "name" => "Category 16",
  "handle" => "category_16",
  "total_skills" => "4",
  "total_completed" => "0",
  "total_verified" => "0"
}

So, given that method is already a monster, I'd rather not try to .inject through the results to cast all the numbers into Fixnum. Is there a way, when using connection.execute, to cast fields to their proper datatypes?

Was it helpful?

Solution

You can take advantage of find_by_sql and to_json

json_records = Arel::Table.find_by_sql(query.to_sql).to_json

Then you can extract your results like

result = JSON.parse json_records

There are several ways to convert ActiveRecord objects to hash. This is just my personal preference.

OTHER TIPS

You could use Virtus

class Summary
  include Virtus.model

  attribute :id, Integer
  attribute :name, String
  attribute :handle, String
  attribute :total_skills, Integer
  attribute :total_completed, Integer
  attribute :total_verified, Integer
end

summary.map { |results| Summary.new(results) }


 hsh = { "id" => "13",
   "name" => "Category 16",
   "handle" => "category_16",
   "total_skills" => "4",
   "total_completed" => "0",
   "total_verified" => "0"
 }

 s = Summary.new(hsh)
 s.total_skills # => 4

btw, here's the method I'd prefer not to use, since the code is hard enough to understand as it is.

def self.summarize_user(user)
  # ... sins against nature ...
  result = connection.execute(query.to_sql)
  self.map_summary(result)
end

protected

def self.map_summary(summary)
  summary.map do |result|
    {
      id: result['id'].to_i,
      name: result['name'],
      handle: result['handle'],
      total_skills: result['total_skills'].to_i,
      total_completed: result['total_completed'].to_i,
      total_verified: result['total_verified'].to_i,
    }
  end
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top