Class: MeterCat::Meter

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/meter_cat/meter.rb

Constant Summary collapse

DEFAULT_EXPIRATION =

The expiration time for an in-memory cached meter

3600
DEFAULT_RETRY_ATTEMPTS =

The number of retries in the event of an optimistic locking failure or creation collision

5
DEFAULT_RETRY_DELAY =

The delay between retries, in seconds. Not using exponential back-off to prevent blocking controllers. Better to lose a little data than create a bad user experience.

1

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.namesObject

Returns all unique meter names sorted



72
73
74
# File 'app/models/meter_cat/meter.rb', line 72

def self.names
  Meter.distinct.pluck(:name).sort.map(&:to_sym)
end

.random(args) ⇒ Object

Generates a random sequence for a meter given the following args:

:name, :min, :max, :days


79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'app/models/meter_cat/meter.rb', line 79

def self.random(args)
  name = args[:name]
  min = args[:min].to_i
  max = args[:max].to_i
  stop = Date.today
  start = Date.today - args[:days].to_i

  (start..stop).each do |date|
    value = min + rand(max - min)
    begin
      Meter.create(name: name, value: value, created_on: date)
    rescue
    end
  end
end

.set(name, value = 1, created_on = Date.today) ⇒ Object

Sets or creates a new record for the name and day



97
98
99
100
101
102
# File 'app/models/meter_cat/meter.rb', line 97

def self.set(name, value = 1, created_on = Date.today)
  meter = Meter.find_by_name_and_created_on(name, created_on)
  meter ||= Meter.new(name: name, created_on: created_on)
  meter.value = value
  return meter.save
end

.to_csv(range, names = nil) ⇒ Object

Returns a CSV where rows represent days



138
139
140
141
142
143
144
145
146
147
148
# File 'app/models/meter_cat/meter.rb', line 138

def self.to_csv(range, names = nil)
  meters = to_h(range, names)
  keys = meters.keys.sort!

  CSV.generate do |csv|
    csv << [nil] + keys
    range.each do |date|
      csv << [date] + keys.map { |key| meters[key][date] }
    end
  end
end

.to_h(range, names = nil) ⇒ Object

Returns a hash of names to dates to values



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'app/models/meter_cat/meter.rb', line 106

def self.to_h(range, names = nil)
  meters = {}

  # Inject dependencies into the names array

  calculator = MeterCat.config.calculator
  calculator.dependencies(names) if names

  # Build conditions for the query

  conditions = {}
  conditions[:created_on] = range if range
  conditions[:name] = names if names

  # Retrieve the data

  Meter.select([:id, :name, :created_on, :value]).where(conditions).find_each do |meter|
    name = meter.name.to_sym
    meters[name] ||= {}
    meters[name][meter.created_on] = meter.value
  end

  # Fill in calculated and missing values

  calculator.calculate(meters, range, names)
  names.each { |name| meters[name] ||= {} } if names

  return meters
end

Instance Method Details

#addObject

Create an object for this name+date in the db if one does not already exist. Add the value from this object to the one in the DB. Returns the result of the ActiveRecord save operation.



34
35
36
37
38
39
40
# File 'app/models/meter_cat/meter.rb', line 34

def add
  meter = nil
  Meter.uncached { meter = Meter.find_by_name_and_created_on(name, created_on) }
  meter ||= Meter.new(name: name, created_on: created_on)
  meter.value += value
  return meter.save
end

#add_with_retryObject

Calls #add with retry logic up to ::MAX_ADD_ATTEMPTS times. Catches ActiveRecord::StaleObjectError and ActiveRecord::RecordNotUnique for retries. Returns the result of the final call to #add



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'app/models/meter_cat/meter.rb', line 46

def add_with_retry
  success = false

  (1..MeterCat.config.retry_attempts).each do
    begin
      success = add
      break if success
    rescue ActiveRecord::StaleObjectError, ActiveRecord::RecordNotUnique
    end
    Kernel.sleep(MeterCat.config.retry_delay)
  end

  return success
end

#expired?Boolean

Determines if the meter is expired and should be flushed from memory to DB

Returns:

  • (Boolean)


63
64
65
# File 'app/models/meter_cat/meter.rb', line 63

def expired?
  return (Time.now - created_at) > MeterCat.config.expiration
end