ffi-icu 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|