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.

I believe 'clicks' is not what you think it is.

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

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

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.

# 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.

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...

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.

Michael Pavling 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.

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

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.

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

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.

Just to be clear, I’m not a Rails-core developer, I’m just a happy user of apidock.com. If you go to apidock.com/rails 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