getting the `current_user` in my User class
Oddly enough, most of this works as it has been written, however I'm not sure how I can evaluate if the current_user
has a badge, (all the relationships are proper, I am only having trouble with my methods in my class (which should partially be moved into a lib or something), regardless, the issue is specifically 1) checking if the current user has a record, and 2) if not create the corresponding new record.
If there is an easier or better way to do this, please share. The following is what I have:
# Recipe Controller
class RecipesController < ApplicationController
def create
# do something
if @recipe.save
current_user.check_if_badges_earned(current_user)
end
end
So as for this, it definitely seems messy, I'd like for it to be just check_if_badges_earned
and not have to pass the current_user
into the method, but may need to because it might not always be the current user initiating this method.
# User model
class User < ActiveRecord::Base
def check_if_badges_earned(user)
if user.recipes.count > 10
award_badge(1, user)
end
if user.recipes.count > 20
award_badge(2, user)
end
end
def award_badge(badge_id, user)
#see if user already has this badge, if not, give it to them!
unless user.badgings.any? { |b| b[:badge_id] == badge_id}
@badging = Badging.new(:badge_id => badge_id, :user_id => user)
@badging.save
end
end
end
So while the first method (check_if_badges_earned
) seems to excucte fine and only give run award_badge()
when the conditions are met, the issu开发者_JAVA百科e happens in the award_badge()
method itself the expression unless user.badgings.any? { |b| b[:badge_id] == badge_id}
always evaluates as true, so the user is given the badge even if it already had the same one (by badge_id), secondly the issue is that it always saves the user_id
as 1.
Any ideas on how to go about debugging this would be awesome!
Regardless of whether you need the current_user
behavior above, award_badge
should just be a regular instance method acting on self
instead of acting on the passed user
argument (same goes for check_if_badges_earned
). In your award_badge
method, try find_or_create_by_...
instead of the logic you currently have. For example, try this:
class User < ActiveRecord::Base
# ...
def award_badge(badge_id)
badgings.find_or_create_by_badge_id(badge_id)
end
end
To access the current_user
in your model classes, I sometimes like to use thread-local variables. It certainly blurs the separation of MVC, but sometimes this kind of coupling is just necessary in an application.
In your ApplicationController, store the current_user
in a thread-local variable:
class ApplicationController < ActionController::Base
before_filter :set_thread_locals
private
# Store thread-local variables so models can access them (Hackish, but useful)
def set_thread_locals
Thread.current[:current_user] = current_user
end
end
Add a new class method to your ActiveRecord model to return the current_user
(you could also extend ActiveRecord::Base to make this available to all models):
class User < ActiveRecord::Base
def self.current_user
Thread.current[:current_user]
end
end
Then, you'll be able to access the current user in the instance methods of your User
model with self.class.current_user
.
What you need to do first of all is make those methods class methods (call on self), which avoids needlessly passing the user reference.
Then, in your award_badge
method, you should add the Badging to the user's list of Badgings, e.g.: user.badgings << Badging.new(:badge_id => badge_id)
精彩评论