Rails: How do you show a (number) difference temporarily? - ruby-on-rails

I am trying to implement a reputation system in my app (similar to stackoverflow). I want to be able to show any recent additions or subtractions to the reputation score next to it. What is the best way to go about such implementation?
For e.g Reputation Score: 150 +10 or Reputation Score: 150 -20
The only method I can think of right now is create another column/field called temporary_reputation and keep the most recent addition/subtraction in there. And then maybe create a cron to clear that column every 20-30minutes. Maybe I can do something with the cache?

I think what you suggest will not solve your problem.
I would prefer implementing an extra model eg: ScoreChange which will have
timestamp
value
and the relations that are needed.
That way for each item with a reputation score you can have an aggregated score field and then show all the changes you want.
eg:
class Item < ActiveRecord::Base
has_many :score_changes
def last_changes(n)
score_changes.last_changes_sum(n)
end
def score_with_last_change(n = 3)
[score, last_changes(n)]
end
and
class ScoreChange < ActiveRecord::Base
belongs_to :item
def self.last_changes_sum(n)
order('timestamp desc').limit(n).pluck(:value).inject(&:+)
end
end

Related

where constraint on a related record

I'm not getting a concept (nothing new there) on how to scope a Active Record query. I want to only receive the records where there is a certain condition in a related record. The example I have happens to be polymorphic just in case that is a factor. I'm sure there is somewhere where this is explained but I have not found it for whatever reason.
My Models:
class User < ActiveRecord::Base
belongs_to :owner, polymorphic: true
end
class Member < ActiveRecord::Base
has_one :user, as: :owner
end
I want to basically run a where on the Member class for related records that have a certain owner_id/owner_type.
Lets say we have 5 Members with ids 1-5 and we have one user with the owner_id set to 3 and the owner_type set to 'Member'. I want to only receive back the one Member object with id 3. I'm trying to run this in Pundit and thus why I'm not just going at it form the User side.
Thanks for any help as always!!!
Based on your comment that you said was close I'd say you should be able to do:
Member.joins(:user).where('users.id = ?', current_user.id)
However based on how I'm reading your question I would say you want to do:
Member.joins(:user).where('users.owner_id = ?', current_user.id)
Assuming current_user.id is 3.
There may be a cleaner way to do this, but that's the syntax I usually use. If these aren't right, try being a little more clear in your question and we can go from there! :)

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

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

How to translate a basic math formula into use in a Rails App - where to put it?

Thanks to anyone sparing a bit of their time in advance, & apologies as I'm sure this is a frustratingly basic thing to solve.
How would I apply a formula like:
total_score = start_score + sum(per_game_scores)/no_of_games - (time/some_value);
...to amend a value in a rails app, for each iteration of per_game_score?
Say, for example, this is a fantasy football scenario. I'd like to be able to submit a form after each game with the player's per_game_score & have the total_score stat update accordingly...
Being very new to Rails, I know the basics of all of this process- except where to store the formula, how to access it on form submission & how to ensure it's executed to keep total_score value current.
Thanks again for any help, am already very impressed with the community on here in my short time!
That sort of thing might work well as a method on the player. I'm assuming you have a Player model, and that it has_many :games
class Player < ActiveRecord::Base
has_many :games
before_save :update_total_score
def update_total_score
self.total_score = start_score + games.average(:score) - time/some_value
end
end
Then you can hook it up with before_save callbacks:
class Game < ActiveRecord::Base
belongs_to :player
before_save :update_player_total_score
private
def update_player_total_score
player.update_total_score
end
end

Update parent Model on child update

I have two scaffold-generated models: Book and Bookbag. A Bookbag has-many Books, and a Book belongs-to a Bookbag. Each Book has a weight, and each Bookbag has an average-weight that is supposed to store the average weight of all of its Books. What is the best way to keep average-weight up to date?
Using a before-save filter on Bookbag doesn't work because it's not called on every update to a Book it contains, and I don't want to update average-weight on every Book update, only when a Book's weight changes.
A quick solution might be something along these lines:
class Book
def before_save
self.bookbag.update_avg if self.weight_changed?
end
end
There is another solution, if you can afford to recalculate the average as needed.
class Bookbag < ActiveRecord::Base
has_many :books
def weight_average
self.books.average(:weight)
end
end
You don't need any callbacks in this case, and you leverage your database's ability to do calculations.

Resources