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 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: