znamenica/dneslov

View on GitHub
app/models/nomen.rb

Summary

Maintainability
A
0 mins
Test Coverage
# bind_kind_name(string)  - kind of binding
# bond_to_id(int)         - id of name which the name is linked (bond) to
class Nomen < ActiveRecord::Base
   extend TotalSize

   # enum :bind_kind_name, %i(несвязаное прилаженое уменьшительное переводное подобное переложеное присвоеное)

   has_many :memory_names
   has_many :memories, through: :memory_names
   has_many :children, class_name: :Nomen, foreign_key: :bond_to_id

   belongs_to :bind_kind, primary_key: :key, foreign_key: :bind_kind_name, class_name: :Subject
   belongs_to :bond_to, primary_key: :id, foreign_key: :bond_to_id, class_name: :Name
   belongs_to :root, primary_key: :id, foreign_key: :root_id, class_name: :Name
   belongs_to :name

   scope :by_token, -> text do
      left_outer_joins(:name).where("unaccent(names.text) ~* unaccent(?)", "\\m#{text}.*").distinct
   end

   # required for short list
   scope :with_key, -> _ do
      join_name = table.table_alias || table.name
      selector = self.select_values.dup | ["#{join_name}.id AS _key"]

      select(selector).group('_key').reorder("_key")
   end

   scope :with_value, -> context do
      join_name = table.table_alias || table.name
      language_codes = [ context[:locales] ].flatten
      alphabeth_codes = Languageble.alphabeth_list_for(language_codes).flatten
      selector = self.select_values.dup |
        ["#{join_name}_names.text || COALESCE(' < ' || #{join_name}_bond_toes.text, '') || ' (' || #{join_name}_names.language_code || '_' || #{join_name}_names.alphabeth_code || ')' AS _value"]

      join = "LEFT OUTER JOIN names AS #{join_name}_names
                           ON #{join_name}.name_id = #{join_name}_names.id
              LEFT OUTER JOIN names AS #{join_name}_bond_toes
                           ON #{join_name}.bond_to_id = #{join_name}_bond_toes.id"

      # binding.pry
      joins(join).select(selector).group(:id, "#{join_name}_names.text", "#{join_name}_bond_toes.text", "#{join_name}_names.language_code", "#{join_name}_names.alphabeth_code")
   end

   scope :with_root_name, -> context do
      join_name = table.table_alias || table.name
      language_codes = [ context[:locales] ].flatten
      selector = self.select_values.dup
      if self.select_values.dup.empty?
         selector << "#{join_name}.*"
      end
      selector << "root_names.text || ' (' || root_descriptions.text || ')' AS _root_name"

      join = "LEFT OUTER JOIN nomina AS root_#{join_name}
                           ON root_#{join_name}.id = #{join_name}.root_id
                    LEFT JOIN names AS root_names
                           ON root_names.id = root_#{join_name}.name_id
                         JOIN subjects AS root_languages
                           ON root_languages.key = root_names.language_code
              LEFT OUTER JOIN descriptions AS root_descriptions
                           ON root_descriptions.describable_id = root_languages.id
                          AND root_descriptions.describable_type = 'Subject'
                          AND root_descriptions.language_code IN ('#{language_codes.join("', '")}')"

      joins(join).select(selector).group(:id, 'root_names.text', 'root_descriptions.text')
   end

   scope :with_bond_to_name, -> context do
      join_name = table.table_alias || table.name
      language_codes = [ context[:locales] ].flatten
      selector = self.select_values.dup
      if selector.empty?
         selector << "#{join_name}.*"
      end
      selector << "bond_to_names.text || ' (' || bond_to_descriptions.text || ')' AS _bond_to_name"

      join = "LEFT OUTER JOIN nomina AS bond_to_#{join_name}
                           ON bond_to_#{join_name}.id = #{join_name}.bond_to_id
                    LEFT JOIN names AS bond_to_names
                           ON bond_to_names.id = bond_to_#{join_name}.name_id
              LEFT OUTER JOIN subjects AS bond_to_languages
                           ON bond_to_languages.key = bond_to_names.language_code
              LEFT OUTER JOIN descriptions AS bond_to_descriptions
                           ON bond_to_descriptions.describable_id = bond_to_languages.id
                          AND bond_to_descriptions.describable_type = 'Subject'
                          AND bond_to_descriptions.language_code IN ('#{language_codes.join("', '")}')"

      joins(join).select(selector).group(:id, 'bond_to_names.text', 'bond_to_descriptions.text')
   end

   scope :by_root, -> do
      model.where(root_id: self.select(:root_id))
   end

   scope :with_bind_kind_name, -> context do
      join_name = table.table_alias || table.name
      language_codes = [ context[:locales] ].flatten
      selector = self.select_values.dup
      if selector.empty?
         selector << "#{join_name}.*"
      end
      selector << 'bind_kind_names.text AS _bind_kind_name'

      join = "LEFT OUTER JOIN subjects AS bind_kinds
                           ON bind_kinds.kind_code = 'NameBind'
                          AND bind_kinds.key = #{join_name}.bind_kind_name
                         JOIN descriptions AS bind_kind_names
                           ON bind_kind_names.describable_id = bind_kinds.id
                          AND bind_kind_names.describable_type = 'Subject'
                          AND bind_kind_names.language_code IN ('#{language_codes.join("', '")}')"

      joins(join).select(selector).group(:id, 'bind_kind_names.text')
   end

   singleton_class.send(:alias_method, :t, :by_token)

   validates_presence_of :bind_kind, :bind_kind_path
   validates_presence_of :bond_to_id, if: :bond?
   validates_absence_of :bond_to_id, unless: :bond?

   before_validation -> { self.bind_kind_name ||= 'несвязаное' }, on: :create
   before_validation :fill_bind_kind_path
   after_save :fill_root_id, unless: :root_id?

   def bond?
      bind_kind_name != 'несвязаное'
   end

   def fill_bind_kind_path
      parent = bond_to.nomina.where(root_id: self.root_id).first || bond_to.nomina.first if bond_to_id
      self.bind_kind_path = [parent&.store_bind_kind_path!, bind_kind.order].compact.join(".")
   end

   def store_bind_kind_path!
      save! if fill_bind_kind_path && changed?

      self.bind_kind_path
   end

   def fill_root_id
      new_root_id = self.bond_to&.nomina&.first&.root_id || name_id
      self.update!(root_id: new_root_id)
   end
end