localist-instagvram 0.6.2

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 (71) 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 -0
  7. data/Rakefile +22 -0
  8. data/instagram.gemspec +42 -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 +26 -0
  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 +156 -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 +19 -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 +148 -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 +307 -0
@@ -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,156 @@
1
+ require 'hmac-sha1'
2
+
3
+ module Instagram
4
+ class Client
5
+ # Defines methods related to real-time
6
+ module Subscriptions
7
+ # Returns a list of active real-time subscriptions
8
+ #
9
+ # @overload subscriptions(options={})
10
+ # @return [Hashie::Mash] The list of subscriptions.
11
+ # @example Returns a list of subscriptions for the authenticated application
12
+ # Instagram.subscriptions
13
+ # @format :json
14
+ # @authenticated true
15
+ #
16
+ # Requires client_secret to be set on the client or passed in options
17
+ # @rate_limited true
18
+ # @see https://api.instagram.com/developer/realtime/
19
+ def subscriptions(options={})
20
+ response = get("subscriptions", options.merge(:client_secret => client_secret))
21
+ response["data"]
22
+ end
23
+
24
+ # Creates a real-time subscription
25
+ #
26
+ # @overload create_subscription(options={})
27
+ # @param options [Hash] A set of parameters
28
+ # @option options [String] :object The object you'd like to subscribe to (user, tag, location or geography)
29
+ # @option options [String] :callback_url The subscription callback URL
30
+ # @option options [String] :aspect The aspect of the object you'd like to subscribe to (in this case, "media").
31
+ # @option options [String, Integer] :object_id When specifying a location or tag use the location's ID or tag name respectively
32
+ # @option options [String, Float] :lat The center latitude of an area, used when subscribing to a geography object
33
+ # @option options [String, Float] :lng The center longitude of an area, used when subscribing to a geography object
34
+ # @option options [String, Integer] :radius The distance in meters you'd like to capture around a given point (max 5000 meters)
35
+ # @overload create_subscription(object, callback_url, aspect="media", options={})
36
+ # @param object [String] The object you'd like to subscribe to (user, tag, location or geography)
37
+ # @param callback_url [String] The subscription callback URL
38
+ # @param aspect [String] he aspect of the object you'd like to subscribe to (in this case, "media").
39
+ # @param options [Hash] Addition options and parameters
40
+ # @option options [String, Integer] :object_id When specifying a location or tag use the location's ID or tag name respectively
41
+ # @option options [String, Float] :lat The center latitude of an area, used when subscribing to a geography object
42
+ # @option options [String, Float] :lng The center longitude of an area, used when subscribing to a geography object
43
+ # @option options [String, Integer] :radius The distance in meters you'd like to capture around a given point (max 5000 meters)
44
+ #
45
+ # Note that we only support "media" at this time, but we might support other types of subscriptions in the future.
46
+ # @return [Hashie::Mash] The subscription created.
47
+ # @example Creates a new subscription to receive notifications for user media changes.
48
+ # Instagram.create_subscription("user", "http://example.com/instagram/callback")
49
+ # @format :json
50
+ # @authenticated true
51
+ #
52
+ # Requires client_secret to be set on the client or passed in options
53
+ # @rate_limited true
54
+ # @see https://api.instagram.com/developer/realtime/
55
+ def create_subscription(*args)
56
+ options = args.last.is_a?(Hash) ? args.pop : {}
57
+ object = args.shift
58
+ callback_url = args.shift
59
+ aspect = args.shift
60
+ options.tap {|o|
61
+ o[:object] = object unless object.nil?
62
+ o[:callback_url] = callback_url unless callback_url.nil?
63
+ o[:aspect] = aspect || o[:aspect] || "media"
64
+ }
65
+ response = post("subscriptions", options.merge(:client_secret => client_secret))
66
+ response["data"]
67
+ end
68
+
69
+ # Deletes a real-time subscription
70
+ #
71
+ # @overload delete_subscription(options={})
72
+ # @param options [Hash] Addition options and parameters
73
+ # @option options [Integer] :subscription_id The subscription's ID
74
+ # @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)
75
+ # @option options [String, Integer] :object_id When specifying :object, inlcude an :object_id to only remove subscriptions of that object and object_id
76
+ # @overload delete_subscription(subscription_id, options={})
77
+ # @param subscription_id [Integer] The subscription's ID
78
+ # @param options [Hash] Addition options and parameters
79
+ # @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)
80
+ # @option options [String, Integer] :object_id When specifying :object, inlcude an :object_id to only remove subscriptions of that object and object_id
81
+ # @return [nil]
82
+ # @example Deletes an application's user change subscription
83
+ # Instagram.delete_subscription(:object => "user")
84
+ # @format :json
85
+ # @authenticated true
86
+ #
87
+ # Requires client_secret to be set on the client or passed in options
88
+ # @rate_limited true
89
+ # @see https://api.instagram.com/developer/realtime/
90
+ def delete_subscription(*args)
91
+ options = args.last.is_a?(Hash) ? args.pop : {}
92
+ subscription_id = args.first
93
+ options.merge!(:id => subscription_id) if subscription_id
94
+ response = delete("subscriptions", options.merge(:client_secret => client_secret))
95
+ response["data"]
96
+ end
97
+
98
+ # Process a subscription notification JSON payload
99
+ #
100
+ # @overload process_subscription(json, &block)
101
+ # @param json [String] The JSON response received by the Instagram real-time server
102
+ # @param block [Proc] A callable in which callbacks are defined
103
+ # @option options [String] :signature Pass in an X-Hub-Signature to use for payload validation
104
+ # @return [nil]
105
+ # @example Process and handle a notification for a user media change
106
+ # Instagram.process_subscription(params[:body]) do |handler|
107
+ #
108
+ # handler.on_user_changed do |user_id, data|
109
+ #
110
+ # user = User.by_instagram_id(user_id)
111
+ # @client = Instagram.client(:access_token => _access_token_for_user(user))
112
+ # latest_media = @client.user_recent_media[0]
113
+ # user.media.create_with_hash(latest_media)
114
+ # end
115
+ #
116
+ # end
117
+ # @format :json
118
+ # @authenticated true
119
+ #
120
+ # Requires client_secret to be set on the client or passed in options
121
+ # @rate_limited true
122
+ # @see https://api.instagram.com/developer/realtime/
123
+ def process_subscription(json, options={}, &block)
124
+ raise ArgumentError, "callbacks block expected" unless block_given?
125
+
126
+ if options[:signature]
127
+ if !client_secret
128
+ raise ArgumentError, "client_secret must be set during configure"
129
+ end
130
+ verify_signature = HMAC::SHA1.hexdigest(client_secret, json)
131
+
132
+ if options[:signature] != verify_signature
133
+ raise Instagram::InvalidSignature, "invalid X-Hub-Signature does not match verify signature against client_secret"
134
+ end
135
+ end
136
+
137
+ payload = MultiJson.decode(json)
138
+ @changes = Hash.new { |h,k| h[k] = [] }
139
+ for change in payload
140
+ @changes[change['object']] << change
141
+ end
142
+ block.call(self)
143
+ end
144
+
145
+ [:user, :tag, :location, :geography].each do |object|
146
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ +1
147
+ def on_#{object}_changed(&block)
148
+ for change in @changes['#{object}']
149
+ yield change.delete('object_id'), change
150
+ end
151
+ end
152
+ RUBY_EVAL
153
+ end
154
+ end
155
+ end
156
+ 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