thumbtack 0.0.3 → 1.0.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/thumbtack.rb +14 -0
  3. data/lib/thumbtack/client.rb +28 -4
  4. data/lib/thumbtack/hash_to_digest.rb +22 -0
  5. data/lib/thumbtack/note.rb +30 -7
  6. data/lib/thumbtack/note_summary.rb +27 -7
  7. data/lib/thumbtack/post.rb +45 -10
  8. data/lib/thumbtack/posts.rb +7 -2
  9. data/lib/thumbtack/specification.rb +1 -2
  10. data/lib/thumbtack/suggestion.rb +6 -0
  11. data/lib/thumbtack/tags.rb +2 -2
  12. data/lib/thumbtack/types.rb +0 -2
  13. data/lib/thumbtack/types/date.rb +1 -4
  14. data/lib/thumbtack/types/date_time.rb +11 -4
  15. data/lib/thumbtack/types/identity.rb +6 -0
  16. data/lib/thumbtack/types/integer.rb +1 -3
  17. data/lib/thumbtack/types/length_validation.rb +27 -0
  18. data/lib/thumbtack/types/range_validation.rb +30 -0
  19. data/lib/thumbtack/types/text.rb +1 -4
  20. data/lib/thumbtack/types/title.rb +1 -4
  21. data/lib/thumbtack/version.rb +1 -1
  22. data/test/test_helper.rb +7 -1
  23. data/test/thumbtack/integration/client_test.rb +7 -2
  24. data/test/thumbtack/note_summary_test.rb +2 -2
  25. data/test/thumbtack/note_test.rb +2 -2
  26. data/test/thumbtack/notes_test.rb +2 -2
  27. data/test/thumbtack/post_test.rb +6 -6
  28. data/test/thumbtack/posts_test.rb +9 -9
  29. data/test/thumbtack/tags_test.rb +3 -3
  30. data/test/thumbtack/types/boolean_test.rb +2 -2
  31. data/test/thumbtack/types/date_test.rb +2 -2
  32. data/test/thumbtack/types/date_time_test.rb +7 -2
  33. data/test/thumbtack/types/integer_test.rb +2 -2
  34. data/test/thumbtack/types/md5_test.rb +2 -2
  35. data/test/thumbtack/types/tags_test.rb +2 -2
  36. data/test/thumbtack/types/text_test.rb +1 -1
  37. data/test/thumbtack/types/title_test.rb +1 -1
  38. data/test/thumbtack/types/url_test.rb +1 -1
  39. data/test/thumbtack/user_test.rb +2 -2
  40. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: af013ae7caf72e5d517bd5ce42ef097a804fca78
4
- data.tar.gz: 35a058f9bc768ab4fadf48882993d3393dd5c627
3
+ metadata.gz: 223435e3e89fab503c5c9e5e42cb6debb6f29a5d
4
+ data.tar.gz: 769a2c552c39a0e87d35ef21c12afb475ab5029b
5
5
  SHA512:
6
- metadata.gz: 73795d3553e3e56dd4cbd6d83cb9e9e47f90750c46963f703811d7a53a6e8019ab8aebf3693f8fe675219e6a8a05adb6779bbd3e2a7af917434a8af740407c85
7
- data.tar.gz: a33adcc946c096f2e8dccfc3a34a166eec774a4006b396fd2a02a0107750424c7f7b495f3cb0be4c9437fcb7b34c53a78d7d6218d5d79a81e7e03661e6e52c8f
6
+ metadata.gz: 86c48c48a0008be9f3cea5d1143c42a7ea87915fd7a92459affc82823d26e355828942974ac9c7af81819998f6d6695c2dd26bb31eb225ef133f34802704c98e
7
+ data.tar.gz: 1a5f9d3a0d61918bcd5d4e546deb18dfe2808a281f96eec5e4ee6bfd41f15d3a739b925c55e609f3a338eea2ee7e94aed53fabfb55c34b77cd8a61c26787fb3b
@@ -2,6 +2,17 @@
2
2
 
3
3
  # A simple Pinboard API wrapper
4
4
  module Thumbtack
5
+ # Raised when the API rate limit has been reached
6
+ class RateLimitError < StandardError; end
7
+
8
+ # Raised when given an invalid parameter
9
+ class ValidationError < StandardError; end
10
+
11
+ # Raised when given an error from a Pinboard result code
12
+ #
13
+ # @see https://pinboard.in/api/#errors
14
+ class ResultError < StandardError; end
15
+
5
16
  # An empty Hash to use for default options
6
17
  EMPTY_HASH = {}.freeze
7
18
 
@@ -15,6 +26,8 @@ require 'net/http'
15
26
  require 'uri'
16
27
 
17
28
  require 'thumbtack/types'
29
+ require 'thumbtack/types/range_validation'
30
+ require 'thumbtack/types/length_validation'
18
31
  require 'thumbtack/types/boolean'
19
32
  require 'thumbtack/types/date'
20
33
  require 'thumbtack/types/date_time'
@@ -31,6 +44,7 @@ require 'thumbtack/suggestion'
31
44
  require 'thumbtack/posts'
32
45
  require 'thumbtack/tags'
33
46
  require 'thumbtack/user'
47
+ require 'thumbtack/hash_to_digest'
34
48
  require 'thumbtack/note'
35
49
  require 'thumbtack/note_summary'
36
50
  require 'thumbtack/notes'
@@ -3,9 +3,6 @@
3
3
  module Thumbtack
4
4
  # Wraps each interaction with the Pinboard API
5
5
  class Client
6
- # Raised when the API rate limit has been reached
7
- class RateLimitError < StandardError; end
8
-
9
6
  # The status code for rate limited responses from the Pinboard API
10
7
  TOO_MANY_REQUESTS_CODE = '429'.freeze
11
8
 
@@ -14,6 +11,9 @@ module Thumbtack
14
11
 
15
12
  # Username used by the client to make authenticated requests
16
13
  #
14
+ # @example
15
+ # client.username # => 'maciej'
16
+ #
17
17
  # @return [String]
18
18
  #
19
19
  # @api public
@@ -21,6 +21,9 @@ module Thumbtack
21
21
 
22
22
  # Token used by the client to make authenticated requests
23
23
  #
24
+ # @example
25
+ # client.token # => 'C9044F4047891CEA74FE'
26
+ #
24
27
  # @return [String]
25
28
  #
26
29
  # @api public
@@ -61,10 +64,31 @@ module Thumbtack
61
64
  uri.query = URI.encode_www_form(params.merge(base_params))
62
65
 
63
66
  response = Net::HTTP.get_response(uri)
64
- fail Client::RateLimitError if response.code == TOO_MANY_REQUESTS_CODE
67
+ fail RateLimitError if response.code == TOO_MANY_REQUESTS_CODE
65
68
  JSON.parse(response.body)
66
69
  end
67
70
 
71
+ # Perform an action request against the Pinboard API
72
+ #
73
+ # @param [String] path
74
+ # the path to fetch from, relative to from the base Pinboard API URL
75
+ #
76
+ # @param [Hash] params
77
+ # query parameters to append to the URL
78
+ #
79
+ # @return [Hash] the response parsed from the JSON
80
+ #
81
+ # @raise [RateLimitError] if the response is rate-limited
82
+ # @raise [ResultError] if the result code isn't "done"
83
+ #
84
+ # @api private
85
+ # @see https://pinboard.in/api/#errors
86
+ def action(path, params = EMPTY_HASH)
87
+ response = get(path, params)
88
+ fail ResultError, response['result'] unless response['result'] == 'done'
89
+ self
90
+ end
91
+
68
92
  # Access posts-related API calls
69
93
  #
70
94
  # @example
@@ -0,0 +1,22 @@
1
+ module Thumbtack
2
+ # Handles renaming the hash attribute to digest from response hashes.
3
+ #
4
+ # @api private
5
+ class HashToDigest
6
+ # Rename any attribute called hash to digest
7
+ #
8
+ # @example
9
+ # HashToDigest.rename('hash' => '1234') # => { :digest => '1234' }
10
+ #
11
+ # @param [Hash{String => Object}]
12
+ # reponse hash
13
+ #
14
+ # @return [Hash{Symbol => Object}]
15
+ # a hash with any key named hash renamed to digest
16
+ def self.rename(hash)
17
+ attrs = hash.dup
18
+ digest = attrs.delete('hash')
19
+ Hash[attrs.map { |k, v| [k.to_sym, v] }].merge(digest: digest)
20
+ end
21
+ end
22
+ end
@@ -20,6 +20,9 @@ module Thumbtack
20
20
 
21
21
  # The identifier for the note
22
22
  #
23
+ # @example
24
+ # note.id # => '8e5d6964bb810e0050b0'
25
+ #
23
26
  # @return [String]
24
27
  #
25
28
  # @api public
@@ -27,6 +30,9 @@ module Thumbtack
27
30
 
28
31
  # The title of the note
29
32
  #
33
+ # @example
34
+ # note.title # => 'StarCraft beta coming this week!'
35
+ #
30
36
  # @return [String]
31
37
  #
32
38
  # @api public
@@ -34,20 +40,33 @@ module Thumbtack
34
40
 
35
41
  # The time at which the note was created
36
42
  #
37
- # @return [String]
43
+ # @example
44
+ # note.created_at # => #<DateTime: 2014-08-13T19:53:16+00:00...
45
+ #
46
+ # @return [DateTime]
38
47
  #
39
48
  # @api public
40
- attr_reader :created_at
49
+ def created_at
50
+ Types::DateTime.from_note_parameter(@created_at)
51
+ end
41
52
 
42
53
  # The time at which the note was last updated
43
54
  #
44
- # @return [String]
55
+ # @example
56
+ # note.updated_at # => #<DateTime: 2014-08-13T19:53:16+00:00...
57
+ #
58
+ # @return [DateTime]
45
59
  #
46
60
  # @api public
47
- attr_reader :updated_at
61
+ def updated_at
62
+ Types::DateTime.from_note_parameter(@updated_at)
63
+ end
48
64
 
49
65
  # 20 character hexadecimal SHA1 hash of the note text
50
66
  #
67
+ # @example
68
+ # note.digest # => '0c9c30f60cadabd31415'
69
+ #
51
70
  # @return [String]
52
71
  #
53
72
  # @api public
@@ -55,6 +74,9 @@ module Thumbtack
55
74
 
56
75
  # The text of the note
57
76
  #
77
+ # @example
78
+ # note.text # => 'It was clear that readers showing up...
79
+ #
58
80
  # @return [String]
59
81
  #
60
82
  # @api public
@@ -62,6 +84,9 @@ module Thumbtack
62
84
 
