megam_api 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a7fec9b83d86dbf90747da9bc441fcd090f15f2b
4
- data.tar.gz: 24c8f581bb48ce1b094bdca1995219ec37daa591
3
+ metadata.gz: ea07146eea7fd29325b48224874415a83953875e
4
+ data.tar.gz: e0fc1cc6c4220220b5ca3fedab8fde4f6e2ef492
5
5
  SHA512:
6
- metadata.gz: 31c0aa4dcadca1422d6d51406d5f73efae369838ef630960b635cabc66be7ebb3dcf2ff16e321bca94855645e47b0a3629e690075df3ac5c77a0bcf9882830ab
7
- data.tar.gz: e35d039644391bb7cabf6b2570b4d23330cd6787c0268e079f35b1c16a29414faf4c2ee90ba376202457724fea07b97f7051f690a3dc378a2eeb9d935a28d367
6
+ metadata.gz: c3cfea38cfcfbaf3ba37d3dc29fccb848977f48ca79430feda954b70db0fa6e1cce030e45955ba998d172974de1505dda56017042f87db81f93f9317feefc44d
7
+ data.tar.gz: fc0faa8e998c1901e506665cce2139091924fcf6531f6d944150c7854ddcd02ed7caf6446362b87d43e0f4a631bc3ddb5cdef8418ca02153b22a42d929c62823
data/lib/megam/api.rb CHANGED
@@ -28,6 +28,7 @@ require "megam/api/predef_clouds"
28
28
  require "megam/api/cloud_tools"
29
29
  require "megam/api/cloud_tool_settings"
30
30
  require "megam/api/sshkeys"
31
+ require "megam/api/marketplaces"
31
32
  require "megam/core/server_api"
32
33
  require "megam/core/config"
33
34
  require "megam/core/stuff"
@@ -66,6 +67,8 @@ require "megam/core/cloudtoolsetting"
66
67
  require "megam/core/cloudtoolsetting_collection"
67
68
  require "megam/core/sshkey"
68
69
  require "megam/core/sshkey_collection"
70
+ require "megam/core/marketplace"
71
+ require "megam/core/marketplace_collection"
69
72
 
70
73
  #we may nuke logs out of the api
71
74
  #require "megam/api/logs"
@@ -0,0 +1,50 @@
1
+ module Megam
2
+ class API
3
+
4
+ # GET /nodes
5
+ def get_marketplaceapps
6
+ @options = {:path => '/marketplaces',:body => ""}.merge(@options)
7
+
8
+ request(
9
+ :expects => 200,
10
+ :method => :get,
11
+ :body => @options[:body]
12
+ )
13
+ end
14
+
15
+ def get_meraketplaceapp(id)
16
+ @options = {:path => "/marketplaces/#{id}",:body => ""}.merge(@options)
17
+
18
+ request(
19
+ :expects => 200,
20
+ :method => :get,
21
+ :body => @options[:body]
22
+ )
23
+ end
24
+
25
+ def post_marketplaceapp(new_node)
26
+ @options = {:path => '/marketplaces/content',
27
+ :body => Megam::JSONCompat.to_json(new_node)}.merge(@options)
28
+
29
+ request(
30
+ :expects => 201,
31
+ :method => :post,
32
+ :body => @options[:body]
33
+ )
34
+ end
35
+
36
+ #Yet to be tested
37
+ # DELETE /marketplacess/:node_id
38
+ def delete_marketplaceapp(node_id)
39
+ @options = {:path => '/marketplaces/#{node_id}',
40
+ :body => ""}.merge(@options)
41
+
42
+ request(
43
+ :expects => 200,
44
+ :method => :delete,
45
+ :body => @options[:body]
46
+ )
47
+ end
48
+
49
+ end
50
+ end
@@ -1,5 +1,5 @@
1
1
  module Megam
2
2
  class API
3
- VERSION = "0.8.1"
3
+ VERSION = "0.8.2"
4
4
  end
5
5
  end
@@ -26,7 +26,7 @@ module Megam
26
26
  end
27
27
 
28
28
  def megam_rest
29
- Megam::API.new(Megam::Config[:email], Megam::Config[:api_key])
29
+ Megam::Scm.new(Megam::Config[:name], Megam::Config[:displayName], Megam::Config[:mail], Megam::Config[:password])
30
30
  end
31
31
 
32
32
  def some_msg(arg=nil)
@@ -53,6 +53,8 @@ module Megam
53
53
  MEGAM_CLOUDINSTRUCTIONGROUP = "Megam::CloudInstructionGroup".freeze
54
54
  MEGAM_SSHKEY = "Megam::SshKey".freeze
55
55
  MEGAM_SSHKEYCOLLECTION = "Megam::SshKeyCollection".freeze
56
+ MEGAM_MARKETPLACE = "Megam::MarketPlace".freeze
57
+ MEGAM_MARKETPLACECOLLECTION = "Megam::MarketPlaceCollection".freeze
56
58
  class <<self
57
59
  # Increase the max nesting for JSON, which defaults
58
60
  # to 19, and isn't enough for some (for example, a Node within a Node)
@@ -185,6 +187,10 @@ module Megam
185
187
  Megam::SshKey
186
188
  when MEGAM_SSHKEYCOLLECTION
187
189
  Megam::SshKeyCollection
190
+ when MEGAM_MARKETPLACE
191
+ Megam::MarketPlace
192
+ when MEGAM_MARKETPLACECOLLECTION
193
+ Megam::MarketPlaceCollection
188
194
  else
189
195
  raise JSON::ParserError, "Unsupported `json_class` type '#{json_class}'"
190
196
  end
@@ -0,0 +1,263 @@
1
+ # Copyright:: Copyright (c) 2012, 2013 Megam Systems
2
+ # License:: Apache License, Version 2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ require 'hashie'
17
+
18
+ module Megam
19
+ class MarketPlace < Megam::ServerAPI
20
+ def initialize(email=nil, api_key=nil)
21
+ @id = nil
22
+ @name = nil
23
+ @logo = nil
24
+ @catagory = nil
25
+ @pricetype = nil
26
+ @features={}
27
+ @plan={}
28
+ @attach =nil
29
+ @predefnode=nil
30
+ @some_msg = {}
31
+ @approved = nil
32
+ @created_at = nil
33
+ super(email, api_key)
34
+ end
35
+
36
+ def market_place
37
+ self
38
+ end
39
+
40
+
41
+ def name(arg=nil)
42
+ if arg != nil
43
+ @name = arg
44
+ else
45
+ @name
46
+ end
47
+ end
48
+
49
+ def logo(arg=nil)
50
+ if arg != nil
51
+ @logo = arg
52
+ else
53
+ @logo
54
+ end
55
+ end
56
+
57
+ def catagory(arg=nil)
58
+ if arg != nil
59
+ @catagory = arg
60
+ else
61
+ @catagory
62
+ end
63
+ end
64
+
65
+ def id(arg=nil)
66
+ if arg != nil
67
+ @id = arg
68
+ else
69
+ @id
70
+ end
71
+ end
72
+
73
+ def pricetype(arg=nil)
74
+ if arg != nil
75
+ @pricetype = arg
76
+ else
77
+ @pricetype
78
+ end
79
+ end
80
+
81
+ def features(arg=nil)
82
+ if arg != nil
83
+ @features = arg
84
+ else
85
+ @features
86
+ end
87
+ end
88
+
89
+ def plan(arg=nil)
90
+ if arg != nil
91
+ @plan = arg
92
+ else
93
+ @plan
94
+ end
95
+ end
96
+
97
+ def predefnode(arg=nil)
98
+ if arg != nil
99
+ @predefnode = arg
100
+ else
101
+ @predefnode
102
+ end
103
+ end
104
+
105
+ def attach(arg=nil)
106
+ if arg != nil
107
+ @attach = arg
108
+ else
109
+ @attach
110
+ end
111
+ end
112
+
113
+ def approved(arg=nil)
114
+ if arg != nil
115
+ @approved = arg
116
+ else
117
+ @approved
118
+ end
119
+ end
120
+
121
+ def created_at(arg=nil)
122
+ if arg != nil
123
+ @created_at = arg
124
+ else
125
+ @created_at
126
+ end
127
+ end
128
+
129
+ def some_msg(arg=nil)
130
+ if arg != nil
131
+ @some_msg = arg
132
+ else
133
+ @some_msg
134
+ end
135
+ end
136
+
137
+ def error?
138
+ crocked = true if (some_msg.has_key?(:msg_type) && some_msg[:msg_type] == "error")
139
+ end
140
+
141
+ # Transform the ruby obj -> to a Hash
142
+ def to_hash
143
+ index_hash = Hash.new
144
+ index_hash["json_claz"] = self.class.name
145
+ index_hash["id"] = id
146
+ index_hash["name"] = name
147
+ index_hash["logo"] = logo
148
+ index_hash["catagory"] = catagory
149
+ index_hash["pricetype"] = pricetype
150
+ index_hash["features"] = features
151
+ index_hash["plan"] = plan
152
+ index_hash["attach"] = attach
153
+ index_hash["predefnode"] = predefnode
154
+ index_hash["approved"] = approved
155
+ index_hash["some_msg"] = some_msg
156
+ index_hash["created_at"] = created_at
157
+ index_hash
158
+ end
159
+
160
+ # Serialize this object as a hash: called from JsonCompat.
161
+ # Verify if this called from JsonCompat during testing.
162
+ def to_json(*a)
163
+ for_json.to_json(*a)
164
+ end
165
+
166
+ def for_json
167
+ result = {
168
+ "id" => id,
169
+ "name" => name,
170
+ "logo" => logo,
171
+ "catagory" => catagory,
172
+ "pricetype" => pricetype,
173
+ "features" => features,
174
+ "plan" => plan,
175
+ "attach" => attach,
176
+ "predefnode" => predefnode,
177
+ "approved" => approved,
178
+ "created_at" => created_at
179
+ }
180
+ result
181
+ end
182
+
183
+ def self.json_create(o)
184
+ app = new
185
+ app.id(o["id"]) if o.has_key?("id")
186
+ app.name(o["name"]) if o.has_key?("name")
187
+ app.logo(o["logo"]) if o.has_key?("logo")
188
+ app.catagory(o["catagory"]) if o.has_key?("catagory")
189
+ app.pricetype(o["pricetype"]) if o.has_key?("pricetype")
190
+ app.attach(["attach"]) if o.has_key?("attach")
191
+ app.predefnode(o["predefnode"]) if o.has_key?("predefnode")
192
+ app.approved(o["approved"]) if o.has_key?("approved")
193
+ app.created_at(o["created_at"]) if o.has_key?("created_at")
194
+ #requests
195
+ oq = o["features"]
196
+ app.features[:feature1] = oq["feature1"] if oq && oq.has_key?("feature1")
197
+ app.features[:feature2] = oq["feature2"] if oq && oq.has_key?("feature2")
198
+ app.features[:feature3] = oq["feature3"] if oq && oq.has_key?("feature3")
199
+ app.features[:feature4] = oq["feature4"] if oq && oq.has_key?("feature4")
200
+
201
+ op = o["plan"]
202
+ app.plan[:price] = op["price"] if op && op.has_key?("price")
203
+ app.plan[:description] = op["description"] if op && op.has_key?("description")
204
+ app.plan[:plantype]= op["plantype"] if op && op.has_key?("plantype")
205
+
206
+ #success or error
207
+ app.some_msg[:code] = o["code"] if o.has_key?("code")
208
+ app.some_msg[:msg_type] = o["msg_type"] if o.has_key?("msg_type")
209
+ app.some_msg[:msg]= o["msg"] if o.has_key?("msg")
210
+ app.some_msg[:links] = o["links"] if o.has_key?("links")
211
+
212
+ app
213
+ end
214
+
215
+ def self.from_hash(o,tmp_email=nil, tmp_api_key=nil)
216
+ app = self.new(tmp_email, tmp_api_key)
217
+ app.from_hash(o)
218
+ app
219
+ end
220
+
221
+ def from_hash(o)
222
+ @name = o["name"] if o.has_key?("name")
223
+ @catagory = o["catagory"] if o.has_key?("catagory")
224
+ @id = o["id"] if o.has_key?("id")
225
+ @logo = o["logo"] if o.has_key?("logo")
226
+ @pricetype = o["pricetype"] if o.has_key?("pricetype")
227
+ @features = o["features"] if o.has_key?("features")
228
+ @plan = o["plan"] if o.has_key?("plan")
229
+ @attach = o["attach"] if o.has_key?("attach")
230
+ @predefnode = o["predefnode"] if o.has_key?("predefnode")
231
+ @approved = o["approved"] if o.has_key?("approved")
232
+ @created_at = o["created_at"] if o.has_key?("created_at")
233
+ self
234
+ end
235
+
236
+ def self.create(o,tmp_email=nil, tmp_api_key=nil)
237
+ acct = from_hash(o, tmp_email, tmp_api_key)
238
+ acct.create
239
+ end
240
+
241
+ # Create the marketplace app via the REST API
242
+ def create
243
+ megam_rest.post_marketplaceapp(to_hash)
244
+ end
245
+
246
+ # Load a account by email_p
247
+ def self.show(name, tmp_email=nil, tmp_api_key=nil)
248
+ app = self.new(tmp_email, tmp_api_key)
249
+ app.megam_rest.get_marketplaceapp(name)
250
+ end
251
+
252
+ def self.list(tmp_email=nil, tmp_api_key=nil)
253
+ app = self.new(tmp_email, tmp_api_key)
254
+ app.megam_rest.get_marketplaceapps
255
+ end
256
+
257
+ def to_s
258
+ Megam::Stuff.styled_hash(to_hash)
259
+ #"---> Megam::Account:[error=#{error?}]\n"+
260
+ end
261
+
262
+ end
263
+ end
@@ -0,0 +1,148 @@
1
+ # Copyright:: Copyright (c) 2012, 2013 Megam Systems
2
+ # License:: Apache License, Version 2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ module Megam
17
+ class MarketPlaceCollection
18
+ include Enumerable
19
+
20
+
21
+ attr_reader :iterator
22
+ def initialize
23
+ @apps = Array.new
24
+ @apps_by_name = Hash.new
25
+ @insert_after_idx = nil
26
+ end
27
+
28
+ def all_apps
29
+ @apps
30
+ end
31
+
32
+ def [](index)
33
+ @apps[index]
34
+ end
35
+
36
+ def []=(index, arg)
37
+ is_megam_app(arg)
38
+ @apps[index] = arg
39
+ @apps_by_name[arg.name] = index
40
+ end
41
+
42
+ def <<(*args)
43
+ args.flatten.each do |a|
44
+ is_megam_app(a)
45
+ @apps << a
46
+ @apps_by_name[a.name] = @apps.length - 1
47
+ end
48
+ self
49
+ end
50
+
51
+ # 'push' is an alias method to <<
52
+ alias_method :push, :<<
53
+
54
+ def insert(app)
55
+ is_megam_app(app)
56
+ if @insert_after_idx
57
+ # in the middle of executing a run, so any nodes inserted now should
58
+ # be placed after the most recent addition done by the currently executing
59
+ # node
60
+ @apps.insert(@insert_after_idx + 1, app)
61
+ # update name -> location mappings and register new node
62
+ @apps_by_name.each_key do |key|
63
+ @apps_by_name[key] += 1 if @apps_by_name[key] > @insert_after_idx
64
+ end
65
+ @apps_by_name[app.name] = @insert_after_idx + 1
66
+ @insert_after_idx += 1
67
+ else
68
+ @apps << app
69
+ @apps_by_name[app.name] = @apps.length - 1
70
+ end
71
+ end
72
+
73
+ def each
74
+ @apps.each do |app|
75
+ yield app
76
+ end
77
+ end
78
+
79
+ def each_index
80
+ @apps.each_index do |i|
81
+ yield i
82
+ end
83
+ end
84
+
85
+ def empty?
86
+ @apps.empty?
87
+ end
88
+
89
+ def lookup(app)
90
+ lookup_by = nil
91
+ if app.kind_of?(Megam::MarketPlace)
92
+ lookup_by = app.name
93
+ elsif app.kind_of?(String)
94
+ lookup_by = app
95
+ else
96
+ raise ArgumentError, "Must pass a Megam::MarketPlace or String to lookup"
97
+ end
98
+ res = @apps_by_name[lookup_by]
99
+ unless res
100
+ raise ArgumentError, "Cannot find a app matching #{lookup_by} (did you define it first?)"
101
+ end
102
+ @apps[res]
103
+ end
104
+
105
+ # Transform the ruby obj -> to a Hash
106
+ def to_hash
107
+ index_hash = Hash.new
108
+ self.each do |app|
109
+ index_hash[app.name] = app.to_s
110
+ end
111
+ index_hash
112
+ end
113
+
114
+ # Serialize this object as a hash: called from JsonCompat.
115
+ # Verify if this called from JsonCompat during testing.
116
+ def to_json(*a)
117
+ for_json.to_json(*a)
118
+ end
119
+
120
+
121
+ def self.json_create(o)
122
+ collection = self.new()
123
+ o["results"].each do |apps_list|
124
+ apps_array = apps_list.kind_of?(Array) ? apps_list : [ apps_list ]
125
+ apps_array.each do |app|
126
+ collection.insert(app)
127
+ end
128
+ end
129
+ collection
130
+ end
131
+
132
+ private
133
+
134
+
135
+
136
+ def is_megam_app(arg)
137
+ unless arg.kind_of?(Megam::MarketPlace)
138
+ raise ArgumentError, "Members must be Megam::MarketPlace's"
139
+ end
140
+ true
141
+ end
142
+
143
+ def to_s
144
+ Megam::Stuff.styled_hash(to_hash)
145
+ end
146
+
147
+ end
148
+ end
@@ -0,0 +1,100 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/test_helper")
2
+
3
+ class TestMarketplaces < MiniTest::Unit::TestCase
4
+ =begin
5
+ def test_post_marketplace
6
+ tmp_hash = {
7
+ "name" => "sample",
8
+ "logo" => "riak.logo",
9
+ "catagory" => "DB",
10
+ "pricetype" => "free",
11
+ "features" => {"feature1" => "feature1","feature2" => "feature2","feature3" => "feature3","feature4" => "feature4"},
12
+ "plan" => {"price" => "50","description" => "description","plantype" => "free"},
13
+ "attach" => "attach",
14
+ "predefnode" => "predefnode",
15
+ "approved" => "approved" }
16
+
17
+ response = megams.post_marketplaceapp(tmp_hash)
18
+ assert_equal(201, response.status)
19
+ end
20
+ =end
21
+ =begin
22
+ def test_post_node2
23
+ tmp_hash = {
24
+ "node_name" => "sundown.megam.co",
25
+ "command" => "commands2",
26
+ "predefs" => {"name" => "rails", "scm" => "https://github.com/awesome.git",
27
+ "db" => "postgres@postgresql2.megam.com/morning.megam.co", "war" => "http://s3pub.com/0.1/orion.war", "queue" => "rabbit@queue1"}
28
+ }
29
+ response = megams.post_node(tmp_hash)
30
+ assert_equal(201, response.status)
31
+ end
32
+ =end
33
+ def test_get_marketplaces
34
+ response = megams.get_marketplaceapps
35
+ assert_equal(200, response.status)
36
+ end
37
+ =begin
38
+ def test_get_node0
39
+ response = megams.get_node("black1.megam.co")
40
+ assert_equal(200, response.status)
41
+ end
42
+
43
+ def test_get_node1
44
+ response = megams.get_node("night.megam.co")
45
+ assert_equal(200, response.status)
46
+ end
47
+
48
+ def test_get_node_not_found
49
+ assert_raises(Megam::API::Errors::NotFound) do
50
+ megams.get_node("stupid.megam.co")
51
+ end
52
+ end
53
+
54
+ def test_delete_node1
55
+
56
+ @com = {
57
+ "systemprovider" => {
58
+ "provider" => {
59
+ "prov" => "chef"
60
+ }
61
+ },
62
+ "compute" => {
63
+ "cctype" => "ec2",
64
+ "cc" => {
65
+ "groups" => "",
66
+ "image" => "",
67
+ "flavor" => "",
68
+ "tenant_id" => ""
69
+ },
70
+ "access" => {
71
+ "ssh_key" => "megam_ec2",
72
+ "identity_file" => "~/.ssh/megam_ec2.pem",
73
+ "ssh_user" => "",
74
+ "vault_location" => "https://s3-ap-southeast-1.amazonaws.com/cloudkeys/sandy@megamsandbox.com/default",
75
+ "sshpub_location" => "",
76
+ "zone" => "",
77
+ "region" => "region"
78
+ }
79
+ },
80
+ "cloudtool" => {
81
+ "chef" => {
82
+ "command" => "knife",
83
+ "plugin" => "ec2 server delete", #ec2 server delete or create
84
+ "run_list" => "",
85
+ "name" => ""
86
+ }
87
+ }
88
+ }
89
+
90
+ tmp_hash = {
91
+ "node_name" => "black1.megam.co",
92
+ "req_type" => "delete", #CREATE OR DELETE
93
+ "command" => @com
94
+ }
95
+
96
+ response = megams.post_request(tmp_hash)
97
+ assert_equal(201, response.status)
98
+ end
99
+ =end
100
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: megam_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kishorekumar Neelamegam, Thomas Alrin, Subash Sethurajan, Rajthilak
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-17 00:00:00.000000000 Z
11
+ date: 2014-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: excon
@@ -138,6 +138,7 @@ files:
138
138
  - lib/megam/api/errors.rb
139
139
  - lib/megam/api/login.rb
140
140
  - lib/megam/api/logs.rb
141
+ - lib/megam/api/marketplaces.rb
141
142
  - lib/megam/api/nodes.rb
142
143
  - lib/megam/api/predef_clouds.rb
143
144
  - lib/megam/api/predefs.rb
@@ -169,6 +170,8 @@ files:
169
170
  - lib/megam/core/error.rb
170
171
  - lib/megam/core/json_compat.rb
171
172
  - lib/megam/core/log.rb
173
+ - lib/megam/core/marketplace.rb
174
+ - lib/megam/core/marketplace_collection.rb
172
175
  - lib/megam/core/monologger.rb
173
176
  - lib/megam/core/node.rb
174
177
  - lib/megam/core/node_collection.rb
@@ -196,6 +199,7 @@ files:
196
199
  - test/test_helper.rb
197
200
  - test/test_login.rb
198
201
  - test/test_logs.rb
202
+ - test/test_marketplaces.rb
199
203
  - test/test_nodes.rb
200
204
  - test/test_predefclouds.rb
201
205
  - test/test_predefs.rb
@@ -221,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
221
225
  version: '0'
222
226
  requirements: []
223
227
  rubyforge_project:
224
- rubygems_version: 2.2.0
228
+ rubygems_version: 2.1.11
225
229
  signing_key:
226
230
  specification_version: 4
227
231
  summary: Ruby Client for the Megam Cloud