For defining the process you want to perform, you can execute the defined process without starting the application.
cron
It is possible to execute a command depending on the time, such as "execute a command of XX at XX".
whenever
A ruby description method that makes cron easier to draw.
The process is described in cron by `bundle exec whenever --update-crontab`
the description written in schedule.rb.
The process to be set for the rake task this time is "If there is a publication waiting date that is in the past, the status will be changed to" Public "."
① First of all, because the article is waiting to be published, that is, the state of the article is wait_publish.
Article.wait_publish
(2) Next, you have to search for the one whose release date is in the past from (1). The fact that the publication date and time is in the past means that the publish_at time of the article is less than Time.current. Therefore, it becomes `` `'published_at <?', Time.current```.
A summary of this with a scope is written in the model
scope :viewable, -> { published.where('published_at < ?', Time.current) }
become. In other words, if you add viewable, you can search from the range where the release date is in the past. Therefore, articles that are waiting to be published and whose publication date and time are smaller than the current one
Article.wait_publish.viewable
Can be expressed like this
③ If the state is waiting for publication and the publication date and time is smaller than the present (in the past) Make it public (that is, make the state publish)
Article.wait_publish.viewable.publish
I think this will make it public, but there is another important point. Article.wait_publish.viewable does not mean that there is only one article. So you have to add publish to each one
When adding publish to each of the acquired multiple values
use find_each
find_each iteratively processes multiple acquired records
You can also add publish! To each one by setting `(&: published!)`
.
The code that combines (1), (2), and (3) and says, "If there is a publication waiting date that is in the past, the status will be changed to" public "."
Article.publish_wait.viewable.find_each(&:publish!)
Can be written as. If you write this in lib/tasks/article_state.rake, you can execute rake tasks.
lib/tasks/article_state.rake
namespace :article_state do
desc 'If the article is waiting to be published and the publication date is in the past, change the status of the article to "Publish".'
task change_publish: :environment do
Article.publish_wait.viewable.find_each(&:publish!)
end
end
Processing is now executed just by executing the article_state command in the terminal
You have to run cron to launch the article_state command defined above once an hour, so use whenever (whenever is just like a cron translator)
db/schedule.rb
# Use this file to easily define all of your cron jobs.
#
# It's helpful, but not entirely necessary to understand cron before proceeding.
# http://en.wikipedia.org/wiki/Cron
require File.expand_path(File.dirname(__FILE__) + '/environment')
# Example:
rails_env = ENV['RAILS_ENV']|| :development
set :environment, rails_env
set :output, "#{Rails.root}/log/cron_log"
# every 2.hours do
# command "/usr/bin/some_great_command"
# runner "MyModel.some_method"
# rake "some:great:rake:task"
# end
#
every :hour do
rake 'article_state:change_publish'
end
# Learn more: http://github.com/javan/whenever
every :hour do
rake 'article_state:change_publish'
end
This part is the code that changes_publish is executed in article_state once an hour. When the description is finished, use ``` bundle exec whenever --update-crontab` `` to reflect it in cron.
Be careful about the description method when adding new information to enum
article.rb
#Change before
enum state: { draft: 0, published: 1}
#NG
enum state: { draft: 0, published: 1, publish_wait: 2 }
#OK
enum state: %i{ draft published publish_wait }
The OK code is cleaner and easier to see. Try to write clean code
Easy to write when retrieving data with enum value
# NG
article.state = 'publish_wait'
Article.where(state: :publish_wait)
# OK
Article.publish_wait
If it is defined by enum, if you put the value of enum after Article like `Article.publish_wait``` without searching in`
Article.where```, the data corresponding to that value will be displayed. Will be able to get
Other points ・ You can tell at a glance that it is defined by an enum. ・ The code is refreshing
Check how to write in the update action
article_controller.rb
def update
authorize(@article) #Point.1
if @article.assign_attributes(article_params) #Point.2
@article.assign_publish_state unless @article.draft? #Point.3
@article.save!
flash[:notice] = 'Has been updated'
redirect_to edit_admin_article_path(@article.uuid)
else
render :edit
end
end
Point.1 About authorize
A gem called Pundit allows you to use the authorization function `` `authorize.
arthorize (@article)```If you define in the controller, that controller@It will check if you can use the article.
This time, `autherize (@article)`
allows you to use `@ article```, so you can use
@ articlewithout writing the definition of
@ article```. Now available
Point.2 Reasons to use assign_attributes (article_params)
The assign_attributes (article_params) method is a method for changing attributes. At first glance it is almost the same as update (article_params), but there is one difference. that is
Is. If you use update it will look like this
For update
article_controller.rb
def update
authorize(@article)
if @article.update(article_params) #Access DB with update
@article.assign_publish_state unless @article.draft?
@article.save! #Access more DB with save
flash[:notice] = 'Has been updated'
redirect_to edit_admin_article_path(@article.uuid)
else
render :edit
end
end
For assign_attribute
article_controller.rb
def update
authorize(@article)
if @article.assign_attributes(article_params) #Do not access DB
@article.assign_publish_state unless @article.draft?
@article.save! #Access DB with save
flash[:notice] = 'Has been updated'
redirect_to edit_admin_article_path(@article.uuid)
else
render :edit
end
end
As mentioned above, the DB is accessed once with save anyway, and update accesses the DB more, which causes the processing to become heavy. So basically, the update method and save method do not coexist.
Point.3 About assign_publish_state (if statement in if statement)
This controller needs conditional branching processing to change the state to "waiting for publication" if the publication date is in the future than the current time, and to "public" if the publication date is the current time or past the current time.
Simply think
article_controller.rb
if @article.assign_attributes(article_params)
unless @article.draft?
@article.state = if @article.published_at <= Time.current
:published
else
:publish_wait
end
end
@article.save!
flash[:notice] = 'Has been updated'
redirect_to edit_admin_article_path(@article.uuid)
else
#abridgement
end
There are unless and if in if, which makes it difficult to understand the controller and increases the number of lines (Fat controller). So
@article.state = if @article.published_at <= Time.current
:published
else
:publish_wait
end
Is defined as one method for simplification
article_controller.rb
if @article.assign_attributes(article_params)
@article.assign_publish_state unless @article.draft?
@article.save!
flash[:notice] = 'Has been updated'
redirect_to edit_admin_article_path(@article.uuid)
else
#abridgement
end
app/model/article.rb
def assign_publish_state
self.state = if self.published_at <= Time.current
:published
else
:publish_wait
end
end
I want to test whether the state changes from the original state depending on the date and time If the original state is a draft, waiting for publication, publishing, etc., conditional branching will eliminate the drill. Therefore, it is better to make the original state random and write a conditional branch that changes depending on the date and time from there so that you do not have to write code.
spec/factories/articles.rb
#Example
trait :random_state_article
# NG
state { Random.rand(1..2) }
# OK
state { Articles.states.values.sample }
end
Random traits should be Articles.states.values.sample instead of Random.rand (1..2)
Recommended Posts