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 +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +300 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/yml_builder/categories.rb +72 -0
- data/lib/yml_builder/common.rb +19 -0
- data/lib/yml_builder/common_offer.rb +135 -0
- data/lib/yml_builder/currencies.rb +63 -0
- data/lib/yml_builder/offer.rb +14 -0
- data/lib/yml_builder/offer_simple.rb +33 -0
- data/lib/yml_builder/offer_vendor_model.rb +32 -0
- data/lib/yml_builder/offers.rb +42 -0
- data/lib/yml_builder/shop.rb +71 -0
- data/lib/yml_builder/stats.rb +22 -0
- data/lib/yml_builder/version.rb +3 -0
- data/lib/yml_builder/yml.rb +83 -0
- data/lib/yml_builder.rb +18 -0
- data/test/example_1_spec.rb +17 -0
- data/test/example_2_spec.rb +28 -0
- data/test/example_3_spec.rb +64 -0
- data/test/example_full_spec.rb +68 -0
- data/test/test_yml.yml +53 -0
- metadata +100 -0
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
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>ООО 'Рога & Копыта'</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>ООО 'Рога & Копыта'</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>ООО 'Рога & Копыта'</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,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(/[\"\&\>\<\']/, '"' => '"',
|
|
6
|
+
'&' => '&',
|
|
7
|
+
'>' => '>',
|
|
8
|
+
'<' => '<',
|
|
9
|
+
"'" => ''')
|
|
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,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,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
|
data/lib/yml_builder.rb
ADDED
|
@@ -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>��� '���� & ������'</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: []
|