Updated

app/models / invalidation.rb

D
264 lines of codes
34 methods
6.9 complexity/method
5 churn
235.06 complexity
0 duplications
require 'textacular/searchable' class Invalidation < ActiveRecord::Base
  1. Invalidation has no descriptive comment
  2. Invalidation has at least 34 methods
extend Searchable(:id, :summary, :details, :petition_id) include Browseable belongs_to :petition has_many :signatures facet :all, -> { by_most_recent } facet :completed, -> { completed.by_most_recent } facet :cancelled, -> { cancelled.by_most_recent } facet :pending, -> { pending.by_most_recent } facet :enqueued, -> { enqueued.by_most_recent } facet :running, -> { running.by_longest_running } CONDITIONS = %i[ petition_id name postcode ip_address email domain constituency_id location_code created_before created_after ] validates :summary, presence: true, length: { maximum: 255 } validates :details, length: { maximum: 10000 } validates :petition_id, numericality: { only_integer: true, allow_blank: true, greater_than_or_equal_to: 100000 } validates :name, length: { maximum: 255, allow_blank: true } validates :postcode, length: { maximum: 255, allow_blank: true } validates :ip_address, length: { maximum: 20 }, format: { with: /\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/ }, allow_blank: true validates :email, length: { maximum: 255, allow_blank: true } validates :domain, length: { maximum: 255, allow_blank: true } validates :constituency_id, length: { maximum: 30, allow_blank: true } validates :location_code, length: { maximum: 30, allow_blank: true } validate do if applied_conditions.empty? errors.add :petition_id, "Please select some conditions, otherwise all signatures will be invalidated" end if petition_id? errors.add :petition_id, "Petition doesn't exist" unless Petition.exists?(petition_id) end if domain? errors.add :domain, "Please enter a domain and not an email address" if domain =~ /@/ end if constituency_id? errors.add :constituency_id, "Constituency doesn't exist" unless Constituency.exists?(external_id: constituency_id) end if location_code? errors.add :location_code, "Location doesn't exist" unless Location.exists?(code: location_code) end if created_before? && created_after? errors.add :created_after, "Starting date is after the finishing date" unless created_before > created_after end end before_destroy do !started? end class << self def by_most_recent reorder(created_at: :desc) end def by_longest_running reorder(started_at: :asc) end def cancelled where(arel_table[:cancelled_at].not_eq(nil)) end def completed where(arel_table[:completed_at].not_eq(nil)) end def enqueued where(arel_table[:enqueued_at].not_eq(nil).and(arel_table[:started_at].eq(nil))) end def not_completed where(arel_table[:completed_at].eq(nil)) end def pending where(enqueued_at: nil, started_at: nil, cancelled_at: nil, completed_at: nil) end def running started.not_completed end def started where(arel_table[:started_at].not_eq(nil)) end end def cancel!(now = Time.current)
  1. Invalidation has missing safe method 'cancel!'
return false if cancelled? || completed? update(cancelled_at: now) end def cancelled? cancelled_at? end def completed? completed_at? end def count!
  1. Invalidation has missing safe method 'count!'
return false unless pending? update(matching_count: matching_signatures.count, counted_at: Time.current) end def start!
  1. Invalidation has missing safe method 'start!'
return false unless pending? InvalidateSignaturesJob.perform_later(self) update(enqueued_at: Time.current) end def started? started_at? end def enqueued? enqueued_at? end def pending? !(enqueued? || started? || cancelled? || completed?) end def running? started? && !(completed? || cancelled?) end def percent_completed if started? || completed? matching_count.zero? ? 100 : calculate_percent_complete
  1. Invalidation#percent_completed calls 'matching_count.zero?' 2 times Locations: 0 1
else matching_count.zero? ? 0 : calculate_percent_complete
  1. Invalidation#percent_completed calls 'matching_count.zero?' 2 times Locations: 0 1
end end def matching_signatures
  1. Invalidation#matching_signatures has a flog score of 27
  2. Invalidation#matching_signatures has approx 11 statements
scope = Signature.for_invalidating scope = petition_scope(scope) if petition_id? scope = name_scope(scope) if name? scope = postcode_scope(scope) if postcode? scope = ip_address_scope(scope) if ip_address? scope = email_scope(scope) if email? scope = domain_scope(scope) if domain? scope = constituency_id_scope(scope) if constituency_id? scope = location_code_scope(scope) if location_code? scope = date_range_scope(scope) if date_range? scope end def invalidate!
  1. Invalidation#invalidate! has a flog score of 26
  2. Invalidation has missing safe method 'invalidate!'
  3. Invalidation#invalidate! has approx 9 statements
return if cancelled? || completed? update( started_at: Time.current,
  1. Invalidation#invalidate! calls 'Time.current' 4 times Locations: 0 1 2 3
matching_count: matching_signatures.count, counted_at: Time.current
  1. Invalidation#invalidate! calls 'Time.current' 4 times Locations: 0 1 2 3
) Appsignal.without_instrumentation do matching_signatures.find_in_batches(batch_size: 100) do |signatures| signatures.each do |signature|
  1. Invalidation#invalidate! contains iterators nested 2 deep
signature.invalidate!(Time.current, self)
  1. Invalidation#invalidate! calls 'Time.current' 4 times Locations: 0 1 2 3
increment!(:invalidated_count) end reload and return if cancelled? end end update(completed_at: Time.current)
  1. Invalidation#invalidate! calls 'Time.current' 4 times Locations: 0 1 2 3
end private def petition_scope(scope) scope.where(petition_id: petition_id) end def name_scope(scope) if name =~ /%/ scope.where("lower(name) LIKE ?", name.strip.downcase)
  1. Invalidation#name_scope calls 'name.strip' 2 times Locations: 0 1
  2. Invalidation#name_scope calls 'name.strip.downcase' 2 times Locations: 0 1
else scope.where("lower(name) = ?", name.strip.downcase)
  1. Invalidation#name_scope calls 'name.strip' 2 times Locations: 0 1
  2. Invalidation#name_scope calls 'name.strip.downcase' 2 times Locations: 0 1
end end def postcode_scope(scope) scope.where(postcode: postcode) end def ip_address_scope(scope) scope.where(ip_address: ip_address) end def email_scope(scope) if email =~ /%/ scope.where("email LIKE ?", email) else scope.where("email = ?", email) end end def domain_scope(scope) if domain =~ /%/ scope.where("SUBSTRING(email FROM POSITION('@' IN email) + 1) LIKE ?", domain) else scope.where("SUBSTRING(email FROM POSITION('@' IN email) + 1) = ?", domain) end end def constituency_id_scope(scope) scope.where(constituency_id: constituency_id) end def location_code_scope(scope) scope.where(location_code: location_code) end def date_range? created_before? || created_after? end def date_range_scope(scope) if created_before? scope = scope.where(table[:created_at].lt(created_before))
  1. Invalidation#date_range_scope calls 'table[:created_at]' 2 times Locations: 0 1
end if created_after? scope = scope.where(table[:created_at].gt(created_after))
  1. Invalidation#date_range_scope calls 'table[:created_at]' 2 times Locations: 0 1
end scope end def table
  1. Invalidation#table doesn't depend on instance state (maybe move it to another class?)
Signature.arel_table end def calculate_percent_complete [[0, ((invalidated_count.to_f / matching_count.to_f) * 100).floor].max, 100].min end def applied_conditions CONDITIONS.select{ |c| read_attribute(c).present? }
  1. Invalidation#applied_conditions has the variable name 'c'
end end