human_number 0.1.1 → 0.1.3

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: 3464be66cdbc375a6612c468bc7aeffdff928b8bc1af7857d2f261440f4a3f7f
4
- data.tar.gz: 6ddf79c2ddd6cf5595b70a798cb035b730a7c5c6ba7c7e5d2dff726993d934c4
3
+ metadata.gz: a9d2eb9cac5e189aec1c4c4ad86e0c3a248f466bac8a1af782fc53ff918a43a9
4
+ data.tar.gz: f5e4e6a026ff264ab89583cbef216cdbdf872d967ad7b74859b7cd011c5f7373
5
5
  SHA512:
6
- metadata.gz: 178108029dc2a438d5a19dc5f66c9b2850d6dd593072c8ceade307549d58749cb809d73094306cd64b8bcfe870bfc3823a0c945d1454c8cc2aef4c703f2a34cd
7
- data.tar.gz: c562f9d5ea7abe1bff8b31b6889a5df768f755a62490e7ea2aa6bbaef312e08feb2e29ffc1aa61f9f01f2f35ca452104d5e46dc1ccd7487c12c439196f59f10b
6
+ metadata.gz: 32ada67f4efa6c18d358a327c18a913b7b18945db2f70f1ca35de0230250f089885e85425cb40547dfeb4aa16a4c0ddd5eb35ea8b65fb5b27919fb2dd5516e8b
7
+ data.tar.gz: d549da397b45f84dff15e15851ef54d1dfb241462ab041d1c0b384dc9ec5811eb744f0c54c28e7dd10087ceeade9b39346708b28cef5d4d09d0fb9e0594b97d8
data/.rubocop.yml CHANGED
@@ -60,4 +60,8 @@ Metrics/MethodLength:
60
60
  Metrics/ParameterLists:
61
61
  Max: 6
62
62
 
63
+ # Gemspec
64
+ Gemspec/RequireMFA:
65
+ Enabled: false
66
+
63
67
  # RSpec rules removed - using basic RuboCop only
data/BUILD.md ADDED
@@ -0,0 +1,246 @@
1
+ # Build and Publish Guide
2
+
3
+ This project provides three different ways to build and publish the gem, choose the one that best fits your workflow:
4
+
5
+ ## 🔧 Option 1: Rake Tasks (Recommended for Ruby developers)
6
+
7
+ The most Ruby-native approach using rake tasks.
8
+
9
+ ### Available Tasks
10
+ ```bash
11
+ # List all gem-related tasks
12
+ rake -T gem
13
+
14
+ # Run validations only
15
+ rake gem:validate
16
+
17
+ # Build gem with full validations
18
+ rake gem:build
19
+
20
+ # Quick build without validations (for testing)
21
+ rake gem:build_quick
22
+
23
+ # Publish to RubyGems with full validations
24
+ rake gem:publish
25
+
26
+ # Show gem information
27
+ rake gem:info
28
+
29
+ # Clean build artifacts
30
+ rake gem:clean
31
+
32
+ # Bump version interactively and commit
33
+ rake gem:bump
34
+
35
+ # Convenient aliases
36
+ rake build # Same as gem:build
37
+ rake release # Same as gem:publish
38
+ ```
39
+
40
+ ### Examples
41
+ ```bash
42
+ # Full build and publish workflow
43
+ rake gem:validate # Run tests, linting, git checks
44
+ rake gem:build # Build the gem
45
+ rake gem:publish # Publish to RubyGems
46
+
47
+ # Or use the all-in-one command
48
+ rake gem:publish # Does validation + build + publish
49
+
50
+ # Quick development testing
51
+ rake gem:build_quick # Build without validations
52
+ ```
53
+
54
+ ## 🐚 Option 2: Shell Script (Cross-platform compatible)
55
+
56
+ A bash script that works on any Unix-like system.
57
+
58
+ ### Available Commands
59
+ ```bash
60
+ # Show help
61
+ ./scripts/build_and_publish.sh help
62
+
63
+ # Run validations only
64
+ ./scripts/build_and_publish.sh validate
65
+
66
+ # Build gem with full validations
67
+ ./scripts/build_and_publish.sh build
68
+
69
+ # Quick build without validations (for testing)
70
+ ./scripts/build_and_publish.sh build-quick
71
+
72
+ # Publish to RubyGems with full validations
73
+ ./scripts/build_and_publish.sh publish
74
+
75
+ # Show gem information
76
+ ./scripts/build_and_publish.sh info
77
+
78
+ # Clean build artifacts
79
+ ./scripts/build_and_publish.sh clean
80
+
81
+ # Bump version interactively and commit
82
+ ./scripts/build_and_publish.sh bump
83
+ ```
84
+
85
+ ### Examples
86
+ ```bash
87
+ # Full build and publish workflow
88
+ ./scripts/build_and_publish.sh validate # Run all checks
89
+ ./scripts/build_and_publish.sh build # Build the gem
90
+ ./scripts/build_and_publish.sh publish # Publish to RubyGems
91
+
92
+ # Quick development testing
93
+ ./scripts/build_and_publish.sh build-quick
94
+ ```
95
+
96
+ ## ⚙️ Option 3: Makefile (Traditional build tool)
97
+
98
+ Standard make targets for those who prefer Makefiles.
99
+
100
+ ### Available Targets
101
+ ```bash
102
+ # Show help
103
+ make help
104
+
105
+ # Run individual validations
106
+ make test # Run tests only
107
+ make lint # Run RuboCop only
108
+ make validate # Run all validations
109
+
110
+ # Build gem with full validations
111
+ make build
112
+
113
+ # Quick build without validations (for testing)
114
+ make build-quick
115
+
116
+ # Publish to RubyGems with full validations
117
+ make publish
118
+
119
+ # Show gem information
120
+ make info
121
+
122
+ # Clean build artifacts
123
+ make clean
124
+
125
+ # Bump version interactively and commit
126
+ make bump
127
+
128
+ # Install dependencies
129
+ make install
130
+ ```
131
+
132
+ ### Examples
133
+ ```bash
134
+ # Full build and publish workflow
135
+ make validate # Run tests, linting, git checks
136
+ make build # Build the gem
137
+ make publish # Publish to RubyGems
138
+
139
+ # Quick development testing
140
+ make build-quick
141
+ ```
142
+
143
+ ## 🔍 What Each Validation Includes
144
+
145
+ All three build systems perform the same comprehensive validations:
146
+
147
+ ### ✅ Test Validation
148
+ - Runs the complete RSpec test suite
149
+ - Ensures 100% test coverage
150
+ - Validates all functionality works correctly
151
+
152
+ ### ✅ Code Quality Validation
153
+ - Runs RuboCop linting
154
+ - Enforces consistent code style
155
+ - Checks for potential issues
156
+
157
+ ### ✅ Git Validation
158
+ - Ensures working directory is clean (no uncommitted changes)
159
+ - Warns if not on `main` branch (with option to continue)
160
+ - Prevents accidental releases from dirty state
161
+
162
+ ### ✅ Build Validation
163
+ - Verifies gemspec is valid
164
+ - Checks all required files are included
165
+ - Ensures gem can be built successfully
166
+
167
+ ## 📦 Build Artifacts
168
+
169
+ All build methods create gems in the `pkg/` directory:
170
+ - `pkg/human_number-<version>.gem` - The built gem file
171
+
172
+ Use `make clean`, `rake gem:clean`, or `./scripts/build_and_publish.sh clean` to remove build artifacts.
173
+
174
+ ## 🚀 Publishing Process
175
+
176
+ When you run the publish command (any of the three options), it will:
177
+
178
+ 1. **Validate**: Run all tests, linting, and git checks
179
+ 2. **Build**: Create the gem file
180
+ 3. **Tag**: Create a git tag for the version
181
+ 4. **Push**: Push the tag to the remote repository
182
+ 5. **Publish**: Upload the gem to RubyGems.org
183
+
184
+ ## 🔖 Version Management
185
+
186
+ All three build systems include interactive version bumping functionality:
187
+
188
+ ### Version Bump Commands
189
+
190
+ | Tool | Command | Description |
191
+ |------|---------|-------------|
192
+ | **Rake** | `rake gem:bump` | Interactive version bump with commit |
193
+ | **Shell Script** | `./scripts/build_and_publish.sh bump` | Interactive version bump with commit |
194
+ | **Make** | `make bump` | Interactive version bump with commit |
195
+
196
+ ### Version Bump Process
197
+
198
+ When you run any version bump command, it will:
199
+
200
+ 1. **Display Current Version**: Shows the current version from `lib/human_number/version.rb`
201
+ 2. **Interactive Input**: Prompts you to enter the new version
202
+ 3. **Validation**: Ensures semantic versioning format (e.g., `1.2.3` or `1.2.3-alpha.1`)
203
+ 4. **Update File**: Modifies the version file with the new version
204
+ 5. **Git Commit**: Creates a commit with message `chore: bump version to X.Y.Z`
205
+ 6. **Next Steps**: Provides guidance on building and publishing
206
+
207
+ ### Example Workflow
208
+
209
+ ```bash
210
+ # Using rake (recommended for Ruby developers)
211
+ rake gem:bump
212
+ # Current version: 0.1.2
213
+ # Enter new version: 0.2.0
214
+ # ✅ Updated version to 0.2.0
215
+ # ✅ Version bump committed successfully!
216
+
217
+ # Build and publish
218
+ rake gem:publish
219
+ ```
220
+
221
+ ## ⚠️ Prerequisites
222
+
223
+ Before publishing:
224
+
225
+ 1. **RubyGems Account**: Ensure you have a RubyGems.org account
226
+ 2. **API Key**: Configure your RubyGems API key (`gem signin`)
227
+ 3. **Version Bump**: Use interactive version bump commands or manually update `lib/human_number/version.rb`
228
+ 4. **Clean Git**: Commit all changes before publishing (version bump commands handle this automatically)
229
+ 5. **Main Branch**: Recommended to publish from `main` branch
230
+
231
+ ## 🔧 Configuration Changes
232
+
233
+ **Important**: This setup has removed MFA requirement from the gemspec to simplify the publishing process. If you need MFA, you can re-enable it by adding this line to `human_number.gemspec`:
234
+
235
+ ```ruby
236
+ spec.metadata["rubygems_mfa_required"] = "true"
237
+ ```
238
+
239
+ ## 💡 Recommendations
240
+
241
+ - **For Ruby developers**: Use rake tasks (most native)
242
+ - **For CI/CD pipelines**: Use shell script (most portable)
243
+ - **For traditional workflows**: Use Makefile (universal)
244
+ - **For quick testing**: Use the `*-quick` variants to skip validations
245
+
246
+ Choose the tool that best fits your development workflow and team preferences!
data/CHANGELOG.md CHANGED
@@ -86,4 +86,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
86
86
  - **Formatters::Currency**: Currency symbol and format string application
