yml_builder 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b10e44faa9d509c11bbe34e4e0add97bb5956f15
4
+ data.tar.gz: 9e7634b0d218b487480bb242a404e9705d5a7991
5
+ SHA512:
6
+ metadata.gz: 14847041d19be93067fb9f0c2ca4cab96030115071191841b55bfcd0fbe0057c90b598140be1db47fad77018bb5551568271881b182f1a276559425d2b217f8f
7
+ data.tar.gz: 1239542340171ddf325f154722a8678787e06d50e19731c699c656ad4180c200522545b05d23043599b37424a32d7ae0e7c817a73624f78d7979378848a7e7df
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.1.0 (2015-05-11)
2
+
3
+ * Начальная версия
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Stan Zhuravlev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,300 @@
1
+ # YmlBuilder
2
+
3
+ Библиотека YmlBuilder предназначена для формирования прайс-листов в формате Яндекс.Маркет. Библиотека обеспечивает формирвоание
4
+ прайс-листов с учетом правил Yandex (названия секций, ключей, порядок следования ключей и пр.), а также валидацию
5
+ большинства ошибок, связанных с генерацией прайс-листов.
6
+
7
+ ## Установка
8
+
9
+ Добавить в Gemfile:
10
+
11
+ ```ruby
12
+ gem 'yml_builder'
13
+ ```
14
+
15
+ Установить гем cредствами Bundler:
16
+
17
+ $ bundle
18
+
19
+ Или установить его отдельно:
20
+
21
+ $ gem install yml_builder
22
+
23
+ # Зависимости
24
+
25
+ Для работы гема требуется Ruby не младше версии 2.2.1. Также для работы необходим гем StTools (https://github.com/StanZhuravlev/st_tools).
26
+
27
+ ## Использование
28
+
29
+ Библиотека работает с кодировкой UTF-8, однако формирование прайс-листа осуществляется в соответствии с рекомендациями,
30
+ в кодировке windows-1251. Все работа строится на базе двух основных классов - YmlBuilder::Yml и YmlBuilder::Offer.
31
+ Остальные классы являются вспомогательными, и в пользовательских приложениях использоваться не должны. Использование
32
+ классов продемонстрировано ниже на базе примеров. Все примеры можно найти в папке /test
33
+
34
+ В соответствии со спецификацией Yandex.Market, все значения в прайс-листе делятся на две группы: обязательные и опциональные.
35
+ Если при формировании прайс-листа не заданы какие-либо обязательные параметры, то будет вызвано исключение с детальным
36
+ описанием причин.
37
+
38
+ _Ограничения библиотеки: поскольку она писалась под конкретный проект, в ней реализован только один тип товара - упрощенный.
39
+ В библиотеке заложены возможности расширения под дугие типы, но такое расширение будет создаваться только по необходимости.
40
+ Пишите, если требуется подержка других карточек, сделаю_
41
+
42
+ ### Пример 1 - Создание прайс-листа и настройка магазина
43
+
44
+ Первым шагом необходимо создать прайс-лист, и настроить параметры Интернет-магазина.
45
+
46
+ ```ruby
47
+ price = YmlBuilder::Yml.new
48
+ price.shop.name = 'Магазин ТЕСТ'
49
+ price.shop.company = "ООО 'Рога & Копыта'"
50
+ price.shop.url = 'http://example-site.ru'
51
+ price.shop.phone = '+7 (123) 456-7890'
52
+ price.shop.platform = 'OpenCart'
53
+ price.shop.version = '2.0'
54
+ puts price.to_yml
55
+ ```
56
+
57
+ Результатом данного кода будет являться сгенерированная XML-сруктура следующего вида.
58
+
59
+ ```xml
60
+ <?xml version="1.0" encoding="windows-1251"?>
61
+ <!DOCTYPE yml_catalog SYSTEM "shops.dtd">
62
+ <yml_catalog date="2015-05-11 14:07">
63
+ <shop>
64
+ <name>Магазин ТЕСТ</name>
65
+ <company>ООО &apos;Рога &amp; Копыта&apos;</company>
66
+ <url>http://example-site.ru</url>
67
+ <phone>+7 (123) 456-7890</phone>
68
+ <platform>OpenCart</platform>
69
+ <version>2.0</version>
70
+ <currencies>
71
+ </currencies>
72
+ <categories>
73
+ </categories>
74
+ <offers>
75
+ </offers>
76
+ </shop>
77
+ </yml_catalog>
78
+ ```
79
+
80
+ Также будет сгенерировано два предупреждения
81
+
82
+ ```txt
83
+ Предупреждение: не указано ни одной валюты в секции 'currencies'
84
+ Предупреждение: не указано ни одной категории в секции 'categories'
85
+ ```
86
+
87
+ ### Пример 2 - Добавление валют и категорий
88
+
89
+ На втором шаге, осуществляется добавление необходимых валют из числа поддерживаемых Яндекс.Маркет, а также категорий
90
+ в которых будут размещены товары.
91
+
92
+ ```ruby
93
+ price = YmlBuilder::Yml.new
94
+ price.shop.name = 'Магазин ТЕСТ'
95
+ price.shop.company = "ООО 'Рога & Копыта'"
96
+ price.shop.url = 'http://example-site.ru'
97
+ price.shop.phone = '+7 (123) 456-7890'
98
+ price.shop.platform = 'OpenCart'
99
+ price.shop.version = '2.0'
100
+
101
+ price.currencies.rub = 1
102
+ price.currencies.usd = 55.04
103
+ price.currencies.eur = :cbrf
104
+
105
+ price.categories.add(id: 1, name: "Игрушки")
106
+ price.categories.add(id: 2, name: "Одежда")
107
+ price.categories.add(id: 4, name: "Игрушки для девочек", parent_id: 1)
108
+ price.categories.add(id: 5, name: "Игрушки для мальчиков", parent_id: 1)
109
+ price.categories.add(id: 3, name: "Книги")
110
+
111
+ puts price.to_yml
112
+ ```
113
+
114
+ Результатом данного кода будет являться сгенерированная XML-сруктура следующего вида.
115
+
116
+ ```xml
117
+ <?xml version="1.0" encoding="windows-1251"?>
118
+ <!DOCTYPE yml_catalog SYSTEM "shops.dtd">
119
+ <yml_catalog date="2015-05-11 14:28">
120
+ <shop>
121
+ <name>Магазин ТЕСТ</name>
122
+ <company>ООО &apos;Рога &amp; Копыта&apos;</company>
123
+ <url>http://example-site.ru</url>
124
+ <phone>+7 (123) 456-7890</phone>
125
+ <platform>OpenCart</platform>
126
+ <version>2.0</version>
127
+ <currencies>
128
+ <currency id="RUB" rate="1"/>
129
+ <currency id="USD" rate="55.04"/>
130
+ <currency id="EUR" rate="CBRF"/>
131
+ </currencies>
132
+ <categories>
133
+ <category id="1">Игрушки</category>
134
+ <category id="2">Одежда</category>
135
+ <category id="3">Книги</category>
136
+ <category id="4" parentId="1">Игрушки для девочек</category>
137
+ <category id="5" parentId="1">Игрушки для мальчиков</category>
138
+ </categories>
139
+ <offers>
140
+ </offers>
141
+ </shop>
142
+ </yml_catalog>
143
+ ```
144
+
145
+ ### Пример 3 - Добавление товаров
146
+
147
+ Затем необходимо сформировать информацию о оферах, и добавить их в список товаров (offers). Офферы создаются через вызов
148
+ отдельного класса - YmlBuilder.Offer. На самом деле, это "рамочный" класс, который на самом деле вернет экземпляры
149
+ классов, соответствующих типам товаров Yandex.Market. Напрмиер, ```ruby YmlBuilder::Offer.new('simple')``` создает
150
+ описание урощенной карточки товара. Если вместо simple указать любой другой тип, то в данной версии библиотеки
151
+ будет вызвано исключение.
152
+
153
+ Кроме того, через вызов ```ruby offer.add_picture``` добавляются ссылки на картинки, описывающие товар. Каждый вызов метода
154
+ добавляет ссылку на фотографю товара в конце списка picture. При этом контролируется, чтобы размер списка изображений
155
+ не превышал 10. При превышении массив фотографий всегда обрезается до 10. Чтобы добавить основную фотографию товара
156
+ необходимо вызвать метод ```ruby offer.add_cover_picture```. Данный метод просто добавляет картинку в начало списка
157
+ фотографий. Если вызвать данный метод два раза, то список фотографий будет выглядеть как две основные фотографии.
158
+
159
+ Через вызов ```ruby offer.add_param``` добавляются различные вспомогательные параметры - вес, продолжительность записи,
160
+ размеры одежды и прочая информация.
161
+
162
+ ```ruby
163
+ item = YmlBuilder::Offer.new('simple')
164
+ item.id = 6
165
+ item.available = true
166
+ item.currency_id = 'RUR'
167
+ item.delivery = true
168
+ item.category_id = 1
169
+ item.name = 'Товар №6'
170
+ item.url = 'http://example-site.ru/items/6'
171
+ item.price = 300.90
172
+ item.add_picture('http://example-site.ru/image1')
173
+ item.add_picture('http://example-site.ru/image2')
174
+ item.add_cover_picture('http://example-site.ru/image_cover')
175
+ item.add_param(name: 'Обложка', value: 'Мягкая')
176
+ item.add_param(name: 'Страниц', value: 10, unit: 'шт.')
177
+
178
+ price.offers.add(item)
179
+
180
+ item = YmlBuilder::Offer.new('simple')
181
+ item.id = 3
182
+ item.available = false
183
+ item.currency_id = 'RUR'
184
+ item.delivery = true
185
+ item.category_id = 2
186
+ item.name = 'Товар №3'
187
+ item.url = 'http://example-site.ru/items/3'
188
+ item.price = 100
189
+ item.add_picture('http://example-site.ru/image1')
190
+ item.add_picture('http://example-site.ru/image2')
191
+ item.add_cover_picture('http://example-site.ru/image_cover')
192
+ item.add_param(name: 'Обложка', value: 'Мягкая')
193
+ item.add_param(name: 'Страниц', value: 10, unit: 'шт.')
194
+
195
+ price.offers.add(item)
196
+ ```
197
+
198
+ Результатом данного кода будет являться сгенерированная XML-сруктура следующего вида.
199
+
200
+ ```xml
201
+ <?xml version="1.0" encoding="windows-1251"?>
202
+ <!DOCTYPE yml_catalog SYSTEM "shops.dtd">
203
+ <yml_catalog date="2015-05-11 14:40">
204
+ <shop>
205
+ <name>Магазин ТЕСТ</name>
206
+ <company>ООО &apos;Рога &amp; Копыта&apos;</company>
207
+ <url>http://example-site.ru</url>
208
+ <phone>+7 (123) 456-7890</phone>
209
+ <platform>OpenCart</platform>
210
+ <version>2.0</version>
211
+ <currencies>
212
+ <currency id="RUB" rate="1"/>
213
+ <currency id="USD" rate="55.04"/>
214
+ <currency id="EUR" rate="CBRF"/>
215
+ </currencies>
216
+ <categories>
217
+ <category id="1">Игрушки</category>
218
+ <category id="2">Одежда</category>
219
+ <category id="3">Книги</category>
220
+ <category id="4" parentId="1">Игрушки для девочек</category>
221
+ <category id="5" parentId="1">Игрушки для мальчиков</category>
222
+ </categories>
223
+ <offers>
224
+ <offer id="3" available="false">
225
+ <url>http://nosite/items/3</url>
226
+ <price>100</price>
227
+ <currencyId>RUR</currencyId>
228
+ <categoryId>2</categoryId>
229
+ <picture>http://example-site.ru/image_cover</picture>
230
+ <picture>http://example-site.ru/image1</picture>
231
+ <picture>http://example-site.ru/image2</picture>
232
+ <delivery>true</delivery>
233
+ <name>Товар №3</name>
234
+ <param name="Обложка">Мягкая</param>
235
+ <param name="Страниц" unit="шт.">10</param>
236
+ </offer>
237
+ <offer id="6" available="true">
238
+ <url>http://nosite/items/6</url>
239
+ <price>300.9</price>
240
+ <currencyId>RUR</currencyId>
241
+ <categoryId>1</categoryId>
242
+ <picture>http://example-site.ru/image_cover</picture>
243
+ <picture>http://example-site.ru/image1</picture>
244
+ <picture>http://example-site.ru/image2</picture>
245
+ <delivery>true</delivery>
246
+ <name>Товар №6</name>
247
+ <param name="Обложка">Мягкая</param>
248
+ <param name="Страниц" unit="шт.">10</param>
249
+ </offer>
250
+ </offers>
251
+ </shop>
252
+ </yml_catalog>
253
+ ```
254
+
255
+ ### Дополнительные возможности
256
+
257
+ В ряде случаев (размещение в тематических прайс-агрегаторах, ограничение показа товаров в Яндекс.Маркет) необходимо
258
+ генерировать различные прайс-листы. При использовании бибилиотеки YmlBuild это можно сделать через указание допустимых
259
+ категорий в методе filter. Вызов данного метода должен быть осуществлен до добавления первой категории или товара.
260
+
261
+ ```ruby
262
+ price.categories.filter = [1, 10, 12, 20]
263
+ ```
264
+
265
+ Для указания в соответствии с требованиями Yandex цены доставки в место локации магазина, необходимо использовать
266
+ метод local_delivery_cost.
267
+
268
+ ```ruby
269
+ price.local_delivery_cost = 300
270
+ ```
271
+
272
+ Метод to_yml формирует выходную строку в формате utf-8. Для записи прайс-листа рекомендуется использовать метод save,
273
+ передава ему имя файла для записи.
274
+
275
+ ```ruby
276
+ price.save('price.xml')
277
+ ```
278
+
279
+ Узнать статистику по экпорту прайс-листа (общее число товаров, число товаров в наличии, общая стоимость товаров в
280
+ наличии (без учета количества)) можно через вызов метода ```ruby price.stats```. Результатом работы метода является
281
+ структура следующего вида
282
+
283
+ ```ruby
284
+ {
285
+ :categories => 5,
286
+ :total => 2,
287
+ :available => 1,
288
+ :price => 300.9
289
+ }
290
+ ```
291
+
292
+
293
+
294
+ ## Contributing
295
+
296
+ 1. Fork it ( https://github.com/StanZhuravlev/yml_builder/fork )
297
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
298
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
299
+ 4. Push to the branch (`git push origin my-new-feature`)
300
+ 5. Create a new Pull Request
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "yml_builder"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,72 @@
1
+ module YmlBuilder
2
+ class Categories
3
+ # Переменная содержит список id категорий, для которых необходимо формировать прайс-лист.
4
+ # Если переменная пуста, то включаются все категории
5
+ attr_reader :filter
6
+
7
+ def initialize(stats)
8
+ @stats = stats
9
+ init_class
10
+ end
11
+
12
+ def add(opts = {})
13
+ return false unless can_add?(opts[:id])
14
+ allow = [:id, :parent_id, :name]
15
+ raise "Ошибка: для добавления категории используйте ключи #{allow.inspect}" if (opts.keys - allow).count > 0
16
+ raise "Ошибка: не указан 'id' для добавления категории" if opts[:id].nil?
17
+ raise "Ошибка: не указан 'name' для добавления категории" if opts[:name].nil?
18
+ @params[opts[:id]] = { parent_id: opts[:parent_id], name: opts[:name] }
19
+ @params = Hash[ @params.sort_by { |id, data| id } ]
20
+ @stats.add(:categories, 1)
21
+ true
22
+ end
23
+
24
+ # Метод проверяет необходимость добавления категории или товара в прайс-лист
25
+ # с учетом выставленных в значении filter настроек.
26
+ #
27
+ # @param [Object] id идентифкатор категории товара
28
+ # @return [Boolean] true, если данный id категории указан в filter как допустимый для включения в прайс-лист
29
+ def can_add?(id)
30
+ @filter.count == 0 ? true : @filter.include?(id)
31
+ end
32
+
33
+ # Метод позволяет огарничить формирование прайс-листа только категориями, указанными в данном поле
34
+ # Filter может принимать значения nil или [], тогда считается, что допустимо включение в прайс-лист
35
+ # всех товаров.
36
+ # @param [Array] allow массив id категорий, который должны попадать в результирующий прайс-лист
37
+ def filter=(allow)
38
+ @filter = allow || Array.new
39
+ end
40
+
41
+ # Метод формирует фрагмент YML файла каталога Яндекс.Маркет, содержащий список категорий
42
+ #
43
+ # @param [Integer] ident отступ от левого края в символах
44
+ # @return [String] фрагмент YML файла каталога Яндекс.Маркет, содержащий список категорий
45
+ def to_yml(ident = 4)
46
+ out = Array.new
47
+ out << '<categories>'
48
+
49
+ @params.each do |id, value|
50
+ if value[:parent_id].nil?
51
+ out << " <category id=#{id.to_s.inspect}>#{::YmlBuilder::Common.encode_special_chars(value[:name])}</category>"
52
+ else
53
+ out << " <category id=#{id.to_s.inspect} parentId=#{value[:parent_id].to_s.inspect}>#{::YmlBuilder::Common.encode_special_chars(value[:name])}</category>"
54
+ end
55
+ end
56
+ warn "Предупреждение: не указано ни одной категории в секции 'categories'" if out.count == 1
57
+
58
+ out << '</categories>'
59
+
60
+ out.map! { |line| line = ' '.rjust(ident, ' ') + line }
61
+ out.join("\n")
62
+ end
63
+
64
+ private
65
+
66
+ def init_class
67
+ @params = Hash.new
68
+ self.filter = nil
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,19 @@
1
+ module YmlBuilder
2
+ class Common
3
+
4
+ def self.encode_special_chars(out)
5
+ return out.to_s.gsub(/[\"\&\>\<\']/, '"' => '&quot;',
6
+ '&' => '&amp;',
7
+ '>' => '&gt;',
8
+ '<' => '&lt;',
9
+ "'" => '&apos;')
10
+ end
11
+
12
+ def self.convert_key(key)
13
+ return 'currencyId' if key == :currency_id
14
+ return 'categoryId' if key == :category_id
15
+ key.to_s
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,135 @@
1
+ module YmlBuilder
2
+ class CommonOffer
3
+ attr_accessor :id
4
+ attr_accessor :type
5
+ attr_accessor :available
6
+ attr_accessor :bid
7
+
8
+ attr_accessor :mandatories
9
+
10
+ def initialize
11
+ init_class
12
+ end
13
+
14
+ def init_class
15
+ @params = Hash.new
16
+ @meta = Hash.new
17
+ @picture = Array.new
18
+
19
+ @id = 0
20
+ @type = 'unknown'
21
+ @available = false
22
+ @bid = nil
23
+
24
+ @mandatories = Array.new
25
+ end
26
+
27
+ def add_picture(url)
28
+ @picture << url
29
+ @picture.uniq!
30
+ warn "Предупреждение: число картинок превышает 10 (offer_id=#{@id}). Сокращаем до 10" if @picture.count > 10
31
+ @picture = @picture[0,9]
32
+ end
33
+
34
+ def add_cover_picture(url)
35
+ @picture.unshift(url)
36
+ @picture.uniq!
37
+ warn "Предупреждение: число картинок превышает 10 (offer_id=#{@id}). Сокращаем до 10" if @picture.count > 10
38
+ @picture = @picture[0,9]
39
+ end
40
+
41
+ def add_param(name:, unit: nil, value:)
42
+ @meta[name] = { unit: unit, value: value}
43
+ end
44
+
45
+ def method_missing(method_sym, *arguments, &block)
46
+ if @params.include?(method_sym.to_s.gsub(/=$/, '').to_sym)
47
+ processing_method(method_sym, arguments.first)
48
+ else
49
+ super
50
+ end
51
+ end
52
+
53
+ def processing_method(method_sym, value)
54
+ if method_sym.to_s.match(/=$/)
55
+ key = method_sym.to_s.gsub(/=$/, '')
56
+ warn "Предупреждение: url не должен превышать 512 символов" if key == 'url' && value.length > 512
57
+ warn "Предупреждение: price не может быть равен нулю" if key == 'price' && value.to_f == 0
58
+ warn "Предупреждение: weight не может быть равен нулю" if key == 'weight' && value.to_f == 0
59
+ @params[key.to_sym] = value
60
+ else
61
+ @params[method_sym.to_s.gsub(/=$/, '').to_sym]
62
+ end
63
+ end
64
+
65
+ def header_line
66
+ out = Array.new
67
+ out << "id=#{@id.to_s.inspect}"
68
+ out << "type=#{@type.to_s.inspect}" if @type != 'simple'
69
+ out << "available=\"#{@available.inspect}\""
70
+ out << "bid=#{@bid.inspect}" unless @bid.nil?
71
+ "<offer #{out.join (' ')}>"
72
+ end
73
+
74
+ def footer_line
75
+ '</offer>'
76
+ end
77
+
78
+ def param_line(name, data)
79
+ if data[:unit].nil?
80
+ "<param name=#{::YmlBuilder::Common.encode_special_chars(name.to_s).inspect}>#{::YmlBuilder::Common.encode_special_chars(data[:value].to_s)}</param>"
81
+ else
82
+ "<param name=#{::YmlBuilder::Common.encode_special_chars(name.to_s).inspect} unit=#{::YmlBuilder::Common.encode_special_chars(data[:unit]).inspect}>#{::YmlBuilder::Common.encode_special_chars(data[:value].to_s)}</param>"
83
+ end
84
+ end
85
+
86
+ def to_yml_subsections(key)
87
+ out = Array.new
88
+
89
+ if key == :picture
90
+ @picture.each do |url|
91
+ out << " <picture>#{url}</picture>"
92
+ end
93
+ else
94
+ @meta.each do |name, data|
95
+ out << " #{param_line(name, data)}"
96
+ end
97
+ end
98
+
99
+ out
100
+ end
101
+
102
+ def to_yml_mandatories(key, value)
103
+ raise "Ошибка секции 'offer': не заполнено обязательное значение #{key.to_s.inspect}" if (value.nil? || value.to_s == '')
104
+ key_xml = ::YmlBuilder::Common.convert_key(key)
105
+ " <#{key_xml}>#{::YmlBuilder::Common.encode_special_chars(value)}</#{key_xml}>"
106
+ end
107
+
108
+ def to_yml_optional(key, value)
109
+ return nil if value.nil?
110
+ key_xml = ::YmlBuilder::Common.convert_key(key)
111
+ " <#{key_xml}>#{::YmlBuilder::Common.encode_special_chars(value)}</#{key_xml}>"
112
+ end
113
+
114
+ def to_yml(ident = 4)
115
+ out = Array.new
116
+ out << header_line
117
+
118
+ @params.each do |key, value|
119
+ if [:picture, :param].include?(key)
120
+ out += to_yml_subsections(key)
121
+ elsif @mandatories.include?(key)
122
+ out << to_yml_mandatories(key, value)
123
+ else
124
+ out << to_yml_optional(key, value)
125
+ end
126
+ end
127
+ out.compact!
128
+
129
+ out << footer_line
130
+ out.map! { |line| ' '.rjust(ident, ' ') + line }
131
+ out.join("\n")
132
+ end
133
+
134
+ end
135
+ end
@@ -0,0 +1,63 @@
1
+ module YmlBuilder
2
+ class Currencies
3
+
4
+ def initialize
5
+ init_class
6
+ end
7
+
8
+ def init_class
9
+ @params = Hash.new
10
+
11
+ @params[:rur] = nil
12
+ @params[:rub] = nil
13
+ @params[:usd] = nil
14
+ @params[:eur] = nil
15
+ @params[:uah] = nil
16
+ @params[:kzt] = nil
17
+ end
18
+
19
+ def method_missing(method_sym, *arguments, &block)
20
+ if @params.include?(method_sym.to_s.gsub(/=$/, '').to_sym)
21
+ processing_method(method_sym, arguments.first)
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def valid?(method_sym, allow, value)
28
+ return true if value.to_s.match(/^\s*[+-]?((\d+_?)*\d+(\.(\d+_?)*\d+)?|\.(\d+_?)*\d+)(\s*|([eE][+-]?(\d+_?)*\d+)\s*)$/)
29
+ return true if allow.include?(value)
30
+ warn "Предупреждение: значение валюты #{method_sym.to_s.upcase} может быть цифрой или значением из #{allow.inspect}"
31
+ false
32
+ end
33
+
34
+ def processing_method(method_sym, value)
35
+ if method_sym.to_s.match(/=$/)
36
+ key = method_sym.to_s.gsub(/=$/, '')
37
+ allow = [:cbrf, :nbu, :nbk, :cb]
38
+ valid?(key.to_s, allow, value)
39
+
40
+ @params[key.to_sym] = value
41
+ else
42
+ @params[method_sym.to_s.gsub(/=$/, '').to_sym]
43
+ end
44
+ end
45
+
46
+ def to_yml(ident = 4)
47
+ out = Array.new
48
+ out << '<currencies>'
49
+
50
+ @params.each do |key, value|
51
+ out << " <currency id=#{key.to_s.upcase.inspect} rate=#{value.to_s.upcase.inspect}/>" unless value.nil?
52
+ end
53
+ warn "Предупреждение: не указано ни одной валюты в секции 'currencies'" if out.count == 1
54
+
55
+ out << '</currencies>'
56
+
57
+ out.map! { |line| line = ' '.rjust(ident, ' ') + line }
58
+
59
+ out.join("\n")
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,14 @@
1
+ module YmlBuilder
2
+ class Offer
3
+
4
+ def self.new(type)
5
+ case type.downcase
6
+ when 'simple'
7
+ ::YmlBuilder::OfferSimple.new
8
+ else
9
+ raise "Ошибка: неизвестный тип оффера (#{type})"
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,33 @@
1
+ module YmlBuilder
2
+ class OfferSimple < YmlBuilder::CommonOffer
3
+
4
+ def initialize
5
+ super
6
+ @type = 'simple'
7
+
8
+ @params = Hash.new
9
+ @params[:url] = nil
10
+ @params[:price] = nil
11
+ @params[:currency_id] = nil
12
+ @params[:category_id] = nil
13
+ @params[:market_category] = nil
14
+ @params[:picture] = nil
15
+ @params[:store] = nil
16
+ @params[:pickup] = nil
17
+ @params[:delivery] = nil
18
+ @params[:local_delivery_cost] = nil
19
+ @params[:name] = nil
20
+ @params[:vendor] = nil
21
+ @params[:vendor_code] = nil
22
+ @params[:description] = nil
23
+ @params[:country_of_origin] = nil
24
+ @params[:adult] = nil
25
+ @params[:param] = nil
26
+ @params[:weight] = nil
27
+ @params[:dimensions] = nil
28
+
29
+ @mandatories = [:url, :price, :currency_id, :category_id, :delivery, :name]
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ module YmlBuilder # :nodoc:
2
+ class OfferVendorModel < YmlBuilder::CommonOffer
3
+
4
+ def initialize
5
+ super
6
+
7
+ @type = 'vendor.model'
8
+
9
+ @params[:m] = Hash.new
10
+ @params[:m][:url] = nil
11
+ @params[:m][:price] = nil
12
+ @params[:m][:currency_id] = nil
13
+ @params[:m][:category_id] = nil
14
+ @params[:m][:delivery] = nil
15
+ @params[:m][:vendor] = nil
16
+ @params[:m][:model] = nil
17
+
18
+ @params[:o] = Hash.new
19
+ @params[:o][:local_delivery_cost] = nil
20
+ @params[:o][:type_prefix] = nil
21
+ @params[:o][:vendor_code] = nil
22
+ @params[:o][:description] = nil
23
+ @params[:o][:manufacturer_warranty] = nil
24
+ @params[:o][:country_of_origin] = nil
25
+ @params[:o][:available] = nil
26
+ @params[:o][:sales_notes] = nil
27
+ @params[:o][:downloadable] = nil
28
+ @params[:o][:adult] = nil
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,42 @@
1
+ module YmlBuilder
2
+ class Offers
3
+
4
+ def initialize(stats, categories)
5
+ @stats = stats
6
+ @categories = categories
7
+ init_class
8
+ end
9
+
10
+ def init_class
11
+ @offers = Hash.new
12
+ end
13
+
14
+ def add(offer)
15
+ return false unless @categories.can_add?(offer.category_id)
16
+ @offers[offer.id] = offer
17
+
18
+ # Формируем статистику
19
+ @stats.add(:total, 1)
20
+ if offer.available
21
+ @stats.add(:available, 1)
22
+ @stats.add(:price, (offer.price || 0))
23
+ end
24
+ true
25
+ end
26
+
27
+ def to_yml(ident = 4)
28
+ @offers = @offers.sort_by { |id, offer| id }
29
+
30
+ out = Array.new
31
+ out << "<offers>"
32
+ @offers.each do |id, offer|
33
+ out += offer.to_yml(2).split(/[\n\r]/)
34
+ end
35
+ out << "</offers>"
36
+
37
+ out.map! { |line| ' '.rjust(ident, ' ') + line }
38
+ out.join("\n")
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,71 @@
1
+ module YmlBuilder
2
+ class Shop
3
+
4
+ def initialize
5
+ init_class
6
+ end
7
+
8
+ def init_class
9
+ @params = Hash.new
10
+
11
+ @params[:m] = Hash.new
12
+ @params[:m][:name] = ''
13
+ @params[:m][:company] = ''
14
+ @params[:m][:url] = ''
15
+
16
+ @params[:o] = Hash.new
17
+ @params[:o][:phone] = nil
18
+ @params[:o][:platform] = nil
19
+ @params[:o][:version] = nil
20
+ @params[:o][:agency] = nil
21
+ @params[:o][:email] = nil
22
+ @params[:o][:adult] = nil
23
+ @params[:o][:cpa] = nil
24
+ end
25
+
26
+ def method_missing(method_sym, *arguments, &block)
27
+ if @params[:m].include?(method_sym.to_s.gsub(/=$/, '').to_sym)
28
+ processing_method(:m, method_sym, arguments.first)
29
+ elsif @params[:o].include?(method_sym.to_s.gsub(/=$/, '').to_sym)
30
+ processing_method(:o, method_sym, arguments.first)
31
+ else
32
+ super
33
+ end
34
+ end
35
+
36
+ def processing_method(part, method_sym, value)
37
+ if method_sym.to_s.match(/=$/)
38
+ key = method_sym.to_s.gsub(/=$/, '')
39
+ warn "Предупреждение: название магазина не должно быть больше 20 символов" if key == 'name' && value.length > 20
40
+ @params[part][key.to_sym] = value
41
+ else
42
+ @params[part][method_sym.to_s.gsub(/=$/, '').to_sym]
43
+ end
44
+ end
45
+
46
+ def to_yml
47
+ out = Array.new
48
+ out << ' <shop>'
49
+
50
+ @params[:m].each do |key, value|
51
+ raise "Ошибка секции 'company': не заполнено значение для обязательного ключа #{key.to_s.inspect}" if value == ''
52
+ out << " <#{key}>#{::YmlBuilder::Common.encode_special_chars(value)}</#{key}>"
53
+ end
54
+
55
+ @params[:o].each do |key, value|
56
+ unless value.nil?
57
+ out << " <#{key}>#{::YmlBuilder::Common.encode_special_chars(value)}</#{key}>"
58
+ end
59
+ end
60
+
61
+ out << ' {replace_currencies}'
62
+ out << ' {replace_categories}'
63
+ out << ' {replace_local_delivery_cost}'
64
+ out << ' {replace_offers}'
65
+ out << ' </shop>'
66
+ out.join("\n")
67
+ end
68
+
69
+
70
+ end
71
+ end
@@ -0,0 +1,22 @@
1
+ module YmlBuilder # :nodoc:
2
+ class Stats
3
+ attr_reader :stats
4
+
5
+ def initialize
6
+ init_class
7
+ end
8
+
9
+ def init_class
10
+ @stats = Hash.new
11
+ @stats[:categories] = 0
12
+ @stats[:total] = 0
13
+ @stats[:available] = 0
14
+ @stats[:price] = 0
15
+ end
16
+
17
+ def add(key, value)
18
+ @stats[key] += value
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module YmlBuilder # :nodoc:
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,83 @@
1
+ # Базовый класс для управления формированием прайс-листа. Содержит ссылки на различные секции:
2
+ # shop, currencies, categories, offers
3
+
4
+ module YmlBuilder
5
+ class Yml
6
+ # Ссылка на класс, описывающий контакты Интернет-магазина
7
+ attr_reader :shop
8
+ # Ссылка на класс, описывающий категории
9
+ attr_reader :categories
10
+ # Ссылка на класс, описывающий валюты
11
+ attr_reader :currencies
12
+ # Ссылка на класс, управляющий товарами (офферами)
13
+ attr_reader :offers
14
+ # Переменая, хранящая стоимость доставки в локации расположения Интернет-магазина
15
+ attr_reader :local_delivery_cost
16
+
17
+
18
+ def initialize
19
+ @stats = ::YmlBuilder::Stats.new
20
+ @shop = ::YmlBuilder::Shop.new
21
+ @currencies = ::YmlBuilder::Currencies.new
22
+ @categories = ::YmlBuilder::Categories.new(@stats)
23
+ @offers = ::YmlBuilder::Offers.new(@stats, @categories)
24
+ @local_delivery_cost = nil
25
+ end
26
+
27
+ # Метод устанавливает стоимость доставки в месте локации магазина. Например, если магазин находится в Москве,
28
+ # то при указанни данной стоимости, она будет показана покупателям в этом же районе.
29
+ #
30
+ # @param [Float] value стоимость доставки в месте локации магазина
31
+ # @return [None] нет
32
+ def local_delivery_cost=(value)
33
+ @local_delivery_cost = value
34
+ end
35
+
36
+ # Метод возвращает статистику по результатам генерации прайс-листа: всего товаров, товаров в наличии, стоимость
37
+ # товаров в наличии (без учета количества)
38
+ #
39
+ # @return [None] нет
40
+ def stats
41
+ @stats.stats
42
+ end
43
+
44
+ # Метод возвращает текстовую строку с прайс-листом в формате Яндекс.Маркет
45
+ #
46
+ # @return [String] строка с прайс-листом в формате utf-8
47
+ def to_yml
48
+ out = @shop.to_yml
49
+ out.gsub!(/^\s{0,100}\{replace\_currencies\}/, @currencies.to_yml)
50
+ out.gsub!(/^\s{0,100}\{replace\_categories\}/, @categories.to_yml)
51
+ out.gsub!(/^\s{0,100}\{replace\_local\_delivery\_cost\}[\n\r]/, lds_to_yml)
52
+ out.gsub!(/^\s{0,100}\{replace\_offers\}/, @offers.to_yml)
53
+
54
+ add_header_footer(out)
55
+ end
56
+
57
+ # Метод для записи прайслиста в файл. Запись осущесствится в кодировке windows-1251
58
+ #
59
+ # @param [String] filename название файла для записи прайс-листа
60
+ # @return [None] нет
61
+ def save(filename)
62
+ File.open(filename, 'w:windows-1251') {|f| f.write(to_yml) }
63
+ end
64
+
65
+ private
66
+
67
+ def add_header_footer(text)
68
+ out = Array.new
69
+ out << "<?xml version=\"1.0\" encoding=\"windows-1251\"?>"
70
+ out << "<!DOCTYPE yml_catalog SYSTEM \"shops.dtd\">"
71
+ out << "<yml_catalog date=#{::Time.now.strftime("%Y-%m-%d %H:%M").inspect}>"
72
+ out << text
73
+ out << "</yml_catalog>"
74
+ out.join("\n")
75
+ end
76
+
77
+ def lds_to_yml(ident = 4)
78
+ return "" if @local_delivery_cost.nil?
79
+ ' '.rjust(ident, ' ') + "<local_delivery_cost>#{@local_delivery_cost}</local_delivery_cost>\n"
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('..', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "yml_builder/version"
5
+
6
+ module YmlBuilder # :nodoc:
7
+ require "yml_builder/stats"
8
+ require "yml_builder/common"
9
+ require "yml_builder/shop"
10
+ require "yml_builder/currencies"
11
+ require "yml_builder/categories"
12
+ require "yml_builder/common_offer"
13
+ require "yml_builder/offer_simple"
14
+ require "yml_builder/offer_vendor_model"
15
+ require "yml_builder/offers"
16
+ require "yml_builder/offer"
17
+ require "yml_builder/yml"
18
+ end
@@ -0,0 +1,17 @@
1
+ require 'rspec'
2
+ require 'yml_builder'
3
+
4
+ describe 'Пример 1 - Инициация прайс-листа и магазина' do
5
+
6
+ it 'Test' do
7
+ price = YmlBuilder::Yml.new
8
+ price.shop.name = 'Магазин ТЕСТ'
9
+ price.shop.company = "ООО 'Рога & Копыта'"
10
+ price.shop.url = 'http://example-site.ru'
11
+ price.shop.phone = '+7 (123) 456-7890'
12
+ price.shop.platform = 'OpenCart'
13
+ price.shop.version = '2.0'
14
+ puts price.to_yml
15
+
16
+ end
17
+ end
@@ -0,0 +1,28 @@
1
+ require 'rspec'
2
+ require 'yml_builder'
3
+
4
+ describe 'Пример 2 - Валюта и категории' do
5
+
6
+ it 'Test' do
7
+ price = YmlBuilder::Yml.new
8
+ price.shop.name = 'Магазин ТЕСТ'
9
+ price.shop.company = "ООО 'Рога & Копыта'"
10
+ price.shop.url = 'http://example-site.ru'
11
+ price.shop.phone = '+7 (123) 456-7890'
12
+ price.shop.platform = 'OpenCart'
13
+ price.shop.version = '2.0'
14
+
15
+ price.currencies.rub = 1
16
+ price.currencies.usd = 55.04
17
+ price.currencies.eur = :cbrf
18
+
19
+ price.categories.add(id: 1, name: "Игрушки")
20
+ price.categories.add(id: 2, name: "Одежда")
21
+ price.categories.add(id: 4, name: "Игрушки для девочек", parent_id: 1)
22
+ price.categories.add(id: 5, name: "Игрушки для мальчиков", parent_id: 1)
23
+ price.categories.add(id: 3, name: "Книги")
24
+
25
+ puts price.to_yml
26
+
27
+ end
28
+ end
@@ -0,0 +1,64 @@
1
+ require 'rspec'
2
+ require 'yml_builder'
3
+
4
+ describe 'Пример 3 - Добавление товара' do
5
+
6
+ it 'Test' do
7
+ price = YmlBuilder::Yml.new
8
+ price.shop.name = 'Магазин ТЕСТ'
9
+ price.shop.company = "ООО 'Рога & Копыта'"
10
+ price.shop.url = 'http://example-site.ru'
11
+ price.shop.phone = '+7 (123) 456-7890'
12
+ price.shop.platform = 'OpenCart'
13
+ price.shop.version = '2.0'
14
+
15
+ price.currencies.rub = 1
16
+ price.currencies.usd = 55.04
17
+ price.currencies.eur = :cbrf
18
+
19
+ price.categories.add(id: 1, name: "Игрушки")
20
+ price.categories.add(id: 2, name: "Одежда")
21
+ price.categories.add(id: 4, name: "Игрушки для девочек", parent_id: 1)
22
+ price.categories.add(id: 5, name: "Игрушки для мальчиков", parent_id: 1)
23
+ price.categories.add(id: 3, name: "Книги")
24
+
25
+ item = YmlBuilder::Offer.new('simple')
26
+ item.id = 6
27
+ item.type = 'simple'
28
+ item.available = true
29
+ item.currency_id = 'RUR'
30
+ item.delivery = true
31
+ item.category_id = 1
32
+ item.name = 'Товар №6'
33
+ item.url = 'http://example-site.ru/items/6'
34
+ item.price = 300.90
35
+ item.add_picture('http://example-site.ru/image1')
36
+ item.add_picture('http://example-site.ru/image2')
37
+ item.add_cover_picture('http://example-site.ru/image_cover')
38
+ item.add_param(name: 'Обложка', value: 'Мягкая')
39
+ item.add_param(name: 'Страниц', value: 10, unit: 'шт.')
40
+
41
+ price.offers.add(item)
42
+
43
+ item = YmlBuilder::Offer.new('simple')
44
+ item.id = 3
45
+ item.type = 'simple'
46
+ item.available = false
47
+ item.currency_id = 'RUR'
48
+ item.delivery = true
49
+ item.category_id = 2
50
+ item.name = 'Товар №3'
51
+ item.url = 'http://example-site.ru/items/3'
52
+ item.price = 100
53
+ item.add_picture('http://example-site.ru/image1')
54
+ item.add_picture('http://example-site.ru/image2')
55
+ item.add_cover_picture('http://example-site.ru/image_cover')
56
+ item.add_param(name: 'Обложка', value: 'Мягкая')
57
+ item.add_param(name: 'Страниц', value: 10, unit: 'шт.')
58
+
59
+ price.offers.add(item)
60
+
61
+ puts price.to_yml
62
+
63
+ end
64
+ end
@@ -0,0 +1,68 @@
1
+ require 'rspec'
2
+ require 'yml_builder'
3
+
4
+ describe 'Проверка формирования файла' do
5
+
6
+ it 'Test' do
7
+ price = YmlBuilder::Yml.new
8
+ price.shop.name = 'Магазин ТЕСТ'
9
+ price.shop.company = "ООО 'Рога & Копыта'"
10
+ price.shop.url = 'http://nosite.ru'
11
+ price.shop.phone = '+7 (123) 456-7890'
12
+ price.shop.platform = 'OpenCart'
13
+ price.shop.version = '2.0'
14
+
15
+ price.currencies.rub = 1
16
+ price.currencies.usd = 55.04
17
+ price.currencies.eur = :cbrf
18
+
19
+ price.categories.add(id: 1, name: "Игрушки")
20
+ price.categories.add(id: 2, name: "Одежда")
21
+ price.categories.add(id: 4, name: "Игрушки для девочек", parent_id: 1)
22
+ price.categories.add(id: 5, name: "Игрушки для мальчиков", parent_id: 1)
23
+ price.categories.add(id: 3, name: "Книги")
24
+
25
+ price.local_delivery_cost = 350
26
+
27
+ item = YmlBuilder::Offer.new('simple')
28
+ item.id = 6
29
+ item.type = 'simple'
30
+ item.available = true
31
+ item.currency_id = 'RUR'
32
+ item.delivery = true
33
+ item.category_id = 1
34
+ item.name = 'Товар №6'
35
+ item.url = 'http://nosite/items/6'
36
+ item.price = 300.90
37
+ item.add_picture('http://nosite.ru/image1')
38
+ item.add_picture('http://nosite.ru/image2')
39
+ item.add_cover_picture('http://nosite.ru/image_cover')
40
+ item.add_param(name: 'Обложка', value: 'Мягкая')
41
+ item.add_param(name: 'Страниц', value: 10, unit: 'шт.')
42
+
43
+ price.offers.add(item)
44
+
45
+ item = YmlBuilder::Offer.new('simple')
46
+ item.id = 3
47
+ item.type = 'simple'
48
+ item.available = false
49
+ item.currency_id = 'RUR'
50
+ item.delivery = true
51
+ item.category_id = 2
52
+ item.name = 'Товар №3'
53
+ item.url = 'http://nosite/items/3'
54
+ item.price = 100
55
+ item.add_picture('http://nosite.ru/image1')
56
+ item.add_picture('http://nosite.ru/image2')
57
+ item.add_cover_picture('http://nosite.ru/image_cover')
58
+ item.add_param(name: 'Обложка', value: 'Мягкая')
59
+ item.add_param(name: 'Страниц', value: 10, unit: 'шт.')
60
+
61
+ price.offers.add(item)
62
+
63
+ puts price.to_yml
64
+ price.save('test_yml.yml')
65
+ puts price.stats.inspect
66
+
67
+ end
68
+ end
data/test/test_yml.yml ADDED
@@ -0,0 +1,53 @@
1
+ <?xml version="1.0" encoding="windows-1251"?>
2
+ <!DOCTYPE yml_catalog SYSTEM "shops.dtd">
3
+ <yml_catalog date="2015-05-11 14:57">
4
+ <shop>
5
+ <name>������� ����</name>
6
+ <company>��� &apos;���� &amp; ������&apos;</company>
7
+ <url>http://nosite.ru</url>
8
+ <phone>+7 (123) 456-7890</phone>
9
+ <platform>OpenCart</platform>
10
+ <version>2.0</version>
11
+ <currencies>
12
+ <currency id="RUB" rate="1"/>
13
+ <currency id="USD" rate="55.04"/>
14
+ <currency id="EUR" rate="CBRF"/>
15
+ </currencies>
16
+ <categories>
17
+ <category id="1">�������</category>
18
+ <category id="2">������</category>
19
+ <category id="3">�����</category>
20
+ <category id="4" parentId="1">������� ��� �������</category>
21
+ <category id="5" parentId="1">������� ��� ���������</category>
22
+ </categories>
23
+ <local_delivery_cost>350</local_delivery_cost>
24
+ <offers>
25
+ <offer id="3" available="false">
26
+ <url>http://nosite/items/3</url>
27
+ <price>100</price>
28
+ <currencyId>RUR</currencyId>
29
+ <categoryId>2</categoryId>
30
+ <picture>http://nosite.ru/image_cover</picture>
31
+ <picture>http://nosite.ru/image1</picture>
32
+ <picture>http://nosite.ru/image2</picture>
33
+ <delivery>true</delivery>
34
+ <name>����� �3</name>
35
+ <param name="�������">������</param>
36
+ <param name="�������" unit="��.">10</param>
37
+ </offer>
38
+ <offer id="6" available="true">
39
+ <url>http://nosite/items/6</url>
40
+ <price>300.9</price>
41
+ <currencyId>RUR</currencyId>
42
+ <categoryId>1</categoryId>
43
+ <picture>http://nosite.ru/image_cover</picture>
44
+ <picture>http://nosite.ru/image1</picture>
45
+ <picture>http://nosite.ru/image2</picture>
46
+ <delivery>true</delivery>
47
+ <name>����� �6</name>
48
+ <param name="�������">������</param>
49
+ <param name="�������" unit="��.">10</param>
50
+ </offer>
51
+ </offers>
52
+ </shop>
53
+ </yml_catalog>
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yml_builder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Stan Zhuravlev
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-05-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: |-
42
+ Библиотека содержит набор классов для формирования и валидации прайс-листов
43
+ в формате Яндекс YAML (Яндекс.Маркет).
44
+ Текущая версия поддерживает только упрощенные товарные карточки.
45
+ email:
46
+ - stan@post-api.ru
47
+ executables: []
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - CHANGELOG.md
52
+ - LICENSE.txt
53
+ - README.md
54
+ - bin/console
55
+ - bin/setup
56
+ - lib/yml_builder.rb
57
+ - lib/yml_builder/categories.rb
58
+ - lib/yml_builder/common.rb
59
+ - lib/yml_builder/common_offer.rb
60
+ - lib/yml_builder/currencies.rb
61
+ - lib/yml_builder/offer.rb
62
+ - lib/yml_builder/offer_simple.rb
63
+ - lib/yml_builder/offer_vendor_model.rb
64
+ - lib/yml_builder/offers.rb
65
+ - lib/yml_builder/shop.rb
66
+ - lib/yml_builder/stats.rb
67
+ - lib/yml_builder/version.rb
68
+ - lib/yml_builder/yml.rb
69
+ - test/example_1_spec.rb
70
+ - test/example_2_spec.rb
71
+ - test/example_3_spec.rb
72
+ - test/example_full_spec.rb
73
+ - test/test_yml.yml
74
+ homepage: https://github.com/StanZhuravlev/yml_builder
75
+ licenses:
76
+ - MIT
77
+ metadata:
78
+ allowed_push_host: https://rubygems.org
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ - lib/yml_builder
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 2.2.1
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.4.5
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: "Набор классов для формирования прайс-листов в формате Яндекс YAML"
100
+ test_files: []