em-instagram 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +0 -0
- data/em-instagram.gemspec +30 -0
- data/lib/em-instagram.rb +74 -0
- data/lib/em-instagram/api/media.rb +34 -0
- data/lib/em-instagram/api/subscriptions.rb +37 -0
- data/lib/em-instagram/request.rb +22 -0
- data/lib/em-instagram/server.rb +39 -0
- data/lib/em-instagram/version.rb +5 -0
- metadata +118 -0
data/README.md
ADDED
File without changes
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/em-instagram/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "em-instagram"
|
6
|
+
s.version = EventMachine::Instagram::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.date = '2013-04-03'
|
9
|
+
s.authors = ["Joe Pym"]
|
10
|
+
s.email = ["joe@joepym.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{}
|
13
|
+
s.description = %q{}
|
14
|
+
|
15
|
+
s.files = Dir["{lib,spec}/**/*", "[A-Z]*", "init.rb"]
|
16
|
+
s.require_path = "lib"
|
17
|
+
|
18
|
+
s.required_rubygems_version = ">= 1.3.4"
|
19
|
+
|
20
|
+
s.add_dependency "eventmachine"
|
21
|
+
s.add_dependency 'eventmachine_httpserver'
|
22
|
+
s.add_dependency 'em-http-request'
|
23
|
+
|
24
|
+
s.add_development_dependency "rspec", '~> 2.5.0'
|
25
|
+
|
26
|
+
s.files = `git ls-files`.split("\n")
|
27
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
28
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
end
|
data/lib/em-instagram.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require File.expand_path('../em-instagram/request', __FILE__)
|
2
|
+
require File.expand_path('../em-instagram/server', __FILE__)
|
3
|
+
require File.expand_path('../em-instagram/api/subscriptions', __FILE__)
|
4
|
+
require File.expand_path('../em-instagram/api/media', __FILE__)
|
5
|
+
|
6
|
+
module EventMachine
|
7
|
+
class Instagram
|
8
|
+
include Subscriptions
|
9
|
+
include Media
|
10
|
+
|
11
|
+
BASE_URI = 'https://api.instagram.com'
|
12
|
+
PORT = 443
|
13
|
+
|
14
|
+
attr_reader :default_params, :host, :port, :server, :logger, :subscription_queue, :update_callback
|
15
|
+
|
16
|
+
def initialize(options = nil)
|
17
|
+
@host, @port, @server = [options[:host], options[:port], options[:server]]
|
18
|
+
@subscription_queue = EventMachine::Queue.new
|
19
|
+
@update_queue = EventMachine::Queue.new
|
20
|
+
@logger = options[:logger]
|
21
|
+
@default_params = {:client_id => options[:client_id], :client_secret => options[:client_secret]}
|
22
|
+
update
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch(update)
|
27
|
+
case update['object']
|
28
|
+
when 'geography'
|
29
|
+
fetch_geography update['object_id']
|
30
|
+
when 'tag'
|
31
|
+
fetch_tag update['object_id']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def stream(&block)
|
36
|
+
@streams ||= []
|
37
|
+
@streams << block
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_update(&block)
|
41
|
+
@update_callback = block
|
42
|
+
end
|
43
|
+
|
44
|
+
def update(data = nil)
|
45
|
+
@update_queue << data if data
|
46
|
+
@update_queue.pop do |item|
|
47
|
+
if @update_callback
|
48
|
+
@update_callback.call(item)
|
49
|
+
else
|
50
|
+
@logger.debug(item)
|
51
|
+
end
|
52
|
+
EventMachine::next_tick { update }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def receive_notification(data)
|
57
|
+
@streams.each { |stream| stream.call(data) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def request(method, path, options = {})
|
61
|
+
url = URI.join(BASE_URI, path).to_s
|
62
|
+
Request.new EventMachine::HttpRequest.new(url).send(method, options)
|
63
|
+
end
|
64
|
+
|
65
|
+
def start_server(&block)
|
66
|
+
@host ||= '0.0.0.0'
|
67
|
+
@port ||= 8080
|
68
|
+
@server ||= Server
|
69
|
+
EventMachine.start_server(host, port, server) do |connection|
|
70
|
+
connection.subscriber = self
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module EventMachine
|
2
|
+
class Instagram
|
3
|
+
module Media
|
4
|
+
def media(id)
|
5
|
+
request :get, "/v1/media/#{id}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def media_by_geography(object_id, params = {})
|
9
|
+
request :get, "/v1/geographies/#{object_id}/media/recent", :query => default_params.merge(params)
|
10
|
+
end
|
11
|
+
|
12
|
+
def media_by_tag(object_id, params = {})
|
13
|
+
request :get, "/v1/tags/#{object_id}/media/recent", :query => default_params.merge(params)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def fetch_geography(object_id)
|
18
|
+
@logger.debug "fetching #{object_id} updates..."
|
19
|
+
# TODO: figure out if min_id parameter would be appropriate for reading recent tagged media
|
20
|
+
request = media_by_geography(object_id)
|
21
|
+
request.errback { |error| @logger.debug "fetch error: #{error}"; EventMachine::add_timer(15) { fetch_geography object_id } }
|
22
|
+
request.callback { |media| @update_queue.push(*media) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch_tag(object_id)
|
26
|
+
@logger.debug "fetching #{object_id} updates..."
|
27
|
+
# TODO: figure out if max_id or min_id parameter would be appropriate for reading recent tagged media
|
28
|
+
request = media_by_tag(object_id)
|
29
|
+
request.errback { |error| @logger.debug "fetch error: #{error}"; EventMachine::add_timer(15) { fetch_tag object_id } }
|
30
|
+
request.callback { |media| @update_queue.push(*media) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module EventMachine
|
2
|
+
class Instagram
|
3
|
+
module Subscriptions
|
4
|
+
def send_subscription(params = {})
|
5
|
+
request :post, "/v1/subscriptions", :body => default_params.merge(params)
|
6
|
+
end
|
7
|
+
|
8
|
+
def subscribe_to(options)
|
9
|
+
options = options.merge(:aspect => 'media', :callback_url => CALLBACK_URL)
|
10
|
+
request = send_subscription options.merge(:aspect => 'media', :callback_url => CALLBACK_URL)
|
11
|
+
request.errback { |error| @logger.debug "subscription error: #{error}"; EventMachine::add_timer(15) { subscribe_to options } }
|
12
|
+
request.callback { |response| @logger.debug "next subscription..."; EventMachine::next_tick { subscribe_next } }
|
13
|
+
end
|
14
|
+
|
15
|
+
def subscriptions(params = {})
|
16
|
+
request :get, "/v1/subscriptions", :query => default_params.merge(params)
|
17
|
+
end
|
18
|
+
|
19
|
+
def subscribe(*args)
|
20
|
+
@subscription_queue.push(*args)
|
21
|
+
subscribe_next
|
22
|
+
end
|
23
|
+
|
24
|
+
def unsubscribe(params = {})
|
25
|
+
request :delete, "/v1/subscriptions", :query => default_params.merge(params)
|
26
|
+
end
|
27
|
+
|
28
|
+
def subscribe_next
|
29
|
+
if @subscription_queue.empty?
|
30
|
+
@logger.debug "subscribed."
|
31
|
+
else
|
32
|
+
@subscription_queue.pop { |hash| send_subscription(hash); subscribe_next }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module EventMachine
|
2
|
+
class Instagram
|
3
|
+
class Request
|
4
|
+
include Deferrable
|
5
|
+
|
6
|
+
def initialize(connection)
|
7
|
+
connection.errback { |response| fail response.error }
|
8
|
+
connection.callback { |response| process_instagram_response response }
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
def process_instagram_response(response)
|
13
|
+
case response.response_header.status
|
14
|
+
when 200
|
15
|
+
succeed JSON.parse(response.response)['data']
|
16
|
+
else
|
17
|
+
fail JSON.parse(response.response)['meta']['error_message']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module EventMachine
|
2
|
+
class Instagram
|
3
|
+
module Server
|
4
|
+
include EventMachine::HttpServer
|
5
|
+
|
6
|
+
attr_accessor :subscriber
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@updates = EventMachine::Queue.new
|
10
|
+
process_update
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def process_http_request
|
15
|
+
response = EventMachine::DelegatedHttpResponse.new(self)
|
16
|
+
case @http_request_method
|
17
|
+
when 'GET'
|
18
|
+
params = CGI::parse(@http_query_string)
|
19
|
+
response.status = 200
|
20
|
+
response.content = params['hub.challenge']
|
21
|
+
when 'POST'
|
22
|
+
@updates.push(*JSON.load(@http_post_content))
|
23
|
+
response.status = 202
|
24
|
+
response.content = 'Accepted'
|
25
|
+
else
|
26
|
+
response.status = 405
|
27
|
+
response.content = 'Method Not Allowed'
|
28
|
+
end
|
29
|
+
|
30
|
+
response.send_response
|
31
|
+
end
|
32
|
+
|
33
|
+
def process_update
|
34
|
+
@updates.pop { |data| subscriber.receive_notification data; EventMachine::next_tick { process_update } }
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: em-instagram
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joe Pym
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: eventmachine
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: eventmachine_httpserver
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: em-http-request
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.5.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.5.0
|
78
|
+
description: ''
|
79
|
+
email:
|
80
|
+
- joe@joepym.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- README.md
|
86
|
+
- em-instagram.gemspec
|
87
|
+
- lib/em-instagram.rb
|
88
|
+
- lib/em-instagram/api/media.rb
|
89
|
+
- lib/em-instagram/api/subscriptions.rb
|
90
|
+
- lib/em-instagram/request.rb
|
91
|
+
- lib/em-instagram/server.rb
|
92
|
+
- lib/em-instagram/version.rb
|
93
|
+
homepage: ''
|
94
|
+
licenses: []
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.3.4
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 1.8.23
|
114
|
+
signing_key:
|
115
|
+
specification_version: 3
|
116
|
+
summary: ''
|
117
|
+
test_files: []
|
118
|
+
has_rdoc:
|