prestashop 2.0.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/LICENSE.txt +56 -0
- data/README.md +111 -0
- data/Rakefile +9 -0
- data/lib/prestashop.rb +21 -0
- data/lib/prestashop/api.rb +9 -0
- data/lib/prestashop/api/connection.rb +225 -0
- data/lib/prestashop/api/converter.rb +152 -0
- data/lib/prestashop/api/error.rb +18 -0
- data/lib/prestashop/api/refinement.rb +14 -0
- data/lib/prestashop/client.rb +19 -0
- data/lib/prestashop/client/cache.rb +53 -0
- data/lib/prestashop/client/error.rb +9 -0
- data/lib/prestashop/client/implementation.rb +33 -0
- data/lib/prestashop/mapper.rb +9 -0
- data/lib/prestashop/mapper/extension.rb +161 -0
- data/lib/prestashop/mapper/model.rb +38 -0
- data/lib/prestashop/mapper/models.rb +1 -0
- data/lib/prestashop/mapper/models/address.rb +9 -0
- data/lib/prestashop/mapper/models/carrier.rb +9 -0
- data/lib/prestashop/mapper/models/cart.rb +9 -0
- data/lib/prestashop/mapper/models/cart_rule.rb +9 -0
- data/lib/prestashop/mapper/models/category.rb +114 -0
- data/lib/prestashop/mapper/models/combination.rb +81 -0
- data/lib/prestashop/mapper/models/contact.rb +9 -0
- data/lib/prestashop/mapper/models/content_management_system.rb +9 -0
- data/lib/prestashop/mapper/models/country.rb +15 -0
- data/lib/prestashop/mapper/models/currency.rb +9 -0
- data/lib/prestashop/mapper/models/customer.rb +9 -0
- data/lib/prestashop/mapper/models/customer_message.rb +9 -0
- data/lib/prestashop/mapper/models/customer_thread.rb +9 -0
- data/lib/prestashop/mapper/models/delivery.rb +9 -0
- data/lib/prestashop/mapper/models/employee.rb +9 -0
- data/lib/prestashop/mapper/models/group.rb +9 -0
- data/lib/prestashop/mapper/models/guest.rb +9 -0
- data/lib/prestashop/mapper/models/image.rb +52 -0
- data/lib/prestashop/mapper/models/image_type.rb +9 -0
- data/lib/prestashop/mapper/models/language.rb +15 -0
- data/lib/prestashop/mapper/models/manufacturer.rb +79 -0
- data/lib/prestashop/mapper/models/order.rb +9 -0
- data/lib/prestashop/mapper/models/order_carrier.rb +9 -0
- data/lib/prestashop/mapper/models/order_detail.rb +9 -0
- data/lib/prestashop/mapper/models/order_discount.rb +9 -0
- data/lib/prestashop/mapper/models/order_history.rb +9 -0
- data/lib/prestashop/mapper/models/order_invoice.rb +9 -0
- data/lib/prestashop/mapper/models/order_payment.rb +9 -0
- data/lib/prestashop/mapper/models/order_state.rb +9 -0
- data/lib/prestashop/mapper/models/price_range.rb +9 -0
- data/lib/prestashop/mapper/models/product.rb +198 -0
- data/lib/prestashop/mapper/models/product_feature.rb +69 -0
- data/lib/prestashop/mapper/models/product_feature_value.rb +60 -0
- data/lib/prestashop/mapper/models/product_option.rb +72 -0
- data/lib/prestashop/mapper/models/product_option_value.rb +56 -0
- data/lib/prestashop/mapper/models/product_supplier.rb +31 -0
- data/lib/prestashop/mapper/models/search.rb +9 -0
- data/lib/prestashop/mapper/models/shop.rb +9 -0
- data/lib/prestashop/mapper/models/shop_group.rb +9 -0
- data/lib/prestashop/mapper/models/specific_price.rb +9 -0
- data/lib/prestashop/mapper/models/specific_price_rule.rb +9 -0
- data/lib/prestashop/mapper/models/state.rb +9 -0
- data/lib/prestashop/mapper/models/stock.rb +9 -0
- data/lib/prestashop/mapper/models/stock_available.rb +35 -0
- data/lib/prestashop/mapper/models/stock_movement.rb +9 -0
- data/lib/prestashop/mapper/models/stock_movement_reason.rb +9 -0
- data/lib/prestashop/mapper/models/store.rb +9 -0
- data/lib/prestashop/mapper/models/supplier.rb +45 -0
- data/lib/prestashop/mapper/models/supply_order.rb +9 -0
- data/lib/prestashop/mapper/models/supply_order_detail.rb +9 -0
- data/lib/prestashop/mapper/models/supply_order_history.rb +9 -0
- data/lib/prestashop/mapper/models/supply_order_receipt_history.rb +9 -0
- data/lib/prestashop/mapper/models/supply_order_state.rb +9 -0
- data/lib/prestashop/mapper/models/tag.rb +9 -0
- data/lib/prestashop/mapper/models/tax.rb +24 -0
- data/lib/prestashop/mapper/models/tax_rule.rb +15 -0
- data/lib/prestashop/mapper/models/tax_rule_group.rb +9 -0
- data/lib/prestashop/mapper/models/translated_configuration.rb +9 -0
- data/lib/prestashop/mapper/models/warehouse.rb +9 -0
- data/lib/prestashop/mapper/models/warehouse_product_location.rb +9 -0
- data/lib/prestashop/mapper/models/weight_range.rb +9 -0
- data/lib/prestashop/mapper/models/zone.rb +9 -0
- data/lib/prestashop/mapper/refinement.rb +43 -0
- data/lib/prestashop/version.rb +3 -0
- metadata +279 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module Prestashop
|
2
|
+
module Api
|
3
|
+
class InvalidCredentials < RuntimeError
|
4
|
+
def initialize
|
5
|
+
super "Your credentials are invalid"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class RequestFailed < RuntimeError
|
10
|
+
attr_reader :response
|
11
|
+
def initialize response
|
12
|
+
@response = response
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ParserError < RuntimeError; end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'prestashop/client/error'
|
2
|
+
require 'prestashop/client/implementation'
|
3
|
+
require 'prestashop/client/cache'
|
4
|
+
|
5
|
+
module Prestashop
|
6
|
+
module Client
|
7
|
+
extend SingleForwardable
|
8
|
+
def_delegators :current, :connection, :cache
|
9
|
+
def_delegators :connection, :create, :read, :update, :delete, :check, :upload
|
10
|
+
def_delegators :cache, :manufacturers_cache, :clear_manufacturers_cache, :categories_cache, :clear_categories_cache, :features_cache, :clear_features_cache,
|
11
|
+
:feature_values_cache, :clear_feature_values_cache, :options_cache, :clear_options_cache, :option_values_cache, :clear_option_values_cache
|
12
|
+
|
13
|
+
# Delegate to current user implementation
|
14
|
+
#
|
15
|
+
def self.current
|
16
|
+
Implementation.current
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Prestashop
|
2
|
+
module Client
|
3
|
+
class Cache
|
4
|
+
def manufacturers_cache
|
5
|
+
@manufacturers_cache ||= Mapper::Manufacturer.cache
|
6
|
+
end
|
7
|
+
|
8
|
+
def clear_manufacturers_cache
|
9
|
+
@manufacturers_cache = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def categories_cache
|
13
|
+
@categories_cache ||= Mapper::Category.cache
|
14
|
+
end
|
15
|
+
|
16
|
+
def clear_categories_cache
|
17
|
+
@categories_cache = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def features_cache
|
21
|
+
@features_cache ||= Mapper::ProductFeature.cache
|
22
|
+
end
|
23
|
+
|
24
|
+
def clear_features_cache
|
25
|
+
@features_cache = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def feature_values_cache
|
29
|
+
@feature_values_cache ||= Mapper::ProductFeatureValue.cache
|
30
|
+
end
|
31
|
+
|
32
|
+
def clear_feature_values_cache
|
33
|
+
@feature_values_cache = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def options_cache
|
37
|
+
@options_cache ||= Mapper::ProductOption.cache
|
38
|
+
end
|
39
|
+
|
40
|
+
def clear_options_cache
|
41
|
+
@options_cache = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def option_values_cache
|
45
|
+
@option_values_cache ||= Mapper::ProductOptionValue.cache
|
46
|
+
end
|
47
|
+
|
48
|
+
def clear_option_values_cache
|
49
|
+
@option_values_cache = nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Prestashop
|
4
|
+
module Client
|
5
|
+
class Implementation
|
6
|
+
include Singleton
|
7
|
+
attr_reader :connection, :cache
|
8
|
+
|
9
|
+
# Initialize new client see +Api::Connection#new+
|
10
|
+
#
|
11
|
+
def initialize api_key, api_url
|
12
|
+
@connection = Api::Connection.new api_key, api_url
|
13
|
+
@cache = Cache.new
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
# Create new user implementation, keep it in current thread to allow multithearding, see +#new+
|
19
|
+
#
|
20
|
+
def create api_key, api_url
|
21
|
+
Thread.current[:prestashop_client] = new api_key, api_url
|
22
|
+
current
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get current client or raise exception, when client isn't initialized
|
26
|
+
#
|
27
|
+
def current
|
28
|
+
Thread.current[:prestashop_client] ? Thread.current[:prestashop_client] : raise(UnitializedClient)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Prestashop
|
2
|
+
module Mapper
|
3
|
+
module Extension
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
# Determinate if model with class resource exists with given id
|
7
|
+
#
|
8
|
+
# Car.exists?(1) # => true # if given car exist
|
9
|
+
# Car.exists?(2) # => false # if given car don't exist
|
10
|
+
#
|
11
|
+
def exists? id
|
12
|
+
Client.check self.resource, id
|
13
|
+
end
|
14
|
+
|
15
|
+
# Find model by class resource and given id, returns hash
|
16
|
+
# with all nodes, based on node name as key, node value as value
|
17
|
+
#
|
18
|
+
# Car.find(1) # => { id: 1, name: 'BMW' }
|
19
|
+
# Car.find(2) # => nil
|
20
|
+
#
|
21
|
+
def find id
|
22
|
+
result = Client.read self.resource, id
|
23
|
+
result ? result[self.model] : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Find model by class resource and params in hash
|
27
|
+
# Returns first result, see #where for more informations
|
28
|
+
#
|
29
|
+
# Car.find_by(name: 'BMW') # => 1
|
30
|
+
#
|
31
|
+
def find_by options = {}
|
32
|
+
results = where(options)
|
33
|
+
results ? results.first : nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get models all results by class resource, you can specifi what
|
37
|
+
# you should to see as result by specifiyng +:display+
|
38
|
+
#
|
39
|
+
# Car.all # => [1,2,3]
|
40
|
+
# Car.all(display: ['name']) # => [{ name: { language: { attr: { id: 2, href: 'http://localhost.com/api/languages/2'}, val: 'BMW 7'} }]
|
41
|
+
#
|
42
|
+
def all options = {}
|
43
|
+
result = if options[:display]
|
44
|
+
Client.read self.resource, nil, display: options[:display]
|
45
|
+
else
|
46
|
+
Client.read self.resource
|
47
|
+
end
|
48
|
+
handle_result result, options
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get results by class resource and given conditionals
|
52
|
+
#
|
53
|
+
# Car.where('filter[id_supplier' => 1) # => [1, 2]
|
54
|
+
#
|
55
|
+
def where options = {}
|
56
|
+
result = Client.read self.resource, nil, options
|
57
|
+
handle_result result, options
|
58
|
+
end
|
59
|
+
|
60
|
+
# Destroy model by class resource and given id
|
61
|
+
#
|
62
|
+
# Car.destroy(1) # => true
|
63
|
+
#
|
64
|
+
def destroy id
|
65
|
+
Client.delete self.resource, id
|
66
|
+
end
|
67
|
+
|
68
|
+
# Create hash suitable for update, contains #fixed_hash as hash with deleted
|
69
|
+
# keys, which shouldn't be in payload, if exist
|
70
|
+
#
|
71
|
+
# Car.update_hash(1, name: 'BMW7') # => {name: 'BMW7', manufacturer: 'BMW'}
|
72
|
+
#
|
73
|
+
def update_hash id, options = {}
|
74
|
+
original = defined?(fixed_hash(nil)) ? fixed_hash(id) : find(id)
|
75
|
+
original.merge(options)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Create payload for update, converts hash to XML
|
79
|
+
#
|
80
|
+
# Car.update_payload(1, name: 'BMW 7') # => <prestashop xmlns:xlink="http://www.w3.org/1999/xlink"><car><name><![CDATA[BMW 7]]></name></car></prestashop>
|
81
|
+
#
|
82
|
+
def update_payload id, options = {}
|
83
|
+
Api::Converter.build(self.resource, self.model, update_hash(id, options))
|
84
|
+
end
|
85
|
+
|
86
|
+
# Update model, with class resource by +id+ and given updates
|
87
|
+
#
|
88
|
+
# Car.update(1, name: 'BMW 7') # => {id: 1, name: 'BMW 7'}
|
89
|
+
#
|
90
|
+
def update id, options = {}
|
91
|
+
result = Client.update self.resource, id, update_payload(id, options)
|
92
|
+
result ? result[self.model] : nil
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
# Handle result to return +id+ or array with +ids+ of requested objects
|
97
|
+
#
|
98
|
+
# handle_result({ customers: { customer: [ 1,2 ] } }) # => [1, 2]
|
99
|
+
# handle_result({ customers: { customer: { attr: { id: 1 }} } }) # => [1]
|
100
|
+
#
|
101
|
+
def handle_result result, options = {}
|
102
|
+
if options[:display]
|
103
|
+
if result[self.resource].kind_of?(Hash) and result[self.resource][self.model]
|
104
|
+
objects = result[self.resource][self.model]
|
105
|
+
objects.kind_of?(Array) ? objects : [objects]
|
106
|
+
end
|
107
|
+
else
|
108
|
+
if result[self.resource].kind_of?(Hash) and result[self.resource][self.model]
|
109
|
+
[objects = result[self.resource][self.model]]
|
110
|
+
objects.kind_of?(Array) ? objects.map{ |o| o[:attr][:id] } : [ objects[:attr][:id] ]
|
111
|
+
else
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
module InstanceMethods
|
119
|
+
|
120
|
+
# Generate hash with ID
|
121
|
+
#
|
122
|
+
# car.hash_id(1) # => {id: 1}
|
123
|
+
#
|
124
|
+
def hash_id id
|
125
|
+
{ id: id } if id
|
126
|
+
end
|
127
|
+
|
128
|
+
# Make array of unique IDs in hash
|
129
|
+
#
|
130
|
+
# car.hash_ids(1,2,3) # => [{id: 1},{id: 2},{id: 3}]
|
131
|
+
#
|
132
|
+
def hash_ids ids
|
133
|
+
ids.flatten.uniq.map{|id| hash_id(id)} if ids
|
134
|
+
end
|
135
|
+
|
136
|
+
# Create payload for create new object, coverts hash to XML
|
137
|
+
#
|
138
|
+
# car.payload # => '<prestashop xmlns:xlink="http://www.w3.org/1999/xlink"><car><name><![CDATA[BMW 7]]></name><manufacturer><![CDATA[BMW]]></manufacturer></car></prestashop>'
|
139
|
+
#
|
140
|
+
def payload
|
141
|
+
Api::Converter.build(self.class.resource, self.class.model, hash)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Create new model from instance, based on class resource a payload generated from
|
145
|
+
# hash method
|
146
|
+
#
|
147
|
+
# Car.new(name: 'BMW 7', manufacturer: 'BMW').create # => { id: 1, name: 'BMW 7', manufacturer: 'BMW' }
|
148
|
+
#
|
149
|
+
def create
|
150
|
+
result = Client.create self.class.resource, payload
|
151
|
+
result ? result[self.class.model] : nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.included(receiver)
|
156
|
+
receiver.extend ClassMethods
|
157
|
+
receiver.send :include, InstanceMethods
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
using Prestashop::Mapper::Refinement
|
2
|
+
module Prestashop
|
3
|
+
module Mapper
|
4
|
+
class Model
|
5
|
+
include Extension
|
6
|
+
extend Extension
|
7
|
+
|
8
|
+
# Meta title is same as name, when is not given
|
9
|
+
def meta_title
|
10
|
+
@meta_title ? @meta_title.plain.truncate(61) : name
|
11
|
+
end
|
12
|
+
|
13
|
+
# Meta description is same as description, when is not given
|
14
|
+
def meta_description
|
15
|
+
@meta_description ? @meta_description.restricted.truncate(252) : ( description_short.plain if description_short )
|
16
|
+
end
|
17
|
+
|
18
|
+
# Meta keywords are generated from name, when are not given
|
19
|
+
def meta_keywords
|
20
|
+
@meta_keywords ? @meta_keywords.plain.truncate(61) : name.split(' ').join(', ')
|
21
|
+
end
|
22
|
+
|
23
|
+
def hash_lang name, id_lang
|
24
|
+
{ language: { val: name, attr: { id: id_lang }}} if name
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def resource value = nil
|
29
|
+
value.nil? ? @resource : @resource = value
|
30
|
+
end
|
31
|
+
|
32
|
+
def model value = nil
|
33
|
+
value.nil? ? @model : @model = value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + '/models/*.rb'].each {|file| require file }
|
@@ -0,0 +1,114 @@
|
|
1
|
+
using Prestashop::Mapper::Refinement
|
2
|
+
module Prestashop
|
3
|
+
module Mapper
|
4
|
+
class Category < Model
|
5
|
+
resource :categories
|
6
|
+
model :category
|
7
|
+
|
8
|
+
attr_accessor :id_lang
|
9
|
+
attr_accessor :id, :id_parent, :level_depth, :active, :id_shop_default, :is_root_category, :position
|
10
|
+
attr_writer :name, :description, :link_rewrite
|
11
|
+
|
12
|
+
def initialize args = {}
|
13
|
+
@id = args[:id]
|
14
|
+
@id_parent = args.fetch(:id_parent, 2)
|
15
|
+
@level_depth = args[:level_depth]
|
16
|
+
# nb_products_recursive
|
17
|
+
@active = args.fetch(:active, 1)
|
18
|
+
@id_shop_default = args.fetch(:id_shop_default, 1)
|
19
|
+
@is_root_category = 0
|
20
|
+
@position = args[:position]
|
21
|
+
# date_add
|
22
|
+
# date_upd
|
23
|
+
@name = args.fetch(:name)
|
24
|
+
@link_rewrite = args[:link_rewrite]
|
25
|
+
@description = args[:description]
|
26
|
+
@meta_title = args[:meta_title]
|
27
|
+
@meta_description = args[:meta_description]
|
28
|
+
@meta_keywords = args[:meta_keywords]
|
29
|
+
|
30
|
+
@id_lang = args.fetch(:id_lang)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Category name can't have some symbols and can't be longer than 63
|
34
|
+
def name
|
35
|
+
@name.plain.truncate(61)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Description can have additional symbols and can't be longer than 255
|
39
|
+
def description
|
40
|
+
@description.restricted.truncate(252) if @description
|
41
|
+
end
|
42
|
+
|
43
|
+
# Link rewrite must be usable in uri
|
44
|
+
def link_rewrite
|
45
|
+
@link_rewrite ? @link_rewrite.parameterize : name.parameterize
|
46
|
+
end
|
47
|
+
|
48
|
+
# Category hash structure, which will be converted to XML
|
49
|
+
def hash
|
50
|
+
{ id_parent: id_parent,
|
51
|
+
active: active ,
|
52
|
+
id_shop_default: id_shop_default,
|
53
|
+
is_root_category: is_root_category,
|
54
|
+
name: hash_lang(name, id_lang),
|
55
|
+
link_rewrite: hash_lang(link_rewrite, id_lang),
|
56
|
+
description: hash_lang(description, id_lang),
|
57
|
+
meta_title: hash_lang(name, id_lang),
|
58
|
+
meta_description: hash_lang(description, id_lang),
|
59
|
+
meta_keywords: hash_lang(meta_keywords, id_lang) }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Find category by name and id parent, create new one from hash, when doesn't exist
|
63
|
+
def find_or_create
|
64
|
+
category = self.class.find_in_cache id_parent, name, id_lang
|
65
|
+
unless category
|
66
|
+
category = create
|
67
|
+
Client.clear_categories_cache
|
68
|
+
end
|
69
|
+
category[:id]
|
70
|
+
end
|
71
|
+
|
72
|
+
class << self
|
73
|
+
|
74
|
+
# Search for category based on args on cached categories, see #cache and #Client::Settings.categories_cache
|
75
|
+
# Returns founded category or nil
|
76
|
+
#
|
77
|
+
def find_in_cache id_parent, name, id_lang
|
78
|
+
Client.categories_cache.find{ |c| c[:id_parent] == id_parent and c[:name].find_lang(name, id_lang) }
|
79
|
+
end
|
80
|
+
|
81
|
+
# Requesting all on Prestashop API, displaying id, id_parent, name
|
82
|
+
def cache
|
83
|
+
all display: '[id, id_parent, name]'
|
84
|
+
end
|
85
|
+
|
86
|
+
# Create new category based on given param, delimited by delimiter in settings
|
87
|
+
#
|
88
|
+
# ==== Example:
|
89
|
+
# Category.create_from_name('Apple||iPhone', 2) # => [1, 2]
|
90
|
+
#
|
91
|
+
def create_from_name category_name, id_lang
|
92
|
+
if category_name and !category_name.empty?
|
93
|
+
names = [category_name.split('||')].flatten!
|
94
|
+
categories = []
|
95
|
+
id_parent = 2
|
96
|
+
names.each do |name|
|
97
|
+
id_parent = new(name: name, id_parent: id_parent, id_lang: id_lang).find_or_create
|
98
|
+
categories << id_parent
|
99
|
+
end
|
100
|
+
categories
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def create_from_names category_names, id_lang
|
105
|
+
categories = []
|
106
|
+
category_names.each do |category_name|
|
107
|
+
categories << create_from_name(category_name, id_lang)
|
108
|
+
end
|
109
|
+
categories.flatten.uniq
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|