directed-edge 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/directed-edge.gemspec +2 -2
- data/lib/directed_edge.rb +74 -54
- data/test/test_directed_edge.rb +35 -9
- metadata +4 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/directed-edge.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{directed-edge}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Directed Edge"]
|
12
|
-
s.date = %q{2010-04-
|
12
|
+
s.date = %q{2010-04-25}
|
13
13
|
s.description = %q{Bindings for the Directed Edge webservices API}
|
14
14
|
s.email = %q{info@directededge.com}
|
15
15
|
s.extra_rdoc_files = [
|
data/lib/directed_edge.rb
CHANGED
@@ -55,6 +55,22 @@ module DirectedEdge
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
class CollectionHash < Hash
|
59
|
+
def initialize(type)
|
60
|
+
@type = type
|
61
|
+
end
|
62
|
+
def [](key)
|
63
|
+
self[key] = @type.new unless include? key
|
64
|
+
super(key)
|
65
|
+
end
|
66
|
+
def each
|
67
|
+
super { |key, value| yield(key, value) unless value.empty? }
|
68
|
+
end
|
69
|
+
def empty?
|
70
|
+
each { |key, value| return false } ; true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
58
74
|
# Base class used for Database and Item that has some basic resource
|
59
75
|
# grabbing functionality.
|
60
76
|
|
@@ -92,26 +108,6 @@ module DirectedEdge
|
|
92
108
|
values
|
93
109
|
end
|
94
110
|
|
95
|
-
# Returns a hash of the elements from the document matching the given
|
96
|
-
# element name. If the specified attribute is present, its value will
|
97
|
-
# be assigned to the hash, otherwise the default value given will be
|
98
|
-
# used.
|
99
|
-
|
100
|
-
def hash_from_document(document, element, attribute, default=0)
|
101
|
-
values = {}
|
102
|
-
document.elements.each("//#{element}") do |v|
|
103
|
-
value = v.attribute(attribute).to_s || default
|
104
|
-
if value.empty?
|
105
|
-
values[v.text] = default
|
106
|
-
elsif value.to_i.to_s == value.to_s
|
107
|
-
values[v.text] = value.to_i
|
108
|
-
else
|
109
|
-
values[v.text] = value.to_s
|
110
|
-
end
|
111
|
-
end
|
112
|
-
values
|
113
|
-
end
|
114
|
-
|
115
111
|
# Normalizes the parameters in an argument hash to a standard form
|
116
112
|
# so that they can be passed off to the web services API -- e.g.
|
117
113
|
# :foo_bar to 'fooBar'
|
@@ -328,13 +324,13 @@ module DirectedEdge
|
|
328
324
|
@database = database
|
329
325
|
|
330
326
|
@id = id
|
331
|
-
@links =
|
327
|
+
@links = CollectionHash.new(Hash)
|
332
328
|
@tags = Set.new
|
333
329
|
@preselected = []
|
334
330
|
@blacklisted = Set.new
|
335
331
|
@properties = {}
|
336
332
|
|
337
|
-
@links_to_remove =
|
333
|
+
@links_to_remove = CollectionHash.new(Set)
|
338
334
|
@tags_to_remove = Set.new
|
339
335
|
@preselected_to_remove = Set.new
|
340
336
|
@blacklisted_to_remove = Set.new
|
@@ -363,9 +359,13 @@ module DirectedEdge
|
|
363
359
|
|
364
360
|
# Creates an item if it does not already exist in the database or overwrites
|
365
361
|
# an existing item if one does.
|
362
|
+
#
|
363
|
+
# This has been deprecated as it's not set up to properly support link types.
|
364
|
+
# use new / save instead.
|
366
365
|
|
367
366
|
def create(links={}, tags=Set.new, properties={})
|
368
|
-
|
367
|
+
warn 'DirectedEdge::Item::create has been deprecated. Use new / save instead.'
|
368
|
+
@links[''] = links
|
369
369
|
@tags = tags
|
370
370
|
@properties = properties
|
371
371
|
|
@@ -390,6 +390,8 @@ module DirectedEdge
|
|
390
390
|
|
391
391
|
put(complete_document, 'add')
|
392
392
|
|
393
|
+
### CHECKING LINKS_TO_REMOVE.EMPTY? ISN'T CORRECT ANYMORE
|
394
|
+
|
393
395
|
if !@links_to_remove.empty? ||
|
394
396
|
!@tags_to_remove.empty? ||
|
395
397
|
!@preselected_to_remove.empty? ||
|
@@ -410,13 +412,11 @@ module DirectedEdge
|
|
410
412
|
# will be discarded.
|
411
413
|
|
412
414
|
def reload
|
413
|
-
|
414
|
-
|
415
|
-
@
|
416
|
-
@
|
417
|
-
@
|
418
|
-
@blacklisted = Set.new(list_from_document(document, 'blacklisted'))
|
419
|
-
@properties = {}
|
415
|
+
@links.clear
|
416
|
+
@tags.clear
|
417
|
+
@preselected.clear
|
418
|
+
@blacklisted.clear
|
419
|
+
@properties.clear
|
420
420
|
|
421
421
|
@links_to_remove.clear
|
422
422
|
@tags_to_remove.clear
|
@@ -424,17 +424,15 @@ module DirectedEdge
|
|
424
424
|
@blacklisted_to_remove.clear
|
425
425
|
@properties_to_remove.clear
|
426
426
|
|
427
|
-
|
428
|
-
|
429
|
-
end
|
430
|
-
@cached = true
|
427
|
+
@cached = false
|
428
|
+
read
|
431
429
|
end
|
432
430
|
|
433
431
|
# Returns a set of items that are linked to from this item.
|
434
432
|
|
435
|
-
def links
|
433
|
+
def links(type='')
|
436
434
|
read
|
437
|
-
@links
|
435
|
+
@links[type.to_s]
|
438
436
|
end
|
439
437
|
|
440
438
|
# Returns a set containing all of this item's tags.
|
@@ -515,27 +513,27 @@ module DirectedEdge
|
|
515
513
|
# item is saved. Otherwise the link will be ignored as the engine tries
|
516
514
|
# to detect 'broken' links that do not terminate at a valid item.
|
517
515
|
|
518
|
-
def link_to(other, weight=0)
|
516
|
+
def link_to(other, weight=0, type='')
|
519
517
|
raise RangeError if (weight < 0 || weight > 10)
|
520
|
-
@links_to_remove.delete(other)
|
521
|
-
@links[other.to_s] = weight
|
518
|
+
@links_to_remove[type.to_s].delete(other)
|
519
|
+
@links[type.to_s][other.to_s] = weight
|
522
520
|
end
|
523
521
|
|
524
522
|
# Deletes a link from this item to other.
|
525
523
|
#
|
526
524
|
# The changes will not be reflected in the database until save is called.
|
527
525
|
|
528
|
-
def unlink_from(other)
|
529
|
-
@links_to_remove.add(other.to_s) unless @cached
|
530
|
-
@links.delete(other.to_s)
|
526
|
+
def unlink_from(other, type='')
|
527
|
+
@links_to_remove[type.to_s].add(other.to_s) unless @cached
|
528
|
+
@links[type.to_s].delete(other.to_s)
|
531
529
|
end
|
532
530
|
|
533
531
|
# If there is a link for "other" then it returns the weight for the given
|
534
532
|
# item. Zero indicates that no weight is assigned.
|
535
533
|
|
536
|
-
def weight_for(other)
|
534
|
+
def weight_for(other, type='')
|
537
535
|
read
|
538
|
-
@links[other.to_s]
|
536
|
+
@links[type.to_s][other.to_s]
|
539
537
|
end
|
540
538
|
|
541
539
|
# Adds a tag to this item.
|
@@ -639,10 +637,18 @@ module DirectedEdge
|
|
639
637
|
# already cached.
|
640
638
|
|
641
639
|
def read
|
642
|
-
|
640
|
+
unless @cached
|
643
641
|
begin
|
644
642
|
document = read_document
|
645
|
-
|
643
|
+
|
644
|
+
document.elements.each('//link') do |link_element|
|
645
|
+
type = link_element.attribute('type')
|
646
|
+
type = type ? type.to_s : ''
|
647
|
+
weight = link_element.attribute('weight').to_s.to_i
|
648
|
+
target = link_element.text
|
649
|
+
@links[type][target] = weight unless @links[type][target]
|
650
|
+
end
|
651
|
+
|
646
652
|
@tags.merge(list_from_document(document, 'tag'))
|
647
653
|
@preselected.concat(list_from_document(document, 'preselected'))
|
648
654
|
@blacklisted.merge(list_from_document(document, 'blacklisted'))
|
@@ -652,7 +658,10 @@ module DirectedEdge
|
|
652
658
|
@properties[name] = element.text unless @properties.has_key?(name)
|
653
659
|
end
|
654
660
|
|
655
|
-
@links_to_remove.each
|
661
|
+
@links_to_remove.each do |type, links|
|
662
|
+
links.each { |link, weight| @links[type].delete(link) }
|
663
|
+
end
|
664
|
+
|
656
665
|
@tags_to_remove.each { |tag| @tags.delete(tag) }
|
657
666
|
@preselected_to_remove.each { |p| @preselected.delete(p) }
|
658
667
|
@blacklisted_to_remove.each { |b| @blacklisted.delete(b) }
|
@@ -665,8 +674,8 @@ module DirectedEdge
|
|
665
674
|
@properties_to_remove.clear
|
666
675
|
|
667
676
|
@cached = true
|
668
|
-
rescue
|
669
|
-
puts "Couldn't read \"#{@id}\" from the database
|
677
|
+
rescue => ex
|
678
|
+
puts "Couldn't read \"#{@id}\" from the database, #{ex}"
|
670
679
|
end
|
671
680
|
end
|
672
681
|
end
|
@@ -689,7 +698,15 @@ module DirectedEdge
|
|
689
698
|
|
690
699
|
def removal_document
|
691
700
|
item = setup_document(REXML::Document.new)
|
692
|
-
|
701
|
+
|
702
|
+
@links_to_remove.each do |type, links|
|
703
|
+
links.each do |link|
|
704
|
+
element = item.add_element('link')
|
705
|
+
element.add_attribute(type) unless type.empty?
|
706
|
+
element.add_text(link.to_s)
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
693
710
|
@tags_to_remove.each { |tag| item.add_element('tag').add_text(tag.to_s) }
|
694
711
|
@preselected_to_remove.each { |p| item.add_element('preselected').add_text(p.to_s) }
|
695
712
|
@blacklisted_to_remove.each { |b| item.add_element('blacklisted').add_text(b.to_s) }
|
@@ -701,10 +718,13 @@ module DirectedEdge
|
|
701
718
|
|
702
719
|
def insert_item(document)
|
703
720
|
item = setup_document(document)
|
704
|
-
@links.each do |
|
705
|
-
|
706
|
-
|
707
|
-
|
721
|
+
@links.each do |type, links|
|
722
|
+
links.each do |link, weight|
|
723
|
+
element = item.add_element('link')
|
724
|
+
element.add_attribute('type', type) unless type.empty?
|
725
|
+
element.add_attribute('weight', weight.to_s) unless weight == 0
|
726
|
+
element.add_text(link.to_s)
|
727
|
+
end
|
708
728
|
end
|
709
729
|
@tags.each { |tag| item.add_element('tag').add_text(tag.to_s) }
|
710
730
|
@preselected.each { |p| item.add_element('preselected').add_text(p.to_s) }
|
data/test/test_directed_edge.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'helper'
|
2
|
+
require 'pp'
|
2
3
|
|
3
4
|
# Defines a multithreaded "each"
|
4
5
|
|
@@ -85,13 +86,17 @@ class TestDirectedEdge < Test::Unit::TestCase
|
|
85
86
|
|
86
87
|
def test_items
|
87
88
|
first_item = DirectedEdge::Item.new(@database, 'test_1')
|
88
|
-
first_item.
|
89
|
+
first_item.save
|
89
90
|
|
90
91
|
second_item = DirectedEdge::Item.new(@database, 'test_2')
|
91
|
-
second_item.
|
92
|
+
second_item.link_to(first_item)
|
93
|
+
second_item.save
|
92
94
|
|
93
95
|
third_item = DirectedEdge::Item.new(@database, 'test_3')
|
94
|
-
third_item.
|
96
|
+
third_item.link_to(first_item)
|
97
|
+
third_item.link_to(second_item)
|
98
|
+
third_item.add_tag('test_tag')
|
99
|
+
third_item.save
|
95
100
|
|
96
101
|
assert_equal('test_1', first_item.name)
|
97
102
|
|
@@ -115,8 +120,8 @@ class TestDirectedEdge < Test::Unit::TestCase
|
|
115
120
|
# Make sure that the third item is linked to both the first and second items
|
116
121
|
|
117
122
|
assert_equal(2, third_item.links.length)
|
118
|
-
assert(third_item.links.include?(first_item))
|
119
|
-
assert(third_item.links.include?(second_item))
|
123
|
+
assert(third_item.links.include?(first_item.to_s))
|
124
|
+
assert(third_item.links.include?(second_item.to_s))
|
120
125
|
|
121
126
|
# Make sure that the first and second items show up in the related items for
|
122
127
|
# the third item
|
@@ -230,13 +235,34 @@ class TestDirectedEdge < Test::Unit::TestCase
|
|
230
235
|
assert(!item.properties.include?('test_property_1'))
|
231
236
|
end
|
232
237
|
|
238
|
+
def test_link_types
|
239
|
+
first = DirectedEdge::Item.new(@database, 'item_1')
|
240
|
+
second = DirectedEdge::Item.new(@database, 'item_2')
|
241
|
+
first.save
|
242
|
+
second.save
|
243
|
+
|
244
|
+
first.link_to(second, 0, :test)
|
245
|
+
first.save
|
246
|
+
|
247
|
+
first = DirectedEdge::Item.new(@database, 'item_1')
|
248
|
+
second = DirectedEdge::Item.new(@database, 'item_2')
|
249
|
+
|
250
|
+
first.save
|
251
|
+
first.reload
|
252
|
+
second.reload
|
253
|
+
|
254
|
+
assert(first.links(:test).include?('item_2'))
|
255
|
+
assert(!first.links.include?('item_2'))
|
256
|
+
end
|
257
|
+
|
233
258
|
def test_load
|
234
259
|
return if ENV['NO_LOAD_TEST']
|
235
260
|
|
236
261
|
def run_load_test(prefix, count)
|
237
262
|
(1..count).concurrently do |i|
|
238
263
|
item = DirectedEdge::Item.new(@database, "test_item_#{prefix}_#{i}")
|
239
|
-
item.
|
264
|
+
item.add_tag('test_tag')
|
265
|
+
item.save
|
240
266
|
end
|
241
267
|
(1..count).concurrently do |i|
|
242
268
|
item = DirectedEdge::Item.new(@database, "test_item_#{prefix}_#{i}")
|
@@ -372,10 +398,10 @@ class TestDirectedEdge < Test::Unit::TestCase
|
|
372
398
|
|
373
399
|
def test_blacklisted
|
374
400
|
customer = DirectedEdge::Item.new(@database, 'customer1')
|
375
|
-
first = customer.recommended.first
|
401
|
+
first = customer.recommended(['product']).first
|
376
402
|
customer.add_blacklisted(first)
|
377
403
|
customer.save
|
378
|
-
|
404
|
+
assert(!customer.recommended(['product']).include?(first))
|
379
405
|
|
380
406
|
assert(customer.blacklisted.include?(first))
|
381
407
|
customer.reload
|
@@ -386,6 +412,6 @@ class TestDirectedEdge < Test::Unit::TestCase
|
|
386
412
|
assert(!customer.blacklisted.include?(first))
|
387
413
|
customer.reload
|
388
414
|
assert(!customer.blacklisted.include?(first))
|
389
|
-
|
415
|
+
assert(customer.recommended(['product']).include?(first))
|
390
416
|
end
|
391
417
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Directed Edge
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-25 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|