instagram 0.3.2 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/.gitignore +12 -0
  2. data/.rspec +3 -0
  3. data/.yardopts +9 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.md +20 -0
  6. data/README.md +144 -55
  7. data/Rakefile +21 -9
  8. data/instagram.gemspec +41 -0
  9. data/lib/faraday/oauth2.rb +36 -0
  10. data/lib/faraday/raise_http_4xx.rb +37 -0
  11. data/lib/faraday/raise_http_5xx.rb +29 -0
  12. data/lib/instagram.rb +21 -55
  13. data/lib/instagram/api.rb +23 -0
  14. data/lib/instagram/client.rb +19 -0
  15. data/lib/instagram/client/comments.rb +62 -0
  16. data/lib/instagram/client/likes.rb +58 -0
  17. data/lib/instagram/client/locations.rb +59 -0
  18. data/lib/instagram/client/media.rb +63 -0
  19. data/lib/instagram/client/real_time.rb +8 -0
  20. data/lib/instagram/client/subscriptions.rb +141 -0
  21. data/lib/instagram/client/tags.rb +59 -0
  22. data/lib/instagram/client/users.rb +165 -0
  23. data/lib/instagram/client/utils.rb +15 -0
  24. data/lib/instagram/configuration.rb +90 -0
  25. data/lib/instagram/connection.rb +31 -0
  26. data/lib/instagram/error.rb +16 -0
  27. data/lib/instagram/oauth.rb +27 -0
  28. data/lib/instagram/request.rb +45 -0
  29. data/lib/instagram/version.rb +3 -0
  30. data/spec/faraday/response_spec.rb +28 -0
  31. data/spec/fixtures/access_token.json +9 -0
  32. data/spec/fixtures/followed_by.json +1 -0
  33. data/spec/fixtures/follows.json +1 -0
  34. data/spec/fixtures/location.json +1 -0
  35. data/spec/fixtures/location_recent_media.json +1 -0
  36. data/spec/fixtures/location_search.json +1 -0
  37. data/spec/fixtures/media.json +1 -0
  38. data/spec/fixtures/media_comment.json +1 -0
  39. data/spec/fixtures/media_comment_deleted.json +1 -0
  40. data/spec/fixtures/media_comments.json +1 -0
  41. data/spec/fixtures/media_liked.json +1 -0
  42. data/spec/fixtures/media_likes.json +1 -0
  43. data/spec/fixtures/media_popular.json +1 -0
  44. data/spec/fixtures/media_search.json +1 -0
  45. data/spec/fixtures/media_unliked.json +1 -0
  46. data/spec/fixtures/mikeyk.json +1 -0
  47. data/spec/fixtures/recent_media.json +1 -0
  48. data/spec/fixtures/requested_by.json +12 -0
  49. data/spec/fixtures/shayne.json +1 -0
  50. data/spec/fixtures/subscription.json +12 -0
  51. data/spec/fixtures/subscription_deleted.json +1 -0
  52. data/spec/fixtures/subscription_payload.json +14 -0
  53. data/spec/fixtures/subscriptions.json +22 -0
  54. data/spec/fixtures/tag.json +1 -0
  55. data/spec/fixtures/tag_recent_media.json +1 -0
  56. data/spec/fixtures/tag_search.json +1 -0
  57. data/spec/fixtures/user_media_feed.json +1 -0
  58. data/spec/fixtures/user_search.json +1 -0
  59. data/spec/instagram/api_spec.rb +110 -0
  60. data/spec/instagram/client/comments_spec.rb +71 -0
  61. data/spec/instagram/client/likes_spec.rb +66 -0
  62. data/spec/instagram/client/locations_spec.rb +78 -0
  63. data/spec/instagram/client/media_spec.rb +78 -0
  64. data/spec/instagram/client/real_time_spec.rb +13 -0
  65. data/spec/instagram/client/subscriptions_spec.rb +118 -0
  66. data/spec/instagram/client/tags_spec.rb +78 -0
  67. data/spec/instagram/client/users_spec.rb +237 -0
  68. data/spec/instagram/client_spec.rb +23 -0
  69. data/spec/instagram_spec.rb +97 -0
  70. data/spec/spec_helper.rb +54 -0
  71. metadata +265 -33
  72. data/MIT-LICENSE +0 -18
  73. data/lib/instagram/cached.rb +0 -25
  74. data/lib/instagram/failsafe_store.rb +0 -52
  75. data/lib/instagram/models.rb +0 -163
@@ -0,0 +1,23 @@
1
+ require File.expand_path('../connection', __FILE__)
2
+ require File.expand_path('../request', __FILE__)
3
+ require File.expand_path('../oauth', __FILE__)
4
+
5
+ module Instagram
6
+ # @private
7
+ class API
8
+ # @private
9
+ attr_accessor *Configuration::VALID_OPTIONS_KEYS
10
+
11
+ # Creates a new API
12
+ def initialize(options={})
13
+ options = Instagram.options.merge(options)
14
+ Configuration::VALID_OPTIONS_KEYS.each do |key|
15
+ send("#{key}=", options[key])
16
+ end
17
+ end
18
+
19
+ include Connection
20
+ include Request
21
+ include OAuth
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ module Instagram
2
+ # Wrapper for the Instagram REST API
3
+ #
4
+ # @note All methods have been separated into modules and follow the same grouping used in {TODO:doc_URL the Instagram API Documentation}.
5
+ # @see TODO:doc_url
6
+ class Client < API
7
+ Dir[File.expand_path('../client/*.rb', __FILE__)].each{|f| require f}
8
+
9
+ include Instagram::Client::Utils
10
+
11
+ include Instagram::Client::Users
12
+ include Instagram::Client::Media
13
+ include Instagram::Client::Locations
14
+ include Instagram::Client::Tags
15
+ include Instagram::Client::Comments
16
+ include Instagram::Client::Likes
17
+ include Instagram::Client::Subscriptions
18
+ end
19
+ end
@@ -0,0 +1,62 @@
1
+ module Instagram
2
+ class Client
3
+ # Defines methods related to comments
4
+ module Comments
5
+ # Returns a list of comments for a given media item ID
6
+ #
7
+ # @overload media_comments(id)
8
+ # @param id [Integer] An Instagram media item ID
9
+ # @return [Hashie::Mash] The requested comment.
10
+ # @example Returns a list of comments for the media item of ID 1234
11
+ # Instagram.media_comments(777)
12
+ # @format :json
13
+ # @authenticated true
14
+ #
15
+ # If getting this data of a protected user, you must be authenticated (and be allowed to see that user).
16
+ # @rate_limited true
17
+ # @see TODO:docs url
18
+ def media_comments(id, *args)
19
+ response = get("media/#{id}/comments")
20
+ response["data"]
21
+ end
22
+
23
+ # Creates a comment for a given media item ID
24
+ #
25
+ # @overload create_media_comment(id, text)
26
+ # @param id [Integer] An Instagram media item ID
27
+ # @param text [String] The text of your comment
28
+ # @return [Hashie::Mash] The comment created.
29
+ # @example Creates a new comment on media item with ID 777
30
+ # Instagram.create_media_comment(777, "Oh noes!")
31
+ # @format :json
32
+ # @authenticated true
33
+ #
34
+ # If getting this data of a protected user, you must be authenticated (and be allowed to see that user).
35
+ # @rate_limited true
36
+ # @see TODO:docs url
37
+ def create_media_comment(id, text, options={})
38
+ response = post("media/#{id}/comments", options.merge(:text => text))
39
+ response["data"]
40
+ end
41
+
42
+ # Deletes a comment for a given media item ID
43
+ #
44
+ # @overload delete_media_comment(media_id, comment_id)
45
+ # @param media_id [Integer] An Instagram media item ID.
46
+ # @param comment_id [Integer] Your comment ID of the comment you wish to delete.
47
+ # @return [nil]
48
+ # @example Delete the comment with ID of 1234, on the media item with ID of 777
49
+ # Instagram.delete_media_comment(777, 1234)
50
+ # @format :json
51
+ # @authenticated true
52
+ #
53
+ # In order to remove a comment, you must be either the owner comment or the media item (or both).
54
+ # @rate_limited true
55
+ # @see TODO:docs url
56
+ def delete_media_comment(media_id, comment_id, options={})
57
+ response = delete("media/#{media_id}/comments/#{comment_id}", options)
58
+ response["data"]
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,58 @@
1
+ module Instagram
2
+ class Client
3
+ # Defines methods related to likes
4
+ module Likes
5
+ # Returns a list of users who like a given media item ID
6
+ #
7
+ # @overload media_likes(id)
8
+ # @param media [Integer] An Instagram media item ID
9
+ # @return [Hashie::Mash] A list of users.
10
+ # @example Returns a list of users who like the media item of ID 1234
11
+ # Instagram.media_likes(777)
12
+ # @format :json
13
+ # @authenticated true
14
+ #
15
+ # If getting this data of a protected user, you must be authenticated (and be allowed to see that user).
16
+ # @rate_limited true
17
+ # @see TODO:docs url
18
+ def media_likes(id, *args)
19
+ response = get("media/#{id}/likes")
20
+ response["data"]
21
+ end
22
+
23
+ # Issues a like by the currently authenticated user, for a given media item ID
24
+ #
25
+ # @overload like_media(id, text)
26
+ # @param id [Integer] An Instagram media item ID
27
+ # @return [nil]
28
+ # @example Like media item with ID 777
29
+ # Instagram.like_media(777)
30
+ # @format :json
31
+ # @authenticated true
32
+ #
33
+ # If getting this data of a protected user, you must be authenticated (and be allowed to see that user).
34
+ # @rate_limited true
35
+ # @see TODO:docs url
36
+ def like_media(id, options={})
37
+ response = post("media/#{id}/likes", options)
38
+ response["data"]
39
+ end
40
+
41
+ # Removes the like on a givem media item ID for the currently authenticated user
42
+ #
43
+ # @overload unlike_media(id)
44
+ # @param media_id [Integer] An Instagram media item ID.
45
+ # @return [nil]
46
+ # @example Remove the like for the currently authenticated user on the media item with the ID of 777
47
+ # Instagram.unlike_media(777)
48
+ # @format :json
49
+ # @authenticated true
50
+ # @rate_limited true
51
+ # @see TODO:docs url
52
+ def unlike_media(id, options={})
53
+ response = delete("media/#{id}/likes", options)
54
+ response["data"]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,59 @@
1
+ module Instagram
2
+ class Client
3
+ # Defines methods related to media items
4
+ module Locations
5
+ # Returns extended information of a given Instagram location
6
+ #
7
+ # @overload location(id)
8
+ # @param location [Integer] An Instagram location ID
9
+ # @return [Hashie::Mash] The requested location.
10
+ # @example Return extended information for the Instagram office
11
+ # Instagram.location(514276)
12
+ # @format :json
13
+ # @authenticated false
14
+ # @rate_limited true
15
+ # @see TODO:docs url
16
+ def location(id, *args)
17
+ response = get("locations/#{id}")
18
+ response["data"]
19
+ end
20
+
21
+ # Returns a list of recent media items for a given Instagram location
22
+ #
23
+ # @overload location_recent_media(id, options={})
24
+ # @param user [Integer] An Instagram user ID.
25
+ # @param options [Hash] A customizable set of options.
26
+ # @option options [Integer] :max_id (nil) Returns results with an ID less than (that is, older than) or equal to the specified ID.
27
+ # @option options [Integer] :count (nil) Limits the number of results returned per page, maximum 150.
28
+ # @return [Hashie::Mash]
29
+ # @example Return a list of the most recent media items taken at the Instagram office
30
+ # Instagram.location_recent_media(514276)
31
+ # @see TODO:docs url
32
+ # @format :json
33
+ # @authenticated false
34
+ # @rate_limited true
35
+ def location_recent_media(id, *args)
36
+ options = args.last.is_a?(Hash) ? args.pop : {}
37
+ response = get("locations/#{id}/media/recent", options)
38
+ response["data"]
39
+ end
40
+
41
+ # Returns Instagram locations within proximity of given lat,lng
42
+ #
43
+ # @param lat [String] A given latitude in decimal format
44
+ # @param lng [String] A given longitude in decimal format
45
+ # @option options [Integer] :count The number of media items to retrieve. Maxiumum of 100 allowed per page.
46
+ # @return [Array]
47
+ # @example Return locations around 37.7808851, -122.3948632 (164 S Park, SF, CA USA)
48
+ # Instagram.location_search("37.7808851", "-122.3948632")
49
+ # @see TODO:doc url
50
+ # @format :json
51
+ # @authenticated false
52
+ # @rate_limited true
53
+ def location_search(lat, lng, options={})
54
+ response = get('locations/search', options.merge(:lat => lat, :lng => lng))
55
+ response["data"]
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,63 @@
1
+ module Instagram
2
+ class Client
3
+ # Defines methods related to media items
4
+ module Media
5
+ # Returns extended information of a given media item
6
+ #
7
+ # @overload media_item(id)
8
+ # @param user [Integer] An Instagram media item ID
9
+ # @return [Hashie::Mash] The requested media item.
10
+ # @example Return extended information for media item 1234
11
+ # Instagram.media_item(1324)
12
+ # @format :json
13
+ # @authenticated false unless requesting media from a protected user
14
+ #
15
+ # If getting this data of a protected user, you must authenticate (and be allowed to see that user).
16
+ # @rate_limited true
17
+ # @see TODO:docs url
18
+ def media_item(*args)
19
+ id = args.first || 'self'
20
+ response = get("media/#{id}")
21
+ response["data"]
22
+ end
23
+
24
+ # Returns a list of the overall most popular media
25
+ #
26
+ # @overload media_popular(options={})
27
+ # @param options [Hash] A customizable set of options.
28
+ # @return [Hashie::Mash]
29
+ # @example Returns a list of the overall most popular media
30
+ # Instagram.media_popular
31
+ # @see TODO:docs url
32
+ # @format :json
33
+ # @authenticated false unless requesting it from a protected user
34
+ #
35
+ # If getting this data of a protected user, you must authenticate (and be allowed to see that user).
36
+ # @rate_limited true
37
+ def media_popular(*args)
38
+ options = args.last.is_a?(Hash) ? args.pop : {}
39
+ id = args.first || "self"
40
+ response = get("media/popular", options)
41
+ response["data"]
42
+ end
43
+
44
+ # Returns media items within proximity of given lat,lng
45
+ #
46
+ # @param lat [String] A given latitude in decimal format
47
+ # @param lng [String] A given longitude in decimal format
48
+ # @param options [Hash] A customizable set of options.
49
+ # @option options [Integer] :count The number of media items to retrieve. Maxiumum of 100 allowed per page.
50
+ # @return [Array]
51
+ # @example Return media around 37.7808851, -122.3948632 (164 S Park, SF, CA USA)
52
+ # Instagram.media_search("37.7808851", "-122.3948632")
53
+ # @see TODO:doc url
54
+ # @format :json
55
+ # @authenticated false
56
+ # @rate_limited true
57
+ def media_search(lat, lng, options={})
58
+ response = get('media/search', options.merge(:lat => lat, :lng => lng))
59
+ response["data"]
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,8 @@
1
+ module Instagram
2
+ class Client
3
+ # Defines methods related to real-time
4
+ module RealTime
5
+ # TODO
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,141 @@
1
+ module Instagram
2
+ class Client
3
+ # Defines methods related to real-time
4
+ module Subscriptions
5
+ # Returns a list of active real-time subscriptions
6
+ #
7
+ # @overload subscriptions(options={})
8
+ # @return [Hashie::Mash] The list of subscriptions.
9
+ # @example Returns a list of subscriptions for the authenticated application
10
+ # Instagram.subscriptions
11
+ # @format :json
12
+ # @authenticated true
13
+ #
14
+ # Requires client_secret to be set on the client or passed in options
15
+ # @rate_limited true
16
+ # @see https://api.instagram.com/developer/realtime/
17
+ def subscriptions(options={})
18
+ response = get("subscriptions", options.merge(:client_secret => client_secret))
19
+ response["data"]
20
+ end
21
+
22
+ # Creates a real-time subscription
23
+ #
24
+ # @overload create_subscription(options={})
25
+ # @param options [Hash] A set of parameters
26
+ # @option options [String] :object The object you'd like to subscribe to (user, tag, location or geography)
27
+ # @option options [String] :callback_url The subscription callback URL
28
+ # @option options [String] :aspect The aspect of the object you'd like to subscribe to (in this case, "media").
29
+ # @option options [String, Integer] :object_id When specifying a location or tag use the location's ID or tag name respectively
30
+ # @option options [String, Float] :lat The center latitude of an area, used when subscribing to a geography object
31
+ # @option options [String, Float] :lng The center longitude of an area, used when subscribing to a geography object
32
+ # @option options [String, Integer] :radius The distance in meters you'd like to capture around a given point (max 5000 meters)
33
+ # @overload create_subscription(object, callback_url, aspect="media", options={})
34
+ # @param object [String] The object you'd like to subscribe to (user, tag, location or geography)
35
+ # @param callback_url [String] The subscription callback URL
36
+ # @param aspect [String] he aspect of the object you'd like to subscribe to (in this case, "media").
37
+ # @param options [Hash] Addition options and parameters
38
+ # @option options [String, Integer] :object_id When specifying a location or tag use the location's ID or tag name respectively
39
+ # @option options [String, Float] :lat The center latitude of an area, used when subscribing to a geography object
40
+ # @option options [String, Float] :lng The center longitude of an area, used when subscribing to a geography object
41
+ # @option options [String, Integer] :radius The distance in meters you'd like to capture around a given point (max 5000 meters)
42
+ #
43
+ # Note that we only support "media" at this time, but we might support other types of subscriptions in the future.
44
+ # @return [Hashie::Mash] The subscription created.
45
+ # @example Creates a new subscription to receive notifications for user media changes.
46
+ # Instagram.create_subscription("user", "http://example.com/instagram/callback")
47
+ # @format :json
48
+ # @authenticated true
49
+ #
50
+ # Requires client_secret to be set on the client or passed in options
51
+ # @rate_limited true
52
+ # @see https://api.instagram.com/developer/realtime/
53
+ def create_subscription(*args)
54
+ options = args.last.is_a?(Hash) ? args.pop : {}
55
+ object = args.shift
56
+ callback_url = args.shift
57
+ aspect = args.shift
58
+ options.tap {|o|
59
+ o[:object] = object unless object.nil?
60
+ o[:callback_url] = callback_url unless callback_url.nil?
61
+ o[:aspect] = aspect || o[:aspect] || "media"
62
+ }
63
+ response = post("subscriptions", options.merge(:client_secret => client_secret))
64
+ response["data"]
65
+ end
66
+
67
+ # Deletes a real-time subscription
68
+ #
69
+ # @overload delete_subscription(options={})
70
+ # @param options [Hash] Addition options and parameters
71
+ # @option options [Integer] :subscription_id The subscription's ID
72
+ # @option options [String] :object When specified will remove all subscriptions of this object type, unless an :object_id is also specified (user, tag, location or geography)
73
+ # @option options [String, Integer] :object_id When specifying :object, inlcude an :object_id to only remove subscriptions of that object and object_id
74
+ # @overload delete_subscription(subscription_id, options={})
75
+ # @param subscription_id [Integer] The subscription's ID
76
+ # @param options [Hash] Addition options and parameters
77
+ # @option options [String] :object When specified will remove all subscriptions of this object type, unless an :object_id is also specified (user, tag, location or geography)
78
+ # @option options [String, Integer] :object_id When specifying :object, inlcude an :object_id to only remove subscriptions of that object and object_id
79
+ # @return [nil]
80
+ # @example Deletes an application's user change subscription
81
+ # Instagram.delete_subscription(:object => "user")
82
+ # @format :json
83
+ # @authenticated true
84
+ #
85
+ # Requires client_secret to be set on the client or passed in options
86
+ # @rate_limited true
87
+ # @see https://api.instagram.com/developer/realtime/
88
+ def delete_subscription(*args)
89
+ options = args.last.is_a?(Hash) ? args.pop : {}
90
+ subscription_id = args.first
91
+ options.merge!(:id => subscription_id) if subscription_id
92
+ response = delete("subscriptions", options.merge(:client_secret => client_secret))
93
+ response["data"]
94
+ end
95
+
96
+ # Process a subscription notification JSON payload
97
+ #
98
+ # @overload process_subscription(json, &block)
99
+ # @param json [String] The JSON response received by the Instagram real-time server
100
+ # @param block [Proc] A callable in which callbacks are defined
101
+ # @return [nil]
102
+ # @example Process and handle a notification for a user media change
103
+ # Instagram.process_subscription(params[:body]) do |handler|
104
+ #
105
+ # handler.on_user_changed do |user_id, data|
106
+ #
107
+ # user = User.by_instagram_id(user_id)
108
+ # @client = Instagram.client(:access_token => _access_token_for_user(user))
109
+ # latest_media = @client.user_recent_media[0]
110
+ # user.media.create_with_hash(latest_media)
111
+ # end
112
+ #
113
+ # end
114
+ # @format :json
115
+ # @authenticated true
116
+ #
117
+ # Requires client_secret to be set on the client or passed in options
118
+ # @rate_limited true
119
+ # @see https://api.instagram.com/developer/realtime/
120
+ def process_subscription(json, &block)
121
+ raise ArgumentError, "callbacks block expected" unless block_given?
122
+ payload = MultiJson.decode(json)
123
+ @changes = Hash.new { |h,k| h[k] = [] }
124
+ for change in payload
125
+ @changes[change['object']] << change
126
+ end
127
+ block.call(self)
128
+ end
129
+
130
+ [:user, :tag, :location, :geography].each do |object|
131
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ +1
132
+ def on_#{object}_changed(&block)
133
+ for change in @changes['#{object}']
134
+ yield change.delete('object_id'), change
135
+ end
136
+ end
137
+ RUBY_EVAL
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,59 @@
1
+ module Instagram
2
+ class Client
3
+ # Defines methods related to tags
4
+ module Tags
5
+ # Returns extended information of a given Instagram tag
6
+ #
7
+ # @overload tag(tag)
8
+ # @param tag [String] An Instagram tag name
9
+ # @return [Hashie::Mash] The requested tag.
10
+ # @example Return extended information for the tag "cat"
11
+ # Instagram.tag('cat')
12
+ # @format :json
13
+ # @authenticated false
14
+ # @rate_limited true
15
+ # @see TODO:docs url
16
+ def tag(tag, *args)
17
+ response = get("tags/#{tag}")
18
+ response["data"]
19
+ end
20
+
21
+ # Returns a list of recent media items for a given Instagram tag
22
+ #
23
+ # @overload tag_recent_media(tag, options={})
24
+ # @param user [String] An Instagram tag name.
25
+ # @param options [Hash] A customizable set of options.
26
+ # @option options [Integer] :max_id (nil) Returns results with an ID less than (that is, older than) or equal to the specified ID.
27
+ # @option options [Integer] :count (nil) Limits the number of results returned per page, maximum 150.
28
+ # @return [Hashie::Mash]
29
+ # @example Return a list of the most recent media items tagged "cat"
30
+ # Instagram.tag_recent_media('cat')
31
+ # @see TODO:docs url
32
+ # @format :json
33
+ # @authenticated false
34
+ # @rate_limited true
35
+ def tag_recent_media(id, *args)
36
+ options = args.last.is_a?(Hash) ? args.pop : {}
37
+ response = get("tags/#{id}/media/recent", options)
38
+ response["data"]
39
+ end
40
+
41
+ # Returns a list of tags starting with the given search query
42
+ #
43
+ # @format :json
44
+ # @authenticated false
45
+ # @rate_limited true
46
+ # @param query [String] The beginning or complete tag name to search for
47
+ # @param options [Hash] A customizable set of options.
48
+ # @option options [Integer] :count The number of media items to retrieve. Maxiumum of 100 allowed per page.
49
+ # @return [Array]
50
+ # @see TODO:doc url
51
+ # @example Return tags that start with "cat"
52
+ # Instagram.tag_search("cat")
53
+ def tag_search(query, options={})
54
+ response = get('tags/search', options.merge(:q => query))
55
+ response["data"]
56
+ end
57
+ end
58
+ end
59
+ end