performance/ruby/rails/db question - ruby-on-rails

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.

Related

Best way to program varying cost of child admissions in Ruby?

I'm writing a program in which visitors would select how many kids they have, then provide their ages. I would then need to calculate their total cost according to the kid's ages and cost for each age.
I assume I would need to serialize a hash in order to get this data into the ActiveRecord but I'm confused as to how I would read this hash and calculate it against the hash provided by the user.
Is serialize the way to go with this particular problem or is there a better way?
Postgresql does recognize Arrays as a data type. So you could go that route. Though, you might want to separate out this data into it's own table.
class User < ActiveRecord::Base
has_many :children
...
class Children < ActiveRecord::Base
belongs_to :user, dependent: :destroy
...
And table schema something like:
table: users
|id|fname|lname|...
table: children
|id|name|user_id|...
So now it doesn't matter how many children each user has, you will always have a simple interface for adding, deleting and querying them.
c = User.first
c.children #returns array of Child objects.
Serialize is certain a way to go, but a model and a table allows you to be much more DB agnostic.
I think, hash serialization is not quite a RoR way.
Instead, having ActiveRecord model for a cost by age
AgeCost
--------
age: int
cost: decimal
one can easily find a total cost
kids_ages = [4, 7, 10]
total_cost = 0
kids_ages.each do |kid_age|
age_cost = AgeCost.find_by_age(kid_age)
total_cost += age_cost.cost if age_cost
end

linked attributes between classes (ruby on rails)

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

Logic of interaction among models in Ruby on Rails?

I'm studying Rails and am now trying to organize interaction among models. What I've written works, but I think that the code smells bad.
For example, I have two models with database tables Parcel and Warehouse. When I create a new Parcel, I want to increase the :current_weight of the Warehouse instance which is related to this new Parcel.
Again, everything works, but this type of code, interaction between two different objects, will be used frequently and something deep in my mind says: "Dude, this code sucks and will cause problems in the future!".
Maybe there are some good practices to organize or refactor it? Maybe it's
better to create a universal module for such interactions, or even create
method_missing logic to use methods with universal put_, remove_,
check_, like warehouse.put_parcel and warehouse.remove_parcel.
In ruby console:
parcel = Parcel.new
parcel.weight = 10
parcel.warehouse_id = 1
parcel.save
# Create parcel and increase :current_weight of related warehouse by 10 after save
warehouse.rb:
class Warehouse < ActiveRecord::Base
has_many :parcels
attr_accessible :name, :current_weight
end
parcel.rb:
class Parcel < ActiveRecord::Base
belongs_to :warehouse
belongs_to :vehicle
attr_accessible :name, :weight, :warehouse_id, :vehicle_id
after_save :set_current_weight
#Bad code:
def set_current_weight
#wh = self.warehouse
#wh.current_weight = #wh.current_weight + self.weight
#wh.save
end
end
How about
warehouse.parcels.sum(:weight)
That way you are running a 'live' query based on the current data, rather the incrementing.
Slightly more terse version of your current model too:
def set_current_weight
#wh = self.warehouse
#wh.current_weight += self.weight
#wh.save
end
The current_weight of the warehouse is really not part of a Parcel object mandate. You have also given it more than one reason to change. Thus, this breaks the single responsibility principle.
I would suggest removing :current_weight and set_current_weight altogether. Get the total weight inside warehouse like this:
def Warehouse < ActiveRecord::Base
has_many :parcels
# ...
def current_weight
parcels.sum(:weight)
end
end
As suggested by #muttonlamb in his post.

How to populate rails table with data from other tables?

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

Rails rolling up values in a model

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

Resources