A
A
Anton Ivanov2017-05-19 18:42:07
Ruby on Rails
Anton Ivanov, 2017-05-19 18:42:07

Why does the behavior of the HABTM association depend on the position of include in the model?

Hello.
I've spent the last hour debugging a very strange behavior of rails.
There are following
app/models/user.rb models

class User < ApplicationRecord
  ...
  has_many :images
  has_many :videos
  ...
  has_many :tags
  ...
end

app/models/image.rb
class Image < ApplicationRecord
  ...
  belongs_to :user
  ...
  has_and_belongs_to_many :tags
  ...
  include TagsFunctions
  ...
end

app/models/video.rb
class Video < ApplicationRecord
  ...
  include TagsFunctions
  ...
  belongs_to :user
  ...
  has_and_belongs_to_many :tags
  ...
end

app/models/concerns/tags_functions.rb
module TagsFunctions
  extend ActiveSupport::Concern

  # хак для заполнения тэгов у вновь создаваемых моделей
  included do
    attr_accessor :tags_after_creation

    after_create -> { self.tags_string = tags_after_creation if tags_after_creation.present? }
  end

  def tags_string
    tags.pluck(:text).join(',')
  end

  def tags_string=(value)
    unless user  # Если пользователь еще не заполнен, запоминаем тэги и выходим
      @tags_after_creation = value
      return
    end

    @tags_after_creation = ''
    self.tags = []
    value.to_s.split(',').map(&:strip).each do |tag_text|  # разбиваем строку, используя запятую, как разделитель
      tag = user.tags.find_or_create_by(text: tag_text)  # получаем/создаем нужные тэги
      self.tags << tag                                # записываем тэги в ассоциацию
    end
  end
end

We execute the following code:
user = User.first
tags_string = 'test'
image = user.images.create(tags_string: tags_string)
video = user.videos.create(tags_string: tags_string)

As a result, video.tagswe have two identical (duplicate) tags.
B image.tags- one tag.
If you run the code like this:
user = User.first
tags_string = 'test'
image = Image.create(user: user,  tags_string: tags_string)
video = Video.create(user: user, tags_string: tags_string)

then everything is in order, one tag both there and there.
And now even more mysticism (for me). If you video.rbmove include TagsFunctionsafter , then everything starts working correctly with the first version of the code. I thought I knew rails well, but this behavior baffles me. Rails version: 5.1.1 Who can explain this behaviour? has_and_belongs_to_many :tags

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question