rails console no method each for model class - ruby-on-rails

Why should this happen:
Sms.each do |sms|
sms.time_stamp=Time.at(sms.time_stamp_long/1000)
sms.save
end
undefined method `each' for Sms:Class (NoMethodError)

It's better to use find_each
Sms.find_each do |sms|
sms.update_columns(time_stamp: Time.at(sms.time_stamp_long / 1000)
end

What you really should be doing here is fixing the regrettable descision that lead you to store EPOCH instead of actual date/time types in the first place with minimal involvement from Rails.
MYSQL has the FROM_UNIXTIME function that takes EPOCH time as input and outputs a date. Use this in a single query which will update all the rows:
Sms.update_all(
'time_stamp = FROM_UNIXTIME(time_stamp_long)'
).where(time_stamp: nil)
This will be an faster then pulling each record out of the database and then creating a UPDATE query for each row by an order of magnitude.
Once you're done drop the time_stamp_long column.

looks like you are trying to call each on the Sms class itself, rather than on an instance of Sms or a collection of Sms objects try this
Sms.all.each do |sms|
sms.time_stamp = Time.at(sms.time_stamp_long/1000)
sms.save
end```

Related

Rails console update

Hello I was trying the update the data in the table using the rails console.
Box.where("code = 'learning'").update(duration: 10)
I ran this command.
The data is temporarily changing.
Box.where("code = 'learning'")
When I run this the precious data is being displayed.
Could anyone let me the issue.
Thank you in advance.
#update updates a single record.
user = User.find_by(name: 'David')
user.update(name: 'Dave')
It will return true/false depending on if the record was actually updated. You can see the validation errors by inspecting the errors object:
user.errors.full_messages
In non user-interactions situations like seed files and the console it can be helpful to use the bang methods such as #update!, #save! and #create! which will raise an exception if the record is invalid.
If you want to update multiple records at once you need to use #update_all:
Box.where("code = 'learning'")
.update_all(duration: 10)
This creates a single SQL update statement and is by far the most performant option.
You can also iterate through the records:
Box.where("code = 'learning'").find_each do |box|
box.update(duration: 10)
end
This is sometimes necissary if the value you are updating must be calculated in the application. But it is much slower as it creates N+1 database queries.

Rails custom model method in where query

In my rails app I have defined in the Kid model a calculation based on the fields from the Kids DB. the method is as follows:
def flip_date
self.dob.advance(months: 10)
end
I want to use this in my controller as I have a method where I am defining something as follows:
new_kids = Kid.where(discharge_date: nil).where('flip_date > ?', Date.current.advance(year: 1).beginning_of_year)
However I keep getting the following error:
SQLite3::SQLException: no such column: flip_date: SELECT "kids".* FROM "kids" WHERE "kids"."discharge_date" IS NULL AND (flip_date < '2017-01-01')
Any ideas on how can I make this work? All help is appreciated!
If you really want to use model methods take a look at http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select
For your case:
new_kids = Kid.where(discharge_date: nil).select{|k| k.flip_date > Date.current.advance(year: 1).beginning_of_year}
But select method takes every object in memory before returning final result. Hence I will advise to use normal where clause and instead of flip_date take dob (which is a column in database) in consideration.
Like this
new_kids = Kid.where(discharge_date: nil).where('dob > ?', <date criteria>)
The select method (http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select) works great if you are okay with the return being an Array.
I am still looking for a way to do this with an ActiveRecord_Relation return.
If others know how to do this, it would be much appreciated if you can share.
This example doesn't respond to your specific code, but to the extent it helps someone else with a similar question, here's a very simple example of how .select can be really handy:
#expired_memberships = User.select{|u| u.membership_expired_yesterday?}
In that example you've looped through all your Users and filtered them based on a custom method you defined on the User model (membership_expired_yesterday?). Now you can easily do stuff with that collection like this example in a mailer:
#expirations.each do |user|
MembershipExpirationMailer.with(user: user).first_reminder.deliver_now
end

Ruby on Rails - ActiveRecord::Relation count method is wrong?

I'm writing an application that allows users to send one another messages about an 'offer'.
I thought I'd save myself some work and use the Mailboxer gem.
I'm following a test driven development approach with RSpec. I'm writing a test that should ensure that only one Conversation is allowed per offer. An offer belongs_to two different users (the user that made the offer, and the user that received the offer).
Here is my failing test:
describe "after a message is sent to the same user twice" do
before do
2.times { sending_user.message_user_regarding_offer! offer, receiving_user, random_string }
end
specify { sending_user.mailbox.conversations.count.should == 1 }
end
So before the test runs a user sending_user sends a message to the receiving_user twice. The message_user_regarding_offer! looks like this:
def message_user_regarding_offer! offer, receiver, body
conversation = offer.conversation
if conversation.nil?
self.send_message(receiver, body, offer.conversation_subject)
else
self.reply_to_conversation(conversation, body)
# I put a binding.pry here to examine in console
end
offer.create_activity key: PublicActivityKeys.message_received, owner: self, recipient: receiver
end
On the first iteration in the test (when the first message is sent) the conversation variable is nil therefore a message is sent and a conversation is created between the two users.
On the second iteration the conversation created in the first iteration is returned and the user replies to that conversation, but a new conversation isn't created.
This all works, but the test fails and I cannot understand why!
When I place a pry binding in the code in the location specified above I can examine what is going on... now riddle me this:
self.mailbox.conversations[0] returns a Conversation instance
self.mailbox.conversations[1] returns nil
self.mailbox.conversations clearly shows a collection containing ONE object.
self.mailbox.conversations.count returns 2?!
What is going on there? the count method is incorrect and my test is failing...
What am I missing? Or is this a bug?!
EDIT
offer.conversation looks like this:
def conversation
Conversation.where({subject: conversation_subject}).last
end
and offer.conversation_subject:
def conversation_subject
"offer-#{self.id}"
end
EDIT 2 - Showing the first and second iteration in pry
Also...
Conversation.all.count returns 1!
and:
Conversation.all == self.mailbox.conversations returns true
and
Conversation.all.count == self.mailbox.conversations.count returns false
How can that be if the arrays are equal? I don't know what's going on here, blown hours on this now. Think it's a bug?!
EDIT 3
From the source of the Mailboxer gem...
def conversations(options = {})
conv = Conversation.participant(#messageable)
if options[:mailbox_type].present?
case options[:mailbox_type]
when 'inbox'
conv = Conversation.inbox(#messageable)
when 'sentbox'
conv = Conversation.sentbox(#messageable)
when 'trash'
conv = Conversation.trash(#messageable)
when 'not_trash'
conv = Conversation.not_trash(#messageable)
end
end
if (options.has_key?(:read) && options[:read]==false) || (options.has_key?(:unread) && options[:unread]==true)
conv = conv.unread(#messageable)
end
conv
end
The reply_to_convesation code is available here -> http://rubydoc.info/gems/mailboxer/frames.
Just can't see what I'm doing wrong! Might rework my tests to get around this. Or ditch the gem and write my own.
see this Rails 3: Difference between Relation.count and Relation.all.count
In short Rails ignores the select columns (if more than one) when you apply count to the query. This is because
SQL's COUNT allows only one or less columns as parameters.
From Mailbox code
scope :participant, lambda {|participant|
select('DISTINCT conversations.*').
where('notifications.type'=> Message.name).
order("conversations.updated_at DESC").
joins(:receipts).merge(Receipt.recipient(participant))
}
self.mailbox.conversations.count ignores the select('DISTINCT conversations.*') and counts the join table with receipts, essentially counting number of receipts with duplicate conversations in it.
On the other hand, self.mailbox.conversations.all.count first gets the records applying the select, which gets unique conversations and then counts it.
self.mailbox.conversations.all == self.mailbox.conversations since both of them query the db with the select.
To solve your problem you can use sending_user.mailbox.conversations.all.count or sending_user.mailbox.conversations.group('conversations.id').length
I have tended to use the size method in my code. As per the ActiveRecord code, size will use a cached count if available and also returns the correct number when models have been created through relations and have not yet been saved.
# File activerecord/lib/active_record/relation.rb, line 228
def size
loaded? ? #records.length : count
end
There is a blog on this here.
In Ruby, #length and #size are synonyms and both do the same thing: they tell you how many elements are in an array or hash. Technically #length is the method and #size is an alias to it.
In ActiveRecord, there are several ways to find out how many records are in an association, and there are some subtle differences in how they work.
post.comments.count - Determine the number of elements with an SQL COUNT query. You can also specify conditions to count only a subset of the associated elements (e.g. :conditions => {:author_name => "josh"}). If you set up a counter cache on the association, #count will return that cached value instead of executing a new query.
post.comments.length - This always loads the contents of the association into memory, then returns the number of elements loaded. Note that this won't force an update if the association had been previously loaded and then new comments were created through another way (e.g. Comment.create(...) instead of post.comments.create(...)).
post.comments.size - This works as a combination of the two previous options. If the collection has already been loaded, it will return its length just like calling #length. If it hasn't been loaded yet, it's like calling #count.
It is also worth mentioning to be careful if you are not creating models through associations, as the related model will not necessarily have those instances in its association proxy/collection.
# do this
mailbox.conversations.build(attrs)
# or this
mailbox.conversations << Conversation.new(attrs)
# or this
mailbox.conversations.create(attrs)
# or this
mailbox.conversations.create!(attrs)
# NOT this
Conversation.new(mailbox_id: some_id, ....)
I don't know if this explains what's going on, but the ActiveRecord count method queries the database for the number of records stored. The length of the Relation could be different, as discussed in http://archive.railsforum.com/viewtopic.php?id=6255, although in that example, the number of records in the database was less than the number of items in the Rails data structure.
Try
self.mailbox.conversations.reload; self.mailbox.conversations.count
or perhaps
self.mailbox.reload; self.mailbox.conversations.count
or, if neither of those work, just try reloading as many of the objects as possible to see if you can get it to work (self, mailbox, conversations, etc.).
My guess is that something is messed up between memory and the DB. This is definitely a really weird error though, might wanna put in an issue on Rails to see why this would be the case.
The result of mailbox.conversations is cached after the first call. To reload it write mailbox.conversations(true)

How do I do auto conversion of Rails time to Unix epoch time?

I have a Rails app that makes heavy use of the created_at and updated_at fields.
I've found that something like Post.last.created_at.to_f will give me epoch time but I always need epoch time so I was wondering if there is some way to write an automated post-query filter that will do the conversion every time I called created_at and update_at. Right now, every time I read created_at/updated_at I repeat myself, which is bad form, and has already caused bugs when I forget to do the conversion.
I'm using Rails 3.2.13 and Ruby 1.9.3p392.
Also, I can't just write post.created_at.to_f in my view since I'm using render JSON for my output.
Why don't you just make a method you can patch in to ActiveRecord::Base?
def created_epoch
self.created_at.to_f
end
Then you won't have to remember to convert, you can just use that method instead.
Personally I believe approach suggested by #tadman is a better one.
created_at method can be as follows to do what you want:
def created_at
created_at= attributes["created_at"]
created_at ? created_at.to_f : nil
end
Place it in the models or attach it to ActiveRecord::Base. Carefully test it before putting to production.

Using Rails update_all method to update fields with value from another column

I'm trying to update a field in using update_all. However I need the value to be taken from another field which is re-written to my specific format.
If I have something like this in my model:
def self.clean_mac_address()
clean_mac_address = :macaddress.gsub(/[^0-9a-z]/i, '')
end
When I run this:
Radacct.update_all("mac_clean = #{clean_mac_address}")
I get an error:
NoMethodError: undefined method `gsub' for :macaddress:Symbol
Any thoughts how I can do this? Or is there a simpler way to update the field?
update_all generates a single SQL query to run - it can't do clever stuff like change arbitrary bits of ruby into equivalent SQL.
You either need to load all you instances (via find_each for example) and fix them one by one (ie don't use update_all), for example
Foo.find_each do |foo|
# update foo here
foo.save!
end
Or find a way of expressing that cleaning operation in SQL. For example Postgres has a regexp_replace function
Foo.update_all("some_column = regexp_replace(some_column, 'your_regexp_here', '','g')")
Which would remove everything replacing that regexp. Obviously you'll need to check the documentation for your database to see whether it supports such a feature.
While the accepted answer provides a nice way to update_all, what I'd use is
read_with_clean_addr = Radacct.where(mac_clean: :macaddress.gsub(/[^0-9a-z]/i, ''))
read_with_clean_add.update_all(mac_clean: "#{clean_mac_address}")

Resources