thumbtack 0.0.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/thumbtack.rb +14 -0
- data/lib/thumbtack/client.rb +28 -4
- data/lib/thumbtack/hash_to_digest.rb +22 -0
- data/lib/thumbtack/note.rb +30 -7
- data/lib/thumbtack/note_summary.rb +27 -7
- data/lib/thumbtack/post.rb +45 -10
- data/lib/thumbtack/posts.rb +7 -2
- data/lib/thumbtack/specification.rb +1 -2
- data/lib/thumbtack/suggestion.rb +6 -0
- data/lib/thumbtack/tags.rb +2 -2
- data/lib/thumbtack/types.rb +0 -2
- data/lib/thumbtack/types/date.rb +1 -4
- data/lib/thumbtack/types/date_time.rb +11 -4
- data/lib/thumbtack/types/identity.rb +6 -0
- data/lib/thumbtack/types/integer.rb +1 -3
- data/lib/thumbtack/types/length_validation.rb +27 -0
- data/lib/thumbtack/types/range_validation.rb +30 -0
- data/lib/thumbtack/types/text.rb +1 -4
- data/lib/thumbtack/types/title.rb +1 -4
- data/lib/thumbtack/version.rb +1 -1
- data/test/test_helper.rb +7 -1
- data/test/thumbtack/integration/client_test.rb +7 -2
- data/test/thumbtack/note_summary_test.rb +2 -2
- data/test/thumbtack/note_test.rb +2 -2
- data/test/thumbtack/notes_test.rb +2 -2
- data/test/thumbtack/post_test.rb +6 -6
- data/test/thumbtack/posts_test.rb +9 -9
- data/test/thumbtack/tags_test.rb +3 -3
- data/test/thumbtack/types/boolean_test.rb +2 -2
- data/test/thumbtack/types/date_test.rb +2 -2
- data/test/thumbtack/types/date_time_test.rb +7 -2
- data/test/thumbtack/types/integer_test.rb +2 -2
- data/test/thumbtack/types/md5_test.rb +2 -2
- data/test/thumbtack/types/tags_test.rb +2 -2
- data/test/thumbtack/types/text_test.rb +1 -1
- data/test/thumbtack/types/title_test.rb +1 -1
- data/test/thumbtack/types/url_test.rb +1 -1
- data/test/thumbtack/user_test.rb +2 -2
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 223435e3e89fab503c5c9e5e42cb6debb6f29a5d
|
4
|
+
data.tar.gz: 769a2c552c39a0e87d35ef21c12afb475ab5029b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86c48c48a0008be9f3cea5d1143c42a7ea87915fd7a92459affc82823d26e355828942974ac9c7af81819998f6d6695c2dd26bb31eb225ef133f34802704c98e
|
7
|
+
data.tar.gz: 1a5f9d3a0d61918bcd5d4e546deb18dfe2808a281f96eec5e4ee6bfd41f15d3a739b925c55e609f3a338eea2ee7e94aed53fabfb55c34b77cd8a61c26787fb3b
|
data/lib/thumbtack.rb
CHANGED
@@ -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'
|
data/lib/thumbtack/client.rb
CHANGED
@@ -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
|
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
|
data/lib/thumbtack/note.rb
CHANGED
@@ -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
|
-
# @
|
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
|
-
|
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
|
-
# @
|
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
|
-
|
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
|
-
|
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
|
-
# @
|
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
|
-
|
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
|
-
# @
|
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
|
-
|
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
|
-
|
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
|
data/lib/thumbtack/post.rb
CHANGED
@@ -13,7 +13,7 @@ module Thumbtack
|
|
13
13
|
:description,
|
14
14
|
:extended,
|
15
15
|
:meta,
|
16
|
-
:
|
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
|
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 :
|
71
|
+
attr_reader :digest
|
57
72
|
|
58
73
|
# The time at which the post was created
|
59
74
|
#
|
60
|
-
# @
|
75
|
+
# @example
|
76
|
+
# post.time # => #<DateTime: 2014-09-17T13:45:27+00:00...
|
77
|
+
#
|
78
|
+
# @return [DateTime]
|
61
79
|
#
|
62
80
|
# @api public
|
63
|
-
|
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
|
-
|
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
|
-
|
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
|
-
# @
|
111
|
+
# @example
|
112
|
+
# post.tags # => ['bookmarks', 'tools']
|
113
|
+
#
|
114
|
+
# @return [Array<String>]
|
82
115
|
#
|
83
116
|
# @api public
|
84
|
-
|
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(
|
131
|
+
new(HashToDigest.rename(hash))
|
97
132
|
end
|
98
133
|
|
99
134
|
# Initialize a Post
|
data/lib/thumbtack/posts.rb
CHANGED
@@ -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.
|
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.
|
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
|
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
|
data/lib/thumbtack/suggestion.rb
CHANGED
@@ -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
|
data/lib/thumbtack/tags.rb
CHANGED
@@ -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.
|
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.
|
71
|
+
@client.action('/tags/rename', parameters)
|
72
72
|
self
|
73
73
|
end
|
74
74
|
end
|
data/lib/thumbtack/types.rb
CHANGED
data/lib/thumbtack/types/date.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/thumbtack/types/text.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
data/lib/thumbtack/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -5,10 +5,16 @@ CodeClimate::TestReporter.start
|
|
5
5
|
require 'thumbtack'
|
6
6
|
require 'minitest/autorun'
|
7
7
|
|
8
|
-
def
|
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('/
|
21
|
-
assert_equal(
|
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
|
21
|
-
assert_equal
|
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
|
data/test/thumbtack/note_test.rb
CHANGED
@@ -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
|
22
|
-
assert_equal
|
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 =
|
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 =
|
39
|
+
client = mock_client_get('/notes/8e5d6964bb810e0050b0',
|
40
40
|
nil,
|
41
41
|
{
|
42
42
|
'id' => '8e5d6964bb810e0050b0',
|
data/test/thumbtack/post_test.rb
CHANGED
@@ -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.
|
25
|
-
assert_equal
|
26
|
-
assert_equal
|
27
|
-
assert_equal
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
140
|
+
client = mock_client_get('/posts/dates',
|
141
141
|
{tag: 'argentina'},
|
142
142
|
{
|
143
143
|
'user' => 'user',
|
data/test/thumbtack/tags_test.rb
CHANGED
@@ -4,7 +4,7 @@ require 'test_helper'
|
|
4
4
|
|
5
5
|
class TagsTest < Minitest::Test
|
6
6
|
def test_update
|
7
|
-
client =
|
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 =
|
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 =
|
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(
|
10
|
+
assert_raises(ValidationError) do
|
11
11
|
Types::Boolean.validate('nope')
|
12
12
|
end
|
13
13
|
|
14
|
-
assert_raises(
|
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(
|
9
|
+
assert_raises(ValidationError) do
|
10
10
|
Types::Date.validate ::Date.new(0, 1, 1)
|
11
11
|
end
|
12
12
|
|
13
|
-
assert_raises(
|
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(
|
9
|
+
assert_raises(ValidationError) do
|
10
10
|
Types::DateTime.validate ::DateTime.new(0, 1, 1)
|
11
11
|
end
|
12
12
|
|
13
|
-
assert_raises(
|
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(
|
10
|
+
assert_raises(ValidationError) do
|
11
11
|
Types::Integer.validate(-1)
|
12
12
|
end
|
13
13
|
|
14
|
-
assert_raises(
|
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(
|
10
|
+
assert_raises(ValidationError) do
|
11
11
|
Types::MD5.validate('0' * 33)
|
12
12
|
end
|
13
13
|
|
14
|
-
assert_raises(
|
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(
|
11
|
+
assert_raises(ValidationError) do
|
12
12
|
Types::Tags.validate [('x' * 256)]
|
13
13
|
end
|
14
14
|
|
15
|
-
assert_raises(
|
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(
|
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(
|
10
|
+
assert_raises(ValidationError) do
|
11
11
|
Types::Title.validate('x' * 256)
|
12
12
|
end
|
13
13
|
end
|
data/test/thumbtack/user_test.rb
CHANGED
@@ -4,7 +4,7 @@ require 'test_helper'
|
|
4
4
|
|
5
5
|
class UserTest < Minitest::Test
|
6
6
|
def test_secret
|
7
|
-
client =
|
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 =
|
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
|
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-
|
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
|