goods 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a0011750815867641124fd10b42a9c29157cf738
4
- data.tar.gz: cf3a8fd8a6212bd050b38d188b62c32d0804cf43
3
+ metadata.gz: 4c0db8c61b2a8ee5ea477222bae0025d6ddf665c
4
+ data.tar.gz: 1f59093e307a69f02c11c7988824bfd90299afb3
5
5
  SHA512:
6
- metadata.gz: bd2df6ed0f8f557529d6826bee5525df56b7c2a379db0b4485630c6e33d17c759ca56ba9943cb395ac6363a1509f18c3d1b748ef61fe91dcab4a9be0ea8d16f4
7
- data.tar.gz: 668f87a2ecc661ab59c7888eb3d7d628d0cf4677f38fa942141883341bd246d1e82ec968673bd868c164d0dd3cf7155267a49f31c8ce0691a92e691c2446b78b
6
+ metadata.gz: cad7b3a73d0d6d48c4e41bed36bc5d05170fb15954fafc9a5f98d6588867950c40f4240b978f785566097983d1fe162f5fda23aec93dd403b897beb342c2e5fe
7
+ data.tar.gz: 06c5c8baabc115059afdb01fbae2f870a78d01c86d9569c6ae58afd446c4be3921ed83ce5a5c9232c269852aca8af37f6db876f30dfc7dfbd56cd5d316ec5bfa
data/.gitignore CHANGED
@@ -18,3 +18,4 @@ tmp
18
18
  .DS_Store
19
19
  *sublime*
20
20
  .idea/
21
+ .floo*
data/goods.gemspec CHANGED
@@ -24,7 +24,7 @@ DESC
24
24
 
25
25
  spec.add_development_dependency "bundler", "~> 1.3"
26
26
  spec.add_development_dependency "rake"
27
- spec.add_development_dependency "rspec", "~> 3.0.0.beta1"
27
+ spec.add_development_dependency "rspec", "~> 3.0.0.beta2"
28
28
 
29
29
  spec.add_runtime_dependency "libxml-ruby"
30
30
  spec.add_runtime_dependency "nokogiri"
data/lib/goods/util.rb ADDED
@@ -0,0 +1,120 @@
1
+ module Goods
2
+ module Util
3
+ class CategoriesGraph
4
+ attr_reader :vertex_count, :edge_count
5
+
6
+ def initialize(categories=[])
7
+ reset
8
+ add_categories(categories)
9
+ end
10
+
11
+ def adjacent_categories?(cid1, cid2)
12
+ vertexes = [
13
+ vertex_for_category(cid1),
14
+ vertex_for_category(cid2)
15
+ ]
16
+ adjacent?(*vertexes)
17
+ end
18
+
19
+ def topsorted
20
+ sorted = []
21
+ on_processed = ->(vertex_num) { sorted.unshift(vertex_num) }
22
+ dfs(nil, on_processed)
23
+ sorted.map { |v| category_for_vertex(v) }
24
+ end
25
+
26
+ private
27
+
28
+ # Make empty graph
29
+ def reset
30
+ @vertex_count = 0
31
+ @edge_count = 0
32
+ @adjacency_list = []
33
+ @vertex_to_category = []
34
+ @category_to_vertex = {}
35
+ end
36
+
37
+ def add_categories(categories)
38
+ categories.each { |c| init_category(c) }
39
+ categories.each { |c| link_category(c) }
40
+ end
41
+
42
+ def init_category(category)
43
+ return if vertex_for_category(category[:id])
44
+
45
+ add_vertex do |vertex_num|
46
+ set_mapping vertex_num, category
47
+ end
48
+ end
49
+
50
+ def link_category(category)
51
+ if category[:parent_id]
52
+ edge = [
53
+ vertex_for_category(category[:parent_id]),
54
+ vertex_for_category(category[:id])
55
+ ]
56
+
57
+ add_edge(*edge)
58
+ end
59
+ end
60
+
61
+ def add_vertex
62
+ @adjacency_list.push([])
63
+ yield @vertex_count if block_given?
64
+ @vertex_count += 1
65
+ end
66
+
67
+ def add_edge(v_start, v_end)
68
+ if v_start && v_end
69
+ @adjacency_list[v_start].push(v_end)
70
+ @edge_count += 1
71
+ end
72
+ end
73
+
74
+ def adjacent?(v1, v2)
75
+ @adjacency_list[v1].include?(v2)
76
+ end
77
+
78
+ def adjacent_for(vertex)
79
+ @adjacency_list[vertex]
80
+ end
81
+
82
+ def vertex_for_category(category_id)
83
+ @category_to_vertex[category_id]
84
+ end
85
+
86
+ def category_for_vertex(vertex_num)
87
+ @vertex_to_category[vertex_num]
88
+ end
89
+
90
+ def set_mapping(vertex_num, category)
91
+ @vertex_to_category[vertex_num] = category
92
+ @category_to_vertex[category[:id]] = vertex_num
93
+ end
94
+
95
+ def dfs(on_discovered, on_processed)
96
+ states = vertex_count.times.map { :undiscovered }
97
+
98
+ vertex_count.times.each do |vertex|
99
+ if states[vertex] == :undiscovered
100
+ dfs_internal(vertex, states, on_discovered, on_processed)
101
+ end
102
+ end
103
+ end
104
+
105
+ def dfs_internal(vertex, states, on_discovered, on_processed)
106
+ states[vertex] = :discovered
107
+ on_discovered.call(vertex) if on_discovered
108
+
109
+ adjacent_for(vertex).each do |adjacent|
110
+ if states[adjacent] == :undiscovered
111
+ dfs_internal(adjacent, states, on_discovered, on_processed)
112
+ end
113
+ end
114
+
115
+ states[vertex] = :processed
116
+ on_processed.call(vertex) if on_processed
117
+ end
118
+ end
119
+ end
120
+ end
data/lib/goods/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Goods
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
data/lib/goods/xml.rb CHANGED
@@ -9,7 +9,9 @@ module Goods
9
9
  end
10
10
 
11
11
  def categories
12
- @categories ||= extract_categories
12
+ @categories ||= begin
13
+ Util::CategoriesGraph.new(extract_categories).topsorted
14
+ end
13
15
  end
14
16
 
15
17
  def currencies
data/lib/goods.rb CHANGED
@@ -2,6 +2,7 @@ require 'open-uri'
2
2
  require "goods/version"
3
3
  require "goods/xml/validator"
4
4
  require "goods/xml"
5
+ require "goods/util"
5
6
  require "goods/element"
6
7
  require "goods/container"
7
8
  require "goods/category"
@@ -0,0 +1,114 @@
1
+ require "spec_helper"
2
+
3
+ describe Goods::Util::CategoriesGraph do
4
+ let(:klass) { Goods::Util::CategoriesGraph }
5
+
6
+ describe "#initialize" do
7
+ it "should create empty graph if nothing passed" do
8
+ graph = klass.new
9
+ expect(graph.vertex_count).to eql(0)
10
+ expect(graph.edge_count).to eql(0)
11
+ end
12
+
13
+ it "should create graph with vertice for each category" do
14
+ graph = klass.new([
15
+ { id: 1, name: "First" },
16
+ { id: 2, name: "Second" }
17
+ ])
18
+ expect(graph.vertex_count).to eql(2)
19
+ expect(graph.edge_count).to eql(0)
20
+ end
21
+
22
+ it "should create edge between parent and child category" do
23
+ normal_categories = [
24
+ { id: 1, name: "First" },
25
+ { id: 2, name: "Second", parent_id: 1 }
26
+ ]
27
+ reversed_categories = normal_categories.reverse
28
+
29
+ [normal_categories, reversed_categories].each do |categories|
30
+ graph = klass.new(categories)
31
+ expect(graph.vertex_count).to eql(2)
32
+ expect(graph.edge_count).to eql(1)
33
+ expect(graph.adjacent_categories?(1, 2)).to eql(true)
34
+ end
35
+ end
36
+
37
+ it "should not duplicate categories" do
38
+ graph = klass.new([
39
+ { id: 1, name: "Name" },
40
+ { id: 1, name: "Name" }
41
+ ])
42
+
43
+ expect(graph.vertex_count).to eql(1)
44
+ end
45
+ end
46
+
47
+ describe "#topsorted" do
48
+
49
+ context "with single strongly connected component" do
50
+ let(:simple_description) {
51
+ [
52
+ { id: 1, name: "First" },
53
+ { id: 2, name: "Second", parent_id: 1 }
54
+ ]
55
+ }
56
+
57
+ it "if already topsorted should return the same" do
58
+ graph = klass.new(simple_description)
59
+ topsorted = graph.topsorted
60
+ expect(topsorted).to eql(simple_description)
61
+ end
62
+
63
+ it "if not topsorted should topsort" do
64
+ graph = klass.new(simple_description.reverse)
65
+ topsorted = graph.topsorted
66
+ expect(topsorted).to eql(simple_description)
67
+ end
68
+ end
69
+
70
+ context "with multiple strongly connected component" do
71
+ let(:complex_description) {
72
+ [
73
+ {id: 1, name: 'C0L0N0', level: 0, component: 1},
74
+ {id: 2, name: 'C0L1N0', parent_id: 1, level: 1, component: 1},
75
+ {id: 3, name: 'C0L1N1', parent_id: 1, level: 1, component: 1},
76
+ {id: 4, name: 'C0L1N2', parent_id: 1, level: 1, component: 1},
77
+ {id: 5, name: 'C0L2N0', parent_id: 2, level: 2, component: 1},
78
+ {id: 6, name: 'C0L3N0', parent_id: 5, level: 2, component: 1},
79
+
80
+ {id: 7, name: 'C1L0N0', level: 0, component: 2},
81
+ {id: 8, name: 'C1L1N0', parent_id: 7, level: 1, component: 2},
82
+ {id: 9, name: 'C1L1N1', parent_id: 7, level: 1, component: 2},
83
+ ]
84
+ }
85
+
86
+ it "if not topsorted should topsort" do
87
+ graph = klass.new(complex_description.reverse)
88
+ topsorted = graph.topsorted
89
+ topsorted.each_with_index do |cat, idx|
90
+ if cat[:parent_id]
91
+ expect(
92
+ topsorted[0, idx].find { |el| el[:id] == cat[:parent_id]}
93
+ ).not_to be_nil
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ context "with graph with cycles" do
100
+ let(:cyclic) {
101
+ [
102
+ {id: 1, name: "Kili", parent_id: 3},
103
+ {id: 2, name: "Fili", parent_id: 1},
104
+ {id: 3, name: "Dili", parent_id: 2}
105
+ ]
106
+ }
107
+
108
+ it "should not fail" do
109
+ graph = klass.new(cyclic)
110
+ expect(graph.topsorted).not_to be_nil
111
+ end
112
+ end
113
+ end
114
+ end
@@ -25,21 +25,32 @@ describe Goods::XML do
25
25
 
26
26
  describe "#categories" do
27
27
  let(:categories) { simple_catalog.categories }
28
+ let(:root_category) {
29
+ categories.find { |el| el[:parent_id] == nil }
30
+ }
31
+ let(:child_category) {
32
+ categories.find { |el| el[:parent_id] == root_category[:id]}
33
+ }
28
34
 
29
35
  it "should extract all categories" do
30
36
  expect(categories.count).to eq(SIMPLE_CATALOG_CATEGORIES_COUNT)
31
37
  end
32
38
 
