directed-edge 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  coverage
2
2
  rdoc
3
3
  pkg
4
+ .yardoc
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (C) 2009, Directed Edge, Inc. <info@directededge.com>
1
+ Copyright (C) 2009-2010, Directed Edge, Inc. <info@directededge.com>
2
2
 
3
3
  Redistribution and use in source and binary forms, with or without
4
4
  modification, are permitted provided that the following conditions
data/README.rdoc CHANGED
@@ -2,16 +2,44 @@
2
2
 
3
3
  Bindings for the Directed Edge Web-services API
4
4
 
5
- == Note on Patches/Pull Requests
5
+ == Usage
6
+
7
+ You should have gotten a user name and password from Directed Edge when you
8
+ signed up for an account. If you don't have one now, you can get one at:
9
+
10
+ - {Signup for Directed Edge Account}[http://www.directededge.com/signup.html]
11
+
12
+ You'll use those when instantiating a Directed Edge database object, which will
13
+ be the hub for other operations:
14
+
15
+ DE_USER = 'testaccount'
16
+ DE_PASS = '1234567890abcd'
17
+
18
+ database = DirectedEdge::Database.new(DE_USER, DE_PASS)
19
+
20
+ From there you can create items:
21
+
22
+ item1 = DirectedEdge::Item.new(database, 'item_1')
23
+ item2 = DirectedEdge::Item.new(database, 'item_2')
24
+
25
+ Push them over to the Directed Edge web service:
26
+
27
+ item1.save
28
+ item2.save
29
+
30
+ And do stuff with them, like set properties, tags, link them to other items:
31
+
32
+ item1['picture'] = 'http://foo.bar.com/1.jpg'
33
+ item1.add_tag('product')
34
+ item1.link_to(item2)
35
+ item1.save
36
+
37
+ There's more info on the Directed Edge developer site:
38
+
39
+ - {Getting started for Ruby developers}[http://developer.directededge.com/article/Getting_started_for_Ruby_developers]
40
+ - {Ruby E-Commerce Tutorial}[http://developer.directededge.com/article/Ruby_Bindings_for_E-Commerce_Tutorial]
6
41
 
7
- * Fork the project.
8
- * Make your feature addition or bug fix.
9
- * Add tests for it. This is important so I don't break it in a
10
- future version unintentionally.
11
- * Commit, do not mess with rakefile, version, or history.
12
- (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
- * Send me a pull request. Bonus points for topic branches.
14
42
 
15
43
  == Copyright
16
44
 
17
- Copyright (c) 2009 Directed Edge, Inc. See LICENSE for details.
45
+ Copyright (c) 2009-2010 Directed Edge, Inc. See LICENSE for details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
data/lib/directed_edge.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2009 Directed Edge, Inc.
1
+ # Copyright (C) 2009-2010 Directed Edge, Inc.
2
2
  #
3
3
  # Redistribution and use in source and binary forms, with or without
4
4
  # modification, are permitted provided that the following conditions
@@ -34,13 +34,10 @@ require 'cgi'
34
34
 
35
35
  module DirectedEdge
36
36
 
37
- # A hash subclass that tracks the insert order, which is useful when returning
38
- # a set of results that include full properties.
37
+ # @private
39
38
 
40
39
  class InsertOrderHash < Hash
41
40
 
42
- # Overridden assignment to track insert order
43
-
44
41
  def []=(key, value)
45
42
  store(key, value)
46
43
  @insert_order = [] if @insert_order.nil?
@@ -48,13 +45,13 @@ module DirectedEdge
48
45
  @insert_order.push(key)
49
46
  end
50
47
 
51
- # Provides an iterator that uses the hash's insert order
52
-
53
48
  def insert_order_each
54
49
  @insert_order.each { |key| yield key, fetch(key) } unless @insert_order.nil?
55
50
  end
56
51
  end
57
52
 
53
+ # @private
54
+
58
55
  class CollectionHash < Hash
59
56
  def initialize(type)
60
57
  @type = type
@@ -71,13 +68,16 @@ module DirectedEdge
71
68
  end
72
69
  end
73
70
 
74
- # Base class used for Database and Item that has some basic resource
75
- # grabbing functionality.
71
+ # @private
76
72
 
77
73
  class Resource
78
74
 
79
75
  private
80
76
 
77
+ def initialize(rest_resource)
78
+ @resource = rest_resource
79
+ end
80
+
81
81
  # Reads an item from the database and puts it into an XML document.
82
82
 
83
83
  def read_document(method='', params={})
@@ -85,7 +85,7 @@ module DirectedEdge
85
85
  REXML::Document.new(@resource[method].get(:accept => 'text/xml').to_s)
86
86
  end
87
87
 
88
- # Returns an array of the elements from the document matching the given
88
+ # @return [Array] The elements from the document matching the given
89
89
  # element name.
90
90
 
91
91
  def list_from_document(document, element)
@@ -112,7 +112,7 @@ module DirectedEdge
112
112
  # so that they can be passed off to the web services API -- e.g.
113
113
  # :foo_bar to 'fooBar'
114
114
 
115
- def normalize_params(hash)
115
+ def normalize_params!(hash)
116
116
  hash.each do |key, value|
117
117
  if !key.is_a?(String)
118
118
  hash.delete(key)
@@ -156,28 +156,48 @@ module DirectedEdge
156
156
  attr_reader :resource
157
157
 
158
158
  # Creates a connection to a Directed Edge database. The name and password
159
- # should have been provided when the account was created. The protocol
160
- # parameter is optional and may be <tt>http</tt> or <tt>https</tt>.
161
- # <tt>http</tt> is used by default as it is somewhat lower latency.
159
+ # should have been provided when the account was created.
160
+ #
161
+ # @param [String] name User name given when the Directed Edge account was
162
+ # created.
163
+ # @param [String] password Password given when the Directed Edge account was
164
+ # created.
165
+ # @param [String] protocol The protocol to connect to the Directed Edge
166
+ # webservices with.
167
+ #
168
+ # @return [DirectedEdge::Item]
162
169
 
163
- def initialize(name, password='', protocol='http')
170
+ def initialize(name, password='', protocol='http', options = {})
164
171
  @name = name
165
- host = ENV['DIRECTEDEDGE_HOST'] || 'webservices.directededge.com'
166
- @resource =
167
- RestClient::Resource.new("#{protocol}://#{name}:#{password}@#{host}/api/v1/#{name}")
172
+ host = options[:host] || ENV['DIRECTEDEDGE_HOST'] || 'webservices.directededge.com'
173
+ url = "#{protocol}://#{name}:#{password}@#{host}/api/v1/#{name}"
174
+
175
+ options[:timeout] ||= 10
176
+
177
+ super(RestClient::Resource.new(url, options))
168
178
  end
169
179
 
170
180
  # Imports a Directed Edge XML file to the database.
171
181
  #
172
- # See http://developer.directededge.com for more information on the XML format or the
173
- # Exporter for help on creating a file for importing.
182
+ # @see Exporter
183
+ #
184
+ # @see {Developer site}[http://developer.directededge.com/] for more information
185
+ # on the XML format.
174
186
 
175
187
  def import(file_name)
176
188
  @resource.put(File.read(file_name), :content_type => 'text/xml')
177
189
  end
178
190
 
179
- # Returns a set of recommendations for the set of items that is passed in in
191
+ # @return [Array] A set of recommendations for the set of items that is passed in in
180
192
  # aggregate, commonly used to do recommendations for a basket of items.
193
+ #
194
+ # @param [Array] items List of items to base the recommendations on, e.g. all of the
195
+ # items in the basket.
196
+ #
197
+ # The tags and params parameters are equivalent to those with the normal Item#related
198
+ # call.
199
+ #
200
+ # @see Item#related
181
201
 
182
202
  def group_related(items=Set.new, tags=Set.new, params={})
183
203
  if !items.is_a?(Array) || items.size < 1
@@ -186,7 +206,7 @@ module DirectedEdge
186
206
  params['items'] = items.to_a.join(',')
187
207
  params['tags'] = tags.to_a.join(',')
188
208
  params['union'] = true
189
- params = normalize_params(params)
209
+ normalize_params!(params)
190
210
  if params['includeProperties'] == 'true'
191
211
  property_hash_from_document(read_document('related', params), 'related')
192
212
  else
@@ -236,6 +256,8 @@ module DirectedEdge
236
256
  # destination is an existing database object, updates will be queued until
237
257
  # finish is called, at which point they will be uploaded to the webservices
238
258
  # in batch.
259
+ #
260
+ # @return [Exporter]
239
261
 
240
262
  def initialize(destination)
241
263
  if destination.is_a?(String)
@@ -321,8 +343,9 @@ module DirectedEdge
321
343
  # manipulated locally and then saved back to the database by calling save.
322
344
 
323
345
  def initialize(database, id)
324
- @database = database
346
+ super(database.resource[URI.escape(id, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))])
325
347
 
348
+ @database = database
326
349
  @id = id
327
350
  @links = CollectionHash.new(Hash)
328
351
  @tags = Set.new
@@ -335,13 +358,11 @@ module DirectedEdge
335
358
  @preselected_to_remove = Set.new
336
359
  @blacklisted_to_remove = Set.new
337
360
  @properties_to_remove = Set.new
338
-
339
- @resource = @database.resource[URI.escape(@id)]
340
361
  @cached = false
341
362
  end
342
363
 
343
- # Returns true if the other item is the same. The item given can either be
344
- # a string or an item object.
364
+ # @return [Boolean] True if the other item has the same ID. The item given
365
+ # can either be a string or an item object.
345
366
 
346
367
  def ==(other)
347
368
  if other.is_a?(Item)
@@ -351,17 +372,13 @@ module DirectedEdge
351
372
  end
352
373
  end
353
374
 
354
- # Returns the item's ID.
375
+ # @return [String] The item's ID
355
376
 
356
377
  def name
357
378
  @id
358
379
  end
359
380
 
360
- # Creates an item if it does not already exist in the database or overwrites
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.
381
+ # @deprecated Use new / save instead.
365
382
 
366
383
  def create(links={}, tags=Set.new, properties={})
367
384
  warn 'DirectedEdge::Item::create has been deprecated. Use new / save instead.'
@@ -378,9 +395,11 @@ module DirectedEdge
378
395
 
379
396
  # Writes all changes to links, tags and properties back to the database and
380
397
  # returns this item.
398
+ #
399
+ # @return [Item]
381
400
 
382
- def save
383
- if @cached
401
+ def save(options={})
402
+ if options[:overwrite] || @cached
384
403
  put(complete_document)
385
404
  else
386
405
 
@@ -410,6 +429,8 @@ module DirectedEdge
410
429
 
411
430
  # Reloads (or loads) the item from the database. Any unsaved changes will
412
431
  # will be discarded.
432
+ #
433
+ # @return [Item]
413
434
 
414
435
  def reload
415
436
  @links.clear
@@ -426,44 +447,56 @@ module DirectedEdge
426
447
 
427
448
  @cached = false
428
449
  read
450
+ self
429
451
  end
430
452
 
431
- # Returns a set of items that are linked to from this item.
453
+ # @return [Set] Items that are linked to from this item.
454
+ #
455
+ # @param [String] type Only links for the specified link-type will be
456
+ # returned.
432
457
 
433
458
  def links(type='')
434
459
  read
435
460
  @links[type.to_s]
436
461
  end
437
462
 
438
- # Returns a set containing all of this item's tags.
463
+ # @return [Set] The tags for this item.
439
464
 
440
465
  def tags
441
466
  read
442
467
  @tags
443
468
  end
444
469
 
445
- # Returns an ordered list of items that are preselected for this item.
470
+ # An ordered list of preselected recommendations for this item.
471
+ #
472
+ # @return [Array] The preselected recommendations for this item.
446
473
 
447
474
  def preselected
448
475
  read
449
476
  @preselected
450
477
  end
451
478
 
452
- # Returns a set of items that should not ever be recommended for this item.
479
+ # An ordered list of blacklisted recommendations for this item.
480
+ #
481
+ # @return [Array] The items blacklisted from being recommended for this item.
453
482
 
454
483
  def blacklisted
455
484
  read
456
485
  @blacklisted
457
486
  end
458
487
 
459
- # Returns a hash of all of this item's properties.
488
+ # All properties for this item.
489
+ #
490
+ # @return [Hash] All of the properties for this item.
460
491
 
461
492
  def properties
462
493
  read
463
494
  @properties
464
495
  end
465
496
 
466
- # Returns the property for the name specified.
497
+ # Fetches properties of the item.
498
+ #
499
+ # @return [String] The property for this item.
467
500
 
468
501
  def [](property_name)
469
502
  read
@@ -473,17 +506,23 @@ module DirectedEdge
473
506
  # Assigns value to the given property_name.
474
507
  #
475
508
  # This will not be written back to the database until save is called.
509
+ #
510
+ # @return [Item]
476
511
 
477
512
  def []=(property_name, value)
478
513
  @properties_to_remove.delete(property_name)
479
514
  @properties[property_name] = value
515
+ self
480
516
  end
481
517
 
482
518
  # Remove the given property_name.
519
+ #
520
+ # @return [Item]
483
521
 
484
522
  def clear_property(property_name)
485
523
  @properties_to_remove.add(property_name) unless @cached
486
524
  @properties.delete(property_name)
525
+ self
487
526
  end
488
527
 
489
528
  # Removes an item from the database, including deleting all links to and
@@ -491,6 +530,7 @@ module DirectedEdge
491
530
 
492
531
  def destroy
493
532
  @resource.delete
533
+ nil
494
534
  end
495
535
 
496
536
  # Creates a link from this item to other.
@@ -503,33 +543,50 @@ module DirectedEdge
503
543
  # user.link_to(product, 5)
504
544
  # user.save
505
545
  #
506
- # If no link is specified then a tradtional, unweighted link will be
507
- # created. This is typical to, for instance, incidate a purchase or click
508
- # from a user to a page or item.
509
- #
510
- # Weights may be in the range of 1 to 10.
546
+ # @param [String] other An identifier (or Item instance) for an item to be linked
547
+ # to.
548
+ # @param [Integer] weight A weight in the range of 1 to 10 for this link. If not
549
+ # specified (which is fine for most situations) an unweighted link will be
550
+ # created.
551
+ # @param [String] type The link type to be used for this connection, or, the
552
+ # default untyped link. This could be, for example, *purchase* or *rating*.
511
553
  #
512
554
  # Note that 'other' must exist in the database or must be saved before this
513
555
  # item is saved. Otherwise the link will be ignored as the engine tries
514
556
  # to detect 'broken' links that do not terminate at a valid item.
557
+ #
558
+ # @return [String] The item ID just linked to
515
559
 
516
560
  def link_to(other, weight=0, type='')
517
561
  raise RangeError if (weight < 0 || weight > 10)
518
562
  @links_to_remove[type.to_s].delete(other)
519
563
  @links[type.to_s][other.to_s] = weight
564
+ other
520
565
  end
521
566
 
522
- # Deletes a link from this item to other.
567
+ # Removes a relationship from this item to another item.
523
568
  #
524
569
  # The changes will not be reflected in the database until save is called.
570
+ #
571
+ # @param [String] other The ID (or Item instance) for an object to be
572
+ # unlinked.
573
+ # @return [String] The item ID just unlinked from.
574
+ # @see Item#link_to
525
575
 
526
576
  def unlink_from(other, type='')
527
577
  @links_to_remove[type.to_s].add(other.to_s) unless @cached
528
578
  @links[type.to_s].delete(other.to_s)
579
+ other
529
580
  end
530
581
 
531
582
  # If there is a link for "other" then it returns the weight for the given
532
583
  # item. Zero indicates that no weight is assigned.
584
+ #
585
+ # @param [String] other The item being queried for.
586
+ # @param [String] type The link type of the relationship.
587
+ #
588
+ # @return [Integer] The weight for a link from this item to the specified
589
+ # item, or nil if not found.
533
590
 
534
591
  def weight_for(other, type='')
535
592
  read
@@ -538,55 +595,118 @@ module DirectedEdge
538
595
 
539
596
  # Adds a tag to this item.
540
597
  #
598
+ # @param [String] tag The tag to be added to this item's tag set.
599
+ # @return [String] The tag just added.
600
+ #
541
601
  # The changes will not be reflected in the database until save is called.
542
602
 
543
603
  def add_tag(tag)
544
604
  @tags_to_remove.delete(tag)
545
605
  @tags.add(tag)
606
+ tag
546
607
  end
547
608
 
548
609
  # Removes a tag from this item.
549
610
  #
611
+ # @param [String] tag The tag to be removed from this item's set of tags.
612
+ # @return [String] The tag just removed.
613
+ #
550
614
  # The changes will not be reflected in the database until save is called.
551
615
 
552
616
  def remove_tag(tag)
553
617
  @tags_to_remove.add(tag) unless @cached
554
618
  @tags.delete(tag)
619
+ tag
555
620
  end
556
621
 
622
+ # Adds a hand-picked recommendation for this item.
623
+ #
624
+ # Note that preselected recommendations are weighted by the order that they
625
+ # are added, i.e. the first preselected item added will be the first one
626
+ # shown.
627
+ #
628
+ # @param [String] item The ID (or an Item instance) of the item that should
629
+ # be always returned as a recommendation for this item.
630
+ # @return [String] The ID just added.
631
+
557
632
  def add_preselected(item)
558
- @preselected_to_remove.delete(item)
559
- @preselected.push(item)
633
+ @preselected_to_remove.delete(item.to_s)
634
+ @preselected.push(item.to_s)
635
+ item
560
636
  end
561
637
 
638
+ # Removes a hand-picked recommendation for this item.
639
+ #
640
+ # @param [String] item The ID (or an Item instance) of the item that should
641
+ # be removed from the preselected list.
642
+ # @return [String] The ID just removed.
643
+ #
644
+ # @see Item#add_preselected
645
+
562
646
  def remove_preselected(item)
563
- @preselected_to_remove.add(item) unless @cached
564
- @preselected.delete(item)
647
+ @preselected_to_remove.add(item.to_s) unless @cached
648
+ @preselected.delete(item.to_s)
649
+ item
565
650
  end
566
651
 
652
+ # Adds a blacklisted item that should never be shown as recommended for this
653
+ # item.
654
+ #
655
+ # @param [String] item The ID (or an Item instance) of the item that should
656
+ # be blacklisted.
657
+ # @return [String] The ID just blacklisted.
658
+
567
659
  def add_blacklisted(item)
568
- @blacklisted_to_remove.delete(item)
569
- @blacklisted.add(item)
660
+ @blacklisted_to_remove.delete(item.to_s)
661
+ @blacklisted.add(item.to_s)
662
+ item
570
663
  end
571
664
 
665
+ # Removes a blacklisted item.
666
+ #
667
+ # @param [String] item The ID (or an Item instance) of the item that should
668
+ # be removed from the blacklist.
669
+ # @return [String] The ID just delisted.
670
+ #
671
+ # @see Item::add_blacklisted
672
+
572
673
  def remove_blacklisted(item)
573
- @blacklisted_to_remove.add(item) unless @cached
574
- @blacklisted.delete(item)
674
+ @blacklisted_to_remove.add(item.to_s) unless @cached
675
+ @blacklisted.delete(item.to_s)
676
+ item
575
677
  end
576
678
 
577
- # Returns the list of items related to this one. Unlike "recommended" this
578
- # may include items which are directly linked from this item. If any tags
579
- # are specified, only items which have one or more of the specified tags
679
+ # related and recommended are the two main methods for querying for
680
+ # recommendations with the Directed Edge API. Related is for *similar*
681
+ # items, e.g. "products like this product", whereas recommended is for
682
+ # personalized recommendations, i.e. "We think you'd like..."
683
+ #
684
+ # @return [Array] List of item IDs related to this one with the most closely
685
+ # related items first.
686
+ #
687
+ # @param [Set] tags Only items which have at least one of the provided tags
580
688
  # will be returned.
581
689
  #
582
- # Parameters that may be passed in include:
583
- # - :exclude_linked (true / false)
584
- # - :max_results (integer)
690
+ # @param [Hash] options A set of options which are passed directly on to
691
+ # the web services API in the query string.
692
+ #
693
+ # @option params [Boolean] :exclude_linked (false)
694
+ # Exclude items which are linked directly from this item.
695
+ # @option params [Integer] :max_results (20)
696
+ # Only returns up to :max_results items.
697
+ # @option params [Integer] :link_type_weight (1)
698
+ # Here link_type should be replace with one of the actual link types in
699
+ # use in your database, i.e. :purchase_weight and specifies how strongly
700
+ # links of that type should be weighted related to other link types. For
701
+ # Instance if you wanted 20% ratings and 80% purchases you would specify:
702
+ # :purchases_weight => 8, :ratings_weight => 2
585
703
  #
586
704
  # This will not reflect any unsaved changes to items.
705
+ #
706
+ # @see Item#recommended
587
707
 
588
708
  def related(tags=Set.new, params={})
589
- params = normalize_params(params)
709
+ normalize_params!(params)
590
710
  params['tags'] = tags.to_a.join(',')
591
711
  if params['includeProperties'] == 'true'
592
712
  property_hash_from_document(read_document('related', params), 'related')
@@ -595,19 +715,37 @@ module DirectedEdge
595
715
  end
596
716
  end
597
717
 
598
- # Returns the list of items recommended for this item, usually a user.
599
- # Unlike "related" this does not include items linked from this item. If
600
- # any tags are specified, only items which have one or more of the specified
601
- # tags will be returned.
718
+ # related and recommended are the two main methods for querying for
719
+ # recommendations with the Directed Edge API. Related is for *similar*
720
+ # items, e.g. "products like this product", whereas recommended is for
721
+ # personalized recommendations, i.e. "We think you'd like..."
722
+ #
723
+ # @return [Array] List of item IDs recommeded for this item with the most
724
+ # strongly recommended items first.
725
+ #
726
+ # @param [Set] tags Only items which have at least one of the provided tags
727
+ # will be returned.
728
+ #
729
+ # @param [Hash] options A set of options which are passed directly on to
730
+ # the web services API in the query string.
602
731
  #
603
- # Parameters that may be passed in include:
604
- # - :exclude_linked (true / false)
605
- # - :max_results (integer)
732
+ # @option params [Boolean] :exclude_linked (false)
733
+ # Exclude items which are linked directly from this item.
734
+ # @option params [Integer] :max_results (20)
735
+ # Only returns up to :max_results items.
736
+ # @option params [Integer] :link_type_weight (1)
737
+ # Here link_type should be replace with one of the actual link types in
738
+ # use in your database, i.e. :purchase_weight and specifies how strongly
739
+ # links of that type should be weighted related to other link types. For
740
+ # Instance if you wanted 20% ratings and 80% purchases you would specify:
741
+ # :purchases_weight => 8, :ratings_weight => 2
606
742
  #
607
743
  # This will not reflect any unsaved changes to items.
744
+ #
745
+ # @see Item#related
608
746
 
609
747
  def recommended(tags=Set.new, params={})
610
- params = normalize_params(params)
748
+ normalize_params!(params)
611
749
  params['tags'] = tags.to_a.join(',')
612
750
  params.key?('excludeLinked') || params['excludeLinked'] = 'true'
613
751
  if params['includeProperties'] == 'true'
@@ -617,13 +755,13 @@ module DirectedEdge
617
755
  end
618
756
  end
619
757
 
620
- # Returns the ID of the item.
758
+ # @return [String] The ID of the item.
621
759
 
622
760
  def to_s
623
761
  @id
624
762
  end
625
763
 
626
- # Returns an XML representation of the item as a string not including the
764
+ # @return [String] An XML representation of the item as a string not including the
627
765
  # usual document regalia, e.g. starting with <item> (used for exporting the
628
766
  # item to a file)
629
767
 
@@ -638,45 +776,41 @@ module DirectedEdge
638
776
 
639
777
  def read
640
778
  unless @cached
641
- begin
642
- document = read_document
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
-
652
- @tags.merge(list_from_document(document, 'tag'))
653
- @preselected.concat(list_from_document(document, 'preselected'))
654
- @blacklisted.merge(list_from_document(document, 'blacklisted'))
655
-
656
- document.elements.each('//property') do |element|
657
- name = element.attribute('name').value
658
- @properties[name] = element.text unless @properties.has_key?(name)
659
- end
660
-
661
- @links_to_remove.each do |type, links|
662
- links.each { |link, weight| @links[type].delete(link) }
663
- end
664
-
665
- @tags_to_remove.each { |tag| @tags.delete(tag) }
666
- @preselected_to_remove.each { |p| @preselected.delete(p) }
667
- @blacklisted_to_remove.each { |b| @blacklisted.delete(b) }
668
- @properties_to_remove.each { |property| @properties.delete(property) }
779
+ document = read_document
780
+
781
+ document.elements.each('//link') do |link_element|
782
+ type = link_element.attribute('type')
783
+ type = type ? type.to_s : ''
784
+ weight = link_element.attribute('weight').to_s.to_i
785
+ target = link_element.text
786
+ @links[type][target] = weight unless @links[type][target]
787
+ end
669
788
 
670
- @links_to_remove.clear
671
- @tags_to_remove.clear
672
- @preselected_to_remove.clear
673
- @blacklisted_to_remove.clear
674
- @properties_to_remove.clear
789
+ @tags.merge(list_from_document(document, 'tag'))
790
+ @preselected.concat(list_from_document(document, 'preselected'))
791
+ @blacklisted.merge(list_from_document(document, 'blacklisted'))
792
+
793
+ document.elements.each('//property') do |element|
794
+ name = element.attribute('name').value
795
+ @properties[name] = element.text unless @properties.has_key?(name)
796
+ end
675
797
 
676
- @cached = true
677
- rescue => ex
678
- puts "Couldn't read \"#{@id}\" from the database, #{ex}"
798
+ @links_to_remove.each do |type, links|
799
+ links.each { |link, weight| @links[type].delete(link) }
679
800
  end
801
+
802
+ @tags_to_remove.each { |tag| @tags.delete(tag) }
803
+ @preselected_to_remove.each { |p| @preselected.delete(p) }
804
+ @blacklisted_to_remove.each { |b| @blacklisted.delete(b) }
805
+ @properties_to_remove.each { |property| @properties.delete(property) }
806
+
807
+ @links_to_remove.clear
808
+ @tags_to_remove.clear
809
+ @preselected_to_remove.clear
810
+ @blacklisted_to_remove.clear
811
+ @properties_to_remove.clear
812
+
813
+ @cached = true
680
814
  end
681
815
  end
682
816
 
@@ -258,6 +258,8 @@ class TestDirectedEdge < Test::Unit::TestCase
258
258
  def test_load
259
259
  return if ENV['NO_LOAD_TEST']
260
260
 
261
+ Process.setrlimit(Process::RLIMIT_NOFILE, 4096, 65536)
262
+
261
263
  def run_load_test(prefix, count)
262
264
  (1..count).concurrently do |i|
263
265
  item = DirectedEdge::Item.new(@database, "test_item_#{prefix}_#{i}")
@@ -295,13 +297,13 @@ class TestDirectedEdge < Test::Unit::TestCase
295
297
  # Test an out of range ranking.
296
298
 
297
299
  customer1.links[customer2] = -1
298
- assert_raise(RestClient::RequestFailed) { customer1.save }
300
+ assert_raise(RestClient::UnprocessableEntity) { customer1.save }
299
301
 
300
302
  # And another.
301
303
 
302
304
  customer1.reload
303
305
  customer1.links[customer2] = 100
304
- assert_raise(RestClient::RequestFailed) { customer1.save }
306
+ assert_raise(RestClient::UnprocessableEntity) { customer1.save }
305
307
 
306
308
  customer1.reload
307
309
  customer1.link_to(customer3, 10)
@@ -322,6 +324,13 @@ class TestDirectedEdge < Test::Unit::TestCase
322
324
 
323
325
  item = DirectedEdge::Item.new(@database, ';@%&!')
324
326
  assert(item['foo'] == 'bar')
327
+
328
+ item = DirectedEdge::Item.new(@database, 'foo/bar')
329
+ item['foo'] = 'bar'
330
+ item.save
331
+
332
+ item = DirectedEdge::Item.new(@database, 'foo/bar')
333
+ assert(item['foo'] == 'bar')
325
334
  end
326
335
 
327
336
  def test_bad_links
@@ -330,7 +339,7 @@ class TestDirectedEdge < Test::Unit::TestCase
330
339
 
331
340
  item = DirectedEdge::Item.new(@database, 'customer1')
332
341
  item.link_to('also does not exist')
333
- assert_raise(RestClient::RequestFailed) { item.save }
342
+ assert_raise(RestClient::UnprocessableEntity) { item.save }
334
343
  end
335
344
 
336
345
  def test_query_parameters
@@ -414,4 +423,23 @@ class TestDirectedEdge < Test::Unit::TestCase
414
423
  assert(!customer.blacklisted.include?(first))
415
424
  assert(customer.recommended(['product']).include?(first))
416
425
  end
426
+
427
+ def test_timeout
428
+ timeout = 5
429
+ database = DirectedEdge::Database.new('dummy', 'dummy', 'http',
430
+ :host => 'localhost:4567', :timeout => timeout)
431
+ start = Time.now
432
+ timed_out = false
433
+
434
+ begin
435
+ item = DirectedEdge::Item.new(database, 'dummy')
436
+ item.tags
437
+ rescue RestClient::RequestTimeout
438
+ timed_out = true
439
+ assert(Time.now - start < timeout + 1)
440
+ rescue
441
+ end
442
+
443
+ assert(timed_out)
444
+ end
417
445
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: directed-edge
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 21
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
8
  - 2
8
- - 0
9
- version: 0.2.0
9
+ - 1
10
+ version: 0.2.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Directed Edge
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-04-25 00:00:00 +02:00
18
+ date: 2010-11-19 00:00:00 +01:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: rest-client
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 3
27
30
  segments:
28
31
  - 0
29
32
  version: "0"
@@ -53,28 +56,32 @@ homepage: http://developer.directededge.com/
53
56
  licenses: []
54
57
 
55
58
  post_install_message:
56
- rdoc_options:
57
- - --charset=UTF-8
59
+ rdoc_options: []
60
+
58
61
  require_paths:
59
62
  - lib
60
63
  required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
61
65
  requirements:
62
66
  - - ">="
63
67
  - !ruby/object:Gem::Version
68
+ hash: 3
64
69
  segments:
65
70
  - 0
66
71
  version: "0"
67
72
  required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
68
74
  requirements:
69
75
  - - ">="
70
76
  - !ruby/object:Gem::Version
77
+ hash: 3
71
78
  segments:
72
79
  - 0
73
80
  version: "0"
74
81
  requirements: []
75
82
 
76
83
  rubyforge_project:
77
- rubygems_version: 1.3.6
84
+ rubygems_version: 1.3.7
78
85
  signing_key:
79
86
  specification_version: 3
80
87
  summary: Bindings for the Directed Edge webservices API