timeboss 0.0.6 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. data/.gitignore +2 -0
  5. data/.travis.yml +4 -1
  6. data/.yardopts +1 -0
  7. data/CODE_OF_CONDUCT.md +76 -0
  8. data/README.md +30 -18
  9. data/bin/tbsh +15 -0
  10. data/doc/TimeBoss.html +146 -0
  11. data/doc/TimeBoss/Calendar.html +137 -0
  12. data/doc/TimeBoss/Calendar/Day.html +594 -0
  13. data/doc/TimeBoss/Calendar/Half.html +396 -0
  14. data/doc/TimeBoss/Calendar/Month.html +396 -0
  15. data/doc/TimeBoss/Calendar/Parser.html +386 -0
  16. data/doc/TimeBoss/Calendar/Period.html +841 -0
  17. data/doc/TimeBoss/Calendar/Quarter.html +396 -0
  18. data/doc/TimeBoss/Calendar/Support.html +131 -0
  19. data/doc/TimeBoss/Calendar/Support/Formatter.html +459 -0
  20. data/doc/TimeBoss/Calendar/Support/MonthBased.html +591 -0
  21. data/doc/TimeBoss/Calendar/Support/MonthBasis.html +437 -0
  22. data/doc/TimeBoss/Calendar/Support/MonthlyUnit.html +591 -0
  23. data/doc/TimeBoss/Calendar/Support/Navigable.html +723 -0
  24. data/doc/TimeBoss/Calendar/Support/Shiftable.html +138 -0
  25. data/doc/TimeBoss/Calendar/Support/Unit.html +1299 -0
  26. data/doc/TimeBoss/Calendar/Waypoints.html +155 -0
  27. data/doc/TimeBoss/Calendar/Waypoints/Absolute.html +1378 -0
  28. data/doc/TimeBoss/Calendar/Waypoints/Relative.html +4308 -0
  29. data/doc/TimeBoss/Calendar/Week.html +671 -0
  30. data/doc/TimeBoss/Calendar/Year.html +319 -0
  31. data/doc/TimeBoss/Calendars.html +336 -0
  32. data/doc/TimeBoss/Calendars/Broadcast.html +221 -0
  33. data/doc/TimeBoss/Calendars/Broadcast/Basis.html +278 -0
  34. data/doc/TimeBoss/Calendars/Entry.html +399 -0
  35. data/doc/TimeBoss/Support.html +115 -0
  36. data/doc/TimeBoss/Support/Shellable.html +249 -0
  37. data/doc/_index.html +416 -0
  38. data/doc/class_list.html +51 -0
  39. data/doc/css/common.css +1 -0
  40. data/doc/css/full_list.css +58 -0
  41. data/doc/css/style.css +496 -0
  42. data/doc/file.README.html +299 -0
  43. data/doc/file_list.html +56 -0
  44. data/doc/frames.html +17 -0
  45. data/doc/index.html +299 -0
  46. data/doc/js/app.js +314 -0
  47. data/doc/js/full_list.js +216 -0
  48. data/doc/js/jquery.js +4 -0
  49. data/doc/method_list.html +1139 -0
  50. data/doc/top-level-namespace.html +110 -0
  51. data/lib/tasks/calendars.rake +5 -0
  52. data/lib/timeboss.rb +4 -0
  53. data/lib/timeboss/calendar.rb +22 -0
  54. data/lib/timeboss/calendar/day.rb +19 -6
  55. data/lib/timeboss/calendar/half.rb +7 -2
  56. data/lib/timeboss/calendar/month.rb +7 -2
  57. data/lib/timeboss/calendar/parser.rb +1 -0
  58. data/lib/timeboss/calendar/period.rb +26 -11
  59. data/lib/timeboss/calendar/quarter.rb +7 -2
  60. data/lib/timeboss/calendar/support.rb +8 -0
  61. data/lib/timeboss/calendar/support/formatter.rb +1 -0
  62. data/lib/timeboss/calendar/support/month_basis.rb +3 -0
  63. data/lib/timeboss/calendar/support/{month_based.rb → monthly_unit.rb} +24 -18
  64. data/lib/timeboss/calendar/support/navigable.rb +73 -0
  65. data/lib/timeboss/calendar/support/shiftable.rb +3 -2
  66. data/lib/timeboss/calendar/support/unit.rb +26 -0
  67. data/lib/timeboss/calendar/waypoints.rb +6 -80
  68. data/lib/timeboss/calendar/waypoints/absolute.rb +114 -0
  69. data/lib/timeboss/calendar/waypoints/relative.rb +268 -0
  70. data/lib/timeboss/calendar/week.rb +26 -18
  71. data/lib/timeboss/calendar/year.rb +6 -5
  72. data/lib/timeboss/calendars.rb +20 -3
  73. data/lib/timeboss/version.rb +1 -1
  74. data/spec/calendar/day_spec.rb +1 -1
  75. data/spec/calendar/support/{month_based_spec.rb → monthly_unit_spec.rb} +18 -8
  76. data/spec/calendar/week_spec.rb +5 -13
  77. data/spec/calendars/broadcast_spec.rb +27 -7
  78. data/spec/calendars_spec.rb +7 -1
  79. data/timeboss.gemspec +1 -0
  80. metadata +77 -8
