Count(*) in find :select returns a String? Why not Fixnum?

clicks = Click.find(:all,:select=>“count(*) as count, date”, :group=>“date”)
=> [#<Click date: “2010-04-05”>, #<Click date: “2010-04-06”>, #<Click
date: “2010-04-07”>]

clicks[0].count.class
=> String

Should this return a String? I would think it would surely be a Fixnum.
Am I crazy? Is this a bug in rails?

Anyone know a way to get around this? I need to make it a Fixnum or
Float using count(*) in find. Casting it using to_i after the fact will
not work in my scenario. I need to this to be correct when it returns
the “clicks” array.

On Wed, Apr 7, 2010 at 8:27 AM, Yanni M. [email protected] wrote:

clicks = Click.find(:all,:select=>“count(*) as count, date”, :group=>“date”)
=> [#<Click date: “2010-04-05”>, #<Click date: “2010-04-06”>, #<Click
date: “2010-04-07”>]

clicks[0].count.class
=> String

Should this return a String? I would think it would surely be a Fixnum.
Am I crazy? Is this a bug in rails?

I believe ‘clicks’ is not what you think it is.

Try using inspect to examine clicks, clicks[0], etc.


Hassan S. ------------------------ [email protected]
twitter: @hassan

Hassan, thanks for quick the reply.

I am not really sure what you mean by this, though. The above example
shows that clicks returns an array of clicks. I am just taking the
first click object and seeing what data type it is using .class

Here is a closer look at the first object in the array. As you can see,
Rails converted “count(*) as count” to a String.

clicks[0].attributes
=> {“date”=>Mon, 05 Apr 2010, “count”=>“1”}

Maybe this is a MySQL issue… I am testing right now to see what the
data type of count(*) is from mysql.

On 7 April 2010 16:27, Yanni M. [email protected] wrote:

Anyone know a way to get around this? I need to make it a Fixnum or
Float using count(*) in find. Casting it using to_i after the fact will
not work in my scenario. I need to this to be correct when it returns
the “clicks” array.

Create a method that returns what you want (by manipulating the string
however is needed). particularly if you are likely to ever to need
this number in two places in the code - abstract it to stay DRY:

your model

def click_count
Click.find(:all,:select=>“count(*) as count, date”,
:group=>“date”).first.count.to_i
end

or on the Click model

def self.click_count
Click.find(:all,:select=>“count(*) as count, date”,
:group=>“date”).first.count.to_i
end

so you can use your_model.click_count, or Click.click_count and be
returned an integer.

But would you not be better using the AR “Model.count” method rather
than changing the select condition of .find?
http://api.rubyonrails.org/classes/ActiveRecord/Calculations/ClassMethods.html#M002187

your model

def click_count
Click.find(:all,:select=>“count(*) as count, date”,
:group=>“date”).first.count.to_i
end

or on the Click model

def self.click_count
Click.find(:all,:select=>“count(*) as count, date”,
:group=>“date”).first.count.to_i
end

so you can use your_model.click_count, or Click.click_count and be
returned an integer.

Michael, thanks for the reply.

Getting the first row click count does not really help since I am
grouping by date. This find statement will return many rows.

Example:
Date | Count
jan 1, 5
jan 2, 6
jan 3, 2

I could iterate thru each date and perform the find count, but that is
extremely inefficient.

On 7 April 2010 16:58, Yanni M. [email protected] wrote:

extremely inefficient.
Ah! Sorry… missed that.

Just a thought… can you be sneaky and add an accessor method to Click?

Click model

def count
read_attribute(:count).to_i rescue nil
end

No idea if it’ll work… maybe worth a bash…

Michael P. wrote:

On 7 April 2010 16:58, Yanni M. [email protected] wrote:

extremely inefficient.
Ah! Sorry… missed that.

Just a thought… can you be sneaky and add an accessor method to Click?

Click model

def count
read_attribute(:count).to_i rescue nil
end

No idea if it’ll work… maybe worth a bash…