63
85
  # The length of the note text
64
86
  #
87
+ # @example
88
+ # note.length # => 153
89
+ #
65
90
  # @return [Integer]
66
91
  #
67
92
  # @api public
@@ -77,9 +102,7 @@ module Thumbtack
77
102
  # @api private
78
103
  # @see Client#get
79
104
  def self.from_hash(hash)
80
- attrs = hash.dup
81
- digest = attrs.delete('hash')
82
- new(Hash[attrs.map { |k, v| [k.to_sym, v] }].merge(digest: digest))
105
+ new(HashToDigest.rename(hash))
83
106
  end
84
107
 
85
108
  # Initialize a Note
@@ -20,6 +20,9 @@ module Thumbtack
20
20
 
21
21
  # The identifier for the note
22
22
  #
23
+ # @example
24
+ # note.id # => '8e5d6964bb810e0050b0'
25
+ #
23
26
  # @return [String]
24
27
  #
25
28
  # @api public
@@ -27,6 +30,9 @@ module Thumbtack
27
30
 
28
31
  # The title of the note
29
32
  #
33
+ # @example
34
+ # note.title # => 'StarCraft beta coming this week!'
35
+ #
30
36
  # @return [String]
31
37
  #
32
38
  # @api public
@@ -34,20 +40,33 @@ module Thumbtack
34
40
 
35
41
  # The time at which the note was created
36
42
  #
37
- # @return [String]
43
+ # @example
44
+ # note.created_at # => #<DateTime: 2014-08-13T19:53:16+00:00...
45
+ #
46
+ # @return [DateTime]
38
47
  #
39
48
  # @api public
40
- attr_reader :created_at
49
+ def created_at
50
+ Types::DateTime.from_note_parameter(@created_at)
51
+ end
41
52
 
42
53
  # The time at which the note was last updated
43
54
  #
44
- # @return [String]
55
+ # @example
56
+ # note.updated_at # => #<DateTime: 2014-08-13T19:53:16+00:00...
57
+ #
58
+ # @return [DateTime]
45
59
  #
46
60
  # @api public
47
- attr_reader :updated_at
61
+ def updated_at
62
+ Types::DateTime.from_note_parameter(@updated_at)
63
+ end
48
64
 
49
65
  # 20 character hexadecimal SHA1 hash of the note text
50
66
  #
67
+ # @example
68
+ # note.digest # => '0c9c30f60cadabd31415'
69
+ #
51
70
  # @return [String]
52
71
  #
53
72
  # @api public
@@ -55,6 +74,9 @@ module Thumbtack
55
74
 
56
75
  # The length of the note text
57
76
  #
77
+ # @example
78
+ # note.length # => 153
79
+ #
58
80
  # @return [Integer]
59
81
  #
60
82
  # @api public
@@ -70,9 +92,7 @@ module Thumbtack
70
92
  # @api private
71
93
  # @see Client#get
72
94
  def self.from_hash(hash)
73
- attrs = hash.dup
74
- digest = attrs.delete('hash')
75
- new(Hash[attrs.map { |k, v| [k.to_sym, v] }].merge(digest: digest))
95
+ new(HashToDigest.rename(hash))
76
96
  end
77
97
 
78
98
  # Initialize a NoteSummary
@@ -13,7 +13,7 @@ module Thumbtack
13
13
  :description,
14
14
  :extended,
15
15
  :meta,
16
- :hash,
16
+ :digest,
17
17
  :time,
18
18
  :shared,
19
19
  :toread,
@@ -22,6 +22,9 @@ module Thumbtack
22
22
 
23
23
  # The url of the post
24
24
  #
25
+ # @example
26
+ # post.href # => 'http://pinboard.in'
27
+ #
25
28
  # @return [String]
26
29
  #
27
30
  # @api public
@@ -29,6 +32,9 @@ module Thumbtack
29
32
 
30
33
  # The title of the post
31
34
  #
35
+ # @example
36
+ # post.description # => 'Pinboard'
37
+ #
32
38
  # @return [String]
33
39
  #
34
40
  # @api public
@@ -36,12 +42,18 @@ module Thumbtack
36
42
 
37
43
  # The description of the post
38
44
  #
45
+ # @example
46
+ # post.extended # => 'antisocial bookmarking'
47
+ #
39
48
  # @return [String]
40
49
  #
41
50
  # @api public
42
51
  attr_reader :extended
43
52
 
44
- # 32 character hexadecimal MD5 hash tag 'version' of the post
53
+ # 32 character hexadecimal MD5 hash change signature for the post
54
+ #
55
+ # @example
56
+ # post.meta # => '92959a96fd69146c5fe7cbde6e5720f2'
45
57
  #
46
58
  # @return [String]
47
59
  #
@@ -50,38 +62,61 @@ module Thumbtack
50
62
 
51
63
  # 32 character hexadecimal MD5 hash of the post URL
52
64
  #
65
+ # @example
66
+ # post.digest # => '92959a96fd69146c5fe7cbde6e5720f2'
67
+ #
53
68
  # @return [String]
54
69
  #
55
70
  # @api public
56
- attr_reader :hash
71
+ attr_reader :digest
57
72
 
58
73
  # The time at which the post was created
59
74
  #
60
- # @return [String]
75
+ # @example
76
+ # post.time # => #<DateTime: 2014-09-17T13:45:27+00:00...
77
+ #
78
+ # @return [DateTime]
61
79
  #
62
80
  # @api public
63
- attr_reader :time
81
+ def time
82
+ Types::DateTime.from_parameter(@time)
83
+ end
64
84
 
65
85
  # If true, this post is public
66
86
  #
87
+ # @example
88
+ # post.shared # => true
89
+ #
67
90
  # @return [Boolean]
68
91
  #
69
92
  # @api public
70
- attr_reader :shared
93
+ def shared
94
+ Types::Boolean.from_parameter(@shared)
95
+ end
71
96
 
72
97
  # If true, this post is marked unread
73
98
  #
99
+ # @example
100
+ # post.toread # => false
101
+ #
74
102
  # @return [Boolean]
75
103
  #
76
104
  # @api public
77
- attr_reader :toread
105
+ def toread
106
+ Types::Boolean.from_parameter(@toread)
107
+ end
78
108
 
79
109
  # The tags for this post, space-seperated
80
110
  #
81
- # @return [String]
111
+ # @example
112
+ # post.tags # => ['bookmarks', 'tools']
113
+ #
114
+ # @return [Array<String>]
82
115
  #
83
116
  # @api public
84
- attr_reader :tags
117
+ def tags
118
+ Types::Tags.from_parameter(@tags)
119
+ end
85
120
 
86
121
  # Creates a new Post from a Hash
87
122
  #
@@ -93,7 +128,7 @@ module Thumbtack
93
128
  # @api private
94
129
  # @see Client#get
95
130
  def self.from_hash(hash)
96
- new(Hash[hash.map { |key, value| [key.to_sym, value] }])
131
+ new(HashToDigest.rename(hash))
97
132
  end
98
133
 
99
134
  # Initialize a Post
@@ -68,7 +68,7 @@ module Thumbtack
68
68
  shared: Types::Boolean,
69
69
  toread: Types::Boolean
70
70
  ).parameters({ url: url, description: description }.merge(options))
71
- @client.get('/posts/add', parameters)
71
+ @client.action('/posts/add', parameters)
72
72
  self
73
73
  end
74
74
 
@@ -87,7 +87,7 @@ module Thumbtack
87
87
  # @see https://pinboard.in/api/#posts_delete
88
88
  def delete(url)
89
89
  parameters = Specification.new(url: Types::URL).parameters(url: url)
90
- @client.get('/posts/delete', parameters)
90
+ @client.action('/posts/delete', parameters)
91
91
  self
92
92
  end
93
93
 
@@ -235,6 +235,11 @@ module Thumbtack
235
235
 
236
236
  private
237
237
 
238
+ # Create Post objects from posts response
239
+ #
240
+ # @return [Array<Post>]
241
+ #
242
+ # @api private
238
243
  def posts_from(response)
239
244
  response.fetch('posts', EMPTY_ARRAY).map do |post_hash|
240
245
  Post.from_hash(post_hash)
@@ -14,8 +14,7 @@ module Thumbtack
14
14
  @type_handlers = type_handlers
15
15
  end
16
16
 
17
- # Validate and translate user-provided parameters to their
18
- # Pinboard-supported values
17
+ # Validate and translate client parameters to their Pinboard values
19
18
  #
20
19
  # @param [Hash{Symbol => Object}] arguments
21
20
  # parameter names associated with their values
@@ -17,6 +17,9 @@ module Thumbtack
17
17
 
18
18
  # A list of popular tags for URL
19
19
  #
20
+ # @example
21
+ # suggestion.popular # => ['blog', 'blogs', 'people']
22
+ #
20
23
  # @return [Array<String>]
21
24
  #
22
25
  # @api public
@@ -24,6 +27,9 @@ module Thumbtack
24
27
 
25
28
  # A list of recommended tags for the URL
26
29
  #
30
+ # @example
31
+ # suggestion.recommended # => ['blog', 'blogs', 'writing'...
32
+ #
27
33
  # @return [Array<String>]
28
34
  #
29
35
  # @api public
@@ -44,7 +44,7 @@ module Thumbtack
44
44
  # @see https://pinboard.in/api/#tags_delete
45
45
  def delete(tag)
46
46
  parameters = Specification.new(tag: Types::Tags).parameters(tag: tag)
47
- @client.get('/tags/delete', parameters)
47
+ @client.action('/tags/delete', parameters)
48
48
  self
49
49
  end
50
50
 
@@ -68,7 +68,7 @@ module Thumbtack
68
68
  old: Types::Tags,
69
69
  new: Types::Tags
70
70
  ).parameters(old: old, new: new)
71
- @client.get('/tags/rename', parameters)
71
+ @client.action('/tags/rename', parameters)
72
72
  self
73
73
  end
74
74
  end
@@ -5,7 +5,5 @@ module Thumbtack
5
5
  #
6
6
  # @see https://pinboard.in/api/#data
7
7
  module Types
8
- # Raised when given an argument that does not satisfy the type constraints
9
- class ValidationError < StandardError; end
10
8
  end
11
9
  end
@@ -22,10 +22,7 @@ module Thumbtack
22
22
  # @raise [Types::ValidationError]
23
23
  # if the date is not between 0001-01-01 and 2100-01-01
24
24
  def self.validate(value)
25
- unless value > EARLIEST && value < LATEST
26
- fail ValidationError,
27
- "#{value} must be between 0001-01-01 and 2100-01-01"
28
- end
25
+ RangeValidation.validate value, EARLIEST..LATEST
29
26
  self
30
27
  end
31
28
 
@@ -24,10 +24,7 @@ module Thumbtack
24
24
  # @raise [Types::ValidationError]
25
25
  # if the time is not between 0001-01-01 00:00:00 and 2100-01-01 00:00:00
26
26
  def self.validate(value)
27
- unless value > EARLIEST && value < LATEST
28
- fail ValidationError,
29
- "#{value} must be between 0001-01-01 and 2100-01-01"
30
- end
27
+ RangeValidation.validate value, EARLIEST..LATEST
31
28
  self
32
29
  end
33
30
 
@@ -51,6 +48,16 @@ module Thumbtack
51
48
  def self.from_parameter(parameter)
52
49
  ::DateTime.strptime(parameter)
53
50
  end
51
+
52
+ # Convert a parameter from Pinboard's notes to a datetime value
53
+ #
54
+ # @param [String] parameter
55
+ # the time formatted yyyy-mm-dd HH:MM:SS
56
+ #
57
+ # @return [DateTime]
58
+ def self.from_note_parameter(parameter)
59
+ ::DateTime.strptime(parameter, '%Y-%m-%d %H:%M:%S')
60
+ end
54
61
  end
55
62
  end
56
63
  end
@@ -15,6 +15,9 @@ module Thumbtack
15
15
 
16
16
  # Value is returned unconverted
17
17
  #
18
+ # @param [Object] value
19
+ # the value to return
20
+ #
18
21
  # @return [value]
19
22
  def self.to_parameter(value)
20
23
  value
@@ -22,6 +25,9 @@ module Thumbtack
22
25
 
23
26
  # Parameter is returned unconverted
24
27
  #
28
+ # @param [Object] parameter
29
+ # the parameter to return
30
+ #
25
31
  # @return [parameter]
26
32
  def self.from_parameter(parameter)
27
33
  parameter
@@ -21,9 +21,7 @@ module Thumbtack
21
21
  # @raise [Types::ValidationError]
22
22
  # if the value is not between 0 and 2^32
23
23
  def self.validate(value)
24
- unless value >= MIN && value <= MAX
25
- fail ValidationError, "#{value} must be in range 0..2^32"
26
- end
24
+ RangeValidation.validate value, MIN..MAX
27
25
  self
28
26
  end
29
27
  end
@@ -0,0 +1,27 @@
1
+ module Thumbtack
2
+ module Types
3
+ # Handles validation of values within a certain range
4
+ # Pinboard
5
+ #
6
+ # @api private
7
+ class LengthValidation
8
+ # Validate a value
9
+ #
10
+ # @param [Object] length
11
+ # the maximum length
12
+ # @param [Object] value
13
+ # the value to validate
14
+ #
15
+ # @return [undefined]
16
+ #
17
+ # @raise [Types::ValidationError]
18
+ # if the value is not between 0001-01-01 and 2100-01-01
19
+ def self.validate(value, length)
20
+ unless value.length <= length
21
+ fail ValidationError,
22
+ "#{value} cannot be greater than #{length} characters"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,30 @@
1
+ module Thumbtack
2
+ module Types
3
+ # Handles validation of values within a certain range
4
+ # Pinboard
5
+ #
6
+ # @api private
7
+ class RangeValidation
8
+ # Validate a value
9
+ #
10
+ # @example
11
+ # RangeValidation.validate((1..2), 3) # => ValidationError raised
12
+ #
13
+ # @param [Object] value
14
+ # the value to validate
15
+ # @param [Range] range
16
+ # the range of valid values
17
+ #
18
+ # @return [undefined]
19
+ #
20
+ # @raise [Types::ValidationError]
21
+ # if the value is not between 0001-01-01 and 2100-01-01
22
+ def self.validate(value, range)
23
+ unless range.cover?(value)
24
+ fail ValidationError,
25
+ "#{value} must be between #{range.begin} and #{range.end}"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -19,10 +19,7 @@ module Thumbtack
19
19
  # @raise [Types::ValidationError]
20
20
  # if the value is longer than 65536 characters
21
21
  def self.validate(value)
22
- unless value.length <= MAXIMUM_LENGTH
23
- fail ValidationError,
24
- "#{value} cannot be greater than #{MAXIMUM_LENGTH} characters"
25
- end
22
+ LengthValidation.validate value, MAXIMUM_LENGTH
26
23
  self
27
24
  end
28
25
  end
@@ -19,10 +19,7 @@ module Thumbtack
19
19
  # @raise [Types::ValidationError]
20
20
  # if the value is longer than 255 characters
21
21
  def self.validate(value)
22
- unless value.length <= MAXIMUM_LENGTH
23
- fail ValidationError,
24
- "#{value} cannot be greater than #{MAXIMUM_LENGTH} characters"
25
- end
22
+ LengthValidation.validate value, MAXIMUM_LENGTH
26
23
  self
27
24
  end
28
25
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Thumbtack
4
4
  # Gem version
5
- VERSION = '0.0.3'.freeze
5
+ VERSION = '1.0.0'.freeze
6
6
  end
@@ -5,10 +5,16 @@ CodeClimate::TestReporter.start
5
5
  require 'thumbtack'
6
6
  require 'minitest/autorun'
7
7
 
8
- def mock_client(url, params, response)
8
+ def mock_client_get(url, params, response)
9
9
  client = Minitest::Mock.new
10
10
  client.expect(:get, response, [url, params].compact)
11
11
  client
12
12
  end
13
13
 
14
+ def mock_client_action(url, params, response)
15
+ client = Minitest::Mock.new
16
+ client.expect(:action, response, [url, params].compact)
17
+ client
18
+ end
19
+
14
20
  include Thumbtack
@@ -17,8 +17,13 @@ module Integration
17
17
  end
18
18
 
19
19
  def test_request_query_parameters
20
- response = @client.get('/tags/delete', tag: 'thumbtack-test-query-params')
21
- assert_equal({ 'result' => 'done' }, response)
20
+ response = @client.get('/posts/recent', tag: 'thumbtack-test-query-params')
21
+ assert_equal([] , response['posts'])
22
+ end
23
+
24
+ def test_action_command_method
25
+ response = @client.action('/tags/delete', tag: 'thumbtack-test-command')
26
+ assert_equal(@client, response)
22
27
  end
23
28
  end
24
29
  end
@@ -17,8 +17,8 @@ class NoteSummaryTest < Minitest::Test
17
17
  assert_equal '8e5d6964bb810e0050b0', note.id
18
18
  assert_equal 'StarCraft beta coming this week!', note.title
19
19
  assert_equal '0c9c30f60cadabd31415', note.digest
20
- assert_equal '2010-02-11 03:46:56', note.created_at
21
- assert_equal '2010-02-11 03:47:47', note.updated_at
20
+ assert_equal DateTime.new(2010, 2, 11, 3, 46, 56), note.created_at
21
+ assert_equal DateTime.new(2010, 2, 11, 3, 47, 47), note.updated_at
22
22
  assert_equal 19, note.length
23
23
  end
24
24
  end
@@ -18,8 +18,8 @@ class NoteTest < Minitest::Test
18
18
  assert_equal '8e5d6964bb810e0050b0', note.id
19
19
  assert_equal 'StarCraft beta coming this week!', note.title
20
20
  assert_equal '0c9c30f60cadabd31415', note.digest
21
- assert_equal '2010-02-11 03:46:56', note.created_at
22
- assert_equal '2010-02-11 03:47:47', note.updated_at
21
+ assert_equal DateTime.new(2010, 2, 11, 3, 46, 56), note.created_at
22
+ assert_equal DateTime.new(2010, 2, 11, 3, 47, 47), note.updated_at
23
23
  assert_equal 19, note.length
24
24
  assert_equal 'This is a test note', note.text
25
25
  end
@@ -4,7 +4,7 @@ require 'test_helper'
4
4
 
5
5
  class NotesTest < Minitest::Test
6
6
  def test_list
