thumbtack 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/thumbtack.rb +2 -1
  3. data/lib/thumbtack/adapters/basic_adapter.rb +83 -0
  4. data/lib/thumbtack/client.rb +26 -19
  5. data/lib/thumbtack/hash_to_digest.rb +12 -7
  6. data/lib/thumbtack/note.rb +1 -1
  7. data/lib/thumbtack/note_summary.rb +1 -1
  8. data/lib/thumbtack/post.rb +1 -1
  9. data/lib/thumbtack/posts.rb +13 -5
  10. data/lib/thumbtack/suggestion.rb +18 -4
  11. data/lib/thumbtack/symbolize_keys.rb +22 -0
  12. data/lib/thumbtack/types/boolean.rb +1 -1
  13. data/lib/thumbtack/types/date.rb +1 -1
  14. data/lib/thumbtack/types/date_time.rb +1 -1
  15. data/lib/thumbtack/types/identity.rb +1 -1
  16. data/lib/thumbtack/types/integer.rb +1 -1
  17. data/lib/thumbtack/types/length_validation.rb +10 -8
  18. data/lib/thumbtack/types/md5.rb +16 -3
  19. data/lib/thumbtack/types/range_validation.rb +5 -3
  20. data/lib/thumbtack/types/tags.rb +16 -3
  21. data/lib/thumbtack/types/text.rb +1 -1
  22. data/lib/thumbtack/types/title.rb +1 -1
  23. data/lib/thumbtack/types/url.rb +1 -1
  24. data/lib/thumbtack/version.rb +1 -1
  25. data/test/test_helper.rb +2 -2
  26. data/test/thumbtack/client_test.rb +29 -1
  27. data/test/thumbtack/integration/basic_adapter_test.rb +24 -0
  28. data/test/thumbtack/note_summary_test.rb +1 -1
  29. data/test/thumbtack/notes_test.rb +34 -34
  30. data/test/thumbtack/post_test.rb +1 -1
  31. data/test/thumbtack/posts_test.rb +113 -95
  32. data/test/thumbtack/specification_test.rb +2 -1
  33. data/test/thumbtack/suggestion_test.rb +4 -4
  34. data/test/thumbtack/tags_test.rb +30 -24
  35. data/test/thumbtack/types/date_test.rb +2 -2
  36. data/test/thumbtack/types/date_time_test.rb +4 -3
  37. data/test/thumbtack/types/identity_test.rb +1 -1
  38. data/test/thumbtack/types/md5_test.rb +3 -3
  39. data/test/thumbtack/types/tags_test.rb +2 -2
  40. data/test/thumbtack/types/text_test.rb +2 -2
  41. data/test/thumbtack/user_test.rb +10 -6
  42. metadata +26 -11
  43. data/lib/thumbtack/types.rb +0 -9
  44. data/test/thumbtack/integration/client_test.rb +0 -29
@@ -1,7 +1,8 @@
1
+ # encoding: utf-8
2
+
1
3
  module Thumbtack
2
4
  module Types
3
5
  # Handles validation of values within a certain range
4
- # Pinboard
5
6
  #
6
7
  # @api private
7
8
  class RangeValidation
@@ -15,15 +16,16 @@ module Thumbtack
15
16
  # @param [Range] range
16
17
  # the range of valid values
17
18
  #
18
- # @return [undefined]
19
+ # @return [self]
19
20
  #
20
21
  # @raise [Types::ValidationError]
21
- # if the value is not between 0001-01-01 and 2100-01-01
22
+ # if the value is not within the range
22
23
  def self.validate(value, range)
23
24
  unless range.cover?(value)
24
25
  fail ValidationError,
25
26
  "#{value} must be between #{range.begin} and #{range.end}"
26
27
  end
28
+ self
27
29
  end
28
30
  end
29
31
  end
@@ -19,13 +19,13 @@ module Thumbtack
19
19
  # @param [String, Array<String>] value
20
20
  # a single tag or an array of many tags
21
21
  #
22
- # @return [undefined]
22
+ # @return [self]
23
23
  #
24
24
  # @raise [Types::ValidationError]
