Module: Currency::Macro::ClassMethods
- Defined in:
- lib/currency/macro.rb
Overview
Macro Suppport
Support for Money attributes.
require 'currency'
require 'currency/macro'
class SomeClass
include ::Currency::Macro
attr_accessor :amount
attr_money :amount_money, :value => :amount, :currency_fixed => :USD, :rep => :float
end
x = SomeClass.new
x.amount = 123.45
x.amount
# => 123.45
x.amount_money
# => $123.45 USD
x.amount_money = x.amount_money + "12.45"
# => $135.90 USD
x.amount
# => 135.9
x.amount = 45.951
x.amount_money
# => $45.95 USD
x.amount
# => 45.951
Instance Method Summary collapse
-
#attr_money(attr_name, *opts) ⇒ Object
Defines a Money object attribute that is bound to other attributes.
Instance Method Details
#attr_money(attr_name, *opts) ⇒ Object
Defines a Money object attribute that is bound to other attributes.
Options:
:value => undef
Defines the value attribute to use for storing the money value. Defaults to the attribute name.
If this attribute is different from the attribute name, the money object will intercept #value=(x) to flush any cached Money object.
:readonly => false
If true, the underlying attribute is readonly. Thus the Money object cannot be cached. This is useful for computed money values.
:rep => :float
This option specifies how the value attribute stores Money values. if :rep is :rep, then the value is stored as a scaled integer as defined by the Currency.
If :rep is :float, or :integer the corresponding #to_f or #to_i method is used. Defaults to :float.
:currency => undef
Defines the attribute used to store and retrieve the Money’s Currency 3-letter ISO code.
:currency_fixed => currency_code (e.g.: :USD)
Defines the Currency to use for storing a normalized Money value.
All Money values will be converted to this Currency before storing. This allows SQL summary operations, like SUM(), MAX(), AVG(), etc., to produce meaningful results, regardless of the initial currency specified. If this option is used, subsequent reads will be in the specified normalization :currency_fixed.
:currency_preferred => undef
Defines the name of attribute used to store and retrieve the Money’s Currency ISO code. This option can be used with normalized Money values to retrieve the Money value in its original Currency, while allowing SQL summary operations on the normalized Money values to still be valid.
:currency_update => undef
If true, the currency attribute is updated upon setting the money attribute.
:time => undef
Defines the attribute used to retrieve the Money’s time. If this option is used, each Money value will use this attribute during historical Currency conversions.
Money values can share a time value with other attributes (e.g. a created_on column in ActiveRecord::Base).
If this option is true, the money time attribute will be named “#attr_name_time” and :time_update will be true.
:time_update => undef
If true, the Money time value is updated upon setting the money attribute.
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/currency/macro.rb', line 154 def attr_money(attr_name, *opts) opts = Hash[*opts] attr_name = attr_name.to_s opts[:class] = self opts[:name] = attr_name.intern ::Currency::Money.register_money_attribute(opts) value = opts[:value] || opts[:name] opts[:value] = value write_value = opts[:write_value] ||= "self.#{value} = " # Intercept value setter? if ! opts[:readonly] && value.to_s != attr_name.to_s alias_accessor = <<-"end_eval" alias :before_money_#{value}= :#{value}= def #{value}=(__value) @#{attr_name} = nil # uncache self.before_money_#{value} = __value end end_eval end # How to convert between numeric representation and Money. rep = opts[:rep] ||= :float to_rep = opts[:to_rep] from_rep = opts[:from_rep] if rep == :rep to_rep = 'rep' from_rep = '::Currency::Money.new_rep' else case rep when :float to_rep = 'to_f' when :integer to_rep = 'to_i' else raise ::Currency::Exception::InvalidMoneyValue, "Cannot use value representation: #{rep.inspect}" end from_rep = '::Currency::Money.new' end to_rep = to_rep.to_s from_rep = from_rep.to_s # Money time values. time = opts[:time] write_time = '' if time if time == true time = "#{attr_name}_time" opts[:time_update] = true end read_time = "self.#{time}" end opts[:time] = time if opts[:time_update] write_time = "self.#{time} = #{attr_name}_money && #{attr_name}_money.time" end time ||= 'nil' read_time ||= time currency_fixed = opts[:currency_fixed] currency_fixed &&= ":#{currency_fixed}" currency = opts[:currency] if currency == true currency = currency.to_s currency = "self.#{attr_name}_currency" end if currency read_currency = "self.#{currency}" if opts[:currency_update] write_currency = "self.#{currency} = #{attr_name}_money.nil? ? nil : #{attr_name}_money.currency.code" else convert_currency = "#{attr_name}_money = #{attr_name}_money.convert(#{read_currency}, #{read_time})" end end opts[:currency] = currency write_currency ||= '' convert_currency ||= '' currency_preferred = opts[:currency_preferred] if currency_preferred currency_preferred = currency_preferred.to_s read_preferred_currency = "@#{attr_name} = @#{attr_name}.convert(#{currency_preferred}, #{read_time})" write_preferred_currency = "self.#{currency_preferred} = @#{attr_name}_money.currency.code" end currency ||= currency_fixed read_currency ||= currency alias_accessor ||= '' validate ||= '' if opts[:readonly] eval_opts = [ (opts[:module_eval] = x = <<-"end_eval"), __FILE__, __LINE__ ] #{validate} def #{attr_name} #{attr_name}_rep = #{value} if #{attr_name}_rep != nil #{attr_name} = #{from_rep}(#{attr_name}_rep, #{read_currency} || #{currency}, #{read_time} || #{time}) #{read_preferred_currency} else #{attr_name} = nil end #{attr_name} end end_eval else eval_opts = [ (opts[:module_eval] = x = <<-"end_eval"), __FILE__, __LINE__ ] #{validate} #{alias_accessor} def #{attr_name} unless @#{attr_name} #{attr_name}_rep = #{value} if #{attr_name}_rep != nil @#{attr_name} = #{from_rep}(#{attr_name}_rep, #{read_currency} || #{currency}, #{read_time} || #{time}) #{read_preferred_currency} end end @#{attr_name} end def #{attr_name}=(value) if value == nil #{attr_name}_money = nil elsif value.kind_of?(Integer) || value.kind_of?(Float) || value.kind_of?(String) #{attr_name}_money = ::Currency.Money(value, #{read_currency}, #{read_time}) #{write_preferred_currency} elsif value.kind_of?(::Currency::Money) #{attr_name}_money = value #{write_preferred_currency} #{convert_currency} else raise ::Currency::Exception::InvalidMoneyValue, value end @#{attr_name} = #{attr_name}_money #{write_value}(#{attr_name}_money.nil? ? nil : #{attr_name}_money.#{to_rep}) #{write_currency} #{write_time} value end end_eval end # $stderr.puts " CODE = #{x}" module_eval(*eval_opts) end |