directed-edge 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.
- data/.gitignore +3 -0
- data/LICENSE +22 -0
- data/README.rdoc +17 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/directed-edge.gemspec +49 -0
- data/examples/example_store.rb +161 -0
- data/lib/directed_edge.rb +672 -0
- data/test/helper.rb +9 -0
- data/test/test_directed_edge.rb +334 -0
- metadata +73 -0
data/test/helper.rb
ADDED
@@ -0,0 +1,334 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
# Defines a multithreaded "each"
|
4
|
+
|
5
|
+
module Enumerable
|
6
|
+
def concurrently
|
7
|
+
map {|item| Thread.new { yield item }}.each {|t| t.join }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class TestDirectedEdge < Test::Unit::TestCase
|
12
|
+
def setup
|
13
|
+
user = ENV['DIRECTEDEDGE_TEST_DB']
|
14
|
+
pass = ENV['DIRECTEDEDGE_TEST_PASS']
|
15
|
+
@database = DirectedEdge::Database.new(user, pass)
|
16
|
+
@database.import('../testdb.xml')
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_exporter
|
20
|
+
exporter = DirectedEdge::Exporter.new('exported.xml')
|
21
|
+
|
22
|
+
product = DirectedEdge::Item.new(exporter.database, 'test_product')
|
23
|
+
product.add_tag('product')
|
24
|
+
product['name'] = 'Test Product'
|
25
|
+
exporter.export(product)
|
26
|
+
|
27
|
+
first_user = DirectedEdge::Item.new(exporter.database, 'test_user_1')
|
28
|
+
second_user = DirectedEdge::Item.new(exporter.database, 'test_user_2')
|
29
|
+
first_user.add_tag('user')
|
30
|
+
first_user['name'] = 'Test User'
|
31
|
+
|
32
|
+
first_user.link_to(second_user)
|
33
|
+
first_user.link_to(product, 5)
|
34
|
+
|
35
|
+
exporter.export(first_user)
|
36
|
+
exporter.export(second_user)
|
37
|
+
|
38
|
+
exporter.finish
|
39
|
+
|
40
|
+
database = DirectedEdge::Database.new('testdb', 'test')
|
41
|
+
database.import('exported.xml')
|
42
|
+
|
43
|
+
user = DirectedEdge::Item.new(database, 'test_user_1')
|
44
|
+
product = DirectedEdge::Item.new(database, 'test_product')
|
45
|
+
|
46
|
+
assert(user.tags.include?('user'))
|
47
|
+
assert_equal('Test User', user['name'])
|
48
|
+
|
49
|
+
assert(user.links.include?('test_product'))
|
50
|
+
assert(user.links.include?('test_user_2'))
|
51
|
+
|
52
|
+
assert_equal(5, user.links['test_product'])
|
53
|
+
|
54
|
+
assert(product.tags.include?('product'))
|
55
|
+
assert_equal('Test Product', product['name'])
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_add
|
59
|
+
exporter = DirectedEdge::Exporter.new(@database)
|
60
|
+
item = DirectedEdge::Item.new(exporter.database, 'Foo')
|
61
|
+
item['name'] = 'Bar'
|
62
|
+
exporter.export(item)
|
63
|
+
exporter.finish
|
64
|
+
|
65
|
+
item = DirectedEdge::Item.new(@database, 'Foo')
|
66
|
+
assert_equal('Bar', item['name'])
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_tags
|
70
|
+
item = DirectedEdge::Item.new(@database, 'customer1')
|
71
|
+
test_tag = 'test_tag'
|
72
|
+
|
73
|
+
assert(!item.tags.include?(test_tag))
|
74
|
+
|
75
|
+
item.add_tag(test_tag);
|
76
|
+
item.save
|
77
|
+
|
78
|
+
assert(item.tags.include?(test_tag))
|
79
|
+
|
80
|
+
item.remove_tag(test_tag);
|
81
|
+
item.save
|
82
|
+
|
83
|
+
assert(!item.tags.include?(test_tag))
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_items
|
87
|
+
first_item = DirectedEdge::Item.new(@database, 'test_1')
|
88
|
+
first_item.create
|
89
|
+
|
90
|
+
second_item = DirectedEdge::Item.new(@database, 'test_2')
|
91
|
+
second_item.create([first_item])
|
92
|
+
|
93
|
+
third_item = DirectedEdge::Item.new(@database, 'test_3')
|
94
|
+
third_item.create([first_item, second_item], 'test_tag')
|
95
|
+
|
96
|
+
assert_equal('test_1', first_item.name)
|
97
|
+
|
98
|
+
# Make sure that the number of tags / links for the first item is zero
|
99
|
+
|
100
|
+
assert_equal(0, first_item.links.length)
|
101
|
+
assert_equal(0, first_item.tags.length)
|
102
|
+
|
103
|
+
# Link the first item to the second item and make sure it worked
|
104
|
+
|
105
|
+
first_item.link_to(second_item)
|
106
|
+
first_item.save
|
107
|
+
assert_equal(1, first_item.links.length)
|
108
|
+
|
109
|
+
# Make sure that the number of tags for the second item is zero and that
|
110
|
+
# there is a link to the second item
|
111
|
+
|
112
|
+
assert_equal(1, second_item.links.length)
|
113
|
+
assert_equal(0, second_item.tags.length)
|
114
|
+
|
115
|
+
# Make sure that the third item is linked to both the first and second items
|
116
|
+
|
117
|
+
assert_equal(2, third_item.links.length)
|
118
|
+
assert(third_item.links.include?(first_item))
|
119
|
+
assert(third_item.links.include?(second_item))
|
120
|
+
|
121
|
+
# Make sure that the first and second items show up in the related items for
|
122
|
+
# the third item
|
123
|
+
|
124
|
+
assert(third_item.related.include?(first_item.to_s))
|
125
|
+
assert(third_item.related.include?(second_item.to_s))
|
126
|
+
|
127
|
+
# Since linked items are excluded from recommendations, nothing should show
|
128
|
+
# up in the recommended items for the third item.
|
129
|
+
|
130
|
+
assert_equal(0, third_item.recommended.length)
|
131
|
+
assert_equal(1, second_item.recommended.length)
|
132
|
+
assert_equal(0, second_item.recommended(['unknown_tag']).length)
|
133
|
+
assert_equal([third_item.to_s], first_item.recommended(['test_tag']))
|
134
|
+
|
135
|
+
# Remove the link from the second item and assure that it was removed
|
136
|
+
|
137
|
+
second_item.unlink_from(first_item)
|
138
|
+
second_item.save
|
139
|
+
|
140
|
+
assert_equal(0, second_item.links.length)
|
141
|
+
|
142
|
+
# Remove the links from the third item and assure that they were removed
|
143
|
+
|
144
|
+
third_item.unlink_from(first_item)
|
145
|
+
third_item.unlink_from(second_item)
|
146
|
+
third_item.save
|
147
|
+
|
148
|
+
assert_equal(0, third_item.links.length)
|
149
|
+
|
150
|
+
# Now make sure that those items no longer show up as related items
|
151
|
+
|
152
|
+
assert(!third_item.related.include?(first_item.to_s))
|
153
|
+
assert(!third_item.related.include?(second_item.to_s))
|
154
|
+
|
155
|
+
# Test item removal
|
156
|
+
|
157
|
+
assert_equal(1, first_item.links.length)
|
158
|
+
|
159
|
+
second_item.destroy
|
160
|
+
first_item.reload
|
161
|
+
|
162
|
+
assert(0, first_item.links.length)
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_tags
|
166
|
+
item = DirectedEdge::Item.new(@database, 'customer1')
|
167
|
+
item.add_tag('dude')
|
168
|
+
assert(item.tags.include?('dude'))
|
169
|
+
|
170
|
+
item.save
|
171
|
+
item.reload
|
172
|
+
assert(item.tags.include?('dude'))
|
173
|
+
|
174
|
+
item.remove_tag('dude')
|
175
|
+
item.add_tag('greek')
|
176
|
+
item.save
|
177
|
+
item.reload
|
178
|
+
assert(item.tags.include?('greek'))
|
179
|
+
assert(!item.tags.include?('dude'))
|
180
|
+
|
181
|
+
item = DirectedEdge::Item.new(@database, 'customer1')
|
182
|
+
item.remove_tag('greek')
|
183
|
+
item.save
|
184
|
+
item.reload
|
185
|
+
|
186
|
+
assert(!item.tags.include?('greek'))
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_properties
|
190
|
+
item = DirectedEdge::Item.new(@database, 'customer1')
|
191
|
+
|
192
|
+
assert_equal(0, item.properties.length)
|
193
|
+
|
194
|
+
item['test_property_1'] = 'test_value'
|
195
|
+
item.save
|
196
|
+
|
197
|
+
assert_equal(1, item.properties.length)
|
198
|
+
assert_equal('test_value', item['test_property_1'])
|
199
|
+
|
200
|
+
item['test_property_2'] = 'test_value'
|
201
|
+
|
202
|
+
assert_equal(2, item.properties.length)
|
203
|
+
assert_equal('test_value', item['test_property_2'])
|
204
|
+
|
205
|
+
item['test_property_1'] = 'test_value_updated'
|
206
|
+
|
207
|
+
assert_equal(2, item.properties.length)
|
208
|
+
assert_equal('test_value_updated', item['test_property_1'])
|
209
|
+
|
210
|
+
# Test the cached example of clearing a property
|
211
|
+
|
212
|
+
item.clear_property('test_property_1')
|
213
|
+
assert(!item.properties.include?('test_property_1'))
|
214
|
+
|
215
|
+
# Make sure that it stays gone when reloading
|
216
|
+
|
217
|
+
item.save
|
218
|
+
item.reload
|
219
|
+
assert(!item.properties.include?('test_property_1'))
|
220
|
+
|
221
|
+
# Test the incremental update
|
222
|
+
|
223
|
+
item['test_property_1'] = 'test_value'
|
224
|
+
item.save
|
225
|
+
|
226
|
+
item = DirectedEdge::Item.new(@database, 'customer1')
|
227
|
+
item.clear_property('test_property_1')
|
228
|
+
item.save
|
229
|
+
item.reload
|
230
|
+
assert(!item.properties.include?('test_property_1'))
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_load
|
234
|
+
def run_load_test(prefix, count)
|
235
|
+
(1..count).concurrently do |i|
|
236
|
+
item = DirectedEdge::Item.new(@database, "test_item_#{prefix}_#{i}")
|
237
|
+
item.create([], ['test_tag'])
|
238
|
+
end
|
239
|
+
(1..count).concurrently do |i|
|
240
|
+
item = DirectedEdge::Item.new(@database, "test_item_#{prefix}_#{i}")
|
241
|
+
item['test_property'] = 'test_value'
|
242
|
+
item.save
|
243
|
+
end
|
244
|
+
(1..count).concurrently do |i|
|
245
|
+
item = DirectedEdge::Item.new(@database, "test_item_#{prefix}_#{i}")
|
246
|
+
assert_equal(1, item.tags.length)
|
247
|
+
assert_equal(1, item.properties.length)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Run 5 sets of load tests which each create 100 items, add a property to
|
252
|
+
# them, and then query them to make sure the tag and properties on each of
|
253
|
+
# them are correct
|
254
|
+
|
255
|
+
(1..5).concurrently do |i|
|
256
|
+
# Stagger the results so that reads and writes are interleaved
|
257
|
+
sleep(i - 1)
|
258
|
+
run_load_test(i, 100)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_rankings
|
263
|
+
customer1 = DirectedEdge::Item.new(@database, 'customer1')
|
264
|
+
customer2 = DirectedEdge::Item.new(@database, 'customer2')
|
265
|
+
customer3 = DirectedEdge::Item.new(@database, 'customer3')
|
266
|
+
|
267
|
+
# Test an out of range ranking.
|
268
|
+
|
269
|
+
customer1.links[customer2] = -1
|
270
|
+
assert_raise(RestClient::RequestFailed) { customer1.save }
|
271
|
+
|
272
|
+
# And another.
|
273
|
+
|
274
|
+
customer1.reload
|
275
|
+
customer1.links[customer2] = 100
|
276
|
+
assert_raise(RestClient::RequestFailed) { customer1.save }
|
277
|
+
|
278
|
+
customer1.reload
|
279
|
+
customer1.link_to(customer3, 10)
|
280
|
+
customer1.save
|
281
|
+
customer1.reload
|
282
|
+
assert_equal(10, customer1.weight_for(customer3))
|
283
|
+
end
|
284
|
+
|
285
|
+
def test_group_related
|
286
|
+
assert_equal(0, @database.group_related([], ['product']).size)
|
287
|
+
assert_equal(20, @database.group_related(['product1', 'product2'], ['product']).size)
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_unsafe_chars
|
291
|
+
item = DirectedEdge::Item.new(@database, ';@%&!')
|
292
|
+
item['foo'] = 'bar'
|
293
|
+
item.save
|
294
|
+
|
295
|
+
item = DirectedEdge::Item.new(@database, ';@%&!')
|
296
|
+
assert(item['foo'] == 'bar')
|
297
|
+
end
|
298
|
+
|
299
|
+
def test_bad_links
|
300
|
+
item = DirectedEdge::Item.new(@database, 'does not exist')
|
301
|
+
assert_raise(RestClient::ResourceNotFound) { item.destroy }
|
302
|
+
|
303
|
+
item = DirectedEdge::Item.new(@database, 'customer1')
|
304
|
+
item.link_to('also does not exist')
|
305
|
+
assert_raise(RestClient::RequestFailed) { item.save }
|
306
|
+
end
|
307
|
+
|
308
|
+
def test_query_parameters
|
309
|
+
item = DirectedEdge::Item.new(@database, 'product1')
|
310
|
+
assert_equal(5, item.related(['product'], :max_results => 5).size)
|
311
|
+
|
312
|
+
item.link_to('product21')
|
313
|
+
item.save
|
314
|
+
|
315
|
+
assert(item.related(['product']).include?('product21'))
|
316
|
+
assert(!item.related(['product'], :exclude_linked => true).include?('product21'))
|
317
|
+
end
|
318
|
+
|
319
|
+
def test_include_properties
|
320
|
+
item = DirectedEdge::Item.new(@database, 'product1')
|
321
|
+
other = DirectedEdge::Item.new(@database, 'product21')
|
322
|
+
other['foo'] = 'bar'
|
323
|
+
other.save
|
324
|
+
related = item.related(['product'], :include_properties => true)
|
325
|
+
assert_equal('bar', related['product21']['foo'])
|
326
|
+
|
327
|
+
related = @database.group_related(['product1'], ['product'], :include_properties => true)
|
328
|
+
assert_equal('bar', related['product21']['foo'])
|
329
|
+
|
330
|
+
customer = DirectedEdge::Item.new(@database, 'customer2')
|
331
|
+
recommended = customer.recommended(['product'], :include_properties => true)
|
332
|
+
assert_equal('bar', recommended['product21']['foo'])
|
333
|
+
end
|
334
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: directed-edge
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Directed Edge
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-28 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rest-client
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: Bindings for the Directed Edge webservices API
|
26
|
+
email: info@directededge.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- LICENSE
|
36
|
+
- README.rdoc
|
37
|
+
- Rakefile
|
38
|
+
- VERSION
|
39
|
+
- directed-edge.gemspec
|
40
|
+
- examples/example_store.rb
|
41
|
+
- lib/directed_edge.rb
|
42
|
+
- test/helper.rb
|
43
|
+
- test/test_directed_edge.rb
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: http://developer.directededge.com/
|
46
|
+
licenses: []
|
47
|
+
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options:
|
50
|
+
- --charset=UTF-8
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.3.5
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Bindings for the Directed Edge webservices API
|
72
|
+
test_files: []
|
73
|
+
|