1require 'textacular/searchable'
 
2
  • Class "Petition" has 883 lines. It should have 300 or less. » roodi
3class Petition < ActiveRecord::Base
 
4  include PerishableTokenGenerator
 
5
 
6  PENDING_STATE     = 'pending'
 
7  VALIDATED_STATE   = 'validated'
 
8  SPONSORED_STATE   = 'sponsored'
 
 9  FLAGGED_STATE     = 'flagged'
 
10  OPEN_STATE        = 'open'
 
11  CLOSED_STATE      = 'closed'
 
12  REJECTED_STATE    = 'rejected'
 
13  HIDDEN_STATE      = 'hidden'
 
14  STOPPED_STATE     = 'stopped'
 
 
16  STATES            = %w[pending validated sponsored flagged open closed rejected hidden stopped]
 
17  DEBATABLE_STATES  = %w[open closed]
 
18  VISIBLE_STATES    = %w[open closed rejected]
 
19  SHOW_STATES       = %w[pending validated sponsored flagged open closed rejected stopped]
 
20  MODERATED_STATES  = %w[open closed hidden rejected]
 
21  PUBLISHED_STATES  = %w[open closed]
 
22  SELECTABLE_STATES = %w[open closed rejected hidden]
 
23  SEARCHABLE_STATES = %w[open closed rejected]
 
24  STOPPABLE_STATES  = %w[pending validated sponsored flagged]
 
 
26  IN_MODERATION_STATES       = %w[sponsored flagged]
 
27  TODO_LIST_STATES           = %w[pending validated sponsored flagged]
 
28  COLLECTING_SPONSORS_STATES = %w[pending validated]
 
29  STOP_COLLECTING_STATES     = %w[pending validated sponsored flagged]
 
 
31  DEBATE_STATES = %w[pending awaiting scheduled debated not_debated]
 
 
33  has_perishable_token called: 'sponsor_token'
 
 
35  before_save :update_debate_state, if: :scheduled_debate_date_changed?
 
36  before_save :update_moderation_lag, unless: :moderation_lag?
 
37  after_create :update_last_petition_created_at
 
 
39  extend Searchable(:action, :background, :additional_details)
 
40  include Browseable, Taggable
 
 
42  facet :all,      -> { by_most_popular }
 
43  facet :open,     -> { open_state.by_most_popular }
 
44  facet :rejected, -> { rejected_state.by_most_recent }
 
45  facet :closed,   -> { closed_state.by_most_popular }
 
46  facet :hidden,   -> { hidden_state.by_most_recent }
 
47  facet :stopped,  -> { stopped_state.by_most_recent }
 
 
49  facet :awaiting_response,    -> { awaiting_response.by_waiting_for_response_longest }
 
50  facet :with_response,        -> { with_response.by_most_recent_response }
 
 
52  facet :awaiting_debate,      -> { awaiting_debate.by_most_relevant_debate_date }
 
53  facet :awaiting_debate_date, -> { awaiting_debate_date.by_waiting_for_debate_longest }
 
54  facet :with_debate_outcome,  -> { with_debate_outcome.by_most_recent_debate_outcome }
 
55  facet :with_debated_outcome, -> { with_debated_outcome.by_most_recent_debate_outcome }
 
56  facet :debated,              -> { debated.by_most_recent_debate_outcome }
 
57  facet :not_debated,          -> { not_debated.by_most_recent_debate_outcome }
 
 
59  facet :collecting_sponsors,  -> { collecting_sponsors.by_most_recent }
 
60  facet :in_moderation,        -> { in_moderation.by_most_recent_moderation_threshold_reached }
 
61  facet :in_debate_queue,      -> { in_debate_queue.by_waiting_for_debate_longest }
 
 
63  facet :recently_in_moderation,       -> { recently_in_moderation.by_most_recent_moderation_threshold_reached }
 
64  facet :nearly_overdue_in_moderation, -> { nearly_overdue_in_moderation.by_most_recent_moderation_threshold_reached }
 
