kippt 1.1.0 → 2.0.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.
Files changed (61) hide show
  1. data/.travis.yml +1 -0
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile +2 -0
  4. data/README.md +20 -3
  5. data/TODO.md +43 -0
  6. data/kippt.gemspec +6 -6
  7. data/lib/kippt/client.rb +46 -8
  8. data/lib/kippt/clip.rb +83 -4
  9. data/lib/kippt/clip_collection.rb +4 -0
  10. data/lib/kippt/clips.rb +8 -4
  11. data/lib/kippt/collection.rb +22 -9
  12. data/lib/kippt/collection_resource.rb +25 -8
  13. data/lib/kippt/comment.rb +25 -0
  14. data/lib/kippt/comment_collection.rb +10 -0
  15. data/lib/kippt/comments.rb +43 -0
  16. data/lib/kippt/connection.rb +14 -8
  17. data/lib/kippt/follow_relationship.rb +50 -0
  18. data/lib/kippt/followers.rb +26 -0
  19. data/lib/kippt/following.rb +26 -0
  20. data/lib/kippt/like.rb +48 -0
  21. data/lib/kippt/likes.rb +48 -0
  22. data/lib/kippt/list.rb +29 -4
  23. data/lib/kippt/list_collection.rb +4 -0
  24. data/lib/kippt/resource.rb +106 -9
  25. data/lib/kippt/saves.rb +25 -0
  26. data/lib/kippt/user.rb +59 -0
  27. data/lib/kippt/user_clips.rb +39 -0
  28. data/lib/kippt/user_collection.rb +14 -0
  29. data/lib/kippt/user_lists.rb +32 -0
  30. data/lib/kippt/users.rb +51 -0
  31. data/lib/kippt/version.rb +1 -1
  32. data/spec/fixtures/clip.json +1 -1
  33. data/spec/fixtures/comment.json +1 -0
  34. data/spec/fixtures/comments.json +1 -0
  35. data/spec/fixtures/feed.json +1 -0
  36. data/spec/fixtures/list.json +1 -1
  37. data/spec/fixtures/user.json +1 -0
  38. data/spec/fixtures/users.json +2 -0
  39. data/spec/fixtures/users_with_multiple_pages.json +1 -0
  40. data/spec/kippt/client_spec.rb +63 -8
  41. data/spec/kippt/clip_collection_spec.rb +3 -3
  42. data/spec/kippt/clip_spec.rb +109 -4
  43. data/spec/kippt/clips_spec.rb +18 -7
  44. data/spec/kippt/comment_spec.rb +25 -0
  45. data/spec/kippt/comments_spec.rb +103 -0
  46. data/spec/kippt/follow_relationship_spec.rb +34 -0
  47. data/spec/kippt/followers_spec.rb +17 -0
  48. data/spec/kippt/following_spec.rb +17 -0
  49. data/spec/kippt/list_collection_spec.rb +3 -3
  50. data/spec/kippt/list_spec.rb +92 -6
  51. data/spec/kippt/lists_spec.rb +2 -1
  52. data/spec/kippt/saves_spec.rb +20 -0
  53. data/spec/kippt/user_clips_spec.rb +29 -0
  54. data/spec/kippt/user_collection_spec.rb +14 -0
  55. data/spec/kippt/user_lists_spec.rb +13 -0
  56. data/spec/kippt/user_spec.rb +71 -0
  57. data/spec/kippt/users_spec.rb +59 -0
  58. data/spec/spec_helper.rb +60 -4
  59. metadata +106 -24
  60. data/lib/kippt/account.rb +0 -10
  61. data/spec/kippt/account_spec.rb +0 -28
@@ -2,6 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 1.8.7
4
4
  - 1.9.3
5
+ - 2.0.0
5
6
  - jruby-18mode
6
7
  - jruby-19mode
