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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +13 -0
  6. data/Guardfile +13 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +104 -0
  9. data/Rakefile +1 -0
  10. data/business_catalyst.gemspec +24 -0
  11. data/lib/business_catalyst/csv/catalog_row.rb +29 -0
  12. data/lib/business_catalyst/csv/file_splitter.rb +125 -0
  13. data/lib/business_catalyst/csv/product_attribute.rb +129 -0
  14. data/lib/business_catalyst/csv/product_row.rb +110 -0
  15. data/lib/business_catalyst/csv/row.rb +108 -0
  16. data/lib/business_catalyst/csv/transformers/catalog_transformer.rb +31 -0
  17. data/lib/business_catalyst/csv/transformers/currency_transformer.rb +53 -0
  18. data/lib/business_catalyst/csv/transformers/invalid_input_error.rb +9 -0
  19. data/lib/business_catalyst/csv/transformers/product_attributes_transformer.rb +45 -0
  20. data/lib/business_catalyst/csv/transformers/product_code_transformer.rb +21 -0
  21. data/lib/business_catalyst/csv/transformers/seo_friendly_url_transformer.rb +38 -0
  22. data/lib/business_catalyst/csv/transformers/transformer.rb +22 -0
  23. data/lib/business_catalyst/csv/transformers/uri_array_transformer.rb +15 -0
  24. data/lib/business_catalyst/csv/transformers/uri_transformer.rb +14 -0
  25. data/lib/business_catalyst/csv/transformers.rb +58 -0
  26. data/lib/business_catalyst/version.rb +3 -0
  27. data/lib/business_catalyst.rb +40 -0
  28. data/spec/lib/business_catalyst/business_catalyst_spec.rb +34 -0
  29. data/spec/lib/business_catalyst/csv/catalog_row_spec.rb +5 -0
  30. data/spec/lib/business_catalyst/csv/file_splitter_spec.rb +103 -0
  31. data/spec/lib/business_catalyst/csv/product_attribute_spec.rb +65 -0
  32. data/spec/lib/business_catalyst/csv/product_row_spec.rb +5 -0
  33. data/spec/lib/business_catalyst/csv/row_spec.rb +110 -0
  34. data/spec/lib/business_catalyst/csv/transformers/catalog_transformer_spec.rb +37 -0
  35. data/spec/lib/business_catalyst/csv/transformers/currency_transformer_spec.rb +65 -0
  36. data/spec/lib/business_catalyst/csv/transformers/product_attributes_transformer_spec.rb +76 -0
  37. data/spec/lib/business_catalyst/csv/transformers/seo_friendly_url_transformer_spec.rb +36 -0
  38. data/spec/lib/business_catalyst/csv/transformers/transformer_spec.rb +25 -0
  39. data/spec/lib/business_catalyst/csv/transformers/urI_transformer_spec.rb +17 -0
  40. data/spec/lib/business_catalyst/csv/transformers_spec.rb +81 -0
  41. data/spec/spec_helper.rb +6 -0
  42. metadata +140 -0
