Reputation: 303
I'm having a weird issue, my app seem to get stuck in a loop.
I have a model called Pages which allows the user to input a URL on save I have a after_save method called process_pages which looks as follows
class Page < ActiveRecord::Base
require 'open-uri'
after_save :process_pages
def process_pages
self.html = open(self.url).read
self.save
end
end
On saving the URL I can see in the development console that it get the HTML of the site but tries to constantly save the record over and over again stalling the page and I have to manually exit the server.
When I start back up again the record has been added and works as expected until I add another URL?
Is there anything wrong with my code which might be causing this continuous loop?
Thanks for reading!
Upvotes: 2
Views: 3778
Reputation: 53048
If you need to call process_pages
only once after Creating a Page
record and not after Updating a Page record then I would suggest to use after_create
instead.
class Page < ActiveRecord::Base
require 'open-uri'
after_create :process_pages
def process_pages
self.html = open(self.url).read
self.save
end
end
With after_save :process_pages
, process_pages
method would be called every time you save a Page record. You are saving a Page record again within process_pages
method which triggers the after_save
callback and you start looping.
See this SO Question for difference between after_save and after_create. You will understand better as to why you are going in loops.
Upvotes: 1
Reputation: 44725
This is because you are resaving it all the time. Instead do:
def process_pages
update_column(:html, open(self.url).read)
end
update_column
is saving one column to the database skipping all the callbacks, so it won't retrigger your after_save callback. It is however pretty pointless to do two queries while you can do one with before_save
filter:
before_save :process_pages
def process_pages
self.html = open(self.url).read
end
But probably best way to go with is overriding setter for url
method:
def url=(value)
super
self.html = open(self.url).read
end
This way you can use page html before the model is saved.
Upvotes: 3
Reputation: 35359
You are stuck in a loop because your callback is triggered after save, and then the method definition itself is calling save, causing the callback to fire again.
You have some options. You can change the callback to before_save
.
class Page < ActiveRecord::Base
require 'open-uri'
before_save :process_pages
def process_pages
self.html = open(self.url).read
end
end
You can change to an after_create
callback that only fires once the record is created and not updated, but this may not be desired behaviour.
You can also skip the callbacks by calling update_column
instead of calling save
as pointed out by @BroiSatse
Upvotes: 4
Reputation: 13626
So does self.url point back to the pages url? If so, when you save it, it hits your server for the page, which might cause another save and another url request, etc.
Upvotes: 0