I'm a bit of a noob programmer so apologies if the question isn't clear enough.
I'm trying to create a basic rails app where I have 3 different tables: usages(month, usage), prices(month, price) and spends(month, spend).
I'm trying to get it so that spend = usages.usage * prices.price. I've put the following code into my Spend model:
class Spend < ActiveRecord::Base
c = Usage.all.count
i = 1
while i <= c
u = Usage.find(i)
p = Price.find(i)
Spend.create(month:u.month, spend:u.usage*p.price)
i += 1
end
end
This works great initially, but as soon as I start adding and removing usages and prices, their id's change so it isn't as clear cut. How can I do this in a much better way?
Thanks,
Kev
In this case, I would lean against making a separate Spend model, since all it does is calculate data that is already present in the database. Unless you have severe caching requirements (and I doubt it in your case), you can use simple instance methods to retrieve the data you want.
First figure out how your Usage and Price models are related. Since you seem to be associating them by id, it appears to be a one-to-one relationship (correct me if I'm wrong on this). However, associating by assuming they have the same primary key is a dangerous approach - rather have one model point to the other using a foreign key. We'll pick the Price model to hold a primary key for Usage, but the reverse can also work. You'll need to add a column using a migration like this:
def change
add_column :prices, :usage_id, :integer
end
Your models should then look like this:
class Usage < ActiveRecord::Base
has_one :price
def spend
usage * price.price
end
end
class Price < ActiveRecord::Base
belongs_to :usage
end
And you can find your spend value for an individual usage item like this:
usage = Usage.find(some_id)
puts usage.spend
Or you can get multiple 'spends' like this:
Usage.include(:price).each do |usage|
puts usage.spend
end
I've left out any reference to month, as I'm not sure how you are using it or if it's needed at all for calculating spend.
Have a look at the Active Record association guide: http://guides.rubyonrails.org/association_basics.html
Related
Good morning, I'm having an issue in my rails app when trying to link attributes between two classes. Let me explain better:
I have a Systemclass, which belongs_to my Area class (one area has_many systems). Both of them have an attribute called price. The price of an area must be the sum of the prices of all the systems it has.
Is there any way to make this relation without having to update the area's price every time I change one of it system's price? (I do something like #system.area.price = #system.area.price + #system.price)
If you're ok with handling this in the database, the sum calculation will do it for you: http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html
class Area < ActiveRecord::Base
has_many :systems
def price
systems.sum('price')
end
end
You could remove the Area's price field altogether.
Unless you want to go down the dark, dangerous road of introducing triggers into your database, short answer is not really.
The long answer is you need to have after_save handlers that do this adjustment for you. Always be sure that the way you're applying this does an increment and not a reset or you will have race conditions.
In your short example it's not clear what you're asking for. Unless Area has a base_price then you'll keep adding on the System price indefinitely. You'll need to work out the exact logic here, but an example of your requirements are:
class System < ActiveRecord::Base
belongs_to :area
after_save :update_area_price
protected
def update_area_price
self.area and self.area.increment!(:price, self.price)
end
end
I'm quite new to Rails, so forgive me if I'm going about this in the wrong way.
I have a model, transaction, which has an amount associated with it. However, since the amount is calculated as the sum of the amounts on a child model (transactor), I didn't want to create redundancy by actually having an amount field on my transaction table. I'd like to be able to get and set the amount at the transaction level (a value set at the transaction would be divided evenly among the transactors).
My question is this: Is composed_of the appropriate implementation for this situation?
No, I don't think composed_of is what you want. The read part is actually pretty easy:
class Transaction < ActiveRecord::Base
has_many :transactors
...
def amount
self.transactors.sum(:amount)
end
...
end
The write part (evenly dividing up among child models) is pretty unusual though and a bit more complicated, I suppose you'd do something like this:
class Transaction < ActiveRecord::Base
has_many :transactors
...
def amount=(value)
self.transactors.each do |transactor|
transactor.update_attributes(:amount => value.to_f / self.transactors.count)
end
end
...
end
Live site: http://iatidata.heroku.com
Github: https://github.com/markbrough/IATI-Data
Based on aid information released through the IATI Registry: iatiregistry.org
I'm a bit of a Rails n00b so sorry if this is a really stupid question.
There are two key Models in this app:
Activity - which contains details
such as recipient country, funding
organisation
Transaction - which contains details such as how much money (value) was committed or disbursed (transaction_type), when, to whom, etc.
All Transactions nest under an Activity. Each Activity has multiple Transactions. They are connected together by activity_id. has_many :transactions and belongs_to :activity are defined in the Activity and Transaction Models respectively.
So: all of this works great when I'm trying to get details of transactions for a single activity - either when looking at a single activity (activity->show) or looping through activities on the all activities page (activity->index). I just call
#activities.each do |activity|
activity.transactions.each do |transaction|
transaction.value # do something like display it
end
end
But what I now really want to do is to get the sum of all transactions for all activities (subject to :conditions for the activity).
What's the best way to do this? I guess I could do something like:
#totalvalue = 0
#activities.each do |activity|
activity.transactions.each do |transaction|
#totalvalue = #totalvalue + transaction.value
end
end
... but that doesn't seem very clean and making the server do unnecessary work. I figure it might be something to do with the model...?! sum() is another option maybe?
This has partly come about because I want to show the total amount going to each country for the nice bubbles on the front page :)
Thanks very much for any help!
Update:
Thanks for all the responses! So, this works now:
#thiscountry_activities.each do |a|
#thiscountry_value = #thiscountry_value + a.transactions.sum(:value)
end
But this doesn't work:
#thiscountry_value = #thiscountry_activities.transactions.sum(:value)
It gives this error:
undefined method `transactions' for #<Array:0xb5670038>
Looks like I have some sort of association problem. This is how the models are set up:
class Transaction < ActiveRecord::Base
belongs_to :activity
end
class Activity < ActiveRecord::Base
has_and_belongs_to_many :policy_markers
has_and_belongs_to_many :sectors
has_many :transactions
end
I think this is probably quite a simple problem, but I can't work out what's going on. The two models are connected together via id (in Activity) and activity_id (in Transactions).
Thanks again!
Use Active Record's awesome sum method, available for classes:
Transaction.sum(:value)
Or, like you want, associations:
activity.transactions.sum(:value)
Let the database do the work:
#total_value = Transaction.sum(:value)
This gives the total for all transactions. If you have some activities already loaded, you can filter them this way:
#total_value = Transaction.where(:activity_id => #activities.map(&:id)).sum(:value)
You can do it with one query:
#total_value = Transaction.joins(:activity).where("activities.name" => 'foo').sum(:value)
My code was getting pretty messy summing up virtual attributes. So I wrote this little method to do it for me. You just pass in a collection and a method name as a string or symbol and you get back a total. I hope someone finds this useful.
def vsum collection, v_attr # Totals the virtual attributes of a collection
total = 0
collection.each { |collect| total += collect.method(v_attr).call }
return total
end
# Example use
total_credits = vsum(Account.transactions, :credit)
Of course you don't need this if :credit is a table column. You are better off using the built in ActiveRecord method above. In my case i have a :quantity column that when positive is a :credit and negative is a :debit. Since :debit and :credit are not table columns they can't be summed using ActiveRecord.
As I understood, you would like to have the sum of all values of the transaction table. You can use SQL for that. I think it will be faster than doing it the Ruby way.
select sum(value) as transaction_value_sum from transaction;
You could do
#total_value = activity.transactions.sum(:value)
http://ar.rubyonrails.org/classes/ActiveRecord/Calculations/ClassMethods.html
I have to define an association that doesn't seem to fit in well to the "has_one / belongs_to" bucket very well.
The situation is this, I have a table whereby each row corresponds to monthly statistics for a given month and year. I'd love to be able to define certain associations on my model such as record.prior_month or record.prior_year which would correspond to the prior month / year of the current record.
I can't think of any clever way to do this as it doesn't make any sense to maintain foreign keys that would have to be updated every month for tons of records.
I can always handle the logic in the controller, but I'd prefer to keep it with the model if I could.
Thanks!
Mike
So rather than store the Month/Year, also store the Month+Year*12. So March 2011 is 24135
That way, you know the next month is 21436, and you can easily paginate over your records.
TrafficGroup.order("month_calculated").paginate(:page=>params[:page])
Something like this?
class MyModel < AR::Base
def prior_month
created_at.month
end
def prior_year
created_at.year
end
end
example = MyModel.last
example.prior_year
#=> 2010
example.prior_month
#=> 3
You can do this a few ways. Assuming the month is stored in the model.
My favorite is scopes, since it plays nicely with other associations.
For instance you can do:
class TrafficRecord < ActiveRecord::Base
scope :only_month, lambda {|month| where :month => month} # this should also contain join conditions
def prior_month
self.class.only_month(self.month - 1) #decrement your month
end
end
I'm creating an online bookmaker odds comparison site for soccer and I'm wondering how to calculate the best odds in Ruby/Rails.
I have two models: fixture and odds
Fixture has home and away teams, and odds model has bookmaker ID, home odds, draw odds and away odds.
I have selections which just stores the selected fixtures/teams in the DB.
I'm thinking of doing it this way where I create an multi-dimensional array of the different bookmakers and then add the fixture_id and 1,2 or 3 for home/draw/away and then use that as the key to add the odds
Something like odds[bookmaker][fixture][1/2/3] = price then add up the odds = count(odds[bookmaker][fixture][1/2/3])?
Is there an easier way? Maybe do it in the DB?
Without taking performance into account - it's probably not an issue and anyway, we shouldn't optimise for performance until we know we have a problem - I'd say you might introduce a Bookmaker model (if only to store the name) and start making use of ActiveRecord associations. I'd also consider splitting Odds into the three individual result types, which could be more flexible, especially if you want to add more bets later. You might get something like:
class Bookmaker < ActiveRecord::Base
has_many :odds
end
class Odd < ActiveRecord::Base # good name? Price is almost as common and less likely to be misinterpreted
belongs_to :fixture
belongs_to :bookmaker
# let's assume we use result type = 1/2/3 or maybe :home/:draw/:away
end
class Fixture < ActiveRecord::Base
has_many :odds
end
What you look to be trying to do is calculate the best price for each result across all bookies making a price on that fixture, or the "overround". If it's less than 100% then a potential arbitrage exists.
class Odd
named_scope :for_result, lambda { |res_tp| {:conditions => ['type = ?', res_tp]}}
end
class Fixture
def best_price(res_type)
# assumes you have odds stored as a percentage
odds.for_result(res_type).minimum(:pctage)
end
def overround
[:home, :away, :draw].inject(0.0){|sum, res_tp| sum + best_price(res_tp)}
end
end
I'm sure the above doesn't exactly fit your data, but it might give an idea of how you might go about it.