kippt 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,25 @@
1
+ require "kippt/resource"
2
+ require "kippt/user"
3
+ require "kippt/clip"
4
+
5
+ class Kippt::Comment
6
+ include Kippt::Resource
7
+
8
+ attributes :body, :id, :resource_uri,
9
+ :created => Time, :user => Kippt::User
10
+
11
+ writable_attributes :body
12
+
13
+ attr_reader :clip
14
+
15
+ def initialize(attributes = {}, client = nil, clip = nil)
16
+ super(attributes, client)
17
+ @clip = clip
18
+ end
19
+
20
+ private
21
+
22
+ def collection_resource
23
+ @collection_resource ||= client.collection_resource_for(Kippt::Comments, clip)
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ require "kippt/collection"
2
+ require "kippt/comment"
3
+
4
+ class Kippt::CommentCollection
5
+ include Kippt::Collection
6
+
7
+ def object_class
8
+ Kippt::Comment
9
+ end
10
+ end
@@ -0,0 +1,43 @@
1
+ require "kippt/comment_collection"
2
+ require "kippt/comment"
3
+
4
+ class Kippt::Comments
5
+ include Kippt::CollectionResource
6
+
7
+ attr_reader :clip
8
+
9
+ def initialize(client, clip)
10
+ @client = client
11
+ @clip = clip
12
+ end
13
+
14
+ def self.valid_filter_parameters
15
+ [:limit, :offset]
16
+ end
17
+
18
+ def object_class
19
+ Kippt::Comment
20
+ end
21
+
22
+ def collection_class
23
+ Kippt::CommentCollection
24
+ end
25
+
26
+ def base_uri
27
+ "clips/#{clip.id}/comments"
28
+ end
29
+
30
+ def all(options = {})
31
+ validate_collection_options(options)
32
+
33
+ if options.empty? && @clip.all_comments_embedded?
34
+ collection_class.new({"objects" => @clip.comments_data}, client)
35
+ else
36
+ collection_class.new(client.get(base_uri, options).body, client)
37
+ end
38
+ end
39
+
40
+ def build(attributes = {})
41
+ object_class.new(attributes, client, clip)
42
+ end
43
+ end
@@ -2,6 +2,8 @@ require "multi_json"
2
2
  require "faraday_middleware/response_middleware"
3
3
 
4
4
  module Kippt::Connection
5
+ HOST = "kippt.com"
6
+
5
7
  class ParseMultiJson < FaradayMiddleware::ResponseMiddleware
6
8
  define_parser do |body|
7
9
  begin
@@ -31,22 +33,26 @@ module Kippt::Connection
31
33
  private
32
34
 
33
35
  def connection
34
- @connection ||= Faraday.new("https://kippt.com/api") do |builder|
36
+ @connection ||= Faraday.new("https://#{HOST}/api") do |builder|
35
37
  builder.use Kippt::Connection::ParseMultiJson
36
- # builder.use Faraday::Response::Logger
38
+ builder.use Faraday::Response::Logger if debug?
37
39
  builder.adapter Faraday.default_adapter
38
40
  end
39
41
  end
40
42
 
41
43
  def request(method, url, options)
44
+ if @password
45
+ connection.basic_auth(@username, @password)
46
+ end
47
+
42
48
  response = connection.send(method) do |req|
43
49
  set_default_headers(req)
44
50
 
45
- if @password
46
- connection.basic_auth(@username, @password)
47
- else
48
- req.headers["X-Kippt-Username"] = @username
49
- req.headers["X-Kippt-API-Token"] = @token
51
+ unless @password
52
+ if @username && @token
53
+ req.headers["X-Kippt-Username"] = @username
54
+ req.headers["X-Kippt-API-Token"] = @token
55
+ end
50
56
  end
51
57
 
52
58
  if method == :get
@@ -56,7 +62,7 @@ module Kippt::Connection
56
62
  req.body = MultiJson.dump(options[:data])
57
63
  end
58
64
  end
59
-
65
+
60
66
  if response.status == 401
61
67
  raise Kippt::APIError.new(response.body["message"])
62
68
  end
@@ -0,0 +1,50 @@
1
+ class Kippt::FollowRelationship
2
+ attr_reader :user
3
+
4
+ def initialize(client, user)
5
+ @client = client
6
+ @user = user
7
+ end
8
+
9
+ def following?
10
+ response = client.get("users/#{user.id}/relationship/")
11
+
12
+ if response.success?
13
+ response.body["following"]
14
+ else
15
+ raise Kippt::APIError.new("Resource could not be loaded: #{response.body["message"]}")
16
+ end
17
+ end
18
+
19
+ def follow
20
+ response = update_following(true)
21
+
22
+ if response.success?
23
+ true
24
+ else
25
+ raise Kippt::APIError.new("Problem with following: #{response.body["message"]}")
26
+ end
27
+ end
28
+
29
+ def unfollow
30
+ response = update_following(false)
31
+
32
+ if response.success?
33
+ true
34
+ else
35
+ raise Kippt::APIError.new("Problem with unfollowing: #{response.body["message"]}")
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def update_following(value)
42
+ follow_value = value ? "follow" : "unfollow"
43
+
44
+ client.post("users/#{user.id}/relationship/", :data => {:action => follow_value})
45
+ end
46
+
47
+ def client
48
+ @client
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ require "kippt/collection_resource"
2
+ require "kippt/user_collection"
3
+ require "kippt/user"
4
+
5
+ class Kippt::Followers
6
+ include Kippt::CollectionResource
7
+
8
+ attr_reader :user
9
+
10
+ def initialize(client, user)
11
+ @client = client
12
+ @user = user
13
+ end
14
+
15
+ def object_class
16
+ Kippt::User
17
+ end
18
+
19
+ def collection_class
20
+ Kippt::UserCollection
21
+ end
22
+
23
+ def base_uri
24
+ "users/#{user.id}/followers"
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ require "kippt/collection_resource"
2
+ require "kippt/user_collection"
3
+ require "kippt/user"
4
+
5
+ class Kippt::Following
6
+ include Kippt::CollectionResource
7
+
8
+ attr_reader :user
9
+
10
+ def initialize(client, user)
11
+ @client = client
12
+ @user = user
13
+ end
14
+
15
+ def object_class
16
+ Kippt::User
17
+ end
18
+
19
+ def collection_class
20
+ Kippt::UserCollection
21
+ end
22
+
23
+ def base_uri
24
+ "users/#{user.id}/following"
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ require "kippt/users"
2
+
3
+ class Kippt::Like
4
+ def initialize(client, clip)
5
+ @client = client
6
+ @errors = []
7
+ @clip = clip
8
+ end
9
+
10
+ def id
11
+ nil
12
+ end
13
+
14
+ def collection_resource_class
15
+ Kippt::Likes
16
+ end
17
+
18
+ def writable_attributes_hash
19
+ nil
20
+ end
21
+
22
+ def save
23
+ @errors = []
24
+ response = collection_resource.save_resource(self)
25
+ if response[:error_message]
26
+ errors << response[:error_message]
27
+ end
28
+ response[:success]
29
+ end
30
+
31
+ def destroy
32
+ collection_resource.destroy_resource(self)
33
+ end
34
+
35
+ private
36
+
37
+ def collection_resource
38
+ @collection_resource ||= client.collection_resource_for(collection_resource_class, clip)
39
+ end
40
+
41
+ def client
42
+ @client
43
+ end
44
+
45
+ def clip
46
+ @clip
47
+ end
48
+ end
@@ -0,0 +1,48 @@
1
+ require "kippt/user_collection"
2
+ require "kippt/user"
3
+ require "kippt/like"
4
+
5
+ class Kippt::Likes
6
+ include Kippt::CollectionResource
7
+
8
+ attr_reader :clip
9
+
10
+ def initialize(client, clip)
11
+ @client = client
12
+ @clip = clip
13
+ end
14
+
15
+ def self.valid_filter_parameters
16
+ []
17
+ end
18
+
19
+ def object_class
20
+ Kippt::User
21
+ end
22
+
23
+ def collection_class
24
+ Kippt::UserCollection
25
+ end
26
+
27
+ def base_uri
28
+ "clips/#{clip.id}/likes"
29
+ end
30
+
31
+ def all(options = {})
32
+ validate_collection_options(options)
33
+
34
+ if options.empty? && @clip.all_likes_embedded?
35
+ collection_class.new({"objects" => @clip.likes_data}, client)
36
+ else
37
+ collection_class.new(client.get(base_uri, options).body, client)
38
+ end
39
+ end
40
+
41
+ def build
42
+ Kippt::Like.new(client, clip)
43
+ end
44
+
45
+ def destroy_resource(resource)
46
+ client.delete(base_uri).success?
47
+ end
48
+ end
@@ -1,10 +1,35 @@
1
- require "ostruct"
1
+ require "kippt/user"
2
2
 
3
3
  class Kippt::List
4
4
  include Kippt::Resource
5
5
 
6
- attributes :id, :rss_url, :updated, :title,
7
- :created, :slug, :resource_uri
6
+ attributes :app_url, :id, :rss_url, :title,
7
+ :slug, :resource_uri, :description,
8
+ :updated => Time, :created => Time,
9
+ :user => Kippt::User
10
+ boolean_attributes :is_private
8
11
 
9
- writable_attributes :title
12
+ writable_attributes :title, :description
13
+
14
+ def collection_resource_class
15
+ Kippt::Lists
16
+ end
17
+
18
+ def collaborators
19
+ attributes.collaborators["data"].map {|collborator_data|
20
+ Kippt::User.new(collborator_data, client)
21
+ }
22
+ end
23
+
24
+ def follow
25
+ response = client.post("#{resource_uri}relationship", :data => {:action => "follow"})
26
+ raise Kippt::APIError.new("There was an error with the request: #{response.body["message"]}") unless response.success?
27
+ response.success?
28
+ end
29
+
30
+ def unfollow
31
+ response = client.post("#{resource_uri}relationship", :data => {:action => "unfollow"})
32
+ raise Kippt::APIError.new("There was an error with the request: #{response.body["message"]}") unless response.success?
33
+ response.success?
34
+ end
10
35
  end
@@ -7,4 +7,8 @@ class Kippt::ListCollection
7
7
  def object_class
8
8
  Kippt::List
9
9
  end
10
+
11
+ def collection_resource_class
12
+ Kippt::Lists
13
+ end
10
14
  end
@@ -1,10 +1,12 @@
1
+ require "ostruct"
2
+
1
3
  module Kippt::Resource
2
4
  def self.included(base)
3
5
  base.instance_eval do
4
6
  extend Forwardable
5
7
  attr_reader :attributes, :errors
6
8
 
7
- def_delegators "self.class", :writable_attribute_names, :attribute_names
9
+ def_delegators "self.class", :writable_attribute_names, :attribute_names, :embedded_attribute_names
8
10
  end
9
11
 
10
12
  base.extend(ClassMethods)
@@ -13,31 +15,116 @@ module Kippt::Resource
13
15
  module ClassMethods
14
16
  attr_reader :writable_attribute_names, :attribute_names
15
17
 
18
+ def embedded_attribute_names
19
+ @embedded_attribute_names || []
20
+ end
21
+
16
22
  def attributes(*attribs)
17
- @attribute_names = attribs.map {|attrib| attrib.to_sym }
18
- def_delegators :attributes, *@attribute_names
23
+ @attribute_names ||= []
24
+ hashes, other = attribs.partition {|attrib| attrib.is_a? Hash }
25
+
26
+ attribute_names = convert_to_symbols(other)
27
+ def_delegators :attributes, *attribute_names
28
+ @attribute_names += attribute_names
29
+
30
+ mappings = hashes.reduce({}, :update)
31
+ mappings.each do |attrib, object_class|
32
+ if object_class.to_s == "Time"
33
+ define_method(attrib) do
34
+ Time.at(attributes.send(attrib))
35
+ end
36
+ else
37
+ reified_class = _get_class(object_class)
38
+ define_method(attrib) do
39
+ value = attributes.send(attrib)
40
+ reified_class.new(value) unless value.nil?
41
+ end
42
+ end
43
+ end
44
+ @attribute_names += convert_to_symbols(mappings.keys)
45
+ end
46
+
47
+ def boolean_attributes(*attribs)
48
+ @attribute_names ||= []
49
+ attribute_names = convert_to_symbols(attribs)
50
+ def_delegators :attributes, *attribute_names
51
+ @attribute_names += attribute_names
52
+
53
+ attribs.each do |attribute_name|
54
+ if result = attribute_name.to_s.match(/\Ais\_(.*)/)
55
+ define_method "#{result[1]}?" do
56
+ send(attribute_name) if respond_to?(attribute_name)
57
+ end
58
+ end
59
+ end
19
60
  end
20
61
 
21
62
  def writable_attributes(*attribs)
22
- @writable_attribute_names = attribs.map {|attrib| attrib.to_sym }
63
+ @writable_attribute_names = convert_to_symbols(attribs)
23
64
  @writable_attribute_names.freeze
24
- def_delegators :attributes, *(@writable_attribute_names.map {|attrib| attrib.to_s + "=" })
65
+ def_delegators :attributes, *(@writable_attribute_names.map {|attrib| attrib.to_s + "=" }).dup
66
+ end
67
+
68
+ def embedded_attributes(*attribs)
69
+ mappings = attribs.reduce({}, :update)
70
+ @embedded_attribute_names = convert_to_symbols(mappings.keys)
71
+ @embedded_attribute_names.freeze
72
+
73
+ mappings.each do |attrib, attribute_class|
74
+ reified_class = _get_class(attribute_class)
75
+ define_method(attrib) do
76
+ value = attributes.send(attrib)
77
+ if value.is_a? String
78
+ client.resource_from_url(reified_class, value)
79
+ elsif value.is_a? reified_class
80
+ value
81
+ else
82
+ reified_class.new(value, client)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def convert_to_symbols(list)
91
+ list.map {|item| item.to_sym }
92
+ end
93
+
94
+ protected
95
+
96
+ def _get_class(camel_cased_word)
97
+ if camel_cased_word.is_a? Class
98
+ camel_cased_word
99
+ else
100
+ names = camel_cased_word.split('::')
101
+ names.shift if names.empty? || names.first.empty?
102
+
103
+ constant = Object
104
+ names.each do |name|
105
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
106
+ end
107
+ constant
108
+ end
25
109
  end
26
110
  end
27
111
 
28
- def initialize(attributes = {}, collection_resource = nil)
112
+ def initialize(attributes = {}, client = nil)
29
113
  @attributes = OpenStruct.new(attributes)
30
114
  @errors = []
31
- @collection_resource = collection_resource
115
+ @client = client
32
116
  end
33
117
 
34
118
  def destroy
35
- @collection_resource.destroy_resource(self)
119
+ collection_resource.destroy_resource(self)
36
120
  end
37
121
 
38
122
  def writable_attributes_hash
39
123
  writable_attribute_names.inject({}) do |parameters, attribute_name|
40
124
  value = self.send(attribute_name)
125
+ if embedded_attribute_names.include?(attribute_name) && !value.is_a?(String)
126
+ value = value.resource_uri
127
+ end
41
128
  unless value.nil?
42
129
  parameters[attribute_name] = value
43
130
  end
@@ -47,7 +134,7 @@ module Kippt::Resource
47
134
 
48
135
  def save
49
136
  @errors = []
50
- response = @collection_resource.save_resource(self)
137
+ response = collection_resource.save_resource(self)
51
138
  if response[:error_message]
52
139
  errors << response[:error_message]
53
140
  else
@@ -60,4 +147,14 @@ module Kippt::Resource
60
147
  end
61
148
  response[:success]
62
149
  end
150
+
151
+ private
152
+
153
+ def collection_resource
154
+ @collection_resource ||= client.collection_resource_for(collection_resource_class)
155
+ end
156
+
157
+ def client
158
+ @client
159
+ end
63
160
  end