directed-edge 0.1.3 → 0.2.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/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
|