@@ -0,0 +1,108 @@
1
+ # encoding: utf-8
2
+ require 'csv'
3
+
4
+ module BusinessCatalyst
5
+ module CSV
6
+
7
+ class NoSuchColumnError < StandardError; end
8
+
9
+ # Shared logic for building a row for a CSV export for Business Catalust.
10
+ # Instead of sublcassing Row directly in your project, subclass CatalogRow
11
+ # or ProductRow, which have column definitions set up for those tables.
12
+ class Row
13
+ def initialize(*args)
14
+ end
15
+
16
+ # Override to return Array of column config Arrays in the format:
17
+ #
18
+ # [
19
+ # ["Header", :mapping_name, default_value, TransformerClass],
20
+ # ...
21
+ # ]
22
+ #
23
+ # "default_value" and "TransformerClass" are optional, and will default to nil and
24
+ # GenericTransformer, respectively, if not specified.
25
+ def self.columns
26
+ raise NotImplementedError, "Implement to return Array of column config Arrays in the format: [[\"Header\", :mapping_name, default_value, TransformerClass]]"
27
+ end
28
+
29
+ # Define value for BC column using a block. Column argument should be
30
+ # one of the mapping name symbols returned by Row.columns.
31
+ def self.map(column, &block)
32
+ unless columns.any? {|c| c[1] == column}
33
+ raise NoSuchColumnError, "no such column '#{column.inspect}'"
34
+ end
35
+ define_method(column, &block)
36
+ end
37
+
38
+ # Set the default currency for the current application.
39
+ # Alias for CurrencyTransformer.default_currency=
40
+ #
41
+ # class MyRow < BusinessCatalyst::CSV:Row
42
+ # default_currency "US"
43
+ # end
44
+ #
45
+ def self.default_currency(currency)
46
+ CurrencyTransformer.default_currency = currency
47
+ end
48
+
49
+ # Nice shortcut for generating a csv.
50
+ # TODO: add option to usea file splitter.
51
+ #
52
+ # Manual:
53
+ #
54
+ # ProductRow.generate("products.csv") do |csv|
55
+ # products.each do |product|
56
+ # csv << ProductRow.new(product).to_a
57
+ # end
58
+ # end
59
+ #
60
+ # Automatic collection handling:
61
+ #
62
+ # ProductRow.generate("products.csv", products) do |product|
63
+ # ProductRow.new(product)
64
+ # end
65
+ #
66
+ def self.generate(file_name, collection = nil)
67
+ ::CSV.open(file_name, 'wb') do |csv|
68
+ csv << headers
69
+ if collection.respond_to?(:each)
70
+ collection.each do |item|
71
+ row = yield(item)
72
+ raise ArgumentError, "input must be a valid Row" unless row.kind_of?(self)
73
+ csv << row.to_a
74
+ end
75
+ else
76
+ yield csv
77
+ end
78
+ end
79
+ end
80
+
81
+ def to_a
82
+ self.class.columns.map { |column|
83
+ csv_value(column[1], column)
84
+ }
85
+ end
86
+
87
+ def self.headers
88
+ @headers ||= columns.map(&:first)
89
+ end
90
+
91
+ def csv_value(method, config = nil)
92
+ config ||= self.class.columns.find {|c| c[1] == method}
93
+ raise NoSuchColumnError, "no configuration found for #{method.inspect} in #{self.class.to_s}.columns" if config.nil?
94
+
95
+ input = if respond_to?(method)
96
+ send(method)
97
+ else
98
+ config.fetch(2, nil)
99
+ end
100
+
101
+ transformer = config.fetch(3, GenericTransformer)
102
+ transformer.transform(input)
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: UTF-8
2
+ module BusinessCatalyst
3
+ module CSV
4
+
5
+ class CatalogTransformer < Transformer
6
+ def normalized_input
7
+ # ensure at least a 1D array
8
+ normalized_input = input.kind_of?(Array) ? input : [input]
9
+
10
+ # now convert to 2D array
11
+ unless normalized_input.first.kind_of?(Array)
12
+ normalized_input = [normalized_input]
13
+ end
14
+
15
+ normalized_input
16
+ end
17
+
18
+ def transform
19
+ normalized_input.map { |path|
20
+ if path.any?
21
+ sanitized_names = path.map { |name|
22
+ BusinessCatalyst.sanitize_catalog_name(name)
23
+ }
24
+ "/" + sanitized_names.join("/")
25
+ end
26
+ }.join(";")
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+ require 'bigdecimal'
3
+
4
+ module BusinessCatalyst
5
+ module CSV
6
+
7
+ class CurrencyTransformer < Transformer
8
+ BC_CURRENCY_REGEX = /\A\w+\/\d/.freeze
9
+
10
+ def self.default_currency
11
+ @default_currency || "US"
12
+ end
13
+
14
+ def self.default_currency=(currency)
15
+ @default_currency = currency
16
+ end
17
+
18
+ attr_accessor :currency
19
+
20
+ def initialize(input, currency = nil)
21
+ @currency = currency || self.class.default_currency
22
+ super(input)
23
+ end
24
+
25
+ def transform
26
+ if input
27
+ inputs = Array(input).map {|n| number_to_currency(n) }.compact
28
+ if inputs.any?
29
+ inputs.join(";")
30
+ end
31
+ end
32
+ end
33
+
34
+ def number_to_currency(input)
35
+ if input
36
+ input_s = input.kind_of?(BigDecimal) ? input.to_s('F') : input.to_s.strip
37
+ if input_s != ""
38
+ if is_bc_currency_string?(input_s)
39
+ input_s
40
+ else
41
+ "#{currency}/#{input_s}"
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def is_bc_currency_string?(input)
48
+ !!(input =~ BC_CURRENCY_REGEX)
49
+ end
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+ module BusinessCatalyst
3
+ module CSV
4
+
5
+ class InvalidInputError < StandardError
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+ module BusinessCatalyst
3
+ module CSV
4
+
5
+ # Example: Chain*|5|N:Rope Chain||US/0,Box Chain||US/5,Snake Chain||US/5;Length*|5|N:16 inch||US/0,18 inch||US/0,20 inch||US/0,24 inch||US/0
6
+ #
7
+ # Name1REQ|display_as|keep_stock:Option1.name|Option1.image|Option1.price,Option2.name|...;Name2...
8
+ class ProductAttributesTransformer < Transformer
9
+
10
+ def transform
11
+ if input.kind_of?(String)
12
+ input
13
+ elsif input
14
+ attributes.map {|attribute|
15
+ name = BusinessCatalyst.sanitize_catalog_name(attribute.name)
16
+ required = attribute.required ? '*' : ''
17
+ display_as = attribute.display_as.to_i
18
+ keep_stock = BooleanTransformer.transform(attribute.keep_stock)
19
+
20
+ text = ["#{name}#{required}", display_as, keep_stock].join("|")
21
+ text << ":"
22
+
23
+ text << attribute.options.map {|option|
24
+ name = BusinessCatalyst.sanitize_catalog_name(option.name.to_s)
25
+ image = option.image.to_s.strip
26
+ price = CurrencyTransformer.transform(option.price)
27
+ [name, image, price].join("|")
28
+ }.join(",")
29
+
30
+ text
31
+ }.join(";")
32
+ end
33
+ end
34
+
35
+ def attributes
36
+ if input.kind_of?(Array)
37
+ input.compact
38
+ else
39
+ [input].compact
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ module BusinessCatalyst
3
+ module CSV
4
+
5
+ class ProductCodeTransformer < Transformer
6
+
7
+ def initialize(input)
8
+ if input.blank?
9
+ raise InvalidInputError, "product_code must not be blank"
10
+ end
11
+ super(input)
12
+ end
13
+
14
+ def transform
15
+ input.to_s
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+ module BusinessCatalyst
3
+ module CSV
4
+
5
+ class SEOFriendlyUrlTransformer < Transformer
6
+
7
+ def initialize(input)
8
+ input = input.to_s
9
+ raise InvalidInputError, "seo_friendly_url must not be blank" if input.nil? || input.strip == ""
10
+ raise InvalidInputError, "seo_friendly_url '#{input}' is not globally unique" unless self.class.is_globally_unique?(input)
11
+ self.class.register_url(input)
12
+ super(input)
13
+ end
14
+
15
+ def transform
16
+ input
17
+ end
18
+
19
+ def self.reset_global_urls!
20
+ @global_urls = {}
21
+ end
22
+
23
+ def self.global_urls
24
+ @global_urls ||= {}
25
+ end
26
+
27
+ def self.register_url(url)
28
+ global_urls[url] = true
29
+ end
30
+
31
+ def self.is_globally_unique?(url)
32
+ !global_urls.fetch(url, false)
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ module BusinessCatalyst
3
+ module CSV
4
+
5
+ class Transformer
6
+ attr_accessor :input
7
+
8
+ def initialize(input)
9
+ @input = input
10
+ end
11
+
12
+ def transform
13
+ raise NotImplementedError, "Transformer subclasses must implement #transform"
14
+ end
15
+
16
+ def self.transform(input)
17
+ self.new(input).transform
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ module BusinessCatalyst
4
+ module CSV
5
+ class URIArrayTransformer < Transformer
6
+
7
+ def transform
8
+ if input
9
+ Array(input).map {|s| URI.encode(s.to_s).gsub(";", " ") }.join(";")
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+ module BusinessCatalyst
3
+ module CSV
4
+
5
+ class URITransformer < Transformer
6
+
7
+ def transform
8
+ URI.encode(input) if input
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+ require 'business_catalyst/csv/transformers/invalid_input_error'
3
+ require 'business_catalyst/csv/transformers/transformer'
4
+ require 'business_catalyst/csv/transformers/catalog_transformer'
5
+ require 'business_catalyst/csv/transformers/currency_transformer'
6
+ require 'business_catalyst/csv/transformers/product_attributes_transformer'
7
+ require 'business_catalyst/csv/transformers/product_code_transformer'
8
+ require 'business_catalyst/csv/transformers/seo_friendly_url_transformer'
9
+ require 'business_catalyst/csv/transformers/uri_array_transformer'
10
+ require 'business_catalyst/csv/transformers/uri_transformer'
11
+
12
+ module BusinessCatalyst
13
+ module CSV
14
+
15
+ # Just calls to_s on input
16
+ class GenericTransformer < Transformer
17
+ def transform
18
+ input.to_s if input
19
+ end
20
+ end
21
+
22
+
23
+ class ArrayTransformer < Transformer
24
+ def transform
25
+ if input
26
+ Array(input).map {|s| s.to_s.gsub(";", " ") }.join(";")
27
+ end
28
+ end
29
+ end
30
+
31
+
32
+ class BooleanTransformer < Transformer
33
+ def transform
34
+ input ? "Y" : "N"
35
+ end
36
+ end
37
+
38
+ class TemplateIDTransformer < Transformer
39
+ def transform
40
+ if input.kind_of?(Symbol)
41
+ case input
42
+ when :default
43
+ 0
44
+ when :none
45
+ -1
46
+ when :parent
47
+ -2
48
+ else
49
+ raise InvalidInputError, "#{input} is not a valid template ID"
50
+ end
51
+ else
52
+ input
53
+ end
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module BusinessCatalyst
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ require "business_catalyst/version"
3
+ require "business_catalyst/csv/product_attribute"
4
+ require "business_catalyst/csv/transformers"
5
+ require "business_catalyst/csv/row"
6
+ require "business_catalyst/csv/product_row"
7
+ require "business_catalyst/csv/catalog_row"
8
+ require "business_catalyst/csv/file_splitter"
9
+
10
+ module BusinessCatalyst
11
+
12
+ # The following characters cause an error message if they appear in a catalog name.
13
+ CATALOG_CHAR_BLACKLIST = /[\\\/;#:"\|_@=\?]/.freeze
14
+ MORE_STRICT_BLACKLIST = /[\\\/;&,#:"\|\._@=\?]/.freeze
15
+
16
+ # Strip all characters out of a catalog name that will cause an error. Replaces them with
17
+ # " " and then squishes all whitespace to single space to preserve word structure.
18
+ def self.sanitize_catalog_name(name)
19
+ return name if name.nil?
20
+ sanitized = name.strip
21
+ sanitized.gsub!(CATALOG_CHAR_BLACKLIST, " ")
22
+ sanitized.gsub!(/\s+/, " ")
23
+ sanitized
24
+ end
25
+
26
+ # A guess as to how business catalyst converts names to URL's, based on this blog entry:
27
+ # http://www.businesscatalyst.com/bc-blog/seo-friendly-urls-for-products-and-catalogs
28
+ #
29
+ # Downcases, converts invalid characters and whitespace to '-', and finally removes multiple
30
+ # consecutive dashes and leading and trailing dashes. Does NOT append
31
+ # numbers to ensure uniqueness, you must do this yourself after conversion.
32
+ def self.seo_friendly_url(name)
33
+ name.strip.downcase.gsub(/[^a-z0-9\-]/, "-").gsub(/-{2,}/, "-").gsub(/\A-+|-+\Z/, "")
34
+ end
35
+
36
+ def self.reset_global_urls!
37
+ BusinessCatalyst::CSV::SEOFriendlyUrlTransformer.reset_global_urls!
38
+ end
39
+
40
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe BusinessCatalyst do
5
+ subject { BusinessCatalyst }
6
+
7
+ describe "#seo_friendly_url" do
8
+ it "replaces bad SEO chars with '-'" do
9
+ subject.seo_friendly_url("a&b").should eq("a-b")
10
+ end
11
+ it "replaces multiple consecutive bad SEO chars with single '-'" do
12
+ subject.seo_friendly_url("a/;&,#:\"|._@=?()b").should eq("a-b")
13
+ end
14
+ it "replaces whitespace with '-'" do
15
+ subject.seo_friendly_url("a b").should eq("a-b")
16
+ end
17
+ it "removes leading and trailing whitespace" do
18
+ subject.seo_friendly_url(" a b \n").should eq("a-b")
19
+ end
20
+ it "removes leading and trailing '-'" do
21
+ subject.seo_friendly_url("-a-b-").should eq("a-b")
22
+ end
23
+ it "squishes multiple consecutive '-'" do
24
+ subject.seo_friendly_url("a--b").should eq("a-b")
25
+ end
26
+ it "downcases input" do
27
+ subject.seo_friendly_url("A B").should eq("a-b")
28
+ end
29
+ it "strips random unicode characters" do
30
+ subject.seo_friendly_url("test”®").should eq("test")
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe BusinessCatalyst::CSV::CatalogRow do
4
+ subject { BusinessCatalyst::CSV::CatalogRow.new }
5
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+
4
+ module BusinessCatalyst
5
+ module CSV
6
+ describe FileSplitter do
7
+ subject {
8
+ FileSplitter.new("test", :max_rows_per_file => 10, :header_row => ["Column 1"], :verbose => false, :logger => nil)
9
+ }
10
+
11
+ after(:each) do
12
+ # cleanup any files generated
13
+
14
+ begin
15
+ subject.close
16
+ rescue IOError
17
+ # if it's already closed, awesome!
18
+ end
19
+
20
+ Dir.glob("test_*").each do |test_file|
21
+ FileUtils.rm(test_file)
22
+ end
23
+ end
24
+
25
+ def get_csv_writer_path(writer)
26
+ if RUBY_VERSION =~ /\A1\.8/
27
+ writer.send(:instance_variable_get, :@dev).path
28
+ else
29
+ writer.path
30
+ end
31
+ end
32
+
33
+ describe "#start" do
34
+ it "yields self" do
35
+ expect {|b| subject.start(&b) }.to yield_control.once
36
+ end
37
+ it "opens a new file with the new row range appended" do
38
+ subject.start do |splitter|
39
+ expect(get_csv_writer_path(splitter.current_file)).to eq("test_1-10.csv")
40
+ end
41
+ end
42
+ end
43
+
44
+ describe "#start_row" do
45
+ it "increments #current_row" do
46
+ expect { subject.start_row }.to change(subject, :current_row).by(1)
47
+ end
48
+ it "increments #total_rows" do
49
+ expect { subject.start_row }.to change(subject, :total_rows).by(1)
50
+ end
51
+ context "when current_row becomes greater than max_rows_per_file" do
52
+ before do
53
+ subject.max_rows_per_file = 10
54
+ subject.send(:instance_variable_set, :@current_row, 10)
55
+ subject.send(:instance_variable_set, :@total_rows, 10)
56
+ end
57
+ it "resets current_row to 1" do
58
+ expect { subject.start_row }.to change(subject, :current_row).to(1)
59
+ end
60
+ it "closes #current_file" do
61
+ file = double("File", :close => nil)
62
+ subject.send(:instance_variable_set, :@current_file, file)
63
+ subject.start_row
64
+ expect(file).to have_received(:close)
65
+ end
66
+ it "opens a new file with the new row range appended" do
67
+ subject.start_row
68
+ expect(get_csv_writer_path(subject.current_file)).to eq("test_11-20.csv")
69
+ end
70
+ it "appends the new file name to #all_files" do
71
+ subject.start_row
72
+ expect(subject.all_files.last).to eq("test_11-20.csv")
73
+ end
74
+ it "prepends the header row to the new file" do
75
+ subject.header_row = ["Column 1", "Column 2"]
76
+ subject.start_row
77
+ file = get_csv_writer_path(subject.current_file)
78
+ subject.close
79
+ line = File.open(file, 'r') do |f|
80
+ f.readline
81
+ end
82
+ expect(line).to eq("Column 1,Column 2\n")
83
+ end
84
+ it "calls a block provided to #on_file_change" do
85
+ it_works = false
86
+ subject.on_file_change do
87
+ it_works = true
88
+ end
89
+ subject.start_row
90
+ expect(it_works).to eq(true)
91
+ end
92
+ it "calls a block provided to #on_file_change with itself, if block arity is 1 or more" do
93
+ subject.on_file_change do |s|
94
+ expect(s).to eq(subject)
95
+ end
96
+ subject.start_row
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ module BusinessCatalyst
5
+ module CSV
6
+ describe ProductAttribute do
7
+
8
+ describe "#new" do
9
+ it "raises ArgumentError when display_as is invalid symbol" do
10
+ expect {
11
+ ProductAttribute.new("Test", :display_as => :invalid, :required => false, :keep_stock => false)
12
+ }.to raise_error(ArgumentError)
13
+ end
14
+ it "raises ArgumentError when display_as is invalid integer" do
15
+ expect {
16
+ ProductAttribute.new("Test", :display_as => -1)
17
+ }.to raise_error(ArgumentError)
18
+ end
19
+ end
20
+
21
+ describe "#add_options" do
22
+ context "when given an Array of Arrays" do
23
+ it "calls add_option with the splat of each array item" do
24
+ attribute = ProductAttribute.new("Test", :display_as => :dropdown, :required => false, :keep_stock => false)
25
+ attribute.add_options([["Opt", "img", 1]])
26
+ expect(attribute.options).to eq([ProductAttribute::Option.new("Opt", "img", 1)])
27
+ end
28
+ it "coerces arguments to array" do
29
+ attribute = ProductAttribute.new("Test", :display_as => :dropdown, :required => false, :keep_stock => false)
30
+ attribute.add_options(["Opt1", "img", 1], ["Opt2", "img", 1])
31
+ expect(attribute.options).to eq([
32
+ ProductAttribute::Option.new("Opt1", "img", 1),
33
+ ProductAttribute::Option.new("Opt2", "img", 1)
34
+ ])
35
+ end
36
+ end
37
+ context "when given an Array of Hashes" do
38
+ it "handles hashes correctly" do
39
+ attribute = ProductAttribute.new("Test", :display_as => :dropdown, :required => false, :keep_stock => false)
40
+ attribute.add_options([{:name => "Opt1", :image => "img", :price => 1}])
41
+ expect(attribute.options).to eq([ProductAttribute::Option.new("Opt1", "img", 1)])
42
+ end
43
+ end
44
+ context "when given an Array of ProductAttribute::Option instances" do
45
+ it "adds just appends the object(s) to #options" do
46
+ attribute = ProductAttribute.new("Test", :display_as => :dropdown, :required => false, :keep_stock => false)
47
+ attribute.add_options([ProductAttribute::Option.new("Opt1", "img", 1)])
48
+ expect(attribute.options).to eq([ProductAttribute::Option.new("Opt1", "img", 1)])
49
+ end
50
+ end
51
+ context "when given several strings" do
52
+ it "it turns each into an option with a name only" do
53
+ attribute = ProductAttribute.new("Test", :display_as => :dropdown, :required => false, :keep_stock => false)
54
+ attribute.add_options("Opt1", "Opt2")
55
+ expect(attribute.options).to eq([
56
+ ProductAttribute::Option.new("Opt1", nil, nil),
57
+ ProductAttribute::Option.new("Opt2", nil, nil)
58
+ ])
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe BusinessCatalyst::CSV::ProductRow do
4
+ subject { ProductRow.new }
5
+ end