7
8
  - rbx-18mode
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0 / July 7, 2013
4
+
5
+ After being long time in development this update adds support for all the
6
+ [new Kippt APIs](http://blog.kippt.com/2013/04/10/say-hi-to-api-and-apps/).
7
+
8
+ README has been updated to reflect the additions to the API. There has also
9
+ been plenty of refactoring done under the covers.
10
+
3
11
  ## 1.1 / July 20, 2012
4
12
 
5
13
  Changes in response to feedback from [Matias Korhonen](https://github.com/k33l0r):
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem "rake"
4
+
3
5
  platform :jruby do
4
6
  gem "jruby-openssl"
5
7
  end
data/README.md CHANGED
@@ -49,19 +49,26 @@ client = Kippt::Client.new(username: "vesan", token: "2544d6bfddf5893ec8617")
49
49
  # Methods called on `client` will use the passed credentials
50
50
  ```
51
51
 
52
+ Or you can use the API unauthenticated:
53
+
54
+ ```ruby
55
+ client = Kippt::Client.new(unauthenticated: true)
56
+ ```
57
+
52
58
 
53
59
  ### Account
54
60
 
55
- You can get the account details (username and token):
61
+ You can get the current authenticated user:
56
62
 
57
63
  ```ruby
58
64
  client = Kippt::Client.new(username: "vesan", token: "2544d6bfddf5893ec8617")
59
65
  account = client.account
60
66
  account.username #=> "vesan"
61
- account.token #=> "2544d6bfddf5893ec8617"
67
+ account = client.account(true) # includes the API token
68
+ account.api_token #=> "2544d6bfddf5893ec8617"
62
69
  ```
63
70
 
64
- Always use token instead of the password if possible because it's more secure.
71
+ Always use the API token instead of the password if possible because it's more secure.
65
72
 
66
73
 
67
74
  ### Resources
@@ -168,6 +175,16 @@ clip = client.clips[clip_id]
168
175
  clip.destroy #=> true
169
176
  ```
170
177
 
178
+ ### Debugging
179
+
180
+ To get more information on what is going on under the covers, set `DEBUG=true`
181
+ as environment variable or pass `debug: true` in the Kippt::Client options hash
182
+ like:
183
+
184
+ ```ruby
185
+ client = Kippt::Client.new(unauthenticated: true, debug: true)
186
+ ```
187
+
171
188
 
172
189
  ## Contributing
173
190
 
data/TODO.md ADDED
@@ -0,0 +1,43 @@
1
+ * [DONE] Adding support for unauthenticated requests
2
+ * [DONE] Timestamps as time
3
+ * Better exceptions
4
+
5
+ # Clip
6
+
7
+ * [DONE] Getting the feed of authenticated user
8
+ * [DONE] Getting the user of the clip
9
+ * [DONE] Support for including list & via data
10
+ * [DONE] Getting the list of the clip
11
+ * [DONE] Getting the original clip the clip was created via
12
+ * [DONE] Getting the comments of a clip
13
+ * [DONE] Getting the likes of a clip
14
+ * [DONE] Getting the saves of a clip
15
+ * [DONE] Getting and setting is favorited (How does is starred work with it?)
16
+ * [DONE] Getting the media of the clip (experimental)
17
+ * [DONE] Getting the type of the clip
18
+ * [DONE] Getting the favicon url of the clip
19
+ * [DONE] Getting the app url fo the clip
20
+
21
+ # User
22
+
23
+ * [DONE] Support for all the user API's (still missing following/unfollowing users)
24
+ * [DONE] Getting the user for the authenticated user
25
+ * [DONE] Getting the embedded user for likes
26
+ * [DONE] Getting the counts
27
+
28
+ # List
29
+
30
+ * [DONE] Support for is private
31
+ * [DONE] Getting the user
32
+ * [DONE] Getting & setting the description
33
+ * [DONE] Getting the collaborators of the list
34
+ * [DONE] Getting the app url fo the clip
35
+ * [DONE] Getting the rss url fo the clip
36
+ * [DONE] Add support for following and unfollowing a list
37
+ * [DONE] Handle "List not found" error
38
+
39
+ # Comment
40
+
41
+ * [DONE] Add support for comments
42
+
43
+
@@ -15,11 +15,11 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Kippt::VERSION
17
17
 
18
- gem.add_dependency "faraday", "~> 0.7.6"
19
- gem.add_dependency "faraday_middleware", "~> 0.8.7"
20
- gem.add_dependency "multi_json", "~> 1.3.4"
18
+ gem.add_dependency "faraday", "~> 0.8"
19
+ gem.add_dependency "faraday_middleware", "~> 0.8"
20
+ gem.add_dependency "multi_json", "~> 1.3"
21
21
 
22
- gem.add_development_dependency "rspec", "~> 2.9.0"
23
- gem.add_development_dependency "webmock", "~> 1.8.6"
24
- gem.add_development_dependency "simplecov", "~> 0.6.4"
22
+ gem.add_development_dependency "rspec", "~> 2.9"
23
+ gem.add_development_dependency "webmock", "~> 1.11"
24
+ gem.add_development_dependency "simplecov", "~> 0.7"
25
25
  end
@@ -1,7 +1,7 @@
1
1
  require "kippt/connection"
2
- require "kippt/account"
3
2
  require "kippt/clips"
4
3
  require "kippt/lists"
4
+ require "kippt/users"
5
5
 
6
6
  class Kippt::Client
7
7
  include Kippt::Connection
@@ -9,18 +9,42 @@ class Kippt::Client
9
9
  attr_reader :username, :token, :password
10
10
 
11
11
  def initialize(options = {})
12
- @username = options.fetch(:username) { raise ArgumentError.new("username is required") }
12
+ self.debug = options.fetch(:debug, nil)
13
13
 
14
- @password = options.fetch(:password) { nil }
15
- @token = options.fetch(:token) { nil }
14
+ if options[:unauthenticated]
15
+ # Unauthenticated
16
+ else
17
+ @username = options.fetch(:username) { raise ArgumentError.new("username is required") }
16
18
 
17
- if @password.nil? && @token.nil?
18
- raise ArgumentError.new("password or token is required")
19
+ @password = options.fetch(:password) { nil }
20
+ @token = options.fetch(:token) { nil }
21
+
22
+ if @password.nil? && @token.nil?
23
+ raise ArgumentError.new("password or token is required")
24
+ end
19
25
  end
20
26
  end
21
27
 
22
- def account
23
- Kippt::Account.new(get("account").body)
28
+ def debug=(value)
29
+ @debug = value
30
+ end
31
+
32
+ def debug
33
+ if @debug.nil?
34
+ !!ENV["DEBUG"]
35
+ else
36
+ @debug
37
+ end
38
+ end
39
+ alias_method :debug?, :debug
40
+
41
+ def account(include_api_token = false)
42
+ if include_api_token
43
+ url = "account?include_data=api_token"
44
+ else
45
+ url = "account"
46
+ end
47
+ Kippt::User.new(get(url).body, self)
24
48
  end
25
49
 
26
50
  def lists
@@ -30,4 +54,18 @@ class Kippt::Client
30
54
  def clips
31
55
  Kippt::Clips.new(self)
32
56
  end
57
+
58
+ def users
59
+ Kippt::Users.new(self)
60
+ end
61
+
62
+ def collection_resource_for(resource_class, *options)
63
+ resource_class.new(*([self] + options))
64
+ end
65
+
66
+ def resource_from_url(resource_class, url)
67
+ raise ArgumentError.new("The parameter URL can't be blank") if url.nil? || url == ""
68
+
69
+ resource_class.new(self.get(url).body, self)
70
+ end
33
71
  end
@@ -1,11 +1,90 @@
1
- require "ostruct"
2
1
  require "kippt/resource"
2
+ require "kippt/list"
3
+ require "kippt/comments"
4
+ require "kippt/likes"
5
+ require "kippt/saves"
6
+ require "kippt/user"
3
7
 
4
8
  class Kippt::Clip
5
9
  include Kippt::Resource
6
10
 
7
- attributes :url_domain, :updated, :is_starred, :title,
8
- :url, :notes, :created, :list, :id, :resource_uri
11
+ attributes :url_domain, :is_starred, :title,
12
+ :url, :notes, :id, :resource_uri,
13
+ :type, :favicon_url, :app_url, :media,
14
+ :updated => Time, :created => Time,
15
+ :user => Kippt::User
9
16
 
10
- writable_attributes :is_starred, :title, :url, :notes, :list
17
+ writable_attributes :is_favorite, :title, :url, :notes, :list
18
+
19
+ boolean_attributes :is_favorite
20
+
21
+ alias :is_starred :is_favorite
22
+ alias :is_starred= :is_favorite=
23
+ alias :starred? :favorite?
24
+
25
+ embedded_attributes :list => "Kippt::List", :via => "Kippt::Clip"
26
+
27
+ def collection_resource_class
28
+ Kippt::Clips
29
+ end
30
+
31
+ def list_uri
32
+ if attributes.list.is_a? String
33
+ attributes.list
34
+ else
35
+ list.resource_uri
36
+ end
37
+ end
38
+
39
+ def comments
40
+ Kippt::Comments.new(client, self)
41
+ end
42
+
43
+ def all_comments_embedded?
44
+ comments_count == comments_data.size
45
+ end
46
+
47
+ def comments_count
48
+ attributes.comments["count"]
49
+ end
50
+
51
+ def comments_data
52
+ attributes.comments["data"]
53
+ end
54
+
55
+ def likes
56
+ Kippt::Likes.new(client, self)
57
+ end
58
+
59
+ def all_likes_embedded?
60
+ likes_count == likes_data.size
61
+ end
62
+
63
+ def likes_count
64
+ attributes.likes["count"]
65
+ end
66
+
67
+ def likes_data
68
+ attributes.likes["data"]
69
+ end
70
+
71
+ def saves
72
+ Kippt::Saves.new(client, self)
73
+ end
74
+
75
+ def saves_count
76
+ attributes.saves["count"]
77
+ end
78
+
79
+ def saves_data
80
+ attributes.saves["data"]
81
+ end
82
+
83
+ def like
84
+ Kippt::Like.new(client, self).save
85
+ end
86
+
87
+ def unlike
88
+ Kippt::Like.new(client, self).destroy
89
+ end
11
90
  end
@@ -7,4 +7,8 @@ class Kippt::ClipCollection
7
7
  def object_class
8
8
  Kippt::Clip
9
9
  end
10
+
11
+ def collection_resource_class
12
+ Kippt::Clips
13
+ end
10
14
  end
@@ -27,17 +27,21 @@ class Kippt::Clips
27
27
  "clips"
28
28
  end
29
29
 
30
+ def feed
31
+ Kippt::ClipCollection.new(client.get("clips/feed").body, client)
32
+ end
33
+
30
34
  def search(parameters)
31
35
  if parameters.is_a?(String)
32
36
  Kippt::ClipCollection.new(
33
- @client.get("search/clips", {:q => parameters}).body,
34
- self)
37
+ client.get("search/clips", {:q => parameters}).body,
38
+ client)
35
39
  else
36
40
  validate_search_parameters(parameters)
37
41
 
38
42
  Kippt::ClipCollection.new(
39
- @client.get("search/clips", parameters).body,
40
- self)
43
+ client.get("search/clips", parameters).body,
44
+ client)
41
45
  end
