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

new_blog = Blog.find_by_title(‘test1’)
=> #<Blog id: 19, user_id: 1, status_id: 2, title: “test1”,
description: “4A63 forever!”, location: “bayswater”, contact_name:
“Jimi Hendrix”, contact_number
: “01233993933”, condition: “233223”, new: false, sold: false,
created_by: “admin”, updated_by: “admin”, created_at: “2010-04-06
06:57:04”, updated_at: “2010
-04-06 06:59:31”>
new_blog.title
=> “test1”
new_status = Status.find_by_name(‘live’)

=> #<Status id: 5, name: “live”, display_title: “live”, created_by:
“admin”, updated_by: “admin”, created_at: “2010-03-15 12:17:14”,
updated_at: “2010-03-15
12:17:14”>

new_status.name
=> “live”
new_blog.status = new_status=> #<Status id: 5, name: “live”, display_title: “live”, created_by: “admin”, updated_by: “admin”, created_at: “2010-03-15 12:17:14”, updated_at: “2010-03-15
12:17:14”>
new_status=> #<Status id: 5, name: “live”, display_title: “live”, created_by: “admin”, updated_by: “admin”, created_at: “2010-03-15 12:17:14”, updated_at: “2010-03-15
12:17:14”>
new_blog.save
=> true

==================Extract from script/console - end

Any ideas?

thanks

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:

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.

On Apr 7, 3:11 pm, ct9a [email protected] wrote:

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

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

Fred

On Apr 7, 10:11 pm, Gordon Y. [email protected] wrote:

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

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

class Blog < ActiveRecord::Base
Blog.publish_entries
end
---------------------------- script/set_blogs_to_published.rb - End

I then moved “publish_entries” to the Blog controller file but still
no luck when I ran “script/set_blogs_to_published.rb”.

Where do I put the “publish_entries” definition (ie controller or
model of Blog)?

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

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

class Blog < ActiveRecord::Base
live_status = Status.find_by_name(‘live’)
live_status_sql_clause = "status_id = " + live_status.id.to_s

Blog.update_all( live_status_sql_clause, "status_id not in

( select id from statuses where name in (‘published’, ‘archived’) )" )
end

------------------ script/publish_blog_entries.rb - End

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