65  facet :overdue_in_moderation,        -> { overdue_in_moderation.by_most_recent_moderation_threshold_reached }
 
66  facet :tagged_in_moderation,         -> { tagged_in_moderation.by_most_recent_moderation_threshold_reached }
 
67  facet :untagged_in_moderation,       -> { untagged_in_moderation.by_most_recent_moderation_threshold_reached }
 
 
69  has_one :creator, -> { creator }, class_name: 'Signature'
 
70  accepts_nested_attributes_for :creator, update_only: true
 
 
72  belongs_to :locked_by, class_name: 'AdminUser'
 
 
74  has_one :debate_outcome, dependent: :destroy
 
75  has_one :email_requested_receipt, dependent: :destroy
 
76  has_one :government_response, dependent: :destroy
 
77  has_one :note, dependent: :destroy
 
78  has_one :rejection, dependent: :destroy
 
79  has_one :statistics, dependent: :destroy
 
 
81  has_many :signatures
 
82  has_many :sponsors, -> { sponsors }, class_name: 'Signature'
 
83  has_many :country_petition_journals, dependent: :destroy
 
84  has_many :constituency_petition_journals, dependent: :destroy
 
85  has_many :emails, dependent: :destroy
 
86  has_many :invalidations
 
87  has_many :trending_ips, dependent: :delete_all
 
88  has_many :trending_domains, dependent: :delete_all
 
 
90  validates :action, presence: true, length: { maximum: 80, allow_blank: true }
 
91  validates :background, presence: true, length: { maximum: 300, allow_blank: true }
 
92  validates :additional_details, length: { maximum: 800, allow_blank: true }
 
93  validates :open_at, presence: true, if: :open?
 
94  validates :creator, presence: true
 
95  validates :state, inclusion: { in: STATES }
 
 
97  with_options allow_nil: true, prefix: true do
 
98    delegate :name, :email, to: :creator
 
 99    delegate :code, :details, to: :rejection
 
100    delegate :summary, :details, :created_at, :updated_at, to: :government_response
 
101    delegate :date, :transcript_url, :video_url, :overview, to: :debate_outcome, prefix: :debate
 
102    delegate :debate_pack_url, to: :debate_outcome, prefix: false
 
103  end
 
 
105  alias_attribute :opened_at, :open_at
 
 
107  class << self
 
108    def by_most_popular
 
109      reorder(signature_count: :desc, created_at: :desc)
 
110    end
 
 
112    def by_most_recent
 
113      reorder(created_at: :desc)
 
114    end
 
 
116    def by_most_recent_debate_outcome
 
117      reorder(debate_outcome_at: :desc, created_at: :desc)
 
118    end
 
 
120    def by_most_recent_moderation_threshold_reached
 
121      reorder(moderation_threshold_reached_at: :desc, created_at: :desc)
 
122    end
 
 
124    def by_most_recent_response
 
125      reorder(government_response_at: :desc, created_at: :desc)
 
126    end
 
 
128    def by_most_relevant_debate_date
 
129      reorder('scheduled_debate_date ASC NULLS LAST, debate_threshold_reached_at ASC NULLS FIRST')
 
130    end
 
 
132    def by_oldest
 
133      reorder(created_at: :asc)
 
134    end
 
 
136    def by_waiting_for_debate_longest
 
137      reorder(debate_threshold_reached_at: :asc, created_at: :desc)
 
138    end
 
 
140    def by_waiting_for_response_longest
 
141      reorder(response_threshold_reached_at: :asc, created_at: :desc)
 
142    end
 
 
144    def current
 
145      open_state.by_most_recent
 
146    end
 
 
148    def open_state
 
149      where(state: OPEN_STATE)
 
150    end
 
 
152    def closed_state
 
153      where(state: CLOSED_STATE)
 
154    end
 
 
156    def hidden_state
 
157      where(state: HIDDEN_STATE)
 
158    end
 
 
160    def rejected_state
 
161      where(state: REJECTED_STATE)
 
162    end
 
 
164    def sponsored_state
 
165      where(state: SPONSORED_STATE)
 
166    end
 
 
168    def stopped_state
 
169      where(state: STOPPED_STATE)
 
170    end
 
 
172    def awaiting_debate
 
173      where(debate_state: %w[awaiting scheduled])
 
174    end
 
 
176    def awaiting_debate_date
 
177      debate_threshold_reached.not_scheduled
 
178    end
 
 
180    def awaiting_response
 
181      response_threshold_reached.not_responded
 
182    end
 
 
184    def collecting_sponsors
 
185      where(state: COLLECTING_SPONSORS_STATES)
 
186    end
 
 
188    def debate_threshold_reached
 
189      where.not(debate_threshold_reached_at: nil)
 
190    end
 
 
192    def debateable
 
193      where(state: DEBATABLE_STATES)
 
194    end
 
 
196    def debated
 
197      where(debate_state: 'debated').preload(:debate_outcome)
 
198    end
 
 
200    def for_state(state)
 
201      where(state: state)
 
202    end
 
 
204    def in_debate_queue
 
205      where(threshold_for_debate_reached.or(scheduled_for_debate))
 
206    end
 
  • DuplicateMethodCall - calls 'where(state: IN_MODERATION_STATES)' 4 times » reek
208    def in_moderation(from: nil, to: nil)
 
209      if from && to
 
210        where(state: IN_MODERATION_STATES).where(moderation_threshold_reached_at.between(from..to))
 
211      elsif from
 
212        where(state: IN_MODERATION_STATES).where(moderation_threshold_reached_at.gt(from))
 
213      elsif to
 
214        where(state: IN_MODERATION_STATES).where(moderation_threshold_reached_at.lt(to))
 
215      else
 
216        where(state: IN_MODERATION_STATES)
 
217      end
 
218    end
 
 
220    def moderated
 
221      where(state: MODERATED_STATES)
 
222    end
 
 
224    def not_debated
 
225      where(debate_state: 'not_debated')
 
226    end
 
 
228    def not_hidden
 
229      where.not(state: HIDDEN_STATE)
 
230    end
 
 
232    def not_responded
 
233      where(government_response_at: nil)
 
234    end
 
 
236    def not_scheduled
 
237      where(scheduled_debate_date: nil)
 
238    end
 
 
240    def respondable
 
241      where(state: RESPONDABLE_STATES)
 
242    end
 
 
244    def response_threshold_reached
 
245      where.not(response_threshold_reached_at: nil)
 
246    end
 
 
248    def selectable
 
249      where(state: SELECTABLE_STATES)
 
250    end
 
 
252    def stoppable
 
253      where(state: STOPPABLE_STATES)
 
254    end
 
 
256    def show
 
257      where(state: SHOW_STATES)
 
258    end
 
 
260    def threshold
 
261      where(arel_table[:signature_count].gteq(Site.threshold_for_debate))
 
262    end
 
 
264    def todo_list
 
265      where(state: TODO_LIST_STATES)
 
266    end
 
 
268    def visible
 
269      where(state: VISIBLE_STATES)
 
270    end
 
 
272    def with_debate_outcome
 
273      where.not(debate_outcome_at: nil)
 
274    end
 
 
276    def with_debated_outcome
 
277      debated.where.not(debate_outcome_at: nil)
 
278    end
 
 
280    def with_response
 
281      where.not(government_response_at: nil).preload(:government_response)
 
282    end
 
 
284    def trending(since = 1.hour.ago, limit = 3)
 
285      select('petitions.*, COUNT(signatures.id) AS signature_count_in_period').
 
286      joins(:signatures).
 
287      where('petitions.state = ?', OPEN_STATE).
 
288      where('petitions.last_signed_at > ?', since).
 
289      where('signatures.validated_at > ?', since).
 
290      where('signatures.invalidated_at IS NULL').
 
291      group('petitions.id').order('signature_count_in_period DESC').
 
292      limit(limit)
 
293    end
 
 
295    def close_petitions!(time = Time.current)
 
