In my application, these "planners" (essentially, article ideas) follow predetermined templates, written in Markdown, with some specific syntax here:
Please write your answer in the following textbox: [...]
Please write your answer in the following textarea:
...So here, on line, you should write one thing.
...Here, on line 2, you should write another.
...
...
...
Essentially, [...]
is a text input, and a group of lines starting with ...
are a textarea. That's not really the issue - it's just to explain what part of this code is doing.
On actions new
and edit
, the standard planner form is displayed, with the correct fields based on the template (for new
) or current planner body (for edit
). On save, the template's fields are filled in with params[:fields], and the resulting Markdown is saved as the planner's body. The code, I'd hope, is now possible to follow, knowing this context. Only relevant controller code is provided, and it uses make_resourceful.
class Staff::PlannersController < StaffController
make_resourceful do
actions :all
before :create do
find_planner_format
if @planner_format
current_object.body = fields_in_template @planner_format.body
else
flash[:error] = 'Planner format not found!'
redirect_to staff_planners_path
end
current_object.user = @current_user
end
before :update do
current_object.body = fields_in_template(current_object.body)
end
end
private
def fields_in_template(template)
fields = params[:fields] || {}
if fields[:inline]
template.gsub! /\[\.\.\..*\]/ do
"[...#{fields[:inline].shift}]"
end
end
if fields[:block]
template.gsub! /^\.{3}.*(\n\.{3}.*)*$/ do
fields[:block].shift.split("\n").collect { |line|
"...#{line}"
}.join("\n")
end
end
current_object.body = template
end
end
And now, the mystery: in the update
action, changes to the body are not saved. After debugging, I've determined that the issue does not lie only in current_object.save, since the following before :update
code does what you would expect:
before :update do
current_object.body = 'test string'
end
In fact, even this gets the expected result:
before :update do
current_object.body = fields_in_template(current_object.body) + 'a'
end
So now, the question: why is Rails so insistent that it not save the result of the function - and even then, only when it comes from update
? More debugging showed that the object attribute is set, and even claims to save successfully, but reload
ing the object after save reverts the changes.
At first it looked like the resulting string was just a "poisoned" variable of sorts, and that rebuilding the string by appending "a" removed that strange state. However, the following code, which ought to add an "a" and remove it again, also failed to save.
before :update do
new_body = fields_in_template(current_object.body) + 'a'
new_body.slice! -1
current_object.body = new_body
end
This is just bizarre to me. What am I doing wrong here, and what can I possibly do to debug further? (Or if you happen to instantly see my mistake, that'd be nice, too...)
EDIT: After checking SQL logs (not sure why I didn't think to do this earlier), it would seem that Rails doesn't seem to acknowledge the new body attribute as actually being different, even though checking the string in the debugger confirms that it is. As such, Rails doesn't even run an UPDATE
query unless something else is modified, in which case body
is not included.