ReadOnly error keeps haunting the objects I want to update on a script (using script/runner)

hi. guys,

I have finally learnt how to use script/runner and have written a script with the intention of updating the status attribute of my "Blog" objects every 24 hours to "published".

I have written a script (which I will paste below). I seem to always get the error below which relates to ActiveRecord::ReadOnlyRecord when I try to save/update the object.

"projects/myApp/vendor/rails/activerecord/lib/active_record/base.rb: 2867:in `create_or_update': ActiveRecord::ReadOnlyRecord (ActiveRecord::ReadOnlyRecord)".

Here is the script:

=============== Start ===============

class Blog < ActiveRecord::Base   new_blogs = Blog.status_name_does_not_equal_all(['live', 'archived'])

  live_status = Status.find_by_name('live')

  new_blogs.each do |new_blog|    new_blog.status = live_status    new_blog.save(false) # new_blog.update_attribute(status, live_status)   end

end

=============== End ===============

Reading from http://api.rubyonrails.org/ under the doc for "find", I do understand that the :readonly flag is set ONLY if ":joins" was specified. In my case, I had never done so.

I used "updates" and that failed. Read Don't Use ActiveRecord::Base.update - Dan Manges's Blog and found that I have to use update_attributes but in this case, I have already set my "status" using the new status object (ie. new_blog.status = live_status )

I then tried using save/save! (in desperation) and fell flat on my face with an error.

When I update objects in my rails app, it's all fine but when I run a script through script/runner, I constantly get this ReadOnly error.

When I run pretty much the same thing on script/console, the Blog record happily gets updated without a hick. Here's an extract:

==================Extract from script/console - start

Got the following from http://ar.rubyonrails.org (Active Record — Object-relation mapping put on rails)

------------------- Start ------------------------ Direct manipulation (instead of service invocation) So instead of (Hibernate example):    long pkId = 1234;    DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) );    // something interesting involving a cat...    sess.save(cat);    sess.flush(); // force the SQL INSERT Active Record lets you:    pkId = 1234    cat = Cat.find(pkId)    # something even more interesting involving the same cat...    cat.save ------------------- End ------------------------

I still do not know why my code fails to work when I use script/ runner....

Your script seems to be in the body of the Blog model.

For what you're trying to do, I suggest that you create a class method in Blog to publish entries e.g.

def self.publish_entries   # your code goes here end

which you can then invoke with Blog.publish_entries

Also, the way you are trying to do it is database intensive. The initial search to get new entries is a hit on the db. Then each call to save will hit the db again.

I recommend looking up update_all in the Rails API

Hmmmm makes good sense. I will try it out and report. Thank you, Franz :slight_smile:

That happens if you do a find with a :joins option (unless you override it by passing :readonly => false)

Fred

yep, Frederick , I read your posting PRIOR to putting this question up. I had also read the API manual and discovered that. As you can see, there’s no use of :joins in my script.

yep, Frederick , I read your posting PRIOR to putting this question up. I had also read the API manual and discovered that. As you can see, there's no use of :joins in my script.

It's just behind the scenes - there pretty much has to be, since you are fetching instances of Blog but with conditions on the statuses table. You're going to have to delve into searchlogic (I assume that's what you're using) and figure out how to get it to supply readonly => false when it does its find.

Fred

On that same note, I actually did try to set :readonly => false onto " new_blogs = Blog.status_name_does_not_equal_all([‘live’,‘archived’])" but had no idea of how to do so. I tried something like new_blogs = Blog.status_name_does_not_equal_all([‘live’,‘archived’], :readonly=>false ) but got a syntax error.

Anyway, as per Franz’s post, I am going to build a method in the model and see how that goes whilst minimising the hits on the db.

thank you

Franz,

I started out by defining "publish_entries" in the model of Blog and got the ugly "method_missing" error when I ran "script/runner script/set_blogs_to_published.rb".

Of course , this is how my "script/set_blogs_to_published.rb" looks like.

---------------------------- script/set_blogs_to_published.rb - Start

Referring to update_all in http://api.rubyonrails.org/, I managed to get the job done with the following script. The only time I relied on using find (which does have return records set to readonly) is when I had to get the live status object out. Aside from that, it's easier on the db hits.

Thank you, everyone esp Franz!

------------------ script/publish_blog_entries.rb - Start

You do not need to put the code in a separate script.

In your Blog model, create a class method called publish_blog_entries

# this goes into blog.rb def self.publish_blog_entries   # your code here end

and then you can invoke it via script/runner

script/runner -e production 'Blog.publish_blog_entries'

/franz