296      in_need_of_closing(time).find_each do |petition|
 
297        petition.close!
 
298      end
 
299    end
 
 
301    def close_petitions_early!(time = Parliament.dissolution_at)
 
302      open_at_dissolution(time).find_each do |petition|
 
303        petition.close!(time)
 
304      end
 
305    end
 
 
307    def stop_petitions_early!(time = Parliament.dissolution_at)
 
308      in_need_of_stopping.find_each do |petition|
 
309        petition.stop!(time)
 
310      end
 
311    end
 
 
313    def in_need_of_closing(time = Time.current)
 
314      where(state: OPEN_STATE).where(arel_table[:open_at].lt(Site.opened_at_for_closing(time)))
 
315    end
 
  • DuplicateMethodCall - calls 'scope.stoppable' 2 times » reek
317    def in_need_of_stopping(time = nil)
 
318      scope = preload(:creator)
 
319      time ? scope.stoppable.created_after(time) : scope.stoppable
 
320    end
 
 
322    def created_after(time)
 
323      where(arel_table[:created_at].gteq(time))
 
324    end
 
  • DuplicateMethodCall - calls 'arel_table[:closed_at]' 2 times » reek
326    def open_at_dissolution(dissolution_at = Parliament.dissolution_at)
 
327      if dissolution_at
 
328        opened_at_for_closing = Site.opened_at_for_closing(dissolution_at)
 
 
330        where(
 
331          arel_table[:state].eq(OPEN_STATE).
 
332          and(arel_table[:open_at].gteq(opened_at_for_closing).
 
333          and(arel_table[:closed_at].eq(nil)).
 
334          or(arel_table[:closed_at].gteq(dissolution_at)))
 
335        )
 
336      else
 
337        none
 
338      end
 
339    end
 
 
341    def popular_in_constituency(constituency_id, count = 50)
 
342      popular_in(constituency_id, count).for_state(OPEN_STATE)
 
343    end
 
 
345    def all_popular_in_constituency(constituency_id, count = 50)
 
346      popular_in(constituency_id, count).for_state(PUBLISHED_STATES)
 
347    end
 
 
349    def sanitized_tag(tag)
 
350      "[#{tag.gsub(/[\[\]%]/,'')}]"
 
351    end
 
 
353    def in_need_of_marking_as_debated(date = Date.current)
 
354      where(scheduled_debate_state.and(debate_date_in_the_past(date)))
 
355    end
 
 
357    def mark_petitions_as_debated!(date = Date.current)
 
358      in_need_of_marking_as_debated(date).update_all(debate_state: 'debated')
 
359    end
 
 
361    def unarchived
 
362      where(archived_at: nil)
 
363    end
 
 
365    def recently_in_moderation
 
366      in_moderation(from: moderation_near_overdue_at)
 
367    end
 
 
369    def nearly_overdue_in_moderation
 
370      in_moderation(from: moderation_overdue_at, to: moderation_near_overdue_at)
 
371    end
 
 
373    def overdue_in_moderation
 
374      in_moderation(to: moderation_overdue_at)
 
375    end
 
 
377    def tagged_in_moderation
 
378      tagged.in_moderation
 
379    end
 
 
381    def untagged_in_moderation
 
382      untagged.in_moderation
 
383    end
 
 
385    def signed_since(timestamp)
 
386      where(arel_table[:last_signed_at].gt(timestamp))
 
387    end
 
 
389    def in_need_of_validating
 
390      where(grouping(last_signed_at.gt(signature_count_validated_at)).eq(true))
 
391    end
 
 
393    private
 
 
395    def grouping(expression)
 
396      Arel::Nodes::Grouping.new(expression)
 
397    end
 
 
399    def last_signed_at
 
400      arel_table[:last_signed_at]
 
401    end
 
 
403    def signature_count_validated_at
 
404      arel_table[:signature_count_validated_at]
 
405    end
 
 
407    def moderation_threshold_reached_at
 
408      arel_table[:moderation_threshold_reached_at]
 
