Reputation: 2227
I have a post model with a datetime field called "published_at". On my form I have a checkbox for an attribute named "publish_now" (see below). I want "published_at" to be set to the Time.now() when the user checks the checkbox for my virtual attribute :publish_now.
<input class="boolean optional" id="post_publish_now" name="post[publish_now]" type="checkbox" value="1" />
Here is my create and update methods in the controller:
def create
@post = Post.new(params[:post])
if current_user
@post.author_id == current_user.id
end
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Post was successfully created.' }
format.json { render json: @post, status: :created, location: @post }
else
format.html { render action: "new" }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
def update
@post = Post.find(params[:id])
respond_to do |format|
if @post.update_attributes(params[:post])
format.html { redirect_to @post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
And here is my model:
class Post < ActiveRecord::Base
belongs_to :author, class_name: "User", foreign_key: "author_id"
attr_accessible :author_id, :content, :published_at, :title, :publish_now
validates_presence_of :content, :title
def publish_now
!published_at.nil?
end
def publish_now=(value)
if value == "1" && published_at.nil?
published_at = Time.now()
end
end
end
This is my best guess of how it should work based on the railscast on virtual attributes, but it's not saving a value for published_at. Any suggestions where the problem might be?
UPDATE: (the form view)
<%= simple_form_for(@post) do |f| %>
<%= f.input :title %>
<%= f.input :content, input_html: { cols: 100, rows: 10, class: "
span5" } %>
<% if @post.published_now == false %>
<%= f.input :publish_now, as: :boolean %>
<% end %>
<%= f.submit %>
<% end %>
UPDATE Example Log:
Started POST "/posts" for 127.0.0.1 at 2012-10-31 14:14:49 -0500
Processing by PostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"zlZ3s18VXwEvONIkk1CZAYESAEAxPP1OKcUtiyEuZgA=", "post"=>{"title"=>"big big love a doodle", "content"=>"aksdfj;skjf;kasjdf; lkj af;lk ;askdfj ;lk ;laf; ksd ;f", "publish_now"=>"1"}, "commit"=>"Create Post"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
(0.2ms) BEGIN
SQL (0.7ms) INSERT INTO "posts" ("author_id", "content", "created_at", "published_at", "title", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["author_id", nil], ["content", "aksdfj;skjf;kasjdf; lkj af;lk ;askdfj ;lk ;laf; ksd ;f"], ["created_at", Wed, 31 Oct 2012 14:14:49 CDT -05:00], ["published_at", nil], ["title", "big big love a doodle"], ["updated_at", Wed, 31 Oct 2012 14:14:49 CDT -05:00]]
(0.4ms) COMMIT
Redirected to http://localhost:3000/posts/5
Completed 302 Found in 18ms (ActiveRecord: 2.1ms)
Upvotes: 2
Views: 666
Reputation: 8258
The error is in this line
published_at = Time.now()
It should be
self.published_at = Time.now()
The parenthesis are also optional. If you assign to something then Ruby will assume it's a local variable unless you explicitly provide the object, only then can Ruby know it's actually a method you want to call (published_at= method). This is a common gotcha moment in Ruby.
What happens in your code is that a new local variable published_at is created. See http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html at "Using Accessors Within a Class" for more information.
When reading attributes like that you don't need to prefix by self, however, because Ruby will not assume something is a local variable until you assign it. So before published_at=..., Ruby will treat published_at as a method call, after you do published_at=... (without self in front), it will treat published_at as a local variable. Only then you would have to use self.published_at to actually call the method instead of reading the local variable. For the attribute writter (ends with =), you always need to prefix with the object. You can alternatively use the attributes hash, that way you don't need to prefix by self:
attributes["published_at"] = whatever
Upvotes: 3