Trouble on counting ActiveRecord instances in an array - ruby-on-rails

I am using Ruby on Rails 3 and I would like to solve a issue counting ActiveRecord instances in an array.
I have this code
data = Account.where({:name => "Test_name", :city => "Test_city"}).limit(10)
The data debug is
#<Account:0x000001029d2da0>#<Account:0x000001029d2c60>#<Account:0x000001029d2bc0>#<Account:0x000001029d2b20>
The data inspecting is
"[#<Account name: \"Test_name\", city: \"Test_city\">, #<Account … >, #<Account id… >, …]"
Doubt: The ##<...> should be something like #<Account...>,#<Account...>,<...> (note commas)?
If in my code I use the following
data_count = data.count
The data_count is
nil
Why is it nil? How should I count accounts?
If I use result = data.class the debug of result is nil, but if I use result = data.classthe debug is "{\"inheritable_attributes\":{}}".
If I use Account.find_by_name("Test_name") instead of Account.where(...) I get same results as above.

To get to the bottom of things, start the rails console with:
$ rails c
Given that Account is an ActiveRecord model, you should be able to do the following in the rails console:
> Account.all.count
=> 100
> Account.where(:status=>'active')
=> [ #<Account id: 1, name: "a1", ...>, #<Account id: 2, name: "a2", ...>, #<Account id: 3, name: "a3", ...>, ...]
I'm doing a lot of hand waving here with ... since I don't know your schema. Replace the where condition with whatever works for your situation. The returned value should look like an array with a list of all the rows in the database that match the condition. BTW, an array is a list of element, and inspect (as well as the default display in the console) show element separated by commas. I haven't used debug so I can't comment on what it should do.
You can verify that the returned value is an AREL, and should be able to do some other operations to verify things work as expected.
> Account.where(:status=>'active').class
=> ActiveRecord::Relation
> Account.where(:status=>'active').size
=> 99
> Account.where(:status=>'active').count
=> 99
> Account.where(:status=>'active').limit(10).count
=> 10
If these work as expected in the console, there may be something in the view that is obscuring the correct behavior. In that case you'll need to post the details of your view code. If the strange behavior still occurs in the console, I would suggest posting the minimal parts of the actual model code that still exhibit the problem, along with the migration so we can see the schema.

I think you are having some problem in where condition.
Can you show the attributes value used in where clause.
For me its working fine:
data = Account.where('id != 0').limit(10)
data_count = data.count

Use the following:
data = Account.where("id = 2 and email = 'test_email#test.com'")

Related

Update Each Array-Object Value in Rails

Basically I want to update each table column for a Model in Rails 5.
str = "abc---def"
str.split('---').map do |a|
Foo.where(product_id:1).update_all(bar: a)
end
Old object would be like:
[
[0] { product_id: 1,
...,
bar: "xxx",
...
},
[1] { product_id: 1,
...,
bar: "xxx",
...
}
]
New should be like:
[
[0] { product_id: 1,
...,
bar: "abc",
...
},
[1] { product_id: 1,
...,
bar: "def",
...
}
]
But what I got is bar: "def" for each. Is there a clean method in rails to achieve what I want? update_attributes gives an error.
Is the title name correct?
First of all let's get started from some basics.
You want to update multiple rows and want to set different value for each row. So it cannot be done in single query like you are doing. So you need to loop through the Foo objects and set each one separately.
So let's assume
str = "abc---def---ghi---jkl"
tokens = str.split('---')
foos_to_update = Foo.where(product_id: 1) #Let's assume it will return 4 or lesser records. (otherwise you need to tell what do you wanna do if it returns more then `tokens`)
foos_to_update.each_with_index {|foo,i| foo.update(bar: tokens[i])}
The last line is looping through returned objects and setting the bar value for each object.
First of all, using Foo.where(id:1).update_all to update a single record may work, but is non-idiomatic. It's better to use Foo.find_by(id: 1).update. For getting single records, I prefer to use find_by instead of find because it returns nil instead of raising NotFound errors, but that's a personal preference.
Second, the way you're using update_all(bar: a) is giving you unexpected results. In a map block, the returned value becomes part of the resulting array. update_all doesn't return the record which were changed. It returns an integer showing the count of records which were changed. Similarly, update doesn't return the record. It returns true or false` depending on if the validations passed.
Tying together these concepts, the following code can be written:
str = "abc---def"
str.split('---').map do |a|
foo = Foo.find_by(id:1)
foo&.update(bar: a)
foo
end
# note that you could instead write `foo.update(bar: a)` if you
# don't want to use the safe navigation operator
Or another way to write it which does the same thing:
str = "abc---def"
str.split('---').map do |a|
Foo.find_by(id:1)&.tap { |foo| foo.update(bar: a) }
end
Note that in these examples I'm using the safe navigation operator which is in Ruby versions newer than 2.3. It helps prevent NoMethodError on nil objects, but isn't really necessary.

Hashie::Rash element access

New to working with Hashie::Rash. I understand how to access elements in the "results" section of the following hashie, but how can I access the "count" element before the "results" in the following example:
hashie => #<Hashie::Rash count=20 page=1 results=[#<Hashie::Rash customer=#<Hashie::Rash addresses=[] custom_external_id="58749" emails=[#<Hashie::Rash email=#<Hashie::Rash created_at="2013-02-13T15:59:26-08:00" email="CENSORED" id=33622514 updated_at="2013-02-13T15:59:26-08:00" verified_at=nil>>] first_name="CENSORED" id=68712186 language=nil last_name="CENSORED" phones=[#<Hashie::Rash phone=#<Hashie::Rash created_at="2013-02-13T16:00:45-08:00" id=1301079 phone="CENSORED" updated_at="2013-02-13T16:00:45-08:00">>] twitters=[nil]>>, #<Hashie::Rash customer=#<Hashie::Rash addresses=[] custom_external_id="58749" emails=[] first_name="CENSORED" id=71095620 language=nil last_name="CENSORED" phones=[] twitters=[nil]>>] total=2>
Hashie::Rash still acts like a Hash for many things so #count still uses Hash#count which you can see with your result being 9.
hashie = Hashie::Rash.new({count:20, page: 1, results:["a","b","c"]})
#=> #<Hashie::Rash count=20 page=1 results=["a", "b", "c"]>
hashie.count #uses Hash#count
#=> 3
but since Hashie::Rash is at its cores still a Hash (just with Truely Indifferent Access) it also maintains quite a few based methods for access such as Hash#[]
hashie['count']
#=> 20
hashie[:count]
#=> 20
hashie['page'] == hashie.page
#=> true
Your answer works as well because Hash#values returns an Array but this seems like an unnecessary step and lacks readability and manageability.
Not particularly eloquent, but in the above example,
hashie.values[1] returns 20
Hope this helps someone else someday.

rspec/shoulda fails comparing two seemingly identical collections

(mentioning shoulda because I know it is in Gemfile - but don't know how it affects rspec tests, have no experience with it, don't know if it's involved here or not)
I have two [controller] tests:
first:
it { should assign_to( :messages ).with(
current_user.messages.ordered.decorate ) }
and second:
it { should assign_to( :messages ).with(
current_user.messages.order("send_at DESC").decorate ) }
note: the message.rb defines:
scope :ordered, order( 'messages.send_at DESC' )
,so they are testing exactly the same thing - except that ordered method used to sort by a wrong field, and the first test would always pass, expected collection being the same as the actual; hence the 2nd test, which explicitly demands the correct sorting order.
Now, the madness: 1st test passes, 2nd test fails. Rspec prints:
Expected action to assign #<DecoratedEnumerableProxy of
MessageDecorator for [#<Message id: 1, ... >, <Message id: 2, ...>]>,
but got #<DecoratedEnumerableProxy of MessageDecorator for [#<Message
id: 1, ... >, <Message id: 2, ...>]>
You guessed it - the "expected.." and "but got.." parts printed here are absolutely identical. I've triple-checked them with diff. If both tests were failing, I would not be so perplexed, but 1st one passes, and 2nd one fails. I suspected that 1st test must be producing some kind of a side effect, but no, reordering or commenting out one of the tests doesn't change anything.
Any ideas?

mongodb: converting object ID's to BSON::ObjectId

We have recently upgraded our application to Rails3 and we are now using Mongoid, and we have a problem retrieving previous documents from mongo-db by _id.
Upon closer investigation, an old record (which I can't retrieve by _id) look as follows:
#<Audit::Log _id: 4d892bfe6bcaff4ffd000001,
failed: nil, request_id: "68ccb38e9e345bb7fc55331389a902a1",
session_id: "54940ff7e8c7336d813a872db7cb7bc0",
_id: "4d892bfe6bcaff4ffd000001", ... }>
and a good record has the following structure:
#<Audit::Log _id: 4d892bfe6bcaff4ffd000001,
failed: nil, request_id: "68ccb38e9e345bb7fc55331389a902a1",
session_id: "54940ff7e8c7336d813a872db7cb7bc0",
_id: BSON::ObjectId('4d892bfe6bcaff4ffd000001'), ... }>
As you can see, the _id field is different. For the old records it seems to be just a string, and for the new records it is a BSON::ObjectID.
I can't seem to be able to retrieve the records with the old format. Trying to find the records using
Audit::Log.where(:_id => BSON::ObjectId('4d892bfe6bcaff4ffd000001')).first
Audit::Log.where(:_id => '4d892bfe6bcaff4ffd000001').first
both return nil.
But for the good record, I can just do
Audit::Log.where(:'_id' => '4e14152d6bcaff26bb000039').first
I am guessing, but maybe Mongoid automatically converts the string to an ObjectId to find on _id? The only fix I see would be to convert
all the _id-fields to BSON::ObjectId if they are not already. But how do I do that?
Or do you have a better approach?
All of these will work, provided the record actually exists:
Account.where(:_id => "4e0a9c6142f5bc769f000008").first
Account.find(BSON::ObjectId("4e0a9c6142f5bc769f000008"))
Account.find("4e0a9c6142f5bc769f000008")
I'm interested in the JSON returned about a Audit::Log... Why are there two _id fields returned?
#<Audit::Log _id: 4d892bfe6bcaff4ffd000001,
failed: nil, request_id: "68ccb38e9e345bb7fc55331389a902a1",
session_id: "54940ff7e8c7336d813a872db7cb7bc0",
_id: "4d892bfe6bcaff4ffd000001", ... }>
You may want to drop to the mongo driver and see if this log truly exists in the database. Unless you are declaring another "_id" field in the audit_log.rb, I believe this record does not exist.
Ha, I should have looked further at the Mongoid documentation. They have a section describing how to upgrade. In the section to upgrade to 2.0.0.BETA.11 + they describe that the _id's now no longer are String and they point to this gist to convert all your ids from string to ObjectId.
Ex: Here is an id
1.9.3-p125 :096 > profile_id
=> “4fe969dd79216d0af9000002″
1.9.3-p125 :099 > BSON::ObjectId.from_string(profile_id)
=> BSON::ObjectId(’4fe969dd79216d0af9000002′)

