Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Новый DSL (обсуждение) #1

Open
take-five opened this issue Aug 20, 2013 · 29 comments
Open

Новый DSL (обсуждение) #1

take-five opened this issue Aug 20, 2013 · 29 comments

Comments

@take-five
Copy link
Owner

Предположим, у нас есть XML-документ следующей структуры:

<?xml version="1.0" encoding="utf-8"?>
<shop>
  <categories>
    <category id="1">Electronics</category>
    <category id="2" parentId="1">Phones</category>
  </categories>

  <offers>
    <offer id="1" available="true">
      <name>iPhone 5</name>
      <categoryId>2</categoryId>
      <price currencyId="RUR">25000</price>
      <params>
        <param name="Color">White</param>
        <param name="Battery">2000 mA/h</param>
      </params>
    </offer>

    <offer>...</offer>
  </offers>
</shop>

Предлагается следующее API для создания парсера документа такой структуры:

parser = Ox::Mapper.document do
  # map означает, что по элементам с таким именем будет производиться итерация
  # :scope => :categories означает, что итерация будет производиться только по элементам
  # <category>, которые находятся внутри элементов <categories>
  map :category, :scope => :categories do
    # из элемента category мы берем атрибуты id и parentId, причем ко второму можно будет обратиться по имени parent_id
    attributes :id, :parentId => :parent_id
  end

  # :scope => '/shop/offers' означает, что итерация будет производиться только по элементам <offer>,
  # вложенным в элемент <offers>, вложенный в элемент <shop>, являющимся корневым
  map :offer, :scope => '/shop/offers' do
    attribute :id
    attribute :available

    # element_value означает, что из элемента <name> мы берем только его текст. к этому значению можно будет обратиться так - element[:name]
    element_value :name
    element_value :categoryId, :as => :category_id
    element :price do
      attribute :currencyId, :as => :currency_id
    end

    # множество однотипных элементов можно отобразить в коллекцию (массив)
    collection :params do
      element :param, :attributes => :name
    end
  end
end

parser.parse(STDIN) do |tag|
  case tag.name
    when :category
      p [tag.name, tag[:id], tag[:parent_id]]

    when :offer
      p [
        tag[:id],
        tag[:available],
        tag[:name],
        tag[:category_id],
        tag[:price][:currency_id], # вот это меня смущает
        tag[:price].text, # и это тоже
        tag[:params][0][:name],
        tag[:params][0].text,
        tag[:params][1][:name],
        tag[:params][1].text
      ]
  end
end

Очень приветствуются замечания и дополнения.

@vkuznetsov
Copy link

надо в map или параметр с названием вложенных элементов передавать
или делать как-то так:

map :categories do
  element :category do
    attributes :id, :parentId => :parent_id
  end
end

@take-five
Copy link
Owner Author

@vkuznetsov обновил

@Strech
Copy link

Strech commented Aug 20, 2013

А почему у тебя обращение не вот такое?

parser.parse(STDIN) do |tag|
  case tag.name
    when :category
      p [tag.name, tag.id, tag.parent_id]

    when :offer
      p [
        tag.id,
        tag.available,
        tag.name,
        tag.category_id,
        tag.price.currency_id,
        tag.price.text,
        tag.params[0].name,
        tag.params[0].text,
        tag.params[1].name,
        tag.params[1].text
      ]
  end
end

@take-five
Copy link
Owner Author

@Strech можно, конечно, и использовать method_missing, но это будет не оч. быстро, да и непонятно тогда, как быть, если встретится атрибут text

@Strech
Copy link

Strech commented Aug 20, 2013

@take-five А если без миссинга, а по карте создавать методы доступа?
Ну и можно системные методы доступа типа text завернуть ...

@take-five
Copy link
Owner Author

@Strech да можно и так, только не в первую очередь

Я все же сначала хочу сделать хороший DSL для описания структуры документа

@Strech
Copy link

Strech commented Aug 20, 2013

А что есть для namespace?

<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' xml:lang='en' id='33758cb8-0122-4f54-8807-2936de0e20f4' from='vines.machines.dev' version='1.0'>

или

<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls></stream:features>

@take-five
Copy link
Owner Author

Можно то же самое, только строками:

Ox::Mapper.document do
  map 'stream:stream' do
  end
end

Можно передавать параметр:

Ox::Mapper.document do
  map :stream, :namespace => :stream do
  end
end

Можно делать, как в рельсе:

Ox::Mapper.document do
  namespace :stream do
    map :stream do
    end
  end
end

Как лучше?

@Strech
Copy link

Strech commented Aug 20, 2013

Мне понравились вариант 1 и 2, 3-ий выглядит запутано

А еще вопрос, т.е для доступа к <required/> из второго примера, мне надо написать так?

Ox::Mapper.document do
  map :stream, :namespace => :features do
    map :starttls do
      element :required
    end
  end
end

@take-five
Copy link
Owner Author

