sof-cycle 0.1.7 → 0.1.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3012f6707b4790074bd8d39619c93abbca8ffd333c4942365c1b995c321040a
4
- data.tar.gz: a9b8032594afa34f6aada7bbd8709cf05898dfb68bdd4f45f3a7e64270853054
3
+ metadata.gz: bcb187376738c3b567bf2bc9fc68bba6f1cb7e766302a897ab272db848b1b081
4
+ data.tar.gz: d841689c22f68f7f273d1791feb7753826c3b5c2235deb467fad88ee163ec18f
5
5
  SHA512:
6
- metadata.gz: 97f6a123c55cc133347ee5cad99639f7105a19397b7a8bfeaad0f72ac9ef0da0748a51bc40a3976816f347e4075463bc18ee9184bfe6837308d2dcc438bd72dc
7
- data.tar.gz: 6fa462d207af7d7fc7e659a5e586ad19d5cec3a6042814ba89254974b566054f27ee6dd2efbb661c2804deef174a99c6f3ae5e26e273d57edf7a0f0f0e8a2d4e
6
+ metadata.gz: f193e2a83e149315956c99df5efebabfd05c9ade453bb4e4763db1a39870c62c63bae5e6c82379bddd1f07a85a1a6f898fb90d53a753f0936f146efdf6e5f786
7
+ data.tar.gz: 7000bd01fd3c3302756b757b406f36ec75db9125a3acf597e5c6fb54ab811356e076b4d45087098db94ffb4d4f0151b33d8bd4ce3c1ebe4e4844ae20029aec66
@@ -0,0 +1,17 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(find:*)",
5
+ "Bash(ls:*)",
6
+ "Bash(mkdir:*)",
7
+ "Bash(touch:*)",
8
+ "Bash(rm:*)",
9
+ "Bash(bundle exec rspec:*)",
10
+ "Bash(bundle exec rake:*)",
11
+ "Bash(bundle exec standardrb:*)",
12
+ "Bash(git checkout:*)",
13
+ "Bash(bundle exec ruby:*)"
14
+ ],
15
+ "deny": []
16
+ }
17
+ }
data/.simplecov CHANGED
@@ -1,4 +1,10 @@
1
1
  require "simplecov"
2
+ require "simplecov_json_formatter"
3
+
2
4
  SimpleCov.start do
5
+ formatter SimpleCov::Formatter::MultiFormatter.new([
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ SimpleCov::Formatter::JSONFormatter
8
+ ])
3
9
  add_filter "/spec/"
4
10
  end
data/CHANGELOG.md CHANGED
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.8] - 2025-09-04
9
+
10
+ ### Changed
11
+
12
+ - Simplified `handles?` logic to do string comparison instead of symbol comparison.
13
+
8
14
  ## [0.1.7] - 2025-06-09
9
15
 
10
16
  ### Added
@@ -16,9 +22,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
22
 
17
23
  - `Cycles::Lookback.volume_to_delay_expiration` now computes correctly when the
18
24
  `#considered_dates` is smaller than the `#covered_dates` of the cycle.
19
-
20
- ## [0.1.6] - 2024-09-04
21
-
22
- ### Added
23
-
24
- - `Cycle.extend_period(count)` to get a new cycle with the modified period count
data/CLAUDE.md ADDED
@@ -0,0 +1,34 @@
1
+ # CLAUDE.md
2
+
3
+ ## Agent OS Documentation
4
+
5
+ ### Product Context
6
+ - **Mission & Vision:** @.agent-os/product/mission.md
7
+ - **Technical Architecture:** @.agent-os/product/tech-stack.md
8
+ - **Development Roadmap:** @.agent-os/product/roadmap.md
9
+ - **Decision History:** @.agent-os/product/decisions.md
10
+
11
+ ### Development Standards
12
+ - **Code Style:** @~/.agent-os/standards/code-style.md
13
+ - **Best Practices:** @~/.agent-os/standards/best-practices.md
14
+
15
+ ### Project Management
16
+ - **Active Specs:** @.agent-os/specs/
17
+ - **Spec Planning:** Use `@~/.agent-os/instructions/create-spec.md`
18
+ - **Tasks Execution:** Use `@~/.agent-os/instructions/execute-tasks.md`
19
+
20
+ ## Workflow Instructions
21
+
22
+ When asked to work on this codebase:
23
+
24
+ 1. **First**, check @.agent-os/product/roadmap.md for current priorities
25
+ 2. **Then**, follow the appropriate instruction file:
26
+ - For new features: @~/.agent-os/instructions/create-spec.md
27
+ - For tasks execution: @~/.agent-os/instructions/execute-tasks.md
28
+ 3. **Always**, adhere to the standards in the files listed above
29
+
30
+ ## Important Notes
31
+
32
+ - Product-specific files in `.agent-os/product/` override any global standards
33
+ - User's specific instructions override (or amend) instructions found in `.agent-os/specs/...`
34
+ - Always adhere to established patterns, code style, and best practices documented above.
@@ -0,0 +1 @@
1
+ 10f35795f9559afbf1b87c26b76c5458fbdca373705539bfb55e3702c372b9198dba2cf1986489c5a95b55359dcdcfe39e27b10564e6570a94552ab95f31de7f
@@ -2,6 +2,6 @@
2
2
 
3
3
  module SOF
4
4
  class Cycle
5
- VERSION = "0.1.7"
5
+ VERSION = "0.1.8"
6
6
  end
7
7
  end
data/lib/sof/cycle.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
3
4
  require_relative "parser"
4
5
 
5
6
  module SOF
6
7
  class Cycle
7
- extend Forwardable
8
+ extend ::Forwardable
8
9
  class InvalidInput < StandardError; end
9
10
 
10
11
  class InvalidPeriod < InvalidInput; end
@@ -99,12 +100,26 @@ module SOF
99
100
  end || raise(InvalidKind, "':#{sym}' is not a valid kind of Cycle")
100
101
  end
101
102
 
102
- def cycle_handlers = @cycle_handlers ||= Set.new
103
-
104
- def inherited(klass) = cycle_handlers << klass
105
-
106
- def handles?(sym)
107
- sym && kind == sym.to_sym
103
+ # Return a legend explaining all notation components
104
+ #
105
+ # @return [Hash] hash with notation components organized by category
106
+ def legend
107
+ {
108
+ "quantity" => {
109
+ "V" => {
110
+ description: "Volume - the number of times something should occur",
111
+ examples: ["V1L1D - once in the prior 1 day", "V3L3D - three times in the prior 3 days", "V10L10D - ten times in the prior 10 days"]
112
+ }
113
+ },
114
+ "kind" => build_kind_legend,
115
+ "period" => build_period_legend,
116
+ "date" => {
117
+ "F" => {
118
+ description: "From - specifies the anchor date for Within cycles",
119
+ examples: ["F2024-01-01 - from January 1, 2024", "F2024-12-31 - from December 31, 2024"]
120
+ }
121
+ }
122
+ }
108
123
  end
109
124
 
110
125
  @volume_only = false
@@ -127,6 +142,65 @@ module SOF
127
142
  #{valid_periods.join(", ")}
128
143
  ERR
129
144
  end
145
+
146
+ def handles?(sym)
147
+ kind.to_s == sym.to_s
148
+ end
149
+
150
+ def cycle_handlers
151
+ @cycle_handlers ||= Set.new
152
+ end
153
+
154
+ def inherited(klass)
155
+ cycle_handlers << klass
156
+ end
157
+
158
+ private
159
+
160
+ def build_kind_legend
161
+ legend = {}
162
+ cycle_handlers.each do |handler|
163
+ # Skip volume_only since it doesn't have a notation_id
164
+ next if handler.instance_variable_get(:@volume_only)
165
+
166
+ notation_id = handler.instance_variable_get(:@notation_id)
167
+ next unless notation_id
168
+
169
+ legend[notation_id] = {
170
+ description: handler.description,
171
+ examples: handler.examples
172
+ }
173
+ end
174
+ legend
175
+ end
176
+
177
+ def build_period_legend
178
+ legend = {}
179
+ # Use known period codes since DatePeriod is private
180
+ period_mappings = {
181
+ "D" => "day",
182
+ "W" => "week",
183
+ "M" => "month",
184
+ "Q" => "quarter",
185
+ "Y" => "year"
186
+ }
187
+
188
+ period_mappings.each do |code, period_name|
189
+ legend[code] = {
190
+ description: "#{period_name.capitalize} - period notation",
191
+ examples: period_examples_for(code, period_name)
192
+ }
193
+ end
194
+ legend
195
+ end
196
+
197
+ def period_examples_for(code, period_name)
198
+ base_example = (code == "D") ? "3#{code} - 3 #{period_name}s" : "2#{code} - 2 #{period_name}s"
199
+ lookback_example = "L#{(code == "D") ? "7" : "4"}#{code} - in the prior #{(code == "D") ? "7" : "4"} #{period_name}s"
200
+ calendar_example = "C1#{code} - this calendar #{period_name}"
201
+
202
+ [base_example, lookback_example, calendar_example]
203
+ end
130
204
  end
131
205
 
132
206
  def initialize(notation, parser: Parser.new(notation))
@@ -176,7 +250,7 @@ module SOF
176
250
  end
177
251
 
178
252
  def considered_dates(completion_dates, anchor: Date.current)
179
- covered_dates(completion_dates, anchor:).max_by(volume) { _1 }
253
+ covered_dates(completion_dates, anchor:).max_by(volume) { it }
180
254
  end
181
255
 
