Module: Statisfy::Counter::ClassMethods

Defined in:
lib/statisfy/counter.rb

Instance Method Summary collapse

Instance Method Details

#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)



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

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
50
51
52
# File 'lib/statisfy/counter.rb', line 41

def apply_default_counter_options(args)
  define_method(:identifier, 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(:value, args[:value] || -> {})
  define_method(:should_run?, args[:if] || -> { true })
  define_method(:on_destroy, args[:on_destroy]) if args[:on_destroy].present?
  define_method(:decrement_on_destroy?, args[:decrement_on_destroy].is_a?(Proc) ? args[:decrement_on_destroy] : lambda {
                                                                                                                  args[:decrement_on_destroy] || true
                                                                                                                })
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



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

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)



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

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

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

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



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

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

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

  counter.perform(resource)
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



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

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



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

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

#redis_clientObject



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

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)



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

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



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

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

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



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

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

#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)



64
65
66
67
68
69
70
71
# File 'lib/statisfy/counter.rb', line 64

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