Не оч. понял, а что именно ты хочешь получить из второго примера?

@Strech
Copy link

Strech commented Aug 20, 2013

Элемент required да/нет есть он или нет starttls.requred

@take-five
Copy link
Owner Author

Тогда так

Ox::Mapper.document do
  map :starttls, :scope => 'stream:features' do
    element :required
  end
end

@Strech
Copy link

Strech commented Aug 20, 2013

Нормально вроде бы

@vkuznetsov
Copy link

👍

1 similar comment
@Strech
Copy link

Strech commented Aug 20, 2013

👍

@take-five
Copy link
Owner Author

@Strech а можешь прислать XML-документы, которые ты обрабатываешь?

@take-five
Copy link
Owner Author

Господа, еще предлагаю совместно подумать над названиями методов в DSL. Имена map, element и element_value придуманы наспех и, честно говоря, не очень-то мне нравятся.

Смотрите, сейчас есть такие слова:

  • map - означает, что указанные элементы будут попадать в финальную выборку.
  • element - указывает, что указанный элемент будет доступен как свойство родительского элемента.
  • element_value - указывает, что текстовое значение данного элемента будет доступно как атрибут родительского элемента
  • attribute - указывает, что указанный атрибут элемента будет доступен как атрибут родительского элемента (простите за тафтологию)
  • collection - указывает, что элементы с заданным именем будут доступны в родительском объекте как свойство (массив)

Давайте подумаем, как можно лучше назвать методы map, element и element_value, а то эти названия какие-то не говорящие

@take-five
Copy link
Owner Author

Можно вообще установить такое правило, что в финальную выборку попадают только элементы, объявленные непосредственно в блоке Ox::Mapper.document do ... end на первом уровне

@Strech
Copy link

Strech commented Aug 20, 2013

<!-- 1 -->
<body xmlns="http://jabber.org/protocol/httpbind" xmlns:stream="http://etherx.jabber.org/streams">
  <stream:features>
    <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
  </stream:features>
</body>

<!-- 2 -->
<body xmlns="http://jabber.org/protocol/httpbind">
  <stream:features xmlns:stream="http://etherx.jabber.org/streams"/>
</body>

<!-- 3 -->
<body xmlns="http://jabber.org/protocol/httpbind">
  <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>
</body>

<!-- 4 -->
<body xmlns="http://jabber.org/protocol/httpbind" xmlns:xmpp="urn:xmpp:xbosh" xmlns:stream="http://etherx.jabber.org/streams" charsets="UTF-8" from="im.test-pulscen.ru" hold="1" inactivity="20" polling="5" requests="2" sid="d4b05531-63ff-4ee9-b988-49aefbd7b54e" ver="1.6" wait="60" xmpp:version="1.0">
  <stream:features>
    <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
      <mechanism>PLAIN</mechanism>
    </mechanisms>
  </stream:features>
</body>

@vkuznetsov
Copy link

Поддерживаю отмену map в пользу вывода только корневого уровня (Смысла писать map внутри другого map нет)
element, по-моему, название нормальное.
Когда он вызывается с блоком то производится разбор аттрибутов и вложенных элементов. Если блока нет, то используется только значение. Т.о. от element_value можно отказаться.

@Strech
Copy link

Strech commented Aug 20, 2013

element_value не нравится

@take-five
Copy link
Owner Author

Т.о. остается три метода: element, attribute и collection. Да, это уже гораздо лучше

@take-five
Copy link
Owner Author

Вместо маловнятного слова scope можно использовать более понятное слово under:

Ox::Mapper.document do
  element :category, :under => :categories

  under :shop do
    under :offers do
      element :offer
    end
  end
end

Как считаете?

@Strech
Copy link

Strech commented Aug 20, 2013

Ox::Mapper.document do
  element :category, :inside => :categories

  inside :shop do
    inside :offers do
      element :offer
    end
  end
end

@take-five
Copy link
Owner Author

inside - тоже хорошо )

Вова, как считаешь?

@vkuznetsov
Copy link

А чем scope непонятен?

@vkuznetsov
Copy link

Если не scope, то плюсую inside

@take-five
Copy link
Owner Author

Подытожим:

parser = Ox::Mapper.document do
  inside :shop do
    inside :categories do
      element :category do
        attribute :id
        attribute :parentId, :as => :parent_id
      end # </category>
    end # </categories>

    inside :offers do
      element :offer do
        attributes :id, :available

        element :name
        element :categoryId, :as => :category_id
        element :price do
          attribute :currencyId, :as => :currency_id
        end

        collection :params do
          element :param, :attributes => :name
        end
      end # </offer>
    end # </offers>
  end # </shop>
end

По-моему, получилось секси )

@Strech
Copy link

Strech commented Aug 21, 2013

@take-five @vkuznetsov Да, вышло круто, легко читается и выглядит круто

Repository owner locked and limited conversation to collaborators Jul 11, 2014
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants