Class: LucidWorks::Datasource::Schedule

Inherits:
Base
  • Object
show all
Defined in:
lib/lucid_works/datasource/schedule.rb

Overview

Datasource::Schedule supplies numerous convenience methods to make it easy and clean to display and set schedules in simple formats

The simple formats supported are:

hourly at 15 minutes past the hour # => {:frequency => 'hourly', :start => {:minutes => 15}}
daily at 3:15                      # => {:frequency => 'daily', :start => {:hours => 3, :minutes => 15}}
weekly at 3:15 on Tuesday          # => {:frequency => 'weekly', :start => {:days => 1, :hours => 3, :minutes => 15}}

Notes:

day of week is specified as the # of days from Monday b/c Rails day of week helpers are monday-based
:start hash options keys are pluralized b/c we feed them direcly to Time.advance, e.g. Time.now.advance(:days => 3)

Constant Summary

Constants included from Utils::BoolConverter

Utils::BoolConverter::FALSE_VALUES, Utils::BoolConverter::TRUE_VALUES

Instance Attribute Summary

Attributes inherited from Base

#attributes, #id, #parent, #persisted, #raw_response, #response_data

Instance Method Summary collapse

Methods inherited from Base

all, collection_url, #collection_url, create, #destroy, extract_parent_from_options, find, first, human_attribute_value, #human_attribute_value, #initialize, #inspect, last, member_url, #member_url, #persisted?, #read_attribute_for_validation, #save, schema, singleton_name, to_select, #update_attributes

Methods included from SimpleNaming

#model_name

Methods included from Utils::BoolConverter

#to_bool

Constructor Details

This class inherits a constructor from LucidWorks::Base

Instance Method Details

#custom?Boolean

convenience method for detecting whether schedule can be represented in simple format or should be described as custom (meaning it is beyond the capabilities of the convenience methods)

Returns:

  • (Boolean)


167
168
169
170
171
172
# File 'lib/lucid_works/datasource/schedule.rb', line 167

def custom?
  return true if self.frequency == 'custom'
  return false unless self.start_time
  return true if self.start_is_more_than_one_period_in_the_future?
  return false
end

#day_of_weekObject

convenince method for setting the current value in a view selector



152
153
154
155
# File 'lib/lucid_works/datasource/schedule.rb', line 152

def day_of_week
  # subract 1 because '%u' returns 1-7, and this is working with zero-indexed ruby arrays
  self.start_time.localtime.strftime('%u').to_i - 1 rescue nil
end

#frequencyObject

returns hourly, daily, weekly based on period returns custom if it doesn’t fit a supported interval



32
33
34
35
36
37
38
39
40
41
# File 'lib/lucid_works/datasource/schedule.rb', line 32

def frequency
  case period
  when 1.minute.seconds..59.minutes.seconds then 'every'
  when 1.weeks.seconds then 'weekly'
  when 1.days.seconds then 'daily'
  when 1.hours.seconds then 'hourly'
  when 0 then nil
  else 'custom'
  end
end

#frequency=(frequency) ⇒ Object

accepts hourly, daily, weekly, and sets period to respective # of seconds



46
47
48
49
50
51
52
53
54
55
# File 'lib/lucid_works/datasource/schedule.rb', line 46

def frequency=(frequency)
  self.period = case frequency
  when 'every' then period
  when 'hourly' then 1.hours.seconds.to_i
  when 'daily' then 1.days.seconds.to_i
  when 'weekly' then 1.weeks.seconds.to_i
  when 'custom' then period # don't change anything
  else raise "unknown frequency"
  end
end

#hourObject

convenince method for setting the current value in a view selector



138
139
140
# File 'lib/lucid_works/datasource/schedule.rb', line 138

def hour
  self.start_time.localtime.hour rescue nil
end

#minObject

convenince method for setting the current value in a view selector



145
146
147
# File 'lib/lucid_works/datasource/schedule.rb', line 145

def min
  self.start_time.localtime.min rescue nil
end

#next_startObject

predict when action will occur next if active at that time



60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/lucid_works/datasource/schedule.rb', line 60

def next_start
  return start_time if (now = Time.now) <= start_time 
  time_since_start = now - start_time
  last_interval_num = (time_since_start / period).to_i
  next_interval_num =  if (time_since_start % period) == 0
    # this is sort of a stupid condition b/c time precision is millisecond or less
    # for human purposes we wouldn't care if the result were as though the call 
    # happened a millisecond later. But whatever.
    last_interval_num # the next interval *is* the last interval if it is exactly now
  else
    last_interval_num + 1
  end
  start_time + period * next_interval_num
end

#schedule=(all_attributes) ⇒ Object

phantom attribute for compiling real start time and frequency from appropriate form data Allows the user to specify a schedule simply with repeat interval (hourly, daily, weekly) and time within the interval to run (e.g 5 past, or Tuesdays at 2:15)

We have to figure out when the actual start time will be because we are not asking the user for this. It needs to be:

* at the requested time relative to the interval chosen
* as soon as possible
* in the future

This is obvious to humans, but when computing it, you run into the following problem. It happens with all interval sizes, but for the sake of example, we’ll use weekly:

Problem 1) If today is Thursday, and the user asks for “weekly on Wednesdays,” Wednesday has already happened this week. We have to make sure we pick the nearest wednesday that is in the future

Problem 2) If today is Tuesday, and the user asks for “weekly on Wednesdays,”, the simple solution to problem 1 (start next week instead of this week) causes you to skip this Tuesday, even though it is valid and is what the user would expect

The algorith to solve both problems at once is this:

* From Time.now, back up to the beginning of the interval even if it is in the past. 
* Fast forward to the requested siimple start time
* If you are still in the past, advance by one interval


102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/lucid_works/datasource/schedule.rb', line 102

def schedule=(all_attributes)
  # convert to format accepted by Time.advance
  if all_attributes['start']
    all_attributes['start'].to_options!
    all_attributes['start'].each{|k,v| all_attributes['start'][k]=v.to_i}
  end
  
  self.active = all_attributes['active']  if all_attributes.keys.include?('active')
  
  now = Time.now
  self.frequency = all_attributes['frequency']
  self.start_time = 
    case all_attributes['frequency']
    when 'every'
      self.period = all_attributes['period'].to_i
      now.ceil(period)
    when 'weekly'
      start = now.beginning_of_week.advance(all_attributes['start'])
      start < now ? start.advance(:weeks => 1) : start 
    when 'daily'                                                       
      start = now.beginning_of_day.advance(all_attributes['start'])
      start < now ? start.advance(:days => 1) : start 
    when 'hourly'                                                      
      start = now.change(:min => 0).advance(all_attributes['start'])
      start < now ? start.advance(:hours => 1) : start 
    when 'custom'
      # don't change this schedule's start_time
    else
      puts "*** frequency: <#{all_attributes[:frequency]}>"
      raise "unexpected frequency encountered"
    end
end

#start_is_more_than_one_period_in_the_future?Boolean

Returns:

  • (Boolean)


157
158
159
160
161
# File 'lib/lucid_works/datasource/schedule.rb', line 157

def start_is_more_than_one_period_in_the_future?
  # This works fine with start times that are multiple periods in the past
  # because that gives a negative difference, and period is always >= 0
    (self.start_time - Time.now) > self.period 
end

#ui_appropriate_defaults!Object

convenience method for setting defaults in a UI that make sense for a user



177
178
179
180
181
# File 'lib/lucid_works/datasource/schedule.rb', line 177

def ui_appropriate_defaults!
  if self.start_time.blank? || self.period == 0
    self.active = true
  end
end