87
87
  - **Clean separation**: Number formatting independent of currency concerns
88
88
 
89
- [0.1.0]: https://github.com/yourusername/human_number/releases/tag/v0.1.0
89
+ [0.1.0]: https://github.com/ether-moon/human_number/releases/tag/v0.1.0
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Your Name
3
+ Copyright (c) 2025 Ether Moon
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/Makefile ADDED
@@ -0,0 +1,142 @@
1
+ # HumanNumber Gem Build and Publish Makefile
2
+
3
+ .PHONY: help build publish build-quick validate clean info test lint bump
4
+
5
+ # Default target
6
+ help:
7
+ @echo "HumanNumber Gem Build and Publish Makefile"
8
+ @echo ""
9
+ @echo "Available targets:"
10
+ @echo " build Build gem with full validations (tests, linting, git checks)"
11
+ @echo " publish Publish gem to RubyGems with full validations"
12
+ @echo " build-quick Build gem without validations (for testing)"
13
+ @echo " validate Run all validations without building"
14
+ @echo " test Run tests only"
15
+ @echo " lint Run RuboCop only"
16
+ @echo " info Show gem and repository information"
17
+ @echo " clean Clean build artifacts"
18
+ @echo " bump Bump version interactively and commit"
19
+ @echo " help Show this help message"
20
+ @echo ""
21
+ @echo "Examples:"
22
+ @echo " make build # Build gem with validations"
23
+ @echo " make publish # Build and publish to RubyGems"
24
+ @echo " make bump # Bump version interactively"
25
+ @echo " make info # Show current gem info"
26
+
27
+ # Run tests
28
+ test:
29
+ @echo "📋 Running tests..."
30
+ @bundle exec rspec
31
+ @echo "✅ All tests passed"
32
+
33
+ # Run linting
34
+ lint:
35
+ @echo "🧹 Running RuboCop..."
36
+ @bundle exec rubocop
37
+ @echo "✅ RuboCop checks passed"
38
+
39
+ # Validate git status
40
+ validate-git:
41
+ @echo "📋 Checking git status..."
42
+ @if [ -n "$$(git status --porcelain)" ]; then \
43
+ echo "❌ Git working directory is not clean. Please commit all changes first."; \
44
+ exit 1; \
45
+ fi
46
+ @echo "✅ Git working directory is clean"
47
+ @current_branch=$$(git rev-parse --abbrev-ref HEAD); \
48
+ if [ "$$current_branch" != "main" ]; then \
49
+ echo "⚠️ Warning: You're on branch '$$current_branch', not 'main'"; \
50
+ read -p "Continue anyway? (y/N): " response; \
51
+ if [ "$$response" != "y" ] && [ "$$response" != "Y" ]; then \
52
+ echo "❌ Aborted by user"; \
53
+ exit 1; \
54
+ fi; \
55
+ fi
56
+ @echo "✅ Git branch check passed"
57
+
58
+ # Run all validations
59
+ validate: test lint validate-git
60
+ @echo "✅ All validations passed!"
61
+
62
+ # Build gem with validations
63
+ build: validate
64
+ @echo "🔨 Building gem..."
65
+ @bundle exec rake build
66
+ @echo "✅ Gem built successfully!"
67
+
68
+ # Build gem without validations (for testing)
69
+ build-quick:
70
+ @echo "🔨 Quick building gem (no validations)..."
71
+ @bundle exec rake build
72
+ @echo "✅ Gem built!"
73
+
74
+ # Publish gem to RubyGems
75
+ publish: validate
76
+ @echo "📦 Publishing gem to RubyGems..."
77
+ @bundle exec rake release
78
+ @echo "✅ Gem published successfully!"
79
+
80
+ # Show gem information
81
+ info:
82
+ @echo "📋 Gem Information:"
83
+ @echo " Name: human_number"
84
+ @echo " Version: $$(ruby -r ./lib/human_number/version -e 'puts HumanNumber::VERSION')"
85
+ @echo " Built gems: $$(ls pkg/*.gem 2>/dev/null || echo 'none')"
86
+ @echo " Git branch: $$(git rev-parse --abbrev-ref HEAD)"
87
+ @git_status=$$(git status --porcelain); \
88
+ if [ -z "$$git_status" ]; then \
89
+ echo " Git status: clean"; \
90
+ else \
91
+ echo " Git status: dirty"; \
92
+ fi
93
+
94
+ # Clean build artifacts
95
+ clean:
96
+ @echo "🧹 Cleaning build artifacts..."
97
+ @rm -rf pkg/
98
+ @echo "✅ Build artifacts cleaned!"
99
+
100
+ # Bump version interactively and commit
101
+ bump:
102
+ @echo "🔖 Version Bump"
103
+ @current_version=$$(ruby -r ./lib/human_number/version -e 'puts HumanNumber::VERSION'); \
104
+ echo "Current version: $$current_version"; \
105
+ echo ""; \
106
+ read -p "Enter new version: " new_version; \
107
+ if [ -z "$$new_version" ]; then \
108
+ echo "❌ No version entered. Aborting."; \
109
+ exit 1; \
110
+ fi; \
111
+ if ! echo "$$new_version" | grep -qE "^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$$"; then \
112
+ echo "❌ Invalid version format. Please use semantic versioning (e.g., 1.2.3 or 1.2.3-alpha.1)"; \
113
+ exit 1; \
114
+ fi; \
115
+ if [ "$$new_version" = "$$current_version" ]; then \
116
+ echo "❌ New version is the same as current version. Aborting."; \
117
+ exit 1; \
118
+ fi; \
119
+ version_file="lib/human_number/version.rb"; \
120
+ sed -i.bak "s/VERSION = \".*\"/VERSION = \"$$new_version\"/" "$$version_file" && rm "$$version_file.bak"; \
121
+ echo "✅ Updated version to $$new_version"; \
122
+ echo ""; \
123
+ echo "📝 Creating version bump commit..."; \
124
+ git add "$$version_file"; \
125
+ commit_message="chore: bump version to $$new_version"; \
126
+ if git commit -m "$$commit_message"; then \
127
+ echo "✅ Version bump committed successfully!"; \
128
+ echo ""; \
129
+ echo "📋 Next steps:"; \
130
+ echo " - Review the changes: git show"; \
131
+ echo " - Build and publish: make publish"; \
132
+ echo " - Push to remote: git push && git push --tags"; \
133
+ else \
134
+ echo "❌ Failed to create commit"; \
135
+ exit 1; \
136
+ fi
137
+
138
+ # Install dependencies
139
+ install:
140
+ @echo "📦 Installing dependencies..."
141
+ @bundle install
142
+ @echo "✅ Dependencies installed!"
data/README.md CHANGED
@@ -34,7 +34,7 @@ HumanNumber.human_number(50_000, locale: :ko) #=> "5만"
34
34
 
