string_to_number 0.2.0 → 0.2.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +83 -0
- data/.rubocop.yml +110 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +32 -1
- data/README.md +9 -5
- data/Rakefile +5 -1
- data/benchmark.rb +41 -40
- data/lib/string_to_number/parser.rb +20 -18
- data/lib/string_to_number/to_number.rb +20 -20
- data/lib/string_to_number/version.rb +3 -1
- data/lib/string_to_number.rb +9 -7
- data/logo.png +0 -0
- data/microbenchmark.rb +81 -80
- data/performance_comparison.rb +34 -35
- data/profile.rb +44 -45
- data/string_to_number.gemspec +5 -6
- metadata +7 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a97e2a69e72adda0aeedace8ab5a9ddae63c1a4741846d6d405a0ef767bdaaaa
|
4
|
+
data.tar.gz: 78553f4c606a717c8bab939e24b18ddd023a8962b902d2fea75b03498b2e3e0a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1042d45f537afc86b43883d29a3ef03ffd3ddc365a37c85843bd78fa54c4d35178c140e32c025d7406681dc31d77c06b7f7e526e4255fef1629a468a7ddf1958
|
7
|
+
data.tar.gz: 1f6b2b1e8dc6c17bf72d0ce759da2fe31dd9771188ae6087b23774b06e55f9678ee8bcc049c611ff8b0a68d9b99c8b3cb3088e28f342ae1f0d842cd618cf81da
|
@@ -0,0 +1,83 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master, main ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master, main ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
ruby-version: ['2.7', '3.0', '3.1', '3.2', '3.3']
|
15
|
+
|
16
|
+
steps:
|
17
|
+
- uses: actions/checkout@v4
|
18
|
+
|
19
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
20
|
+
uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
ruby-version: ${{ matrix.ruby-version }}
|
23
|
+
bundler-cache: true
|
24
|
+
|
25
|
+
- name: Run tests
|
26
|
+
run: bundle exec rake spec
|
27
|
+
|
28
|
+
- name: Run performance tests
|
29
|
+
run: bundle exec rspec spec/performance_spec.rb
|
30
|
+
|
31
|
+
lint:
|
32
|
+
runs-on: ubuntu-latest
|
33
|
+
steps:
|
34
|
+
- uses: actions/checkout@v4
|
35
|
+
|
36
|
+
- name: Set up Ruby
|
37
|
+
uses: ruby/setup-ruby@v1
|
38
|
+
with:
|
39
|
+
ruby-version: '3.3'
|
40
|
+
bundler-cache: true
|
41
|
+
|
42
|
+
- name: Install RuboCop
|
43
|
+
run: gem install rubocop
|
44
|
+
|
45
|
+
- name: Run RuboCop
|
46
|
+
run: rubocop --format github
|
47
|
+
|
48
|
+
security:
|
49
|
+
runs-on: ubuntu-latest
|
50
|
+
steps:
|
51
|
+
- uses: actions/checkout@v4
|
52
|
+
|
53
|
+
- name: Set up Ruby
|
54
|
+
uses: ruby/setup-ruby@v1
|
55
|
+
with:
|
56
|
+
ruby-version: '3.3'
|
57
|
+
bundler-cache: true
|
58
|
+
|
59
|
+
- name: Install bundler-audit
|
60
|
+
run: gem install bundler-audit
|
61
|
+
|
62
|
+
- name: Run bundler-audit
|
63
|
+
run: bundle audit --update
|
64
|
+
|
65
|
+
gem-build:
|
66
|
+
runs-on: ubuntu-latest
|
67
|
+
steps:
|
68
|
+
- uses: actions/checkout@v4
|
69
|
+
|
70
|
+
- name: Set up Ruby
|
71
|
+
uses: ruby/setup-ruby@v1
|
72
|
+
with:
|
73
|
+
ruby-version: '3.3'
|
74
|
+
bundler-cache: true
|
75
|
+
|
76
|
+
- name: Build gem
|
77
|
+
run: gem build string_to_number.gemspec
|
78
|
+
|
79
|
+
- name: Install gem locally
|
80
|
+
run: gem install string_to_number-*.gem
|
81
|
+
|
82
|
+
- name: Test gem installation
|
83
|
+
run: ruby -e "require 'string_to_number'; puts StringToNumber.in_numbers('vingt et un')"
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.7
|
3
|
+
NewCops: enable
|
4
|
+
Exclude:
|
5
|
+
- 'vendor/**/*'
|
6
|
+
- 'pkg/**/*'
|
7
|
+
- 'tmp/**/*'
|
8
|
+
- 'bin/**/*'
|
9
|
+
- 'spec/fixtures/**/*'
|
10
|
+
SuggestExtensions: false
|
11
|
+
|
12
|
+
# Prefer double quotes for strings
|
13
|
+
Style/StringLiterals:
|
14
|
+
EnforcedStyle: single_quotes
|
15
|
+
|
16
|
+
# Allow longer lines for readability
|
17
|
+
Layout/LineLength:
|
18
|
+
Max: 120
|
19
|
+
Exclude:
|
20
|
+
- 'spec/**/*'
|
21
|
+
|
22
|
+
# Allow longer methods for complex parsing logic
|
23
|
+
Metrics/MethodLength:
|
24
|
+
Max: 25
|
25
|
+
Exclude:
|
26
|
+
- 'spec/**/*'
|
27
|
+
- 'benchmark.rb'
|
28
|
+
- 'microbenchmark.rb'
|
29
|
+
- 'performance_comparison.rb'
|
30
|
+
- 'lib/string_to_number/parser.rb'
|
31
|
+
- 'lib/string_to_number/to_number.rb'
|
32
|
+
|
33
|
+
# Allow longer classes for main implementation
|
34
|
+
Metrics/ClassLength:
|
35
|
+
Max: 250
|
36
|
+
|
37
|
+
# Allow longer blocks for test files
|
38
|
+
Metrics/BlockLength:
|
39
|
+
Max: 300
|
40
|
+
Exclude:
|
41
|
+
- 'spec/**/*'
|
42
|
+
|
43
|
+
# Allow more complex methods in parser
|
44
|
+
Metrics/CyclomaticComplexity:
|
45
|
+
Max: 12
|
46
|
+
Exclude:
|
47
|
+
- 'benchmark.rb'
|
48
|
+
- 'microbenchmark.rb'
|
49
|
+
- 'performance_comparison.rb'
|
50
|
+
- 'lib/string_to_number/parser.rb'
|
51
|
+
- 'lib/string_to_number/to_number.rb'
|
52
|
+
|
53
|
+
# Allow higher ABC size for parsing methods
|
54
|
+
Metrics/AbcSize:
|
55
|
+
Max: 20
|
56
|
+
Exclude:
|
57
|
+
- 'spec/**/*'
|
58
|
+
- 'benchmark.rb'
|
59
|
+
- 'microbenchmark.rb'
|
60
|
+
- 'performance_comparison.rb'
|
61
|
+
- 'lib/string_to_number/parser.rb'
|
62
|
+
- 'lib/string_to_number/to_number.rb'
|
63
|
+
|
64
|
+
# Allow higher perceived complexity for performance-critical methods
|
65
|
+
Metrics/PerceivedComplexity:
|
66
|
+
Max: 8
|
67
|
+
Exclude:
|
68
|
+
- 'benchmark.rb'
|
69
|
+
- 'microbenchmark.rb'
|
70
|
+
- 'performance_comparison.rb'
|
71
|
+
- 'lib/string_to_number/parser.rb'
|
72
|
+
- 'lib/string_to_number/to_number.rb'
|
73
|
+
|
74
|
+
# Allow multiple assignments for performance reasons
|
75
|
+
Style/ParallelAssignment:
|
76
|
+
Enabled: false
|
77
|
+
|
78
|
+
# Allow guard clauses without else
|
79
|
+
Style/GuardClause:
|
80
|
+
Enabled: false
|
81
|
+
|
82
|
+
# Documentation requirements
|
83
|
+
Style/Documentation:
|
84
|
+
Enabled: false
|
85
|
+
|
86
|
+
# Allow frozen string literal comments
|
87
|
+
Style/FrozenStringLiteralComment:
|
88
|
+
Enabled: true
|
89
|
+
EnforcedStyle: always
|
90
|
+
|
91
|
+
|
92
|
+
# Layout preferences
|
93
|
+
Layout/MultilineMethodCallIndentation:
|
94
|
+
EnforcedStyle: aligned
|
95
|
+
|
96
|
+
Layout/MultilineOperationIndentation:
|
97
|
+
EnforcedStyle: aligned
|
98
|
+
|
99
|
+
# Naming conventions
|
100
|
+
Naming/PredicatePrefix:
|
101
|
+
ForbiddenPrefixes:
|
102
|
+
- is_
|
103
|
+
|
104
|
+
# Allow single line methods
|
105
|
+
Style/SingleLineMethods:
|
106
|
+
AllowIfMethodIsEmpty: true
|
107
|
+
|
108
|
+
# Gem specific rules
|
109
|
+
Gemspec/RequiredRubyVersion:
|
110
|
+
Enabled: false
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,25 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
string_to_number (0.2.
|
4
|
+
string_to_number (0.2.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
+
ast (2.4.3)
|
9
10
|
diff-lcs (1.5.0)
|
11
|
+
json (2.12.2)
|
12
|
+
language_server-protocol (3.17.0.5)
|
13
|
+
lint_roller (1.1.0)
|
14
|
+
parallel (1.27.0)
|
15
|
+
parser (3.3.8.0)
|
16
|
+
ast (~> 2.4.1)
|
17
|
+
racc
|
18
|
+
prism (1.4.0)
|
19
|
+
racc (1.8.1)
|
20
|
+
rainbow (3.1.1)
|
10
21
|
rake (13.0.6)
|
22
|
+
regexp_parser (2.10.0)
|
11
23
|
rspec (3.11.0)
|
12
24
|
rspec-core (~> 3.11.0)
|
13
25
|
rspec-expectations (~> 3.11.0)
|
@@ -21,6 +33,24 @@ GEM
|
|
21
33
|
diff-lcs (>= 1.2.0, < 2.0)
|
22
34
|
rspec-support (~> 3.11.0)
|
23
35
|
rspec-support (3.11.1)
|
36
|
+
rubocop (1.77.0)
|
37
|
+
json (~> 2.3)
|
38
|
+
language_server-protocol (~> 3.17.0.2)
|
39
|
+
lint_roller (~> 1.1.0)
|
40
|
+
parallel (~> 1.10)
|
41
|
+
parser (>= 3.3.0.2)
|
42
|
+
rainbow (>= 2.2.2, < 4.0)
|
43
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
44
|
+
rubocop-ast (>= 1.45.1, < 2.0)
|
45
|
+
ruby-progressbar (~> 1.7)
|
46
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
47
|
+
rubocop-ast (1.45.1)
|
48
|
+
parser (>= 3.3.7.2)
|
49
|
+
prism (~> 1.4)
|
50
|
+
ruby-progressbar (1.13.0)
|
51
|
+
unicode-display_width (3.1.4)
|
52
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
53
|
+
unicode-emoji (4.0.4)
|
24
54
|
|
25
55
|
PLATFORMS
|
26
56
|
x86_64-linux
|
@@ -29,6 +59,7 @@ DEPENDENCIES
|
|
29
59
|
bundler
|
30
60
|
rake
|
31
61
|
rspec
|
62
|
+
rubocop (~> 1.21)
|
32
63
|
string_to_number!
|
33
64
|
|
34
65
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
<div align="center">
|
2
|
+
<img src="logo.png" alt="StringToNumber Logo" width="200" height="200">
|
3
|
+
|
4
|
+
# StringToNumber
|
5
|
+
|
6
|
+
[](https://badge.fury.io/rb/string_to_number)
|
7
|
+
[](https://github.com/FabienPiette/string_to_number/actions)
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
9
|
+
</div>
|
6
10
|
|
7
11
|
A high-performance Ruby gem for converting French written numbers into their numeric equivalents. Features intelligent caching, thread-safe operations, and support for complex French number formats.
|
8
12
|
|
data/Rakefile
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler/gem_tasks'
|
2
4
|
require 'rspec/core/rake_task'
|
5
|
+
require 'rubocop/rake_task'
|
3
6
|
|
4
7
|
RSpec::Core::RakeTask.new(:spec)
|
8
|
+
RuboCop::RakeTask.new
|
5
9
|
|
6
|
-
task default:
|
10
|
+
task default: %i[rubocop spec]
|
7
11
|
|
8
12
|
task :env, [:env] do |_t, _args|
|
9
13
|
require 'string_to_number'
|
data/benchmark.rb
CHANGED
@@ -10,8 +10,8 @@ require 'benchmark'
|
|
10
10
|
class StringToNumberBenchmark
|
11
11
|
# Test data organized by complexity
|
12
12
|
TEST_CASES = {
|
13
|
-
simple: [
|
14
|
-
|
13
|
+
simple: %w[
|
14
|
+
un vingt cent mille
|
15
15
|
],
|
16
16
|
medium: [
|
17
17
|
'vingt et un', 'deux cent cinquante', 'mille deux cent'
|
@@ -20,20 +20,20 @@ class StringToNumberBenchmark
|
|
20
20
|
'trois milliards cinq cents millions',
|
21
21
|
'soixante-quinze million trois cent quarante six mille sept cent quatre-vingt-dix neuf'
|
22
22
|
],
|
23
|
-
edge_cases: [
|
24
|
-
|
23
|
+
edge_cases: %w[
|
24
|
+
VINGT une septante quatre-vingts
|
25
25
|
]
|
26
26
|
}.freeze
|
27
27
|
|
28
28
|
def self.run_benchmark
|
29
|
-
puts
|
30
|
-
puts
|
29
|
+
puts 'StringToNumber Performance Benchmark'
|
30
|
+
puts '=' * 50
|
31
31
|
puts "Ruby version: #{RUBY_VERSION}"
|
32
32
|
puts "Platform: #{RUBY_PLATFORM}"
|
33
33
|
puts
|
34
34
|
|
35
35
|
# Warm up
|
36
|
-
puts
|
36
|
+
puts 'Warming up...'
|
37
37
|
TEST_CASES.values.flatten.each { |text| StringToNumber.in_numbers(text) }
|
38
38
|
puts
|
39
39
|
|
@@ -41,7 +41,7 @@ class StringToNumberBenchmark
|
|
41
41
|
|
42
42
|
TEST_CASES.each do |category, test_cases|
|
43
43
|
puts "#{category.to_s.capitalize} Numbers:"
|
44
|
-
puts
|
44
|
+
puts '-' * 30
|
45
45
|
|
46
46
|
results = benchmark_category(test_cases)
|
47
47
|
total_results[category] = results
|
@@ -53,46 +53,44 @@ class StringToNumberBenchmark
|
|
53
53
|
puts
|
54
54
|
|
55
55
|
# Show individual case performance for complex numbers
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
avg_ms = (individual_time / 1000) * 1000
|
63
|
-
puts " #{index + 1}. #{avg_ms.round(4)}ms - '#{text[0..50]}#{text.length > 50 ? '...' : ''}'"
|
56
|
+
next unless category == :complex
|
57
|
+
|
58
|
+
puts 'Individual case breakdown:'
|
59
|
+
test_cases.each_with_index do |text, index|
|
60
|
+
individual_time = Benchmark.realtime do
|
61
|
+
1000.times { StringToNumber.in_numbers(text) }
|
64
62
|
end
|
65
|
-
|
63
|
+
avg_ms = (individual_time / 1000) * 1000
|
64
|
+
puts " #{index + 1}. #{avg_ms.round(4)}ms - '#{text[0..50]}#{'...' if text.length > 50}'"
|
66
65
|
end
|
66
|
+
puts
|
67
67
|
end
|
68
68
|
|
69
69
|
# Summary
|
70
|
-
puts
|
71
|
-
puts
|
72
|
-
puts
|
70
|
+
puts '=' * 50
|
71
|
+
puts 'PERFORMANCE SUMMARY'
|
72
|
+
puts '=' * 50
|
73
73
|
|
74
74
|
total_results.each do |category, results|
|
75
75
|
status = case results[:avg_time_ms]
|
76
|
-
when 0..0.1 then
|
77
|
-
when 0.1..0.5 then
|
78
|
-
when 0.5..1.0 then
|
79
|
-
else
|
76
|
+
when 0..0.1 then '🟢 Excellent'
|
77
|
+
when 0.1..0.5 then '🟡 Good'
|
78
|
+
when 0.5..1.0 then '🟠 Acceptable'
|
79
|
+
else '🔴 Needs optimization'
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
puts "#{category.to_s.capitalize.ljust(12)} #{status.ljust(15)} #{results[:avg_time_ms].round(4)}ms avg"
|
83
83
|
end
|
84
84
|
|
85
85
|
puts
|
86
|
-
puts
|
86
|
+
puts 'Memory efficiency test...'
|
87
87
|
test_memory_usage
|
88
88
|
|
89
89
|
puts
|
90
|
-
puts
|
90
|
+
puts 'Scalability test...'
|
91
91
|
test_scalability
|
92
92
|
end
|
93
93
|
|
94
|
-
private
|
95
|
-
|
96
94
|
def self.benchmark_category(test_cases, iterations = 2000)
|
97
95
|
total_time = Benchmark.realtime do
|
98
96
|
test_cases.each do |text|
|
@@ -130,7 +128,7 @@ class StringToNumberBenchmark
|
|
130
128
|
|
131
129
|
puts "Object creation: #{object_growth} new objects (#{object_growth > 1000 ? '🔴 High' : '🟢 Low'})"
|
132
130
|
else
|
133
|
-
puts
|
131
|
+
puts 'Memory tracking not available on this platform'
|
134
132
|
end
|
135
133
|
end
|
136
134
|
|
@@ -138,27 +136,32 @@ class StringToNumberBenchmark
|
|
138
136
|
# Test how performance scales with input complexity
|
139
137
|
inputs = [
|
140
138
|
'un', # 2 chars
|
141
|
-
'vingt et un', # 11 chars
|
139
|
+
'vingt et un', # 11 chars
|
142
140
|
'mille deux cent trente-quatre', # 29 chars
|
143
141
|
'trois milliards cinq cents millions deux cent mille et une' # 58 chars
|
144
142
|
]
|
145
143
|
|
146
|
-
puts
|
147
|
-
|
144
|
+
puts 'Input length vs. performance:'
|
145
|
+
|
148
146
|
results = inputs.map do |input|
|
149
147
|
time = Benchmark.realtime do
|
150
148
|
1000.times { StringToNumber.in_numbers(input) }
|
151
149
|
end
|
152
150
|
avg_ms = (time / 1000) * 1000
|
153
|
-
|
151
|
+
|
154
152
|
{ length: input.length, time: avg_ms, input: input }
|
155
153
|
end
|
156
154
|
|
157
155
|
results.each do |result|
|
158
156
|
complexity_ratio = result[:time] / results.first[:time]
|
159
|
-
status = complexity_ratio < 5
|
160
|
-
|
161
|
-
|
157
|
+
status = if complexity_ratio < 5
|
158
|
+
'🟢'
|
159
|
+
else
|
160
|
+
complexity_ratio < 10 ? '🟡' : '🔴'
|
161
|
+
end
|
162
|
+
|
163
|
+
puts " #{result[:length].to_s.rjust(2)} chars: #{result[:time].round(4)}ms #{status} " \
|
164
|
+
"(#{complexity_ratio.round(1)}x baseline)"
|
162
165
|
end
|
163
166
|
|
164
167
|
# Check if performance degrades reasonably
|
@@ -172,6 +175,4 @@ class StringToNumberBenchmark
|
|
172
175
|
end
|
173
176
|
|
174
177
|
# Run the benchmark
|
175
|
-
if __FILE__ == $
|
176
|
-
StringToNumberBenchmark.run_benchmark
|
177
|
-
end
|
178
|
+
StringToNumberBenchmark.run_benchmark if __FILE__ == $PROGRAM_NAME
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module StringToNumber
|
4
4
|
# High-performance French text to number parser
|
5
|
-
#
|
5
|
+
#
|
6
6
|
# This class provides a clean, optimized implementation that maintains
|
7
7
|
# compatibility with the original algorithm while adding significant
|
8
8
|
# performance improvements through caching and memoization.
|
@@ -23,9 +23,9 @@ module StringToNumber
|
|
23
23
|
# Pre-compiled regex patterns for optimal performance
|
24
24
|
MULTIPLIER_KEYS = MULTIPLIERS.keys.reject { |k| %w[un dix].include?(k) }
|
25
25
|
.sort_by(&:length).reverse.freeze
|
26
|
-
MULTIPLIER_PATTERN = /(?<f>.*?)\s?(?<m>#{MULTIPLIER_KEYS.join('|')})
|
27
|
-
QUATRE_VINGT_PATTERN = /(quatre(-|\s)vingt(s?)((-|\s)dix)?)((-|\s)?)(\w*)
|
28
|
-
|
26
|
+
MULTIPLIER_PATTERN = /(?<f>.*?)\s?(?<m>#{MULTIPLIER_KEYS.join('|')})/.freeze
|
27
|
+
QUATRE_VINGT_PATTERN = /(quatre(-|\s)vingt(s?)((-|\s)dix)?)((-|\s)?)(\w*)/.freeze
|
28
|
+
|
29
29
|
# Cache configuration
|
30
30
|
MAX_CACHE_SIZE = 1000
|
31
31
|
private_constant :MAX_CACHE_SIZE
|
@@ -45,7 +45,7 @@ module StringToNumber
|
|
45
45
|
# @raise [ArgumentError] if text is not a string
|
46
46
|
def convert(text)
|
47
47
|
validate_input!(text)
|
48
|
-
|
48
|
+
|
49
49
|
normalized = normalize_text(text)
|
50
50
|
return 0 if normalized.empty?
|
51
51
|
|
@@ -56,7 +56,7 @@ module StringToNumber
|
|
56
56
|
# Get or create parser instance and convert
|
57
57
|
parser = get_cached_instance(normalized)
|
58
58
|
result = parser.parse_optimized(normalized)
|
59
|
-
|
59
|
+
|
60
60
|
# Cache the result
|
61
61
|
cache_conversion(normalized, result)
|
62
62
|
result
|
@@ -68,7 +68,7 @@ module StringToNumber
|
|
68
68
|
@conversion_cache.clear
|
69
69
|
@cache_access_order.clear
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
@instance_mutex.synchronize do
|
73
73
|
@instance_cache.clear
|
74
74
|
end
|
@@ -115,7 +115,7 @@ module StringToNumber
|
|
115
115
|
oldest = @cache_access_order.shift
|
116
116
|
@conversion_cache.delete(oldest)
|
117
117
|
end
|
118
|
-
|
118
|
+
|
119
119
|
@conversion_cache[normalized_text] = result
|
120
120
|
@cache_access_order.push(normalized_text)
|
121
121
|
end
|
@@ -129,6 +129,7 @@ module StringToNumber
|
|
129
129
|
|
130
130
|
def calculate_hit_ratio
|
131
131
|
return 0.0 if @cache_access_order.empty?
|
132
|
+
|
132
133
|
@conversion_cache.size.to_f / @cache_access_order.size
|
133
134
|
end
|
134
135
|
end
|
@@ -147,7 +148,7 @@ module StringToNumber
|
|
147
148
|
# but with performance optimizations
|
148
149
|
def parse_optimized(text)
|
149
150
|
return 0 if text.nil? || text.empty?
|
150
|
-
|
151
|
+
|
151
152
|
# Direct lookup (fastest path)
|
152
153
|
return WORD_VALUES[text] if WORD_VALUES.key?(text)
|
153
154
|
|
@@ -162,12 +163,12 @@ module StringToNumber
|
|
162
163
|
# but with performance improvements
|
163
164
|
def extract_optimized(sentence, keys, detail: false)
|
164
165
|
return 0 if sentence.nil? || sentence.empty?
|
165
|
-
|
166
|
+
|
166
167
|
# Direct lookup
|
167
168
|
return WORD_VALUES[sentence] if WORD_VALUES.key?(sentence)
|
168
169
|
|
169
170
|
# Main pattern matching using pre-compiled regex
|
170
|
-
if result = MULTIPLIER_PATTERN.match(sentence)
|
171
|
+
if (result = MULTIPLIER_PATTERN.match(sentence))
|
171
172
|
# Remove matched portion
|
172
173
|
sentence = sentence.gsub(result[0], '') if result[0]
|
173
174
|
|
@@ -193,19 +194,19 @@ module StringToNumber
|
|
193
194
|
}
|
194
195
|
end
|
195
196
|
|
196
|
-
|
197
|
+
extract_optimized(sentence, keys) + (factor * multiple_of_ten)
|
197
198
|
|
198
199
|
# Quatre-vingt special handling
|
199
|
-
elsif m = QUATRE_VINGT_PATTERN.match(sentence)
|
200
|
+
elsif (m = QUATRE_VINGT_PATTERN.match(sentence))
|
200
201
|
normalize_str = m[1].tr(' ', '-')
|
201
202
|
normalize_str = normalize_str[0...-1] if normalize_str[-1] == 's'
|
202
203
|
|
203
204
|
sentence = sentence.gsub(m[0], '')
|
204
205
|
|
205
|
-
|
206
|
-
|
206
|
+
extract_optimized(sentence, keys) +
|
207
|
+
WORD_VALUES[normalize_str] + (WORD_VALUES[m[8]] || 0)
|
207
208
|
else
|
208
|
-
|
209
|
+
match_optimized(sentence)
|
209
210
|
end
|
210
211
|
end
|
211
212
|
|
@@ -213,8 +214,9 @@ module StringToNumber
|
|
213
214
|
def match_optimized(sentence)
|
214
215
|
return 0 if sentence.nil?
|
215
216
|
|
216
|
-
sentence.tr('-', ' ').split
|
217
|
+
sentence.tr('-', ' ').split.reverse.sum do |word|
|
217
218
|
next 0 if word == 'et'
|
219
|
+
|
218
220
|
WORD_VALUES[word] || (MULTIPLIERS[word] ? 10 * MULTIPLIERS[word] : 0)
|
219
221
|
end
|
220
222
|
end
|
@@ -227,4 +229,4 @@ module StringToNumber
|
|
227
229
|
end
|
228
230
|
end
|
229
231
|
end
|
230
|
-
end
|
232
|
+
end
|