@@ -0,0 +1,110 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.9.25
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+
41
+
42
+ <span class="title">Top Level Namespace</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Top Level Namespace
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ </div>
80
+
81
+ <h2>Defined Under Namespace</h2>
82
+ <p class="children">
83
+
84
+
85
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="TimeBoss.html" title="TimeBoss (module)">TimeBoss</a></span>
86
+
87
+
88
+
89
+
90
+ </p>
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+ </div>
101
+
102
+ <div id="footer">
103
+ Generated on Sun Jul 19 10:12:10 2020 by
104
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
+ 0.9.25 (ruby-2.4.1).
106
+ </div>
107
+
108
+ </div>
109
+ </body>
110
+ </html>
@@ -4,6 +4,11 @@ namespace :timeboss do
4
4
  namespace :calendars do
5
5
  TimeBoss::Calendars.each do |entry|
6
6
  namespace entry.name do
7
+ desc "Evaluate an expression for the #{entry.name} calendar"
8
+ task :evaluate, %i[expression] => ['timeboss:init'] do |_, args|
9
+ puts entry.calendar.parse(args[:expression])
10
+ end
11
+
7
12
  desc "Open a shell with the #{entry.name} calendar"
8
13
  task shell: ['timeboss:init'] do
9
14
  require 'timeboss/support/shellable'
@@ -1,2 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "timeboss/version"
3
+
4
+ # TimeBoss
5
+ module TimeBoss
6
+ end
@@ -7,10 +7,32 @@ require 'active_support/core_ext/numeric/time'
7
7
  require_relative './calendar/support/month_basis'
8
8
 
9
9
  module TimeBoss
10
+ # A calendar is built upon a basis, and provides methods for period identification and navigation.
10
11
  class Calendar
11
12
  include Waypoints
13
+
14
+ # @!method parse
15
+ # Parse an identifier into a unit or period.
16
+ # Valid identifiers can include simple units (like "2020Q3", "2020M8W3", "last_quarter"),
17
+ # mathematical expressions (like "this_month+6"),
18
+ # or period expressions (like "2020W1..2020W8", "this_quarter-2..next_quarter")
19
+ # @param identifier [String]
20
+ # @return [Support::Unit, Period]
21
+
12
22
  delegate :parse, to: :parser
13
23
 
24
+ # Get a name by which this calendar can be referenced.
25
+ # @return [String]
26
+ def name
27
+ self.class.to_s.demodulize.underscore
28
+ end
29
+
30
+ # Get a friendly title for this calendar.
31
+ # @return [String]
32
+ def title
33
+ name.titleize
34
+ end
35
+
14
36
  protected
15
37
 
16
38
  attr_reader :basis
@@ -3,32 +3,45 @@ require_relative './support/unit'
3
3
 
4
4
  module TimeBoss
5
5
  class Calendar
6
+ # Representation of a single day within a calendar.
6
7
  class Day < Support::Unit
7
8
  def initialize(calendar, start_date)
8
9
  super(calendar, start_date, start_date)
9
10
  end
10
11
 
12
+ # Get a simple representation of this day.
13
+ # @return [String] (e.g. "2020-08-03")
11
14
  def name
12
15
  start_date.to_s
13
16
  end
14
17
 
18
+ # Get a "pretty" representation of this day.
19
+ # @return [String] (e.g. "August 3, 2020")
15
20
  def title
16
21
  start_date.strftime('%B %-d, %Y')
17
22
  end
18
23
 
19
- def to_s
20
- name
21
- end
24
+ alias_method :to_s, :name
22
25
 
26
+ # Get the index of this day within its containing year.
27
+ # @return [Integer]
23
28
  def index
24
- @_index ||= (start_date - calendar.year_for(start_date).start_date).to_i + 1
29
+ @_index ||= (start_date - year.start_date).to_i + 1
30
+ end
31
+
32
+ # Get the year number for this day.
33
+ # @return [Integer] (e.g. 2020)
34
+ def year_index
35
+ @_year_index ||= year.year_index
25
36
  end
26
37
 
27
- def previous
38
+ private
39
+
40
+ def down
28
41
  self.class.new(calendar, start_date - 1.day)
29
42
  end
30
43
 
31
- def next
44
+ def up
32
45
  self.class.new(calendar, start_date + 1.day)
33
46
  end
34
47
  end
@@ -1,15 +1,20 @@
1
1
  # frozen_string_literal: true
2
- require_relative './support/month_based'
2
+ require_relative './support/monthly_unit'
3
3
 
4
4
  module TimeBoss
5
5
  class Calendar
6
- class Half < Support::MonthBased
6
+ # Representation of a 6-month period within a calendar.
7
+ class Half < Support::MonthlyUnit
7
8
  NUM_MONTHS = 6
8
9
 
10
+ # Get a simple representation of this half.
11
+ # @return [String] (e.g. "2020H2")
9
12
  def name
10
13
  "#{year_index}H#{index}"
11
14
  end
12
15
 
16
+ # Get a "pretty" representation of this half.
17
+ # @return [String] (e.g. "H2 2020")
13
18
  def title
14
19
  "H#{index} #{year_index}"
15
20
  end
@@ -1,15 +1,20 @@
1
1
  # frozen_string_literal: true
2
- require_relative './support/month_based'
2
+ require_relative './support/monthly_unit'
3
3
 
4
4
  module TimeBoss
5
5
  class Calendar
6
- class Month < Support::MonthBased
6
+ # Representation of a single month within a calendar.
7
+ class Month < Support::MonthlyUnit
7
8
  NUM_MONTHS = 1
8
9
 
10
+ # Get a simple representation of this month.
11
+ # @return [String] (e.g. "2020M8")
9
12
  def name
10
13
  "#{year_index}M#{index}"
11
14
  end
12
15
 
16
+ # Get a "pretty" representation of this month.
17
+ # @return [String] (e.g. "August 2020")
13
18
  def title
14
19
  "#{Date::MONTHNAMES[index]} #{year_index}"
15
20
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module TimeBoss
3
3
  class Calendar
4
+ # The parser is responsible for the implementation of a calendar's identifier parsing abilities.
4
5
  class Parser
5
6
  RANGE_DELIMITER = '..'
6
7
  InvalidPeriodIdentifierError = Class.new(StandardError)
@@ -1,11 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
  module TimeBoss
3
3
  class Calendar
4
+ # A calendar period represents a range of units.
4
5
  class Period
5
6
  attr_reader :begin, :end
7
+
8
+ # @!method start_date
9
+ # Get the start date of this period.
10
+ # @return [Date]
6
11
  delegate :start_date, to: :begin
12
+
13
+ # @!method end_date
14
+ # Get the end date of this period.
15
+ # @return [Date]
7
16
  delegate :end_date, to: :end
8
17
 
18
+ # @!method name
19
+ # Get a simple representation of this period.
20
+ # @return [String]
21
+
22
+ # @!method title
23
+ # Get a "pretty" representation of this period.
24
+ # @return [String]
25
+
26
+ # @!method to_s
27
+ # Get a stringified representation of this period.
28
+ # @return [String]
29
+
9
30
  %i[name title to_s].each do |message|
10
31
  define_method(message) do
11
32
  text = self.begin.send(message)
@@ -14,7 +35,7 @@ module TimeBoss
14
35
  end
15
36
  end
16
37
 
17
- %w[week month quarter half year].each do |size|
38
+ %w[day week month quarter half year].each do |size|
18
39
  define_method(size.pluralize) do
19
40
  entry = calendar.send("#{size}_for", self.begin.start_date)
20
41
  build_entries entry
@@ -27,20 +48,14 @@ module TimeBoss
27
48
  end
28
49
  end
29
50
 
51
+ # Does this period cover the current date?
52
+ # @return [Boolean]
30
53
  def current?
31
54
  to_range.include?(Date.today)
32
55
  end
33
56
 
34
- def days
35
- to_range.map { |d| Day.new(calendar, d) }
36
- end
37
-
38
- def day
39
- entries = days
40
- return nil unless entries.length == 1
41
- entries.first
42
- end
43
-
57
+ # Express this period as a date range.
58
+ # @return [Range<Date, Date>]
44
59
  def to_range
45
60
  @_to_range ||= start_date .. end_date
46
61
  end
@@ -1,15 +1,20 @@
1
1
  # frozen_string_literal: true
2
- require_relative './support/month_based'
2
+ require_relative './support/monthly_unit'
3
3
 
4
4
  module TimeBoss
5
5
  class Calendar
6
- class Quarter < Support::MonthBased
6
+ # Representation of a 3-month period within a calendar.
7
+ class Quarter < Support::MonthlyUnit
7
8
  NUM_MONTHS = 3
8
9
 
10
+ # Get a simple representation of this quarter.
11
+ # @return [String] (e.g. "2020Q3")
9
12
  def name
10
13
  "#{year_index}Q#{index}"
11
14
  end
12
15
 
16
+ # Get a "pretty" representation of this quarter.
17
+ # @return [String] (e.g. "Q3 2020")
13
18
  def title
14
19
  "Q#{index} #{year_index}"
15
20
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ module TimeBoss
3
+ module Calendar
4
+ # Provides modules and abstract base classes to facilitate calendar implementation.
5
+ module Support
6
+ end
7
+ end
8
+ end
@@ -6,6 +6,7 @@ module TimeBoss
6
6
  module Support
7
7
  private
8
8
 
9
+ # The formatter is responsible for the implementation of name formatting for a unit.
9
10
  class Formatter
10
11
  PERIODS = Shiftable::PERIODS.reverse.map(&:to_sym).drop(1)
11
12
  attr_reader :unit, :periods
@@ -1,6 +1,9 @@
1
1
  module TimeBoss
2
2
  class Calendar
3
3
  module Support
4
+ # @abstract
5
+ # Implementation of a month basis allows a custom calendar to be built.
6
+ # A month basis must return a start/end date for a given year and month index.
4
7
  class MonthBasis
5
8
  attr_reader :year, :month
6
9
 
@@ -4,7 +4,9 @@ require_relative './unit'
4
4
  module TimeBoss
5
5
  class Calendar
6
6
  module Support
7
- class MonthBased < Unit
7
+ # Units that are built off of month-granularities (months, quarters, etc).
8
+ # Days and weeks are not built from these.
9
+ class MonthlyUnit < Unit
8
10
  attr_reader :year_index, :index
9
11
 
10
12
  def initialize(calendar, year_index, index, start_date, end_date)
@@ -13,30 +15,18 @@ module TimeBoss
13
15
  @index = index
14
16
  end
15
17
 
16
- def next
17
- if index == max_index
18
- calendar.send(self.class.type, year_index + 1, 1)
19
- else
20
- calendar.send(self.class.type, year_index, index + 1)
21
- end
22
- end
23
-
24
- def previous
25
- if index == 1
26
- calendar.send(self.class.type, year_index - 1, max_index)
27
- else
28
- calendar.send(self.class.type, year_index, index - 1)
29
- end
30
- end
31
-
18
+ # Get a stringified representation of this unit.
19
+ # @return [String] (e.g. "2020Q3: 2020-06-29 thru 2020-09-27")
32
20
  def to_s
33
21
  "#{name}: #{start_date} thru #{end_date}"
34
22
  end
35
23
 
24
+ # Get a list of weeks contained within this period.
25
+ # @return [Array<Week>]
36
26
  def weeks
37
27
  base = calendar.year(year_index)
38
28
  num_weeks = (((base.end_date - base.start_date) + 1) / 7.0).to_i
39
- num_weeks.times.map { |i| Week.new(calendar, year_index, i + 1, base.start_date + (i * 7).days, base.start_date + ((i * 7) + 6).days) }
29
+ num_weeks.times.map { |i| Week.new(calendar, base.start_date + (i * 7).days, base.start_date + ((i * 7) + 6).days) }
40
30
  .select { |w| w.start_date.between?(start_date, end_date) }
41
31
  end
42
32
 
@@ -45,6 +35,22 @@ module TimeBoss
45
35
  def max_index
46
36
  12 / self.class::NUM_MONTHS
47
37
  end
38
+
39
+ def up
40
+ if index == max_index
41
+ calendar.send(self.class.type, year_index + 1, 1)
42
+ else
43
+ calendar.send(self.class.type, year_index, index + 1)
44
+ end
45
+ end
46
+
47
+ def down
48
+ if index == 1
49
+ calendar.send(self.class.type, year_index - 1, max_index)
50
+ else
51
+ calendar.send(self.class.type, year_index, index - 1)
52
+ end
53
+ end
48
54
  end
49
55
  end
50
56
  end