Module: TickTock

Defined in:
lib/tick_tock.rb,
lib/tick_tock/card.rb,
lib/tick_tock/clock.rb,
lib/tick_tock/punch.rb,
lib/tick_tock/locals.rb,
lib/tick_tock/version.rb,
lib/tick_tock/card_logger.rb

Overview

TickTock makes it easy to wrap your Ruby code to measure nested timings and to log them – even when the code is asynchronous or lazy, so straight-forward blocks do not work.

The TickTock module uses Ruby’s module_function directive to enable two different usage patterns:

  1. Calling methods directly on the module

  2. Including the module into classes as a mix-in

Configuration

When calling methods directly on the module, the instance of Clock configured via TickTock.clock= will be used for all calls.

On the other hand, when including the TickTock module into a class, the class may choose to override #clock to return a specific instance.

Examples:

  1. Calling TickTock methods directly on the module

TickTock.tick_tock(subject: "a block") do
  times_2 = ->(n) { n * 2 }
  [1, 2].map(&TickTock.tick_tock_proc(times_2, subject: :to_s.to_proc))
end
# => [2, 4]

# Logs the following
# I, [2018-02-04T14:22:01.1261 #26]  INFO -- : > Started a block
# I, [2018-02-04T14:22:01.1264 #26]  INFO -- : >> Started 1
# I, [2018-02-04T14:22:01.1265 #26]  INFO -- : << Completed 1 [0.000s]
# I, [2018-02-04T14:22:01.1267 #26]  INFO -- : >> Started 2
# I, [2018-02-04T14:22:01.1267 #26]  INFO -- : << Completed 2 [0.000s]
# I, [2018-02-04T14:22:01.1268 #26]  INFO -- : < Completed a block [0.001s]

  1. Including the module into classes as a mix-in

class Times2
  include TickTock

  def call
    tick_tock(subject: "a block") do
      [1, 2].map(&tick_tock_proc(method(:times_2), subject: :to_s.to_proc))
    end
  end

  def times_2(n)
    n * 2
  end
end

Times2.new.call
# => [2, 4]

# Logs the same as above

Defined Under Namespace

Modules: Locals Classes: Card, CardLogger, Clock, Punch

Constant Summary collapse

VERSION =
"0.2.0"

Class Attribute Summary collapse

Basics: Start and stop a timing context collapse

Helpers: Wrap an asynchronous construct in a timing context collapse

Instance Method Summary collapse

Class Attribute Details

.clockClock

Returns the configured global instance of Clock, assigned by calling clock=.

Returns:

  • (Clock)

    the configured global instance of Clock, assigned by calling clock=.



61
62
63
# File 'lib/tick_tock.rb', line 61

def clock
  @clock
end

Class Method Details

.tick(subject: nil) ⇒ Object

Starts a new timing context, returns a “card” representing it.

Parameters:

  • subject (Object) (defaults to: nil)

    Description of the subject we are timing

Returns:

  • (Object)

    A new card representing the new timing context



80
81
82
# File 'lib/tick_tock.rb', line 80

def tick(subject: nil)
  clock.tick(subject: subject)
end

.tick_tock(subject: nil) ⇒ Object

Executes the given block in a timing context using tick and tock.

Parameters:

  • subject (Object) (defaults to: nil)

    Description of the subject we are timing

Returns:

  • (Object)

    Return value of the given block



100
101
102
103
104
105
# File 'lib/tick_tock.rb', line 100

def tick_tock(subject: nil)
  card = tick(subject: subject)
  yield
ensure
  tock(card: card) unless card.nil?
end

.tick_tock_lazy(lazy_enum_to_wrap, subject: nil) ⇒ Enumerator::Lazy

Wraps a lazy enumerator with a timing context using lazy calls to tick and tock, which will be called when the enumerator starts and completes enumeration respectively.

Parameters:

  • lazy_enum_to_wrap (Enumerator::Lazy)

    Lazy enumerator to wrap

  • subject (Object) (defaults to: nil)

    Description of the subject

Returns:

  • (Enumerator::Lazy)

    Wrapped lazy enumerator



115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/tick_tock.rb', line 115

def tick_tock_lazy(lazy_enum_to_wrap, subject: nil)
  shared_state = [nil]

  lazy_tick = proc { shared_state[0] = tick(subject: subject); [] }
  lazy_tock = proc { shared_state[0] = tock(card: shared_state[0]); [] }

  arr_with_callbacks = [
    [:dummy].lazy.flat_map(&lazy_tick),
    lazy_enum_to_wrap.lazy,
    [:dummy].lazy.flat_map(&lazy_tock)
  ]

  arr_with_callbacks.lazy.flat_map(&:itself)
end

.tick_tock_proc(callable_to_wrap = nil, subject: nil, save_context: false, &proc_to_wrap) ⇒ Proc

Wraps a Proc with a timing context using a call to tick_tock. Can optionally wrap the current nested timing contexts into the Proc, so that when executed asynchronously it will retain the same context.

Parameters:

  • callable_to_wrap (Proc, #call) (defaults to: nil)

    Callable to wrap, given as a normal parameter.

  • subject (Object, Proc, #call) (defaults to: nil)

    Description of the subject, or optionally a function which, when called on the args that are actually eventually passed to the final proc, will generate the actual subject.

  • save_context (Boolean) (defaults to: false)

    Optionally wrap the currently active timing contexts into the Proc, so that when executed asynchronously it will retain the same context.

  • proc_to_wrap (Proc)

    Alternatively you can pass the callable as a “block” parameter – only used if callable_to_wrap is nil.

Returns:

  • (Proc)

    A Proc wrapping the given callable in a timing context.



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/tick_tock.rb', line 152

def tick_tock_proc(
  callable_to_wrap = nil,
  subject: nil,
  save_context: false,
  &proc_to_wrap
)
  tt_proc = proc do |*proc_args|
    # if original subject was a Proc, apply it to args to create the subject
    subject = subject&.respond_to?(:call) ? subject.call(*proc_args) : subject

    tick_tock(subject: subject) do
      (callable_to_wrap || proc_to_wrap).call(*proc_args)
    end
  end

  save_context ? Locals.wrap_proc(&tt_proc) : tt_proc
end

.tock(card:) ⇒ Object

Completes a timing context represented by the given “card”.

Parameters:

  • card (Object)

    A card representing the timing context to complete

Returns:

  • (Object)

    The card after being marked completed



88
89
90
# File 'lib/tick_tock.rb', line 88

def tock(card:)
  clock.tock(card: card)
end

Instance Method Details

#clockClock

When the TickTock module is included in a class, by default returns the configured global instance of Clock. Override #clock in classes to return a class-specific instance instead.

Returns:

  • (Clock)

    when the TickTock module is included in a class, by default returns the configured global instance of Clock. Override #clock in classes to return a class-specific instance instead.



68
69
70
# File 'lib/tick_tock.rb', line 68

def clock
  self.class.clock
end