42
46
  end
43
47
 
@@ -3,21 +3,24 @@ module Kippt::Collection
3
3
 
4
4
  attr_reader :total_count, :limit, :offset
5
5
 
6
- def initialize(data, collection_resource = nil)
7
- meta = data.fetch("meta")
8
- @limit = meta.fetch("limit")
9
- @offset = meta.fetch("offset")
6
+ def initialize(data, client = nil)
7
+ meta = data.fetch("meta") { {} }
8
+ @limit = meta.fetch("limit") { nil }
9
+ @offset = meta.fetch("offset") { nil }
10
10
  @next = meta.fetch("next") { nil }
11
11
  @previous = meta.fetch("previous") { nil }
12
- @total_count = meta.fetch("total_count")
12
+ @total_count = meta.fetch("total_count") { nil }
13
13
 
14
- @collection_resource = collection_resource
14
+ @client = client
15
15
 
16
16
  @object_data = data.fetch("objects")
17
17
  end
18
18
 
19
+ extend Forwardable
20
+ def_delegators :objects, :size, :length
21
+
19
22
  def objects
20
- @objects ||= @object_data.map {|data| object_class.new(data, @collection_resource) }
23
+ @objects ||= @object_data.map {|data| object_class.new(data, client) }
21
24
  end
22
25
 
23
26
  def [](index)
@@ -35,7 +38,7 @@ module Kippt::Collection
35
38
  def next_page
36
39
  raise Kippt::APIError.new("There is no next page") if @next.nil? || @next == ""
37
40
 
38
- @collection_resource.collection_from_url(@next)
41
+ collection_resource.collection_from_url(@next)
39
42
  end
40
43
 
41
44
  def previous_page?
@@ -46,7 +49,17 @@ module Kippt::Collection
46
49
  def previous_page
47
50
  raise Kippt::APIError.new("There is no previous page") if @previous.nil? || @previous == ""
48
51
 
49
- @collection_resource.collection_from_url(@previous)
52
+ collection_resource.collection_from_url(@previous)
50
53
  end
51
54
  alias_method :prev_page, :previous_page
55
+
56
+ private
57
+
58
+ def collection_resource
59
+ @collection_resource ||= client.collection_resource_for(collection_resource_class)
60
+ end
61
+
62
+ def client
63
+ @client
64
+ end
52
65
  end
@@ -1,16 +1,29 @@
1
1
  module Kippt::CollectionResource
2
+ # For certain objects you can get extra data by giving option `include_data`.
3
+ # For example with clips you can add `include_data: "list,via"`.
2
4
  def all(options = {})
3
5
  validate_collection_options(options)