182
256
  def covered_dates(dates, anchor: Date.current)
@@ -10,6 +10,14 @@ module SOF
10
10
 
11
11
  class << self
12
12
  def frame_of_reference = "total"
13
+
14
+ def description
15
+ "Calendar - occurrences within the current calendar period"
16
+ end
17
+
18
+ def examples
19
+ ["V2C1M - twice this calendar month", "V4C1Y - 4 times this calendar year"]
20
+ end
13
21
  end
14
22
 
15
23
  def self.recurring? = true
@@ -22,7 +30,7 @@ module SOF
22
30
  # @return [Date, nil] the date on which the cycle will expire given the
23
31
  # provided completion dates. Returns nil if the cycle is already unsatisfied.
24
32
  def expiration_of(completion_dates)
25
- anchor = completion_dates.max_by(volume) { _1 }.min
33
+ anchor = completion_dates.max_by(volume) { it }.min
26
34
  return unless satisfied_by?(completion_dates, anchor:)
27
35
 
28
36
  window_end(anchor) + duration
@@ -18,6 +18,14 @@ module SOF
18
18
 
19
19
  def self.recurring? = true
20
20
 
21
+ def self.description
22
+ "End of - occurrences by the end of a time period"
23
+ end
24
+
25
+ def self.examples
26
+ ["V1E1M - once by the end of next month", "V2E2Q - twice by the end of 2 quarters"]
27
+ end
28
+
21
29
  def to_s
22
30
  return dormant_to_s if dormant?
23
31
 
@@ -10,6 +10,14 @@ module SOF
10
10
 
11
11
  def self.recurring? = true
12
12
 
13
+ def self.description
14
+ "Lookback - occurrences within a prior time period counting backwards from today"
15
+ end
16
+
17
+ def self.examples
18
+ ["V3L3D - 3 times in the prior 3 days", "V1L2W - once in the prior 2 weeks"]
19
+ end
20
+
13
21
  def to_s = "#{volume}x in the prior #{period_count} #{humanized_period}"
14
22
 
15
23
  def volume_to_delay_expiration(completion_dates, anchor:)
@@ -25,7 +33,7 @@ module SOF
25
33
  # @return [Date, nil] the date on which the cycle will expire given the
26
34
  # provided completion dates. Returns nil if the cycle is already unsatisfied.
27
35
  def expiration_of(completion_dates)
28
- anchor = completion_dates.max_by(volume) { _1 }.min
36
+ anchor = completion_dates.max_by(volume) { it }.min
29
37
  return unless satisfied_by?(completion_dates, anchor:)
30
38
 
31
39
  window_end anchor
@@ -9,7 +9,7 @@ module SOF
9
9
  @valid_periods = []
10
10
 
11
11
  class << self
12
- def handles?(sym) = sym.nil? || super
12
+ def handles?(sym) = sym.nil? || sym.to_s == "volume_only"
13
13
 
14
14
  def validate_period(period)
15
15
  raise InvalidPeriod, <<~ERR.squish unless period.nil?
@@ -10,6 +10,14 @@ module SOF
10
10
 
11
11
  def self.recurring? = false
12
12
 
13
+ def self.description
14
+ "Within - occurrences within a time period from a specific date"
15
+ end
16
+
17
+ def self.examples
18
+ ["V2W3DF2024-01-01 - twice within 3 days from Jan 1, 2024"]
19
+ end
20
+
13
21
  def to_s = "#{volume}x within #{date_range}"
14
22
 
15
23
  def extend_period(count)
@@ -23,7 +31,7 @@ module SOF
23
31
  def date_range
24
32
  return humanized_span unless active?
25
33
 
26
- [start_date, final_date].map { _1.to_fs(:american) }.join(" - ")
34
+ [start_date, final_date].map { it.to_fs(:american) }.join(" - ")
27
35
  end
28
36
 
29
37
  def final_date(_ = nil) = time_span.end_date(start_date)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sof-cycle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Gay
@@ -43,15 +43,18 @@ executables: []
43
43
  extensions: []
44
44
  extra_rdoc_files: []
45
45
  files:
46
+ - ".claude/settings.local.json"
46
47
  - ".rspec"
47
48
  - ".simplecov"
48
49
  - CHANGELOG.md
50
+ - CLAUDE.md
49
51
  - README.md
50
52
  - Rakefile
51
53
  - checksums/sof-cycle-0.1.0.gem.sha512
52
54
  - checksums/sof-cycle-0.1.1.gem.sha512
53
55
  - checksums/sof-cycle-0.1.2.gem.sha512
54
56
  - checksums/sof-cycle-0.1.6.gem.sha512
57
+ - checksums/sof-cycle-0.1.7.gem.sha512
55
58
  - lib/sof-cycle.rb
56
59
  - lib/sof/cycle.rb
57
60
  - lib/sof/cycle/version.rb