em-instagram 0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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
@@ -0,0 +1,5 @@
1
+ module EventMachine
2
+ class Instagram
3
+ VERSION = '0.1'
4
+ end
5
+ 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: