Class: Workpattern::Workpattern

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

Overview

Represents the working and resting periods across a given number of whole years. Each Workpatternhas a unique name so it can be easily identified amongst all the other Workpattern objects.

This and the Clock class are the only two that should be referenced by calling applications when using this gem.

Constant Summary collapse

@@workpatterns =

Holds collection of Workpattern objects

{}
@@tz =

Holds local timezone info

nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = DEFAULT_WORKPATTERN_NAME, base = DEFAULT_BASE_YEAR, span = DEFAULT_SPAN) ⇒ Workpattern

The new Workpattern object is created with all working minutes.

31st December.

Parameters:

  • name (String) (defaults to: DEFAULT_WORKPATTERN_NAME)

    Every workpattern has a unique name

  • base (Integer) (defaults to: DEFAULT_BASE_YEAR)

    Workpattern starts on the 1st January of this year.

  • span (Integer) (defaults to: DEFAULT_SPAN)

    Workpattern spans this number of years ending on

Raises:

  • (NameError)

    if the given name already exists



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/workpattern/workpattern.rb', line 64

def initialize(name = DEFAULT_WORKPATTERN_NAME, base = DEFAULT_BASE_YEAR, span = DEFAULT_SPAN)
  if workpatterns.key?(name)
    raise(NameError, "Workpattern '#{name}' already exists and can't be created again")
  end
  offset = span < 0 ? span.abs - 1 : 0

  @name = name
  @base = base
  @span = span
  @from = Time.gm(@base.abs - offset)
  @to = Time.gm(@from.year + @span.abs - 1, 12, 31, 23, 59)
  @weeks = SortedSet.new
  @weeks << Week.new(@from, @to)

  workpatterns[@name] = self
  @week_pattern = WeekPattern.new(self)
end

Instance Attribute Details

#baseObject (readonly)

Starting year



35
# File 'lib/workpattern/workpattern.rb', line 35

attr_reader :name, :base, :span, :from, :to, :weeks

#fromObject (readonly)

First date in Workpattern



35
# File 'lib/workpattern/workpattern.rb', line 35

attr_reader :name, :base, :span, :from, :to, :weeks

#nameObject (readonly)

Name given to the Workpattern



35
36
37
# File 'lib/workpattern/workpattern.rb', line 35

def name
  @name
end

#spanObject (readonly)

Number of years



35
# File 'lib/workpattern/workpattern.rb', line 35

attr_reader :name, :base, :span, :from, :to, :weeks

#toObject (readonly)

Last date in Workpattern



35
# File 'lib/workpattern/workpattern.rb', line 35

attr_reader :name, :base, :span, :from, :to, :weeks

#weeksObject (readonly)

Returns the value of attribute weeks.



35
# File 'lib/workpattern/workpattern.rb', line 35

attr_reader :name, :base, :span, :from, :to, :weeks

Class Method Details

.clearObject

Deletes all Workpattern objects



93
94
95
# File 'lib/workpattern/workpattern.rb', line 93

def self.clear
  workpatterns.clear
end

.delete(name) ⇒ Boolean

Deletes the specific named Workpattern if it doesn’t

Parameters:

  • name (String)

    of the required Workpattern

Returns:

  • (Boolean)

    true if the named Workpattern existed or false



119
120
121
# File 'lib/workpattern/workpattern.rb', line 119

def self.delete(name)
  workpatterns.delete(name).nil? ? false : true
end

.from_h(hash, overwrite: false) ⇒ Object

Raises:

  • (ArgumentError)


176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/workpattern/workpattern.rb', line 176

