pinboard 0.0.51 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -3
- data/lib/pinboard.rb +2 -0
- data/lib/pinboard/client.rb +158 -13
- data/lib/pinboard/note.rb +22 -0
- data/lib/pinboard/post.rb +13 -0
- data/lib/pinboard/tag.rb +8 -0
- data/pinboard.gemspec +4 -4
- data/spec/fixtures/dates.xml +6 -0
- data/spec/fixtures/dates_filtered.xml +6 -0
- data/spec/fixtures/post_update.xml +5 -0
- data/spec/pinboard/client_spec.rb +91 -2
- metadata +15 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87cc2921a9f8b072ac29ce9be0629ebc368cca61
|
4
|
+
data.tar.gz: 9bb8e0dc7769f13d49227efe154d5678178083de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d2ff69a5c1f26f2721cb53f26bd67d4672112b205c7000dde9de8e400df1bae1245e747822cc703f3efcfa26c151fdbc94f33b5ce1847a99eeb8eec91cf8651
|
7
|
+
data.tar.gz: e129eaa534e5cf5f6c4ad85f0888c1f38941914bd9564ceda05fdb1d987a757ff60ce514b8b91d220caf9c5ec5c3fff48386b45f619a2938745d187c4f683689
|
data/.travis.yml
CHANGED
data/lib/pinboard.rb
CHANGED
data/lib/pinboard/client.rb
CHANGED
@@ -1,31 +1,56 @@
|
|
1
1
|
require 'httparty'
|
2
|
+
require 'time'
|
2
3
|
|
3
4
|
module Pinboard
|
5
|
+
# Raised when API returns failure
|
4
6
|
Error = Class.new(StandardError)
|
5
7
|
|
6
8
|
class Client
|
7
9
|
include HTTParty
|
8
10
|
base_uri 'api.pinboard.in:443/v1'
|
9
11
|
|
12
|
+
# Construct a new instance of the client
|
13
|
+
#
|
14
|
+
# @param [Hash] options Options for the connection
|
15
|
+
# @option options [String] :token The Pinboard API token (prefered over username & password)
|
16
|
+
# @option options [String] :username Pinboard username
|
17
|
+
# @option options [String] :password Pinboard password
|
10
18
|
def initialize(options={})
|
11
|
-
@auth
|
12
|
-
|
19
|
+
@auth = nil
|
20
|
+
@auth_token = nil
|
21
|
+
|
22
|
+
if (token=options.delete(:token))
|
23
|
+
@auth_token = token
|
24
|
+
else
|
25
|
+
@auth = {
|
26
|
+
username: options[:username],
|
27
|
+
password: options[:password]
|
28
|
+
}
|
29
|
+
end
|
13
30
|
end
|
14
31
|
|
32
|
+
# Returns all bookmarks in the user's account.
|
33
|
+
#
|
34
|
+
# @return [Array<Post>] the list of bookmarks.
|
15
35
|
def posts(params={})
|
16
|
-
options =
|
17
|
-
options[:basic_auth] = @auth
|
18
|
-
options[:query] = params
|
36
|
+
options = create_params(params)
|
19
37
|
posts = self.class.get('/posts/all', options)['posts']['post']
|
20
38
|
posts = [] if posts.nil?
|
21
39
|
posts = [posts] if posts.class != Array
|
22
40
|
posts.map { |p| Post.new(Util.symbolize_keys(p)) }
|
23
41
|
end
|
24
42
|
|
43
|
+
# Add a bookmark
|
44
|
+
#
|
45
|
+
# @param [Hash] params Arguments for this call
|
46
|
+
# @option params [String] :url the URL of the item (required)
|
47
|
+
# @option params [String] :description Title of the item
|
48
|
+
# @option params [String] :extended Description of the item
|
49
|
+
# @option params [Array] :tags List of up to 100 tags
|
50
|
+
# @option params [Boolean] :replace Replace any existing bookmark with this URL (default: true)
|
51
|
+
# @option params [Boolean] :shared Make bookmark public (default: true)
|
52
|
+
# @option params [Boolean] :toread Marks the bookmark as unread (default: false)
|
25
53
|
def add(params={})
|
26
|
-
options = {}
|
27
|
-
options[:basic_auth] = @auth
|
28
|
-
|
29
54
|
# Pinboard expects multiple tags=foo,bar separated by comma instead of tag=foo&tag=bar
|
30
55
|
params[:tags] = Array(params[:tags]).join(',') if params[:tags]
|
31
56
|
|
@@ -34,19 +59,139 @@ module Pinboard
|
|
34
59
|
params[boolean] = params[boolean] ? 'yes' : 'no' if params.has_key?(boolean)
|
35
60
|
end
|
36
61
|
|
37
|
-
options
|
62
|
+
options = create_params(params)
|
38
63
|
result_code = self.class.post('/posts/add', options).parsed_response["result"]["code"]
|
39
64
|
|
40
65
|
raise Error.new(result_code) if result_code != "done"
|
41
66
|
end
|
42
67
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
68
|
+
# Returns the most recent time a bookmark was added, updated or deleted.
|
69
|
+
#
|
70
|
+
# Use this before calling {#posts} to see if the data has changed
|
71
|
+
# since the last fetch.
|
72
|
+
#
|
73
|
+
# @return [Time] the time a bookmark was modified
|
74
|
+
def update
|
75
|
+
options = create_params({})
|
76
|
+
time = self.class.get('/posts/update', options)["update"]["time"]
|
77
|
+
|
78
|
+
Time.parse time
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns a list of the user's most recent posts, filtered by tag.
|
82
|
+
#
|
83
|
+
# @param <Hash> params the options to filter current posts
|
84
|
+
# @option params [String] :tag filter by up to three tags
|
85
|
+
# @option params [String] :count Number of results to return.
|
86
|
+
# Default is 15, max is 100
|
87
|
+
#
|
88
|
+
# @return [Array<Post>] the list of recent posts.
|
89
|
+
def recent(params={})
|
90
|
+
options = create_params(params)
|
91
|
+
posts = self.class.get('/posts/recent', options)['posts']['post']
|
92
|
+
posts = [] if posts.nil?
|
93
|
+
posts = [*posts]
|
94
|
+
posts.map { |p| Post.new(Util.symbolize_keys(p)) }
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns a list of dates with the number of posts at each date.
|
98
|
+
#
|
99
|
+
# @param [String] tag Filter by up to three tags
|
100
|
+
#
|
101
|
+
# @return [Hash<String,Fixnum>] List of dates with number of posts
|
102
|
+
# at each date
|
103
|
+
def dates(tag=nil)
|
104
|
+
params = {}
|
105
|
+
params[:tag] = tag if tag
|
106
|
+
|
107
|
+
options = create_params(params)
|
108
|
+
dates = self.class.get('/posts/dates', options)['dates']['date']
|
109
|
+
dates = [] if dates.nil?
|
110
|
+
dates = [*dates]
|
111
|
+
dates.each_with_object({}) { |value, hash|
|
112
|
+
hash[value["date"]] = value["count"].to_i
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
# Delete a bookmark
|
118
|
+
#
|
119
|
+
# @param [String] url The url to delete
|
120
|
+
def delete(url)
|
121
|
+
params = { url: url }
|
122
|
+
options = create_params(params)
|
47
123
|
result_code = self.class.get('/posts/delete', options).parsed_response["result"]["code"]
|
48
124
|
|
49
125
|
raise Error.new(result_code) if result_code != "done"
|
50
126
|
end
|
127
|
+
|
128
|
+
# Returns a full list of the user's tags along with the number of
|
129
|
+
# times they were used.
|
130
|
+
#
|
131
|
+
# @return [Array<Tag>] List of all tags
|
132
|
+
def tags_get(params={})
|
133
|
+
options = create_params(params)
|
134
|
+
tags = self.class.get('/tags/get', options)['tags']['tag']
|
135
|
+
tags = [] if tags.nil?
|
136
|
+
tags = [*tags]
|
137
|
+
tags.map { |p| Tag.new(Util.symbolize_keys(p)) }
|
138
|
+
end
|
139
|
+
|
140
|
+
# Rename an tag or fold it into an existing tag
|
141
|
+
#
|
142
|
+
# @param [String] old_tag Tag to rename (not case sensitive)
|
143
|
+
# @param [String] new_tag New tag (if empty nothing will happen)
|
144
|
+
#
|
145
|
+
# @raise [Error] if result code is not "done"
|
146
|
+
def tags_rename(old_tag, new_tag=nil, params={})
|
147
|
+
params[:old] = old_tag
|
148
|
+
params[:new] = new_tag if new_tag
|
149
|
+
|
150
|
+
options = create_params(params)
|
151
|
+
result_code = self.class.get('/tags/rename', options).parsed_response["result"]
|
152
|
+
|
153
|
+
raise Error.new(result_code) if result_code != "done"
|
154
|
+
end
|
155
|
+
|
156
|
+
# Delete an existing tag
|
157
|
+
#
|
158
|
+
# @param [String] tag Tag to delete
|
159
|
+
# @return [nil]
|
160
|
+
def tags_delete(tag)
|
161
|
+
params = { tag: tag }
|
162
|
+
|
163
|
+
options = create_params(params)
|
164
|
+
self.class.get('/tags/delete', options)
|
165
|
+
nil
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns a list of the user's notes
|
169
|
+
#
|
170
|
+
# @return [Array<Note>] list of notes
|
171
|
+
def notes_list
|
172
|
+
options = create_params(params)
|
173
|
+
notes = self.class.get('/notes/list', options)['notes']['note']
|
174
|
+
notes = [] if notes.nil?
|
175
|
+
notes = [*notes]
|
176
|
+
notes.map { |p| Note.new(Util.symbolize_keys(p)) }
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
# Construct params hash for HTTP request
|
181
|
+
#
|
182
|
+
# @param [Hash] params Query arguments to include in request
|
183
|
+
# @return [Hash] Options hash for request
|
184
|
+
def create_params params
|
185
|
+
options = {}
|
186
|
+
options[:query] = params
|
187
|
+
|
188
|
+
if @auth_token
|
189
|
+
options[:query].merge!(auth_token: @auth_token)
|
190
|
+
else
|
191
|
+
options[:basic_auth] = @auth
|
192
|
+
end
|
193
|
+
|
194
|
+
options
|
195
|
+
end
|
51
196
|
end
|
52
197
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Pinboard
|
2
|
+
class Note < Struct.new(:id, :title, :created_at, :updated_at, :length)
|
3
|
+
def initialize(attributes={})
|
4
|
+
self.id = attributes[:id]
|
5
|
+
self.title = attributes[:title]
|
6
|
+
self.created_at = Util.parse_time(attributes[:created_at])
|
7
|
+
self.updated_at = Util.parse_time(attributes[:updated_at])
|
8
|
+
self.length = attributes[:length].to_i
|
9
|
+
self.text = attributes[:text]
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_json(*args)
|
13
|
+
{
|
14
|
+
id: id,
|
15
|
+
title: title,
|
16
|
+
created_at: created_at,
|
17
|
+
updated_at: updated_at,
|
18
|
+
length: length
|
19
|
+
}.to_json(*args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/pinboard/post.rb
CHANGED
@@ -15,5 +15,18 @@ module Pinboard
|
|
15
15
|
send("#{attribute}=", value) if respond_to?("#{attribute}=")
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
def to_json(*args)
|
20
|
+
{
|
21
|
+
href: href,
|
22
|
+
description: description,
|
23
|
+
extended: extended,
|
24
|
+
tag: tag,
|
25
|
+
time: time,
|
26
|
+
replace: replace,
|
27
|
+
shared: shared,
|
28
|
+
toread: toread
|
29
|
+
}.to_json(*args)
|
30
|
+
end
|
18
31
|
end
|
19
32
|
end
|
data/lib/pinboard/tag.rb
ADDED
data/pinboard.gemspec
CHANGED
@@ -10,13 +10,13 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.add_development_dependency 'rb-fsevent'
|
11
11
|
s.add_development_dependency 'terminal-notifier-guard'
|
12
12
|
|
13
|
-
s.add_runtime_dependency 'httparty', '= 0.
|
13
|
+
s.add_runtime_dependency 'httparty', '= 0.11.0'
|
14
14
|
s.name = 'pinboard'
|
15
|
-
s.version = '0.0
|
16
|
-
s.date = '2013-
|
15
|
+
s.version = '0.1.0'
|
16
|
+
s.date = '2013-04-28'
|
17
17
|
s.summary = "Ruby wrapper for the Pinboard API"
|
18
18
|
s.description = "Ruby wrapper for the Pinboard API"
|
19
|
-
s.authors = ["
|
19
|
+
s.authors = ["Ry Waker", "Jan-Erik Rediger"]
|
20
20
|
s.email = 'ry@rywalker.com'
|
21
21
|
s.require_paths = ['lib']
|
22
22
|
s.files = `git ls-files`.split("\n")
|
@@ -70,7 +70,7 @@ describe Pinboard::Client do
|
|
70
70
|
end
|
71
71
|
|
72
72
|
it "succeeds without raising an error" do
|
73
|
-
expect{client.delete(
|
73
|
+
expect{client.delete("http://bar.com/")}.to_not raise_error
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
@@ -82,7 +82,7 @@ describe Pinboard::Client do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
it "throws an error" do
|
85
|
-
expect{client.delete(
|
85
|
+
expect{client.delete("http://baz.com/")}.to raise_error(Pinboard::Error, 'item not found')
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
@@ -235,4 +235,93 @@ describe Pinboard::Client do
|
|
235
235
|
end
|
236
236
|
end
|
237
237
|
end
|
238
|
+
|
239
|
+
describe "#update" do
|
240
|
+
let(:client) { Pinboard::Client.new(auth_params) }
|
241
|
+
|
242
|
+
before do
|
243
|
+
stub_get("posts/update?").
|
244
|
+
to_return(:body => fixture("post_update.xml"),
|
245
|
+
:headers => { 'content-type' => 'text/xml' })
|
246
|
+
end
|
247
|
+
|
248
|
+
it "returns the time of last update" do
|
249
|
+
expected = Time.parse "2013-04-20 13:58:56 +0200"
|
250
|
+
|
251
|
+
client.update.should == expected
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe "#recent" do
|
256
|
+
let(:client) { Pinboard::Client.new(auth_params) }
|
257
|
+
|
258
|
+
before do
|
259
|
+
stub_get("posts/recent?").
|
260
|
+
to_return(:body => fixture("multiple_posts.xml"),
|
261
|
+
:headers => { 'content-type' => 'text/xml' })
|
262
|
+
end
|
263
|
+
|
264
|
+
it "returns recent items" do
|
265
|
+
expected = [
|
266
|
+
Pinboard::Post.new(
|
267
|
+
:href => "http://foo.com/",
|
268
|
+
:description => "Foo!",
|
269
|
+
:extended => "long description Foo",
|
270
|
+
:tag => 'foo bar',
|
271
|
+
:toread => 'yes',
|
272
|
+
:time => Time.parse("2011-07-26T17:52:04Z")),
|
273
|
+
Pinboard::Post.new(
|
274
|
+
:href => "http://bar.com/",
|
275
|
+
:description => "Bar!",
|
276
|
+
:extended => "long description Bar",
|
277
|
+
:tag => 'foo bar',
|
278
|
+
:toread => 'yes',
|
279
|
+
:time => Time.parse("2011-07-26T17:52:04Z")),
|
280
|
+
]
|
281
|
+
|
282
|
+
client.recent.should == expected
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe "#dates" do
|
287
|
+
let(:client) { Pinboard::Client.new(auth_params) }
|
288
|
+
|
289
|
+
context "unfiltered" do
|
290
|
+
before do
|
291
|
+
stub_get("posts/dates?").
|
292
|
+
to_return(:body => fixture("dates.xml"),
|
293
|
+
:headers => { 'content-type' => 'text/xml' })
|
294
|
+
end
|
295
|
+
|
296
|
+
it "returns a list of dates with the number of posts at each date" do
|
297
|
+
|
298
|
+
expected = {
|
299
|
+
"2013-04-19" => 1,
|
300
|
+
"2013-04-18" => 2,
|
301
|
+
"2013-04-17" => 3
|
302
|
+
}
|
303
|
+
|
304
|
+
client.dates.should == expected
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
context "filtered by tag" do
|
309
|
+
before do
|
310
|
+
stub_get("posts/dates?tag=tag").
|
311
|
+
to_return(:body => fixture("dates_filtered.xml"),
|
312
|
+
:headers => { 'content-type' => 'text/xml' })
|
313
|
+
end
|
314
|
+
|
315
|
+
it "returns a list of dates with the number of posts at each date" do
|
316
|
+
|
317
|
+
expected = {
|
318
|
+
"2013-04-19" => 1,
|
319
|
+
"2013-04-18" => 2,
|
320
|
+
"2013-04-17" => 3
|
321
|
+
}
|
322
|
+
|
323
|
+
client.dates("tag").should == expected
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
238
327
|
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pinboard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Ry Waker
|
8
|
+
- Jan-Erik Rediger
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2013-
|
12
|
+
date: 2013-04-28 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: yard
|
@@ -156,14 +157,14 @@ dependencies:
|
|
156
157
|
requirements:
|
157
158
|
- - '='
|
158
159
|
- !ruby/object:Gem::Version
|
159
|
-
version: 0.
|
160
|
+
version: 0.11.0
|
160
161
|
type: :runtime
|
161
162
|
prerelease: false
|
162
163
|
version_requirements: !ruby/object:Gem::Requirement
|
163
164
|
requirements:
|
164
165
|
- - '='
|
165
166
|
- !ruby/object:Gem::Version
|
166
|
-
version: 0.
|
167
|
+
version: 0.11.0
|
167
168
|
description: Ruby wrapper for the Pinboard API
|
168
169
|
email: ry@rywalker.com
|
169
170
|
executables: []
|
@@ -181,16 +182,21 @@ files:
|
|
181
182
|
- Rakefile
|
182
183
|
- lib/pinboard.rb
|
183
184
|
- lib/pinboard/client.rb
|
185
|
+
- lib/pinboard/note.rb
|
184
186
|
- lib/pinboard/post.rb
|
187
|
+
- lib/pinboard/tag.rb
|
185
188
|
- lib/pinboard/util.rb
|
186
189
|
- pinboard.gemspec
|
187
190
|
- spec/fixtures/created.xml
|
191
|
+
- spec/fixtures/dates.xml
|
192
|
+
- spec/fixtures/dates_filtered.xml
|
188
193
|
- spec/fixtures/deleted.xml
|
189
194
|
- spec/fixtures/missing_description.xml
|
190
195
|
- spec/fixtures/missing_url.xml
|
191
196
|
- spec/fixtures/multiple_posts.xml
|
192
197
|
- spec/fixtures/no_posts.xml
|
193
198
|
- spec/fixtures/not_found.xml
|
199
|
+
- spec/fixtures/post_update.xml
|
194
200
|
- spec/fixtures/single_post.xml
|
195
201
|
- spec/helper.rb
|
196
202
|
- spec/pinboard/client_spec.rb
|
@@ -215,18 +221,21 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
215
221
|
version: 1.3.6
|
216
222
|
requirements: []
|
217
223
|
rubyforge_project:
|
218
|
-
rubygems_version: 2.0.
|
224
|
+
rubygems_version: 2.0.3
|
219
225
|
signing_key:
|
220
226
|
specification_version: 4
|
221
227
|
summary: Ruby wrapper for the Pinboard API
|
222
228
|
test_files:
|
223
229
|
- spec/fixtures/created.xml
|
230
|
+
- spec/fixtures/dates.xml
|
231
|
+
- spec/fixtures/dates_filtered.xml
|
224
232
|
- spec/fixtures/deleted.xml
|
225
233
|
- spec/fixtures/missing_description.xml
|
226
234
|
- spec/fixtures/missing_url.xml
|
227
235
|
- spec/fixtures/multiple_posts.xml
|
228
236
|
- spec/fixtures/no_posts.xml
|
229
237
|
- spec/fixtures/not_found.xml
|
238
|
+
- spec/fixtures/post_update.xml
|
230
239
|
- spec/fixtures/single_post.xml
|
231
240
|
- spec/helper.rb
|
232
241
|
- spec/pinboard/client_spec.rb
|