33
- context "category format" do
34
- let(:root_category) { categories[0] }
35
- let(:child_category) { categories[1] }
39
+ it "should have root category" do
40
+ expect(root_category).not_to be_nil
41
+ end
36
42
 
43
+ it "should have child category" do
44
+ expect(child_category).not_to be_nil
45
+ end
46
+
47
+ context "category format" do
37
48
  it "should have an id" do
38
- expect(root_category[:id]).to eq("1")
49
+ expect(root_category).to have_key(:id)
39
50
  end
40
51
 
41
52
  it "should have a name" do
42
- expect(root_category[:name]).to eq("Оргтехника")
53
+ expect(root_category[:name]).not_to be_empty
43
54
  end
44
55
 
45
56
  it "should have nil parent_id for root category" do
@@ -47,7 +58,7 @@ describe Goods::XML do
47
58
  end
48
59
 
49
60
  it "should have non-nil parent_id for child_category" do
50
- expect(child_category[:parent_id]).to eq("1")
61
+ expect(child_category[:parent_id]).not_to be_nil
51
62
  end
52
63
  end
53
64
 
@@ -177,8 +188,8 @@ describe Goods::XML do
177
188
  end
178
189
  end
179
190
 
180
- describe "#generation_date" do
181
- it "should correctly get yml_catalog date" do
191
+ describe "#generation_date" do
192
+ it "should correctly get yml_catalog date" do
182
193
  expect(simple_catalog.generation_date).to eq(SIMPLE_CATALOG_GENERATION_TIME)
183
194
  end
184
195
  end
data/spec/goods_spec.rb CHANGED
@@ -11,8 +11,13 @@ describe Goods do
11
11
 
12
12
  it "should return catalog if valid xml io is passed" do
13
13
  expect(Goods::Catalog).to receive(:new).
14
- with({io: valid_document, url: "url", encoding: "UTF-8"})
15
- Goods.from_io(valid_document, "url", "UTF-8")
14
+ with({io: valid_document, url: "url", encoding: "UTF-8"}).and_call_original
15
+
16
+ catalog = Goods.from_io(valid_document, "url", "UTF-8")
17
+
18
+ expect(catalog.categories.size).to be > 0
19
+ expect(catalog.currencies.size).to be > 0
20
+ expect(catalog.offers.size).to be > 0
16
21
  end
17
22
 
18
23
  it "should raise error if invalid xml io is passed" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goods
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Artem Pyanykh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-27 00:00:00.000000000 Z
11
+ date: 2014-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - ~>
46
46
  - !ruby/object:Gem::Version
47
- version: 3.0.0.beta1
47
+ version: 3.0.0.beta2
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: 3.0.0.beta1
54
+ version: 3.0.0.beta2
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: libxml-ruby
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +108,7 @@ files:
108
108
  - lib/goods/element.rb
109
109
  - lib/goods/offer.rb
110
110
  - lib/goods/offers_list.rb
111
+ - lib/goods/util.rb
111
112
  - lib/goods/version.rb
112
113
  - lib/goods/xml.rb
113
114
  - lib/goods/xml/validator.rb
@@ -121,6 +122,7 @@ files:
121
122
  - spec/goods/currency_spec.rb
122
123
  - spec/goods/offer_spec.rb
123
124
  - spec/goods/offers_list_spec.rb
125
+ - spec/goods/util_spec.rb
124
126
  - spec/goods/xml/validator_spec.rb
125
127
  - spec/goods/xml_spec.rb
126
128
  - spec/goods_spec.rb
@@ -161,6 +163,7 @@ test_files:
161
163
  - spec/goods/currency_spec.rb
162
164
  - spec/goods/offer_spec.rb
163
165
  - spec/goods/offers_list_spec.rb
166
+ - spec/goods/util_spec.rb
164
167
  - spec/goods/xml/validator_spec.rb
165
168
  - spec/goods/xml_spec.rb
166
169
  - spec/goods_spec.rb