409    end
 
 
411    def moderation_near_overdue_at
 
412      Site.moderation_near_overdue_in_days.ago
 
413    end
 
 
415    def moderation_overdue_at
 
416      Site.moderation_overdue_in_days.ago
 
417    end
 
 
419    def popular_in(constituency_id, count)
 
420      klass = ConstituencyPetitionJournal
 
421      constituency_signature_count = klass.arel_table[:signature_count].as('constituency_signature_count')
 
422      constituency_signatures_for = klass.with_signatures_for(constituency_id).ordered
 
 
424      select(arel_table[Arel.star], constituency_signature_count).
 
425      joins(:constituency_petition_journals).
 
426      merge(constituency_signatures_for).
 
427      limit(count)
 
428    end
 
 
430    def threshold_for_debate_reached
 
431      arel_table[:debate_threshold_reached_at].not_eq(nil)
 
432    end
 
 
434    def scheduled_for_debate
 
435      arel_table[:scheduled_debate_date].not_eq(nil)
 
436    end
 
 
438    def awaiting_debate_state
 
439      arel_table[:debate_state].eq('awaiting')
 
440    end
 
 
442    def debate_date_in_the_past(date)
 
443      arel_table[:scheduled_debate_date].lt(date)
 
444    end
 
 
446    def scheduled_debate_state
 
447      arel_table[:debate_state].eq('scheduled')
 
448    end
 
449  end
 
 
451  def statistics
 
452    super || create_statistics!
 
453  end
 
 
455  def reset_signature_count!(time = Time.current)
 
456    update_column(:signature_count_reset_at, time)
 
457    update_signature_count!(time)
 
458    ConstituencyPetitionJournal.reset_signature_counts_for(self)
 
459    CountryPetitionJournal.reset_signature_counts_for(self)
 
460    update_column(:signature_count_reset_at, nil)
 
461  end
 
 
463  def update_signature_count!(time = Time.current)
 
464    sql = "signature_count = ?, last_signed_at = ?, updated_at = ?"
 
465    count = signatures.validated_count(nil, time)
 
 
467    if update_all([sql, count, time, time]) > 0
 
468      self.reload
 
469    end
 
470  end
 
  • DuplicateMethodCall - calls 'self.reload' 2 times » reek
  • TooManyStatements - has approx 14 statements » reek
  • Method name "increment_signature_count!" cyclomatic complexity is 10. It should be 8 or less. » roodi
  • Method "increment_signature_count!" has 36 lines. It should have 20 or less. » roodi
472  def increment_signature_count!(time = Time.current)
 
473    sql = "signature_count = signature_count + ?, last_signed_at = ?, updated_at = ?"
 
474    count = signatures.validated_count(last_signed_at, time)
 
 
476    return false if count.zero?
 
  • Found = in conditional. It should probably be an == » roodi
478    if result = update_all([sql, count, time, time]) > 0
 
479      self.reload
 
 
481      updates = []
 
 
483      if at_threshold_for_moderation? && collecting_sponsors?
 
484        updates << "state = '#{SPONSORED_STATE}'"
 
485        updates << "moderation_threshold_reached_at = :now"
 
486      elsif pending?
 
487        updates << "state = '#{VALIDATED_STATE}'"
 
488      end
 
 
490      if at_threshold_for_response?
 
491        updates << "response_threshold_reached_at = :now"
 
492      end
 
 
494      if at_threshold_for_debate?
 
495        updates << "debate_threshold_reached_at = :now"
 
496        updates << "debate_state = 'awaiting'"
 
497      end
 
 
499      updates = updates.join(", ")
 
 
501      if updates.present?
 
502        if update_all([updates, now: time]) > 0
 
503          self.reload
 
504        end
 
505      end
 
506    end
 
 
508    result
 
509  end
 
  • TooManyStatements - has approx 7 statements » reek
511  def decrement_signature_count!(time = Time.current)
 
512    updates = ""
 
 
514    if below_threshold_for_debate?
 
515      updates << "debate_threshold_reached_at = NULL, "
 
