module Article::States class Base < Stateful::State alias content model def to_s self.class.to_s.demodulize end def exit_hook(_target) ::Rails.logger.debug("#{content} leaving state #{self.class}") end def enter_hook ::Rails.logger.debug("#{content} entering state #{self.class}") end def post_trigger true end def send_notifications true end def send_pings true end def withdraw end end class New < Base def enter_hook super content[:published] = false content[:published_at] = nil end def published=(boolean) content.state = :just_published if boolean boolean end def published_at=(new_time) unless new_time.nil? content.state = new_time <= Time.new ? :just_published : :publication_pending end content[:published_at] = new_time end def draft? true end end class JustPublished < Base def enter_hook super content.just_changed_published_status = true content.state = :published end end class Published < Base def enter_hook super content[:published] = true content[:published_at] ||= Time.now end def published=(boolean) content.state = :just_withdrawn unless boolean end def withdraw content.state = :just_withdrawn end def published_at=(new_time) return if new_time.blank? content[:published_at] = new_time content.state = :publication_pending if new_time > Time.now end def send_notifications content.really_send_notifications if just_published? true end def send_pings content.really_send_pings if just_published? true end def just_published? content.just_changed_published_status? end end class JustWithdrawn < Base def enter_hook super content.just_changed_published_status = true content.state = :withdrawn end end class Withdrawn < Base def enter_hook super content[:published] = false end def published=(boolean) return unless boolean content.state = :published end def published_at=(new_time) content[:published_at] = new_time Trigger.remove(content, trigger_method: 'publish!') return if new_time.nil? || new_time <= Time.now content.state = :publication_pending end end class PublicationPending < Base def enter_hook super content[:published] = false if content.new_record? end def published=(published) content[:published] = published if published && content.published_at <= Time.now content.state = :just_published end end def published_at=(new_time) content[:published_at] = new_time Trigger.remove(content, trigger_method: 'publish!') if new_time.nil? content.state = :draft elsif new_time <= Time.now content.state = :just_published end end def post_trigger Trigger.post_action(content.published_at, content, 'publish!') end def withdraw(content) content.state = :draft end end class Draft < Base def enter_hook super content[:published] = false content[:published_at] = nil end def published=(boolean) content.state = :just_published if boolean end def published_at=(new_time) # Because of the workings of the controller, we should ignore # publication times before the current time. return if new_time.nil? || new_time <= Time.now content[:published_at] = new_time content.state = :publication_pending end end end