find_by_foo failing to return some records (but not others) in Rails 3

In my Rails app I have a model called Cycle with a "start" attribute that is a date. I'm running into a very strange problem where sometimes Cycle.find_by_start will return the expected record, but at other times it will return nil.
For example Cycle.find_by_start("2011-05-01") returns the following:
=> #<Cycle id: 45, created_at: "2011-05-15 22:38:35",
updated_at: "2011-05-15 22:38:35", user_id: 20,
start: "2011-05-01", ending: nil, startguess: false, endingguess: nil>
But running Cycle.find_by_start("2011-05-13") returns nil, even though there is a record with a matching start value. I've verified that the record exists and the start value matches by running the following at the Rails console.
irb(main):012:0> Cycle.find(47)
=> #<Cycle id: 47, created_at: "2011-05-23 01:28:59",
updated_at: "2011-06-21 00:38:34", user_id: 12,
start: "2011-05-13", ending: "2011-05-31", startguess: false, endingguess: false>
irb(main):011:0> Cycle.find(47).start == "2011-05-13".to_date
=> true
Possibly relevant info: Running Rails 3.0.7 in development mode with an SQLite database.
Any ideas or troubleshooting tips?
Edit 1
Log of the SQL queries used:
[94m19:10:11 active_record [37mCycle Load (1.0ms) SELECT "cycles".* FROM "cycles" WHERE "cycles"."start" = '2011-05-01' LIMIT 1
[94m19:10:19 active_record [37mCycle Load (0.0ms) SELECT "cycles".* FROM "cycles" WHERE "cycles"."start" = '2011-05-13' LIMIT 1
Dates.... you may have a parsing problem, with US/UK formats getting swapped around and confusing things. I often find it helps to make the date unambiguous (assuming English months):
Cycle.find_by_start("13 May 2011")
If :start is a date field, then it's best to pass find_by_start an actual Date object rather than a string. So:
Cycle.find_by_start(Date.parse("2011-05-13"))
(I'm using Date.parse to create the date object here, but you could also use Date.new or Date.today or some other method)
Passing a string to the finder method might work but, as you've discovered, might also not - depending on the database type and how the database interprets the string.

Resources