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, #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)


161
162
163
164
165
166
# File 'lib/lucid_works/datasource/schedule.rb', line 161

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



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

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
# File 'lib/lucid_works/datasource/schedule.rb', line 32

def frequency
  case period
  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



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

def frequency=(frequency)
  self.period = case frequency
  when 'hourly' then 1.hours.seconds.to_i
  when 'daily' then 1.days.seconds.to_i
  when 'weekly' then 1.weeks.seconds.to_i
  else raise "unknown frequency"
  end
end

#hourObject

convenince method for setting the current value in a view selector



132
133
134
# File 'lib/lucid_works/datasource/schedule.rb', line 132

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

#minObject

convenince method for setting the current value in a view selector



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

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

#next_startObject

predict when action will occur next if active at that time



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

def next_start
  return start_time if (now = Time.now) <= start_time 
  # require 'ruby-debug'; debugger
  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


100
101
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
# File 'lib/lucid_works/datasource/schedule.rb', line 100

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 'weekly'
      # require 'ruby-debug'; debugger
      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 
    else
      puts "*** frequency: <#{all_attributes[:frequency]}>"
      raise "unexpected frequency encountered"
    end
end

#start_is_more_than_one_period_in_the_future?Boolean

Returns:

  • (Boolean)


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

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



171
172
173
174
175
# File 'lib/lucid_works/datasource/schedule.rb', line 171

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