开发者

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)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