<commit_before>class Moysklad::Resources::Base
  PREFIX_PATH = 'exchange/rest/ms/xml/'

  def self.inherited superclass 
    super
    Moysklad::Resources.register_resource superclass
  end

  # Возвращает этот-же ресурс только индексированный
  #
  # @return [Moysklad::Resources::Indexed]
  def self.indexed *args
    Moysklad::Resources::Indexed.new new(*args)
  end

  # https://support.moysklad.ru/hc/ru/articles/203404253-REST-сервис-синхронизации-данных
  def initialize client: nil
    raise "Должен быть Moysklad::Client" unless client.is_a? Moysklad::Client
    @client = client
  end

  # Возвращает список элементов как есть
  #
  # @return [Array of Moysklad::Entities::Base]
  def list params={}
    parse client.get list_path, params
  end

  # Возвращает страницу со списком элементов
  #
  # @return [Moysklad::Entities::Page]
  def page params={}
    parse_page client.get list_path, params
  end

  # Забираем элемент по uuid
  #
  # @return [Moysklad::Entities::Base]
  def get uuid
    parse client.get item_path uuid
  end

  # Модифицируем элемент по uuid
  #
  # @return [Moysklad::Entities::Base]
  def update model
    create model
  end

  # Создаем запись
  #
  # @param [Moysklad::Entities::Base]
  #
  # Возвращается созданная на сервере сущность
  #
  # @return [Moysklad::Entities::Base]
  def create model
    raise "Должна быть модель типа Moysklad::Entities::Base" unless model.is_a? Moysklad::Entities::Base
    parse client.put create_path, prepare_resource(model)
  end

  # Удаляем запись по uuid
  #
  # @param uuid
  def delete uuid
    client.delete item_path uuid 
  end

  def self.type
    ActiveSupport::Inflector.singularize name.split('::').last.to_sym
  end

  def self.pluralized_type
    ActiveSupport::Inflector.underscore ActiveSupport::Inflector.pluralize type
  end

  def self.entity_class
    ActiveSupport::Inflector.constantize "Moysklad::Entities::#{type.to_s}"
  end

  private

  attr_reader :client

  def prepare_resource resource
    if resource.is_a? Moysklad::Entities::Base
      resource.to_xml.to_s
    else
      resource.to_s
    end
  end

  def parse content
    self.class.entity_class.parse parse_content content
  end

  def parse_content content
    Nokogiri::XML content
  end

  def parse_page content
    col = Moysklad::Entities::Collection.parse parse_content content

    # TODO Парсится два раза. Оптимизировать. Например сделать динамические CollectionFeature 
    # и парсить через них

    items = parse content
    Moysklad::Entities::Page.new items, col.total,  col.start, col.count
  end

  def item_path uuid
    prefix_path + '/' + uuid
  end

  def create_path
    prefix_path
  end

  def list_path
    prefix_path + '/list'
  end

  def prefix_path
    PREFIX_PATH + self.class.type
  end

end
<commit_msg>Ловим специальные XML ошибки moysklad<commit_after>class Moysklad::Resources::Base
  PREFIX_PATH = 'exchange/rest/ms/xml/'

  class XMLResponseError < StandardError
    def initialize(xml)
      @xml = xml
    end

    def to_s
      message
    end

    def message
      @xml.to_s
    end
  end

  def self.inherited superclass
    super
    Moysklad::Resources.register_resource superclass
  end

  # Возвращает этот-же ресурс только индексированный
  #
  # @return [Moysklad::Resources::Indexed]
  def self.indexed *args
    Moysklad::Resources::Indexed.new new(*args)
  end

  # https://support.moysklad.ru/hc/ru/articles/203404253-REST-сервис-синхронизации-данных
  def initialize client: nil
    raise "Должен быть Moysklad::Client" unless client.is_a? Moysklad::Client
    @client = client
  end

  # Возвращает список элементов как есть
  #
  # @return [Array of Moysklad::Entities::Base]
  def list params={}
    parse client.get list_path, params
  end

  # Возвращает страницу со списком элементов
  #
  # @return [Moysklad::Entities::Page]
  def page params={}
    parse_page client.get list_path, params
  end

  # Забираем элемент по uuid
  #
  # @return [Moysklad::Entities::Base]
  def get uuid
    parse client.get item_path uuid
  end

  # Модифицируем элемент по uuid
  #
  # @return [Moysklad::Entities::Base]
  def update model
    create model
  end

  # Создаем запись
  #
  # @param [Moysklad::Entities::Base]
  #
  # Возвращается созданная на сервере сущность
  #
  # @return [Moysklad::Entities::Base]
  def create model
    raise "Должна быть модель типа Moysklad::Entities::Base" unless model.is_a? Moysklad::Entities::Base
    parse client.put create_path, prepare_resource(model)
  end

  # Удаляем запись по uuid
  #
  # @param uuid
  def delete uuid
    client.delete item_path uuid
  end

  def self.type
    ActiveSupport::Inflector.singularize name.split('::').last.to_sym
  end

  def self.pluralized_type
    ActiveSupport::Inflector.underscore ActiveSupport::Inflector.pluralize type
  end

  def self.entity_class
    ActiveSupport::Inflector.constantize "Moysklad::Entities::#{type.to_s}"
  end

  private

  attr_reader :client

  def prepare_resource resource
    if resource.is_a? Moysklad::Entities::Base
      resource.to_xml.to_s
    else
      resource.to_s
    end
  end

  def parse content
    xml = parse_content content
    raise XMLResponseError, xml if xml.root.name == 'error'
    self.class.entity_class.parse xml
  end

  def parse_content content
    Nokogiri::XML content
  end

  def parse_page content
    col = Moysklad::Entities::Collection.parse parse_content content

    # TODO Парсится два раза. Оптимизировать. Например сделать динамические CollectionFeature
    # и парсить через них

    items = parse content
    Moysklad::Entities::Page.new items, col.total,  col.start, col.count
  end

  def item_path uuid
    prefix_path + '/' + uuid
  end

  def create_path
    prefix_path
  end

  def list_path
    prefix_path + '/list'
  end

  def prefix_path
    PREFIX_PATH + self.class.type
  end
end

{'fraction_non_alphanumeric': 0.0842983651226158, 'fraction_numerical': 0.0030653950953678476, 'mean_word_length': 3.4157894736842107, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 12}, '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': '337688', 'n_tokens_mistral': 2125, 'n_tokens_neox': 2164, 'n_words': 491}