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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2a47f5a65ad78641ecce2ffbbbe66ece2f1f176
4
- data.tar.gz: 6b6843b7b398a028e98b1254c22392bfa2ff7394
3
+ metadata.gz: 529b56ff2b576d863a9e7d221572ecce3c82575b
4
+ data.tar.gz: ba2279127152fc41ff8ee1c5663ed10c92809a80
5
5
  SHA512:
6
- metadata.gz: 70c720e7259427e54f4157f3ed0dafd868547a943a07348bbd91936927b64887b5b23fa73e1a29428a48266c70134e9872c846d2d1a947749cd19a00742aaec7
7
- data.tar.gz: 74a4c5a488584a3eee26b880c7af78a308fb4a3fc4d544cc4c3a903f67858e140f90e192bce1bafdfd73929fc4a10ec937b9f79391517962f58985abd46b2dfe
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
@@ -7,6 +7,7 @@ Bundler::GemHelper.install_tasks
7
7
  require 'rspec/core/rake_task'
8
8
  RSpec::Core::RakeTask.new(:spec) do |spec|
9
9
  spec.pattern = 'spec/**/*_spec.rb'
10
+ spec.rspec_opts = '--backtrace'
10
11
  end
11
12
 
12
13
  RSpec::Core::RakeTask.new(:rcov) do |spec|
@@ -39,4 +39,5 @@ require "ffi-icu/locale"
39
39
  require "ffi-icu/transliteration"
40
40
  require "ffi-icu/normalization"
41
41
  require "ffi-icu/break_iterator"
42
+ require "ffi-icu/number_formatting"
42
43
 
@@ -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
@@ -23,6 +23,8 @@ module ICU
23
23
  class Transliterator
24
24
 
25
25
  def initialize(id, rules = nil, direction = :forward)
26
+ warn 'the ffi-icu transliteration support is broken, see https://github.com/jarib/ffi-icu/issues/15'
27
+
26
28
  rules_length = 0
27
29
 
28
30
  if rules
@@ -1,3 +1,3 @@
1
1
  module ICU
2
- VERSION = "0.1.3"
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
@@ -7,4 +7,7 @@ require 'rspec'
7
7
 
8
8
  RSpec.configure do |config|
9
9
 
10
+ if ENV['TRAVIS']
11
+ config.filter_run_excluding broken: true
12
+ end
10
13
  end
@@ -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.3
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: