0

I'm having a problem based on the excellent RailsCast #258 from Ryan Bates.

The situation is as follows:

class User < ActiveRecord::Base
  has_many :capabilities,
           :dependent => :destroy
  has_many :skills, :through => :capabilities,
           :uniq => true

  has_many :raters,
           :through => :capabilities,
           :foreign_key => :rater_id,
           :uniq => true

  attr_accessible :name, :skill_tokens
  attr_reader :skill_tokens      

  def skill_tokens=(tokens)
      self.skill_ids = Skill.ids_from_tokens(tokens)
  end
end

class Capability < ActiveRecord::Base
  belongs_to :user
  belongs_to :rater, class_name: "User"
  belongs_to :skill

  validates_uniqueness_of :rater_id, :scope => [:user_id, :skill_id]
end

class Skill < ActiveRecord::Base
  has_many :capabilities
  has_many :users, :through => :capabilities,
           :uniq => true

  has_many :raters, :through => :capabilities,
           :foreign_key => :rater_id
end

The form contains a normal textfield for the skill tokens which are passed as ids:

.field  
    = f.label :skill_tokens, "Skills"
    = f.text_field :skill_tokens, data: {load: @user.skills}

So a user can get many skills assigned through capabilities. While assigning the skill, the rater should also be tracked in the capability model.

Using Ryans example of jquery TokenInput I created an appropriate form to allow a user to assign (and create) skills using a tokenInput text field.

The Problem lies now in processing the data and setting the rater before the association is saved.

Through some ruby magic, self.skill_ids on the user model sets the ids used for the association model creation so the controller action is quite simple:

def update
    @user = User.find(params[:id])

    respond_to do |format|
      if @user.update_attributes(params[:user])      
        format.html { redirect_to @user, notice: 'User was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

Obviously, if I want to set the additional rater attribute on the capability model it won't work so easily with update_attributes.

So how can I achieve this with "the rails way" to do it - writing beautiful, readable code? ANY help would be greately appreciated!

origami addict
  • 108
  • 1
  • 5

1 Answers1

0

How are you setting the rater_id?

If you plan accept a user input for the rater for each skill the user adds on the form, I can't see how you'll be able to use input fields based on token inputs to achieve this. You're going to have to choose some other types of inputs.

If you plan to set the rater to the currently logged in user, or are setting the rater based on some other business logic, my approach would be overwriting the skill_ids= method in the User model to work how you want it, adding an attr_accessor to store the current_rater and passing the current_rate from the controller.

Something like:

#user.rb
attr_accessor :current_rater
def skill_ids=(ids)
  return false if current_rater.nil? || User.find_by_id(current_rater).nil?
  capabilities.where("skill_id not in (?)", ids).destroy_all
  ids.each do |skill_id|      
    capabilities.create(:skill_id => skill_id, :rater_id => self.current_rater) if capabilities.find_by_id(skill_id).nil?
  end
end

#users_controller.rb
def update
  @user = User.find(params[:id])

  #Replace 'current_user' with whatever method you are using to track the logged in user
  params[:user].merge(:current_rater => current_user) 

  respond_to do |format|
    ...
  end
end

Probably not as elegant as you were hoping, but it should do the job?

Rob d'Apice
  • 2,416
  • 1
  • 19
  • 29