money 3.6.1 → 3.6.2
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.
- data/CHANGELOG.md +335 -319
- data/LICENSE +21 -21
- data/README.md +214 -209
- data/Rakefile +49 -49
- data/lib/money.rb +27 -27
- data/lib/money/bank/base.rb +131 -131
- data/lib/money/bank/variable_exchange.rb +252 -251
- data/lib/money/core_extensions.rb +82 -63
- data/lib/money/currency.rb +422 -415
- data/lib/money/money.rb +387 -1210
- data/lib/money/money/arithmetic.rb +246 -0
- data/lib/money/money/formatting.rb +234 -0
- data/lib/money/money/parsing.rb +350 -0
- data/money.gemspec +34 -27
- data/spec/bank/base_spec.rb +72 -72
- data/spec/bank/variable_exchange_spec.rb +238 -238
- data/spec/core_extensions_spec.rb +158 -142
- data/spec/currency_spec.rb +133 -128
- data/spec/money/arithmetic_spec.rb +479 -0
- data/spec/money/formatting_spec.rb +352 -0
- data/spec/money/parsing_spec.rb +197 -0
- data/spec/money_spec.rb +271 -1268
- data/spec/spec_helper.rb +28 -17
- metadata +33 -23
- data/lib/money.rbc +0 -170
- data/lib/money/bank/base.rbc +0 -800
- data/lib/money/bank/variable_exchange.rbc +0 -2496
- data/lib/money/core_extensions.rbc +0 -474
- data/lib/money/currency.rbc +0 -22600
- data/lib/money/money.rbc +0 -10070
- data/spec/bank/base_spec.rbc +0 -2409
- data/spec/bank/variable_exchange_spec.rbc +0 -7389
- data/spec/core_extensions_spec.rbc +0 -5215
- data/spec/currency_spec.rbc +0 -4341
- data/spec/money_spec.rbc +0 -50121
- data/spec/spec_helper.rbc +0 -346
@@ -0,0 +1,246 @@
|
|
1
|
+
class Money
|
2
|
+
module Arithmetic
|
3
|
+
|
4
|
+
# Checks whether two money objects have the same currency and the same
|
5
|
+
# amount. Checks against money objects with a different currency and checks
|
6
|
+
# against objects that do not respond to #to_money will always return false.
|
7
|
+
#
|
8
|
+
# @param [Money] other_money Value to compare with.
|
9
|
+
#
|
10
|
+
# @return [Boolean]
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# Money.new(100) == Money.new(101) #=> false
|
14
|
+
# Money.new(100) == Money.new(100) #=> true
|
15
|
+
def ==(other_money)
|
16
|
+
if other_money.respond_to?(:to_money)
|
17
|
+
other_money = other_money.to_money
|
18
|
+
cents == other_money.cents && self.currency == other_money.currency
|
19
|
+
else
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Synonymous with +#==+.
|
25
|
+
#
|
26
|
+
# @param [Money] other_money Value to compare with.
|
27
|
+
#
|
28
|
+
# @return [Money]
|
29
|
+
#
|
30
|
+
# @see #==
|
31
|
+
def eql?(other_money)
|
32
|
+
self == other_money
|
33
|
+
end
|
34
|
+
|
35
|
+
def <=>(other_money)
|
36
|
+
if other_money.respond_to?(:to_money)
|
37
|
+
other_money = other_money.to_money
|
38
|
+
if self.currency == other_money.currency
|
39
|
+
cents <=> other_money.cents
|
40
|
+
else
|
41
|
+
cents <=> other_money.exchange_to(currency).cents
|
42
|
+
end
|
43
|
+
else
|
44
|
+
raise ArgumentError, "Comparison of #{self.class} with #{other_money.inspect} failed"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a new Money object containing the sum of the two operands' monetary
|
49
|
+
# values. If +other_money+ has a different currency then its monetary value
|
50
|
+
# is automatically exchanged to this object's currency using +exchange_to+.
|
51
|
+
#
|
52
|
+
# @param [Money] other_money Other +Money+ object to add.
|
53
|
+
#
|
54
|
+
# @return [Money]
|
55
|
+
#
|
56
|
+
# @example
|
57
|
+
# Money.new(100) + Money.new(100) #=> #<Money @cents=200>
|
58
|
+
def +(other_money)
|
59
|
+
if currency == other_money.currency
|
60
|
+
Money.new(cents + other_money.cents, other_money.currency)
|
61
|
+
else
|
62
|
+
Money.new(cents + other_money.exchange_to(currency).cents, currency)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns a new Money object containing the difference between the two
|
67
|
+
# operands' monetary values. If +other_money+ has a different currency then
|
68
|
+
# its monetary value is automatically exchanged to this object's currency
|
69
|
+
# using +exchange_to+.
|
70
|
+
#
|
71
|
+
# @param [Money] other_money Other +Money+ object to subtract.
|
72
|
+
#
|
73
|
+
# @return [Money]
|
74
|
+
#
|
75
|
+
# @example
|
76
|
+
# Money.new(100) - Money.new(99) #=> #<Money @cents=1>
|
77
|
+
def -(other_money)
|
78
|
+
if currency == other_money.currency
|
79
|
+
Money.new(cents - other_money.cents, other_money.currency)
|
80
|
+
else
|
81
|
+
Money.new(cents - other_money.exchange_to(currency).cents, currency)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Multiplies the monetary value with the given number and returns a new
|
86
|
+
# +Money+ object with this monetary value and the same currency.
|
87
|
+
#
|
88
|
+
# Note that you can't multiply a Money object by an other +Money+ object.
|
89
|
+
#
|
90
|
+
# @param [Numeric] value Number to multiply by.
|
91
|
+
#
|
92
|
+
# @return [Money] The resulting money.
|
93
|
+
#
|
94
|
+
# @raise [ArgumentError] If +value+ is a Money instance.
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# Money.new(100) * 2 #=> #<Money @cents=200>
|
98
|
+
#
|
99
|
+
def *(value)
|
100
|
+
if value.is_a?(Money)
|
101
|
+
raise ArgumentError, "Can't multiply a Money by a Money"
|
102
|
+
else
|
103
|
+
Money.new(cents * value, currency)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Divides the monetary value with the given number and returns a new +Money+
|
108
|
+
# object with this monetary value and the same currency.
|
109
|
+
# Can also divide by another +Money+ object to get a ratio.
|
110
|
+
#
|
111
|
+
# +Money/Numeric+ returns +Money+. +Money/Money+ returns +Float+.
|
112
|
+
#
|
113
|
+
# @param [Money, Numeric] value Number to divide by.
|
114
|
+
#
|
115
|
+
# @return [Money] The resulting money if you divide Money by a number.
|
116
|
+
# @return [Float] The resulting number if you divide Money by a Money.
|
117
|
+
#
|
118
|
+
# @example
|
119
|
+
# Money.new(100) / 10 #=> #<Money @cents=10>
|
120
|
+
# Money.new(100) / Money.new(10) #=> 10.0
|
121
|
+
#
|
122
|
+
def /(value)
|
123
|
+
if value.is_a?(Money)
|
124
|
+
if currency == value.currency
|
125
|
+
(cents / BigDecimal.new(value.cents.to_s)).to_f
|
126
|
+
else
|
127
|
+
(cents / BigDecimal(value.exchange_to(currency).cents.to_s)).to_f
|
128
|
+
end
|
129
|
+
else
|
130
|
+
Money.new(cents / value, currency)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Synonym for +#/+.
|
135
|
+
#
|
136
|
+
# @param [Money, Numeric] value Number to divide by.
|
137
|
+
#
|
138
|
+
# @return [Money] The resulting money if you divide Money by a number.
|
139
|
+
# @return [Float] The resulting number if you divide Money by a Money.
|
140
|
+
#
|
141
|
+
# @see #/
|
142
|
+
#
|
143
|
+
def div(value)
|
144
|
+
self / value
|
145
|
+
end
|
146
|
+
|
147
|
+
# Divide money by money or fixnum and return array containing quotient and
|
148
|
+
# modulus.
|
149
|
+
#
|
150
|
+
# @param [Money, Fixnum] val Number to divmod by.
|
151
|
+
#
|
152
|
+
# @return [Array<Money,Money>,Array<Fixnum,Money>]
|
153
|
+
#
|
154
|
+
# @example
|
155
|
+
# Money.new(100).divmod(9) #=> [#<Money @cents=11>, #<Money @cents=1>]
|
156
|
+
# Money.new(100).divmod(Money.new(9)) #=> [11, #<Money @cents=1>]
|
157
|
+
def divmod(val)
|
158
|
+
if val.is_a?(Money)
|
159
|
+
a = self.cents
|
160
|
+
b = self.currency == val.currency ? val.cents : val.exchange_to(self.currency).cents
|
161
|
+
q, m = a.divmod(b)
|
162
|
+
return [q, Money.new(m, self.currency)]
|
163
|
+
else
|
164
|
+
return [self.div(val), Money.new(self.cents.modulo(val), self.currency)]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Equivalent to +self.divmod(val)[1]+
|
169
|
+
#
|
170
|
+
# @param [Money, Fixnum] val Number take modulo with.
|
171
|
+
#
|
172
|
+
# @return [Money]
|
173
|
+
#
|
174
|
+
# @example
|
175
|
+
# Money.new(100).modulo(9) #=> #<Money @cents=1>
|
176
|
+
# Money.new(100).modulo(Money.new(9)) #=> #<Money @cents=1>
|
177
|
+
def modulo(val)
|
178
|
+
self.divmod(val)[1]
|
179
|
+
end
|
180
|
+
|
181
|
+
# Synonym for +#modulo+.
|
182
|
+
#
|
183
|
+
# @param [Money, Fixnum] val Number take modulo with.
|
184
|
+
#
|
185
|
+
# @return [Money]
|
186
|
+
#
|
187
|
+
# @see #modulo
|
188
|
+
def %(val)
|
189
|
+
self.modulo(val)
|
190
|
+
end
|
191
|
+
|
192
|
+
# If different signs +self.modulo(val) - val+ otherwise +self.modulo(val)+
|
193
|
+
#
|
194
|
+
# @param [Money, Fixnum] val Number to rake remainder with.
|
195
|
+
#
|
196
|
+
# @return [Money]
|
197
|
+
#
|
198
|
+
# @example
|
199
|
+
# Money.new(100).remainder(9) #=> #<Money @cents=1>
|
200
|
+
def remainder(val)
|
201
|
+
a, b = self, val
|
202
|
+
b = b.exchange_to(a.currency) if b.is_a?(Money) and a.currency != b.currency
|
203
|
+
|
204
|
+
a_sign, b_sign = :pos, :pos
|
205
|
+
a_sign = :neg if a.cents < 0
|
206
|
+
b_sign = :neg if (b.is_a?(Money) and b.cents < 0) or (b < 0)
|
207
|
+
|
208
|
+
return a.modulo(b) if a_sign == b_sign
|
209
|
+
a.modulo(b) - (b.is_a?(Money) ? b : Money.new(b, a.currency))
|
210
|
+
end
|
211
|
+
|
212
|
+
# Return absolute value of self as a new Money object.
|
213
|
+
#
|
214
|
+
# @return [Money]
|
215
|
+
#
|
216
|
+
# @example
|
217
|
+
# Money.new(-100).abs #=> #<Money @cents=100>
|
218
|
+
def abs
|
219
|
+
Money.new(self.cents.abs, self.currency)
|
220
|
+
end
|
221
|
+
|
222
|
+
# Test if the money amount is zero.
|
223
|
+
#
|
224
|
+
# @return [Boolean]
|
225
|
+
#
|
226
|
+
# @example
|
227
|
+
# Money.new(100).zero? #=> false
|
228
|
+
# Money.new(0).zero? #=> true
|
229
|
+
def zero?
|
230
|
+
cents == 0
|
231
|
+
end
|
232
|
+
|
233
|
+
# Test if the money amount is non-zero. Returns this money object if it is
|
234
|
+
# non-zero, or nil otherwise, like +Numeric#nonzero?+.
|
235
|
+
#
|
236
|
+
# @return [Money, nil]
|
237
|
+
#
|
238
|
+
# @example
|
239
|
+
# Money.new(100).nonzero? #=> #<Money @cents=100>
|
240
|
+
# Money.new(0).nonzero? #=> nil
|
241
|
+
def nonzero?
|
242
|
+
cents != 0 ? self : nil
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
class Money
|
2
|
+
module Formatting
|
3
|
+
if Object.const_defined?("I18n")
|
4
|
+
def thousands_separator
|
5
|
+
I18n.t(
|
6
|
+
:"number.currency.format.delimiter",
|
7
|
+
:default => I18n.t(
|
8
|
+
:"number.format.delimiter",
|
9
|
+
:default => (currency.thousands_separator || ",")
|
10
|
+
)
|
11
|
+
)
|
12
|
+
end
|
13
|
+
else
|
14
|
+
def thousands_separator
|
15
|
+
currency.thousands_separator || ","
|
16
|
+
end
|
17
|
+
end
|
18
|
+
alias :delimiter :thousands_separator
|
19
|
+
|
20
|
+
if Object.const_defined?("I18n")
|
21
|
+
def decimal_mark
|
22
|
+
I18n.t(
|
23
|
+
:"number.currency.format.separator",
|
24
|
+
:default => I18n.t(
|
25
|
+
:"number.format.separator",
|
26
|
+
:default => (currency.decimal_mark || ".")
|
27
|
+
)
|
28
|
+
)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
def decimal_mark
|
32
|
+
currency.decimal_mark || "."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
alias :separator :decimal_mark
|
36
|
+
|
37
|
+
# Creates a formatted price string according to several rules.
|
38
|
+
#
|
39
|
+
# @param [Hash] *rules The options used to format the string.
|
40
|
+
#
|
41
|
+
# @return [String]
|
42
|
+
#
|
43
|
+
# @option *rules [Boolean, String] :display_free (false) Whether a zero
|
44
|
+
# amount of money should be formatted of "free" or as the supplied string.
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# Money.us_dollar(0).format(:display_free => true) #=> "free"
|
48
|
+
# Money.us_dollar(0).format(:display_free => "gratis") #=> "gratis"
|
49
|
+
# Money.us_dollar(0).format #=> "$0.00"
|
50
|
+
#
|
51
|
+
# @option *rules [Boolean] :with_currency (false) Whether the currency name
|
52
|
+
# should be appended to the result string.
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# Money.ca_dollar(100).format => "$1.00"
|
56
|
+
# Money.ca_dollar(100).format(:with_currency => true) #=> "$1.00 CAD"
|
57
|
+
# Money.us_dollar(85).format(:with_currency => true) #=> "$0.85 USD"
|
58
|
+
#
|
59
|
+
# @option *rules [Boolean] :no_cents (false) Whether cents should be omitted.
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# Money.ca_dollar(100).format(:no_cents => true) #=> "$1"
|
63
|
+
# Money.ca_dollar(599).format(:no_cents => true) #=> "$5"
|
64
|
+
#
|
65
|
+
# @option *rules [Boolean] :no_cents_if_whole (false) Whether cents should be
|
66
|
+
# omitted if the cent value is zero
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
# Money.ca_dollar(10000).format(:no_cents_if_whole => true) #=> "$100"
|
70
|
+
# Money.ca_dollar(10034).format(:no_cents_if_whole => true) #=> "$100.34"
|
71
|
+
#
|
72
|
+
# @option *rules [Boolean, String, nil] :symbol (true) Whether a money symbol
|
73
|
+
# should be prepended to the result string. The default is true. This method
|
74
|
+
# attempts to pick a symbol that's suitable for the given currency.
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# Money.new(100, "USD") #=> "$1.00"
|
78
|
+
# Money.new(100, "GBP") #=> "£1.00"
|
79
|
+
# Money.new(100, "EUR") #=> "€1.00"
|
80
|
+
#
|
81
|
+
# # Same thing.
|
82
|
+
# Money.new(100, "USD").format(:symbol => true) #=> "$1.00"
|
83
|
+
# Money.new(100, "GBP").format(:symbol => true) #=> "£1.00"
|
84
|
+
# Money.new(100, "EUR").format(:symbol => true) #=> "€1.00"
|
85
|
+
#
|
86
|
+
# # You can specify a false expression or an empty string to disable
|
87
|
+
# # prepending a money symbol.§
|
88
|
+
# Money.new(100, "USD").format(:symbol => false) #=> "1.00"
|
89
|
+
# Money.new(100, "GBP").format(:symbol => nil) #=> "1.00"
|
90
|
+
# Money.new(100, "EUR").format(:symbol => "") #=> "1.00"
|
91
|
+
#
|
92
|
+
# # If the symbol for the given currency isn't known, then it will default
|
93
|
+
# # to "¤" as symbol.
|
94
|
+
# Money.new(100, "AWG").format(:symbol => true) #=> "¤1.00"
|
95
|
+
#
|
96
|
+
# # You can specify a string as value to enforce using a particular symbol.
|
97
|
+
# Money.new(100, "AWG").format(:symbol => "ƒ") #=> "ƒ1.00"
|
98
|
+
#
|
99
|
+
# @option *rules [Boolean, String, nil] :decimal_mark (true) Whether the
|
100
|
+
# currency should be separated by the specified character or '.'
|
101
|
+
#
|
102
|
+
# @example
|
103
|
+
# # If a string is specified, it's value is used.
|
104
|
+
# Money.new(100, "USD").format(:decimal_mark => ",") #=> "$1,00"
|
105
|
+
#
|
106
|
+
# # If the decimal_mark for a given currency isn't known, then it will default
|
107
|
+
# # to "." as decimal_mark.
|
108
|
+
# Money.new(100, "FOO").format #=> "$1.00"
|
109
|
+
#
|
110
|
+
# @option *rules [Boolean, String, nil] :thousands_separator (true) Whether
|
111
|
+
# the currency should be delimited by the specified character or ','
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# # If false is specified, no thousands_separator is used.
|
115
|
+
# Money.new(100000, "USD").format(:thousands_separator => false) #=> "1000.00"
|
116
|
+
# Money.new(100000, "USD").format(:thousands_separator => nil) #=> "1000.00"
|
117
|
+
# Money.new(100000, "USD").format(:thousands_separator => "") #=> "1000.00"
|
118
|
+
#
|
119
|
+
# # If a string is specified, it's value is used.
|
120
|
+
# Money.new(100000, "USD").format(:thousands_separator => ".") #=> "$1.000.00"
|
121
|
+
#
|
122
|
+
# # If the thousands_separator for a given currency isn't known, then it will
|
123
|
+
# # default to "," as thousands_separator.
|
124
|
+
# Money.new(100000, "FOO").format #=> "$1,000.00"
|
125
|
+
#
|
126
|
+
# @option *rules [Boolean] :html (false) Whether the currency should be
|
127
|
+
# HTML-formatted. Only useful in combination with +:with_currency+.
|
128
|
+
#
|
129
|
+
# @example
|
130
|
+
# s = Money.ca_dollar(570).format(:html => true, :with_currency => true)
|
131
|
+
# s #=> "$5.70 <span class=\"currency\">CAD</span>"
|
132
|
+
def format(*rules)
|
133
|
+
# support for old format parameters
|
134
|
+
rules = normalize_formatting_rules(rules)
|
135
|
+
|
136
|
+
if cents == 0
|
137
|
+
if rules[:display_free].respond_to?(:to_str)
|
138
|
+
return rules[:display_free]
|
139
|
+
elsif rules[:display_free]
|
140
|
+
return "free"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
symbol_value =
|
145
|
+
if rules.has_key?(:symbol)
|
146
|
+
if rules[:symbol] === true
|
147
|
+
symbol
|
148
|
+
elsif rules[:symbol]
|
149
|
+
rules[:symbol]
|
150
|
+
else
|
151
|
+
""
|
152
|
+
end
|
153
|
+
elsif rules[:html]
|
154
|
+
currency.html_entity
|
155
|
+
else
|
156
|
+
symbol
|
157
|
+
end
|
158
|
+
|
159
|
+
formatted = case rules[:no_cents]
|
160
|
+
when true
|
161
|
+
"#{self.to_s.to_i}"
|
162
|
+
else
|
163
|
+
"#{self.to_s}"
|
164
|
+
end
|
165
|
+
|
166
|
+
if rules[:no_cents_if_whole] && cents % currency.subunit_to_unit == 0
|
167
|
+
formatted = "#{self.to_s.to_i}"
|
168
|
+
end
|
169
|
+
|
170
|
+
symbol_position =
|
171
|
+
if rules.has_key?(:symbol_position)
|
172
|
+
rules[:symbol_position]
|
173
|
+
elsif currency.symbol_first?
|
174
|
+
:before
|
175
|
+
else
|
176
|
+
:after
|
177
|
+
end
|
178
|
+
|
179
|
+
if symbol_value && !symbol_value.empty?
|
180
|
+
formatted = (symbol_position == :before ? "#{symbol_value}#{formatted}" : "#{formatted} #{symbol_value}")
|
181
|
+
end
|
182
|
+
|
183
|
+
if rules.has_key?(:decimal_mark) and rules[:decimal_mark] and
|
184
|
+
rules[:decimal_mark] != decimal_mark
|
185
|
+
formatted.sub!(decimal_mark, rules[:decimal_mark])
|
186
|
+
end
|
187
|
+
|
188
|
+
thousands_separator_value = thousands_separator
|
189
|
+
# Determine thousands_separator
|
190
|
+
if rules.has_key?(:thousands_separator)
|
191
|
+
if rules[:thousands_separator] === false or rules[:thousands_separator].nil?
|
192
|
+
thousands_separator_value = ""
|
193
|
+
elsif rules[:thousands_separator]
|
194
|
+
thousands_separator_value = rules[:thousands_separator]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Apply thousands_separator
|
199
|
+
formatted.gsub!(/(\d)(?=(?:\d{3})+(?:[^\d]|$))/, "\\1#{thousands_separator_value}")
|
200
|
+
|
201
|
+
if rules[:with_currency]
|
202
|
+
formatted << " "
|
203
|
+
formatted << '<span class="currency">' if rules[:html]
|
204
|
+
formatted << currency.to_s
|
205
|
+
formatted << '</span>' if rules[:html]
|
206
|
+
end
|
207
|
+
formatted
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
private
|
212
|
+
|
213
|
+
# Cleans up formatting rules.
|
214
|
+
#
|
215
|
+
# @param [Hash]
|
216
|
+
#
|
217
|
+
# @return [Hash]
|
218
|
+
def normalize_formatting_rules(rules)
|
219
|
+
if rules.size == 0
|
220
|
+
rules = {}
|
221
|
+
elsif rules.size == 1
|
222
|
+
rules = rules.pop
|
223
|
+
rules = { rules => true } if rules.is_a?(Symbol)
|
224
|
+
end
|
225
|
+
if not rules.include?(:decimal_mark) and rules.include?(:separator)
|
226
|
+
rules[:decimal_mark] = rules[:separator]
|
227
|
+
end
|
228
|
+
if not rules.include?(:thousands_separator) and rules.include?(:delimiter)
|
229
|
+
rules[:thousands_separator] = rules[:delimiter]
|
230
|
+
end
|
231
|
+
rules
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|