35
35
  # Currency formatting
36
36
  HumanNumber.currency(1234.56, currency_code: 'USD', locale: :en) #=> "$1,234.56"
37
- HumanNumber.human_currency(1_234_567, currency_code: 'USD', locale: :en) #=> "$1.2M"
37
+ HumanNumber.human_currency(1_234_567, currency_code: 'USD', locale: :en) #=> "$1M"
38
38
  ```
39
39
 
40
40
  ## Installation
@@ -71,17 +71,17 @@ HumanNumber.human_number(1_234_567) #=> "1.2M"
71
71
  HumanNumber.human_number(50_000, locale: :ko) #=> "5만"
72
72
  HumanNumber.human_number(50_000, locale: :ja) #=> "5万"
73
73
 
74
- # Precision control (default: max_digits: 2)
75
- HumanNumber.human_number(1_234_567, max_digits: 1) #=> "1M"
76
- HumanNumber.human_number(1_234_567, max_digits: 3) #=> "1.23M"
77
- HumanNumber.human_number(1_234_567, max_digits: nil) #=> "1M 234K 567"
74
+ # Significant digits control (default: max_digits: 2)
75
+ HumanNumber.human_number(1_234_567, max_digits: 1) #=> "1M" # 1 significant digit
76
+ HumanNumber.human_number(1_234_567, max_digits: 3) #=> "1.23M" # 3 significant digits
77
+ HumanNumber.human_number(1_234_567, max_digits: nil) #=> "1M 234K 567" # Complete breakdown
78
78
 
79
79
  # Unit preferences (Western locales only)
80
80
  HumanNumber.human_number(1_000_000, abbr_units: true) #=> "1M"
81
81
  HumanNumber.human_number(1_000_000, abbr_units: false) #=> "1 million"
82
82
 
83
83
  # Minimum thresholds
84
- HumanNumber.human_number(5_000, min_unit: 10_000) #=> "5000"
84
+ HumanNumber.human_number(5_000, min_unit: 10_000) #=> "5,000"
85
85
  HumanNumber.human_number(50_000, min_unit: 10_000) #=> "50K"
86
86
 
87
87
  # Zero trimming (default: true)
@@ -115,7 +115,7 @@ HumanNumber.currency(1234.56, currency_code: 'JPY', locale: :en) #=> "1,235円"
115
115
  Human-readable currency formatting with cultural abbreviations.
116
116
 
117
117
  ```ruby
118
- HumanNumber.human_currency(1_234_567, currency_code: 'USD', locale: :en) #=> "$1.2M"
118
+ HumanNumber.human_currency(1_234_567, currency_code: 'USD', locale: :en) #=> "$1M"
119
119
  HumanNumber.human_currency(50_000, currency_code: 'KRW', locale: :ko) #=> "5만원"
120
120
 
121
121
  # Combined options
@@ -129,7 +129,7 @@ In Rails applications, helper methods are automatically available:
129
129
 
130
130
  ```erb
131
131
  <%= human_number(1_234_567) %> <!-- 1.2M -->
132
- <%= human_currency(1_234_567, currency_code: 'USD') %> <!-- $1.2M -->
132
+ <%= human_currency(1_234_567, currency_code: 'USD') %> <!-- $1M -->
133
133
  <%= currency(1234.56, currency_code: 'USD') %> <!-- $1,234.56 -->
134
134
 
135
135
  <!-- With options -->
@@ -155,8 +155,8 @@ HumanNumber.human_number(1_000_000_000, locale: :en) #=> "1B"
155
155
  **Used by:** Korean (ko), Japanese (ja), Chinese (zh, zh-CN, zh-TW)
156
156
 
157
157
  ```ruby
158
- HumanNumber.human_number(1_234_567, locale: :ko) #=> "123만"
159
- HumanNumber.human_number(1_234_567, locale: :ja) #=> "123万"
158
+ HumanNumber.human_number(1_234_567, locale: :ko) #=> "120만"
159
+ HumanNumber.human_number(1_234_567, locale: :ja) #=> "120万"
160
160
  HumanNumber.human_number(100_000_000, locale: :ko) #=> "1억"
