business_catalyst 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/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +11 -0
- data/Gemfile +13 -0
- data/Guardfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +104 -0
- data/Rakefile +1 -0
- data/business_catalyst.gemspec +24 -0
- data/lib/business_catalyst/csv/catalog_row.rb +29 -0
- data/lib/business_catalyst/csv/file_splitter.rb +125 -0
- data/lib/business_catalyst/csv/product_attribute.rb +129 -0
- data/lib/business_catalyst/csv/product_row.rb +110 -0
- data/lib/business_catalyst/csv/row.rb +108 -0
- data/lib/business_catalyst/csv/transformers/catalog_transformer.rb +31 -0
- data/lib/business_catalyst/csv/transformers/currency_transformer.rb +53 -0
- data/lib/business_catalyst/csv/transformers/invalid_input_error.rb +9 -0
- data/lib/business_catalyst/csv/transformers/product_attributes_transformer.rb +45 -0
- data/lib/business_catalyst/csv/transformers/product_code_transformer.rb +21 -0
- data/lib/business_catalyst/csv/transformers/seo_friendly_url_transformer.rb +38 -0
- data/lib/business_catalyst/csv/transformers/transformer.rb +22 -0
- data/lib/business_catalyst/csv/transformers/uri_array_transformer.rb +15 -0
- data/lib/business_catalyst/csv/transformers/uri_transformer.rb +14 -0
- data/lib/business_catalyst/csv/transformers.rb +58 -0
- data/lib/business_catalyst/version.rb +3 -0
- data/lib/business_catalyst.rb +40 -0
- data/spec/lib/business_catalyst/business_catalyst_spec.rb +34 -0
- data/spec/lib/business_catalyst/csv/catalog_row_spec.rb +5 -0
- data/spec/lib/business_catalyst/csv/file_splitter_spec.rb +103 -0
- data/spec/lib/business_catalyst/csv/product_attribute_spec.rb +65 -0
- data/spec/lib/business_catalyst/csv/product_row_spec.rb +5 -0
- data/spec/lib/business_catalyst/csv/row_spec.rb +110 -0
- data/spec/lib/business_catalyst/csv/transformers/catalog_transformer_spec.rb +37 -0
- data/spec/lib/business_catalyst/csv/transformers/currency_transformer_spec.rb +65 -0
- data/spec/lib/business_catalyst/csv/transformers/product_attributes_transformer_spec.rb +76 -0
- data/spec/lib/business_catalyst/csv/transformers/seo_friendly_url_transformer_spec.rb +36 -0
- data/spec/lib/business_catalyst/csv/transformers/transformer_spec.rb +25 -0
- data/spec/lib/business_catalyst/csv/transformers/urI_transformer_spec.rb +17 -0
- data/spec/lib/business_catalyst/csv/transformers_spec.rb +81 -0
- data/spec/spec_helper.rb +6 -0
- metadata +140 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 3417d7cd299af29d9ef7c26b7fd7f4b6667c2ed5
|
|
4
|
+
data.tar.gz: 5574296fb9838e4fcbf76c4a89b2319866fb0b75
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c83fe2e411426b7ab1fb19ed55a10265f33e66f264d75bbe33e821161bfb1896fbfb4ccb74541e34e11e8c36a6452dc68c69df500f6878bdca209b42e417c41a
|
|
7
|
+
data.tar.gz: 814db6e8948d5dde147520763164662b78dae8fed2e8dae662b1739e6641c5d4f225744856125c1dd9bad25d45f46d3827019ed6888d64af98ba83375396d5e9
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
source 'https://rubygems.org'
|
|
2
|
+
|
|
3
|
+
# Specify your gem's dependencies in business_catalyst.gemspec
|
|
4
|
+
gemspec
|
|
5
|
+
|
|
6
|
+
# Skip guard if we are on 1.8 (it won't work).
|
|
7
|
+
# Guard also needs to be in its own group so Travis won't waste time installing it.
|
|
8
|
+
if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new("1.9.3")
|
|
9
|
+
group :guard do
|
|
10
|
+
gem 'ruby_gntp'
|
|
11
|
+
gem 'guard-rspec'
|
|
12
|
+
end
|
|
13
|
+
end
|
data/Guardfile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# A sample Guardfile
|
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
|
3
|
+
|
|
4
|
+
guard :rspec,
|
|
5
|
+
:cmd => "bundle exec rspec",
|
|
6
|
+
:all_after_pass => true,
|
|
7
|
+
:all_on_start => true,
|
|
8
|
+
:failed_mode => :keep do
|
|
9
|
+
watch(%r{^spec/.+_spec\.rb$})
|
|
10
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
|
11
|
+
watch('spec/spec_helper.rb') { "spec" }
|
|
12
|
+
end
|
|
13
|
+
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2014 Tortus Technologies, Inc.
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# BusinessCatalyst
|
|
2
|
+
|
|
3
|
+
Tools for building CSV's for Adobe Business Catalyst e-commerce platform in Ruby.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
gem 'business_catalyst', '~> 0.1.0'
|
|
10
|
+
|
|
11
|
+
And then execute:
|
|
12
|
+
|
|
13
|
+
$ bundle
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Start with subclassing any of the provided Row classes:
|
|
18
|
+
|
|
19
|
+
* BusinessCatalyst::CSV::ProductRow
|
|
20
|
+
* BusinessCatalyst::CSV::CatalogRow
|
|
21
|
+
|
|
22
|
+
Then define methods to return values for the various BC columns.
|
|
23
|
+
Use the #map method to ensure correct naming. (In general, it is just the
|
|
24
|
+
underscored version of the BC column name, with '?' appended for
|
|
25
|
+
boolean columns. The COLUMNS constant in the base class has the complete
|
|
26
|
+
list, and I recommend referring to it extensively.)
|
|
27
|
+
|
|
28
|
+
### Building a Product CSV
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
class MyRow < BusinessCatalyst::CSV::ProductRow
|
|
32
|
+
|
|
33
|
+
default_currency "US" # optional, US is already the default
|
|
34
|
+
|
|
35
|
+
def initialize(product)
|
|
36
|
+
@product = product
|
|
37
|
+
super
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
map(:product_code) { @product.code }
|
|
41
|
+
map(:name) { @product.name }
|
|
42
|
+
map(:catalog) { ["Some", "Accessories"] }
|
|
43
|
+
map(:enabled?) { @product.active }
|
|
44
|
+
map(:inventory_control?) { false }
|
|
45
|
+
|
|
46
|
+
# ... (other properties here)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Later, with a CSV opened for writing:
|
|
50
|
+
|
|
51
|
+
csv << MyRow.headers
|
|
52
|
+
products.each do |product|
|
|
53
|
+
csv << MyRow.new(product).to_a
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Returning Ruby data types in your methods
|
|
58
|
+
|
|
59
|
+
In general, you can define your methods to return Ruby types such as Array,
|
|
60
|
+
Float (try to use BigDecimal or FixNum for currency though), True/False, etc.,
|
|
61
|
+
and the gem will turn it into the correct text for Business Catalyst for you,
|
|
62
|
+
and handle all escaping.
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
# Array:
|
|
66
|
+
map(:catalog) { ["Value1", "Value2"] } # becomes: "Value1;Value2"
|
|
67
|
+
|
|
68
|
+
# Numeric:
|
|
69
|
+
map(:sell_price) { 10.0 } # becomes: "US/10.0"
|
|
70
|
+
|
|
71
|
+
# boolean:
|
|
72
|
+
map(:enabled?) { true } # becomes: "Y"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### If you have more than 10k product rows to import, use BusinessCatalyst::CSV::FileSplitter
|
|
76
|
+
|
|
77
|
+
Business Catalyst limits the number of products you can import in a single
|
|
78
|
+
CSV to 10000. However, you can get around this limit by importing multiple
|
|
79
|
+
files. We have a class to help!
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
# Simplest usage, assuming we already have some products to export:
|
|
83
|
+
splitter = BusinessCatalyst::CSV::FileSplitter.new("output_base_name.csv", header_row: MyRow.headers)
|
|
84
|
+
splitter.start do |splitter|
|
|
85
|
+
product_rows.each do |row|
|
|
86
|
+
splitter.start_row
|
|
87
|
+
splitter.current_file << row.to_a
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
See the class definition for all available options.
|
|
92
|
+
|
|
93
|
+
## Contributing
|
|
94
|
+
|
|
95
|
+
1. Fork it
|
|
96
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
97
|
+
3. Run tests:
|
|
98
|
+
|
|
99
|
+
$ bundle install
|
|
100
|
+
$ bundle exec rspec
|
|
101
|
+
|
|
102
|
+
4. Commit your changes (`git commit -am 'Add some feature'`)
|
|
103
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
|
104
|
+
6. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'business_catalyst/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "business_catalyst"
|
|
8
|
+
spec.version = BusinessCatalyst::VERSION
|
|
9
|
+
spec.authors = ["Tortus Tek, Inc."]
|
|
10
|
+
spec.email = ["support@tortus.com"]
|
|
11
|
+
spec.description = %q{Libraries to help with exporting product data to BC.}
|
|
12
|
+
spec.summary = "business_catalyst-#{BusinessCatalyst::VERSION}"
|
|
13
|
+
spec.homepage = "https://github.com/tortus/business_catalyst"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($/)
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
22
|
+
spec.add_development_dependency "rake", "~> 10"
|
|
23
|
+
spec.add_development_dependency "rspec", "~> 2.14"
|
|
24
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module BusinessCatalyst
|
|
2
|
+
module CSV
|
|
3
|
+
class CatalogRow < Row
|
|
4
|
+
|
|
5
|
+
COLUMNS = [
|
|
6
|
+
["Catalog Name/Heirarchy", :catalog_name_hierarchy, nil, CatalogTransformer],
|
|
7
|
+
["Description", :description],
|
|
8
|
+
["Image", :image],
|
|
9
|
+
["Weighting", :weighting],
|
|
10
|
+
["Release Date", :release_date],
|
|
11
|
+
["Expiry Date", :expiration_date],
|
|
12
|
+
["Template ID", :template_id, 0, TemplateIDTransformer],
|
|
13
|
+
["Enabled", :enabled?, true, BooleanTransformer],
|
|
14
|
+
["Enable XML Feed", :enable_xml_feed?, true, BooleanTransformer],
|
|
15
|
+
["Show Product Prices", :show_product_prices?, true, BooleanTransformer],
|
|
16
|
+
["Catalog Title", :catalog_title],
|
|
17
|
+
["Browse Panel Min Price", :browse_panel_min_price, nil, CurrencyTransformer],
|
|
18
|
+
["Browse Panel Max Price", :browse_panel_max_price, nil, CurrencyTransformer],
|
|
19
|
+
["Browse Panel Slots", :browse_panel_min_slots],
|
|
20
|
+
["SEO Friendly URL", :seo_friendly_url, nil, SEOFriendlyUrlTransformer] # Must be globally unique
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
def self.columns
|
|
24
|
+
COLUMNS
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'csv'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module BusinessCatalyst
|
|
6
|
+
module CSV
|
|
7
|
+
|
|
8
|
+
# Business Catalyst can only import 10k product rows at a time.
|
|
9
|
+
# Use this class to help split up files.
|
|
10
|
+
#
|
|
11
|
+
# == Usage
|
|
12
|
+
#
|
|
13
|
+
# headers = MyRowClass.headers
|
|
14
|
+
# splitter = FileSplitter.new("output_base_name", max_rows_per_file: 10_000, header_row: headers)
|
|
15
|
+
# splitter.start do |splitter|
|
|
16
|
+
# product_rows.each do |row|
|
|
17
|
+
# splitter.start_row
|
|
18
|
+
# splitter.current_file << row.to_a
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# If you want to watch for when a new file is opened and maybe
|
|
23
|
+
# do some logging, you can use the #on_file_change method:
|
|
24
|
+
#
|
|
25
|
+
# splitter.on_file_change do |splitter|
|
|
26
|
+
# puts "opened #{splitter.current_file.path}"
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
class FileSplitter
|
|
30
|
+
|
|
31
|
+
attr_accessor :base_name, :max_rows_per_file, :header_row, :verbose, :logger
|
|
32
|
+
attr_reader :current_row, :total_rows, :current_file, :all_files
|
|
33
|
+
|
|
34
|
+
alias_method :file_names, :all_files
|
|
35
|
+
|
|
36
|
+
def initialize(base_name, options = {})
|
|
37
|
+
@base_name = base_name
|
|
38
|
+
@max_rows_per_file = options.fetch(:max_rows_per_file) { 10_000 }
|
|
39
|
+
@header_row = options.fetch(:header_row) { nil }
|
|
40
|
+
@verbose = options.fetch(:verbose) { false }
|
|
41
|
+
@logger = options.fetch(:logger) { nil }
|
|
42
|
+
|
|
43
|
+
@current_row = 0
|
|
44
|
+
@total_rows = 0
|
|
45
|
+
|
|
46
|
+
@all_files = []
|
|
47
|
+
@current_file = nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def start
|
|
51
|
+
begin
|
|
52
|
+
increment_file(1, max_rows_per_file) # will be off by one unless we set manually
|
|
53
|
+
yield self
|
|
54
|
+
ensure
|
|
55
|
+
close
|
|
56
|
+
end
|
|
57
|
+
rename_last_file
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Call before writing new row
|
|
61
|
+
def start_row
|
|
62
|
+
@current_row += 1
|
|
63
|
+
@total_rows += 1
|
|
64
|
+
if @current_row > max_rows_per_file
|
|
65
|
+
increment_file
|
|
66
|
+
@current_row = 1
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def on_file_change(&block)
|
|
71
|
+
@on_file_change = Proc.new(&block)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def close
|
|
75
|
+
current_file.close if current_file
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def increment_file(min_row = total_rows, max_row = total_rows + max_rows_per_file - 1)
|
|
81
|
+
@current_file.close if @current_file
|
|
82
|
+
new_file_name = file_name_with_range(base_name, min_row, max_row)
|
|
83
|
+
@all_files << new_file_name
|
|
84
|
+
log "writing to #{new_file_name}" if verbose
|
|
85
|
+
@current_file = ::CSV.open(new_file_name, 'wb')
|
|
86
|
+
if header_row
|
|
87
|
+
@current_file << header_row
|
|
88
|
+
end
|
|
89
|
+
trigger_on_file_change
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def trigger_on_file_change
|
|
93
|
+
if @on_file_change.kind_of?(Proc)
|
|
94
|
+
if @on_file_change.arity >= 1
|
|
95
|
+
@on_file_change.call(self)
|
|
96
|
+
else
|
|
97
|
+
@on_file_change.call
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def file_name_with_range(base_name, min, max)
|
|
103
|
+
"#{base_name}_#{min}-#{max}.csv"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def rename_last_file
|
|
107
|
+
last_file = @all_files.pop
|
|
108
|
+
if last_file
|
|
109
|
+
new_name = last_file.sub(/-\d+\.csv\z/i, "-#{total_rows}.csv")
|
|
110
|
+
FileUtils.mv last_file, new_name
|
|
111
|
+
@all_files << new_name
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def log(msg)
|
|
116
|
+
if logger
|
|
117
|
+
logger.info msg
|
|
118
|
+
else
|
|
119
|
+
puts msg
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module BusinessCatalyst
|
|
3
|
+
module CSV
|
|
4
|
+
|
|
5
|
+
class ProductAttribute
|
|
6
|
+
Option = Struct.new(:name, :image, :price)
|
|
7
|
+
|
|
8
|
+
attr_accessor :name, :required, :keep_stock
|
|
9
|
+
attr_reader :display_as
|
|
10
|
+
|
|
11
|
+
def initialize(name, options = {})
|
|
12
|
+
@name = name
|
|
13
|
+
self.display_as = options.delete(:display_as) { nil }
|
|
14
|
+
@required = options.delete(:required) { false }
|
|
15
|
+
@keep_stock = options.delete(:keep_stock) { false }
|
|
16
|
+
unless options.empty?
|
|
17
|
+
raise ArgumentError, "unrecognized arguments: #{options.keys.inspect}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def display_as=(value)
|
|
22
|
+
@display_as = self.class.normalize_display_as(value)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def options
|
|
26
|
+
@options ||= []
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Usage:
|
|
30
|
+
#
|
|
31
|
+
# # Simplest:
|
|
32
|
+
# add_option(name, image, price)
|
|
33
|
+
#
|
|
34
|
+
# # Manually create ProductAttribute::Option instance:
|
|
35
|
+
# add_option(ProductAttribute::Option.new(name, image, price))
|
|
36
|
+
#
|
|
37
|
+
def add_option(*args)
|
|
38
|
+
option = nil
|
|
39
|
+
if args.length == 1
|
|
40
|
+
arg = args.first
|
|
41
|
+
if arg.kind_of?(Option)
|
|
42
|
+
option = arg
|
|
43
|
+
elsif arg.kind_of?(Hash)
|
|
44
|
+
option = Option.new(arg.fetch(:name), arg.fetch(:image, nil), arg.fetch(:price, nil))
|
|
45
|
+
else
|
|
46
|
+
raise ArgumentError, "unrecognized argument: #{arg.inspect}"
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
name, image, price = *args
|
|
50
|
+
option = Option.new(name, image, price)
|
|
51
|
+
end
|
|
52
|
+
options << option
|
|
53
|
+
option
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Usage:
|
|
57
|
+
#
|
|
58
|
+
# # List of names:
|
|
59
|
+
# add_options("Name 1", "Name 2", ...)
|
|
60
|
+
#
|
|
61
|
+
# # Arrays:
|
|
62
|
+
# add_options([name, image, price], [name, image, price], ...)
|
|
63
|
+
#
|
|
64
|
+
# # Hashes:
|
|
65
|
+
# add_options({name: name, image: image, price: price}, ...)
|
|
66
|
+
#
|
|
67
|
+
# # ProductAttribute::Option instances:
|
|
68
|
+
# option_1 = ProductAttribute::Option.new(name, image, price)
|
|
69
|
+
# add_options(option_1, ...)
|
|
70
|
+
#
|
|
71
|
+
def add_options(*args)
|
|
72
|
+
if args.length > 1
|
|
73
|
+
options = args.map {|arg| Array(arg)}
|
|
74
|
+
elsif args.first.respond_to?(:each)
|
|
75
|
+
options = args.first
|
|
76
|
+
else
|
|
77
|
+
raise ArgumentError, "options must be an Array of ProductAttribute::Option"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
options.each do |option|
|
|
81
|
+
if option.kind_of?(Hash)
|
|
82
|
+
name = option.fetch(:name)
|
|
83
|
+
image = option.fetch(:image, nil)
|
|
84
|
+
price = option.fetch(:price, nil)
|
|
85
|
+
add_option(name, image, price)
|
|
86
|
+
|
|
87
|
+
elsif option.kind_of?(Array)
|
|
88
|
+
name, image, price = *option
|
|
89
|
+
add_option(name, image, price)
|
|
90
|
+
|
|
91
|
+
elsif option.kind_of?(Option)
|
|
92
|
+
add_option(option)
|
|
93
|
+
|
|
94
|
+
else
|
|
95
|
+
raise ArgumentError, "#{option} is not a valid ProductAttribute::Option"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Convert display_as to integer for BC, raises ArgumentError
|
|
101
|
+
# if input cannot be mapped to a valid integer. The following
|
|
102
|
+
# symbols are accepted:
|
|
103
|
+
#
|
|
104
|
+
# * :dropdown = 5
|
|
105
|
+
# * :checkbox = 6
|
|
106
|
+
# * :radio = 7
|
|
107
|
+
def self.normalize_display_as(display_as)
|
|
108
|
+
if display_as.kind_of?(Symbol)
|
|
109
|
+
case display_as
|
|
110
|
+
when :dropdown
|
|
111
|
+
5
|
|
112
|
+
when :checkbox
|
|
113
|
+
6
|
|
114
|
+
when :radio
|
|
115
|
+
7
|
|
116
|
+
else
|
|
117
|
+
raise ArgumentError, "display_as must be :dropdown, :checkbox, or :radio"
|
|
118
|
+
end
|
|
119
|
+
elsif ![5, 6, 7].include?(display_as)
|
|
120
|
+
raise ArgumentError, "display_as must be 5, 6, or 7"
|
|
121
|
+
else
|
|
122
|
+
display_as
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module BusinessCatalyst
|
|
3
|
+
module CSV
|
|
4
|
+
|
|
5
|
+
# Subclass and call #map to set values for columns. You may return
|
|
6
|
+
# arrays for columns that take multiple values, like catalog,
|
|
7
|
+
# and true/false for boolean columns.
|
|
8
|
+
#
|
|
9
|
+
# nil will always be interpreted as blank.
|
|
10
|
+
#
|
|
11
|
+
# == What method name to use?
|
|
12
|
+
#
|
|
13
|
+
# Refer to the COLUMNS constant, but in general use the
|
|
14
|
+
# underscored name of the column, with '?' appended to columns
|
|
15
|
+
# that expect a boolean.
|
|
16
|
+
#
|
|
17
|
+
# == Example
|
|
18
|
+
#
|
|
19
|
+
# class MyRow < BusinessCatalyst::CSV::ProductRow
|
|
20
|
+
#
|
|
21
|
+
# def initialize(product)
|
|
22
|
+
# @product = product
|
|
23
|
+
# super
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# map(:product_code) { @product.code }
|
|
27
|
+
# map(:name) { @product.name }
|
|
28
|
+
# map(:catalog) { ["Some", "Accessories"] }
|
|
29
|
+
# map(:enabled?) { @product.active }
|
|
30
|
+
# map(:inventory_control?) { false }
|
|
31
|
+
#
|
|
32
|
+
# # ... (other properties here)
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# Later, with a CSV opened for writing:
|
|
36
|
+
#
|
|
37
|
+
# csv << MyRow.headers
|
|
38
|
+
# products.each do |product|
|
|
39
|
+
# csv << MyRow.new(product).to_a
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
class ProductRow < Row
|
|
43
|
+
|
|
44
|
+
def self.columns
|
|
45
|
+
COLUMNS
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# [Header, method, default, transformer]
|
|
49
|
+
COLUMNS = [
|
|
50
|
+
["Product Code", :product_code, nil, ProductCodeTransformer],
|
|
51
|
+
["Name", :name],
|
|
52
|
+
["Description", :description],
|
|
53
|
+
["Small Image", :small_image, nil, URITransformer],
|
|
54
|
+
["Large Image", :large_image, nil, URITransformer],
|
|
55
|
+
["Catalog", :catalog, [], CatalogTransformer], # Can be an array of path names, or an array of arrays of path names (multiple catalogs)
|
|
56
|
+
["Sell Price", :sell_price, nil, CurrencyTransformer],
|
|
57
|
+
["Recommended Retail Price", :recommended_retail_price, nil, CurrencyTransformer],
|
|
58
|
+
["Tax Code", :tax_code],
|
|
59
|
+
["SEO friendly URL", :seo_friendly_url],
|
|
60
|
+
["Grouping Product Codes", :grouping_product_codes, nil, ArrayTransformer],
|
|
61
|
+
["Grouping Product Descriptions", :grouping_product_descriptions, nil, ArrayTransformer],
|
|
62
|
+
["Supplier CRM ID", :supplier_crm_id],
|
|
63
|
+
["Supplier Commission Percentage", :supplier_commission_percentage],
|
|
64
|
+
["Weighting/Order", :weighting_order],
|
|
65
|
+
["Related Products", :related_products, [], ArrayTransformer],
|
|
66
|
+
["Weight In KG/Pounds", :weight_in_kg_or_pounds],
|
|
67
|
+
["Product Width (Previously Volume)", :product_width],
|
|
68
|
+
["Keywords", :keywords],
|
|
69
|
+
["Unit Type", :unit_type],
|
|
70
|
+
["Min Units", :min_units],
|
|
71
|
+
["Max Units", :max_units],
|
|
72
|
+
["In Stock", :in_stock],
|
|
73
|
+
["On Order", :on_order],
|
|
74
|
+
["Re-order Threshold", :re_order_threshold],
|
|
75
|
+
["Inventory Control", :inventory_control?, false, BooleanTransformer],
|
|
76
|
+
["Can Pre-Order", :can_pre_order?, false, BooleanTransformer],
|
|
77
|
+
["Custom 1", :custom_1],
|
|
78
|
+
["Custom 2", :custom_2],
|
|
79
|
+
["Custom 3", :custom_3],
|
|
80
|
+
["Custom 4", :custom_4],
|
|
81
|
+
["Poplets", :poplets, [], URIArrayTransformer],
|
|
82
|
+
["Enabled", :enabled?, true, BooleanTransformer],
|
|
83
|
+
["Capture Details", :capture_details?, false, BooleanTransformer],
|
|
84
|
+
["Limit Download Count", :limit_download_count],
|
|
85
|
+
["Limit Download IP", :limit_download_ip],
|
|
86
|
+
["On Sale", :on_sale?, false, BooleanTransformer],
|
|
87
|
+
["Hide if Out of Stock", :hide_if_out_of_stock?, false, BooleanTransformer],
|
|
88
|
+
["Attributes", :attributes, nil, ProductAttributesTransformer],
|
|
89
|
+
["Gift Voucher", :gift_voucher?, false, BooleanTransformer],
|
|
90
|
+
["Drop Shipping", :drop_shipping?, false, BooleanTransformer],
|
|
91
|
+
["Product Height", :product_height],
|
|
92
|
+
["Product Depth", :product_depth],
|
|
93
|
+
["Exclude From Search Results", :exclude_from_search_results?, false, BooleanTransformer],
|
|
94
|
+
["Product Title", :product_title],
|
|
95
|
+
["Wholesale Sale Price", :wholesale_sale_price, nil, CurrencyTransformer],
|
|
96
|
+
["Tax Code", :tax_code],
|
|
97
|
+
["Cycle Type", :cycle_type],
|
|
98
|
+
["Cycle Type Count", :cycle_type_count],
|
|
99
|
+
["Product URL", :product_url],
|
|
100
|
+
["Product Affiliate URL", :product_affiliate_url],
|
|
101
|
+
["Variations Enabled", :variations_enabled?, false, BooleanTransformer],
|
|
102
|
+
["Variations Code", :variations_code],
|
|
103
|
+
["Variation Options", :variations_options],
|
|
104
|
+
["Role Responsible", :role_responsible],
|
|
105
|
+
["Product Meta Description", :product_meta_description]
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|