25
- # if any tag contains commas or are longer than 255 characters
25
+ # if any tags contain commas or are longer than 255 characters
26
26
  def self.validate(value)
27
27
  Array(value).each do |tag|
28
- unless tag.length < MAXIMUM_LENGTH && !tag.include?(INVALID_CHARACTER)
28
+ unless tag_valid?(tag)
29
29
  fail ValidationError,
30
30
  "#{tag} cannot contain commas or be longer than 255 characters"
31
31
  end
@@ -53,6 +53,19 @@ module Thumbtack
53
53
  def self.from_parameter(parameter)
54
54
  parameter.split(SEPARATOR)
55
55
  end
56
+
57
+ # If true, the tag is valid
58
+ #
59
+ # @param [String] tag
60
+ # a tag to validate
61
+ #
62
+ # @return [Boolean]
63
+ #
64
+ # @api private
65
+ def self.tag_valid?(tag)
66
+ tag.length < MAXIMUM_LENGTH && !tag.include?(INVALID_CHARACTER)
67
+ end
68
+ private_class_method :tag_valid?
56
69
  end
57
70
  end
58
71
  end
@@ -14,7 +14,7 @@ module Thumbtack
14
14
  # @param [String] value
15
15
  # text to validate
16
16
  #
17
- # @return [undefined]
17
+ # @return [self]
18
18
  #
19
19
  # @raise [Types::ValidationError]
20
20
  # if the value is longer than 65536 characters
@@ -14,7 +14,7 @@ module Thumbtack
14
14
  # @param [String] value
15
15
  # the title to validate
16
16
  #
17
- # @return [undefined]
17
+ # @return [self]
18
18
  #
19
19
  # @raise [Types::ValidationError]
20
20
  # if the value is longer than 255 characters
@@ -14,7 +14,7 @@ module Thumbtack
14
14
  # @param [String] value
15
15
  # the URL to validate
16
16
  #
17
- # @return [undefined]
17
+ # @return [self]
18
18
  #
19
19
  # @raise [Types::ValidationError]
20
20
  # if the URL's scheme isn't one of http, https, javascript, mailto, ftp,
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Thumbtack
4
4
  # Gem version
5
- VERSION = '1.0.0'.freeze
5
+ VERSION = '1.1.0'.freeze
6
6
  end
data/test/test_helper.rb CHANGED
@@ -11,9 +11,9 @@ def mock_client_get(url, params, response)
11
11
  client
12
12
  end
13
13
 
14
- def mock_client_action(url, params, response)
14
+ def mock_client_action(url, params)
15
15
  client = Minitest::Mock.new
16
- client.expect(:action, response, [url, params].compact)
16
+ client.expect(:action, { 'result' => 'done' }, [url, params].compact)
17
17
  client
18
18
  end
19
19
 
@@ -4,7 +4,8 @@ require 'test_helper'
4
4
 
5
5
  class ClientTest < Minitest::Test
6
6
  def setup
7
- @client = Client.new(nil, nil)
7
+ @adapter = Minitest::Mock.new
8
+ @client = Client.new(nil, nil, adapter: @adapter)
8
9
  end
9
10
 
10
11
  def test_posts
@@ -22,4 +23,31 @@ class ClientTest < Minitest::Test
22
23
  def test_notes
23
24
  assert_instance_of Notes, @client.notes
24
25
  end
26
+
27
+ def test_default_adapter
28
+ assert_kind_of Adapters::BasicAdapter, Client.new(nil, nil).adapter
29
+ end
30
+
31
+ def test_get
32
+ @adapter.expect(:get,
33
+ { 'posts' => [] },
34
+ ['/posts/recent', { tag: 'thumbtack' }])
35
+ @client.get('/posts/recent', tag: 'thumbtack')
36
+ @adapter.verify
37
+ end
38
+
39
+ def test_action
40
+ @adapter.expect(:get, { 'result_code' => 'done' }, ['/posts/update', {}])
41
+ @client.action('/posts/update', {})
42
+ @adapter.verify
43
+ end
44
+
45
+ def test_action_result_error
46
+ assert_raises ResultError do
47
+ @adapter.expect(:get,
48
+ { 'result_code' => 'notdone' },
49
+ ['/posts/update', {}])
50
+ @client.action('/posts/update', {})
51
+ end
52
+ end
25
53
  end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ require 'test_helper'
4
+
5
+ module Integration
6
+ class BasicAdapterTest < Minitest::Test
7
+ def setup
8
+ path = File.expand_path('../../../auth_token.txt', __FILE__)
9
+ auth_token = File.open(path).read.strip
10
+ @username, @token = auth_token.split(':')
11
+ @adapter = Adapters::BasicAdapter.new(@username, @token)
12
+ end
13
+
14
+ def test_requests_return_parsed_json
15
+ response = @adapter.get('/user/api_token')
16
+ assert_equal({ 'result' => @token }, response)
17
+ end
18
+
19
+ def test_request_query_parameters
20
+ response = @adapter.get('/posts/recent', tag: 'thumbtack-test-xyz123')
21
+ assert_equal([], response['posts'])
22
+ end
23
+ end
24
+ end
@@ -10,7 +10,7 @@ class NoteSummaryTest < Minitest::Test
10
10
  'hash' => '0c9c30f60cadabd31415',
11
11
  'created_at' => '2010-02-11 03:46:56',
12
12
  'updated_at' => '2010-02-11 03:47:47',
13
- 'length' => 19,
13
+ 'length' => 19
14
14
  }
15
15
  note = NoteSummary.from_hash(hash)
16
16
 
@@ -4,29 +4,29 @@ require 'test_helper'
4
4
 
5
5
  class NotesTest < Minitest::Test
6
6
  def test_list
7
- client = mock_client_get('/notes/list',
8
- nil,
9
- {
10
- 'count' => 2,
11
- 'notes' => [
12
- {
13
- 'id' => 'cf73bfc02e00edaa1e2b',
14
- 'title' => 'Paul Graham on Hirin\' The Ladies',
15
- 'hash' => '0bbca3cba9246bbbda2c',
16
- 'created_at' => '2011-10-28 13:37:23',
17
- 'updated_at' => '2011-10-28 13:37:23',
18
- 'length' => '890',
19
- },
20
- {
21
- 'id' => '8e5d6964bb810e0050b0',
22
- 'title' => 'StarCraft beta coming this week!',
23
- 'hash' => '0c9c30f60cadabd31415',
24
- 'created_at' => '2010-02-11 03:46:56',
25
- 'updated_at' => '2010-02-11 03:47:47',
26
- 'length' => '153',
27
- },
28
- ]
29
- })
7
+ client = mock_client_get(
8
+ '/notes/list',
9
+ nil,
10
+ 'count' => 2,
11
+ 'notes' => [
12
+ {
13
+ 'id' => 'cf73bfc02e00edaa1e2b',
14
+ 'title' => 'Paul Graham on Hirin\' The Ladies',
15
+ 'hash' => '0bbca3cba9246bbbda2c',
16
+ 'created_at' => '2011-10-28 13:37:23',
17
+ 'updated_at' => '2011-10-28 13:37:23',
18
+ 'length' => '890'
19
+ },
20
+ {
21
+ 'id' => '8e5d6964bb810e0050b0',
22
+ 'title' => 'StarCraft beta coming this week!',
23
+ 'hash' => '0c9c30f60cadabd31415',
24
+ 'created_at' => '2010-02-11 03:46:56',
25
+ 'updated_at' => '2010-02-11 03:47:47',
26
+ 'length' => '153'
27
+ }
28
+ ]
29
+ )
30
30
  notes = Notes.new(client)
31
31
  result = notes.list
32
32
 
@@ -36,17 +36,17 @@ class NotesTest < Minitest::Test
36
36
  end
37
37
 
38
38
  def test_get