161
161
  ```
162
162
 
@@ -277,7 +277,7 @@ $ rake # Runs both spec and rubocop
277
277
 
278
278
  ## Contributing
279
279
 
280
- 1. Fork it (https://github.com/yourusername/human_number/fork)
280
+ 1. Fork it (https://github.com/ether-moon/human_number/fork)
281
281
  2. Create your feature branch (`git checkout -b feature/my-new-feature`)
282
282
  3. Commit your changes (`git commit -am 'Add some feature'`)
283
283
  4. Push to the branch (`git push origin feature/my-new-feature`)
@@ -320,4 +320,4 @@ See [CHANGELOG.md](CHANGELOG.md) for details.
320
320
 
321
321
  ## Issues
322
322
 
323
- Please report bugs and feature requests at [GitHub Issues](https://github.com/yourusername/human_number/issues).
323
+ Please report bugs and feature requests at [GitHub Issues](https://github.com/ether-moon/human_number/issues).
data/Rakefile CHANGED
@@ -10,3 +10,138 @@ require "rubocop/rake_task"
10
10
  RuboCop::RakeTask.new
11
11
 
12
12
  task default: %i[spec rubocop]
13
+
14
+ # Gem build and publish tasks
15
+ namespace :gem do
16
+ desc "Run all pre-publish validations"
17
+ task :validate do
18
+ puts "🔍 Running pre-publish validations..."
19
+
20
+ # Run tests
21
+ puts "\n📋 Running tests..."
22
+ Rake::Task[:spec].invoke
23
+
24
+ # Run linting
25
+ puts "\n🧹 Running RuboCop..."
26
+ Rake::Task[:rubocop].invoke
27
+
28
+ # Check git status
29
+ puts "\n📋 Checking git status..."
30
+ unless `git status --porcelain`.strip.empty?
31
+ abort "❌ Git working directory is not clean. Please commit all changes first."
32
+ end
33
+
34
+ # Check if we're on main branch
35
+ current_branch = `git rev-parse --abbrev-ref HEAD`.strip
36
+ unless current_branch == "main"
37
+ puts "⚠️ Warning: You're on branch '#{current_branch}', not 'main'"
38
+ print "Continue anyway? (y/N): "
39
+ response = $stdin.gets.chomp.downcase
40
+ abort "❌ Aborted by user" unless %w[y yes].include?(response)
41
+ end
42
+
43
+ puts "✅ All validations passed!"
44
+ end
45
+
46
+ desc "Build the gem"
47
+ task build: :validate do
48
+ puts "\n🔨 Building gem..."
49
+ Rake::Task["build"].invoke
50
+ puts "✅ Gem built successfully!"
51
+ end
52
+
53
+ desc "Publish gem to RubyGems (with validations)"
54
+ task publish: :validate do
55
+ puts "\n📦 Publishing gem to RubyGems..."
56
+
57
+ # Build and push
58
+ Rake::Task["release"].invoke
59
+ puts "✅ Gem published successfully!"
60
+ end
61
+
62
+ desc "Quick build without validations (for testing)"
63
+ task :build_quick do
64
+ puts "🔨 Quick building gem..."
65
+ Rake::Task["build"].invoke
66
+ puts "✅ Gem built!"
67
+ end
68
+
69
+ desc "Clean build artifacts"
70
+ task :clean do
71
+ puts "🧹 Cleaning build artifacts..."
72
+ FileUtils.rm_rf("pkg")
73
+ puts "✅ Build artifacts cleaned!"
74
+ end
75
+
76
+ desc "Show gem info"
77
+ task :info do
78
+ require_relative "lib/human_number/version"
79
+ puts "\n📋 Gem Information:"
80
+ puts " Name: human_number"
81
+ puts " Version: #{HumanNumber::VERSION}"
82
+ puts " Built gems: #{Dir.glob("pkg/*.gem").join(", ")}"
83
+ puts " Git branch: #{`git rev-parse --abbrev-ref HEAD`.strip}"
84
+ puts " Git status: #{`git status --porcelain`.strip.empty? ? "clean" : "dirty"}"
85
+ end
86
+
87
+ desc "Bump version interactively and commit"
88
+ task :bump do
89
+ require_relative "lib/human_number/version"
90
+
91
+ puts "🔖 Version Bump"
92
+ puts "Current version: #{HumanNumber::VERSION}"
93
+ puts ""
94
+
95
+ # Get new version from user
96
+ print "Enter new version: "
97
+ new_version = $stdin.gets.chomp.strip
98
+
99
+ if new_version.empty?
100
+ puts "❌ No version entered. Aborting."
101
+ exit 1
102
+ end
103
+
104
+ # Validate version format (basic semver check)
105
+ unless new_version.match?(/^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/)
106
+ puts "❌ Invalid version format. Please use semantic versioning (e.g., 1.2.3 or 1.2.3-alpha.1)"
107
+ exit 1
108
+ end
109
+
110
+ # Check if version is different
111
+ if new_version == HumanNumber::VERSION
112
+ puts "❌ New version is the same as current version. Aborting."
113
+ exit 1
114
+ end
115
+
116
+ # Update version file
117
+ version_file = "lib/human_number/version.rb"
118
+ version_content = File.read(version_file)
119
+ new_content = version_content.gsub(/VERSION = ".*"/, "VERSION = \"#{new_version}\"")
120
+
121
+ File.write(version_file, new_content)
122
+ puts "✅ Updated version to #{new_version}"
123
+
124
+ # Create commit
125
+ puts "\n📝 Creating version bump commit..."
126
+ system("git add #{version_file}")
127
+ commit_message = "chore: bump version to #{new_version}"
128
+
129
+ if system("git commit -m '#{commit_message}'")
130
+ puts "✅ Version bump committed successfully!"
131
+ puts "\n📋 Next steps:"
132
+ puts " - Review the changes: git show"
133
+ puts " - Build and publish: rake gem:publish"
134
+ puts " - Push to remote: git push && git push --tags"
135
+ else
136
+ puts "❌ Failed to create commit"
137
+ exit 1
138
+ end
139
+ end
140
+ end
141
+
142
+ # Convenient aliases
143
+ desc "Build and publish gem (same as gem:publish)"
144
+ task release: "gem:publish"
145
+
146
+ desc "Build gem only (same as gem:build)"
147
+ task build: "gem:build"
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/human_number/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "human_number"
7
+ spec.version = HumanNumber::VERSION
8
+ spec.authors = ["Ether Moon"]
9
+ spec.email = ["ethermoon42@gmail.com"]
10
+
11
+ spec.summary = "International standard-based number formatting for Ruby applications"
12
+ spec.description = "HumanNumber implements accurate number formatting based on international standards " \
13
+ "including Microsoft Globalization documentation and Unicode CLDR. Provides human-readable " \
14
+ "number formats with precise locale-specific decimal separators, thousand separators, " \
15
+ "currency formats, and cultural number conventions."
16
+ spec.homepage = "https://github.com/ether-moon/human_number"
17
+ spec.license = "MIT"
18
+ spec.required_ruby_version = ">= 3.1.0"
19
+
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = "https://github.com/ether-moon/human_number"
23
+ spec.metadata["changelog_uri"] = "https://github.com/ether-moon/human_number/blob/main/CHANGELOG.md"
24
+
25
+ # Specify which files should be added to the gem when it is released.
26
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
28
+ `git ls-files -z`.split("\x0").reject do |f|
29
+ (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
30
+ end
31
+ end
32
+ spec.bindir = "exe"
33
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
34
+ spec.require_paths = ["lib"]
35
+
36
+ # Runtime dependencies
37
+ spec.add_dependency "actionview", ">= 5.2"
38
+ spec.add_dependency "i18n", ">= 1.6", "< 2"
39
+ spec.add_dependency "rails-i18n", ">= 5.1"
40
+ end
@@ -27,14 +27,14 @@ module HumanNumber
27
27
  # @param number [Numeric] Number to format
28
28
  # @param locale [Symbol, String] Locale for unit system selection
29
29
  # @param abbr_units [Boolean] Use abbreviated symbols vs full words
30
- # @param max_digits [Integer, nil] Max digits after decimal, nil for complete mode
30
+ # @param max_digits [Integer, nil] Maximum significant digits to display, nil for complete mode
31
31
  # @param min_unit [Integer, nil] Minimum unit threshold for abbreviation
32
32
  # @param trim_zeros [Boolean] Remove trailing decimal zeros
33
33
  # @return [String] Formatted number string
34
34
  #
35
35
  # @api private
36
36
  def format(number, locale:, abbr_units:, max_digits:, min_unit:, trim_zeros:)
37
- locale = locale.to_sym
37
+ locale = (locale || I18n.locale).to_sym
38
38
 
39
39
  # Direct delegation to appropriate system class
40
40
  system_class = determine_system_class(locale)
@@ -48,42 +48,41 @@ module HumanNumber
48
48
  )
49
49
  end
50
50
 
51
- # Default options for human number formatting
52
- def default_options
53
- {
54
- abbr_units: true,
55
- max_digits: 2,
56
- min_unit: nil,
57
- trim_zeros: true,
58
- }.freeze
59
- end
60
-
61
- # Default options for currency number formatting (tighter display)
62
- def default_currency_number_options
63
- {
64
- abbr_units: true,
65
- max_digits: 1,
66
- min_unit: nil,
67
- trim_zeros: true,
68
- }.freeze
69
- end
70
-
71
51
  # Formats a number using Rails' currency precision rules for the given currency.
72
52
  # This ensures consistent precision regardless of display locale.
73
53
  #
74
54
  # @param number [Numeric] The number to format
75
55
  # @param currency_code [String] ISO 4217 currency code
76
56
  # @param locale [Symbol] Display locale (for context)
57
+ # @param options [Hash] Additional formatting options passed to number_to_currency
77
58
  # @return [String] Formatted number with currency-appropriate precision
78
- def format_with_currency_precision(number, currency_code:, locale:)
79
- locale = locale.to_sym
59
+ def format_with_currency_precision(number, currency_code:, locale:, **options)
60
+ locale = (locale || I18n.locale).to_sym
80
61
 
81
- # Use currency's native locale for precision rules
62
+ # Use currency's native locale for precision rules unless overridden
82
63
  precision_locale = LocaleSupport.currency_precision_locale(currency_code, locale)
83
64
 
84
- I18n.with_locale(precision_locale) do
85
- number_to_currency(number, format: "%n", locale: precision_locale)
86
- end
65
+ # Prepare options for number_to_currency
66
+ currency_options = options.dup
67
+ currency_options[:locale] = precision_locale
68
+ currency_options[:format] = "%n" # Always format as just the number (no currency symbol)
69
+
70
+ number_to_currency(number, **currency_options) || ZERO_STRING
71
+ end
72
+
73
+ # Default options for human number formatting
74
+ def default_options
75
+ {
76
+ abbr_units: true,
77
+ max_digits: 2,
78
+ min_unit: nil,
79
+ trim_zeros: true,
80
+ }.freeze
81
+ end
82
+
83
+ # Format numbers below min_unit threshold with locale-appropriate delimiters
84
+ def format_below_min_unit(number, locale)
85
+ number_with_delimiter(number, locale: locale) || number.to_s
87
86
  end
88
87
 
89
88
  private
@@ -117,6 +116,9 @@ module HumanNumber
117
116
  def format_number(number, locale:, abbr_units: true, min_unit: nil, max_digits: 2, trim_zeros: true)
118
117
  return ZERO_STRING if number.zero?
119
118
 
119
+ # Check if number meets minimum unit threshold for human formatting
120
+ return Number.format_below_min_unit(number, locale) if min_unit && number.abs < min_unit
121
+
120
122
  parts = if max_digits.nil?
121
123
  format_in_complete_mode(number, locale, abbr_units, min_unit)
122
124
  else
@@ -128,22 +130,20 @@ module HumanNumber
128
130
  finalize_result(number, parts)
129
131
  end
130
132
 
131
- def format_in_complete_mode(number, locale, abbr_units, min_unit)
133
+ def format_in_complete_mode(number, locale, abbr_units, _min_unit)
132
134
  # Complete mode shows all units: "1M 234K 567"
133
135
  unit_breakdown = break_down_into_all_units(number)
134
- units_above_threshold = filter_by_minimum_unit_threshold(unit_breakdown, min_unit)
135
- return nil if units_above_threshold.empty?
136
+ return nil if unit_breakdown.empty?
136
137
 
137
- format_all_unit_parts(units_above_threshold, locale, abbr_units)
138
+ format_all_unit_parts(unit_breakdown, locale, abbr_units)
138
139
  end
139
140
 
140
- def format_in_abbreviated_mode(number, locale, abbr_units, max_digits, trim_zeros, min_unit)
141
+ def format_in_abbreviated_mode(number, locale, abbr_units, max_digits, trim_zeros, _min_unit)
141
142
  # Abbreviated mode shows single largest unit: "1.2M"
142
143
  unit_breakdown = break_down_into_largest_unit(number)
143
- units_above_threshold = filter_by_minimum_unit_threshold(unit_breakdown, min_unit)
144
- return nil if units_above_threshold.empty?
144
+ return nil if unit_breakdown.empty?
145
145
 
146
- format_abbreviated_unit_parts(units_above_threshold, locale, abbr_units, max_digits, trim_zeros)
146
+ format_abbreviated_unit_parts(unit_breakdown, locale, abbr_units, max_digits, trim_zeros)
147
147
  end
148
148
 
149
149
  private
@@ -267,10 +267,13 @@ module HumanNumber
267
267
  return handle_edge_cases(number, digits) if should_handle_edge_case?(number, digits)
268
268
 
269
269
  abs_num = number.abs
270
- int_digits = count_integer_digits(abs_num.to_i)
271
270
 
272
- result = calculate_significant_digits_result(abs_num, digits, int_digits)
273
- apply_sign(number, result)
271
+ # Simple significant digits rounding using scientific notation
272
+ magnitude = Math.log10(abs_num).floor
273
+ scale_factor = 10**(digits - 1 - magnitude)
274
+
275
+ rounded = (abs_num * scale_factor).round / scale_factor.to_f
276
+ apply_sign(number, rounded)
274
277
  end
275
278
 
276
279
  def should_handle_edge_case?(number, digits)
@@ -283,14 +286,6 @@ module HumanNumber
283
286
  number
284
287
  end
285
288
 
286
- def calculate_significant_digits_result(abs_num, digits, int_digits)
287
- if int_digits >= digits
288
- truncate_to_digits(abs_num.to_i, digits)
289
- else
290
- round_with_decimals(abs_num, digits, int_digits)
291
- end
292
- end
293
-
294
289
  def apply_sign(original_number, result)
295
290
  original_number.negative? ? -result : result
296
291
  end
@@ -299,15 +294,6 @@ module HumanNumber
299
294
  int_part.zero? ? SINGLE_DIGIT_LIMIT : int_part.to_s.length
300
295
  end
301
296
 
302
- def truncate_to_digits(int_part, digits)
303
- int_part.to_s[0, digits].to_i.to_f
304
- end
305
-
306
- def round_with_decimals(abs_num, digits, int_digits)
307
- decimal_places = digits - int_digits
308
- (abs_num * (10**decimal_places)).round / (10**decimal_places).to_f
309
- end
310
-
311
297
  # Look up the localized symbol for a unit (e.g., 'M' for million)
312
298
  def lookup_unit_symbol(locale, unit_key, abbr_units)
313
299
  section = abbr_units ? I18N_ABBR_UNITS_SECTION : I18N_UNITS_SECTION
@@ -7,75 +7,49 @@ module HumanNumber
7
7
  # These helpers provide convenient access to HumanNumber functionality
8
8
  # within Rails views and controllers with Rails-friendly parameter handling.
9
9
  module Helpers
10
- # Formats a number with intelligent, locale-aware abbreviations.
11
- #
12
- # This is a Rails helper wrapper around HumanNumber.human_number that provides
13
- # Rails-friendly parameter handling with optional locale detection.
10
+ # Rails helper for formatting numbers with intelligent abbreviations.
14
11
  #
15
12
  # @param number [Numeric] The number to format
16
13
  # @param locale [Symbol, String, nil] The locale for formatting (default: current I18n locale)
17
14
  # @param options [Hash] Additional formatting options
18
- # @option options [Boolean] :abbr_units (true) Use abbreviated unit symbols
19
- # @option options [Integer, nil] :max_digits (1) Maximum significant digits
20
- # @option options [Integer, nil] :min_unit (nil) Minimum unit threshold
21
- # @option options [Boolean] :trim_zeros (true) Remove trailing decimal zeros
22
- #
23
- # @return [String] The formatted number string
24
15
  #
25
16
  # @example Basic usage in views
26
- # <%= human_number(1234567) %> #=> "1.2M"
27
- # <%= human_number(50000, locale: :ko) %> #=> "5만"
28
- # <%= human_number(1234567, max_digits: 2) %> #=> "1.23M"
17
+ # <%= human_number(1234567) %> #=> "1.2M"
18
+ # <%= human_number(50000, locale: :ko) %> #=> "5만"
29
19
  #
30
- # @see HumanNumber.human_number Main implementation
20
+ # @see HumanNumber.human_number For detailed documentation and all available options
31
21
  def human_number(number, locale: I18n.locale, **options)
32
22
  HumanNumber.human_number(number, locale:, **options)
33
23
  end
34
24
 
35
- # Formats a currency amount using standard Rails currency formatting.
36
- #
37
- # This helper provides Rails-friendly parameter handling for currency formatting
38
- # with automatic locale detection and parameter normalization.
25
+ # Rails helper for formatting currency amounts with standard precision.
39
26
  #
40
27
  # @param number [Numeric] The amount to format
41
28
  # @param currency_code [String] ISO 4217 currency code (e.g., 'USD', 'EUR')
42
29
  # @param locale [Symbol, String, nil] Display locale (default: current I18n locale)
43
- #
44
- # @return [String] The formatted currency string
30
+ # @param options [Hash] Additional formatting options
45
31
  #
46
32
  # @example Basic usage in views
47
- # <%= currency(1234.56, currency_code: 'USD') %> #=> "$1,234.56"
48
- # <%= currency(50000, currency_code: 'KRW', locale: :ko) %> #=> "50,000원"
49
- # <%= currency(1234.99, currency_code: 'JPY') %> #=> "1,235円"
33
+ # <%= currency(1234.56, currency_code: 'USD') %> #=> "$1,234.56"
34
+ # <%= currency(50000, currency_code: 'KRW') %> #=> "50,000원"
50
35
  #
51
- # @see HumanNumber.currency Main implementation
52
- def currency(number, currency_code:, locale: I18n.locale)
53
- HumanNumber.currency(number, currency_code:, locale:)
36
+ # @see HumanNumber.currency For detailed documentation and all available options
37
+ def currency(number, currency_code:, locale: I18n.locale, **options)
38
+ HumanNumber.currency(number, currency_code:, locale:, **options)
54
39
  end
55
40
 
56
- # Formats a currency amount with intelligent, locale-aware abbreviations.
57
- #
58
- # This helper combines human-readable number formatting with currency symbols,
59
- # providing Rails-friendly access to abbreviated currency formatting.
41
+ # Rails helper for formatting currency amounts with intelligent abbreviations.
60
42
  #
61
43
  # @param number [Numeric] The amount to format
62
44
  # @param currency_code [String] ISO 4217 currency code (e.g., 'USD', 'EUR')
63
45
  # @param locale [Symbol, String, nil] Locale for formatting (default: current I18n locale)
64
46
  # @param options [Hash] Additional formatting options
65
- # @option options [Boolean] :abbr_units (true) Use abbreviated unit symbols
66
- # @option options [Integer, nil] :max_digits (1) Maximum significant digits
67
- # @option options [Integer, nil] :min_unit (nil) Minimum unit threshold
68
- # @option options [Boolean] :trim_zeros (true) Remove trailing decimal zeros
69
- #
70
- # @return [String] The formatted currency string with abbreviations
71
47
  #
72
48
  # @example Basic usage in views
73
- # <%= human_currency(1234567, currency_code: 'USD') %> #=> "$1.2M"
74
- # <%= human_currency(50000, currency_code: 'KRW', locale: :ko) %> #=> "5만원"
75
- # <%= human_currency(1234567, currency_code: 'USD', max_digits: 2) %> #=> "$1.23M"
76
- # <%= human_currency(1000000, currency_code: 'USD', abbr_units: false) %> #=> "$1 million"
49
+ # <%= human_currency(1234567, currency_code: 'USD') %> #=> "$1.2M"
50
+ # <%= human_currency(50000, currency_code: 'KRW') %> #=> "5만원"
77
51
  #
78
- # @see HumanNumber.human_currency Main implementation
52
+ # @see HumanNumber.human_currency For detailed documentation and all available options
79
53
  def human_currency(number, currency_code:, locale: I18n.locale, **options)
80
54
  HumanNumber.human_currency(number, currency_code:, locale:, **options)
81
55
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HumanNumber
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/human_number.rb CHANGED
@@ -66,7 +66,6 @@ module HumanNumber
66
66
  #
67
67
  # @note Complete mode (max_digits: nil) shows full precision across multiple units
68
68
  # @see Formatters::Number.format The underlying formatter implementation
69
- # @since 1.0.0
70
69
  def human_number(number, locale: I18n.locale, **options)
71
70
  validate_number_input!(number)
72
71
  validate_locale!(locale)
@@ -76,23 +75,41 @@ module HumanNumber
76
75
  Formatters::Number.format(number, locale:, **final_options)
77
76
  end
78
77
 
79
- # Formats a currency amount using standard Rails currency formatting.
78
+ # Formats a currency amount with standard precision and symbol placement.
80
79
  #
81
- # This method applies currency-specific precision rules (e.g., 2 decimals for USD,
82
- # 0 decimals for JPY/KRW) based on the currency's native locale, ensuring
83
- # consistent precision regardless of the display locale.
80
+ # This method provides currency formatting with automatic precision rules based on
81
+ # international standards, ensuring consistent display across different locales:
82
+ # - **Currency-specific precision**: 2 decimals for USD/EUR, 0 for JPY/KRW
83
+ # - **Native locale rules**: Precision determined by currency's origin locale
84
+ # - **Cross-locale consistency**: USD shows 2 decimals regardless of display locale
85
+ # - **Symbol placement**: Follows locale-specific conventions ($1,234 vs 1,234원)
84
86
  #
85
87
  # @param number [Numeric] The amount to format
86
88
  # @param currency_code [String] ISO 4217 currency code (e.g., 'USD', 'EUR', 'KRW', 'JPY')
87
- # @param locale [Symbol, String] Display locale for formatting rules
89
+ # @param locale [Symbol, String] Display locale for formatting rules (default: current I18n locale)
90
+ # Determines delimiter, separator, and symbol placement conventions
91
+ #
92
+ # @option options [Integer] :precision Decimal precision level. Overrides currency-specific precision
93
+ # @option options [Symbol] :round_mode Rounding mode (see BigDecimal.mode). Defaults to :default
94
+ # @option options [String] :separator Decimal separator. Defaults to locale-specific separator
95
+ # @option options [String] :delimiter Thousands delimiter. Defaults to locale-specific delimiter
96
+ # @option options [Boolean] :strip_insignificant_zeros (false) Remove trailing decimal zeros
97
+ #
98
+ # @note Only numeric formatting options are supported. Currency symbols and formats are
99
+ # automatically determined by ISO 4217 standards and locale conventions.
88
100
  #
89
101
  # @return [String] The formatted currency string
90
102
  #
91
- # @example Standard currency formatting
103
+ # @example Basic currency formatting
92
104
  # HumanNumber.currency(1234.56, currency_code: 'USD', locale: :en) #=> "$1,234.56"
93
105
  # HumanNumber.currency(50000, currency_code: 'KRW', locale: :ko) #=> "50,000원"
94
106
  # HumanNumber.currency(1234.99, currency_code: 'JPY', locale: :ja) #=> "1,235円"
95
107
  #
108
+ # @example Custom precision and formatting
109
+ # HumanNumber.currency(1234.56, currency_code: 'USD', precision: 1) #=> "$1,234.6"
110
+ # HumanNumber.currency(1234.56, currency_code: 'USD', delimiter: " ") #=> "$1 234.56"
111
+ # HumanNumber.currency(1234.50, currency_code: 'USD', strip_insignificant_zeros: true) #=> "$1,234.5"
112
+ #
96
113
  # @example Cross-locale precision consistency
97
114
  # # USD always shows 2 decimals regardless of display locale
98
115
  # HumanNumber.currency(1234.56, currency_code: 'USD', locale: :ko) #=> "$1,234.56"
@@ -100,16 +117,20 @@ module HumanNumber
100
117
  # # JPY always shows 0 decimals regardless of display locale
101
118
  # HumanNumber.currency(1234.56, currency_code: 'JPY', locale: :en) #=> "1,235円"
102
119
  #
103
- # @note Precision is determined by currency's native locale, not display locale
120
+ # @example Edge cases
121
+ # HumanNumber.currency(0, currency_code: 'USD') #=> "$0.00"
122
+ # HumanNumber.currency(-1234.56, currency_code: 'USD') #=> "-$1,234.56"
123
+ #
124
+ # @note Precision is determined by currency's native locale unless overridden via :precision option
125
+ # @see #human_currency For currency formatting with intelligent abbreviations (K/M/B)
104
126
  # @see Formatters::Number.format_with_currency_precision Currency precision logic
105
127
  # @see Formatters::Currency.format Currency symbol and format application
106
- # @since 1.0.0
107
- def currency(number, currency_code:, locale: I18n.locale)
128
+ def currency(number, currency_code:, locale: I18n.locale, **options)
108
129
  validate_number_input!(number)
109
130
  validate_currency_code!(currency_code)
110
131
  validate_locale!(locale)
111
132
 
112
- formatted_number = Formatters::Number.format_with_currency_precision(number, currency_code:, locale:)
133
+ formatted_number = Formatters::Number.format_with_currency_precision(number, currency_code:, locale:, **options)
113
134
  Formatters::Currency.format(formatted_number, currency_code:, locale:)
114
135
  end
115
136
 
@@ -139,7 +160,7 @@ module HumanNumber
139
160
  # HumanNumber.human_currency(50000, currency_code: 'KRW', locale: :ko) #=> "5만원"
140
161
  #
141
162
  # @example Precision control
142
- # HumanNumber.human_currency(1234567, currency_code: 'USD', locale: :en, max_digits: 2) #=> "$1.23M"
163
+ # HumanNumber.human_currency(1234567, currency_code: 'USD', locale: :en, max_digits: 2) #=> "$1.2M"
143
164
  # HumanNumber.human_currency(1234567, currency_code: 'USD', locale: :en, max_digits: nil) #=> "$1M 234K 567"
144
165
  #
145
166
  # @example Unit preferences
@@ -153,13 +174,12 @@ module HumanNumber
153
174
  # @note Combines human number formatting with currency-specific symbol placement
154
175
  # @see #human_number For detailed number formatting options
155
176
  # @see #currency For standard currency formatting without abbreviations
156
- # @since 1.0.0
157
177
  def human_currency(number, currency_code:, locale: I18n.locale, **options)
158
178
  validate_number_input!(number)
159
179
  validate_currency_code!(currency_code)
160
180
  validate_locale!(locale)
161
181
 
162
- final_options = Formatters::Number.default_currency_number_options.merge(options)
182
+ final_options = Formatters::Number.default_options.merge(options)
163
183
 
164
184
  formatted_number = Formatters::Number.format(number, locale:, **final_options)
165
185
  Formatters::Currency.format(formatted_number, currency_code:, locale:)
@@ -0,0 +1,204 @@
1
+ #!/bin/bash
2
+
3
+ # HumanNumber Gem Build and Publish Script
4
+ # Usage: ./scripts/build_and_publish.sh [build|publish|info|clean]
5
+
6
+ set -e # Exit on any error
7
+
8
+ # Colors for output
9
+ RED='\033[0;31m'
10
+ GREEN='\033[0;32m'
11
+ YELLOW='\033[1;33m'
12
+ BLUE='\033[0;34m'
13
+ NC='\033[0m' # No Color
14
+
15
+ # Helper functions
16
+ log_info() {
17
+ echo -e "${BLUE}ℹ️ $1${NC}"
18
+ }
19
+
20
+ log_success() {
21
+ echo -e "${GREEN}✅ $1${NC}"
22
+ }
23
+
24
+ log_warning() {
25
+ echo -e "${YELLOW}⚠️ $1${NC}"
26
+ }
27
+
28
+ log_error() {
29
+ echo -e "${RED}❌ $1${NC}"
30
+ exit 1
31
+ }
32
+
33
+ # Validation functions
34
+ validate_git_status() {
35
+ log_info "Checking git status..."
36
+ if [ -n "$(git status --porcelain)" ]; then
37
+ log_error "Git working directory is not clean. Please commit all changes first."
38
+ fi
39
+ log_success "Git working directory is clean"
40
+ }
41
+
42
+ validate_git_branch() {
43
+ log_info "Checking git branch..."
44
+ current_branch=$(git rev-parse --abbrev-ref HEAD)
45
+ if [ "$current_branch" != "main" ]; then
46
+ log_warning "You're on branch '$current_branch', not 'main'"
47
+ read -p "Continue anyway? (y/N): " -n 1 -r
48
+ echo
49
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
50
+ log_error "Aborted by user"
51
+ fi
52
+ fi
53
+ log_success "Git branch check passed"
54
+ }
55
+
56
+ run_tests() {
57
+ log_info "Running tests..."
58
+ if ! bundle exec rspec; then
59
+ log_error "Tests failed"
60
+ fi
61
+ log_success "All tests passed"
62
+ }
63
+
64
+ run_linting() {
65
+ log_info "Running RuboCop..."
66
+ if ! bundle exec rubocop; then
67
+ log_error "RuboCop checks failed"
68
+ fi
69
+ log_success "RuboCop checks passed"
70
+ }
71
+
72
+ validate_all() {
73
+ echo "🔍 Running pre-publish validations..."
74
+ run_tests
75
+ run_linting
76
+ validate_git_status
77
+ validate_git_branch
78
+ log_success "All validations passed!"
79
+ }
80
+
81
+ build_gem() {
82
+ log_info "Building gem..."
83
+ if ! bundle exec rake build; then
84
+ log_error "Gem build failed"
85
+ fi
86
+ log_success "Gem built successfully!"
87
+ }
88
+
89
+ publish_gem() {
90
+ log_info "Publishing gem to RubyGems..."
91
+ if ! bundle exec rake release; then
92
+ log_error "Gem publish failed"
93
+ fi
94
+ log_success "Gem published successfully!"
95
+ }
96
+
97
+ show_info() {
98
+ echo "📋 Gem Information:"
99
+ echo " Name: human_number"
100
+ echo " Version: $(ruby -r ./lib/human_number/version -e 'puts HumanNumber::VERSION')"
101
+ echo " Built gems: $(ls pkg/*.gem 2>/dev/null || echo 'none')"
102
+ echo " Git branch: $(git rev-parse --abbrev-ref HEAD)"
103
+ echo " Git status: $([ -z "$(git status --porcelain)" ] && echo 'clean' || echo 'dirty')"
104
+ }
105
+
106
+ clean_artifacts() {
107
+ log_info "Cleaning build artifacts..."
108
+ rm -rf pkg/
109
+ log_success "Build artifacts cleaned!"
110
+ }
111
+
112
+ version_bump() {
113
+ echo "🔖 Version Bump"
114
+ current_version=$(ruby -r ./lib/human_number/version -e 'puts HumanNumber::VERSION')
115
+ echo "Current version: $current_version"
116
+ echo ""
117
+
118
+ # Get new version from user
119
+ read -p "Enter new version: " new_version
120
+
121
+ if [ -z "$new_version" ]; then
122
+ log_error "No version entered. Aborting."
123
+ fi
124
+
125
+ # Validate version format (basic semver check)
126
+ if ! echo "$new_version" | grep -qE "^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$"; then
127
+ log_error "Invalid version format. Please use semantic versioning (e.g., 1.2.3 or 1.2.3-alpha.1)"
128
+ fi
129
+
130
+ # Check if version is different
131
+ if [ "$new_version" = "$current_version" ]; then
132
+ log_error "New version is the same as current version. Aborting."
133
+ fi
134
+
135
+ # Update version file
136
+ version_file="lib/human_number/version.rb"
137
+ sed -i.bak "s/VERSION = \".*\"/VERSION = \"$new_version\"/" "$version_file" && rm "$version_file.bak"
138
+ log_success "Updated version to $new_version"
139
+
140
+ # Create commit
141
+ log_info "Creating version bump commit..."
142
+ git add "$version_file"
143
+ commit_message="chore: bump version to $new_version"
144
+
145
+ if git commit -m "$commit_message"; then
146
+ log_success "Version bump committed successfully!"
147
+ echo ""
148
+ echo "📋 Next steps:"
149
+ echo " - Review the changes: git show"
150
+ echo " - Build and publish: $0 publish"
151
+ echo " - Push to remote: git push && git push --tags"
152
+ else
153
+ log_error "Failed to create commit"
154
+ fi
155
+ }
156
+
157
+ # Main script logic
158
+ case "${1:-help}" in
159
+ "build")
160
+ validate_all
161
+ build_gem
162
+ ;;
163
+ "publish")
164
+ validate_all
165
+ publish_gem
166
+ ;;
167
+ "build-quick")
168
+ log_info "Quick building gem (no validations)..."
169
+ build_gem
170
+ ;;
171
+ "info")
172
+ show_info
173
+ ;;
174
+ "clean")
175
+ clean_artifacts
176
+ ;;
177
+ "validate")
178
+ validate_all
179
+ ;;
180
+ "bump")
181
+ version_bump
182
+ ;;
183
+ "help"|*)
184
+ echo "HumanNumber Gem Build and Publish Script"
185
+ echo ""
186
+ echo "Usage: $0 [command]"
187
+ echo ""
188
+ echo "Commands:"
189
+ echo " build Build gem with full validations (tests, linting, git checks)"
190
+ echo " publish Publish gem to RubyGems with full validations"
191
+ echo " build-quick Build gem without validations (for testing)"
192
+ echo " validate Run all validations without building"
193
+ echo " info Show gem and repository information"
194
+ echo " clean Clean build artifacts"
195
+ echo " bump Bump version interactively and commit"
196
+ echo " help Show this help message"
197
+ echo ""
198
+ echo "Examples:"
199
+ echo " $0 build # Build gem with validations"
200
+ echo " $0 publish # Build and publish to RubyGems"
201
+ echo " $0 bump # Bump version interactively"
202
+ echo " $0 info # Show current gem info"
203
+ ;;
204
+ esac
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: human_number
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
- - Your Name
7
+ - Ether Moon
8
8
  bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
@@ -62,17 +62,19 @@ description: HumanNumber implements accurate number formatting based on internat
62
62
  human-readable number formats with precise locale-specific decimal separators, thousand
63
63
  separators, currency formats, and cultural number conventions.
64
64
  email:
65
- - your.email@example.com
65
+ - ethermoon42@gmail.com
66
66
  executables: []
67
67
  extensions: []
68
68
  extra_rdoc_files: []
69
69
  files:
70
70
  - ".rspec"
71
71
  - ".rubocop.yml"
72
+ - BUILD.md
72
73
  - CHANGELOG.md
73
74
  - CLAUDE.md
74
75
  - Gemfile
75
76
  - LICENSE
77
+ - Makefile
76
78
  - README.md
77
79
  - Rakefile
78
80
  - config/locales/bn.yml
@@ -89,6 +91,7 @@ files:
89
91
  - config/locales/zh-CN.yml
90
92
  - config/locales/zh-TW.yml
91
93
  - config/locales/zh.yml
94
+ - human_number.gemspec
92
95
  - lib/human_number.rb
93
96
  - lib/human_number/formatters/currency.rb
94
97
  - lib/human_number/formatters/number.rb
@@ -96,15 +99,15 @@ files:
96
99
  - lib/human_number/rails/helpers.rb
97
100
  - lib/human_number/railtie.rb
98
101
  - lib/human_number/version.rb
99
- homepage: https://github.com/yourusername/human_number
102
+ - scripts/build_and_publish.sh
103
+ homepage: https://github.com/ether-moon/human_number
100
104
  licenses:
101
105
  - MIT
102
106
  metadata:
103
107
  allowed_push_host: https://rubygems.org
104
- homepage_uri: https://github.com/yourusername/human_number
105
- source_code_uri: https://github.com/yourusername/human_number
106
- changelog_uri: https://github.com/yourusername/human_number/blob/main/CHANGELOG.md
107
- rubygems_mfa_required: 'true'
108
+ homepage_uri: https://github.com/ether-moon/human_number
109
+ source_code_uri: https://github.com/ether-moon/human_number
110
+ changelog_uri: https://github.com/ether-moon/human_number/blob/main/CHANGELOG.md
108
111
  rdoc_options: []
109
112
  require_paths:
110
113
  - lib