7
- client = mock_client('/notes/list',
7
+ client = mock_client_get('/notes/list',
8
8
  nil,
9
9
  {
10
10
  'count' => 2,
@@ -36,7 +36,7 @@ class NotesTest < Minitest::Test
36
36
  end
37
37
 
38
38
  def test_get
39
- client = mock_client('/notes/8e5d6964bb810e0050b0',
39
+ client = mock_client_get('/notes/8e5d6964bb810e0050b0',
40
40
  nil,
41
41
  {
42
42
  'id' => '8e5d6964bb810e0050b0',
@@ -13,7 +13,7 @@ class PostTest < Minitest::Test
13
13
  'time' => '2014-06-29T16:57:45Z',
14
14
  'shared' => 'yes',
15
15
  'toread' => 'no',
16
- 'tags' => 'test123'
16
+ 'tags' => 'test123 another'
17
17
  }
18
18
  post = Post.from_hash(hash)
19
19
 
@@ -21,10 +21,10 @@ class PostTest < Minitest::Test
21
21
  assert_equal 'example.org', post.description
22
22
  assert_equal '', post.extended
23
23
  assert_equal '46ca40b9b92ee0ea1284785a5d2a9b38', post.meta
24
- assert_equal 'dab521de65f9250b4cca7383feef67dc', post.hash
25
- assert_equal '2014-06-29T16:57:45Z', post.time
26
- assert_equal 'yes', post.shared
27
- assert_equal 'no', post.toread
28
- assert_equal 'test123', post.tags
24
+ assert_equal 'dab521de65f9250b4cca7383feef67dc', post.digest
25
+ assert_equal DateTime.new(2014, 6, 29, 16, 57, 45), post.time
26
+ assert_equal true, post.shared
27
+ assert_equal false, post.toread
28
+ assert_equal ['test123', 'another'], post.tags
29
29
  end
30
30
  end
@@ -4,7 +4,7 @@ require 'test_helper'
4
4
 
5
5
  class PostsTest < Minitest::Test
6
6
  def test_update
7
- client = mock_client('/posts/update',
7
+ client = mock_client_get('/posts/update',
8
8
  nil,
9
9
  { 'update_time' => '2014-06-26T19:01:33Z' })
10
10
  posts = Posts.new(client)
@@ -14,7 +14,7 @@ class PostsTest < Minitest::Test
14
14
  end
15
15
 
16
16
  def test_add
17
- client = mock_client('/posts/add',
17
+ client = mock_client_action('/posts/add',
18
18
  { url: 'http://example.org',
19
19
  description: 'example.org' },
20
20
  { 'result_code' => 'done' })
@@ -25,7 +25,7 @@ class PostsTest < Minitest::Test
25
25
  end
26
26
 
27
27
  def test_add_with_tags
28
- client = mock_client('/posts/add',
28
+ client = mock_client_action('/posts/add',
29
29
  { url: 'http://example.org',
30
30
  description: 'example.org',
31
31
  tags: 'thumbtack test' },
@@ -38,7 +38,7 @@ class PostsTest < Minitest::Test
38
38
  end
39
39
 
40
40
  def test_delete
41
- client = mock_client('/posts/delete',
41
+ client = mock_client_action('/posts/delete',
42
42
  { url: 'http://example.org' },
43
43
  { 'result_code' => 'done' })
44
44
  posts = Posts.new(client)
@@ -48,7 +48,7 @@ class PostsTest < Minitest::Test
48
48
  end
49
49
 
50
50
  def test_get
51
- client = mock_client('/posts/get',
51
+ client = mock_client_get('/posts/get',
52
52
  { url: 'http://example.org' },
53
53
  {
54
54
  'date' => '2014-06-29T16:57:45Z',
@@ -74,7 +74,7 @@ class PostsTest < Minitest::Test
74
74
  end
75
75
 
76
76
  def test_recent
77
- client = mock_client('/posts/recent',
77
+ client = mock_client_get('/posts/recent',
78
78
  {tag: 'webdev'},
79
79
  {
80
80
  'date' => '2014-06-29T16:57:45Z',
@@ -100,7 +100,7 @@ class PostsTest < Minitest::Test
100
100
  end
101
101
 
102
102
  def test_all
103
- client = mock_client('/posts/all',
103
+ client = mock_client_get('/posts/all',
104
104
  {tag: 'webdev'},
105
105
  [{
106
106
  'href' => 'http://example.org',
@@ -122,7 +122,7 @@ class PostsTest < Minitest::Test
122
122
  end
123
123
 
124
124
  def test_suggest
125
- client = mock_client('/posts/suggest',
125
+ client = mock_client_get('/posts/suggest',
126
126
  {url: 'http://blog.com'},
127
127
  [
128
128
  { 'popular' => ['blog', 'blogs', 'people'] },
@@ -137,7 +137,7 @@ class PostsTest < Minitest::Test
137
137
  end
138
138
 
139
139
  def test_dates
140
- client = mock_client('/posts/dates',
140
+ client = mock_client_get('/posts/dates',
141
141
  {tag: 'argentina'},
142
142
  {
143
143
  'user' => 'user',
@@ -4,7 +4,7 @@ require 'test_helper'
4
4
 
5
5
  class TagsTest < Minitest::Test
6
6
  def test_update
7
- client = mock_client('/tags/get',
7
+ client = mock_client_get('/tags/get',
8
8
  nil,
9
9
  {
10
10
  'activedesktop' => '1',
@@ -29,7 +29,7 @@ class TagsTest < Minitest::Test
29
29
  end
30
30
 
31
31
  def test_delete
32
- client = mock_client('/tags/delete',
32
+ client = mock_client_action('/tags/delete',
33
33
  { tag: 'argentina' },
34
34
  { 'result_code' => 'done' })
35
35
  tags = Tags.new(client)
@@ -39,7 +39,7 @@ class TagsTest < Minitest::Test
39
39
  end
40
40
 
41
41
  def test_rename
42
- client = mock_client('/tags/rename',
42
+ client = mock_client_action('/tags/rename',
43
43
  { old: 'argentina', new: 'evita' },
44
44
  { 'result_code' => 'done' })
45
45
  tags = Tags.new(client)
@@ -7,11 +7,11 @@ class BooleanTest < Minitest::Test
7
7
  assert_equal Types::Boolean, Types::Boolean.validate(true)
8
8
  assert_equal Types::Boolean, Types::Boolean.validate(false)
9
9
 
10
- assert_raises(Types::ValidationError) do
10
+ assert_raises(ValidationError) do
11
11
  Types::Boolean.validate('nope')
12
12
  end
13
13
 
14
- assert_raises(Types::ValidationError) do
14
+ assert_raises(ValidationError) do
15
15
  Types::Boolean.validate(nil)
16
16
  end
17
17
  end
@@ -6,11 +6,11 @@ class DateTest < Minitest::Test
6
6
  def test_validate
7
7
  assert_equal Types::Date, Types::Date.validate(::Date.today)
8
8
 
9
- assert_raises(Types::ValidationError) do
9
+ assert_raises(ValidationError) do
10
10
  Types::Date.validate ::Date.new(0, 1, 1)
11
11
  end
12
12
 
13
- assert_raises(Types::ValidationError) do
13
+ assert_raises(ValidationError) do
14
14
  Types::Date.validate ::Date.new(2101, 1, 1)
15
15
  end
16
16
  end
@@ -6,11 +6,11 @@ class DateTimeTest < Minitest::Test
6
6
  def test_validate
7
7
  assert_equal Types::DateTime, Types::DateTime.validate(::DateTime.now)
8
8
 
9
- assert_raises(Types::ValidationError) do
9
+ assert_raises(ValidationError) do
10
10
  Types::DateTime.validate ::DateTime.new(0, 1, 1)
11
11
  end
12
12
 
13
- assert_raises(Types::ValidationError) do
13
+ assert_raises(ValidationError) do
14
14
  Types::DateTime.validate ::DateTime.new(2101, 1, 1)
15
15
  end
16
16
  end
@@ -24,4 +24,9 @@ class DateTimeTest < Minitest::Test
24
24
  assert_equal ::DateTime.new(2010, 12, 11, 19, 48, 2),
25
25
  Types::DateTime.from_parameter('2010-12-11T19:48:02Z')
26
26
  end
27
+
28
+ def test_from_note_parameter
29
+ assert_equal ::DateTime.new(2010, 12, 11, 19, 48, 2),
30
+ Types::DateTime.from_note_parameter('2010-12-11 19:48:02')
31
+ end
27
32
  end
@@ -7,11 +7,11 @@ class IntegerTest < Minitest::Test
7
7
  assert_equal Types::Integer, Types::Integer.validate(0)
8
8
  assert_equal Types::Integer, Types::Integer.validate(2**32)
9
9
 
10
- assert_raises(Types::ValidationError) do
10
+ assert_raises(ValidationError) do
11
11
  Types::Integer.validate(-1)
12
12
  end
13
13
 
14
- assert_raises(Types::ValidationError) do
14
+ assert_raises(ValidationError) do
15
15
  Types::Integer.validate(2**32 + 1)
16
16
  end
17
17
  end
@@ -7,11 +7,11 @@ class MD5Test < Minitest::Test
7
7
  assert_equal Types::MD5,
8
8
  Types::MD5.validate('437b930db84b8079c2dd804a71936b5f')
9
9
 
10
- assert_raises(Types::ValidationError) do
10
+ assert_raises(ValidationError) do
11
11
  Types::MD5.validate('0' * 33)
12
12
  end
13
13
 
14
- assert_raises(Types::ValidationError) do
14
+ assert_raises(ValidationError) do
15
15
  Types::MD5.validate('gggggggggggggggggggggggggggggggg')
16
16
  end
17
17
  end
@@ -8,11 +8,11 @@ class TagsTest < Minitest::Test
8
8
  assert_equal Types::Tags, Types::Tags.validate(['argentina'])
9
9
  assert_equal Types::Tags, Types::Tags.validate(['.private'])
10
10
 
11
- assert_raises(Types::ValidationError) do
11
+ assert_raises(ValidationError) do
12
12
  Types::Tags.validate [('x' * 256)]
13
13
  end
14
14
 
15
- assert_raises(Types::ValidationError) do
15
+ assert_raises(ValidationError) do
16
16
  Types::Tags.validate ['comma,']
17
17
  end
18
18
  end
@@ -7,7 +7,7 @@ class TextTest < Minitest::Test
7
7
  assert_equal Types::Text, Types::Text.validate('')
8
8
  assert_equal Types::Text, Types::Text.validate('x' * 65536)
9
9
 
10
- assert_raises(Types::ValidationError) do
10
+ assert_raises(ValidationError) do
11
11
  Types::Text.validate('x' * 65537)
12
12
  end
13
13
  end
@@ -7,7 +7,7 @@ class TitleTest < Minitest::Test
7
7
  assert_equal Types::Title, Types::Title.validate('')
8
8
  assert_equal Types::Title, Types::Title.validate('x' * 255)
9
9
 
10
- assert_raises(Types::ValidationError) do
10
+ assert_raises(ValidationError) do
11
11
  Types::Title.validate('x' * 256)
12
12
  end
13
13
  end
@@ -6,7 +6,7 @@ class URLTest < Minitest::Test
6
6
  def test_validate
7
7
  assert_equal Types::URL, Types::URL.validate('http://pinboard.in')
8
8
 
9
- assert_raises(Types::ValidationError) do
9
+ assert_raises(ValidationError) do
10
10
  Types::URL.validate('scp://pinboard.in')
11
11
  end
12
12
  end
@@ -4,7 +4,7 @@ require 'test_helper'
4
4
 
5
5
  class UserTest < Minitest::Test
6
6
  def test_secret
7
- client = mock_client('/user/secret',
7
+ client = mock_client_get('/user/secret',
8
8
  nil,
9
9
  { 'result' => '6493a84f72d86e7de130' })
10
10
  user = User.new(client)
@@ -13,7 +13,7 @@ class UserTest < Minitest::Test
13
13
  end
14
14
 
15
15
  def test_api_token
16
- client = mock_client('/user/api_token',
16
+ client = mock_client_get('/user/api_token',
17
17
  nil,
18
18
  { 'result' => 'XOG86E7JIYMI' })
19
19
  user = User.new(client)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thumbtack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-29 00:00:00.000000000 Z
11
+ date: 2014-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,6 +75,7 @@ extra_rdoc_files: []
75
75
  files:
76
76
  - lib/thumbtack.rb
77
77
  - lib/thumbtack/client.rb
78
+ - lib/thumbtack/hash_to_digest.rb
78
79
  - lib/thumbtack/note.rb
79
80
  - lib/thumbtack/note_summary.rb
80
81
  - lib/thumbtack/notes.rb
@@ -89,7 +90,9 @@ files:
89
90
  - lib/thumbtack/types/date_time.rb
90
91
  - lib/thumbtack/types/identity.rb
91
92
  - lib/thumbtack/types/integer.rb
93
+ - lib/thumbtack/types/length_validation.rb
92
94
  - lib/thumbtack/types/md5.rb
95
+ - lib/thumbtack/types/range_validation.rb
93
96
  - lib/thumbtack/types/tags.rb
94
97
  - lib/thumbtack/types/text.rb
95
98
  - lib/thumbtack/types/title.rb