From 230a012f0090c496fc5cdb011bcc8ed732fd0f5c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 3 Mar 2019 22:18:23 +0100 Subject: Add polls (#10111) * Add polls Fix #1629 * Add tests * Fixes * Change API for creating polls * Use name instead of content for votes * Remove poll validation for remote polls * Add polls to public pages * When updating the poll, update options just in case they were changed * Fix public pages showing both poll and other media --- app/models/poll.rb | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 app/models/poll.rb (limited to 'app/models/poll.rb') diff --git a/app/models/poll.rb b/app/models/poll.rb new file mode 100644 index 000000000..ba0b17f91 --- /dev/null +++ b/app/models/poll.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: polls +# +# id :bigint(8) not null, primary key +# account_id :bigint(8) +# status_id :bigint(8) +# expires_at :datetime +# options :string default([]), not null, is an Array +# cached_tallies :bigint(8) default([]), not null, is an Array +# multiple :boolean default(FALSE), not null +# hide_totals :boolean default(FALSE), not null +# votes_count :bigint(8) default(0), not null +# last_fetched_at :datetime +# created_at :datetime not null +# updated_at :datetime not null +# + +class Poll < ApplicationRecord + include Expireable + + belongs_to :account + belongs_to :status + + has_many :votes, class_name: 'PollVote', inverse_of: :poll, dependent: :destroy + + validates :options, presence: true + validates :expires_at, presence: true, if: :local? + validates_with PollValidator, if: :local? + + scope :attached, -> { where.not(status_id: nil) } + scope :unattached, -> { where(status_id: nil) } + + before_validation :prepare_votes_count + after_initialize :prepare_cached_tallies + after_commit :reset_parent_cache, on: :update + + def loaded_options + options.map.with_index { |title, key| Option.new(self, key.to_s, title, cached_tallies[key]) } + end + + def unloaded_options + options.map.with_index { |title, key| Option.new(self, key.to_s, title, nil) } + end + + def possibly_stale? + remote? && last_fetched_before_expiration? && time_passed_since_last_fetch? + end + + delegate :local?, to: :account + + def remote? + !local? + end + + class Option < ActiveModelSerializers::Model + attributes :id, :title, :votes_count, :poll + + def initialize(poll, id, title, votes_count) + @poll = poll + @id = id + @title = title + @votes_count = votes_count + end + end + + private + + def prepare_cached_tallies + self.cached_tallies = options.map { 0 } if cached_tallies.empty? + end + + def prepare_votes_count + self.votes_count = cached_tallies.sum unless cached_tallies.empty? + end + + def reset_parent_cache + return if status_id.nil? + Rails.cache.delete("statuses/#{status_id}") + end + + def last_fetched_before_expiration? + last_fetched_at.nil? || expires_at.nil? || last_fetched_at < expires_at + end + + def time_passed_since_last_fetch? + last_fetched_at.nil? || last_fetched_at < 1.minute.ago + end +end -- cgit From 05dfd632c73b605232a77f27ff8d5b888efe5b00 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 5 Mar 2019 03:45:56 +0100 Subject: Fix poll options not being stripped of surrounding whitespace on save (#10168) --- app/models/poll.rb | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'app/models/poll.rb') diff --git a/app/models/poll.rb b/app/models/poll.rb index ba0b17f91..ab7236d45 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -32,8 +32,11 @@ class Poll < ApplicationRecord scope :attached, -> { where.not(status_id: nil) } scope :unattached, -> { where(status_id: nil) } + before_validation :prepare_options before_validation :prepare_votes_count + after_initialize :prepare_cached_tallies + after_commit :reset_parent_cache, on: :update def loaded_options @@ -75,6 +78,10 @@ class Poll < ApplicationRecord self.votes_count = cached_tallies.sum unless cached_tallies.empty? end + def prepare_options + self.options = options.map(&:strip).reject(&:blank?) + end + def reset_parent_cache return if status_id.nil? Rails.cache.delete("statuses/#{status_id}") -- cgit