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 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