scalingo-ruby-api 1.0.0.alpha1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e414ecac6ae02a9b85d286fdfea4bb6c9a8f3c4d
4
+ data.tar.gz: b01e544df9c7ea1b4bc39f62ee68699764305d7e
5
+ SHA512:
6
+ metadata.gz: c14839d474e165ebe4deaba60e98c2193aa654a2018df1b9955da73292042a8c17c8f92f8f8a9a0842093a5cf02616eff38180c7eb6cd2f6518abfb02b174aea
7
+ data.tar.gz: 40ac42171d6091944d4d540647d132fa4bfc061e28d6932a4ea00f5512b064ffdfea75221e18732af0a4c756eee8f04f55dd223f9e65858b98142c34f5509897
@@ -0,0 +1,47 @@
1
+ require 'faraday'
2
+
3
+ module FaradayMiddleware
4
+ class RaiseHttpException < Faraday::Middleware
5
+ def call(env)
6
+ @app.call(env).on_complete do |response|
7
+ case response[:status].to_i
8
+ when 400
9
+ raise Scalingo::BadRequest, error_message_400(response)
10
+ when 404
11
+ raise Scalingo::NotFound, error_message_400(response)
12
+ when 500
13
+ raise Scalingo::InternalServerError, error_message_500(response, "Something is technically wrong.")
14
+ end
15
+ end
16
+ end
17
+
18
+ def initialize(app)
19
+ super app
20
+ @parser = nil
21
+ end
22
+
23
+ private
24
+
25
+ def error_message_400(response)
26
+ "#{response[:method].to_s.upcase} #{response[:url].to_s}: #{response[:status]}#{error_body(response[:body])}"
27
+ end
28
+
29
+ def error_body(body)
30
+ # body gets passed as a string, not sure if it is passed as something else from other spots?
31
+ if not body.nil? and not body.empty? and body.kind_of?(String)
32
+ # removed multi_json thanks to wesnolte's commit
33
+ body = ::JSON.parse(body)
34
+ end
35
+
36
+ if body.nil?
37
+ nil
38
+ elsif body['meta'] and body['meta']['error_message'] and not body['meta']['error_message'].empty?
39
+ ": #{body['meta']['error_message']}"
40
+ end
41
+ end
42
+
43
+ def error_message_500(response, body=nil)
44
+ "#{response[:method].to_s.upcase} #{response[:url].to_s}: #{[response[:status].to_s + ':', body].compact.join(' ')}"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'connection'
2
+ require_relative 'request'
3
+ require_relative 'configuration'
4
+ require_relative 'endpoint'
5
+
6
+ module Scalingo
7
+ class Api
8
+ attr_accessor(*Configuration::VALID_OPTIONS_KEYS)
9
+
10
+ def initialize(options = {})
11
+ options = Scalingo.options.merge(options)
12
+ Configuration::VALID_OPTIONS_KEYS.each do |key|
13
+ send("#{key}=", options[key])
14
+ end
15
+ end
16
+
17
+ include Connection
18
+ include Request
19
+ include Endpoint
20
+ end
21
+ end
22
+
@@ -0,0 +1,5 @@
1
+ module Scalingo
2
+ class Client < Api
3
+ end
4
+ end
5
+
@@ -0,0 +1,65 @@
1
+ require 'faraday'
2
+ require_relative 'version'
3
+
4
+ module Scalingo
5
+ # Defines constants and methods related to configuration
6
+ module Configuration
7
+ # An array of valid keys in the options hash when configuring a {Scalingo::Api}
8
+ VALID_OPTIONS_KEYS = [
9
+ :adapter,
10
+ :token,
11
+ :endpoint,
12
+ :user_agent,
13
+ :proxy
14
+ ].freeze
15
+
16
+ # The adapter that will be used to connect if none is set
17
+ #
18
+ # @note The default faraday adapter is Net::HTTP.
19
+ DEFAULT_ADAPTER = Faraday.default_adapter
20
+
21
+ # By default, don't set an token
22
+ DEFAULT_TOKEN = nil
23
+
24
+ # The endpoint that will be used to connect if none is set
25
+ #
26
+ # @note There is no reason to use any other endpoint at this time
27
+ DEFAULT_ENDPOINT = 'https://api.scalingo.com/v1/'.freeze
28
+
29
+ # By default, don't use a proxy server
30
+ DEFAULT_PROXY = nil
31
+
32
+ # The user agent that will be sent to the Api endpoint if none is set
33
+ DEFAULT_USER_AGENT = "Scalingo Ruby Gem #{Scalingo::VERSION}".freeze
34
+
35
+ # @private
36
+ attr_accessor *VALID_OPTIONS_KEYS
37
+
38
+ # When this module is extended, set all configuration options to their default values
39
+ def self.extended(base)
40
+ base.reset
41
+ end
42
+
43
+ # Convenience method to allow configuration options to be set in a block
44
+ def configure
45
+ yield self
46
+ end
47
+
48
+ # Create a hash of options and their values
49
+ def options
50
+ VALID_OPTIONS_KEYS.inject({}) do |option, key|
51
+ option.merge!(key => send(key))
52
+ end
53
+ end
54
+
55
+ # Reset all configuration options to defaults
56
+ def reset
57
+ self.adapter = DEFAULT_ADAPTER
58
+ self.token = DEFAULT_TOKEN
59
+ self.endpoint = DEFAULT_ENDPOINT
60
+ self.user_agent = DEFAULT_USER_AGENT
61
+ self.proxy = DEFAULT_PROXY
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,34 @@
1
+ require 'faraday_middleware'
2
+ Dir[File.expand_path('../../faraday/*.rb', __FILE__)].each{|f| require f}
3
+
4
+ module Scalingo
5
+ module Connection
6
+ protected
7
+ def parse_json
8
+ true
9
+ end
10
+
11
+ private
12
+ def connection
13
+ options = {
14
+ :headers => {
15
+ 'Accept' => 'application/json; charset=utf-8',
16
+ 'Content-Type' => 'application/json',
17
+ 'User-Agent' => user_agent,
18
+ },
19
+ :proxy => proxy,
20
+ :url => endpoint,
21
+ }
22
+
23
+ Faraday::Connection.new(options) do |connection|
24
+ connection.use Faraday::Request::Multipart
25
+ connection.use Faraday::Request::UrlEncoded
26
+ connection.use Faraday::Response::ParseJson if parse_json
27
+ connection.use FaradayMiddleware::RaiseHttpException
28
+ connection.adapter(adapter)
29
+ connection.basic_auth('', token) if token
30
+ end
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,23 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class AccountKeys < Collection
4
+ def initialize(api)
5
+ super(api, 'account/keys')
6
+ end
7
+
8
+ def collection_name
9
+ 'keys'
10
+ end
11
+
12
+ def create(name, content)
13
+ post(nil, {key: {name: name, content: content}})
14
+ end
15
+ end
16
+ class AccountKey < Resource
17
+ def destroy
18
+ delete
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,7 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class AddonCategories < Collection
4
+ end
5
+ end
6
+ end
7
+
@@ -0,0 +1,7 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class AddonProviders < Collection
4
+ end
5
+ end
6
+ end
7
+
@@ -0,0 +1,19 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class Addons < Collection
4
+ def create(addon_provider_id, plan_id)
5
+ post(nil, {addon:{addon_provider_id: addon_provider_id, plan_id: plan_id}})
6
+ end
7
+ end
8
+ class Addon < Resource
9
+ def update(plan_id)
10
+ patch(nil, {addon: {plan_id: plan_id}})
11
+ end
12
+
13
+ def destroy
14
+ delete
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,61 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class Apps < Collection
4
+ def create(name)
5
+ post(nil, {app: {name: name}})
6
+ end
7
+
8
+ def find_by
9
+ 'name'
10
+ end
11
+ end
12
+ class App < Resource
13
+ def scale(containers)
14
+ post('scale', {containers: containers})
15
+ end
16
+
17
+ def restart(*scopes)
18
+ post('restart', {scope: scopes})
19
+ end
20
+
21
+ def destroy(current_name)
22
+ delete(nil, {current_name: current_name})
23
+ end
24
+
25
+ def destroy!
26
+ destroy(prefix)
27
+ end
28
+
29
+ def rename(new_name, current_name)
30
+ post('rename', {new_name: new_name, current_name: current_name})
31
+ end
32
+ def rename!(new_name)
33
+ rename(new_name, prefix)
34
+ end
35
+
36
+ def transfer(email)
37
+ patch(nil, {app: {owner: {email: email}}})
38
+ end
39
+
40
+ def logs_url
41
+ get('logs')['logs_url']
42
+ end
43
+
44
+ def logs
45
+ Scalingo::Logs.new(self)
46
+ end
47
+
48
+ def run(command, env = {})
49
+ post('run', {command: command, env: env})
50
+ end
51
+
52
+ resources :addons
53
+ resources :collaborators
54
+ resources :deployments
55
+ resources :domains
56
+ resources :variables
57
+ resources :events, collection_only: true
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,15 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class Collaborators < Collection
4
+ def create(email)
5
+ post(nil, {collaborator: {email: email}})
6
+ end
7
+ end
8
+ class Collaborator < Resource
9
+ def destroy
10
+ delete
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,12 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class Deployments < Collection
4
+ end
5
+ class Deployment < Resource
6
+ def output
7
+ get('output')
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,23 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class Domains < Collection
4
+ def create(name, tlscert = nil, tlskey = nil)
5
+ if tlskey
6
+ post(nil, {domain: {name: name, tlscert: tlscert, tlskey: tlskey}})
7
+ else
8
+ post(nil, {domain: {name: name}})
9
+ end
10
+ end
11
+ end
12
+ class Domain < Resource
13
+ def update(tlscert, tlskey)
14
+ path(nil, {domain: {tlscert: tlscert, tlskey: tlskey}})
15
+ end
16
+
17
+ def destroy
18
+ delete
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,7 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class Events < Collection
4
+ end
5
+ end
6
+ end
7
+
@@ -0,0 +1,23 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class Variables < Collection
4
+ def all(aliases = true)
5
+ get(nil, {aliases: aliases})[collection_name]
6
+ end
7
+
8
+ def create(name, value)
9
+ post(nil, {variable: {name: name, value: value}})
10
+ end
11
+ end
12
+ class Variable < Resource
13
+ def update(value)
14
+ patch(nil, {variable: {value: value}})
15
+ end
16
+
17
+ def destroy
18
+ delete
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,94 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def resources(name, opts = {})
9
+ name = name.to_s
10
+ define_method(name.pluralize.underscore) do
11
+ Scalingo::Endpoint.const_get(name.pluralize.camelize).new(self)
12
+ end
13
+
14
+ return if opts[:collection_only]
15
+
16
+ define_method(name.singularize.underscore) do |id|
17
+ send(name.pluralize.underscore).find(id)
18
+ end
19
+ end
20
+ end
21
+
22
+ extend ClassMethods
23
+ resources :apps
24
+ resources :account_keys
25
+ resources :addon_providers, collection_only: true
26
+ resources :addon_categories, collection_only: true
27
+
28
+ module Base
29
+ attr_accessor :api
30
+ attr_accessor :prefix
31
+
32
+ def initialize(api, prefix = nil)
33
+ self.api = api
34
+ self.prefix = prefix || self.class.name.split('::').last.underscore.pluralize
35
+ end
36
+
37
+ Request::REQUEST_METHODS.each do |method|
38
+ define_method(method) do |path = nil, options = {}|
39
+ api.send(method, "#{prefix}/#{path}", options)
40
+ end
41
+ end
42
+ end
43
+
44
+ class Resource < OpenStruct
45
+ include Base
46
+ include Endpoint
47
+
48
+ def initialize(api, prefix, data = {})
49
+ Base.instance_method(:initialize).bind(self).call(api, prefix)
50
+ OpenStruct.instance_method(:initialize).bind(self).call(data)
51
+ end
52
+ end
53
+
54
+ class Collection
55
+ include Base
56
+ include Endpoint
57
+ include Enumerable
58
+
59
+ def all
60
+ get[collection_name].map{|r| resource_class.new(self, r[find_by], r)}
61
+ end
62
+ alias_method :to_a, :all
63
+
64
+ def each
65
+ block_given? ? all.each(&Proc.new) : all.each
66
+ end
67
+
68
+ def find(id)
69
+ detect{|r| r[find_by] == id}
70
+ end
71
+
72
+ def collection_name
73
+ @collection_name ||= self.class.name.underscore.split('/').last
74
+ end
75
+
76
+ def resource_class
77
+ @resource_class ||= begin
78
+ Scalingo::Endpoint.const_get(self.class.name.singularize.split('::').last)
79
+ rescue
80
+ OpenStruct
81
+ end
82
+ end
83
+
84
+ def find_by
85
+ 'id'
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ Dir[File.join(File.dirname(__FILE__), 'endpoint', '*.rb')].each do |endpoint|
92
+ require endpoint
93
+ end
94
+
@@ -0,0 +1,13 @@
1
+ module Scalingo
2
+ # Custom error class for rescuing from all Scalingo errors
3
+ class Error < StandardError; end
4
+
5
+ # Raised when Scalingo returns the HTTP status code 400
6
+ class BadRequest < Error; end
7
+
8
+ # Raised when Scalingo returns the HTTP status code 404
9
+ class NotFound < Error; end
10
+
11
+ # Raised when Scalingo returns the HTTP status code 500
12
+ class InternalServerError < Error; end
13
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'realtime/logs'
2
+
3
+ module Scalingo
4
+ class Logs < Client
5
+ attr_reader :app
6
+
7
+ def initialize(app)
8
+ super({endpoint: ''})
9
+ @app = app
10
+ end
11
+
12
+ def dump(lines = 10)
13
+ get('', n: lines)
14
+ end
15
+
16
+ def realtime
17
+ Scalingo::Realtime::Logs.new(app)
18
+ end
19
+
20
+ protected
21
+ def log_token
22
+ self.endpoint, log_token = app.logs_url.split('?')
23
+ @log_token = log_token.split('=').last
24
+ end
25
+
26
+ def parse_json
27
+ false
28
+ end
29
+
30
+ def request(method, path, options)
31
+ options.merge!(token: log_token)
32
+ super(method, path, options)
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,50 @@
1
+ require 'faye/websocket'
2
+ require 'eventmachine'
3
+
4
+ module Scalingo
5
+ module Realtime
6
+ class Logs
7
+ attr_reader :app
8
+
9
+ def initialize(app)
10
+ @app = app
11
+ @callbacks = []
12
+ end
13
+
14
+ def each_line(&block)
15
+ @callbacks << block
16
+ end
17
+
18
+ def start
19
+ each_line(&Proc.new) if block_given?
20
+
21
+ EM.run do
22
+ ws = Faye::WebSocket::Client.new(url)
23
+
24
+ ws.on :open do |event|
25
+ end
26
+
27
+ ws.on :message do |event|
28
+ data = JSON.parse(event.data)
29
+ if data['event'] == 'log'
30
+ @callbacks.each{|c| c.call(data['log'])}
31
+ end
32
+ end
33
+
34
+ ws.on :close do |event|
35
+ ws = nil
36
+ end
37
+
38
+ Signal.trap('INT') { EM.stop }
39
+ Signal.trap('TERM') { EM.stop }
40
+ end
41
+ end
42
+
43
+ protected
44
+ def url
45
+ "#{app.logs_url}&stream=true"
46
+ end
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,32 @@
1
+ module Scalingo
2
+ module Request
3
+ REQUEST_METHODS = [
4
+ :get,
5
+ :post,
6
+ :patch,
7
+ :put,
8
+ :delete
9
+ ]
10
+
11
+ REQUEST_METHODS.each do |method|
12
+ define_method(method) do |path, options = {}|
13
+ request(method, path, options)
14
+ end
15
+ end
16
+
17
+ protected
18
+ def request(method, path, options)
19
+ response = connection.send(method) do |request|
20
+ case method
21
+ when :get, :delete
22
+ request.url(path, options)
23
+ when :post, :patch, :put
24
+ request.path = path
25
+ request.body = options if !options.empty?
26
+ end
27
+ end
28
+ return response.body
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,4 @@
1
+ module Scalingo
2
+ VERSION = '1.0.0.alpha1'
3
+ end
4
+
data/lib/scalingo.rb ADDED
@@ -0,0 +1,26 @@
1
+ require 'active_support/inflector'
2
+
3
+ require_relative 'scalingo/error'
4
+ require_relative 'scalingo/configuration'
5
+ require_relative 'scalingo/api'
6
+ require_relative 'scalingo/client'
7
+ require_relative 'scalingo/logs'
8
+ require_relative 'scalingo/version'
9
+
10
+ module Scalingo
11
+ extend Configuration
12
+
13
+ def self.client(options = {})
14
+ Scalingo::Client.new(options)
15
+ end
16
+
17
+ def self.method_missing(method, *args, &block)
18
+ return super unless client.respond_to?(method)
19
+ client.send(method, *args, &block)
20
+ end
21
+
22
+ def self.respond_to?(method)
23
+ return client.respond_to?(method) || super
24
+ end
25
+ end
26
+
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scalingo-ruby-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.alpha1
5
+ platform: ruby
6
+ authors:
7
+ - Geoffroy Planquart
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0.7'
20
+ - - <=
21
+ - !ruby/object:Gem::Version
22
+ version: '0.9'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0.7'
30
+ - - <=
31
+ - !ruby/object:Gem::Version
32
+ version: '0.9'
33
+ - !ruby/object:Gem::Dependency
34
+ name: faraday_middleware
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ version: '0.8'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: '0.8'
47
+ - !ruby/object:Gem::Dependency
48
+ name: multi_json
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.0.3
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: '1.0'
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - '>='
62
+ - !ruby/object:Gem::Version
63
+ version: 1.0.3
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ version: '1.0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: faye-websocket
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ version: 0.9.2
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ~>
79
+ - !ruby/object:Gem::Version
80
+ version: 0.9.2
81
+ - !ruby/object:Gem::Dependency
82
+ name: activesupport
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ~>
86
+ - !ruby/object:Gem::Version
87
+ version: '3'
88
+ type: :runtime
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ~>
93
+ - !ruby/object:Gem::Version
94
+ version: '3'
95
+ description: Ruby wrapper around the web API of scalingo.io
96
+ email:
97
+ - geoffroy@planquart.fr
98
+ executables: []
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - lib/faraday/raise_http_exception.rb
103
+ - lib/scalingo.rb
104
+ - lib/scalingo/api.rb
105
+ - lib/scalingo/client.rb
106
+ - lib/scalingo/configuration.rb
107
+ - lib/scalingo/connection.rb
108
+ - lib/scalingo/endpoint.rb
109
+ - lib/scalingo/endpoint/account_keys.rb
110
+ - lib/scalingo/endpoint/addon_categories.rb
111
+ - lib/scalingo/endpoint/addon_provider.rb
112
+ - lib/scalingo/endpoint/addons.rb
113
+ - lib/scalingo/endpoint/apps.rb
114
+ - lib/scalingo/endpoint/collaborators.rb
115
+ - lib/scalingo/endpoint/deployments.rb
116
+ - lib/scalingo/endpoint/domains.rb
117
+ - lib/scalingo/endpoint/events.rb
118
+ - lib/scalingo/endpoint/variables.rb
119
+ - lib/scalingo/error.rb
120
+ - lib/scalingo/logs.rb
121
+ - lib/scalingo/realtime/logs.rb
122
+ - lib/scalingo/request.rb
123
+ - lib/scalingo/version.rb
124
+ homepage: https://github.com/Aethelflaed/scalingo-ruby-api
125
+ licenses:
126
+ - MIT
127
+ metadata: {}
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - '>'
140
+ - !ruby/object:Gem::Version
141
+ version: 1.3.1
142
+ requirements: []
143
+ rubyforge_project:
144
+ rubygems_version: 2.4.6
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: Ruby API for the awesome scalingo project !
148
+ test_files: []
149
+ has_rdoc: