<commit_before>module BraintreeRails
  module Attributes
    module ClassMethods
      def define_attributes(attributes)
        all_attributes = attributes.values.flatten.uniq
        attr_accessor(*all_attributes)
        singleton_class.send(:define_method, :attributes_for) { |action| attributes[action] }
        singleton_class.send(:define_method, :attributes) { all_attributes }
      end
    end

    module InstanceMethods
      def attributes
        self.class.attributes.inject({}) do |hash, attribute|
          value = self.send(attribute)
          hash[attribute] = value if value.present?
          hash[attribute] = value.attributes_for(:as_association) if value.respond_to?(:attributes_for) && value.changed?
          hash
        end
      end

      def attributes_for(action)
        attributes.slice(*self.class.attributes_for(action)).slice(*changed)
      end

      def assign_attributes(hash)
        hash.each do |attribute, value|
          send("#{attribute}=", value) if respond_to?("#{attribute}=")
        end
      end

      def extract_values(obj)
        self.class.attributes.inject({}) do |hash, attr|
          hash[attr] = obj.send(attr) if obj.respond_to?(attr)
          hash
        end
      end

      def changed
        return attributes.keys if new_record?
        attributes.keys.select do |attribute|
          attributes[attribute] != raw_object.send(attribute)
        end
      end

      def changed?
        changed.any?
      end
    end

    def self.included(receiver)
      receiver.extend         ClassMethods
      receiver.send :include, InstanceMethods
      receiver.send :include, ::ActiveModel::Serializers::JSON
      receiver.send :include, ::ActiveModel::Serializers::Xml
    end
  end
end<commit_msg>Fix an issue where attributes is not always serializable hash<commit_after>module BraintreeRails
  module Attributes
    module ClassMethods
      def define_attributes(attributes)
        all_attributes = attributes.values.flatten.uniq
        attr_accessor(*all_attributes)
        singleton_class.send(:define_method, :attributes_for) { |action| attributes[action] }
        singleton_class.send(:define_method, :attributes) { all_attributes }
      end
    end

    module InstanceMethods
      def attributes
        self.class.attributes.inject({}) do |hash, attribute|
          value = self.send(attribute)
          hash[attribute] = value if value.present?
          hash[attribute] = value.attributes_for(:as_association) if value.respond_to?(:attributes_for)
          hash
        end
      end

      def attributes_for(action)
        attributes_for_action = attributes.slice(*self.class.attributes_for(action))
        attributes_for_action.slice!(*changed) unless action == :as_association
        attributes_for_action
      end

      def assign_attributes(hash)
        hash.each do |attribute, value|
          send("#{attribute}=", value) if respond_to?("#{attribute}=")
        end
      end

      def extract_values(obj)
        self.class.attributes.inject({}) do |hash, attr|
          hash[attr] = obj.send(attr) if obj.respond_to?(attr)
          hash
        end
      end

      def changed
        new_record? ? changed_for_new_record : changed_for_persisted
      end

      def changed_for_new_record
        attributes.map do |attribute, value|
          attribute if value.present?
        end.compact
      end

      def changed_for_persisted
        attributes.map do |attribute, value|
          attribute if value != raw_object.send(attribute)
        end.compact
      end
    end

    def self.included(receiver)
      receiver.extend         ClassMethods
      receiver.send :include, InstanceMethods
      receiver.send :include, ::ActiveModel::Serializers::JSON
      receiver.send :include, ::ActiveModel::Serializers::Xml
    end
  end
end
{'fraction_non_alphanumeric': 0.0883419689119171, 'fraction_numerical': 0.0, 'mean_word_length': 2.5916279069767443, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '5087847', 'n_tokens_mistral': 1049, 'n_tokens_neox': 1045, 'n_words': 235}