em-instagram 0.1

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.
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: