www-delicious 0.1.0 → 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.
Files changed (55) hide show
  1. data/CHANGELOG.rdoc +41 -0
  2. data/{MIT-LICENSE → MIT-LICENSE.rdoc} +0 -0
  3. data/Manifest +49 -0
  4. data/{README → README.rdoc} +13 -14
  5. data/Rakefile +36 -118
  6. data/TODO +4 -0
  7. data/lib/www/delicious.rb +527 -446
  8. data/lib/www/delicious/bundle.rb +41 -22
  9. data/lib/www/delicious/element.rb +72 -0
  10. data/lib/www/delicious/errors.rb +2 -2
  11. data/lib/www/delicious/post.rb +71 -61
  12. data/lib/www/delicious/tag.rb +43 -62
  13. data/lib/www/delicious/version.rb +1 -1
  14. data/test/fixtures/net_response_invalid_account.yml +25 -0
  15. data/test/fixtures/net_response_success.yml +23 -0
  16. data/test/helper.rb +19 -79
  17. data/test/test_all.rb +17 -0
  18. data/test/test_offline.rb +17 -0
  19. data/test/test_online.rb +19 -0
  20. data/test/testcases/element/bundle.xml +1 -0
  21. data/test/testcases/element/invalid_root.xml +2 -0
  22. data/test/testcases/element/post.xml +2 -0
  23. data/test/testcases/element/post_unshared.xml +2 -0
  24. data/test/testcases/element/tag.xml +1 -0
  25. data/test/testcases/response/bundles_all.xml +5 -0
  26. data/test/testcases/response/bundles_all_empty.xml +2 -0
  27. data/test/testcases/response/bundles_delete.xml +2 -0
  28. data/test/testcases/response/bundles_set.xml +2 -0
  29. data/test/testcases/response/bundles_set_error.xml +2 -0
  30. data/test/testcases/response/posts_add.xml +2 -0
  31. data/test/testcases/response/posts_all.xml +12 -0
  32. data/test/testcases/response/posts_dates.xml +14 -0
  33. data/test/testcases/response/posts_dates_with_tag.xml +14 -0
  34. data/test/testcases/response/posts_delete.xml +2 -0
  35. data/test/testcases/response/posts_get.xml +7 -0
  36. data/test/testcases/response/posts_get_with_tag.xml +6 -0
  37. data/test/testcases/response/posts_recent.xml +19 -0
  38. data/test/testcases/response/posts_recent_with_tag.xml +19 -0
  39. data/test/testcases/response/tags_get.xml +5 -0
  40. data/test/testcases/response/tags_get_empty.xml +2 -0
  41. data/test/testcases/response/tags_rename.xml +2 -0
  42. data/test/testcases/response/update.delicious1.xml +2 -0
  43. data/test/testcases/response/update.xml +3 -0
  44. data/test/unit/bundle_test.rb +62 -0
  45. data/test/unit/delicious_test.rb +186 -238
  46. data/test/unit/online/online_test.rb +147 -0
  47. data/test/unit/post_test.rb +67 -0
  48. data/test/unit/tag_test.rb +68 -0
  49. data/www-delicious.gemspec +146 -0
  50. metadata +85 -31
  51. data/CHANGELOG +0 -5
  52. data/test/unit/delicious_bundle_test.rb +0 -90
  53. data/test/unit/delicious_online_test.rb +0 -143
  54. data/test/unit/delicious_post_test.rb +0 -102
  55. data/test/unit/delicious_tag_test.rb +0 -140
@@ -14,41 +14,60 @@
14
14
  #++
15
15
 
16
16
 
17
- module WWW #:nodoc:
17
+ require 'www/delicious/element'
18
+
19
+
20
+ module WWW
18
21
  class Delicious
19
22
 
20
- class Bundle
23
+ #
24
+ # = Delicious Bundle
25
+ #
26
+ # Represents a single Bundle element.
27
+ #
28
+ class Bundle < Element
21
29
 
22
- # The name of the bundle
30
+ # The name of the bundle.
23
31
  attr_accessor :name
24
32
 
25
- # The collection of <tt>WWW::Delicious::Tags</tt>
33
+ # The collection of <tt>WWW::Delicious::Tags</tt>.
26
34
  attr_accessor :tags
27
35
 
28
- public
29
- #
30
- # Creates a new <tt>WWW::Delicious::Bundle</tt> with given +name+
31
- # and adds given array of +tags+ to current tags collection.
32
- #
33
- def initialize(name, tags = [], &block) # :yields: bundle
34
- raise ArgumentError, '`tags` expected to be an Array' unless tags.kind_of?(Array)
35
-
36
- self.name = name.to_s()
37
- self.tags = tags
38
- yield(self) if block_given?
39
- self
36
+
37
+ # Returns value for <tt>name</tt> attribute.
38
+ # Value is always normalized as lower string.
39
+ def name
40
+ @name.to_s.strip unless @name.nil?
40
41
  end
41
42
 
42
- public
43
43
  #
44
- # Creates a new <tt>WWW::Delicious::Bundle</tt> from a REXML fragment.
44
+ # Returns a string representation of this Bundle.
45
+ # In case name is nil this method will return an empty string.
45
46
  #
46
- def self.from_rexml(element)
47
- return new(element.attribute_value(:name) { |v| v.to_s() },
48
- element.attribute_value(:tags) { |v| v.to_s().split(' ') })
47
+ def to_s
48
+ name.to_s
49
49
  end
50
50
 
51
+
52
+ class << self
53
+
54
+ #
55
+ # Creates and returns new instance from a REXML +element+.
56
+ #
57
+ # Implements Element#from_rexml.
58
+ #
59
+ def from_rexml(element)
60
+ raise ArgumentError, "`element` expected to be a `REXML::Element`" unless element.kind_of? REXML::Element
61
+ self.new do |instance|
62
+ instance.name = element.if_attribute_value(:name)
63
+ # FIXME: value must be converted to array of Tag
64
+ instance.tags = element.if_attribute_value(:tags) { |value| value.split(' ') }
65
+ end
66
+ end
67
+
68
+ end
69
+
51
70
  end
52
-
71
+
53
72
  end
54
73
  end
@@ -0,0 +1,72 @@
1
+ #
2
+ # = WWW::Delicious
3
+ #
4
+ # Ruby client for del.icio.us API.
5
+ #
6
+ #
7
+ # Category:: WWW
8
+ # Package:: WWW::Delicious
9
+ # Subpackage:: WWW::Delicious::Post
10
+ # Author:: Simone Carletti <weppos@weppos.net>
11
+ #
12
+ #--
13
+ # SVN: $Id$
14
+ #++
15
+
16
+
17
+ module WWW
18
+ class Delicious
19
+
20
+ #
21
+ # = Abstract structure
22
+ #
23
+ # Represent the most basic structure all Struc(s) must inherith from.
24
+ #
25
+ class Element
26
+
27
+ #
28
+ # Initializes a new instance and populate attributes from +attrs+.
29
+ #
30
+ # class User < Element
31
+ # attr_accessor :first_name
32
+ # attr_accessor :last_name
33
+ # end
34
+ #
35
+ # User.new
36
+ # User.new(:first_name => 'foo')
37
+ # User.new(:first_name => 'John', :last_name => 'Doe')
38
+ #
39
+ # You can even use a block.
40
+ # The following statements are equals:
41
+ #
42
+ # User.new(:first_name => 'John', :last_name => 'Doe')
43
+ #
44
+ # User.new do |user|
45
+ # user.first_name => 'John'
46
+ # user.last_name => 'Doe'
47
+ # end
48
+ #
49
+ # Warning. In order to set an attribute a valid attribute writer must be available,
50
+ # otherwise this method will raise an exception.
51
+ #
52
+ def initialize(attrs = {}, &block)
53
+ attrs.each { |key, value| self.send("#{key}=".to_sym, value) }
54
+ yield self if block_given?
55
+ self
56
+ end
57
+
58
+ class << self
59
+
60
+ #
61
+ # Creates and returns new instance from a REXML +element+.
62
+ #
63
+ def from_rexml(element, options)
64
+ raise NotImplementedError
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+ end
@@ -13,7 +13,7 @@
13
13
  #++
14
14
 
15
15
 
16
- module WWW #:nodoc:
16
+ module WWW
17
17
  class Delicious
18
18
 
19
19
 
@@ -39,7 +39,7 @@ module WWW #:nodoc:
39
39
  # Usually raised in case of a malformed, invalid or empty XML response.
40
40
  #
41
41
  class ResponseError < Error; end
42
-
42
+
43
43
 
44
44
  end
45
45
  end
@@ -14,82 +14,68 @@
14
14
  #++
15
15
 
16
16
 
17
- module WWW #:nodoc:
18
- class Delicious
17
+ require 'www/delicious/element'
18
+
19
19
 
20
- class Post
20
+ module WWW
21
+ class Delicious
22
+
23
+ class Post < Element
21
24
 
22
- # TODO: filter and validate
23
- attr_accessor :url, :title, :notes, :others, :uid, :tags, :time
24
- attr_accessor :replace, :shared
25
+ # The Post URL
26
+ attr_accessor :url
25
27
 
26
- public
27
- #
28
- # Creates a new <tt>WWW::Delicious::Post</tt> with given values.
29
- # If +values_or_rexml+ is a REXML element, the element is parsed
30
- # and all values assigned to this instance attributes.
31
- #
32
- def initialize(values_or_rexml, &block) # :yields: post
33
- case values_or_rexml
34
- when Hash
35
- initialize_from_hash(values_or_rexml)
36
- when REXML::Element
37
- initialize_from_rexml(values_or_rexml)
38
- else
39
- raise ArgumentError, 'Expected `values_or_rexml` to be `Hash` or `REXML::Element`'
40
- end
41
-
42
- yield(self) if block_given?
43
- self
44
- end
28
+ # The title of the Post
29
+ attr_accessor :title
45
30
 
46
- public
47
- #
48
- # Initializes <tt>WWW::Delicious::Post</tt> from an +Hash+.
49
- #
50
- def initialize_from_hash(values)
51
- %w(url title notes others udi tags time shared replace).each do |v|
52
- self.instance_variable_set("@#{v}".to_sym(), values[v.to_sym()])
53
- end
54
- self.shared = true if self.shared.nil?
55
- self.replace = true if self.replace.nil?
31
+ # The extended description for the Post
32
+ attr_accessor :notes
33
+
34
+ # The number of other users who saved this Post
35
+ attr_accessor :others
36
+
37
+ # The unique Id for this Post
38
+ attr_accessor :uid
39
+
40
+ # Tags for this Post
41
+ attr_accessor :tags
42
+
43
+ # Timestamp this Post was last saved at
44
+ attr_accessor :time
45
+
46
+ # Whether this Post must replace previous version of the same Post.
47
+ attr_accessor :replace
48
+
49
+ # Whether this Post is private
50
+ attr_accessor :shared
51
+
52
+
53
+ # Returns the value for <tt>shared</tt> attribute.
54
+ def shared
55
+ !(@shared == false)
56
56
  end
57
57
 
58
- public
59
- #
60
- # Initializes <tt>WWW::Delicious::Post</tt> from a REXML fragment.
61
- #
62
- def initialize_from_rexml(element)
63
- self.url = element.attribute_value(:href) { |v| URI.parse(v) }
64
- self.title = element.attribute_value(:description)
65
- self.notes = element.attribute_value(:extended)
66
- self.others = element.attribute_value(:others).to_i() # cast nil to 0
67
- self.uid = element.attribute_value(:hash)
68
- self.tags = element.attribute_value(:tag) { |v| v.split(' ') }.to_a()
69
- self.time = element.attribute_value(:time) { |v| Time.parse(v) }
70
- self.shared = element.attribute_value(:shared) { |v| v == 'no' ? false : true }
58
+ # Returns the value for <tt>replace</tt> attribute.
59
+ def replace
60
+ !(@replace == false)
71
61
  end
72
62
 
73
- public
74
- #
75
63
  # Returns a params-style representation suitable for API calls.
76
- #
77
64
  def to_params()
78
65
  params = {}
79
- params[:url] = self.url
80
- params[:description] = self.title
81
- params[:extended] = self.notes if self.notes
82
- params[:shared] = self.shared
83
- params[:tags] = self.tags.join(' ') if self.tags
84
- params[:replace] = self.replace
85
- params[:dt] = WWW::Delicious::TIME_CONVERTER.call(self.time) if self.time
86
- return params
66
+ params[:url] = url
67
+ params[:description] = title
68
+ params[:extended] = notes if notes
69
+ params[:shared] = shared
70
+ params[:tags] = tags.join(' ') if tags.respond_to? :join
71
+ params[:replace] = replace
72
+ params[:dt] = WWW::Delicious::TIME_CONVERTER.call(time) if time
73
+ params
87
74
  end
88
75
 
89
76
 
90
- public
91
77
  #
92
- # Returns wheter this object is valid for an API request.
78
+ # Returns whether this object is valid for an API request.
93
79
  #
94
80
  # To be valid +url+ and +title+ must not be empty.
95
81
  #
@@ -107,6 +93,30 @@ module WWW #:nodoc:
107
93
  return !(url.nil? or url.empty? or title.nil? or title.empty?)
108
94
  end
109
95
 
96
+
97
+ class << self
98
+
99
+ #
100
+ # Creates and returns new instance from a REXML +element+.
101
+ #
102
+ # Implements Element#from_rexml.
103
+ #
104
+ def from_rexml(element)
105
+ raise ArgumentError, "`element` expected to be a `REXML::Element`" unless element.kind_of? REXML::Element
106
+ self.new do |instance|
107
+ instance.url = element.if_attribute_value(:href) { |v| URI.parse(v) }
108
+ instance.title = element.if_attribute_value(:description)
109
+ instance.notes = element.if_attribute_value(:extended)
110
+ instance.others = element.if_attribute_value(:others).to_i # cast nil to 0
111
+ instance.uid = element.if_attribute_value(:hash)
112
+ instance.tags = element.if_attribute_value(:tag) { |v| v.split(' ') }.to_a
113
+ instance.time = element.if_attribute_value(:time) { |v| Time.parse(v) }
114
+ instance.shared = element.if_attribute_value(:shared) { |v| v == 'no' ? false : true }
115
+ end
116
+ end
117
+
118
+ end
119
+
110
120
  end
111
121
 
112
122
  end
@@ -14,82 +14,45 @@
14
14
  #++
15
15
 
16
16
 
17
- module WWW #:nodoc:
17
+ require 'www/delicious/element'
18
+
19
+
20
+ module WWW
18
21
  class Delicious
19
22
 
20
- class Tag
23
+ #
24
+ # = Delicious Tag
25
+ #
26
+ # Represents a single Tag element.
27
+ #
28
+ class Tag < Element
21
29
 
22
- # The name of the tag
23
- attr_reader :name
30
+ # The name of the tag.
31
+ attr_accessor :name
24
32
 
25
33
  # The number of links tagged with this tag.
26
34
  # It should be set only from an API response.
27
- attr_reader :count
28
-
29
-
30
- public
31
- #
32
- # Creates a new <tt>WWW::Delicious::Tag</tt>.
33
- #
34
- def initialize(values_or_rexml, &block) # :yields: tag
35
- case values_or_rexml
36
- when Hash
37
- initialize_from_hash(values_or_rexml)
38
- when REXML::Element
39
- initialize_from_rexml(values_or_rexml)
40
- else
41
- raise ArgumentError, 'Expected `values_or_rexml` to be `Hash` or `REXML::Element`'
42
- end
43
-
44
- yield(self) if block_given?
45
- self
46
- end
47
-
48
- public
49
- #
50
- # Initializes <tt>WWW::Delicious::Tag</tt> from a REXML fragment.
51
- #
52
- def initialize_from_rexml(element)
53
- self.name = element.attribute_value(:tag)
54
- self.count = element.attribute_value(:count)
55
- end
56
-
57
- public
58
- #
59
- # Initializes <tt>WWW::Delicious::Tag</tt> from an Hash.
60
- #
61
- def initialize_from_hash(values)
62
- self.name = values[:name]
63
- self.count = values[:count]
64
- end
35
+ attr_accessor :count
65
36
 
66
37
 
67
- public
68
- #
69
- # Sets +name+ for this instance to given +value+.
70
- # +value+ is always cast to a +String+.
71
- #
72
- # Leading and trailing whitespaces are stripped.
73
- #
74
- def name=(value)
75
- @name = value.to_s().strip()
38
+ # Returns value for <tt>name</tt> attribute.
39
+ # Value is always normalized as lower string.
40
+ def name
41
+ @name.to_s.strip unless @name.nil?
76
42
  end
77
43
 
78
- public
79
- #
80
- # Sets +count+ for this instance to given +value+.
81
- # +value+ is always cast to +Integer+.
82
- #
83
- def count=(value)
84
- @count = value.to_i()
44
+ # Returns value for <tt>count</tt> attribute.
45
+ # Value is always normalized to Fixnum.
46
+ def count
47
+ @count.to_i
85
48
  end
86
49
 
87
- public
88
50
  #
89
51
  # Returns a string representation of this Tag.
52
+ # In case name is nil this method will return an empty string.
90
53
  #
91
- def to_s()
92
- return self.name
54
+ def to_s
55
+ name.to_s
93
56
  end
94
57
 
95
58
 
@@ -111,7 +74,25 @@ module WWW #:nodoc:
111
74
  # # => false
112
75
  #
113
76
  def api_valid?
114
- return !name.empty?
77
+ return !name.empty?
78
+ end
79
+
80
+
81
+ class << self
82
+
83
+ #
84
+ # Creates and returns new instance from a REXML +element+.
85
+ #
86
+ # Implements Element#from_rexml.
87
+ #
88
+ def from_rexml(element)
89
+ raise ArgumentError, "`element` expected to be a `REXML::Element`" unless element.kind_of? REXML::Element
90
+ self.new do |instance|
91
+ instance.name = element.if_attribute_value(:tag)
92
+ instance.count = element.if_attribute_value(:count) { |value| value.to_i }
93
+ end
94
+ end
95
+
115
96
  end
116
97
 
117
98
  end