Update Attribute in Other Model - ruby-on-rails

I have two tables/models (User and Holiday). A user has a set number of holidays. In the Holiday model, a user can book a number of holidays (integer).
Example:
1) John has a total 10 holiday credit
2) John books 4 holidays
Result: John has 6 holidays left
I know it has something to do with adding a method in User and passing the holidays taken into it.
How do I go about deducting the holidays booked from the holiday credit?

In the in User model:
# assuming, you have a method `holidays_booked`,
# that returns the number of booked holidays
def holidays_left
10 - holidays_booked
end
That's it.

If each user has a holiday property/field, then through associations between the two tables the value can be calculated.
When setting up the database a default value for the holiday credit can be defined. For example:
If you are adding a column to hold the value:
add_column :users, :holiday_credit, :integer, :default => 10
Now every user will have a default holiday credit in your database. This could also be done at object creation time during the object's initialization.
To update the value, a method within the User model can be written like so:
class User < ActiveRecord::Base
def calculate_holiday_credits
self.holiday_credit = default_value - Holiday.find(self.id).count
end
end
The function can be modified to accommodate your needs, but I hope this gives you the general idea.
In short, you want to search in the other table the number of times the id for the user model shows up and carry out your calculation with the results.
The booking mechanism would be sort of the inverse of this. In the holiday table, you want to make a method that creates an entry in the database with the user who booked the holiday, so that when the user runs the method detailed above, the value is update appropriately.

Related

Increase a counter in parent when a child is added in has_many association

I have a schema where User has many Student with a user_id field.
In the User table, I am saving a counter next_student_number with default value as 1, and there is a roll_number column in Student.
In the Student class, I have before_create :generate_roll_number callback which sets the student's roll number to next_student_number and increments the value in User class.
Some thing like this :-
def generate_roll_number
self.roll_number = user.next_roll_number
user.increment! :next_roll_number
end
I feel there will be an issue when two records are trying to save at the same time here. Either they'll have a clash, or some roll numbers will be skipped.
What is the best way to implement this?
I think this should work fine:
Controller
def create
Student.transaction do
Student.create(user_id: current_user, ...)
end
end
Student Model
before_create :generate_roll_number
def generate_roll_number
user.increment! :next_roll_number
# Fires query like
# UPDATE users SET next_roll_number=2, WHERE id=xxx
self.roll_number = user.next_roll_number
end
Now, if any error happens while Student record is saved, the transaction will also rollback the incremented next_roll_number value in User table

Rails use Boolean similar to counter_cache?

A Miniatures model has many Collections. Users can have and vote for the best Collection version of a Miniature. The votes are in a model called Imagevotes which update a counter_cache attribute in the Collections model.
What I want to do is flag Collections which are ranked first for a Miniature as GOLD, then rank the 2nd, 3rd and 4th as SILVER. I realise I can do this on the Miniature model by selecting the #miniature.collection.first, but I would like to be able to store that like you would store the vote-count in a counter_cache so that I could display the total number of GOLDS or SILVERS for any one user.
Is there a way that each model could have Boolean fields called GOLD and SILVER which would be updated as new votes are cast in the same way that a counter_cache is updated?
Any pointers and further reading much appreciated.
Update:
It occurs to me that this could also be done with a sort of second index column. A vote_position column if you will, that updated with a number from "1" for the record with the highest counter_cache number and ascended from there. Then I could use #miniature.collection.where(:vote_position => "1") or similar. Perhaps this is more ideal?
As it seems for me you just need to implement method in Miniature model:
def set_gold_and_silver
top_collections = self.collections.order("imagevotes_count desc").limit(4)
gold = top_collections.shift
gold.update_attribute :is_gold, true if gold
top_collections.each {|s| s.update_attribute :is_silver, true}
end
after that you can add it to after_create filter of Imagevotes model:
class Imagevotes < ActiveRecord::Base
after_create :set_gold_and_silver
def set_gold_and_silver
self.miniature.set_gold_and_silver
end
end

Model 2 way relationships to store id in user table

I'm trying to understand how I would go about storing the ID of one model record in a separate active record table (in this case the user table) when it gets created.
Basically, I have two models. A user which has_many :taxes and a tax model which belongs_to: user. In my application a user can only have one tax record, which currently I'm achieving by storing the user.id in a column in the tax model, and checking in a before filter to see if the user already has already created a tax record (which checks to see if their user.id is in the table.)
create_table "taxes", force: true do |t|
t.integer "user_id"
t.integer "income"
t.integer "taxes"
.....
Keep in mind this is all currently in an index action, which is kind of pointless since a user can only have one tax record, and only view their own tax records. It should be done with the show action I assume.
Now here's the problem, when a user creates their single tax record, the tax model is setup like this, all interacting with the taxes active record table:
def store_raw_taxes
tax = Tax.new(user_id: user.id, income: income, taxes: taxes, rrsp: rrsp)
tax.save
end
In order to use show (and check to see if the user already has created a tax record), I want to store the tax_id in the actual User's table in their row. I have created a migration AddTaxIdToUsers which made a reference and added a column called tax_id to the users table.
I don't know how to store the created tax record's ID in the user table though. This is a 2 way relationship I guess, but I don't understand how in my store_raw_taxes function, I would also interact with the User model. I guess it would be done in a after_save callback?
Would I also need to add belongs_to: tax to the user model in that case?
I would go a bit differently.
In a one-to-one relation you don't need the belongs_to definition, but the has_one
The conceptually right way to do it, in my point of view, would be:
user.rb:
has_one :tax
Then, your method would be
#user=User.find(..)
#user.build_tax(income: income, taxes: taxes, rrsp: rrsp)
Basically, I have two models. A user which has_many :taxes and a tax model which belongs_to: user. In my application a user can only have one tax record
If a user can only have one tax record, then you should not use has_many :taxes but has_one :tax instead. Rails will handle everything automatically.
See http://guides.rubyonrails.org/association_basics.html#the-has-one-association
Then you won't need "before filter to see if the user already has already". However is you want to ensure that user have a tax record at all times you can validates_presence_of :tax
See http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates
It is possible, if you use nested form, that you don't need TaxesController at all.
Also if you use nested form you will be using accepts_nested_attributes and therefore will not need your store_raw_taxes method anymore
See http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

How can I update and save one model from another in Rails?

In my program, I have a model, Calorie, that takes what a person ate and gives them a point total. After that point value is calculated for each day's nutritional information, I want to update the 'points' variable in the User model.
The code I have in the Calorie model is
before_save :calculate_points
def calculate_points
# snipped calculations
User.where(user_id).first.point_calculation
end
In the User model, I have
def point_calculation
self.points = Calorie.where(user_id: id).sum(:points)
end
I've tested the point_calculation model by creating a callback before_save, and it works fine there. But it makes a lot more sense to update after each new Calorie entry, instead of a user updating their settings. Any advice? What am I missing?
Thanks for your help.
I'm assuming your Calorie model has a has_one relationship with the User, and User has_many Calories.
In Calorie model:
after_save :update_user_points
def update_user_points
self.user.update_calorie_points!
end
In User model:
def update_calorie_points!
self.update_column(:points, self.calories.sum(:points))
end

Rails 3 Associations for Traffic Data without Foreign Key

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

Resources