goods 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,87 @@
1
+ require "spec_helper"
2
+
3
+ describe Goods::Offer do
4
+ let(:books) { Goods::Category.new(id: "1", name: "Books") }
5
+ let(:rur) { Goods::Currency.new(id: "RUR", rate: 1, plus: 0) }
6
+ let(:valid_description) do
7
+ {id: "1", name: "The Lord of The Rings", category_id: "1", currency_id: "RUR", price: 10}
8
+ end
9
+ let(:valid_offer) do
10
+ offer = Goods::Offer.new(valid_description)
11
+ offer.category = books
12
+ offer.currency = rur
13
+ offer
14
+ end
15
+
16
+ it_should_behave_like "containable" do
17
+ let(:element) { valid_offer }
18
+ end
19
+
20
+ describe "#valid?" do
21
+ context "invalid cases" do
22
+ let(:invalid_offer) { Goods::Offer.new({}) }
23
+
24
+ [:id, :category_id, :currency_id, :price].each do |field|
25
+ it "should reject offers without #{field}" do
26
+ expect(invalid_offer).not_to be_valid
27
+ expect(invalid_offer.invalid_fields).to include(field)
28
+ end
29
+ end
30
+
31
+ it "should reject offers with non-positive price" do
32
+ invalid_offer = Goods::Offer.new(price: -5.0)
33
+ expect(invalid_offer).not_to be_valid
34
+ expect(invalid_offer.invalid_fields).to include(:price)
35
+ end
36
+ end
37
+
38
+ context "valid cases" do
39
+ it "should accept offer when it has id, category, currency and price" do
40
+ expect(valid_offer).to be_valid
41
+ end
42
+ end
43
+ end
44
+
45
+ [:available, :description, :model, :name, :picture, :vendor].each do |field|
46
+ it "should have #{field}" do
47
+ expect(valid_offer).to respond_to(field)
48
+ end
49
+ end
50
+
51
+ it "should have floting point price" do
52
+ expect(Goods::Offer.new(price: 5).price).to be_kind_of(Float)
53
+ end
54
+
55
+ describe "#convert_currency" do
56
+ let(:usd) { Goods::Currency.new(id: "USD", rate: 30, plus: 0) }
57
+
58
+ it "should change currency" do
59
+ valid_offer.convert_currency(usd)
60
+ expect(valid_offer.currency).to be(usd)
61
+ end
62
+
63
+ it "should leave offer valid" do
64
+ valid_offer.convert_currency(usd)
65
+ expect(valid_offer).to be_valid
66
+ end
67
+
68
+ it "should change price according to rate" do
69
+ valid_offer.convert_currency(usd)
70
+ expect(valid_offer.price).to eql(1.0/3)
71
+ end
72
+ end
73
+
74
+ describe "#change_category" do
75
+ let(:printers) { Goods::Category.new(id: "print", name: "Printers") }
76
+
77
+ it "should change category to specified one" do
78
+ valid_offer.change_category(printers)
79
+ expect(valid_offer.category).to be(printers)
80
+ end
81
+
82
+ it "should leave offer valid" do
83
+ valid_offer.change_category(printers)
84
+ expect(valid_offer).to be_valid
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Goods::OffersList do
4
+ let(:categories) { Goods::CategoriesList.new([{id: "1", name: "Category"}]) }
5
+ let(:currencies) { Goods::CurrenciesList.new([{id: "RUR", rate: 1, plus: 0}]) }
6
+ let(:offers) { Goods::OffersList.new(categories, currencies) }
7
+ let(:subject) { offers }
8
+
9
+ it_should_behave_like "a container",
10
+ Goods::Offer,
11
+ Goods::Offer.new(id: "1", url: "url.com", category_id: "1", currency_id: "RUR", price: 10) do
12
+ let(:subject) { offers }
13
+ end
14
+
15
+ describe "#add" do
16
+ it "should setup category and currency for offer" do
17
+ offer = Goods::Offer.new(id: "1", url: "url.com", category_id: "1", currency_id: "RUR", price: 10)
18
+ subject.add(offer)
19
+
20
+ expect(offer.category).to be(categories.find("1"))
21
+ expect(offer.currency).to be(currencies.find("RUR"))
22
+ end
23
+ end
24
+
25
+ describe "#prune_categories" do
26
+ let(:categories) do
27
+ list = Goods::CategoriesList.new [
28
+ {id: "1", name: "root"},
29
+ {id: "11", name: "root", parent_id: "1"},
30
+ {id: "12", name: "root", parent_id: "11"}
31
+ ]
32
+ list
33
+ end
34
+ let(:offers) do
35
+ list = Goods::OffersList.new categories, currencies, [
36
+ {id: "1", category_id: "1", currency_id: "RUR", price: 1},
37
+ {id: "2", category_id: "11", currency_id: "RUR", price: 1},
38
+ {id: "3", category_id: "12", currency_id: "RUR", price: 1}
39
+ ]
40
+ list
41
+ end
42
+
43
+ it "should raise error if level < 0" do
44
+ expect{ offers.prune_categories }.to raise_error(ArgumentError)
45
+ end
46
+
47
+ it "should replace deep categories with their parents on specified level" do
48
+ offers.prune_categories(1)
49
+ expect(offers.find("3").category).to be(categories.find("11"))
50
+ end
51
+
52
+ it "should not affect offers with categories having lower level" do
53
+ offers.prune_categories(1)
54
+ expect(offers.find("1").category).to be(categories.find("1"))
55
+ end
56
+ end
57
+
58
+ describe "#convert_currency" do
59
+ let(:currencies) do
60
+ Goods::CurrenciesList.new([
61
+ {id: "RUR", rate: 1, plus: 0},
62
+ {id: "USD", rate: 30, plus: 0},
63
+ {id: "GBP", rate: 50, plus: 0}
64
+ ])
65
+ end
66
+ let(:offers) do
67
+ list = Goods::OffersList.new categories, currencies, [
68
+ {id: "1", category_id: "1", currency_id: "USD", price: 1},
69
+ {id: "2", category_id: "1", currency_id: "GBP", price: 1}
70
+ ]
71
+ list
72
+ end
73
+
74
+ it "should convert currency for all offers" do
75
+ expect(offers.find("1")).to receive(:convert_currency)
76
+ expect(offers.find("2")).to receive(:convert_currency)
77
+ offers.convert_currency(currencies.find("RUR"))
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,25 @@
1
+ require "spec_helper"
2
+
3
+ describe Goods::XML::Validator do
4
+ let(:valid_document) { File.read(File.expand_path("../../../fixtures/simple_catalog.xml", __FILE__)) }
5
+ let(:invalid_document) { File.read(File.expand_path("../../../fixtures/empty_catalog.xml", __FILE__)) }
6
+
7
+ describe "#valid?" do
8
+ it "should return true if document is valid according to dtd" do
9
+ validator = Goods::XML::Validator.new
10
+ expect(validator.valid? valid_document).to eql(true)
11
+ expect(validator.error).to be_nil
12
+ end
13
+
14
+ it "should return false if document is not valid" do
15
+ validator = Goods::XML::Validator.new
16
+ expect(validator.valid? invalid_document).to eql(false)
17
+ end
18
+
19
+ it "should have non-empty errors if document is not valid" do
20
+ validator = Goods::XML::Validator.new
21
+ validator.valid? invalid_document
22
+ expect(validator.error).not_to be_nil
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,178 @@
1
+ require 'spec_helper'
2
+
3
+ describe Goods::XML do
4
+ let(:simple_catalog_data) do
5
+ File.read(File.expand_path('../../fixtures/simple_catalog.xml', __FILE__))
6
+ end
7
+ let(:simple_catalog) { Goods::XML.new(simple_catalog_data) }
8
+ SIMPLE_CATALOG_CATEGORIES_COUNT = 9
9
+ SIMPLE_CATALOG_CURRENCIES_COUNT = 3
10
+ SIMPLE_CATALOG_OFFERS_COUNT = 2
11
+
12
+ describe "#initialize" do
13
+ it 'should use Nokogiri for parsing' do
14
+ params = ["string", "url", "encoding"]
15
+ expect(Nokogiri::XML::Document).to receive(:parse).with(*params)
16
+
17
+ Goods::XML.new(*params)
18
+ end
19
+
20
+ it 'should parse valid document' do
21
+ expect(simple_catalog.instance_variable_get("@xml_source")).to be_kind_of(Nokogiri::XML::Document)
22
+ end
23
+ end
24
+
25
+ describe "#categories" do
26
+ let(:categories) { simple_catalog.categories }
27
+
28
+ it "should extract all categories" do
29
+ expect(categories.count).to eq(SIMPLE_CATALOG_CATEGORIES_COUNT)
30
+ end
31
+
32
+ context "category format" do
33
+ let(:root_category) { categories[0] }
34
+ let(:child_category) { categories[1] }
35
+
36
+ it "should have an id" do
37
+ expect(root_category[:id]).to eq("1")
38
+ end
39
+
40
+ it "should have a name" do
41
+ expect(root_category[:name]).to eq("Оргтехника")
42
+ end
43
+
44
+ it "should have nil parent_id for root category" do
45
+ expect(root_category[:parent_id]).to be_nil
46
+ end
47
+
48
+ it "should have non-nil parent_id for child_category" do
49
+ expect(child_category[:parent_id]).to eq("1")
50
+ end
51
+ end
52
+
53
+ it 'should call #extract_categories only at the first invocation' do
54
+ expect_any_instance_of(Goods::XML).to receive(:extract_categories).once.and_call_original
55
+ 2.times { simple_catalog.categories }
56
+ end
57
+ end
58
+
59
+ describe "#currencies" do
60
+ it "should extract all currencies" do
61
+ expect(simple_catalog.currencies.count).to eq(SIMPLE_CATALOG_CURRENCIES_COUNT)
62
+ end
63
+
64
+ context "currency format" do
65
+ let(:rur) { simple_catalog.currencies[0] }
66
+ let(:usd) { simple_catalog.currencies[1] }
67
+ let(:kzt) { simple_catalog.currencies[2] }
68
+
69
+ it "should have an id" do
70
+ expect(rur[:id]).to eq("RUR")
71
+ end
72
+
73
+ it "should have a rate" do
74
+ expect(rur[:rate]).to eq("1")
75
+ end
76
+
77
+ it "should have a custom rate" do
78
+ expect(usd[:rate]).to eq("30")
79
+ end
80
+
81
+ it "should have a default rate" do
82
+ expect(kzt[:rate]).to eq("1")
83
+ end
84
+
85
+ it "should have a plus" do
86
+ expect(rur[:plus]).to eq("0")
87
+ end
88
+
89
+ it "should have a default plus" do
90
+ expect(kzt[:plus]).to eq("0")
91
+ end
92
+ end
93
+
94
+ it 'should call #extract_currencies only at the first invocation' do
95
+ expect_any_instance_of(Goods::XML).to receive(:extract_currencies).once.and_call_original
96
+ 2.times { simple_catalog.currencies }
97
+ end
98
+ end
99
+
100
+ describe "#offers" do
101
+ it 'should extract all offers' do
102
+ expect(simple_catalog.offers.count).to eq(SIMPLE_CATALOG_OFFERS_COUNT)
103
+ end
104
+
105
+ context "offer format" do
106
+ let(:printer) { simple_catalog.offers[0] }
107
+ let(:book) { simple_catalog.offers[1] }
108
+
109
+ it "should have an id" do
110
+ expect(printer[:id]).to eq("123")
111
+ end
112
+
113
+ it "should have availability status" do
114
+ expect(book[:available]).to eq(false)
115
+ end
116
+
117
+ it "should have default availability status" do
118
+ expect(printer[:available]).to eq(true)
119
+ end
120
+
121
+ it "should have a URL which is not empty" do
122
+ expect(printer[:url]).to eq("http://magazin.ru/product_page.asp?pid=14344")
123
+ end
124
+
125
+ it "should have a nil URL if offer doesn't have one" do
126
+ expect(book[:url]).to be_nil
127
+ end
128
+
129
+ it "should have a price" do
130
+ expect(printer[:price]).to eq(15000.00)
131
+ end
132
+
133
+ it "should have a currency_id" do
134
+ expect(printer[:currency_id]).to eq("RUR")
135
+ end
136
+
137
+ it "should use first category_id" do
138
+ expect(printer[:category_id]).to eq("100")
139
+ end
140
+
141
+ it "should use first picture if there at least one" do
142
+ expect(printer[:picture]).to eq("http://magazin.ru/img/device1.jpg")
143
+ end
144
+
145
+ it "should have a nil picture if offer doesn't have one" do
146
+ expect(book[:picture]).to be_nil
147
+ end
148
+
149
+ it "should have a description" do
150
+ expect(printer[:description]).to eq("A4, 64Mb, 600x600 dpi, USB 2.0, 29стр/мин ч/б / 15стр/мин цв, лотки на 100л и 250л, плотность до 175г/м, до 60000 стр/месяц")
151
+ end
152
+
153
+ it "should have a non-empty name" do
154
+ expect(book[:name]).to eq("Все не так. В 2 томах. Том 1")
155
+ end
156
+
157
+ it "should have a nil name if offer doesn't have one" do
158
+ expect(printer[:name]).to be_nil
159
+ end
160
+
161
+ it "should have a non-empty vendor" do
162
+ expect(printer[:vendor]).to eq("НP")
163
+ end
164
+
165
+ it "should have a nil vendor if offer doesn't have one" do
166
+ expect(book[:vendor]).to be_nil
167
+ end
168
+
169
+ it "should have a non-empty model" do
170
+ expect(printer[:model]).to eq("Color LaserJet 3000")
171
+ end
172
+
173
+ it "should have a nil model if offer doesn't have one" do
174
+ expect(book[:model]).to be_nil
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe Goods do
4
+ it 'should have a version number' do
5
+ expect(Goods::VERSION).to_not be_nil
6
+ end
7
+
8
+ describe ".from_string" do
9
+ let(:valid_document) { File.read(File.expand_path("../fixtures/simple_catalog.xml", __FILE__)) }
10
+ let(:invalid_document) { File.read(File.expand_path("../fixtures/empty_catalog.xml", __FILE__)) }
11
+
12
+ it "should return catalog if valid xml file is passed" do
13
+ expect(Goods::Catalog).to receive(:new).
14
+ with({string: valid_document, url: "url", encoding: "UTF-8"})
15
+ Goods.from_string(valid_document, "url", "UTF-8")
16
+ end
17
+
18
+ it "should raise error if invalid file is passed" do
19
+ expect { Goods.from_string(invalid_document) }.to raise_error(Goods::XML::InvalidFormatError)
20
+ end
21
+ end
22
+
23
+ describe ".from_file" do
24
+ it "should load file and call .from_string" do
25
+ params = {
26
+ file: "file", string: "string", url: nil, encoding: "UTF-8"
27
+ }
28
+ expect(Goods).to receive(:load).with(params[:file]) { params[:string] }
29
+ expect(Goods).to receive(:from_string).with(params[:string], nil, params[:encoding])
30
+ Goods.from_file params[:file], params[:encoding]
31
+ end
32
+ end
33
+
34
+ describe ".from_url" do
35
+ it "should load remote page and call .from_string" do
36
+ params = {
37
+ string: "string", url: "url", encoding: "UTF-8"
38
+ }
39
+ expect(Goods).to receive(:load).with(params[:url]) { params[:string] }
40
+ expect(Goods).to receive(:from_string).with(params[:string], params[:url], params[:encoding])
41
+ Goods.from_url params[:url], params[:encoding]
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ require 'bundler/setup'
2
+ require 'goods'
3
+
4
+ Dir[File.expand_path("../support/**/*.rb", __FILE__)].each { |f| require f }
5
+
6
+ RSpec.configure do |config|
7
+ # Limit the spec run to only specs with the focus metadata. If no specs have
8
+ # the filtering metadata and `run_all_when_everything_filtered = true` then
9
+ # all specs will run.
10
+ config.filter_run :focus
11
+
12
+ # Run all specs when none match the provided filter. This works well in
13
+ # conjunction with `config.filter_run :focus`, as it will run the entire
14
+ # suite when no specs have `:filter` metadata.
15
+ config.run_all_when_everything_filtered = true
16
+
17
+ # Run specs in random order to surface order dependencies. If you find an
18
+ # order dependency and want to debug it, you can fix the order by providing
19
+ # the seed, which is printed after each run.
20
+ # --seed 1234
21
+ #config.order = 'random'
22
+ end
@@ -0,0 +1,16 @@
1
+ shared_examples "containable" do
2
+ [:description, :id, :invalid_fields].each do |prop|
3
+ it "should give access to #{prop}" do
4
+ expect(element).to respond_to(prop)
5
+ end
6
+ end
7
+
8
+ describe "#reset_validation" do
9
+ it "should clear invalid_fields" do
10
+ element.instance_variable_set("@invalid_fields", [:a, :b])
11
+ expect(element.invalid_fields.size).to eql(2)
12
+ element.send :reset_validation
13
+ expect(element.invalid_fields).to be_empty
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,43 @@
1
+ shared_examples "a container" do |containable_class, element|
2
+ describe "#add" do
3
+ context "when hash is passed" do
4
+ it "should at first create an object" do
5
+ expect(containable_class).to receive(:new) { element }
6
+ subject.add({})
7
+ end
8
+ end
9
+
10
+ context "when object is passed" do
11
+ it "should not create new object" do
12
+ expect(containable_class).to_not receive(:new)
13
+ subject.add element
14
+ end
15
+
16
+ it "should add to list valid elements" do
17
+ expect { subject.add(element) }.to change(subject, :size).by(1)
18
+ end
19
+
20
+ it "should remember invalid elements" do
21
+ allow(element).to receive(:valid?).and_return(false)
22
+ expect { subject.add(element) }.to change(subject.defectives, :size).by(1)
23
+ end
24
+ end
25
+ end
26
+
27
+ describe "#find" do
28
+ [:one, :two].each do |el|
29
+ let(el) {
30
+ el = containable_class.new({})
31
+ allow(el).to receive(:valid?) { true }
32
+ allow(el).to receive(:id) { el.to_s.upcase }
33
+ el
34
+ }
35
+ end
36
+
37
+ it "should return element with correct id" do
38
+ subject.add(one); subject.add(two)
39
+ expect(subject.size).to eq(2)
40
+ expect(subject.find(one.id)).to eq(one)
41
+ end
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,168 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: goods
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Artem Pyanykh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-28 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.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 3.0.0.beta1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 3.0.0.beta1
55
+ - !ruby/object:Gem::Dependency
56
+ name: libxml-ruby
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: |2
84
+ The purpose of this gem is to provide simple, yet reliable solution for parsing
85
+ YML (Yandex Market Language) files, with clean and convenient interface,
86
+ and extra capabilites, such as categories prunning.
87
+ email:
88
+ - artem.pyanykh@gmail.com
89
+ executables: []
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - .gitignore
94
+ - .rspec
95
+ - Gemfile
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - goods.gemspec
100
+ - lib/goods.rb
101
+ - lib/goods/catalog.rb
102
+ - lib/goods/categories_list.rb
103
+ - lib/goods/category.rb
104
+ - lib/goods/containable.rb
105
+ - lib/goods/container.rb
106
+ - lib/goods/currencies_list.rb
107
+ - lib/goods/currency.rb
108
+ - lib/goods/offer.rb
109
+ - lib/goods/offers_list.rb
110
+ - lib/goods/version.rb
111
+ - lib/goods/xml.rb
112
+ - lib/goods/xml/validator.rb
113
+ - lib/support/shops.dtd
114
+ - spec/fixtures/empty_catalog.xml
115
+ - spec/fixtures/simple_catalog.xml
116
+ - spec/goods/catalog_spec.rb
117
+ - spec/goods/categories_list_spec.rb
118
+ - spec/goods/category_spec.rb
119
+ - spec/goods/currencies_list_spec.rb
120
+ - spec/goods/currency_spec.rb
121
+ - spec/goods/offer_spec.rb
122
+ - spec/goods/offers_list_spec.rb
123
+ - spec/goods/xml/validator_spec.rb
124
+ - spec/goods/xml_spec.rb
125
+ - spec/goods_spec.rb
126
+ - spec/spec_helper.rb
127
+ - spec/support/shared_examples_for_containable.rb
128
+ - spec/support/shared_examples_for_container.rb
129
+ homepage: https://github.com/ArtemPyanykh/goods
130
+ licenses:
131
+ - MIT
132
+ metadata: {}
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - '>='
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubyforge_project:
149
+ rubygems_version: 2.1.11
150
+ signing_key:
151
+ specification_version: 4
152
+ summary: Simple parser for YML (Yandex Market Language) files with a few twists.
153
+ test_files:
154
+ - spec/fixtures/empty_catalog.xml
155
+ - spec/fixtures/simple_catalog.xml
156
+ - spec/goods/catalog_spec.rb
157
+ - spec/goods/categories_list_spec.rb
158
+ - spec/goods/category_spec.rb
159
+ - spec/goods/currencies_list_spec.rb
160
+ - spec/goods/currency_spec.rb
161
+ - spec/goods/offer_spec.rb
162
+ - spec/goods/offers_list_spec.rb
163
+ - spec/goods/xml/validator_spec.rb
164
+ - spec/goods/xml_spec.rb
165
+ - spec/goods_spec.rb
166
+ - spec/spec_helper.rb
167
+ - spec/support/shared_examples_for_containable.rb
168
+ - spec/support/shared_examples_for_container.rb