39
- client = mock_client_get('/notes/8e5d6964bb810e0050b0',
40
- nil,
41
- {
42
- 'id' => '8e5d6964bb810e0050b0',
43
- 'title' => 'StarCraft beta coming this week!',
44
- 'text' => 'This is a test note',
45
- 'hash' => '0c9c30f60cadabd31415',
46
- 'created_at' => '2010-02-11 03:46:56',
47
- 'updated_at' => '2010-02-11 03:47:47',
48
- 'length' => 19
49
- })
39
+ client = mock_client_get(
40
+ '/notes/8e5d6964bb810e0050b0',
41
+ nil,
42
+ 'id' => '8e5d6964bb810e0050b0',
43
+ 'title' => 'StarCraft beta coming this week!',
44
+ 'text' => 'This is a test note',
45
+ 'hash' => '0c9c30f60cadabd31415',
46
+ 'created_at' => '2010-02-11 03:46:56',
47
+ 'updated_at' => '2010-02-11 03:47:47',
48
+ 'length' => 19
49
+ )
50
50
  notes = Notes.new(client)
51
51
  result = notes.get('8e5d6964bb810e0050b0')
52
52
  assert_equal '8e5d6964bb810e0050b0', result.id
@@ -25,6 +25,6 @@ class PostTest < Minitest::Test
25
25
  assert_equal DateTime.new(2014, 6, 29, 16, 57, 45), post.time
26
26
  assert_equal true, post.shared
27
27
  assert_equal false, post.toread
28
- assert_equal ['test123', 'another'], post.tags
28
+ assert_equal %w(test123 another), post.tags
29
29
  end
30
30
  end
@@ -4,9 +4,11 @@ require 'test_helper'
4
4
 
5
5
  class PostsTest < Minitest::Test
6
6
  def test_update
7
- client = mock_client_get('/posts/update',
8
- nil,
9
- { 'update_time' => '2014-06-26T19:01:33Z' })
7
+ client = mock_client_get(
8
+ '/posts/update',
9
+ nil,
10
+ 'update_time' => '2014-06-26T19:01:33Z'
11
+ )
10
12
  posts = Posts.new(client)
11
13
 
12
14
  assert_equal DateTime.new(2014, 6, 26, 19, 1, 33), posts.update
@@ -14,10 +16,11 @@ class PostsTest < Minitest::Test
14
16
  end
15
17
 
16
18
  def test_add
17
- client = mock_client_action('/posts/add',
18
- { url: 'http://example.org',
19
- description: 'example.org' },
20
- { 'result_code' => 'done' })
19
+ client = mock_client_action(
20
+ '/posts/add',
21
+ url: 'http://example.org',
22
+ description: 'example.org'
23
+ )
21
24
  posts = Posts.new(client)
22
25
 
23
26
  assert_equal posts, posts.add('http://example.org', 'example.org')
@@ -25,22 +28,28 @@ class PostsTest < Minitest::Test
25
28
  end
26
29
 
27
30
  def test_add_with_tags
28
- client = mock_client_action('/posts/add',
29
- { url: 'http://example.org',
30
- description: 'example.org',
31
- tags: 'thumbtack test' },
32
- { 'result_code' => 'done' })
31
+ client = mock_client_action(
32
+ '/posts/add',
33
+ url: 'http://example.org',
34
+ description: 'example.org',
35
+ tags: 'thumbtack test'
36
+ )
33
37
  posts = Posts.new(client)
34
38
 
35
39
  assert_equal posts,
36
- posts.add('http://example.org', 'example.org', tags: %w(thumbtack test))
40
+ posts.add(
41
+ 'http://example.org',
42
+ 'example.org',
43
+ tags: %w(thumbtack test)
44
+ )
37
45
  client.verify
38
46
  end
39
47
 
40
48
  def test_delete
41
- client = mock_client_action('/posts/delete',
42
- { url: 'http://example.org' },
43
- { 'result_code' => 'done' })
49
+ client = mock_client_action(
50
+ '/posts/delete',
51
+ url: 'http://example.org'
52
+ )
44
53
  posts = Posts.new(client)
45
54
 
46
55
  assert_equal posts, posts.delete('http://example.org')
@@ -48,23 +57,23 @@ class PostsTest < Minitest::Test
48
57
  end
49
58
 
50
59
  def test_get
