fasti 1.0.0 → 1.0.1

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.
data/RELEASING.md DELETED
@@ -1,202 +0,0 @@
1
- # Releasing
2
-
3
- This document describes the release process for Ruby gems used in this project, which is fully automated through GitHub Actions workflows.
4
-
5
- ## Overview
6
-
7
- The release process consists of three automated workflows:
8
-
9
- 1. **Release Preparation** - Creates a release branch with version updates
10
- 2. **Release Validation** - Validates the release branch on PR creation
11
- 3. **Release Publish** - Publishes the gem after PR merge
12
-
13
- ## Prerequisites
14
-
15
- Before initiating a release, ensure:
16
-
17
- - [ ] All desired features and fixes are merged to `main`
18
- - [ ] CI is passing on `main` branch
19
- - [ ] `CHANGELOG.md` has entries under `## [Unreleased]` section
20
- - [ ] `RUBYGEMS_API_KEY` secret is configured in repository settings
21
-
22
- ## Release Process
23
-
24
- ### Step 1: Initiate Release
25
-
26
- 1. Go to the [Actions tab](../../actions) in the GitHub repository
27
- 2. Select "Release Preparation" workflow
28
- 3. Click "Run workflow"
29
- 4. Enter the version number (e.g., `1.0.0`)
30
- 5. Click "Run workflow"
31
-
32
- The workflow will automatically:
33
- - Create a new branch `release-v{version}`
34
- - Update `lib/[gem_name]/version.rb` with the new version
35
- - Update `CHANGELOG.md`:
36
- - Replace `## [Unreleased]` with `## [{version}] - {date}`
37
- - Add a new `## [Unreleased]` section for future changes
38
- - Create a git tag `v{version}` on the release branch
39
- - Push the branch and tag to GitHub
40
- - Create a Pull Request to `main`
41
-
42
- ### Step 2: Review and Merge
43
-
44
- Once the PR is created, the Release Validation workflow automatically:
45
- - Validates version format (must be `x.y.z`)
46
- - Verifies version consistency between branch name and `version.rb`
47
- - Checks that the git tag doesn't already exist (or can be updated)
48
- - Confirms the version isn't already published on RubyGems
49
- - Verifies `RUBYGEMS_API_KEY` secret is configured
50
- - Validates `CHANGELOG.md` has an entry for this version
51
-
52
- **Note**: Quality checks (tests and RuboCop) are handled by the CI workflow to avoid duplication.
53
-
54
- **Important**: If you push additional commits to the release PR (e.g., bug fixes, workflow updates, documentation changes):
55
- - The validation workflow automatically moves the release tag to the latest commit
56
- - This ensures the tag always points to the final reviewed code
57
- - No manual intervention required
58
- - All changes will be included in the final release
59
-
60
- If all checks pass, merge the PR.
61
-
62
- ### Step 3: Automatic Publishing
63
-
64
- After the PR is merged, the Release Publish workflow automatically:
65
- - Checks out the exact git tag created on the release branch
66
- - Builds the gem from the tagged commit
67
- - Publishes the gem to RubyGems
68
- - Creates a GitHub Release with:
69
- - Release notes extracted from `CHANGELOG.md`
70
- - The built gem file as an attachment
71
- - Cleans up the release branch
72
-
73
- ## Workflow Architecture
74
-
75
- ### Key Design Decisions
76
-
77
- #### Tag-Based Deployment
78
-
79
- The release workflow uses a **tag-based deployment strategy** to ensure the released gem contains exactly the code that was reviewed and approved in the release PR, without any subsequent changes from `main`.
80
-
81
- ```mermaid
82
- graph LR
83
- A[main branch] -->|Create release branch| B[release-v1.0.0]
84
- B -->|Update version & tag| C[Tagged: v1.0.0]
85
- B -->|Create PR| D[Pull Request]
86
- A -->|Other PRs merged| E[main with new commits]
87
- D -->|Merge PR| E
88
- C -->|Checkout tag| F[Build & Publish gem]
89
- F -->|Contains only| G[Code from release branch]
90
- ```
91
-
92
- #### Automatic Tag Movement
93
-
94
- When additional commits are pushed to a release PR, the tag automatically moves to the latest commit:
95
-
96
- ```mermaid
97
- graph LR
98
- A[Initial commit] -->|Tag v1.0.0| B[Tagged commit]
99
- B -->|Fix typo| C[New commit]
100
- C -->|Auto-move tag| D[Tag v1.0.0 on latest]
101
- D -->|Final review| E[Ready to merge]
102
- ```
103
-
104
- This ensures:
105
- - The tag always points to the final reviewed code
106
- - No manual tag management required
107
- - The published gem matches exactly what was approved
108
-
109
- ## Manual Release (Emergency Only)
110
-
111
- If automation fails, you can release manually using our tag-based strategy:
112
-
113
- ```bash
114
- # 1. Create release branch
115
- git checkout -b release-v1.0.0
116
-
117
- # 2. Update version
118
- vim lib/[gem_name]/version.rb
119
-
120
- # 3. Update CHANGELOG (move content from [Unreleased] to [1.0.0])
121
- vim CHANGELOG.md
122
-
123
- # 4. Commit changes and create tag
124
- git add -A
125
- git commit -m ":bookmark: Release v1.0.0"
126
- git tag -a v1.0.0 -m "Release v1.0.0"
127
-
128
- # 5. Push release branch and tag
129
- git push origin release-v1.0.0
130
- git push origin v1.0.0
131
-
132
- # 6. Create PR and merge after review
133
- gh pr create --title "Release v1.0.0" --body "Release v1.0.0"
134
- # (Review and merge PR)
135
-
136
- # 7. Checkout the tag and build gem
137
- git checkout v1.0.0
138
- bundle exec rake build
139
-
140
- # 8. Push to RubyGems
141
- gem push pkg/[gem_name]-1.0.0.gem
142
-
143
- # 9. Create GitHub release
144
- gh release create v1.0.0 --title "[gem_name] v1.0.0" --generate-notes pkg/[gem_name]-1.0.0.gem
145
- ```
146
-
147
- ## Troubleshooting
148
-
149
- ### Release Validation Fails
150
-
151
- **Version already exists on RubyGems**
152
- - Solution: Increment the version number and try again
153
-
154
- **RUBYGEMS_API_KEY not configured**
155
- 1. Generate an API key at https://rubygems.org/profile/edit
156
- 2. Go to repository Settings → Secrets and variables → Actions
157
- 3. Add new secret: `RUBYGEMS_API_KEY` with your API key
158
-
159
- **Tests or RuboCop failing**
160
- - These are checked by the CI workflow, not in release validation
161
- - Fix the issues on the release branch or `main` as appropriate
162
- - Release validation focuses on version consistency and format
163
-
164
- ### Release Publish Fails
165
-
166
- **Tag not found**
167
- - The release preparation workflow may have failed to create the tag
168
- - Check if the tag exists: `git tag | grep v1.0.0`
169
- - If missing, the release validation workflow will recreate it on PR updates
170
- - For manual fix: create tag on release branch with `git tag -a v1.0.0 -m "Release v1.0.0"`
171
-
172
- **Gem push fails**
173
- - Verify RubyGems API key is valid
174
- - Check if you have push permissions for the gem
175
- - Ensure the version doesn't already exist on RubyGems
176
-
177
- ## Version Numbering
178
-
179
- Follow [Semantic Versioning](https://semver.org/):
180
-
181
- - **MAJOR** version for incompatible API changes
182
- - **MINOR** version for backwards-compatible functionality additions
183
- - **PATCH** version for backwards-compatible bug fixes
184
-
185
- Examples:
186
- - `0.1.0` → `0.1.1`: Bug fixes only
187
- - `0.1.1` → `0.2.0`: New features added
188
- - `0.2.0` → `1.0.0`: Breaking changes or stable release
189
-
190
- ## Workflow Files
191
-
192
- The release automation is implemented in:
193
-
194
- - `.github/workflows/release-preparation.yml` - Creates release branch and PR
195
- - `.github/workflows/release-validation.yml` - Validates release PR
196
- - `.github/workflows/release-publish.yml` - Publishes gem after merge
197
-
198
- ## Security Notes
199
-
200
- - The `RUBYGEMS_API_KEY` secret is only accessible to workflows running on the default branch
201
- - Release branches are automatically deleted after successful release
202
- - All releases are tagged for audit trail and rollback capability
data/Rakefile DELETED
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
-
5
- require "rake/clean"
6
- CLEAN.include("coverage", ".rspec_status", ".yardoc")
7
- CLOBBER.include("docs/api", "pkg")
8
-
9
- require "rspec/core/rake_task"
10
- RSpec::Core::RakeTask.new(:spec)
11
-
12
- require "rubocop/rake_task"
13
- RuboCop::RakeTask.new
14
-
15
- require "gemoji"
16
- require "yard"
17
- YARD::Rake::YardocTask.new(:doc)
18
- Rake::Task[:doc].enhance do
19
- # Convert GitHub emoji shortcodes to actual emojis in generated HTML
20
- Dir["docs/api/**/*.html"].each do |file|
21
- content = File.read(file)
22
-
23
- # Replace :emoji_name: patterns with actual unicode emojis
24
- content.gsub!(/:([a-z0-9+\-_]+):/) do |match|
25
- alias_name = $1
26
- emoji = Emoji.find_by_alias(alias_name)
27
- emoji ? emoji.raw : match # Keep original if emoji not found
28
- end
29
-
30
- File.write(file, content)
31
- end
32
- end
33
-
34
- task default: %i[spec rubocop]
data/TODO.md DELETED
@@ -1,11 +0,0 @@
1
- # TODO
2
-
3
- **Tasks were previously managed in this file, but have been migrated to GitHub Issues for better tracking and collaboration.**
4
-
5
- ## Quick Links
6
- - [Open Issues](https://github.com/sakuro/fasti/issues)
7
- - [Current Milestone](https://github.com/sakuro/fasti/milestones)
8
- - [All Milestones](https://github.com/sakuro/fasti/milestones?state=closed)
9
- - [Project Board](https://github.com/sakuro/fasti/projects) (planned)
10
-
11
- For ongoing maintenance guidelines, see the [AI Development Guide](CLAUDE.md).
@@ -1,111 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "benchmark"
5
- require "bundler/setup"
6
- require "fasti"
7
-
8
- # Temporary class to simulate old behavior without caching
9
- class LegacyCalendar < Fasti::Calendar
10
- # Override to disable caching for benchmark comparison
11
- def holiday?(day)
12
- date = to_date(day)
13
- return false unless date
14
-
15
- begin
16
- Holidays.on(date, country).any?
17
- rescue Holidays::InvalidRegion, StandardError
18
- false
19
- end
20
- end
21
- end
22
-
23
- # Benchmarks month display performance by checking holidays for all days in a month
24
- def benchmark_month_display(calendar_class, year, month, country, iterations=100)
25
- calendar = calendar_class.new(year, month, country:)
26
-
27
- Benchmark.realtime do
28
- iterations.times do
29
- # Simulate checking all days in the month (like formatter does)
30
- (1..calendar.days_in_month).each do |day|
31
- calendar.holiday?(day)
32
- end
33
- end
34
- end
35
- end
36
-
37
- # Benchmarks year display performance by checking holidays for all days in all months
38
- def benchmark_year_display(calendar_class, year, country, iterations=10)
39
- Benchmark.realtime do
40
- iterations.times do
41
- (1..12).each do |month|
42
- calendar = calendar_class.new(year, month, country:)
43
- (1..calendar.days_in_month).each do |day|
44
- calendar.holiday?(day)
45
- end
46
- end
47
- end
48
- end
49
- end
50
-
51
- # Formats benchmark time results in appropriate units (μs/ms/s)
52
- def format_time(seconds)
53
- if seconds < 0.001
54
- "#{(seconds * 1_000_000).round(2)}μs"
55
- elsif seconds < 1
56
- "#{(seconds * 1000).round(2)}ms"
57
- else
58
- "#{seconds.round(3)}s"
59
- end
60
- end
61
-
62
- # Calculates and formats performance improvement ratio between two benchmark times
63
- def format_speedup(old_time, new_time)
64
- return "N/A" if new_time.zero?
65
-
66
- speedup = old_time / new_time
67
- "#{speedup.round(2)}x faster"
68
- end
69
-
70
- puts "Fasti Holiday Cache Performance Benchmark"
71
- puts "=" * 50
72
- puts
73
-
74
- # Test parameters
75
- YEAR = 2024
76
- COUNTRIES = %i[us jp gb].freeze
77
- MONTH_ITERATIONS = 100
78
- YEAR_ITERATIONS = 10
79
-
80
- COUNTRIES.each do |country|
81
- puts "Country: #{country.upcase}"
82
- puts "-" * 20
83
-
84
- # Month display benchmark
85
- puts "Month Display (July #{YEAR}, #{MONTH_ITERATIONS} iterations):"
86
-
87
- legacy_month_time = benchmark_month_display(LegacyCalendar, YEAR, 7, country, MONTH_ITERATIONS)
88
- cached_month_time = benchmark_month_display(Fasti::Calendar, YEAR, 7, country, MONTH_ITERATIONS)
89
-
90
- puts " Legacy (no cache): #{format_time(legacy_month_time)}"
91
- puts " Cached: #{format_time(cached_month_time)}"
92
- puts " Improvement: #{format_speedup(legacy_month_time, cached_month_time)}"
93
- puts
94
-
95
- # Year display benchmark
96
- puts "Year Display (#{YEAR}, #{YEAR_ITERATIONS} iterations):"
97
-
98
- legacy_year_time = benchmark_year_display(LegacyCalendar, YEAR, country, YEAR_ITERATIONS)
99
- cached_year_time = benchmark_year_display(Fasti::Calendar, YEAR, country, YEAR_ITERATIONS)
100
-
101
- puts " Legacy (no cache): #{format_time(legacy_year_time)}"
102
- puts " Cached: #{format_time(cached_year_time)}"
103
- puts " Improvement: #{format_speedup(legacy_year_time, cached_year_time)}"
104
- puts
105
- puts
106
- end
107
-
108
- puts "Benchmark completed!"
109
- puts
110
- puts "Note: Results may vary based on network latency to holiday data sources"
111
- puts "and system performance. Run multiple times for consistent results."
@@ -1,86 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "fasti"
6
-
7
- # Simple memory measurement helper
8
- def measure_memory_usage
9
- # Force garbage collection to get accurate measurements
10
- GC.start
11
- GC.compact if GC.respond_to?(:compact)
12
-
13
- before = GC.stat[:heap_live_slots]
14
- yield
15
- GC.start
16
- after = GC.stat[:heap_live_slots]
17
-
18
- after - before
19
- end
20
-
21
- # Formats memory usage in human-readable units (slots/K slots/M slots)
22
- def format_memory(slots)
23
- # Rough estimation: each object slot ≈ 40 bytes on 64-bit systems
24
- bytes = slots * 40
25
- if bytes < 1024
26
- "#{bytes}B"
27
- elsif bytes < 1024 * 1024
28
- "#{(bytes / 1024.0).round(2)}KB"
29
- else
30
- "#{(bytes / (1024.0 * 1024)).round(2)}MB"
31
- end
32
- end
33
-
34
- puts "Fasti Memory Usage Benchmark"
35
- puts "=" * 40
36
- puts
37
-
38
- # Test creating calendars and checking holidays
39
- YEAR = 2024
40
- COUNTRIES = %i[us jp gb].freeze
41
-
42
- COUNTRIES.each do |country|
43
- puts "Country: #{country.upcase}"
44
- puts "-" * 15
45
-
46
- # Single month calendar
47
- month_memory = measure_memory_usage {
48
- calendar = Fasti::Calendar.new(YEAR, 7, country:)
49
- (1..calendar.days_in_month).each {|day| calendar.holiday?(day) }
50
- }
51
-
52
- # Year worth of calendars (simulating year view)
53
- year_memory = measure_memory_usage {
54
- (1..12).each do |month|
55
- calendar = Fasti::Calendar.new(YEAR, month, country:)
56
- (1..calendar.days_in_month).each {|day| calendar.holiday?(day) }
57
- end
58
- }
59
-
60
- puts " Single month: #{format_memory(month_memory)}"
61
- puts " Full year: #{format_memory(year_memory)}"
62
- puts
63
- end
64
-
65
- # Test cache behavior - multiple accesses to same month
66
- puts "Cache Efficiency Test"
67
- puts "-" * 20
68
-
69
- cache_memory = measure_memory_usage {
70
- calendar = Fasti::Calendar.new(YEAR, 7, country: :us)
71
-
72
- # First pass - cache gets populated
73
- (1..calendar.days_in_month).each {|day|
74
- calendar.holiday?(day)
75
- # Second pass - should use cache
76
- calendar.holiday?(day)
77
-
78
- # Third pass - still using cache
79
- calendar.holiday?(day)
80
- }
81
- }
82
-
83
- puts "Triple access (cache test): #{format_memory(cache_memory)}"
84
- puts
85
- puts "Note: Memory measurements are approximate and may vary"
86
- puts "based on Ruby version and system configuration."