Class: Iyyov::Task

Inherits:
Object
  • Object
show all
Includes:
RJack
Defined in:
lib/iyyov/task.rb

Overview

A task to be scheduled and run.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}, &block) ⇒ Task

New task given options matching accessors and block containing work.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/iyyov/task.rb', line 63

def initialize( opts = {}, &block )
  @name        = nil
  @next_time   = nil
  @period      = nil
  @fixed_times = nil
  @fixed_days  = (0..6) #all

  opts.each { |k,v| send( k.to_s + '=', v ) }

  @block = block

  unless period || fixed_times
    raise( SetupError,
           "Task #{ opts.inspect } needs one of period or fixed_times" )
  end

  @log = SLF4J[ [ SLF4J[ self.class ].name, name ].compact.join( '.' ) ]

  @lock = ( Mutex.new if mode == :async )
  @async_rc = nil

  @log.info { "Task created : #{ opts.inspect }" }
end

Instance Attribute Details

#fixed_daysObject

Array or range for days of week in which fixed_times apply. Days are 0 (Sunday) .. 6 (Saturday). Example: M-F == (1..5)

~include?( day_of_week ) (default: (0..6))



42
43
44
# File 'lib/iyyov/task.rb', line 42

def fixed_days
  @fixed_days
end

#fixed_timesObject

One or more fixed time values in 24hour format, local timezone, i.e: [ “11:30”, “23:30” ]

Array(String) (default: nil, use period)



36
37
38
# File 'lib/iyyov/task.rb', line 36

def fixed_times
  @fixed_times
end

#logObject (readonly)

SLF4J logger



59
60
61
# File 'lib/iyyov/task.rb', line 59

def log
  @log
end

#modeObject

Execution mode. If :async, run in separate thread, but only allow one thread for this task to run at any time.

Symbol :sync|:async (default: :sync)



53
54
55
# File 'lib/iyyov/task.rb', line 53

def mode
  @mode
end

#nameObject

Name the task for log reporting.

String (default: nil)



47
48
49
# File 'lib/iyyov/task.rb', line 47

def name
  @name
end

#next_timeObject (readonly)

Once schedule succeeds, the absolute next time to execute.



56
57
58
# File 'lib/iyyov/task.rb', line 56

def next_time
  @next_time
end

#periodObject

Regular interval between subsequent executions in seconds.

Numeric (default: nil, use fixed_times)



30
31
32
# File 'lib/iyyov/task.rb', line 30

def period
  @period
end

Instance Method Details

#filter(rc) ⇒ Object



144
145
146
# File 'lib/iyyov/task.rb', line 144

def filter( rc )
  rc.is_a?( Symbol ) ? rc : :continue
end

#next_fixed_time(now) ⇒ Object



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/iyyov/task.rb', line 169

def next_fixed_time( now )
  day = Date.civil( now.year, now.month, now.day )
  last = day + 7
  ntime = nil
  while ntime.nil? && day <= last
    if fixed_days.include?( day.wday )
      Array( fixed_times ).each do |ft|
        ft = time_on_date( day, Time.parse( ft, now ) )
        ntime = ft if ( ( ft > now ) && ( ntime.nil? || ft < ntime ) )
      end
    end
    day += 1
  end
  ntime
end

#next_time_to_sObject



165
166
167
# File 'lib/iyyov/task.rb', line 165

def next_time_to_s
  @next_time.strftime( '%Y-%m-%dT%H:%M:%S' ) if @next_time
end

#runObject

Execute the task, after which the task will be scheduled again in period time or for the next of fixed_times, unless :stop is returned.



90
91
92
93
94
95
96
97
98
99
# File 'lib/iyyov/task.rb', line 90

def run
  rc = :continue
  if mode == :async
    rc = test_async_return_code
    run_thread if rc == :continue
  else
    rc = run_direct
  end
  rc
end

#run_directObject



133
134
135
136
137
138
139
140
141
142
# File 'lib/iyyov/task.rb', line 133

def run_direct
  @log.debug "Running."
  begin
    rc = ( @block.call( self ) if @block )
    filter( rc )
  rescue StandardError => e
    @log.error( "Handled and stopped with: ", e )
    :stop
  end
end

#run_threadObject



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

def run_thread
  Thread.new do
    if @lock.try_lock
      begin
        @async_rc = run_direct
      ensure
        @lock.unlock
      end
    else
      @log.warn "Already running, skipping this run."
    end
  end
end

#schedule(now) ⇒ Object

Determine next_time from now based on period or fixed_times



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/iyyov/task.rb', line 149

def schedule( now )
  @next_time = nil

  if fixed_times
    @next_time = next_fixed_time( now )
  elsif period
    @next_time = ( now + period )
  end

  if @next_time && ( @next_time - now ) > 60.0
    @log.debug { "Next run scheduled @ #{ next_time_to_s }" }
  end

  @next_time
end

#test_async_return_codeObject



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/iyyov/task.rb', line 101

def test_async_return_code
  rc = :continue
  # Note: Currently only the main event loop thread goes here and
  # so the only case for contention is this task already running
  # in run_thread. In this case we can warn + :skip early.
  if @lock.try_lock
    begin
      rc = @async_rc if ( @async_rc == :stop ) || ( @async_rc == :shutdown )
    ensure
      @lock.unlock
    end
  else
    @log.warn "Already running, (pre) skipping this run."
    rc = :skip
  end
  rc
end

#time_on_date(d, t) ⇒ Object



185
186
187
# File 'lib/iyyov/task.rb', line 185

def time_on_date( d, t )
  Time.local( d.year, d.month, d.day, t.hour, t.min, t.sec, t.usec )
end