pinboard 0.0.51 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|