516      updates << "debate_state = 'pending', "
 
517    end
 
 
519    if below_threshold_for_response?
 
520      updates << "response_threshold_reached_at = NULL, "
 
521    end
 
 
523    updates << "signature_count = greatest(signature_count - 1, 1), "
 
524    updates << "updated_at = :now"
 
 
526    if update_all([updates, now: time]) > 0
 
527      self.reload
 
528    end
 
529  end
 
 
531  def signature_count_difference
 
532    signature_count - signatures.validated_count(nil, last_signed_at)
 
533  end
 
 
535  def valid_signature_count?
 
536    signature_count_difference.zero?
 
537  end
 
 
539  def valid_signature_count!
 
540    valid_signature_count? && touch(:signature_count_validated_at)
 
541  end
 
 
543  def will_reach_threshold_for_moderation?
 
544    unless moderation_threshold_reached_at?
 
545      signature_count >= Site.threshold_for_moderation
 
546    end
 
547  end
 
 
549  def at_threshold_for_moderation?
 
550    unless moderation_threshold_reached_at?
 
551      signature_count >= Site.threshold_for_moderation + 1
 
552    end
 
553  end
 
 
555  def at_threshold_for_response?
 
556    unless response_threshold_reached_at?
 
557      signature_count >= Site.threshold_for_response - 1
 
558    end
 
559  end
 
 
561  def at_threshold_for_debate?
 
562    unless debate_threshold_reached_at?
 
563      signature_count >= Site.threshold_for_debate - 1
 
564    end
 
565  end
 
 
567  def below_threshold_for_response?
 
568    if response_threshold_reached_at?
 
569      signature_count <= Site.threshold_for_response
 
570    end
 
571  end
 
 
573  def below_threshold_for_debate?
 
574    if debate_threshold_reached_at?
 
575      signature_count <= Site.threshold_for_debate
 
576    end
 
577  end
 
 
579  def signatures_by_country
 
580    country_petition_journals.joins(:location).preload(:location).to_a.sort_by(&:name)
 
581  end
 
 
583  def signatures_by_constituency
 
584    constituency_petition_journals.preload(:constituency).to_a.sort_by(&:constituency_id)
 
585  end
 
 
587  def approve?
 
588    moderation == 'approve'
 
589  end
 
 
591  def reject?
 
592    moderation == 'reject'
 
593  end
 
 
595  def flag?
 
596    moderation == 'flag'
 
597  end
 
 
599  def moderation=(value)
 
600    @moderation = value if value.in?(%w[approve reject flag])
 
601  end
 
 
603  def moderation
 
604    @moderation
 
605  end
 
  • TooManyStatements - has approx 6 statements » reek
607  def moderate(params)
 
608    self.moderation = params[:moderation]
 
 
610    if approve?
 
611      publish
 
612    elsif reject?
 
613      reject(params[:rejection])
 
614    elsif flag?
 
615      flag
 
616    else
 
617      errors.add :moderation, :blank
 
618      false
 
619    end
 
620  end
 
 
622  def publish(time = Time.current)
 
623    update(state: OPEN_STATE, open_at: time)
 
624  end
 
  • UncommunicativeVariableName - has the variable name 'e' » reek
626  def reject(attributes)
 
627    begin
 
628      build_rejection(attributes) && rejection.save
 
629    rescue ActiveRecord::RecordNotUnique => e
 
630      rejection(true).update(attributes)
 
631    end
 
632  end
 
 
634  def flag
 
635    update(state: FLAGGED_STATE)
 
636  end
 
 
638  def rejection(*args)
 
639    super || build_rejection
 
640  end
 
 
642  def close!(time = deadline)
 
643    if open?
 
644      update!(state: CLOSED_STATE, closed_at: time)
 
645    else
 
646      raise RuntimeError, "can't stop a petition that is in the #{state} state"
 
647    end
 
648  end
 
 
650  def stop!(time = Time.current)
 
651    if state.in?(STOPPABLE_STATES)
 
652      update!(state: STOPPED_STATE, stopped_at: time)
 
653    else
 
654      raise RuntimeError, "can't stop a petition that is in the #{state} state"
 
655    end
 
656  end
 
 
658  def validate_creator!(now = Time.current)
 
659    if pending?
 
660      # Set the validated_at time to 1 second ago so that if
 
661      # the signature count update runs for it then it won't
 
662      # prevent this signature being missed.
 
663      creator && creator.validate!(1.second.ago(now)) && reload
 
664    end
 
665  end
 
 
667  def count_validated_signatures
 
668    signatures.validated.count
 
669  end
 
 
671  def collecting_sponsors?
 
672    state.in?(COLLECTING_SPONSORS_STATES)
 
673  end
 
 
675  def awaiting_moderation?
 
676    state == VALIDATED_STATE
 
677  end
 
 
679  def in_moderation?
 
680    state.in?(IN_MODERATION_STATES)
 
681  end
 
 
683  def moderated?
 
684    state.in?(MODERATED_STATES)
 
685  end
 
 
687  def open?
 
688    state == OPEN_STATE
 
689  end
 
690  alias_method :can_be_signed?, :open?
 
 
692  def rejected?
 
693    state == REJECTED_STATE
 
694  end
 
 
696  def hidden?
 
697    state == HIDDEN_STATE
 
698  end
 
 
700  def closed?
 
701    state == CLOSED_STATE
 
702  end
 
 
704  def stopped?
 
705    state == STOPPED_STATE
 
706  end
 
 
708  def flagged?
 
709    state == FLAGGED_STATE
 
710  end
 
 
712  def pending?
 
713    state == PENDING_STATE
 
714  end
 
 
716  def validated?
 
717    state == VALIDATED_STATE
 
718  end
 
 
720  def published?
 
721    state.in?(PUBLISHED_STATES)
 
722  end
 
 
724  def visible?
 
725    state.in?(VISIBLE_STATES)
 
726  end
 
 
728  def closed_for_signing?(now = Time.current)
 
729    rejected? || closed_at? && closed_at < 24.hours.ago(now)
 
730  end
 
 
732  def archiving?
 
733    archiving_started_at? && !archived_at?
 
734  end
 
 
736  def archived?
 
737    archived_at?
 
738  end
 
 
740  def editing_disabled?
 
741    archiving_started_at? || archived_at?
 
742  end
 
  • ControlParameter - is controlled by argument 'user' » reek
744  def update_lock!(user, now = Time.current)
 
745    if locked_by == user
 
746      update!(locked_at: now)
 
747    end
 
748  end
 
 
750  def checkout!(user, now = Time.current)
 
751    with_lock do
 
752      if locked_by.present? && locked_by != user
 
753        false
 
754      else
 
755        update!(locked_by: user, locked_at: now)
 
756      end
 
757    end
 
758  end
 
 
760  def force_checkout!(user, now = Time.current)
 
761    update!(locked_by: user, locked_at: now)
 
762  end
 
  • ControlParameter - is controlled by argument 'user' » reek
764  def release!(user)
 
765    with_lock do
 
766      if locked_by.present? && locked_by == user
 
767        update!(locked_by: nil, locked_at: nil)
 
768      end
 
769    end
 
770  end
 
 
772  def can_have_debate_added?
 
773    state.in?(DEBATABLE_STATES)
 
774  end
 
 
776  def in_todo_list?
 
777    state.in?(TODO_LIST_STATES)
 
778  end
 
 
780  def government_response?
 
781    government_response_at? && government_response
 
782  end
 
 
784  def debate_outcome?
 
785    debate_outcome_at? && debate_outcome
 
786  end
 
 
788  def deadline
 
789    open_at && (closed_at || Site.closed_at_for_opening(open_at))
 
790  end
 
 
792  def closing_early_for_dissolution?(dissolution_at = Parliament.dissolution_at)
 
793    open_at && dissolution_at ? deadline > dissolution_at : false
 
