thumbtack 0.0.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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