currency-in-words 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ gem "activesupport", ">= 3.1"
7
+ gem "actionpack", ">= 3.1"
8
+
9
+ # Add dependencies to develop your gem here.
10
+ # Include everything needed to run rake, tests, features, etc.
11
+ group :development do
12
+ gem "rake", "0.8.7"
13
+ gem "shoulda", ">= 0"
14
+ gem "bundler", "~> 1.0.0"
15
+ gem "jeweler", "~> 1.6.4"
16
+ gem "rcov", ">= 0"
17
+ end
@@ -0,0 +1,57 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ actionpack (3.1.1)
5
+ activemodel (= 3.1.1)
6
+ activesupport (= 3.1.1)
7
+ builder (~> 3.0.0)
8
+ erubis (~> 2.7.0)
9
+ i18n (~> 0.6)
10
+ rack (~> 1.3.2)
11
+ rack-cache (~> 1.1)
12
+ rack-mount (~> 0.8.2)
13
+ rack-test (~> 0.6.1)
14
+ sprockets (~> 2.0.2)
15
+ activemodel (3.1.1)
16
+ activesupport (= 3.1.1)
17
+ builder (~> 3.0.0)
18
+ i18n (~> 0.6)
19
+ activesupport (3.1.1)
20
+ multi_json (~> 1.0)
21
+ builder (3.0.0)
22
+ erubis (2.7.0)
23
+ git (1.2.5)
24
+ hike (1.2.1)
25
+ i18n (0.6.0)
26
+ jeweler (1.6.4)
27
+ bundler (~> 1.0)
28
+ git (>= 1.2.5)
29
+ rake
30
+ multi_json (1.0.3)
31
+ rack (1.3.4)
32
+ rack-cache (1.1)
33
+ rack (>= 0.4)
34
+ rack-mount (0.8.3)
35
+ rack (>= 1.0.0)
36
+ rack-test (0.6.1)
37
+ rack (>= 1.0)
38
+ rake (0.8.7)
39
+ rcov (0.9.11)
40
+ shoulda (2.11.3)
41
+ sprockets (2.0.2)
42
+ hike (~> 1.2)
43
+ rack (~> 1.0)
44
+ tilt (~> 1.1, != 1.3.0)
45
+ tilt (1.3.3)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ actionpack (>= 3.1)
52
+ activesupport (>= 3.1)
53
+ bundler (~> 1.0.0)
54
+ jeweler (~> 1.6.4)
55
+ rake (= 0.8.7)
56
+ rcov
57
+ shoulda
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Bruno Carrere
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,153 @@
1
+ = currency-in-words
2
+
3
+ Provides a view helper for Rails that displays a currency amount in words (eg. <tt>number_to_currency_in_words(100)</tt> \=> 'one hundred dollars').
4
+
5
+ This helper comes with two supported locales (English and French) but it is possible and easy to implement your own (see "Implementing a new locale").
6
+
7
+ == Installation
8
+
9
+ Add the gem to your Gemfile.
10
+
11
+ gem 'currency-in-words'
12
+
13
+ == Usage
14
+
15
+ ==== General Options
16
+ * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
17
+ * <tt>:currency</tt> - Sets the denomination of the currency (defaults to +:default+ currency for the locale or "dollar" if not set).
18
+ * <tt>:connector</tt> - Sets the connector between integer part and decimal part of the currency (defaults to ", ").
19
+ * <tt>:format</tt> - Sets the format for non-negative numbers (defaults to "%n").
20
+ Field is <tt>%n</tt> for the currency amount in words.
21
+ * <tt>:negative_format</tt> - Sets the format for negative numbers (defaults to prepending "minus" to the number in words 'minus %n').
22
+ Field is <tt>%n</tt> for the currency amount in words (same as format).
23
+
24
+ ===== Examples
25
+ [<tt>number_to_currency_in_words(123456.50)</tt>]
26
+ \=> one hundred and twenty-three thousand four hundred and fifty-six dollars, fifty cents
27
+ [<tt>number_to_currency_in_words(123456.50, connector: ' and ')</tt>]
28
+ \=> one hundred and twenty-three thousand four hundred and fifty-six dollars and fifty cents
29
+ [<tt>number_to_currency_in_words(123456.50, locale: :fr, connector: ' et ')</tt>]
30
+ \=> cent vingt-trois mille quatre cent cinquante-six dollars et cinquante cents
31
+ [<tt>number_to_currency_in_words(80300.80, locale: :fr, currency: :euro, connector: ' et ')</tt>]
32
+ \=> quatre-vingt mille trois cents euros et quatre-vingts centimes
33
+
34
+
35
+ ==== Specifics options for locale :en
36
+ * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to +false+)
37
+ * <tt>:skip_and</tt> - Skips the 'and' part in number - US (defaults to +false+)
38
+
39
+ ===== Examples
40
+ [<tt>number_to_currency(201201201.201, delimiter: true)</tt>]
41
+ \=> two hundred and one million, two hundred and one thousand, two hundred and one dollars, twenty cents
42
+ [<tt>number_to_currency(201201201.201, delimiter: true, skip_and: true)</tt>]
43
+ \=> two hundred one million, two hundred one thousand, two hundred one dollars, twenty cents
44
+
45
+ == Options settings
46
+
47
+ Modify +config/locales/xx.yml+ to set default options and add currencies for locales.
48
+
49
+ === Example for locale :en
50
+
51
+ en:
52
+ number:
53
+ currency_in_words:
54
+ connector: ' and '
55
+ negative_format: '(%n)'
56
+ skip_and: true
57
+ delimiter: true
58
+ currencies:
59
+ euro:
60
+ unit:
61
+ one: 'euro'
62
+ many: 'euros'
63
+ decimal:
64
+ one: 'cent'
65
+ many: 'cents'
66
+ pound:
67
+ unit:
68
+ one: 'pound'
69
+ many: 'pounds'
70
+ decimal:
71
+ one: 'penny'
72
+ many: 'pence'
73
+
74
+
75
+ === Example for locale :fr
76
+
77
+ fr:
78
+ number:
79
+ currency_in_words:
80
+ format: '%n'
81
+ negative_format: 'moins %n'
82
+ connector: ' et '
83
+ currencies:
84
+ default: # euro will be the default currency for locale :fr
85
+ unit:
86
+ one: 'euro'
87
+ many: 'euros' # can be omit
88
+ more: "d'euros" # can be omit (specific to :fr, eg. 'un million d'euros')
89
+ decimal:
90
+ one: 'centime'
91
+ many: 'centimes' # can be omit
92
+ dollar:
93
+ unit:
94
+ one: 'dollar'
95
+ many: 'dollars'
96
+ decimal:
97
+ one: 'cent'
98
+ many: 'cents'
99
+ livre:
100
+ unit:
101
+ feminine: true # specific to :fr, eg. 'un euro' but 'une livre'
102
+ one: 'livre'
103
+ many: 'livres'
104
+ decimal:
105
+ one: 'penny'
106
+ many: 'pence'
107
+
108
+ == Implementing a new locale
109
+
110
+ +CurrencyInWords+ expects a _texterizer_ by locale. A _texterizer_ is a simple class that returns the amount in words for the currency and the given locale (eg. a class +EnTexterizer+ for English locale, a class +FrTexterizer+ for French locale). So, for example, if you want to support Italian, you have to implement an +ItTexterizer+ class for the module +CurrencyInWords+.
111
+
112
+ A _texterizer_ :
113
+ * must provide a <b>+texterize+</b> method
114
+ * must return a <b>+string+</b>
115
+
116
+ That's all.
117
+
118
+ Example :
119
+
120
+ class CurrencyInWords::ItTexterizer
121
+ def texterize(context)
122
+ ../..
123
+ end
124
+ end
125
+
126
+ Parameter +context+ provides :
127
+ * an array that includes the integer part and the decimal part of the number (+context.number_parts+)
128
+ * a hash that includes the options passed to +number_to_currency_in_words+ (+context.options+).
129
+
130
+ See the source code of +EnTexterizer+ for an example (+FrTexterizer+ is perhaps too specific but +EnTexterizer+ can be a starting point to implement your own _texterizer_).
131
+
132
+ If you need more options for your language, simply create yours and use them (options +:delimiter+ and +:skip_and+ are for example specifics to +EnTexterizer+ while +:feminine+ is specific to +FrTexterizer+).
133
+
134
+ == ToDo
135
+
136
+ * clean up and push tests
137
+ * add comments in source code
138
+
139
+ == Contributing to currency-in-words
140
+
141
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
142
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
143
+ * Fork the project
144
+ * Start a feature/bugfix branch
145
+ * Commit and push until you are happy with your contribution
146
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
147
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
148
+
149
+ == Copyright
150
+
151
+ Copyright (c) 2011 Bruno Carrere. See LICENSE.txt for
152
+ further details.
153
+
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "currency-in-words"
18
+ gem.homepage = "http://github.com/bcarrere/currency-in-words"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{View helper for Rails that displays a currency amount in words (eg. 'one hundred dollars')}
21
+ gem.description = %Q{Provides number_to_currency_in_words helper that displays a currency amount in words (eg. 'one hundred dollars')}
22
+ gem.email = "bruno@carrere.cc"
23
+ gem.authors = ["Bruno Carrere"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ #require 'rake/testtask'
29
+ #Rake::TestTask.new(:test) do |test|
30
+ # test.libs << 'lib' << 'test'
31
+ # test.pattern = 'test/**/test_*.rb'
32
+ # test.verbose = true
33
+ #end
34
+
35
+ #require 'rcov/rcovtask'
36
+ #Rcov::RcovTask.new do |test|
37
+ # test.libs << 'test'
38
+ # test.pattern = 'test/**/test_*.rb'
39
+ # test.verbose = true
40
+ # test.rcov_opts << '--exclude "gems/*"'
41
+ #end
42
+
43
+ #task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "currency-in-words #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,66 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "currency-in-words"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Bruno Carrere"]
12
+ s.date = "2011-10-16"
13
+ s.description = "Provides number_to_currency_in_words helper that displays a currency amount in words (eg. 'one hundred dollars')"
14
+ s.email = "bruno@carrere.cc"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "currency-in-words.gemspec",
28
+ "lib/currency-in-words.rb"
29
+ ]
30
+ s.homepage = "http://github.com/bcarrere/currency-in-words"
31
+ s.licenses = ["MIT"]
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = "1.8.10"
34
+ s.summary = "View helper for Rails that displays a currency amount in words (eg. 'one hundred dollars')"
35
+
36
+ if s.respond_to? :specification_version then
37
+ s.specification_version = 3
38
+
39
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
40
+ s.add_runtime_dependency(%q<activesupport>, [">= 3.1"])
41
+ s.add_runtime_dependency(%q<actionpack>, [">= 3.1"])
42
+ s.add_development_dependency(%q<rake>, ["= 0.8.7"])
43
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
44
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
45
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
46
+ s.add_development_dependency(%q<rcov>, [">= 0"])
47
+ else
48
+ s.add_dependency(%q<activesupport>, [">= 3.1"])
49
+ s.add_dependency(%q<actionpack>, [">= 3.1"])
50
+ s.add_dependency(%q<rake>, ["= 0.8.7"])
51
+ s.add_dependency(%q<shoulda>, [">= 0"])
52
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
53
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
54
+ s.add_dependency(%q<rcov>, [">= 0"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<activesupport>, [">= 3.1"])
58
+ s.add_dependency(%q<actionpack>, [">= 3.1"])
59
+ s.add_dependency(%q<rake>, ["= 0.8.7"])
60
+ s.add_dependency(%q<shoulda>, [">= 0"])
61
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
62
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
63
+ s.add_dependency(%q<rcov>, [">= 0"])
64
+ end
65
+ end
66
+
@@ -0,0 +1,318 @@
1
+ # encoding: utf-8
2
+ module CurrencyInWords
3
+ ActionView::Helpers::NumberHelper.class_eval do
4
+
5
+ DEFAULT_CURRENCY_IN_WORDS_VALUES = {:currencies=>{:default=>{:unit=>{:one=>'dollar',:many=>'dollars'},
6
+ :decimal=>{:one=>'cent',:many=>'cents'}}},
7
+ :connector=>', ',:format=>'%n',:negative_format=>'minus %n'}
8
+
9
+ # Formats a +number+ into a currency string (e.g., 'one hundred dollars'). You can customize the
10
+ # format in the +options+ hash.
11
+ #
12
+ # === Options for all locales
13
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
14
+ # * <tt>:currency</tt> - Sets the denomination of the currency (defaults to :default currency for the locale or "dollar" if not set).
15
+ # * <tt>:connector</tt> - Sets the connector between integer part and decimal part of the currency (defaults to ", ").
16
+ # * <tt>:format</tt> - Sets the format for non-negative numbers (defaults to "%n").
17
+ # Field is <tt>%n</tt> for the currency amount in words.
18
+ # * <tt>:negative_format</tt> - Sets the format for negative numbers (defaults to prepending "minus" to the number in words).
19
+ # Field is <tt>%n</tt> for the currency amount in words (same as format).
20
+ #
21
+ # ==== Examples
22
+ # [<tt>number_to_currency_in_words(123456.50)</tt>]
23
+ # \=> one hundred and twenty-three thousand four hundred and fifty-six dollars, fifty cents
24
+ # [<tt>number_to_currency_in_words(123456.50, :connector => ' and ')</tt>]
25
+ # \=> one hundred and twenty-three thousand four hundred and fifty-six dollars and fifty cents
26
+ # [<tt>number_to_currency_in_words(123456.50, :locale => :fr, :connector => ' et ')</tt>]
27
+ # \=> cent vingt-trois mille quatre cent cinquante-six dollars et cinquante cents
28
+ # [<tt>number_to_currency_in_words(80300.80, :locale => :fr, :currency => :euro, :connector => ' et ')</tt>]
29
+ # \=> quatre-vingt mille trois cents euros et quatre-vingts centimes
30
+ #
31
+ # === Options only available for :en locale
32
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to false).
33
+ # * <tt>:skip_and</tt> - Skips the 'and' part in number - US (defaults to false).
34
+ #
35
+ # ==== Examples
36
+ # [<tt>number_to_currency(201201201.201, :delimiter => true)</tt>]
37
+ # \=> two hundred and one million, two hundred and one thousand, two hundred and one dollars, twenty cents
38
+ # [<tt>number_to_currency(201201201.201, :delimiter => true, :skip_and => true)</tt>]
39
+ # \=> two hundred one million, two hundred one thousand, two hundred one dollars, twenty cents
40
+ def number_to_currency_in_words number, options = {}
41
+
42
+ options.symbolize_keys!
43
+
44
+ currency_in_words = I18n.translate(:'number.currency_in_words', :locale => options[:locale], :default => {})
45
+
46
+ defaults = DEFAULT_CURRENCY_IN_WORDS_VALUES.merge(currency_in_words)
47
+
48
+ options = defaults.merge!(options)
49
+
50
+ unless options[:currencies].has_key?(:default)
51
+ options[:currencies].merge!(DEFAULT_CURRENCY_IN_WORDS_VALUES[:currencies])
52
+ end
53
+
54
+ format = options.delete(:format)
55
+ currency = options.delete(:currency)
56
+ currencies = options.delete(:currencies)
57
+ options[:currency] = currency && currencies.has_key?(currency) ? currencies[currency] : currencies[:default]
58
+ options[:locale] ||= I18n.default_locale
59
+
60
+ if number.to_f < 0
61
+ format = options.delete(:negative_format)
62
+ number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
63
+ end
64
+
65
+ options_precision = {
66
+ :precision => 2,
67
+ :delimiter => '',
68
+ :significant => false,
69
+ :strip_insignificant_zeros => false,
70
+ :separator => '.',
71
+ :raise => true
72
+ }
73
+
74
+ begin
75
+ rounded_number = number_with_precision(number, options_precision)
76
+ rescue ActionView::Helpers::NumberHelper::InvalidNumberError => e
77
+ if options[:raise]
78
+ raise
79
+ else
80
+ rounded_number = format.gsub(/%n/, e.number)
81
+ return e.number.to_s.html_safe? ? rounded_number.html_safe : rounded_number
82
+ end
83
+ end
84
+
85
+ begin
86
+ klass = "CurrencyInWords::#{options[:locale].to_s.capitalize}Texterizer".constantize
87
+ rescue NameError
88
+ if options[:raise]
89
+ raise NameError, "Implement a class #{options[:locale].to_s.capitalize}Texterizer to support this locale, please."
90
+ else
91
+ klass = EnTexterizer
92
+ end
93
+ end
94
+
95
+ number_parts = rounded_number.split(options_precision[:separator]).map(&:to_i)
96
+ texterizer = CurrencyInWords::Texterizer.new(klass.new, number_parts, options)
97
+ texterized_number = texterizer.texterize
98
+ format.gsub(/%n/, texterized_number).html_safe
99
+ end
100
+ end
101
+
102
+ ####
103
+ # :nodoc: all
104
+ # This is the context class for texterizers
105
+ class Texterizer
106
+ attr_reader :number_parts, :options, :texterizer
107
+
108
+ def initialize texterizer, splitted_number, options = {}
109
+ @texterizer = texterizer
110
+ @number_parts = splitted_number
111
+ @options = options
112
+ end
113
+
114
+ def texterize
115
+ if @texterizer.respond_to?('texterize')
116
+ texterized_number = @texterizer.texterize self
117
+ if texterized_number.is_a?(String)
118
+ return texterized_number
119
+ else
120
+ raise TypeError, "a texterizer must return a String" if @options[:raise]
121
+ end
122
+ else
123
+ raise NoMethodError, "a texterizer must provide a 'texterize' method" if @options[:raise]
124
+ end
125
+ # Fallback on EnTexterizer
126
+ unless @texterizer.instance_of?(EnTexterizer)
127
+ @texterizer = EnTexterizer.new
128
+ self.texterize
129
+ else
130
+ raise RuntimeError, "you should use the option ':raise => true' to see what goes wrong"
131
+ end
132
+ end
133
+ end
134
+
135
+ ####
136
+ # :nodoc: all
137
+ # This is the strategy class for English language
138
+ class EnTexterizer
139
+
140
+ def texterize context
141
+ int_part, dec_part = context.number_parts
142
+ connector = context.options[:connector]
143
+ int_unit_one = context.options[:currency][:unit][:one]
144
+ int_unit_many = context.options[:currency][:unit][:many]
145
+ dec_unit_one = context.options[:currency][:decimal][:one]
146
+ dec_unit_many = context.options[:currency][:decimal][:many]
147
+ @skip_and = context.options[:skip_and] || false
148
+ @delimiter = context.options[:delimiter] || false
149
+
150
+ unless int_unit_many
151
+ int_unit_many = int_unit_one+'s'
152
+ end
153
+ unless dec_unit_many
154
+ dec_unit_many = dec_unit_one+'s'
155
+ end
156
+
157
+ int_unit = int_part > 1 ? int_unit_many : int_unit_one
158
+ dec_unit = dec_part > 1 ? dec_unit_many : dec_unit_one
159
+
160
+ texterized_int_part = (texterize_by_group(int_part).compact << int_unit).flatten.join(' ')
161
+ texterized_dec_part = (texterize_by_group(dec_part).compact << dec_unit).flatten.join(' ')
162
+
163
+ if dec_part.zero?
164
+ texterized_int_part
165
+ else
166
+ texterized_int_part << connector << texterized_dec_part
167
+ end
168
+ end
169
+
170
+ private
171
+
172
+ # :nodoc: all
173
+ A = %w(zero one two three four five six seven eight nine)
174
+ B = %w(ten eleven twelve thirteen fourteen fifteen sixteen
175
+ seventeen eighteen nineteen)
176
+ C = [nil,nil,'twenty','thirty','forty','fifty','sixty','seventy',
177
+ 'eighty','ninety']
178
+ D = [nil,'thousand','million','billion','trillion','quadrillion']
179
+
180
+ def texterize_by_group number, group=0
181
+ return [under_100(number)] if number.zero?
182
+ q,r = number.divmod 1000
183
+ arr = texterize_by_group(q, group+1) if q > 0
184
+ if r.zero?
185
+ arr.last.chop! if group.zero? && @delimiter && arr.last.respond_to?('chop!')
186
+ arr
187
+ else
188
+ arr = arr.to_a
189
+ unless group.zero?
190
+ arr << under_1000(r)
191
+ arr << D[group] + (',' if @delimiter).to_s
192
+ else
193
+ arr.last.chop! if @delimiter && r < 100 && arr.last.respond_to?('chop!')
194
+ arr << 'and' if !@skip_and && q > 0 && r < 100
195
+ arr << under_1000(r)
196
+ end
197
+ end
198
+ end
199
+
200
+ def under_1000 number
201
+ q,r = number.divmod 100
202
+ arr = ([A[q]] << 'hundred' + (' and' unless @skip_and || r.zero?).to_s) if q > 0
203
+ r.zero? ? arr : arr.to_a << under_100(r)
204
+ end
205
+
206
+ def under_100 number
207
+ case number
208
+ when 0..9 then A[number]
209
+ when 10..19 then B[number - 10]
210
+ else
211
+ q,r = number.divmod 10
212
+ C[q] + ('-' + A[r] unless r.zero?).to_s
213
+ end
214
+ end
215
+ end
216
+
217
+ ####
218
+ # :nodoc: all
219
+ # This is the strategy class for French language
220
+ class FrTexterizer
221
+
222
+ def texterize context
223
+ int_part, dec_part = context.number_parts
224
+ connector = context.options[:connector]
225
+ int_unit_one = context.options[:currency][:unit][:one]
226
+ int_unit_many = context.options[:currency][:unit][:many]
227
+ int_unit_more = context.options[:currency][:unit][:more]
228
+ dec_unit_one = context.options[:currency][:decimal][:one]
229
+ dec_unit_many = context.options[:currency][:decimal][:many]
230
+
231
+ unless int_unit_more
232
+ unless int_unit_many
233
+ int_unit_many = int_unit_one+'s'
234
+ end
235
+ int_unit_more = if int_unit_many.start_with?("a","e","i","o","u")
236
+ "d'"+int_unit_many
237
+ else
238
+ "de "+int_unit_many
239
+ end
240
+ end
241
+ unless dec_unit_many
242
+ dec_unit_many = dec_unit_one+'s'
243
+ end
244
+
245
+ int_unit = if int_part > 1
246
+ (int_part % 10**6).zero? ? int_unit_more : int_unit_many
247
+ else
248
+ int_unit_one
249
+ end
250
+ dec_unit = dec_part > 1 ? dec_unit_many : dec_unit_one
251
+
252
+ feminize = context.options[:currency][:unit][:feminine] || false
253
+ texterized_int_part = (texterize_by_group(int_part, 0, feminize).compact << int_unit).flatten.join(' ')
254
+
255
+ feminize = context.options[:currency][:decimal][:feminine] || false
256
+ texterized_dec_part = (texterize_by_group(dec_part, 0, feminize).compact << dec_unit).flatten.join(' ')
257
+
258
+ if dec_part.zero?
259
+ texterized_int_part
260
+ else
261
+ texterized_int_part << connector << texterized_dec_part
262
+ end
263
+ end
264
+
265
+ private
266
+
267
+ # :nodoc: all
268
+ A = %w(z&eacute;ro un deux trois quatre cinq six sept huit neuf)
269
+ B = %w(dix onze douze treize quatorze quinze seize dix-sept dix-huit dix-neuf)
270
+ C = [nil,nil,'vingt','trente','quarante','cinquante',
271
+ 'soixante','soixante','quatre-vingt','quatre-vingt']
272
+ D = [nil,'mille','million','milliard','billion']
273
+
274
+ def texterize_by_group number, group, feminine
275
+ return [under_100(number, 0, feminine)] if number.zero?
276
+ q,r = number.divmod 1000
277
+ arr = texterize_by_group(q, group+1, feminine) if q > 0
278
+ if r.zero?
279
+ arr
280
+ else
281
+ arr = arr.to_a
282
+ arr << under_1000(r, group, feminine)
283
+ group.zero? ? arr : arr << (D[group] + ('s' if r > 1 && group != 1).to_s)
284
+ end
285
+ end
286
+
287
+ def under_1000 number, group, feminine
288
+ q,r = number.divmod 100
289
+ arr = (q > 1 ? [A[q]] : []) << (r == 0 && q > 1 && group != 1 ? 'cents' : 'cent') if q > 0
290
+ r.zero? ? arr : (r == 1 && q == 0 && group == 1 ? nil : arr.to_a << under_100(r, group, feminine))
291
+ end
292
+
293
+ def under_100 number, group, feminine
294
+ feminine = (feminine and group.zero?)
295
+ case number
296
+ when 0..9 then A[number] + ('e' if feminine && number == 1).to_s
297
+ when 10..19 then B[number - 10]
298
+ else
299
+ q,r = number.divmod 10
300
+ case r
301
+ when 1
302
+ case q
303
+ when 7 then C[q] + ('-et-' + B[r]).to_s
304
+ when 8 then C[q] + ('-' + A[r]).to_s + ('e' if feminine).to_s
305
+ when 9 then C[q] + ('-' + B[r]).to_s
306
+ else C[q] + ('-et-' + A[r]).to_s + ('e' if feminine).to_s
307
+ end
308
+ else
309
+ if [7,9].include?(q)
310
+ C[q] + ('-' + B[r]).to_s
311
+ else
312
+ C[q] + ('-' + A[r] if not r.zero?).to_s + ('s' if number == 80 && group != 1).to_s
313
+ end
314
+ end
315
+ end
316
+ end
317
+ end
318
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: currency-in-words
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bruno Carrere
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: &2153019480 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '3.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2153019480
25
+ - !ruby/object:Gem::Dependency
26
+ name: actionpack
27
+ requirement: &2153018480 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '3.1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2153018480
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &2153017100 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - =
42
+ - !ruby/object:Gem::Version
43
+ version: 0.8.7
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2153017100
47
+ - !ruby/object:Gem::Dependency
48
+ name: shoulda
49
+ requirement: &2153015740 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2153015740
58
+ - !ruby/object:Gem::Dependency
59
+ name: bundler
60
+ requirement: &2153014840 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 1.0.0
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2153014840
69
+ - !ruby/object:Gem::Dependency
70
+ name: jeweler
71
+ requirement: &2153013760 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 1.6.4
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *2153013760
80
+ - !ruby/object:Gem::Dependency
81
+ name: rcov
82
+ requirement: &2153012640 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *2153012640
91
+ description: Provides number_to_currency_in_words helper that displays a currency
92
+ amount in words (eg. 'one hundred dollars')
93
+ email: bruno@carrere.cc
94
+ executables: []
95
+ extensions: []
96
+ extra_rdoc_files:
97
+ - LICENSE.txt
98
+ - README.rdoc
99
+ files:
100
+ - .document
101
+ - Gemfile
102
+ - Gemfile.lock
103
+ - LICENSE.txt
104
+ - README.rdoc
105
+ - Rakefile
106
+ - VERSION
107
+ - currency-in-words.gemspec
108
+ - lib/currency-in-words.rb
109
+ homepage: http://github.com/bcarrere/currency-in-words
110
+ licenses:
111
+ - MIT
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ segments:
123
+ - 0
124
+ hash: 4257452098342617448
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ! '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 1.8.10
134
+ signing_key:
135
+ specification_version: 3
136
+ summary: View helper for Rails that displays a currency amount in words (eg. 'one
137
+ hundred dollars')
138
+ test_files: []