File: observation.rb

package info (click to toggle)
ruby-scientist 1.6.5-1.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 292 kB
  • sloc: ruby: 1,239; sh: 23; makefile: 4
file content (114 lines) | stat: -rw-r--r-- 3,406 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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
107
108
109
110
111
112
113
114
# What happened when this named behavior was executed? Immutable.
class Scientist::Observation

  # An Array of Exception types to rescue when initializing an observation.
  # NOTE: This Array will change to `[StandardError]` in the next major release.
  RESCUES = [Exception]

  # The experiment this observation is for
  attr_reader :experiment

  # The String name of the behavior.
  attr_reader :name

  # The value returned, if any.
  attr_reader :value

  # The raised exception, if any.
  attr_reader :exception

  # The Float seconds elapsed.
  attr_reader :duration

  # The Float CPU time elapsed, in seconds
  attr_reader :cpu_time

  def initialize(name, experiment, fabricated_duration: nil, &block)
    @name       = name
    @experiment = experiment

    start_wall_time, start_cpu_time = capture_times unless fabricated_duration

    begin
      @value = block.call
    rescue *RESCUES => e
      @exception = e
    end

    if fabricated_duration.is_a?(Hash)
      @duration = fabricated_duration["duration"]
      @cpu_time = fabricated_duration["cpu_time"]
    elsif fabricated_duration
      @duration = fabricated_duration
      @cpu_time = 0.0 # setting a default value
    else
      end_wall_time, end_cpu_time = capture_times
      @duration = end_wall_time - start_wall_time
      @cpu_time = end_cpu_time - start_cpu_time
    end

    freeze
  end

  # Return a cleaned value suitable for publishing. Uses the experiment's
  # defined cleaner block to clean the observed value.
  def cleaned_value
    experiment.clean_value value unless value.nil?
  end

  # Is this observation equivalent to another?
  #
  # other            - the other Observation in question
  # comparator       - an optional comparison proc. This observation's value and the
  #                    other observation's value are passed to this to determine
  #                    their equivalency. Proc should return true/false.
  # error_comparator - an optional comparison proc. This observation's Error and the
  #                    other observation's Error are passed to this to determine
  #                    their equivalency. Proc should return true/false.
  #
  # Returns true if:
  #
  # * The values of the observation are equal (using `==`)
  # * The values of the observations are equal according to a comparison
  #   proc, if given
  # * The exceptions raised by the observations are equal according to the
  #   error comparison proc, if given.
  # * Both observations raised an exception with the same class and message.
  #
  # Returns false otherwise.
  def equivalent_to?(other, comparator=nil, error_comparator=nil)
    return false unless other.is_a?(Scientist::Observation)

    if raised? || other.raised?
      if error_comparator
        return error_comparator.call(exception, other.exception)
      else
        return other.exception.class == exception.class &&
          other.exception.message == exception.message
      end
    end

    if comparator
      comparator.call(value, other.value)
    else
      value == other.value
    end
  end

  def hash
    [value, exception, self.class].compact.map(&:hash).inject(:^)
  end

  def raised?
    !exception.nil?
  end

  private

  def capture_times
    [
      Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second),
      Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :float_second)
    ]
  end
end