# Terminal
rails g migration change_published_to_published_on_posts
# db/migrate/20250406012935_change_published_to_published_on_posts.rb
class ChangePublishedToPublishedOnPosts < ActiveRecord::Migration[8.0]
def up
add_column :posts, :published_at, :datetime
execute <<-SQL
UPDATE posts
SET published_at = updated_at
WHERE published = TRUE
SQL
remove_column :posts, :published
add_index :posts, :published_at
end
def down
add_column :posts, :published, :boolean, default: false
execute <<-SQL
UPDATE posts
SET published = (published_at IS NOT NULL AND published_at <= CURRENT_TIMESTAMP)
SQL
remove_column :posts, :published_at
end
end
# app/controllers/posts_controller.rb
def index
@posts = Post.published.order(published_at: :desc)
end
def post_params
params.expect(post: [ :title, :published_at, :content ])
end
# app/models/post.rb
class Post < ApplicationRecord
has_rich_text :content
scope :published, -> { where(published_at: ..Time.current) }
scope :scheduled, -> { where(published_at: Time.current..) }
# scope :scheduled, -> { where("published_at > ?", Time.current) }
scope :unpublished, -> { where(published_at: nil) }
def published?
published_at.present? && published_at.past?
end
def scheduled?
published_at.present? && published_at.future?
end
end
# app/views/posts/_form.html.erb
<div class="my-5 flex items-center gap-2">
<%= form.label :published_at %>
<%= form.datetime_field :published_at %>
</div>
# app/views/posts/_post.html.erb
<div>
<%= "Published" if post.published? %>
<%= "Scheduled" if post.scheduled? %>
<%= "Unpublished" if !post.scheduled? && !post.published? %>
</div>