<commit_before>class ActiveShepherd::ApplyChanges
  attr_reader :aggregate, :hash

  def initialize(aggregate, hash)
    @aggregate = aggregate
    @hash      = hash.dup
  end

  def apply_changes
    handle_create_or_destroy_keys

    hash.each do |attribute_or_association_name, (before, after)|
      association_reflection = aggregate.model.class.reflect_on_association(attribute_or_association_name.to_sym)

      if association_reflection.present?
        if aggregate.traverse_association?(association_reflection)
          unless after.nil?
            raise ::ActiveShepherd::AggregateRoot::BadChangeError
          end

          foreign_key_to_self = association_reflection.foreign_key

          if association_reflection.macro == :has_many
            association = aggregate.model.send(association_reflection.name)

            before.each do |index, changes_for_associated_model|
              # FIXME
              until association.size >= (index + 1)
                association.build
              end
              # /FIXME

              associated_model = association[index]

              if associated_model.nil?
                raise ::ActiveShepherd::AggregateRoot::BadChangeError, "Can't find record ##{index}"
              end

              ::ActiveShepherd::Aggregate.new(associated_model, foreign_key_to_self).changes = changes_for_associated_model
            end
            
          elsif association_reflection.macro == :has_one
            associated_model = aggregate.model.send(association_reflection.name)
            changes_for_associated_model = before
            ::ActiveShepherd::Aggregate.new(associated_model, foreign_key_to_self).changes = changes_for_associated_model
          end
        end
      else
        attribute_name = attribute_or_association_name
        setter = "#{attribute_or_association_name}="

        current_value = aggregate.model.send(attribute_name)

        before = aggregate.deserialize_value(attribute_name, before)
        after  = aggregate.deserialize_value(attribute_name, after)
 
        unless current_value == before
          raise ::ActiveShepherd::AggregateRoot::BadChangeError, "Expecting "\
            "`#{attribute_or_association_name} to be `#{before.inspect}', not "\
            "`#{current_value.inspect}'"
        end

        aggregate.model.send(setter, after)
      end
    end
  end

private

  def handle_create_or_destroy_keys
    hash.delete :_create
    if hash.delete :_destroy
      aggregate.model.mark_for_destruction
    end
  end
end
<commit_msg>Reduce demeter violations :)<commit_after>class ActiveShepherd::ApplyChanges
  attr_reader :aggregate, :hash

  def initialize(aggregate, hash)
    @aggregate = aggregate
    @hash      = hash.dup
  end

  def apply_changes
    handle_create_or_destroy_keys

    association_reflections = aggregate.traversable_associations

    hash.each do |attribute_or_association_name, (before, after)|
      association_reflection = association_reflections[attribute_or_association_name]

      if association_reflection.present?
        if aggregate.traverse_association?(association_reflection)
          unless after.nil?
            raise ::ActiveShepherd::AggregateRoot::BadChangeError
          end

          foreign_key_to_self = association_reflection.foreign_key

          if association_reflection.macro == :has_many
            association = aggregate.model.send(association_reflection.name)

            before.each do |index, changes_for_associated_model|
              # FIXME
              until association.size >= (index + 1)
                association.build
              end
              # /FIXME

              associated_model = association[index]

              if associated_model.nil?
                raise ::ActiveShepherd::AggregateRoot::BadChangeError, "Can't find record ##{index}"
              end

              ::ActiveShepherd::Aggregate.new(associated_model, foreign_key_to_self).changes = changes_for_associated_model
            end
            
          elsif association_reflection.macro == :has_one
            associated_model = aggregate.model.send(association_reflection.name)
            changes_for_associated_model = before
            ::ActiveShepherd::Aggregate.new(associated_model, foreign_key_to_self).changes = changes_for_associated_model
          end
        end
      else
        attribute_name = attribute_or_association_name
        setter = "#{attribute_or_association_name}="

        current_value = aggregate.model.send(attribute_name)

        before = aggregate.deserialize_value(attribute_name, before)
        after  = aggregate.deserialize_value(attribute_name, after)
 
        unless current_value == before
          raise ::ActiveShepherd::AggregateRoot::BadChangeError, "Expecting "\
            "`#{attribute_or_association_name} to be `#{before.inspect}', not "\
            "`#{current_value.inspect}'"
        end

        aggregate.model.send(setter, after)
      end
    end
  end

private

  def handle_create_or_destroy_keys
    hash.delete :_create
    if hash.delete :_destroy
      aggregate.model.mark_for_destruction
    end
  end
end

{'fraction_non_alphanumeric': 0.07265700483091787, 'fraction_numerical': 0.0003864734299516908, 'mean_word_length': 2.673527324343506, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1064404', 'n_tokens_mistral': 1431, 'n_tokens_neox': 1385, 'n_words': 247}