4
6
 
5
- collection_class.new(@client.get(base_uri, options).body, self)
7
+ collection_class.new(client.get(base_uri, options).body, client)
6
8
  end
7
9
 
8
10
  def build(attributes = {})
9
- object_class.new(attributes, self)
11
+ object_class.new(attributes, client)
10
12
  end
11
13
 
12
- def [](resource_id)
13
- object_class.new(@client.get("#{base_uri}/#{resource_id}").body)
14
+ def create(attributes = {})
15
+ build(attributes).save
16
+ end
17
+
18
+ # For certain objects you can get extra data by giving option `include_data`.
19
+ # For example with clips you can add `include_data: "list,via"`.
20
+ def [](resource_id, options = {})
21
+ response = client.get("#{base_uri}/#{resource_id}", options)
22
+ if response.success?
23
+ object_class.new(response.body, client)
24
+ else
25
+ raise Kippt::APIError.new("Resource could not be loaded: #{response.body["message"]}")
26
+ end
14
27
  end
15
28
 
16
29
  alias find []
@@ -18,14 +31,14 @@ module Kippt::CollectionResource
18
31
  def collection_from_url(url)
19
32
  raise ArgumentError.new("The parameter URL can't be blank") if url.nil? || url == ""
20
33
 
21
- collection_class.new(@client.get(url).body, self)
34
+ collection_class.new(client.get(url).body, client)
22
35
  end
23
36
 
24
37
  def save_resource(object)
25
38
  if object.id
26
- response = @client.put("#{base_uri}/#{object.id}", :data => writable_parameters_from(object))
39
+ response = client.put("#{base_uri}/#{object.id}", :data => writable_parameters_from(object))
27
40
  else
28
- response = @client.post("#{base_uri}", :data => writable_parameters_from(object))
41
+ response = client.post("#{base_uri}", :data => writable_parameters_from(object))
29
42
  end
30
43
 
31
44
  save_response = {:success => response.success?}
@@ -39,7 +52,7 @@ module Kippt::CollectionResource
39
52
 
40
53
  def destroy_resource(resource)
41
54
  if resource.id
42
- @client.delete("#{base_uri}/#{resource.id}").success?
55
+ client.delete("#{base_uri}/#{resource.id}").success?
43
56
  end
44
57
  end
45
58
 
@@ -56,4 +69,8 @@ module Kippt::CollectionResource
56
69
  def writable_parameters_from(resource)
57
70
  resource.writable_attributes_hash
58
71
  end
72
+
73
+ def client
74
+ @client
75
+ end
59
76
  end