794  end
 
  • DuplicateMethodCall - calls 'timestamp.change(sec: (timestamp.sec.div(5) * 5))' 2 times » reek
  • DuplicateMethodCall - calls 'timestamp.sec' 2 times » reek
  • DuplicateMethodCall - calls 'timestamp.sec.div(5) * 5' 2 times » reek
  • DuplicateMethodCall - calls 'timestamp.sec.div(5)' 2 times » reek
  • DuplicateMethodCall - calls 'timestamp.utc' 2 times » reek
  • DuplicateMethodCall - calls 'timestamp.utc.to_s(cache_timestamp_format)' 2 times » reek
  • TooManyStatements - has approx 9 statements » reek
796  def cache_key(*timestamp_names)
 
797    case
 
798    when new_record?
 
799      "petitions/new"
 
800    when timestamp_names.any?
 
801      timestamp = max_updated_column_timestamp(timestamp_names)
 
802      timestamp = timestamp.change(sec: (timestamp.sec.div(5) * 5))
 
803      timestamp = timestamp.utc.to_s(cache_timestamp_format)
 
804      "petitions/#{id}-#{timestamp}"
 
805    when timestamp = max_updated_column_timestamp
 
806      timestamp = timestamp.change(sec: (timestamp.sec.div(5) * 5))
 
807      timestamp = timestamp.utc.to_s(cache_timestamp_format)
 
808      "petitions/#{id}-#{timestamp}"
 
809    else
 
810      "petitions/#{id}"
 
811    end
 
812  end
 
  • UtilityFunction - doesn't depend on instance state (maybe move it to another class?) » reek
814  def update_last_petition_created_at
 
815    Site.last_petition_created_at!
 
816  end
 
 
818  def has_maximum_sponsors?
 
819    sponsors.validated.count >= Site.maximum_number_of_sponsors
 
820  end
 
 
822  def update_all(updates)
 
823    self.class.unscoped.where(id: id).update_all(updates)
 
824  end
 
 
826  def email_requested_receipt!
 
827    email_requested_receipt || create_email_requested_receipt
 
828  end
 
 
830  def get_email_requested_at_for(name)
 
831    email_requested_receipt!.get(name)
 
832  end
 
 
834  def set_email_requested_at_for(name, to: Time.current)
 
835    email_requested_receipt!.set(name, to)
 
836  end
 
  • NilCheck - performs a nil-check » reek
838  def signatures_to_email_for(name)
 
839    timestamp = get_email_requested_at_for(name)
 
840    raise ArgumentError if timestamp.nil?
 
841    signatures.need_emailing_for(name, since: timestamp)
 
842  end
 
 
844  def awaiting_debate?
 
845    debate_state.in?(%w[awaiting scheduled])
 
846  end
 
 
848  def debated?
 
849    debate_state == 'debated'
 
850  end
 
 
852  def not_debated?
 
853    debate_state == 'not_debated'
 
854  end
 
 
856  def update_debate_state
 
857    self.debate_state = evaluate_debate_state
 
858  end
 
 
860  def evaluate_debate_state
 
861    if scheduled_debate_date?
 
862      scheduled_debate_date > Date.current ? 'scheduled' : 'debated'
 
863    else
 
864      'awaiting'
 
865    end
 
866  end
 
 
868  def fraudulent_domains
 
869    @fraudulent_domains ||= signatures.fraudulent_domains
 
870  end
 
 
872  def fraudulent_domains?
 
873    !fraudulent_domains.empty?
 
874  end
 
 
876  def closed_early_due_to_election?(dissolution_at = Parliament.dissolution_at)
 
877    closed_at == dissolution_at
 
878  end
 
 
880  def update_moderation_lag
 
881    if open_at_changed? || rejected_at_changed?
 
882      self.moderation_lag = calculate_moderation_lag(Date.current)
 
883    end
 
884  end
 
 
886  def calculate_moderation_lag(today)
 
887    if moderation_threshold_reached_at?
 
888      today - moderation_threshold_reached_at.to_date
 
889    else
 
890      0
 
891    end
 
892  end
 
893end