Thanks Michael, that got me thinking! This is a hack, but it still
works for me because I can call it on one line. I am developing a
reporting system where I convert an ActiveRecord object list to a table,
so I really wanted to avoid hard coding something in each class. This
allows me to create my list on one line and then pass it to my
conversion method.

Click.find(:all,:select=>“count(*) as click_count, date”,
:group=>“date”).each{|c|c.click_count = c.click_count.to_i}

I still want to figure out why count(*) does not return a number data
type, but this will work for now.

I am also not crazy about using Model.count, because it just returns an
ordered hash. Some of my code is dependant on using AR classes when
manipulating this data set… might just have to deal with it though and
code a workaround.

On 7 April 2010 17:16, Yanni M. [email protected] wrote:

Thanks Michael, that got me thinking!

Click.find(:all,:select=>“count(*) as click_count, date”,
:group=>“date”).each{|c|c.click_count = c.click_count.to_i}

You should still look to putting that code in a method somewhere to
avoid duplication.
YMMV

I still want to figure out why count(*) does not return a number data
type, but this will work for now.

The answer’s quite straight forward - because Rails doesn’t work that
way.

The typecasting for a MySQL attribute to a Ruby class is done in the
read_attribute method in
the activerecord/lib/active_record/attribute_methods.rb file. In that
method
you can see they only typecast the value if column_for_attribute returns
the
column (i.e. if it’s defined in the schema for the table).

So, your dynamically created column is not in the schema, so it won’t be
typecast to an integer.

Cheers,

Andy

On 7 April 2010 17:03, Michael P. [email protected] wrote:

Just a thought… can you be sneaky and add an accessor method to Click?

Click model

def count
read_attribute(:count).to_i rescue nil
end

No idea if it’ll work… maybe worth a bash…

FYI: This does work. Just tried it here and I get integers returned.
No need to do the extra iteration of each result.

You might want to try an after_find() or after_initialize() filter for
ActiveRecord. If I remember right they’ll let you coerce the value to
an integer before the object gets returned to the calling code.

On Wed, Apr 7, 2010 at 9:30 AM, Andy J.
[email protected]wrote:

The typecasting for a MySQL attribute to a Ruby class is done in the
read_attribute method in
the activerecord/lib/active_record/attribute_methods.rb file. In that method
you can see they only typecast the value if column_for_attribute returns the
column (i.e. if it’s defined in the schema for the table).

Excellent info, this little tibdit helps me, too, coming to Rails for a
mod_perl world where everything is dynamically typed (“1” == 1 etc). The
typecasting logic of ActiveRecord has been a learning curve.

Anyone know of a more detailed description of how the typecasting
workflow
works, from ActiveRecord down to the MySQL level? That’d be a super
useful
document/overview to have. Each layer (app, activerecord, ruby mysql
client,
mysql itself) will have it’s own concerns on this chain of events.

In certain versions of MySQL, the typecasting can become extremely
important, due to strange bugs in MySQL. For instance, if you do
something
like

“select * from mytable where id > ‘1234’”

and 1234 is an integer column but you’ve put in quotes (i.e. a string)
for
the query, and it is outside the bounds of the index on id, MySQL will
resort to a table scan. i saw this bug wreak havoc on a large rig once,
in
mod_perl world where the database adapter put quotes around integers.

But that’s just an example of how fully understanding typecasting from
top-down can be a really useful thing.

jsw

typecasting logic of ActiveRecord has been a learning curve.

Just to be clear, I’m not a Rails-core developer, I’m just a happy user
of
apidock.com. If you go to Ruby on Rails - APIdock you can search for Rails
functions, see other user’s comments and click “View Source” to see the
methods source. It’s often a good starting point in to trying to read
the
Rails source code (which is actually very readable if you know Ruby).

If you read either of the books Ruby Best Practices or Metaprogramming
Ruby
you’ll be good to go in reading the Rails source code (I’d possibly say
the
latter is slightly better for this purpose).

Cheers,

Andy