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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 34dae5a7d0157a62075e08ae484d3b97959ed666
4
- data.tar.gz: 4f2c559e7af3f1f43e37270872c18e202f0776ca
3
+ metadata.gz: 87cc2921a9f8b072ac29ce9be0629ebc368cca61
4
+ data.tar.gz: 9bb8e0dc7769f13d49227efe154d5678178083de
5
5
  SHA512:
6
- metadata.gz: 65a44277439c422d52ff695b557c4e781c7b5225459ab321e64735445b52c3ff4e41198e323a786b69b82b8a8c0f03f80bedad6209739ee4b44014c99475b66d
7
- data.tar.gz: 3498b877afc4bef15ba34728aa2b302f7ceeb7f521b2c8e6dd7a1f35fffe3c7729123ff457a2866103677f9ab3b7796f5c8f73b927e26f16d3a9e4fa984ab9e5
6
+ metadata.gz: 1d2ff69a5c1f26f2721cb53f26bd67d4672112b205c7000dde9de8e400df1bae1245e747822cc703f3efcfa26c151fdbc94f33b5ce1847a99eeb8eec91cf8651
7
+ data.tar.gz: e129eaa534e5cf5f6c4ad85f0888c1f38941914bd9564ceda05fdb1d987a757ff60ce514b8b91d220caf9c5ec5c3fff48386b45f619a2938745d187c4f683689
@@ -1,6 +1,4 @@
1
1
  rvm:
2
- - 1.8.7
3
- - 1.9.2
2
+ - 1.9.3
4
3
  - jruby
5
- - ree
6
4
  - ruby-head
@@ -1,6 +1,8 @@
1
1
  require 'pinboard/util'
2
2
  require 'pinboard/client'
3
3
  require 'pinboard/post'
4
+ require 'pinboard/tag'
5
+ require 'pinboard/note'
4
6
 
5
7
  module Pinboard
6
8
  class << self
@@ -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 = { :username => options[:username],
12
- :password => options[:password] }
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[:query] = params
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
- def delete(params={})
44
- options = {}
45
- options[:basic_auth] = @auth
46
- options[:query] = params
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
@@ -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
@@ -0,0 +1,8 @@
1
+ module Pinboard
2
+ class Tag < Struct.new(:tag, :count)
3
+ def initialize(attributes={})
4
+ self.tag = attributes.delete(:tag)
5
+ self.count = attributes.delete(:count).to_i
6
+ end
7
+ end
8
+ end
@@ -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.7.8'
13
+ s.add_runtime_dependency 'httparty', '= 0.11.0'
14
14
  s.name = 'pinboard'
15
- s.version = '0.0.51'
16
- s.date = '2013-03-28'
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 = ["Ryan Waker"]
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")
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <dates user="user" tag="">
3
+ <date count="1" date="2013-04-19" />
4
+ <date count="2" date="2013-04-18" />
5
+ <date count="3" date="2013-04-17" />
6
+ </dates>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <dates user="user" tag="tag">
3
+ <date count="1" date="2013-04-19" />
4
+ <date count="2" date="2013-04-18" />
5
+ <date count="3" date="2013-04-17" />
6
+ </dates>
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <update>
3
+ <time>2013-04-20 13:58:56 +0200</time>
4
+ </update>
5
+
@@ -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(:url => "http://bar.com/")}.to_not raise_error
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(:url => "http://baz.com/")}.to raise_error(Pinboard::Error, 'item not found')
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.51
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
- - Ryan Waker
7
+ - Ry Waker
8
+ - Jan-Erik Rediger
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-03-28 00:00:00.000000000 Z
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.7.8
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.7.8
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.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