<commit_before>module Structure
  module InstanceMethods
    # Validate that the structure don't include itself
    def structure_exclude_self
      errors.add(:base, "#{self.class.name.humanize} cannot be a descendant of itself.") if ancestor_ids.include? self.id
    end

    # Update descendants with new structure
    def update_descendants_with_new_structure
      # Skip this if callbacks are disabled
      unless structure_callbacks_disabled?
        # If node is not a new record and structure was updated and the new structure is sane ...
        if changed.include?(self.base_class.structure_column.to_s) && !new_record? && sane_structure?
          # ... for each descendant ...
          unscoped_descendants.each do |descendant|
            # ... replace old ancestry with new ancestry
            descendant.without_structure_callbacks do
              column = self.class.structure_column
              v = read_attribute(column)
              descendant.update_attribute(
                  column,
                  descendant.read_attribute(descendant.class.structure_column).gsub(
                      /^#{self.child_structure}/,
                      if v.blank? then
                        id.to_s
                      else
                        "#{v}/#{id}"
                      end
                  )
              )
            end
          end
        end
      end
    end

    # Apply orphan strategy
    def apply_orphan_strategy
      # Skip this if callbacks are disabled
      unless structure_callbacks_disabled?
        # If this isn't a new record ...
        unless new_record?
          # ...destroy all descendants if orphan strategy is destroy
          if self.base_class.orphan_strategy == :destroy
            unscoped_descendants.each do |descendant|
              descendant.without_structure_callbacks do
                descendant.destroy
              end
            end
          end
        end
      end
    end

    # The structure value for this record's children
    def child_structure
      # New records cannot have children
      raise Structure::StructureException.new('No child structure for new record. Save record before performing tree operations.') if new_record?
      v = "#{self.send "#{self.base_class.structure_column}_was"}"
      return id.to_s if v.blank?
      v.split(',').map{|x| x + '/' + id.to_s}.join(',')
    end

    # Ancestors = all nodes above but NOT including the current ID
    def ancestor_ids
      read_attribute(self.base_class.structure_column).to_s.split(%r|[,/]|).uniq.map { |id| cast_primary_key(id) }
    end

    def ancestor_conditions
      {self.base_class.primary_key => ancestor_ids}
    end

    def ancestors(depth_options = {})
      self.base_class.scope_depth(depth_options, depth).where(ancestor_conditions)
    end

    # Path = all nodes above and INCLUDING the current node
    def path_ids
      ancestor_ids + [id]
    end

    def path_conditions
      {self.base_class.primary_key => path_ids}
    end

    def path(depth_options = {})
      self.base_class.scope_depth(depth_options, depth).where(path_conditions)
    end

    #lineage = all nodes that are parents of the current node + current node + all descendants of the current node in the tree
    def lineage_ids
      ancestor_ids + subtree_ids
    end

    def lineage_conditions
      {self.base_class.primary_key => lineage_ids}
    end

    def lineage(depth_options = {})
      self.base_class.scope_depth(depth_options, depth).where(lineage_conditions)
    end

    def depth
      ancestor_ids.size # this is probably incorrect as it just counts the structure without regard to how layered
    end

    def cache_depth
      write_attribute self.base_class.depth_cache_column, depth
    end

    # Parent
    def parent=(parent)
      write_attribute(self.base_class.structure_column, if parent.blank? then
                                                         nil
                                                       else
                                                         parent.child_structure
                                                       end)
    end

    def parents=(new_parents)
       ancestry = if new_parents.nil?
                    nil
                  else
                    (new_parents.collect { |x| (self.base_class.find(x).child_structure.split("/") - [self.id.to_s]).join("/") }).join(",")
                  end
                  
       write_attribute(self.base_class.structure_column, structure)
    end


    def parent_id=(parent_id)
      self.parent = if parent_id.blank? then
                      nil
                    else
                      unscoped_find(parent_id)
                    end
    end

    # branches are the multiple parents of the current node
    def branches
      if ancestor_ids.empty? then
        nil
      else<commit_msg>bump version for parent helper<commit_after>module Structure
  module InstanceMethods
    # Validate that the structure don't include itself
    def structure_exclude_self
      errors.add(:base, "#{self.class.name.humanize} cannot be a descendant of itself.") if ancestor_ids.include? self.id
    end

    # Update descendants with new structure
    def update_descendants_with_new_structure
      # Skip this if callbacks are disabled
      unless structure_callbacks_disabled?
        # If node is not a new record and structure was updated and the new structure is sane ...
        if changed.include?(self.base_class.structure_column.to_s) && !new_record? && sane_structure?
          # ... for each descendant ...
          unscoped_descendants.each do |descendant|
            # ... replace old ancestry with new ancestry
            descendant.without_structure_callbacks do
              column = self.class.structure_column
              v = read_attribute(column)
              descendant.update_attribute(
                  column,
                  descendant.read_attribute(descendant.class.structure_column).gsub(
                      /^#{self.child_structure}/,
                      if v.blank? then
                        id.to_s
                      else
                        "#{v}/#{id}"
                      end
                  )
              )
            end
          end
        end
      end
    end

    # Apply orphan strategy
    def apply_orphan_strategy
      # Skip this if callbacks are disabled
      unless structure_callbacks_disabled?
        # If this isn't a new record ...
        unless new_record?
          # ...destroy all descendants if orphan strategy is destroy
          if self.base_class.orphan_strategy == :destroy
            unscoped_descendants.each do |descendant|
              descendant.without_structure_callbacks do
                descendant.destroy
              end
            end
          end
        end
      end
    end

    # The structure value for this record's children
    def child_structure
      # New records cannot have children
      raise Structure::StructureException.new('No child structure for new record. Save record before performing tree operations.') if new_record?
      v = "#{self.send "#{self.base_class.structure_column}_was"}"
      return id.to_s if v.blank?
      v.split(',').map{|x| x + '/' + id.to_s}.join(',')
    end

    # Ancestors = all nodes above but NOT including the current ID
    def ancestor_ids
      read_attribute(self.base_class.structure_column).to_s.split(%r|[,/]|).uniq.map { |id| cast_primary_key(id) }
    end

    def ancestor_conditions
      {self.base_class.primary_key => ancestor_ids}
    end

    def ancestors(depth_options = {})
      self.base_class.scope_depth(depth_options, depth).where(ancestor_conditions)
    end

    # Path = all nodes above and INCLUDING the current node
    def path_ids
      ancestor_ids + [id]
    end

    def path_conditions
      {self.base_class.primary_key => path_ids}
    end

    def path(depth_options = {})
      self.base_class.scope_depth(depth_options, depth).where(path_conditions)
    end

    #lineage = all nodes that are parents of the current node + current node + all descendants of the current node in the tree
    def lineage_ids
      ancestor_ids + subtree_ids
    end

    def lineage_conditions
      {self.base_class.primary_key => lineage_ids}
    end

    def lineage(depth_options = {})
      self.base_class.scope_depth(depth_options, depth).where(lineage_conditions)
    end

    def depth
      ancestor_ids.size # this is probably incorrect as it just counts the structure without regard to how layered
    end

    def cache_depth
      write_attribute self.base_class.depth_cache_column, depth
    end

    # Parent
    def parent=(parent)
      write_attribute(self.base_class.structure_column, if parent.blank? then
                                                         nil
                                                       else
                                                         parent.child_structure
                                                       end)
    end

    def parents=(new_parents)
       structure = if new_parents.nil?
                    nil
                  else
                    (new_parents.collect { |x| (self.base_class.find(x.id).child_structure.split("/") - [self.id.to_s]).join("/") }).join(",")
                  end
                  
       write_attribute(self.base_class.structure_column, structure)
    end


    def parent_id=(parent_id)
      self.parent = if parent_id.blank? then
                      nil
                    else
                      unscoped_find(parent_id)
                    end
    end

    # branches are the multiple parents of the current node
    def branches
      if ancestor_ids.empty? then
        nil
      else
{'fraction_non_alphanumeric': 0.06970098989692826, 'fraction_numerical': 0.0, 'mean_word_length': 1.7825099375354911, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1987676', 'n_tokens_mistral': 2581, 'n_tokens_neox': 2470, 'n_words': 697}