51
- client = mock_client_get('/posts/get',
52
- { url: 'http://example.org' },
53
- {
54
- 'date' => '2014-06-29T16:57:45Z',
55
- 'user' => 'nwjsmith',
56
- 'posts' => [{
57
- 'href' => 'http://example.org',
58
- 'description' => 'example.org',
59
- 'extended' => '',
60
- 'meta' => '46ca40b9b92ee0ea1284785a5d2a9b38',
61
- 'hash' => 'dab521de65f9250b4cca7383feef67dc',
62
- 'time' => '2014-06-29T16:57:45Z',
63
- 'shared' => 'yes',
64
- 'toread' => 'no',
65
- 'tags' => 'test123'
66
- }]
67
- })
60
+ client = mock_client_get(
61
+ '/posts/get',
62
+ { url: 'http://example.org' },
63
+ 'date' => '2014-06-29T16:57:45Z',
64
+ 'user' => 'nwjsmith',
65
+ 'posts' => [{
66
+ 'href' => 'http://example.org',
67
+ 'description' => 'example.org',
68
+ 'extended' => '',
69
+ 'meta' => '46ca40b9b92ee0ea1284785a5d2a9b38',
70
+ 'hash' => 'dab521de65f9250b4cca7383feef67dc',
71
+ 'time' => '2014-06-29T16:57:45Z',
72
+ 'shared' => 'yes',
73
+ 'toread' => 'no',
74
+ 'tags' => 'test123'
75
+ }]
76
+ )
68
77
  posts = Posts.new(client)
69
78
  response = posts.get(url: 'http://example.org')
70
79
 
@@ -74,23 +83,23 @@ class PostsTest < Minitest::Test
74
83
  end
75
84
 
76
85
  def test_recent
77
- client = mock_client_get('/posts/recent',
78
- {tag: 'webdev'},
79
- {
80
- 'date' => '2014-06-29T16:57:45Z',
81
- 'user' => 'nwjsmith',
82
- 'posts' => [{
83
- 'href' => 'http://example.org',
84
- 'description' => 'example.org',
85
- 'extended' => '',
86
- 'meta' => '46ca40b9b92ee0ea1284785a5d2a9b38',
87
- 'hash' => 'dab521de65f9250b4cca7383feef67dc',
88
- 'time' => '2014-06-29T16:57:45Z',
89
- 'shared' => 'yes',
90
- 'toread' => 'no',
91
- 'tags' => 'webdev'
92
- }]
93
- })
86
+ client = mock_client_get(
87
+ '/posts/recent',
88
+ { tag: 'webdev' },
89
+ 'date' => '2014-06-29T16:57:45Z',
90
+ 'user' => 'nwjsmith',
91
+ 'posts' => [{
92
+ 'href' => 'http://example.org',
93
+ 'description' => 'example.org',
94
+ 'extended' => '',
95
+ 'meta' => '46ca40b9b92ee0ea1284785a5d2a9b38',
96
+ 'hash' => 'dab521de65f9250b4cca7383feef67dc',
97
+ 'time' => '2014-06-29T16:57:45Z',
98
+ 'shared' => 'yes',
99
+ 'toread' => 'no',
100
+ 'tags' => 'webdev'
101
+ }]
102
+ )
94
103
  posts = Posts.new(client)
95
104
  response = posts.recent(tag: 'webdev')
96
105
 
@@ -100,19 +109,23 @@ class PostsTest < Minitest::Test
100
109
  end
101
110
 
102
111
  def test_all
