ffi-icu 0.1.3 → 0.1.4
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/README.md +21 -1
- data/Rakefile +1 -0
- data/lib/ffi-icu.rb +1 -0
- data/lib/ffi-icu/lib.rb +36 -0
- data/lib/ffi-icu/number_formatting.rb +130 -0
- data/lib/ffi-icu/transliteration.rb +2 -0
- data/lib/ffi-icu/version.rb +2 -2
- data/spec/number_formatting_spec.rb +71 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/transliteration_spec.rb +2 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 529b56ff2b576d863a9e7d221572ecce3c82575b
|
4
|
+
data.tar.gz: ba2279127152fc41ff8ee1c5663ed10c92809a80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b5e99bb6e1ed6d3309e0b024a73a38813feaf6b642070da6037baec8c4d99b7c6f3541ede0439cb90c69efaf286a39569bfcf2c7a354178cbac5907ab6286f3
|
7
|
+
data.tar.gz: fcba16704ab2146ae58c452d635a69125eca9bf358c2a5d5839ff9d011ee40201cd4530a4a6b6119c7d5276be24b2e6034b096df79e362f5a85b651ac9dad723
|
data/README.md
CHANGED
@@ -80,6 +80,27 @@ Examples:
|
|
80
80
|
iterator.to_a #=> [0, 4, 5, 7, 8, 9, 10, 18, 19]
|
81
81
|
```
|
82
82
|
|
83
|
+
Number/Currency Formatting
|
84
|
+
--------------------------
|
85
|
+
|
86
|
+
Examples:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
# class method interface
|
90
|
+
ICU::NumberFormatting.format_number("en", 1_000) #=> "1,000"
|
91
|
+
ICU::NumberFormatting.format_number("de-DE", 1234.56) #=> "1.234,56"
|
92
|
+
ICU::NumberFormatting.format_currency("en", 123.45, 'USD') #=> "$123.45"
|
93
|
+
ICU::NumberFormatting.format_percent("en", 0.53, 'USD') #=> "53%"
|
94
|
+
ICU::NumberFormatting.spell("en_US", 1_000) #=> "one thousand"
|
95
|
+
|
96
|
+
# reusable formatting objects
|
97
|
+
numf = ICU::NumberFormatting.create('fr-CA')
|
98
|
+
numf.format(1000) #=> "1 000"
|
99
|
+
|
100
|
+
curf = ICU::NumberFormatting.create('en-US', :currency)
|
101
|
+
curf.format(1234.56, 'USD') #=> "$1,234.56"
|
102
|
+
```
|
103
|
+
|
83
104
|
Tested on:
|
84
105
|
==========
|
85
106
|
|
@@ -97,7 +118,6 @@ TODO:
|
|
97
118
|
=====
|
98
119
|
|
99
120
|
* Useful ICU stuff:
|
100
|
-
- number formatting (decimal points, thousand separators, currency)
|
101
121
|
- date formatting
|
102
122
|
* Windows?!
|
103
123
|
|
data/Rakefile
CHANGED
data/lib/ffi-icu.rb
CHANGED
data/lib/ffi-icu/lib.rb
CHANGED
@@ -371,5 +371,41 @@ module ICU
|
|
371
371
|
attach_function :ubrk_following, "ubrk_following#{suffix}", [:pointer, :int32_t], :int32_t
|
372
372
|
attach_function :ubrk_isBoundary, "ubrk_isBoundary#{suffix}", [:pointer, :int32_t], :int32_t
|
373
373
|
|
374
|
+
enum :number_format_style, [
|
375
|
+
:pattern_decimal,
|
376
|
+
:decimal,
|
377
|
+
:currency,
|
378
|
+
:percent,
|
379
|
+
:scientific,
|
380
|
+
:spellout,
|
381
|
+
:ordinal,
|
382
|
+
:duration,
|
383
|
+
:numbering_system,
|
384
|
+
:pattern_rule_based,
|
385
|
+
:currency_iso,
|
386
|
+
:currency_plural,
|
387
|
+
:format_style_count,
|
388
|
+
:default,
|
389
|
+
:ignore
|
390
|
+
]
|
391
|
+
enum :number_format_attribute, [
|
392
|
+
:parse_int_only, :grouping_used, :decimal_always_show, :max_integer_digits,
|
393
|
+
:min_integer_digits, :integer_digits, :max_fraction_digits, :min_fraction_digits,
|
394
|
+
:fraction_digits, :multiplier, :grouping_size, :rounding_mode,
|
395
|
+
:rounding_increment, :format_width, :padding_position, :secondary_grouping_size,
|
396
|
+
:significant_digits_used, :min_significant_digits, :max_significant_digits, :lenient_parse
|
397
|
+
]
|
398
|
+
attach_function :unum_open, "unum_open#{suffix}", [:number_format_style, :pointer, :int32_t, :string, :pointer, :pointer ], :pointer
|
399
|
+
attach_function :unum_close, "unum_close#{suffix}", [:pointer], :void
|
400
|
+
attach_function :unum_format_int32, "unum_format#{suffix}", [:pointer, :int32_t, :pointer, :int32_t, :pointer, :pointer], :int32_t
|
401
|
+
attach_function :unum_format_int64, "unum_formatInt64#{suffix}", [:pointer, :int64_t, :pointer, :int32_t, :pointer, :pointer], :int32_t
|
402
|
+
attach_function :unum_format_double, "unum_formatDouble#{suffix}", [:pointer, :double, :pointer, :int32_t, :pointer, :pointer], :int32_t
|
403
|
+
begin
|
404
|
+
attach_function :unum_format_decimal, "unum_formatDecimal#{suffix}", [:pointer, :string, :int32_t, :pointer, :int32_t, :pointer, :pointer], :int32_t
|
405
|
+
rescue FFI::NotFoundError
|
406
|
+
end
|
407
|
+
attach_function :unum_format_currency, "unum_formatDoubleCurrency#{suffix}", [:pointer, :double, :pointer, :pointer, :int32_t, :pointer, :pointer], :int32_t
|
408
|
+
attach_function :unum_set_attribute, "unum_setAttribute#{suffix}", [:pointer, :number_format_attribute, :int32_t], :void
|
409
|
+
|
374
410
|
end # Lib
|
375
411
|
end # ICU
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
module ICU
|
4
|
+
module NumberFormatting
|
5
|
+
@default_options = {}
|
6
|
+
|
7
|
+
def self.create(locale, type = :decimal, options = {})
|
8
|
+
case type
|
9
|
+
when :currency
|
10
|
+
CurrencyFormatter.new(locale, options.delete(:style)).set_attributes(@default_options.merge(options))
|
11
|
+
else
|
12
|
+
NumberFormatter.new(locale, type).set_attributes(@default_options.merge(options))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.clear_default_options
|
17
|
+
@default_options.clear
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.set_default_options(options)
|
21
|
+
@default_options.merge!(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.format_number(locale, number, options = {})
|
25
|
+
create(locale, :decimal, options).format(number)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.format_percent(locale, number, options = {})
|
29
|
+
create(locale, :percent, options).format(number)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.format_currency(locale, number, currency, options = {})
|
33
|
+
create(locale, :currency, options).format(number, currency)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.spell(locale, number, options = {})
|
37
|
+
create(locale, :spellout, options).format(number)
|
38
|
+
end
|
39
|
+
|
40
|
+
class BaseFormatter
|
41
|
+
|
42
|
+
def set_attributes(options)
|
43
|
+
options.each { |key, value| Lib.unum_set_attribute(@f, key, value) }
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def make_formatter(type, locale)
|
50
|
+
ptr = Lib.check_error { | error| Lib.unum_open(type, FFI::MemoryPointer.new(4), 0, locale, FFI::MemoryPointer.new(4), error) }
|
51
|
+
FFI::AutoPointer.new(ptr, Lib.method(:unum_close))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class NumberFormatter < BaseFormatter
|
56
|
+
def initialize(locale, type = :decimal)
|
57
|
+
@f = make_formatter(type, locale)
|
58
|
+
end
|
59
|
+
|
60
|
+
def format(number)
|
61
|
+
needed_length = 0
|
62
|
+
out_ptr = UCharPointer.new(needed_length)
|
63
|
+
|
64
|
+
retried = false
|
65
|
+
|
66
|
+
begin
|
67
|
+
Lib.check_error do |error|
|
68
|
+
case number
|
69
|
+
when Float
|
70
|
+
needed_length = Lib.unum_format_double(@f, number, out_ptr, needed_length, nil, error)
|
71
|
+
when Fixnum
|
72
|
+
needed_length = Lib.unum_format_int32(@f, number, out_ptr, needed_length, nil, error)
|
73
|
+
when BigDecimal
|
74
|
+
string_version = number.to_s('F')
|
75
|
+
if Lib.respond_to? :unum_format_decimal
|
76
|
+
needed_length = Lib.unum_format_decimal(@f, string_version, string_version.bytesize, out_ptr, needed_length, nil, error)
|
77
|
+
else
|
78
|
+
needed_length = Lib.unum_format_double(@f, number.to_f, out_ptr, needed_length, nil, error)
|
79
|
+
end
|
80
|
+
when Bignum
|
81
|
+
needed_length = Lib.unum_format_int64(@f, number, out_ptr, needed_length, nil, error)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
out_ptr.string
|
85
|
+
rescue BufferOverflowError
|
86
|
+
raise BufferOverflowError, "needed: #{needed_length}" if retried
|
87
|
+
out_ptr = out_ptr.resized_to needed_length
|
88
|
+
retried = true
|
89
|
+
retry
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end # NumberFormatter
|
93
|
+
|
94
|
+
class CurrencyFormatter < BaseFormatter
|
95
|
+
def initialize(locale, style = :default)
|
96
|
+
if %w(iso plural).include?((style || '').to_s)
|
97
|
+
if Lib.version.to_a.first >= 53
|
98
|
+
style = "currency_#{style}".to_sym
|
99
|
+
else
|
100
|
+
fail "Your version of ICU (#{Lib.version.to_a.join('.')}) does not support #{style} currency formatting (supported only in version >= 53)"
|
101
|
+
end
|
102
|
+
elsif style && style.to_sym != :default
|
103
|
+
fail "The ffi-icu ruby gem does not support :#{default} currency formatting (only :default, :iso, and :plural)"
|
104
|
+
else
|
105
|
+
style = :currency
|
106
|
+
end
|
107
|
+
@f = make_formatter(style, locale)
|
108
|
+
end
|
109
|
+
|
110
|
+
def format(number, currency)
|
111
|
+
needed_length = 0
|
112
|
+
out_ptr = UCharPointer.new(needed_length)
|
113
|
+
|
114
|
+
retried = false
|
115
|
+
|
116
|
+
begin
|
117
|
+
Lib.check_error do |error|
|
118
|
+
needed_length = Lib.unum_format_currency(@f, number, UCharPointer.from_string(currency, 3), out_ptr, needed_length, nil, error)
|
119
|
+
end
|
120
|
+
out_ptr.string
|
121
|
+
rescue BufferOverflowError
|
122
|
+
raise BufferOverflowError, "needed: #{needed_length}" if retried
|
123
|
+
out_ptr = out_ptr.resized_to needed_length
|
124
|
+
retried = true
|
125
|
+
retry
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end # CurrencyFormatter
|
129
|
+
end # Formatting
|
130
|
+
end # ICU
|
data/lib/ffi-icu/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module ICU
|
2
|
-
VERSION = "0.1.
|
3
|
-
end
|
2
|
+
VERSION = "0.1.4"
|
3
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
module ICU
|
6
|
+
module NumberFormatting
|
7
|
+
describe 'NumberFormatting' do
|
8
|
+
it 'should format a simple integer' do
|
9
|
+
NumberFormatting.format_number("en", 1).should == "1"
|
10
|
+
NumberFormatting.format_number("en", 1_000).should == "1,000"
|
11
|
+
NumberFormatting.format_number("de-DE", 1_000_000).should == "1.000.000"
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should format a float' do
|
15
|
+
NumberFormatting.format_number("en", 1.0).should == "1"
|
16
|
+
NumberFormatting.format_number("en", 1.123).should == "1.123"
|
17
|
+
NumberFormatting.format_number("en", 1_000.1238).should == "1,000.124"
|
18
|
+
NumberFormatting.format_number("en", 1_000.1238, max_fraction_digits: 4).should == "1,000.1238"
|
19
|
+
NumberFormatting.set_default_options(fraction_digits: 5)
|
20
|
+
NumberFormatting.format_number("en", 1_000.1238).should == "1,000.12380"
|
21
|
+
NumberFormatting.clear_default_options
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should format a decimal' do
|
25
|
+
NumberFormatting.format_number("en", BigDecimal.new("10000.123")).should == "10,000.123"
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should format a currency' do
|
29
|
+
NumberFormatting.format_currency("en", 123.45, 'USD').should == "$123.45"
|
30
|
+
NumberFormatting.format_currency("en", 123_123.45, 'USD').should == "$123,123.45"
|
31
|
+
NumberFormatting.format_currency("de-DE", 123_123.45, 'EUR').should == "123.123,45\u{A0}€"
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should format a percent' do
|
35
|
+
NumberFormatting.format_percent("en", 1.1).should == "110%"
|
36
|
+
NumberFormatting.format_percent("da", 0.15).should == "15\u{A0}%"
|
37
|
+
NumberFormatting.format_percent("da", -0.1545, max_fraction_digits: 10).should == "-15,45\u{A0}%"
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should spell numbers' do
|
41
|
+
NumberFormatting.spell("en_US", 1_000).should == 'one thousand'
|
42
|
+
NumberFormatting.spell("de-DE", 123.456).should == "ein\u{AD}hundert\u{AD}drei\u{AD}und\u{AD}zwanzig Komma vier fünf sechs"
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should be able to re-use number formatter objects' do
|
46
|
+
numf = NumberFormatting.create('fr-CA')
|
47
|
+
numf.format(1_000).should == "1\u{A0}000"
|
48
|
+
numf.format(1_000.123).should == "1\u{A0}000,123"
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should be able to re-use currency formatter objects' do
|
52
|
+
curf = NumberFormatting.create('en-US', :currency)
|
53
|
+
curf.format(1_000.12, 'USD').should == "$1,000.12"
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should allow for various styles of currency formatting if the version is new enough' do
|
57
|
+
if ICU::Lib.version.to_a.first >= 53
|
58
|
+
curf = NumberFormatting.create('en-US', :currency, style: :iso)
|
59
|
+
curf.format(1_000.12, 'USD').should == "USD1,000.12"
|
60
|
+
curf = NumberFormatting.create('en-US', :currency, style: :plural)
|
61
|
+
curf.format(1_000.12, 'USD').should == "1,000.12 US dollars"
|
62
|
+
expect { NumberFormatting.create('en-US', :currency, style: :fake) }.to raise_error(StandardError)
|
63
|
+
else
|
64
|
+
curf = NumberFormatting.create('en-US', :currency, style: :default)
|
65
|
+
curf.format(1_000.12, 'USD').should == '$1,000.12'
|
66
|
+
expect { NumberFormatting.create('en-US', :currency, style: :iso) }.to raise_error(StandardError)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end # NumberFormatting
|
71
|
+
end # ICU
|
data/spec/spec_helper.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
module ICU
|
6
|
-
describe Transliteration::Transliterator do
|
6
|
+
describe Transliteration::Transliterator, broken: true do
|
7
7
|
def transliterator_for(*args)
|
8
8
|
Transliteration::Transliterator.new(*args)
|
9
9
|
end
|
@@ -21,7 +21,7 @@ module ICU
|
|
21
21
|
end
|
22
22
|
end # Transliterator
|
23
23
|
|
24
|
-
describe Transliteration do
|
24
|
+
describe Transliteration, broken: true do
|
25
25
|
it "should provide a list of available ids" do
|
26
26
|
ids = ICU::Transliteration.available_ids
|
27
27
|
ids.should be_kind_of(Array)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-icu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jari Bakken
|
@@ -86,6 +86,7 @@ files:
|
|
86
86
|
- lib/ffi-icu/lib/util.rb
|
87
87
|
- lib/ffi-icu/locale.rb
|
88
88
|
- lib/ffi-icu/normalization.rb
|
89
|
+
- lib/ffi-icu/number_formatting.rb
|
89
90
|
- lib/ffi-icu/transliteration.rb
|
90
91
|
- lib/ffi-icu/uchar.rb
|
91
92
|
- lib/ffi-icu/version.rb
|
@@ -96,6 +97,7 @@ files:
|
|
96
97
|
- spec/lib_spec.rb
|
97
98
|
- spec/locale_spec.rb
|
98
99
|
- spec/normalization_spec.rb
|
100
|
+
- spec/number_formatting_spec.rb
|
99
101
|
- spec/spec.opts
|
100
102
|
- spec/spec_helper.rb
|
101
103
|
- spec/transliteration_spec.rb
|
@@ -133,8 +135,8 @@ test_files:
|
|
133
135
|
- spec/lib_spec.rb
|
134
136
|
- spec/locale_spec.rb
|
135
137
|
- spec/normalization_spec.rb
|
138
|
+
- spec/number_formatting_spec.rb
|
136
139
|
- spec/spec.opts
|
137
140
|
- spec/spec_helper.rb
|
138
141
|
- spec/transliteration_spec.rb
|
139
142
|
- spec/uchar_spec.rb
|
140
|
-
has_rdoc:
|