Module: Statisfy::Counter::ClassMethods

Defined in:
lib/statisfy/counter.rb

Instance Method Summary collapse

Instance Method Details

#aggregate_counter?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/statisfy/counter.rb', line 70

def aggregate_counter?
  const_get(:COUNTER_TYPE) == :aggregate
end

#all_keys(scope: nil, month: nil) ⇒ Object

Returns the list of all the keys of this counter for a given scope (optional) and a given month (optional)



145
146
147
148
149
150
151
152
153
154
# File 'lib/statisfy/counter.rb', line 145

def all_keys(scope: nil, month: nil)
  redis_client.keys("*\"counter\":\"#{name.demodulize.underscore}\"*").filter do |json|
    key = JSON.parse(json)

    scope_matches = scope.nil? || (key["scope_type"] == scope.class.name && key["scope_id"] == scope.id)
    month_matches = month.nil? || key["month"] == month

    scope_matches && month_matches
  end
end

#apply_default_counter_options(args) ⇒ Object

This method serves as a syntactic sugar The below methods could be written directly in the class definition but the ‘count` DSL defines them automatically based on the options provided



41
42
43
44
45
46
47
48
49
# File 'lib/statisfy/counter.rb', line 41

def apply_default_counter_options(args)
  define_method(:identifier, args[:value] || args[:uniq_by] || -> { nil })
  define_method(:scopes, args[:scopes] || Statisfy.configuration.default_scopes || -> { [] })
  define_method(:if_async, args[:if_async] || -> { true })
  define_method(:decrement?, args[:decrement_if] || -> { false })
  define_method(:should_run?, args[:if] || -> { true })
  define_method(:decrement_on_destroy?, -> { args[:decrement_on_destroy] != false })
  define_method(:month_to_set, args[:date_override] || -> { params["created_at"] })
end

#average(scope: nil, month: nil) ⇒ Object

Returns the average of the elements in the set Example: append(value: 1) append(value: 2) average

> 1.5



104
105
106
107
108
109
# File 'lib/statisfy/counter.rb', line 104

def average(scope: nil, month: nil)
  stored_values = elements_in(scope:, month:)
  return 0 if stored_values.empty?

  stored_values.map(&:to_i).reduce(:+) / stored_values.length.to_f
end

#count(args = {}) ⇒ Object

This is a DSL method that helps you define a counter It will create a method that will be called when the event is triggered It will also create a method that will be called when you want to get the value of the counter

Parameters:

  • every:

    the event(s) that will trigger the counter

  • type:

    by default it increments, but you can also use :average

  • if:

    a block that returns a condition that must be met for the counter to be incremented (optional)

  • if_async:

    same as if option but runs async to avoid slowing down inserts and updates (optional)

  • uniq_by:

    a block to get the identifier of the element to be counted (optional)

  • scopes:

    a block to get the list of scopes for which the counter must be incremented (optional)

Raises:

  • (ArgumentError)


27
28
29
30
31
32
33
34
# File 'lib/statisfy/counter.rb', line 27

def count(args = {})
  raise ArgumentError, "You must provide at least one event" if args[:every].blank?

  catch_events(*args[:every])
  apply_default_counter_options(args)
  const_set(:COUNTER_TYPE, args[:type] || :increment)
  class_eval(&Statisfy.configuration.append_to_counters) if Statisfy.configuration.append_to_counters.present?
end

#elements_in(scope: nil, month: nil) ⇒ Object

Returns the list of elements in the set (in case you use .append and not .increment)



85
86
87
# File 'lib/statisfy/counter.rb', line 85

def elements_in(scope: nil, month: nil)
  redis_client.lrange(key_for(scope:, month:), 0, -1)
end

#key_for(scope:, month: nil, key_value: nil) ⇒ Object

This is the name of the Redis key that will be used to store the counter



114
115
116
117
118
119
120
121
122
# File 'lib/statisfy/counter.rb', line 114

def key_for(scope:, month: nil, key_value: nil)
  {
    counter: name.demodulize.underscore,
    month:,
    scope_type: scope&.class&.name,
    scope_id: scope&.id,
    key_value:
  }.to_json
end

#members(scope: nil, month: nil) ⇒ Object



78
79
80
# File 'lib/statisfy/counter.rb', line 78

def members(scope: nil, month: nil)
  redis_client.smembers(key_for(scope:, month:))
end

#redis_clientObject



124
125
126
# File 'lib/statisfy/counter.rb', line 124

def redis_client
  Statisfy.configuration.redis_client
end

#reset(scope: nil, month: nil) ⇒ Object

This allows to reset all the counters for a given scope (optional) and a given month (optional)



161
162
163
164
165
166
167
# File 'lib/statisfy/counter.rb', line 161

def reset(scope: nil, month: nil)
  all_keys(scope:, month:).each do |key|
    redis_client.del(key)
  end

  true
end

#size(scope: nil, month: nil) ⇒ Object



74
75
76
# File 'lib/statisfy/counter.rb', line 74

def size(scope: nil, month: nil)
  redis_client.scard(key_for(scope:, month:))
end

#sum(scope: nil, month: nil) ⇒ Object



89
90
91
92
93
94
# File 'lib/statisfy/counter.rb', line 89

def sum(scope: nil, month: nil)
  stored_values = elements_in(scope:, month:)
  return 0 if stored_values.empty?

  stored_values.map(&:to_i).reduce(:+)
end

#trigger_with(resource, options = {}) ⇒ Object

This allows to run a counter increment manually It is useful when you want to backfill counters



132
133
134
135
136
137
138
139
# File 'lib/statisfy/counter.rb', line 132

def trigger_with(resource, options = {})
  counter = new
  counter.params = resource

  return unless options[:skip_validation] || counter.should_run?

  counter.perform(resource)
end

#value(scope: nil, month: nil) ⇒ Object

This is the method that is called when you want to get the value of a counter.

By default it returns the number of elements in the set. You can override it if the counter requires more complex logic see RateOfAutonomousUsers for example

Parameters:

  • scope: (defaults to: nil)

    the scope of the counter (an Organisation or a Department)

  • month: (defaults to: nil)

    the month for which you want the value of the counter (optional)



61
62
63
64
65
66
67
68
# File 'lib/statisfy/counter.rb', line 61

def value(scope: nil, month: nil)
  month = month&.strftime("%Y-%m") if month.present?
  if const_get(:COUNTER_TYPE) == :aggregate
    average(scope:, month:)
  else
    size(scope:, month:)
  end
end