103
- client = mock_client_get('/posts/all',
104
- {tag: 'webdev'},
105
- [{
106
- 'href' => 'http://example.org',
107
- 'description' => 'example.org',
108
- 'extended' => '',
109
- 'meta' => '46ca40b9b92ee0ea1284785a5d2a9b38',
110
- 'hash' => 'dab521de65f9250b4cca7383feef67dc',
111
- 'time' => '2014-06-29T16:57:45Z',
112
- 'shared' => 'yes',
113
- 'toread' => 'no',
114
- 'tags' => 'webdev'
115
- }])
112
+ client = mock_client_get(
113
+ '/posts/all',
114
+ { tag: 'webdev' },
115
+ [
116
+ {
117
+ 'href' => 'http://example.org',
118
+ 'description' => 'example.org',
119
+ 'extended' => '',
120
+ 'meta' => '46ca40b9b92ee0ea1284785a5d2a9b38',
121
+ 'hash' => 'dab521de65f9250b4cca7383feef67dc',
122
+ 'time' => '2014-06-29T16:57:45Z',
123
+ 'shared' => 'yes',
124
+ 'toread' => 'no',
125
+ 'tags' => 'webdev'
126
+ }
127
+ ]
128
+ )
116
129
  posts = Posts.new(client)
117
130
  response = posts.all(tag: 'webdev')
118
131
 
@@ -122,12 +135,14 @@ class PostsTest < Minitest::Test
122
135
  end
123
136
 
124
137
  def test_suggest
125
- client = mock_client_get('/posts/suggest',
126
- {url: 'http://blog.com'},
127
- [
128
- { 'popular' => ['blog', 'blogs', 'people'] },
129
- { 'recommended' => ['blog', 'writing', 'weblog'] }
130
- ])
138
+ client = mock_client_get(
139
+ '/posts/suggest',
140
+ { url: 'http://blog.com' },
141
+ [
142
+ { 'popular' => %w(blog blogs people) },
143
+ { 'recommended' => %w(blog writing weblog) }
144
+ ]
145
+ )
131
146
  posts = Posts.new(client)
132
147
  response = posts.suggest('http://blog.com')
133
148
 
@@ -137,35 +152,38 @@ class PostsTest < Minitest::Test
137
152
  end
138
153
 
139
154
  def test_dates
140
- client = mock_client_get('/posts/dates',
141
- {tag: 'argentina'},
142
- {
143
- 'user' => 'user',
144
- 'tag' => 'argentina',
145
- 'dates' => {
146
- '2010-11-29' => '5',
147
- '2010-11-28' => '15',
148
- '2010-11-26' => '2',
149
- '2010-11-25' => '2',
150
- '2010-11-23' => '7',
151
- '2010-11-22' => '20',
152
- '2010-11-21' => '16',
153
- '2010-11-19' => '4'
154
- }
155
- })
155
+ client = mock_client_get(
156
+ '/posts/dates',
157
+ { tag: 'argentina' },
158
+ 'user' => 'user',
159
+ 'tag' => 'argentina',
160
+ 'dates' => {
161
+ '2010-11-29' => '5',
162
+ '2010-11-28' => '15',
163
+ '2010-11-26' => '2',
164
+ '2010-11-25' => '2',
165
+ '2010-11-23' => '7',
166
+ '2010-11-22' => '20',
167
+ '2010-11-21' => '16',
168
+ '2010-11-19' => '4'
169
+ }
170
+ )
156
171
  posts = Posts.new(client)
157
172
  response = posts.dates(tag: 'argentina')
158
173
 
159
- assert_equal({
160
- Date.new(2010, 11, 29) => 5,
161
- Date.new(2010, 11, 28) => 15,
162
- Date.new(2010, 11, 26) => 2,
163
- Date.new(2010, 11, 25) => 2,
164
- Date.new(2010, 11, 23) => 7,
165
- Date.new(2010, 11, 22) => 20,
166
- Date.new(2010, 11, 21) => 16,
167
- Date.new(2010, 11, 19) => 4
168
- }, response)
174
+ assert_equal(
175
+ {
176
+ Date.new(2010, 11, 29) => 5,
177
+ Date.new(2010, 11, 28) => 15,
178
+ Date.new(2010, 11, 26) => 2,
179
+ Date.new(2010, 11, 25) => 2,
180
+ Date.new(2010, 11, 23) => 7,
181
+ Date.new(2010, 11, 22) => 20,
182
+ Date.new(2010, 11, 21) => 16,
183
+ Date.new(2010, 11, 19) => 4
184
+ },
185
+ response
186
+ )
169
187
  client.verify
170
188
  end
171
189
  end