Class: Tickwork::Manager

Inherits:
Object
  • Object
show all
Defined in:
lib/tickwork/manager.rb

Defined Under Namespace

Classes: DuplicateJobName, NoDataStoreDefined, NoHandlerDefined

Constant Summary collapse

MANAGER_KEY =
'__manager'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeManager

Returns a new instance of Manager.



11
12
13
14
15
16
17
# File 'lib/tickwork/manager.rb', line 11

def initialize
  @events = []
  @callbacks = {}
  @config = default_configuration
  @handler = nil
  @error_handler = nil
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



9
10
11
# File 'lib/tickwork/manager.rb', line 9

def config
  @config
end

Instance Method Details

#clear!Object



134
135
136
# File 'lib/tickwork/manager.rb', line 134

def clear!
  data_store.write(data_store_key, nil)
end

#configure {|config| ... } ⇒ Object

Yields:



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/tickwork/manager.rb', line 23

def configure
  yield(config)
  [:max_threads, :tick_size, :max_ticks, :max_catchup].each do |int_config_key|
    config[int_config_key] = config[int_config_key].to_i
  end
  if config[:sleep_timeout]
    config[:logger].warn 'INCORRECT USAGE: sleep_timeout is not used'
    if config[:sleep_timeout] < 1
      config[:logger].warn 'sleep_timeout must be >= 1 second'
    end
  end
  if config[:data_store].nil?
    raise NoDataStoreDefined.new
  end
  if config[:tick_size] > 60
    config[:logger].warn 'tick_size is greater than 60. Events scheduled for a specific time may be missed'
  end
end

#data_storeObject



65
66
67
# File 'lib/tickwork/manager.rb', line 65

def data_store
  config[:data_store]
end

#data_store_keyObject



89
90
91
# File 'lib/tickwork/manager.rb', line 89

def data_store_key
  @data_store_key ||= config[:namespace] + MANAGER_KEY
end

#default_configurationObject



42
43
44
45
46
47
48
49
50
51
52
# File 'lib/tickwork/manager.rb', line 42

def default_configuration
  { 
    logger: Logger.new(STDOUT), 
    thread: false, 
    max_threads: 10,
    namespace: '_tickwork_',
    tick_size: 60, # 1 minute
    max_ticks: 10,
    max_catchup: 3600 # 1 hour
  }
end

#error_handler(&block) ⇒ Object



60
61
62
63
# File 'lib/tickwork/manager.rb', line 60

def error_handler(&block)
  @error_handler = block if block_given?
  @error_handler
end

#every(period, job, options = {}, &block) ⇒ Object



74
75
76
77
78
79
80
81
82
83
# File 'lib/tickwork/manager.rb', line 74

def every(period, job, options={}, &block)
  if period < config[:tick_size]
    config[:logger].warn 'period is smaller than tick size. will fail to schedule all events'
  end
  if options[:at].respond_to?(:each)
    every_with_multiple_times(period, job, options, &block)
  else
    register(period, job, block, options)
  end
end

#fire_callbacks(event, *args) ⇒ Object



85
86
87
# File 'lib/tickwork/manager.rb', line 85

def fire_callbacks(event, *args)
  @callbacks[event].nil? || @callbacks[event].all? { |h| h.call(*args) }
end

#handle_error(e) ⇒ Object



142
143
144
# File 'lib/tickwork/manager.rb', line 142

def handle_error(e)
  error_handler.call(e) if error_handler
end

#handler(&block) ⇒ Object

Raises:



54
55
56
57
58
# File 'lib/tickwork/manager.rb', line 54

def handler(&block)
  @handler = block if block_given?
  raise NoHandlerDefined unless @handler
  @handler
end

#log(msg) ⇒ Object



146
147
148
# File 'lib/tickwork/manager.rb', line 146

def log(msg)
  config[:logger].info(msg)
end

#log_error(e) ⇒ Object



138
139
140
# File 'lib/tickwork/manager.rb', line 138

def log_error(e)
  config[:logger].error(e)
end

#on(event, options = {}, &block) ⇒ Object



69
70
71
72
# File 'lib/tickwork/manager.rb', line 69

def on(event, options={}, &block)
  raise "Unsupported callback #{event}" unless [:before_tick, :after_tick, :before_run, :after_run].include?(event.to_sym)
  (@callbacks[event.to_sym]||=[]) << block
end

#runObject

pretty straight forward if you think about it run the ticks from the last time we ran to our max but don’t run ticks in the future

Raises:



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/tickwork/manager.rb', line 96

def run
  raise NoDataStoreDefined.new if data_store.nil?
  log "Starting clock for #{@events.size} events: [ #{@events.map(&:to_s).join(' ')} ]"

  last = last_t = data_store.read(data_store_key)
  last ||= Time.now.to_i - config[:tick_size] 
  if !config[:max_catchup].nil? && config[:max_catchup] > 0 && last < Time.now.to_i - config[:max_catchup]
    last = Time.now.to_i - config[:max_catchup] - config[:tick_size]
  end

  ticks = 0
  tick_time = last + config[:tick_size]

  while ticks < config[:max_ticks] && tick_time <= Time.now.to_i do
    tick(tick_time) 
    last = tick_time
    tick_time += config[:tick_size]
    ticks += 1
  end
  data_store.write(data_store_key, last)
  last
end

#thread_available?Boolean

Returns:

  • (Boolean)


19
20
21
# File 'lib/tickwork/manager.rb', line 19

def thread_available?
  Thread.list.select { |t| t['creator'] == self }.count < config[:max_threads]
end

#tick(t = Time.now.to_i) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/tickwork/manager.rb', line 119

def tick(t=Time.now.to_i)
  t = Time.at(t) # TODO refactor below
  if (fire_callbacks(:before_tick))
    events = events_to_run(t)
    events.each do |event|
      if (fire_callbacks(:before_run, event, t))
        event.run(t)
        fire_callbacks(:after_run, event, t)
      end
    end
  end
  fire_callbacks(:after_tick)
  events
end