Class: IWonder::Metric

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/i_wonder/metric.rb

Constant Summary collapse

BACK_DATE_ITERATIONS =
30
QUOTE_REMOVER =

this should strip out quoted text first. This way variables can still be used in where statements

/(\".*?\")/m
DANGEROUS_WORDS =
/(save|update|create|destroy|delete)/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#back_date_snapshotsObject

Returns the value of attribute back_date_snapshots.



6
7
8
# File 'app/models/i_wonder/metric.rb', line 6

def back_date_snapshots
  @back_date_snapshots
end

Class Method Details

.take_snapshotsObject



165
166
167
# File 'app/models/i_wonder/metric.rb', line 165

def take_snapshots
  needs_to_be_measured.find_each(&:take_snapshot)
end

Instance Method Details

#adjust_snapshot_settingsObject



52
53
54
55
56
57
58
59
60
# File 'app/models/i_wonder/metric.rb', line 52

def adjust_snapshot_settings
  self.frequency ||= -1
  if self.takes_snapshots_changed? and not self.takes_snapshots?
    self.frequency = -1
  end            
  self.takes_snapshots = !!(self.frequency.nil? or self.frequency > 0)

  true # this preventing it from thinking the validation failed
end

#avoid_dangerous_wordsObject



71
72
73
74
75
# File 'app/models/i_wonder/metric.rb', line 71

def avoid_dangerous_words
  if collection_method.present? and collection_method.gsub(QUOTE_REMOVER, "") =~ DANGEROUS_WORDS
    errors.add(:collection_method, "Doesn't like the word \"#{$1}\"")
  end
end

#back_date_if_chosenObject



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'app/models/i_wonder/metric.rb', line 218

def back_date_if_chosen
  if @back_date_snapshots and @back_date_snapshots.to_s =~ /1|true|on/i and takes_snapshots? and self.earliest_measurement.nil?
    
    self.reload # this keeps bad variables and change states from sneaking in
    
    start_time = Time.zone.now - BACK_DATE_ITERATIONS * frequency
    end_time = start_time + frequency
    start_time += 1.second
    self.snapshots.create(:data => run_collection_method_from(start_time, end_time), :start_time => start_time, :end_time => end_time)
    self.update_attribute(:earliest_measurement, start_time)
    
    # not that the first snapshot is taken, running the :take_snapshot command will fill in the rest
    take_snapshot
  end
end

#custom_type?Boolean

Returns:

  • (Boolean)


114
115
116
# File 'app/models/i_wonder/metric.rb', line 114

def custom_type?
  self.collection_type == "custom"
end

#event_counter_type?Boolean

Returns:

  • (Boolean)


108
109
110
# File 'app/models/i_wonder/metric.rb', line 108

def event_counter_type?
  self.collection_type == "event_counter" or self.collection_type.nil?
end

#locked?Boolean

TODO: some safegaurds need to be added for editing metrics that are already running.

Returns:

  • (Boolean)


119
120
121
# File 'app/models/i_wonder/metric.rb', line 119

def locked?
  snapshots.count > 0
end

#model_counter_is_validObject



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'app/models/i_wonder/metric.rb', line 78

def model_counter_is_valid
  if model_counter_type?
    
    begin
      self.model_counter_class.constantize
      
      if self.model_counter_scopes.present?
        if self.model_counter_scopes =~ /\s/
          errors.add(:model_counter_scopes, "can't contain spaces")
        elsif self.model_counter_scopes =~ DANGEROUS_WORDS
          errors.add(:model_counter_scopes, "can't contain dangerous words (save|update|create|destroy|delete)")
        else
          self.model_counter_scopes.split(".").each{|sc|
            unless self.model_counter_class.constantize.respond_to?(sc)
              errors.add(:model_counter_scopes, "doesn't respond to '#{sc}'")
            end
          }
        end
      end
      
      if self.model_counter_method == "Creation Rate" and !self.model_counter_class.constantize.new.respond_to?("created_at")
        errors.add(:base, "Can't calculate creation rate on models without a :created_at column")
      end
      
    rescue Exception => e
      errors.add(:model_counter_class, "is not a valid class")
    end
  end
end

#model_counter_type?Boolean

Returns:

  • (Boolean)


111
112
113
# File 'app/models/i_wonder/metric.rb', line 111

def model_counter_type?
  self.collection_type == "model_counter"
end

#remove_existing_snapshotsObject



63
64
65
66
67
68
# File 'app/models/i_wonder/metric.rb', line 63

def remove_existing_snapshots
  if collection_method_changed? or frequency_changed?
    self.earliest_measurement = nil
    self.snapshots.each(&:mark_for_destruction)
  end
end

#set_collection_methodObject



41
42
43
44
45
46
47
48
49
# File 'app/models/i_wonder/metric.rb', line 41

def set_collection_method
  if event_counter_type?
    set_event_collection_method
  elsif model_counter_type?
    set_model_collection_method
  elsif custom_type?
    # It should have saved directly
  end
end

#take_snapshotObject

TODO: some code to avoid overlap in snapshots needs to be added



204
205
206
207
208
209
210
211
212
213
214
215
# File 'app/models/i_wonder/metric.rb', line 204

def take_snapshot
  start_time, end_time = timeframe_for_next_snapshot
  while end_time <= Time.zone.now do
    self.snapshots.create(:data => run_collection_method_from(start_time, end_time), :start_time => start_time, :end_time => end_time)
  
    if self.earliest_measurement.nil? or self.earliest_measurement > start_time
      self.update_attribute(:earliest_measurement, start_time)
    end
    
    start_time, end_time = timeframe_for_next_snapshot
  end
end

#value_from(start_time, end_time) ⇒ Object

returns a hash with all the key values between the two times. If it has been collecting integers, the key will be the name of the metric



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'app/models/i_wonder/metric.rb', line 172

def value_from(start_time, end_time)
  if takes_snapshots?
    data = self.snapshots.where("start_time < ? AND end_time > ?", end_time, start_time).collect(&:data)
  else
    data = [run_collection_method_from(start_time, end_time)]
  end
  
  # at this point we have an array of hashs or integers
  array_of_hashes = data.collect{|d| 
    d.is_a?(Hash) ? d.stringify_keys! : {self.name => d}
  }
  
  # At this point we have an array of hashes where the key is the series name. Now we have to merge them into a single hash
  
  # TODO: if it is a custom metric, follow the combination_rule
  final_hash = {}
  array_of_hashes.each{|hash|
    hash.each{|key, value|
      final_hash[key] = (final_hash[key] || 0.0) + value.to_f
    }
  }
  
  if combination_rule == "average" # we have to now divide by the number of snapshots we added together
    final_hash.each{|key, value|
      final_hash[key] = final_hash[key] / array_of_hashes.length.to_f
    }
  end
  
  return final_hash
end