Обновление атрибутов одной модели из контроллера другой модели

У меня есть простое приложение для сообщений и пользователей, в котором пользователи отправляют сообщения и голосуют за них. У меня есть две модели, пост и пользователь, и прямо сейчас я работаю с пост-моделью, чтобы добавить функцию «голосования».

Каждое сообщение имеет атрибуты :id, :upvote и :users_voted_by. У каждого пользователя есть атрибуты :username, :posts_voted_on и good_karma.

Когда пользователь нажимает кнопку «за» в сообщении, мне нужно убедиться, что текущий пользователь не находится в столбце :users_voted_by для этого сообщения, если нет, добавьте 1 к атрибуту :upvote этого сообщения. Эта часть сделана.

Но мне также нужно добавить сообщение :id в поле :posts_voted_on текущего пользователя и добавить 1 в поле :good_karma отправителя.

Почтовая модель:

class Post < ActiveRecord::Base
  attr_accessible :comment_count, :downvote, :id, :text, :title, :upvote, :url, :user, :users_voted_by
end

Пользовательская модель:

class User < ActiveRecord::Base
  attr_accessible :email, :password, :password_confirmation, :username, :posts_voted_on, :good_karma, :bad_karma

  attr_accessor :password
  before_save :encrypt_password

  validates_confirmation_of :password
  validates_presence_of :password, :on => :create
  validates_presence_of :email
  validates_uniqueness_of :email
  validates_presence_of :username
  validates_uniqueness_of :username

  def self.authenticate(email, password)
    user = find_by_email(email)
    if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
      user
    else
      nil
    end
  end

  def encrypt_password
    if password.present?
      self.password_salt = BCrypt::Engine.generate_salt
      self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
    end
  end
end

Мой метод upvote находится внутри posts_controller:

  def upvote
    @post = Post.find(params[:post_id])

    respond_to do |format|
      if @post.users_voted_by.index(current_user.username) == nil && @post.update_attributes(:upvote => @post.upvote + 1, :users_voted_by => @post.users_voted_by + ',' + current_user.username)
        format.html { redirect_to @post }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

5-я линия — это то, где волшебство. Это гарантирует, что сообщение, за которое я голосую, не имеет текущего пользователя в поле :users_voted_by. Затем он обновляет поля :upvote и :users_voted_by.

Как я могу добавить к этому методу (действие?), чтобы он также обновлял атрибуты голосующего пользователя и отправителя сообщения для сохранения увеличения кармы.


person alt    schedule 10.01.2013    source источник


Ответы (1)


Угадайте сервисный объект здесь может помочь что-то вроде (не проверено)

class Voter
  def initialize(post, user)
    @post = post
    @user = user
  end

  def upvote
    return false unless @post.users_voted_by.index(@user.username)
    @post.upvote += 1
    @post.users_voted_by = @post.users_voted_by + ',' + @user.username
    @user.good_carma += 1
    @post.save && @user.save
  end

  def downvote
    ...
  end
end

Тогда контроллер будет выглядеть

   def upvote
    @post = Post.find(params[:post_id])
    respond_to do |format|
      if Voter.new(@post, current_user).upvote
        format.html { redirect_to @post }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end
person dimuch    schedule 10.01.2013
comment
Куда девается служебный объект? - person alt; 10.01.2013
comment
app/services/voter.rb. Папка services не существует по умолчанию, просто создайте ее. На самом деле, вы можете назвать его как хотите. - person dimuch; 10.01.2013
comment
Как включить его в свое приложение? Должен ли я делать что-то еще, чтобы заставить класс Voter работать? - person alt; 10.01.2013
comment
Ничего не делать, просто создать папку/файл. - person dimuch; 10.01.2013
comment
Сладкий! Как я могу хранить массив пользователей, которые проголосовали за сообщение. Кто-то рекомендовал сериализацию. Любые подсказки? - person alt; 10.01.2013
comment
Вполне стандартный подход. Подробности и примеры см. в документации api.rubyonrails.org/classes/ActiveRecord/Base.html. - person dimuch; 10.01.2013
comment
давайте продолжим это обсуждение в чате - person alt; 10.01.2013