Class: MeterCat::Meter
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- MeterCat::Meter
- 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
-
.names ⇒ Object
Returns all unique meter names sorted.
-
.random(args) ⇒ Object
Generates a random sequence for a meter given the following args: [ :name, :min, :max, :days ].
-
.set(name, value = 1, created_on = Date.today) ⇒ Object
Sets or creates a new record for the name and day.
-
.to_csv(range, names = nil) ⇒ Object
Returns a CSV where rows represent days.
-
.to_h(range, names = nil) ⇒ Object
Returns a hash of names to dates to values.
Instance Method Summary collapse
-
#add ⇒ Object
Create an object for this name+date in the db if one does not already exist.
-
#add_with_retry ⇒ Object
Calls #add with retry logic up to ::MAX_ADD_ATTEMPTS times.
-
#expired? ⇒ Boolean
Determines if the meter is expired and should be flushed from memory to DB.
Class Method Details
.names ⇒ Object
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
#add ⇒ Object
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_retry ⇒ Object
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 |