Money Rails - Order Of Amount And Currency - ruby-on-rails

Does the money-rails gem require a specific order of price and currency when initializing an object? For example, take a look at the following:
Object.new(currency: 'JPY', price: 25)
=> #<Object id: nil, price_cents: 25, currency: "JPY">
If we specify the price first, we get an incorrect value (2500) for the price:
Object.new(price: 25, currency: 'JPY')
=> #<Object id: nil, price_cents: 2500, currency: "JPY">
Object contains the following: monetize :price_cents.

It looks like the order matters for certain currencies (including JPY because it doesn't have cents). This may not be the best solution but if anyone is stuck, here is what I did.
I added the following to the self.monetize method in money-rails to override the initialize methods for classes that use it:
define_method "initialize" do |opts = {}|
opts = opts.deep_symbolize_keys
opts = {currency: opts[:currency]}.merge(opts)
super(opts)
end
This way it will send the currency first.

Related

How to display currency with humanized_money_with_symbol method? Like USD$ 100

I would like to humanized_money_with_symbol method to return something like USD$ 100, not only $ 100. Also I would like to do it only when currency symbol is $, we want to let users know when $ is USD and when AUD.
Never used MoneyRails but it looks like humanized_money_with_symbol just calls humanized_money merging in symbol: true to the parameters you passed it.
That helper then in turn calls format on the money object passed in, passing in the options you specified. In the Money gem, you can pass in a :symbol to render the currency with such as
m = Money.new('123', :gbp) # => #<Money fractional:123 currency:GBP>
m.format( symbol: m.currency.to_s + ' ') # => "GBP 1.23"
So, if you call
humanized_money(Money.new('123', :usd), symbol: 'USD $')
# => "USD $1.23"
You could then set up a helper method in your application, to avoid always having to pass that symbol in such as:
def render_custom_currency(value, options = {})
value.currency.iso_code == "USD" ? humanized_money(value, options.merge(symbol: 'USD $')) : humanized_money(value, options.merge(symbol: true))
end
that should get you what you're wanting to do.
You can override the USD configuration in initializers/money.rb to display "USD" as part of the symbol:
MoneyRails.configure do |config|
config.register_currency = {
"priority": 2,
"iso_code": "USD",
"name": "United States Dollar",
"symbol": "USD $",
"subunit": "Cent",
"subunit_to_unit": 100,
"symbol_first": true,
"decimal_mark": ".",
"thousands_separator": ",",
}
end
Restart the server and you should see "USD $100". I don't use multiple currencies but this should leave your other currencies displayed as normal.
Finally I used built-in in MoneyRails gem option disambiguate: true.
To use it you call method like below:
humanized_money_with_symbol(value, disambiguate: true)
Some examples how it works are here

Stop FactoryGirl converting my strings to floats

I have these factories setup:
FactoryGirl.define do
factory :product do
name { Faker::Commerce.product_name }
price { Faker::Commerce.price }
image { Faker::Internet.url }
end
factory :new_product, parent: :product do
name nil
price nil
image nil
end
factory :string_product, parent: :product do
price { Faker::Commerce.price.to_s }
end
end
Why do I want to use :string_product? Well, although the price attribute is of datatype float at the database level, occasionally I want to build a Factory with all of the attributes as strings.
This is so I can build the factory and then run expectations against its attributes when they are passed into the params hash. (All params from the URL are strings)
However, in the rails console:
> FactoryGirl.build :string_product
=> #<Product:0x00000007279780 id: nil, name: "Sleek Plastic Hat", price: 43.54, image: "http://blick.name/moie", created_at: nil, updated_at: nil>
As you can see, price is still being saved as a string.
An experiment to attempt to see what's going on:
...
factory :string_product, parent: :product do
price { "WHY ARE YOU NOT A STRING?" }
end
...
results in:
=> #<Product:0x000000077ddfa0 id: nil, name: "Awesome Steel Pants", price: 0.0, image: "http://rogahn.com/kavon", created_at: nil, updated_at: nil>
My string is now converted to the float 0.0
How do I prevent this behavior? If I want to have one of my attributes as a string, especially when I'm only building it I should be allowed to. Is there a FactoryGirl configuration where I can stop this happening? Exactly the same thing happens with the Fabrication gem, so I'm guessing this is something to do with the model? My Product model is literally empty right now...no validations or anything, so how can that be? The only way FactoryGirl knows price is a float is because it has that datatype on the database level.
Anyway, this is really annoying, if someone could show me how to let me write strings to my Factory's attributes I would be very appreciative. I could use .to_s in the spec itself but I want to keep my specs clean as possible and thought factories would be a great place to keep this configuration...
Is there a fabrication library that would let me do this?
Just some more experimentation:
> "WHY ARE YOU NOT A STRING".to_f
=> 0.0
Okay, so somewhere, in rails or in factorygirl, to_f is being called on my beloved string. Where? And how do I stop it?
With fabrication you need to use attributes_for to generate a hash representation of your object. It will bypass the ActiveRecord model entirely so nothing should be coerced.
Fabricate.attributes_for(:string_product)

ActiveRecord: Inserting data to the output

I have a few very basic questions in mind regarding active records. Please help me undestand:
What is the (data)type of the query result?
On doing '.class' to it, I get
the class name (Model name) to which it belongs. Makes complete
sense, but can we add more to the result?
Explaining my question with example:
u = User.find 1
=>#<User id: 1, email: "shivam#example.com", lang: 0, currency: 0, state: 0, category: 0, verified: true>
Checking the class:
u.class
=>User(id: integer, email: string, lang: integer, currency: integer, state: integer, category: integer, verified: boolean)
Is there someway to do this?
u.new_attr = "new_val"
Not entirely sure if this is what you want to do, but you can just select extra fields and they will be available on the object.
An example to demonstrate this:
results = Sample.find_by_sql(<<-SQL
select s.id, count(*) as results_count
from samples s, sample_results ss
where ss.sample_id = s.id
group by s.id
SQL)
So this query will return the sample-id and the count of linked sample-results. Now I can just type
results.first.results_count
and, obviously, results_count is not defined as attribute on the model Sample.
Not sure if that is what you ment. Maybe you just want to add an instance variable which is not saved to the database. In that case
you can simply do
class Sample
attr_accessor :my_special_field
end
and now I can just write
sample.my_special_field = 'something'
and it will only be available as long as the object "exists" and is never saved to the database.
Sorry if my question was not self explanatory. Here's a short explanation:
I do an active record query.
I get results.
I want to add something further to it.
As in the question the example is:
u = User.find 1
=>#<User id: 1, email: "shivam#example.com", lang: 0, currency: 0, state: 0, category: 0, verified: true>
AIM
To add some more information to the result set.
ISSUE
query result is an object of class User. As objects are instances of a class, we cannot simply add new attributes to the object without modifying the class. (Which is certainly not what we are looking for).
SOLUTION
It is plain stupidity to even think of directly modifying the object (which I did and got a -1 on my question. I acknowledge my mistake).
What we should rather do is, somehow convert the object into a data-structure that can be modified. In a case like this Hash suits the best.
Rails just provide an api to do so, its called attributes. Here:
u = User.find 1
=>#<User id: 1, email: "shivam#example.com", lang: 0, currency: 0, state: 0, category: 0, verified: true>
u.attributes
=> {"id" => 1, "email" => "shivam#example.com", "lang" => 0, "currency" => 0, "state" => 0, "category" => 0, "verified" => true}
Now that we have a hash. we can easily add/remove/modify/append any thing we want to:
u.attributes.merge({"something" => "added"})
=> {"id" => 1, "email" => "shivam#example.com", "lang" => 0, "currency" => 0, "state" => 0, "category" => 0, "verified" => true, "something" => "added"}
PS: Silly as it may, I have seen a lot of people asking such question over SO and other communities. I hope this may help someone. :)

Rails money gem - impossible to override default currency

I am using the Money gem for handling transaction amounts. I'd like to use different currencies for different transactions (but without conversions). The default currency is set in the money.rb:
config.default_currency = :usd
Even though I can set different currencies when creating a Transaction, the view always shows the amount in US dollars. For example this 12.00 transaction with RUB as currency:
<Transaction id: 100, amount_cents: 1200, currency: "RUB", created_at: "2013-12-11 09:32:52", updated_at: "2013-12-11 09:32:52">
Is being shown as a USD transaction in my views.
<% transactions.each do |transaction| %>
<%= transaction.amount_cents.to_money.format %>
...
=> $12.00
Here's the code from my Transaction.rb (just in case I'm missing something)
composed_of :amount_cents,
class_name: 'Money',
mapping: %w(amount_cents cents),
converter: Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : Money.empty }
monetize :amount_cents, as: "amount"
Any ideas on how to override the default? I'll be thankful for any advice.
Resolved the issue using amount instead of amount_cents.

How to convert from a string to object attribute name?

I am trying to convert a string value into a name of an attribute that belongs to an object. For example, in the following code, I need all the string values in the column_array turned into attribute names. The names "student_identification", "email", etc. are actual column names of my Student table. In the real scenario, column_array will be set by the user (by ticking check boxes). And new_array will be replaced by csv, as I want the data go into a csv file.
At the moment I am really struggling at the following line:
new_array << r."#{column_array[i]}"
I want "#{column_array[i]}" to be turned into the attribute name so I can access the data.
def exp_tst
#records = Student.find(:all, :conditions=> session[:selection_scope],
:order => sort_order('laboratory_id'))
column_array = ["student_identification", "laboratory_id", "email", "current_status"]
new_array = Array.new()
#records.each do |r|
(0..(column_array.size-1)).each do |i|
new_array << r."#{column_array[i]}"
end
end
end
Let's say column_array[i] = "foo", for an example.
If you want to call the method r.foo, use Object#send:
r.send(column_array[i], arg1, arg2, arg3, ...)
If you want to access r's instance variable #foo, use Object#instance_variable_get and Object#instance_variable_set:
r.instance_variable_get('#'+column_array[i])
r.instance_variable_set('#'+column_array[i], new_value)
In this case we have to prepend the given name with an # sigil, since that is required at the start of all instance variable names.
Since this is rails, and there's a whole lot of ActiveRecord magic going on with your models (and I'm guessing Student is a subclass of ActiveRecord::Base) you probably want to use the former, since ActiveRecord creates methods to access the database, and the values stored in instance variables may not be what you want or expect.
I'll use an example from some test data I've got lying around:
% script/console
Loading development environment (Rails 2.3.2)
irb> Customer
#=> Customer(id: integer, date_subscribed: datetime, rental_plan_id: integer, name: string, address: string, phone_number: string, credit_limit: decimal, last_bill_end_date: datetime, balance: decimal)
irb> example_customer = Customer.find(:all)[0]
#=> #<Customer id: 6, date_subscribed: "2007-12-24 05:00:00", rental_plan_id: 3, name: "Evagation Governessy", address: "803 Asbestous St, Uneradicated Stannous MP 37441", phone_number: "(433) 462-3416", credit_limit: #<BigDecimal:191edc0,'0.732E3',4(12)>, last_bill_end_date: "2009-05-15 04:00:00", balance: #<BigDecimal:191e870,'0.743E3',4(12)>>
irb> example_customer.name
#=> "Evagation Governessy"
irb> field = 'name'
#=> "name"
irb> example_customer.instance_variable_get(field)
NameError: `name` is not allowed as an instance variable name
from (irb):8:in `instance_variable_get`
from (irb):8
irb> example_customer.instance_variable_get('#'+field)
#=> nil
irb> example_customer.send(field)
#=> "Evagation Governessy"
irb> example_customer.send(field+'=', "Evagation Governessy Jr.")
#=> "Evagation Governessy Jr."
irb> example_customer.send(field)
#=> "Evagation Governessy Jr."
irb> example_customer.name
#=> "Evagation Governessy Jr."
So you can see how #send(field) accesses the record information, and trying to access the attributes doesn't.
Also, we can use #send(field+'=') to change record information.
Look at instance_eval method ...
if you have 'attribute' and need do
object.attribute = 'ololo'
you can do:
object.instance_eval('attribute') = 'ololo'

Resources