def self.from_h(hash, overwrite: false)
  unless hash.key?(:version)
    raise ArgumentError, "from_h: hash is missing a :version key " \
                         "(if deserialising from JSON, use symbolize_names: true)"
  end
  unless hash[:version] == 1
    raise ArgumentError, "from_h: unsupported version #{hash[:version].inspect} " \
                         "(supported: 1)"
  end
  unless hash[:name].is_a?(String) && !hash[:name].empty?
    raise ArgumentError, "from_h: :name must be a non-empty String"
  end
  unless hash[:base].is_a?(Integer)
    raise ArgumentError, "from_h: :base must be an Integer"
  end
  unless hash[:span].is_a?(Integer) && hash[:span] != 0
    raise ArgumentError, "from_h: :span must be a non-zero Integer"
  end
  unless hash[:weeks].is_a?(Array)
    raise ArgumentError, "from_h: :weeks must be an Array"
  end

  name = hash[:name]
  if workpatterns.key?(name) && !overwrite
    raise NameError, "Workpattern '#{name}' already exists and can't be created again"
  end

  wp = allocate
  wp.instance_variable_set(:@name, name)
  wp.instance_variable_set(:@base, hash[:base])
  wp.instance_variable_set(:@span, hash[:span])

  offset    = hash[:span] < 0 ? hash[:span].abs - 1 : 0
  from_time = Time.gm(hash[:base].abs - offset)
  to_time   = Time.gm(from_time.year + hash[:span].abs - 1, 12, 31, 23, 59)
  wp.instance_variable_set(:@from, from_time)
  wp.instance_variable_set(:@to,   to_time)

  weeks = SortedSet.new
  hash[:weeks].each { |wh| weeks << Week.from_h(wh) }
  raise ArgumentError, "from_h: :weeks must not be empty" if weeks.empty?
  wp.instance_variable_set(:@weeks, weeks)
  wp.instance_variable_set(:@week_pattern, WeekPattern.new(wp))

  workpatterns.delete(name) if overwrite
  workpatterns[name] = wp
  wp
end

.get(name) ⇒ Object

Returns the specific named Workpattern exist

Parameters:

  • name (String)

    of the required Workpattern

Raises:

  • (NameError)

    if a Workpattern of the supplied name does not



109
110
111
112
# File 'lib/workpattern/workpattern.rb', line 109

def self.get(name)
  return workpatterns[name] if workpatterns.key?(name)
  raise(NameError, "Workpattern '#{name}' doesn't exist so can't be retrieved")
end

.to_aArray

Returns an Array containing all the Workpattern objects

Returns:

  • (Array)

    all Workpattern objects



100
101
102
# File 'lib/workpattern/workpattern.rb', line 100

def self.to_a
  workpatterns.to_a
end

.workpatternsObject



18
19
20
# File 'lib/workpattern/workpattern.rb', line 18

def self.workpatterns
  @@workpatterns
end

Instance Method Details

#calc(start, duration) ⇒ DateTime

Calculates the resulting date when the duration in minutes is added to the start date. The duration is always in whole minutes and subtracts from start when it is a negative number.

start

Parameters:

  • start (DateTime)

    date to add or subtract minutes

  • duration (Integer)

    in minutes to add or subtract to date

Returns:

  • (DateTime)

    the date when duration is added to



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/workpattern/workpattern.rb', line 235

def calc(start, duration)
  return start if duration == 0
  a_day = SAME_DAY

  utc_start = to_utc(start)

  while duration != 0

    if a_day == PREVIOUS_DAY
     utc_start -= DAY
     a_day = SAME_DAY
      utc_start = Time.gm(utc_start.year, utc_start.month, utc_start.day,LAST_TIME_IN_DAY.hour, LAST_TIME_IN_DAY.min)
     week = find_weekpattern(utc_start)
	  
     if week.working?(utc_start)
       duration += 1
     end
    else
     week = find_weekpattern(utc_start)
    end
    utc_start, duration, a_day = week.calc(utc_start, duration, a_day)
  end
  to_local(utc_start)
end

#diff(start, finish) ⇒ Integer

Returns number of minutes between two dates

Parameters:

  • start (DateTime)

    is the date to start from

  • finish (DateTime)

    is the date to end with

Returns:

  • (Integer)

    number of minutes between the two dates



276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/workpattern/workpattern.rb', line 276

def diff(start, finish)
  utc_start = to_utc(start)
  utc_finish = to_utc(finish)
  utc_start, utc_finish = utc_finish, utc_start if utc_finish < utc_start
  minutes = 0

  while utc_start != utc_finish
    week = find_weekpattern(utc_start)
    r_minutes, utc_start = week.diff(utc_start, utc_finish)
    minutes += r_minutes
  end
  minutes
end

#find_weekpattern(date) ⇒ Week

Retrieve the correct Week pattern for the supplied date.

If the supplied date is outside the span of the Workpattern object then it returns an all working Week object for the calculation.

date in it’s range

Parameters:

  • date (DateTime)

    whose containing Week pattern is required

Returns:

  • (Week)

    Week object that includes the supplied



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/workpattern/workpattern.rb', line 300

def find_weekpattern(date)
  # find the pattern that fits the date
  #
  if date < @from
    result = Week.new(Time.at(0), @from - MINUTE, WORK_TYPE)
  elsif date > to
    result = Week.new(@to + MINUTE, Time.new(9999), WORK_TYPE)
  else

    date = Time.gm(date.year, date.month, date.day)

    result = @weeks.find { |week| week.start <= date && week.finish >= date }
  end
  result
end

#resting(args = {}) ⇒ Object

Convenience method that calls #workpattern with the :work_type specified as resting.

See Also:



153
154
155
156
# File 'lib/workpattern/workpattern.rb', line 153

def resting(args = {})
  args[:work_type] = REST_TYPE
  workpattern(args)
end

#timezoneObject

Retrieves the local timezone



52
53
54
# File 'lib/workpattern/workpattern.rb', line 52

def timezone
  @@tz || @@tz = TZInfo::Timezone.get(Time.now.zone)
end

#to_hObject



168
169
170
171
172
173
174
# File 'lib/workpattern/workpattern.rb', line 168

def to_h
  { version: 1,
    name:    @name,
    base:    @base,
    span:    @span,
    weeks:   @weeks.map(&:to_h) }
end

#to_local(date) ⇒ Object

Converts a date like object into local time



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

def to_local(date)
  date.to_time.getgm
end

#to_utc(date) ⇒ Object

Converts a date like object into utc



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

def to_utc(date)
  date.to_time.utc
end

#week_patternObject



82
83
84
# File 'lib/workpattern/workpattern.rb', line 82

def week_pattern
  @week_pattern
end

#working(args = {}) ⇒ Object

Convenience method that calls #workpattern with the :work_type specified as working.

See Also:



163
164
165
166
# File 'lib/workpattern/workpattern.rb', line 163

def working(args = {})
  args[:work_type] = WORK_TYPE
  workpattern(args)
end

#working?(start) ⇒ Boolean

Returns true if the given minute is working and false if it is resting.

Parameters:

  • start (DateTime)

    DateTime being tested

Returns:

  • (Boolean)

    true if working and false if resting



265
266
267
268
# File 'lib/workpattern/workpattern.rb', line 265

def working?(start)
  utc_start = to_utc(start)
  find_weekpattern(utc_start).working?(utc_start)
end

#workpattern(opts = {}) ⇒ Object

Applys a working or resting pattern to the Workpattern object.

The #resting and #working methods are convenience methods that call this with the appropriate :work_type already set.

apply to.It defaults to :all days to apply the pattern. Defaults to 00:00. days to apply the pattern. Defaults to 23:59. Defaults to working.

Parameters:

  • opts (Hash) (defaults to: {})

    the options used to apply a workpattern

Options Hash (opts):

  • :start (Date)

    The first date to apply the pattern. Defaults to the start attribute.

  • :finish (Date)

    The last date to apply the pattern. Defaults to the finish attribute.

  • :days (DAYNAMES)

    The specific day or days the pattern will

  • :start_time ((#hour, #min))

    The first time in the selected

  • :finish_time ((#hour, #min))

    The last time in the selected

  • :work_type ((WORK_TYPE || REST_TYPE))

    Either working or resting.

See Also:



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

def workpattern(opts = {})
  week_pattern.workpattern(opts)
end