capgun 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +3 -0
  3. data/Gemfile +1 -8
  4. data/Gemfile.lock +32 -0
  5. data/HISTORY.md +14 -3
  6. data/LICENSE.md +26 -0
  7. data/README.md +306 -4
  8. data/Rakefile +10 -2
  9. data/capgun.gemspec +27 -16
  10. data/examples/hubot/capgun.coffee +68 -0
  11. data/lib/capgun.rb +29 -1
  12. data/lib/capgun/account.rb +13 -0
  13. data/lib/capgun/base.rb +48 -0
  14. data/lib/capgun/client.rb +82 -0
  15. data/lib/capgun/config.rb +73 -0
  16. data/lib/capgun/connection.rb +36 -0
  17. data/lib/capgun/core_ext/hash.rb +19 -0
  18. data/lib/capgun/creatable.rb +21 -0
  19. data/lib/capgun/error.rb +17 -0
  20. data/lib/capgun/error/bad_gateway.rb +7 -0
  21. data/lib/capgun/error/bad_request.rb +7 -0
  22. data/lib/capgun/error/client_error.rb +7 -0
  23. data/lib/capgun/error/forbidden.rb +7 -0
  24. data/lib/capgun/error/internal_server_error.rb +7 -0
  25. data/lib/capgun/error/not_acceptable.rb +7 -0
  26. data/lib/capgun/error/not_found.rb +7 -0
  27. data/lib/capgun/error/server_error.rb +7 -0
  28. data/lib/capgun/error/service_unavailable.rb +7 -0
  29. data/lib/capgun/error/unauthorized.rb +7 -0
  30. data/lib/capgun/estimate.rb +11 -0
  31. data/lib/capgun/job.rb +13 -0
  32. data/lib/capgun/order.rb +13 -0
  33. data/lib/capgun/request.rb +33 -0
  34. data/lib/capgun/request/gateway.rb +20 -0
  35. data/lib/capgun/response/parse_json.rb +28 -0
  36. data/lib/capgun/response/raise_client_error.rb +48 -0
  37. data/lib/capgun/response/raise_server_error.rb +23 -0
  38. data/lib/capgun/version.rb +27 -7
  39. data/spec/capgun/account_spec.rb +45 -0
  40. data/spec/capgun/base_spec.rb +29 -0
  41. data/spec/capgun/client_spec.rb +181 -0
  42. data/spec/capgun/config_spec.rb +43 -0
  43. data/spec/capgun/estimate_spec.rb +7 -0
  44. data/spec/capgun/job_spec.rb +45 -0
  45. data/spec/capgun/order_spec.rb +45 -0
  46. data/spec/capgun/version_spec.rb +11 -0
  47. data/spec/capgun_spec.rb +17 -0
  48. data/spec/faraday/response_spec.rb +68 -0
  49. data/spec/fixtures/account.json +9 -0
  50. data/spec/fixtures/completed-order.json +46 -0
  51. data/spec/fixtures/estimate.json +34 -0
  52. data/spec/fixtures/job.json +9 -0
  53. data/spec/fixtures/notfound.json +1 -0
  54. data/spec/fixtures/order.json +43 -0
  55. data/spec/fixtures/unauthorized.json +1 -0
  56. data/spec/spec_helper.rb +27 -0
  57. metadata +218 -10
@@ -0,0 +1,68 @@
1
+ # Description
2
+ # Grab web screens/thumbs of URLs using the capgun.io service
3
+ #
4
+ # Requires a CapGun API token to be set in the env var HUBOT_CAPGUN_TOKEN
5
+ #
6
+ # Dependencies:
7
+ # none
8
+ #
9
+ # Configuration:
10
+ # HUBOT_CAPGUN_TOKEN
11
+ #
12
+ # Commands:
13
+ # hubot cap <url> - Get a web screen of the url
14
+ #
15
+ # Notes:
16
+ # none
17
+ #
18
+ # Author:
19
+ # monde
20
+
21
+ module.exports = (robot) ->
22
+ robot.respond /cap (.*)/i, (msg) ->
23
+ Capgun.start msg, msg.match[1]
24
+
25
+ Capgun =
26
+
27
+ interval: 2500
28
+
29
+ token: process.env.HUBOT_CAPGUN_TOKEN
30
+
31
+ start: (msg, url) ->
32
+ this.submitOrder(msg, url)
33
+
34
+ submitOrder: (msg, url) ->
35
+ capgun = this
36
+ data = JSON.stringify({"url": url})
37
+ msg.http('https://api.capgun.io/v1/orders.json')
38
+ .headers
39
+ 'Authorization': capgun.token,
40
+ 'Accept': 'application/json'
41
+ .post(data) (err, res, body) ->
42
+ result = JSON.parse(body)
43
+ if err || res.statusCode != 200
44
+ message = result.message || "???"
45
+ msg.send "Capgun job failed with message '" + message + "', I'm bailing out!"
46
+ else
47
+ setTimeout (-> capgun.checkJob(msg, url, result.order.id, 0)), capgun.interval
48
+
49
+ checkJob: (msg, url, order_id, duration) ->
50
+ capgun = this
51
+
52
+ if duration > 90000
53
+ msg.send "Capgun job hung past 90 seconds for URL " + url + " , I'm bailing out!"
54
+ else
55
+ msg.http('https://api.capgun.io/v1/orders/' + order_id + '.json')
56
+ .headers
57
+ 'Authorization': capgun.token,
58
+ 'Accept': 'application/json'
59
+ .get() (err, res, body) ->
60
+ result = JSON.parse(body)
61
+ state = result.order.job.state
62
+
63
+ if state.search(/failed/) >= 0
64
+ msg.send "Capgun order " + order_id + " failed for URL " + url
65
+ else if state.search(/completed/) >= 0
66
+ msg.send result.order.asset_urls['640x480']
67
+ else
68
+ setTimeout (-> capgun.checkJob(msg, url, result.order.id, duration + capgun.interval)), capgun.interval
@@ -1,3 +1,31 @@
1
1
  module Capgun
2
- require 'capgun/version'
2
+
3
+ require 'capgun/config'
4
+ require 'capgun/client'
5
+
6
+ extend Config
7
+
8
+ class << self
9
+
10
+ extend Config
11
+
12
+ # Alias for Capgun::Client.new
13
+ #
14
+ # @param [Hash] options options for the Capgun::Client
15
+ # @return [Capgun::Client]
16
+ def new(options={})
17
+ Capgun::Client.new(options)
18
+ end
19
+
20
+ # Delegate methods to Capgun::Client
21
+ def method_missing(method, *args, &block)
22
+ return super unless new.respond_to?(method)
23
+ new.send(method, *args, &block)
24
+ end
25
+
26
+ # Corollary of #method_missing delegated to Capgun::Client
27
+ def respond_to?(method, include_private=false)
28
+ new.respond_to?(method, include_private) || super(method, include_private)
29
+ end
30
+ end
3
31
  end
@@ -0,0 +1,13 @@
1
+ module Capgun
2
+
3
+ # capgun account
4
+
5
+ class Account < Capgun::Base
6
+
7
+ include Capgun::Creatable
8
+
9
+ lazy_attr_reader :id, :name, :balance
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,48 @@
1
+ require 'capgun/creatable'
2
+
3
+ module Capgun
4
+ class Base
5
+ attr_accessor :attrs
6
+ alias :to_hash :attrs
7
+
8
+ # Define methods that retrieve the value from an initialized instance variable Hash, using the attribute as a key
9
+ #
10
+ # @overload self.lazy_attr_reader(attr)
11
+ # @param attr [Symbol]
12
+ # @overload self.lazy_attr_reader(attrs)
13
+ # @param attrs [Array<Symbol>]
14
+ def self.lazy_attr_reader(*attrs)
15
+ attrs.each do |attribute|
16
+ class_eval do
17
+ define_method attribute do
18
+ @attrs[attribute.to_s]
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ # Initializes a new Base object
25
+ #
26
+ # @param attrs [Hash]
27
+ # @return [Capgun::Base]
28
+ def initialize(attrs={})
29
+ @attrs = attrs.dup
30
+ end
31
+
32
+ # Initializes a new Base object
33
+ #
34
+ # @param method [String, Symbol] Message to send to the object
35
+ def [](method)
36
+ self.__send__(method.to_sym)
37
+ rescue NoMethodError
38
+ nil
39
+ end
40
+
41
+ # @param other [Capgun::Base]
42
+ # @return [Boolean]
43
+ def ==(other)
44
+ super || (other.class == self.class && other.id == self.id)
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,82 @@
1
+ require 'json'
2
+ require 'json/pure'
3
+ require 'capgun/base'
4
+ require 'capgun/account'
5
+ require 'capgun/estimate'
6
+ require 'capgun/job'
7
+ require 'capgun/order'
8
+ require 'capgun/config'
9
+ require 'capgun/connection'
10
+ require 'capgun/request'
11
+
12
+ module Capgun
13
+
14
+ # Wrapper for the Capgun REST API
15
+ class Client
16
+
17
+ include Capgun::Connection
18
+ include Capgun::Request
19
+
20
+ attr_accessor *Config::VALID_OPTIONS_KEYS
21
+
22
+ # Initializes a new API object. Accessor attributes are set based
23
+ # on Capgun::Config::VALID_OPTIONS_KEYS
24
+ #
25
+ # @param [Hash] attrs Addition client attributes.
26
+ # @return [Capgun::Client]
27
+ def initialize(attrs={})
28
+ attrs = Capgun.options.merge(attrs)
29
+ Config::VALID_OPTIONS_KEYS.each do |key|
30
+ instance_variable_set("@#{key}".to_sym, attrs[key])
31
+ end
32
+ end
33
+
34
+ # Estimates a capgun job cost / availability.
35
+ #
36
+ # @param [String] url A url that will be captured.
37
+ # @param [Hash] options Additional options to the capture request.
38
+ # @return [Capgun::Estimate] The estimate for the capture request.
39
+ def estimate(url, options = {})
40
+ estimate = post("/v1/orders/estimate.json", options.merge(:url => url))
41
+ Capgun::Estimate.new(estimate['order'])
42
+ end
43
+
44
+ # Creates a capgun job
45
+ #
46
+ # @param [String] url A url that will be captured.
47
+ # @param [Hash] options Additional options to the capture request.
48
+ # @return [Capgun::Order] The capture request order.
49
+ def capture(url, options = {})
50
+ order = post("/v1/orders.json", options.merge(:url => url))
51
+ Capgun::Order.new(order['order'])
52
+ end
53
+
54
+ # Query the order
55
+ #
56
+ # @param [String] order_id The id of a previously submitted order.
57
+ # @return [Capgun::Order] The capture order.
58
+ def order(order_id)
59
+ order = get("/v1/orders/#{order_id}.json")
60
+ Capgun::Order.new(order['order'])
61
+ end
62
+
63
+ # Query the status of a capture job.
64
+ #
65
+ # @param [String] job_id The id of a previously submitted capture job.
66
+ # @return [Capgun::Job] The capture request is a job that encapsulates status.
67
+ def status(job_id)
68
+ job = get("/v1/jobs/#{job_id}.json")
69
+ Capgun::Job.new(job['job'])
70
+ end
71
+
72
+ # Fetch account information
73
+ #
74
+ # @return [Capgun::Account] The account
75
+ def account
76
+ account = get("/v1/account.json")
77
+ Capgun::Account.new(account['account'])
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,73 @@
1
+ require 'capgun/version'
2
+
3
+ module Capgun
4
+
5
+ # Defines constants and methods related to configuration
6
+ module Config
7
+
8
+ # The HTTP connection adapter that will be used to connect if none is set
9
+ DEFAULT_ADAPTER = :net_http
10
+
11
+ # The Faraday connection options if none is set
12
+ DEFAULT_CONNECTION_OPTIONS = {}
13
+
14
+ # API endpoint
15
+ DEFAULT_ENDPOINT = 'https://api.capgun.io'
16
+
17
+ # The gateway server if none is set
18
+ DEFAULT_GATEWAY = nil
19
+
20
+ # The auth token if none is set
21
+ DEFAULT_AUTH_TOKEN = ""
22
+
23
+ # The value sent in the 'User-Agent' header if none is set
24
+ DEFAULT_USER_AGENT = "Capgun.io Ruby Gem #{Capgun::Version}"
25
+
26
+ # The proxy server if none is set
27
+ DEFAULT_PROXY = nil
28
+
29
+ # An array of valid keys in the options hash when configuring a {Capgun::Client}
30
+ VALID_OPTIONS_KEYS = [
31
+ :adapter,
32
+ :connection_options,
33
+ :endpoint,
34
+ :gateway,
35
+ :auth_token,
36
+ :user_agent,
37
+ :proxy,
38
+ ]
39
+
40
+ attr_accessor *VALID_OPTIONS_KEYS
41
+
42
+ # When this module is extended, set all configuration options to their default values
43
+ def self.extended(base)
44
+ base.reset
45
+ end
46
+
47
+ # Convenience method to allow configuration options to be set in a block
48
+ def configure
49
+ yield self
50
+ self
51
+ end
52
+
53
+ # Create a hash of options and their values
54
+ def options
55
+ options = {}
56
+ VALID_OPTIONS_KEYS.each{|k| options[k] = send(k)}
57
+ options
58
+ end
59
+
60
+ # Reset all configuration options to defaults
61
+ def reset
62
+ self.adapter = DEFAULT_ADAPTER
63
+ self.connection_options = DEFAULT_CONNECTION_OPTIONS
64
+ self.endpoint = DEFAULT_ENDPOINT
65
+ self.gateway = DEFAULT_GATEWAY
66
+ self.auth_token = DEFAULT_AUTH_TOKEN
67
+ self.user_agent = DEFAULT_USER_AGENT
68
+ self.proxy = DEFAULT_PROXY
69
+ self
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,36 @@
1
+ require 'faraday'
2
+ require 'capgun/core_ext/hash'
3
+ require 'capgun/request/gateway'
4
+ require 'capgun/response/parse_json'
5
+ require 'capgun/response/raise_client_error'
6
+ require 'capgun/response/raise_server_error'
7
+
8
+ module Capgun
9
+ module Connection
10
+ private
11
+
12
+ # Returns a Faraday::Connection object
13
+ #
14
+ # @param options [Hash] A hash of options
15
+ # @return [Faraday::Connection]
16
+ def connection(options={})
17
+ default_options = {
18
+ :headers => {
19
+ :accept => 'application/json',
20
+ :user_agent => user_agent,
21
+ :authorization => auth_token,
22
+ },
23
+ :proxy => proxy,
24
+ :ssl => {:verify => false},
25
+ :url => options.fetch(:endpoint, endpoint),
26
+ }
27
+ @connection ||=Faraday.new(default_options.deep_merge(connection_options)) do |builder|
28
+ builder.use Capgun::Request::Gateway, gateway if gateway
29
+ builder.use Capgun::Response::RaiseClientError
30
+ builder.use Capgun::Response::ParseJson
31
+ builder.use Capgun::Response::RaiseServerError
32
+ builder.adapter(adapter)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ class Hash
2
+
3
+ # Merges self with another hash, recursively
4
+ #
5
+ # @param hash [Hash] The hash to merge
6
+ # @return [Hash]
7
+ def deep_merge(hash)
8
+ target = self.dup
9
+ hash.keys.each do |key|
10
+ if hash[key].is_a?(Hash) && self[key].is_a?(Hash)
11
+ target[key] = target[key].deep_merge(hash[key])
12
+ next
13
+ end
14
+ target[key] = hash[key]
15
+ end
16
+ target
17
+ end
18
+
19
+ end
@@ -0,0 +1,21 @@
1
+ require 'time'
2
+
3
+ module Capgun
4
+ module Creatable
5
+
6
+ # Time when the object was created
7
+ #
8
+ # @return [Time]
9
+ def created_at
10
+ @created_at ||= Time.parse(@attrs['created_at']) unless @attrs['created_at'].nil?
11
+ end
12
+
13
+ # Time when the object was updated
14
+ #
15
+ # @return [Time]
16
+ def updated_at
17
+ @updated_at ||= Time.parse(@attrs['updated_at']) unless @attrs['updated_at'].nil?
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Capgun
2
+ # Custom error class for rescuing from all Capgun errors
3
+ class Error < StandardError
4
+ attr_reader :http_headers
5
+
6
+ # Initializes a new Error object
7
+ #
8
+ # @param message [String]
9
+ # @param http_headers [Hash]
10
+ # @return [Capgun::Error]
11
+ def initialize(message, http_headers)
12
+ @http_headers = Hash[http_headers]
13
+ super(message)
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ require 'capgun/error/server_error'
2
+
3
+ module Capgun
4
+ # Raised when Capgun returns the HTTP status code 502
5
+ class Error::BadGateway < Capgun::Error::ServerError
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'capgun/error/client_error'
2
+
3
+ module Capgun
4
+ # Raised when Capgun returns the HTTP status code 400
5
+ class Error::BadRequest < Capgun::Error::ClientError
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'capgun/error'
2
+
3
+ module Capgun
4
+ # Raised when Capgun returns a 4xx HTTP status code
5
+ class Error::ClientError < Capgun::Error
6
+ end
7
+ end