Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 489
- Log:
Jan 2024 bounty system updates
- Author:
- rool
- Date:
- Sun Jan 28 09:30:54 +0000 2024
- Size:
- 11139 Bytes
1 | ######################################################################## |
2 | # File:: currencies_helper.rb |
3 | # (C):: Hipposoft 2009, 2010, 2011 |
4 | # |
5 | # Purpose:: Utility methods for currency-related views. |
6 | # ---------------------------------------------------------------------- |
7 | # 08-Mar-2009 (ADH): Created. |
8 | # 18-Feb-2011 (ADH): Imported from Artisan. |
9 | ######################################################################## |
10 | |
11 | module CurrenciesHelper |
12 | |
13 | # As currencyhelp_print below, but composes an integer part and a fraction |
14 | # part, expressed as strings, into one price. Non-numeric characters in |
15 | # either part are stripped (though, in addition to the currency symbol, |
16 | # non-numeric characters might appear in the output because they're present |
17 | # in the formatting template for the given currency). |
18 | # |
19 | # The two optional parameters are passed through to currencyhelp_print - |
20 | # see there for details. |
21 | # |
22 | # See also: currencyhelp_print() |
23 | # |
24 | def currencyhelp_compose( currency, integer, fraction, converter = false, omit_zero_fraction = false ) |
25 | return currencyhelp_print( |
26 | currency, |
27 | Currency.simplify( integer, fraction ), |
28 | converter, |
29 | omit_zero_fraction |
30 | ) |
31 | end |
32 | |
33 | # Use the given currency's settings to print a fully formatted monetary |
34 | # value passed in as a string containing a floating point value, or a type |
35 | # which can convert to a string with "to_s" and give a floating point style |
36 | # result (e.g. a BigDecimal or an actual Float). |
37 | # |
38 | # If the optional "converter" parameter is set to 'true', then HTML which |
39 | # pops up a currency conversion hint via Google will be included, but only |
40 | # if there's a currently logged in user (so the target currency is known) |
41 | # and only if the currency requested differs from the target. |
42 | # |
43 | # If the optional "omit_zero_fraction" is set to 'true', then for currencies |
44 | # with a fractional component and where that component is zero, the fraction |
45 | # will be omitted (e.g. generate "£750" instead of the default "$750.00"). |
46 | # |
47 | # See also: currencyhelp_compose() |
48 | # |
49 | def currencyhelp_print( currency, value, converter = false, omit_zero_fraction = false ) |
50 | rounding = Currency::ROUNDING_ALGORITHMS[ currency.rounding_algorithm ] || |
51 | Currency::ROUNDING_ALGORITHMS[ Currency::DEFAULT_ROUNDING_ALGORITHM ] |
52 | numeric = rounding[ :proc ].call( currency, value.to_s ) |
53 | decimal = numeric.index( '.' ) |
54 | |
55 | # Split up the number, which is stored as a string in mathematical |
56 | # "integer-dot-fraction" notation. |
57 | |
58 | if ( decimal.nil? ) |
59 | fraction = '' |
60 | integer = numeric |
61 | else |
62 | fraction = numeric[ ( decimal + 1 )..-1 ] |
63 | integer = numeric[ 0..( decimal - 1 ) ] |
64 | end |
65 | |
66 | # Use the template formatter to process these. |
67 | |
68 | formatted_integer = currencyhelp_format( |
69 | ( currency.integer_template || '' ).reverse, |
70 | integer |
71 | ) |
72 | |
73 | formatted_fraction = currencyhelp_format( |
74 | ( currency.fraction_template || '' ), |
75 | fraction |
76 | ) |
77 | |
78 | formatted_value = formatted_integer |
79 | formatted_value += currency.delimiter + formatted_fraction unless ( formatted_fraction.empty? || ( omit_zero_fraction == true && fraction.to_i == 0 ) ) |
80 | |
81 | # Canvass doesn't use Locations like Artisan. |
82 | # |
83 | # # Build a currency converter link? |
84 | # |
85 | # if ( converter && |
86 | # logged_in? && |
87 | # ! current_user.location.nil? && |
88 | # ! current_user.location.currency.nil? && |
89 | # current_user.location.currency_id != currency.id ) |
90 | # |
91 | # converter_link = " " + link_to( |
92 | # image_tag( |
93 | # 'famfamfam_silk_icons/currency.png', |
94 | # :size => '16x16', |
95 | # :alt => '£', |
96 | # :align => 'top', |
97 | # :class => 'currency_converter' |
98 | # ), |
99 | # "https://www.google.com/search?q=convert+#{ formatted_value }+#{ currency.code }+to+#{ current_user.location.currency.code }", |
100 | # :target => '_blank', |
101 | # :class => 'help' |
102 | # ) |
103 | # |
104 | # end # (Else converter_link ends up 'nil' by default) |
105 | converter_link = nil |
106 | |
107 | # Compile the result |
108 | |
109 | if ( currency.show_after_number ) |
110 | return "#{ formatted_value }#{ currency.symbol }#{ converter_link }" |
111 | else |
112 | return "#{ currency.symbol }#{ formatted_value }#{ converter_link }" |
113 | end |
114 | end |
115 | |
116 | # For a given user or currency object, return form fields for the given form |
117 | # object (that is, the "f" in view code such as "form for <foo> do |f|...") |
118 | # which let someone input a price for a given named field in either the given |
119 | # currency or a currency appropriate to the given user's configured location. |
120 | # |
121 | # The optional fourth parameter is used if the text input field generator |
122 | # methods are not going to be able to determine the initial value (if any) |
123 | # to use, perhaps because the fields are being generated for a non- |
124 | # ActiveRecord object, such as a simple hash. Provide this object (e.g. a |
125 | # hash) in the fourth parameter; the field names used ("<name>_integer" and |
126 | # "<name>_fraction") are looked up in this object with the "[]" operator to |
127 | # obtain the value. |
128 | # |
129 | # Text fields are generated for the given name with "_integer" or "_fraction" |
130 | # appended, for the integer and fractional parts respectively. |
131 | # |
132 | def currencyhelp_edit( user_or_currency, form, name, data_source = nil ) |
133 | currency = ( user_or_currency.is_a?( User ) ) ? Currency.get_best_currency( user_or_currency ) : user_or_currency |
134 | result = '' |
135 | |
136 | # TODO: This is for the variable "column names" (attribute names) in the |
137 | # currency editor row(s) for advanced artwork search originally. The search |
138 | # model's "price" column is a serialized hash of the price data. The form |
139 | # calling to this method uses proper nested attributes format, but even if |
140 | # the hash is converted to an OpenStruct, the contents are ignored (noting |
141 | # that "form.object" is always "nil" herein). The solution of passing in a |
142 | # parameter as presently implemented is a nasty hack just to avoid wasting |
143 | # a lot of time on a cleaner mechanism - hence "to do" later, if ever! |
144 | |
145 | extra = {} |
146 | field = "#{ name }_integer" |
147 | value = data_source[ field ] unless ( data_source.nil? ) |
148 | extra[ :value ] = value || "0" |
149 | |
150 | result << currency.symbol unless ( currency.show_after_number ) |
151 | result << form.text_field( |
152 | field.to_sym, |
153 | { |
154 | :size => 5, # 5 digits - that's a costly item! |
155 | :maxlength => Currency::MAXLEN_INTEGER_TEMPLATE |
156 | }.merge( extra ) |
157 | ) |
158 | |
159 | if ( currency.decimal_precision > 0 ) |
160 | extra = {} |
161 | field = "#{ name }_fraction" |
162 | value = data_source[ field ] unless ( data_source.nil? ) |
163 | extra[ :value ] = value || "0" * ( currency.fraction_template.length ) |
164 | |
165 | result << ( currency.delimiter || '' ) |
166 | result << form.text_field( |
167 | field.to_sym, |
168 | { |
169 | :size => currency.decimal_precision, |
170 | :maxlength => currency.decimal_precision |
171 | }.merge( extra ) |
172 | ) |
173 | end |
174 | |
175 | result << currency.symbol if ( currency.show_after_number ) |
176 | return result |
177 | end |
178 | |
179 | # Given an internal rounding algorithm name, return a human-readable |
180 | # equivalent including hint string if available. Returned value is *not* |
181 | # escaped with "h". |
182 | # |
183 | def currencyhelp_rounding_algorithm( algorithm ) |
184 | details = Currency::ROUNDING_ALGORITHMS[ algorithm ] |
185 | humanized = algorithm.humanize |
186 | |
187 | if ( details.nil? ) |
188 | humanized |
189 | else |
190 | "#{ humanized } (#{ details[ :hint ] })" |
191 | end |
192 | end |
193 | |
194 | # Return the result of 'select_tag' for a list of all currencies (this may be |
195 | # modified by internal sorting, to avoid creating unnecessary copies). Runs |
196 | # via apphelp_menu in the back-end so see that for details; broadly, pass the |
197 | # array of Currency objects you want to list and an options hash which will |
198 | # contain ":form" for a form_for based menu or nothing/":selected" for a |
199 | # stand-alone menu (in which case, "params[ :currency_id ]" will be set when |
200 | # the surrounding form is submitted). |
201 | # |
202 | def currencyhelp_menu( currencies, options = {} ) |
203 | Currency.apply_default_sort_order( currencies ) |
204 | values = currencies.collect { | c | [ "#{ h c.name }", c.id ] } |
205 | |
206 | return apphelp_menu( values, :currency_id, options ) |
207 | end |
208 | |
209 | # As "currencyhelp_menu" but builds a rounding algorithm selector menu |
210 | # instead of a currency selector menu. Sets the "rounding_algorithm" field |
211 | # of a form_for <some object> / sets params[ :rounding_algorithm ] for |
212 | # stand-alone menus. |
213 | # |
214 | def currencyhelp_rounding_algorithm_menu( options = {} ) |
215 | algorithms = Currency::ROUNDING_ALGORITHMS |
216 | values = Currency::ROUNDING_ALGORITHM_ORDER.collect do | name | |
217 | [ currencyhelp_rounding_algorithm( name ), name ] |
218 | end |
219 | |
220 | return apphelp_menu( values, :rounding_algorithm, options ) |
221 | end |
222 | |
223 | # As "currencyhelp_menu" but provides a fixed preset list of decimal |
224 | # precision choices. |
225 | # |
226 | def currencyhelp_decimal_precision_menu( options = {} ) |
227 | values = [] |
228 | precisions = { |
229 | '4' => '4: 1234.1234', |
230 | '3' => '3: 1234.123', |
231 | '2' => '2: 1234.12', |
232 | '1' => '1: 1234.1', |
233 | '0' => '0: 1234', |
234 | '-1' => '-1: 1230', |
235 | '-2' => '-2: 1200', |
236 | '-3' => '-3: 1000' |
237 | } |
238 | |
239 | sorted = precisions.keys.sort { | a, b | b.to_i <=> a.to_i } |
240 | values = sorted.collect do | key | |
241 | [ precisions[ key ], key.to_i ] |
242 | end |
243 | |
244 | return apphelp_menu( values, :decimal_precision, options ) |
245 | end |
246 | |
247 | private |
248 | |
249 | # Run through a template strings from left to right adding characters from |
250 | # a given integer value (expressed as a string) if there's a numeric entry in |
251 | # the template, else adding the template's non-numeric character at that |
252 | # position. If there's anything left of the value string once the loop has |
253 | # finished, add the remaining data to the front of the formatted result. |
254 | # |
255 | # Using per-byte processing means that multibyte characters drop out "in |
256 | # the wash", since they'll all be added from the template before the next |
257 | # numeric character (0-9 => single byte) gets encountered - bearing in mind |
258 | # that this is a Rails application using UTF-8 encoding in Ruby via $KCODE. |
259 | # |
260 | # Can be used to process integer parts of floats-as-strings by passing in the |
261 | # template as "template.reverse", or for the fraction parts by passing in the |
262 | # template directly. See currencyhelp_print for example usage. |
263 | # |
264 | def currencyhelp_format( template, value ) |
265 | formatted = '' |
266 | |
267 | template.each_byte do | byte | |
268 | break if ( value.empty? ) |
269 | |
270 | byte = '' << byte # Convert from character code to string |
271 | add = ( byte < '0' || byte > '9' ) ? byte : value.slice!( -1..-1 ) |
272 | formatted = "#{ add }#{ formatted }" |
273 | end |
274 | |
275 | formatted = "#{ value }#{ formatted }" unless ( value.empty? ) |
276 | return formatted |
277 | end |
278 | end |