cloudster 2.5.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,10 +3,10 @@ GEM
3
3
  specs:
4
4
  builder (3.1.4)
5
5
  diff-lcs (1.1.3)
6
- excon (0.14.2)
7
- fog (1.4.0)
6
+ excon (0.16.8)
7
+ fog (1.7.0)
8
8
  builder
9
- excon (~> 0.14.0)
9
+ excon (~> 0.14)
10
10
  formatador (~> 0.2.0)
11
11
  mime-types
12
12
  multi_json (~> 1.0)
@@ -14,7 +14,7 @@ GEM
14
14
  net-ssh (>= 2.1.3)
15
15
  nokogiri (~> 1.5.0)
16
16
  ruby-hmac
17
- formatador (0.2.3)
17
+ formatador (0.2.4)
18
18
  git (1.2.5)
19
19
  jeweler (1.8.4)
20
20
  bundler (~> 1.0)
@@ -23,22 +23,22 @@ GEM
23
23
  rdoc
24
24
  json (1.7.5)
25
25
  mime-types (1.19)
26
- multi_json (1.3.6)
26
+ multi_json (1.3.7)
27
27
  net-scp (1.0.4)
28
28
  net-ssh (>= 1.99.1)
29
29
  net-ssh (2.6.1)
30
30
  nokogiri (1.5.5)
31
- rake (0.9.2.2)
31
+ rake (10.0.1)
32
32
  rdoc (3.12)
33
33
  json (~> 1.4)
34
- rspec (2.8.0)
35
- rspec-core (~> 2.8.0)
36
- rspec-expectations (~> 2.8.0)
37
- rspec-mocks (~> 2.8.0)
38
- rspec-core (2.8.0)
39
- rspec-expectations (2.8.0)
40
- diff-lcs (~> 1.1.2)
41
- rspec-mocks (2.8.0)
34
+ rspec (2.12.0)
35
+ rspec-core (~> 2.12.0)
36
+ rspec-expectations (~> 2.12.0)
37
+ rspec-mocks (~> 2.12.0)
38
+ rspec-core (2.12.0)
39
+ rspec-expectations (2.12.0)
40
+ diff-lcs (~> 1.1.3)
41
+ rspec-mocks (2.12.0)
42
42
  ruby-hmac (0.4.0)
43
43
 
44
44
  PLATFORMS
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # Cloudster [![Build Status](https://travis-ci.org/emilsoman/cloudster.png)](https://travis-ci.org/emilsoman/cloudster) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/emilsoman/cloudster) [![Dependency Status](https://gemnasium.com/emilsoman/cloudster.png)](https://gemnasium.com/emilsoman/cloudster)
2
2
 
3
- Cloudster exposes simple helper methods to provision your AWS cloud.
4
- Cloudster uses the AWS APIs to provision stacks on Amazon Cloud.
3
+ Cloudster is a Ruby gem that was born to cut the learning curve involved in writing your own CloudFormation templates. If you don't know what
4
+ a CloudFormation template is, but know about the AWS Cloud offerings, you can still use cloudster to provision your stack. Still in infancy , cloudster
5
+ can create a very basic stack like a breeze. All kinds of contribution welcome !
5
6
 
6
7
  ##Installation
7
8
 
@@ -9,7 +10,7 @@ Cloudster uses the AWS APIs to provision stacks on Amazon Cloud.
9
10
 
10
11
  ## Usage
11
12
 
12
- Create AWS resources as shown here:
13
+ Create AWS resources :
13
14
 
14
15
  app_server = Cloudster::Ec2.new(:name => 'AppServer',
15
16
  :key_name => 'mykey',
@@ -20,7 +21,9 @@ Create AWS resources as shown here:
20
21
  chef_client = Cloudster::ChefClient.new(
21
22
  :validation_key => 'asd3e33880889098asdnmnnasd8900890a8sdmasdjna9s880808asdnmnasd90-a',
22
23
  :server_url => 'http://10.50.60.70:4000',
23
- :node_name => 'project.environment.appserver_1'
24
+ :node_name => 'project.environment.appserver_1',
25
+ :environment => 'production',
26
+ :interval => 1800
24
27
  )
25
28
 
26
29
  chef_client.add_to(app_server)
@@ -32,7 +35,8 @@ Create AWS resources as shown here:
32
35
 
33
36
  #Add your app servers to the ElasticLoadBalancer
34
37
  load_balancer = Cloudster::Elb.new(:name => 'LoadBalancer',
35
- :instance_names => ['AppServer', 'AppServer2']
38
+ :instance_names => ['AppServer', 'AppServer2'],
39
+ :listeners => [{:port => 80, :instance_port => 8080, :protocol => 'HTTP'}]
36
40
  )
37
41
 
38
42
  database = Cloudster::Rds.new(
@@ -48,13 +52,16 @@ Make a cloud :
48
52
 
49
53
  cloud = Cloudster::Cloud.new(:access_key_id => 'accesskeyid', :secret_access_key => 'topsecretaccesskey')
50
54
 
51
- - Get the CloudFormation template for a resource in Ruby Hash :
52
-
53
- app_server.template
54
- - Get the CloudFormation template for the stack :
55
+ Get the CloudFormation template for the stack :
55
56
 
56
57
  cloud.template(:resources => [app_server, app_server_2, load_balancer, database], :description => 'Description of the stack')
57
-
58
+
59
+ Get the CloudFormation template for a resource as a Ruby Hash :
60
+
61
+ app_server.template
62
+
63
+ Cloudster can also do things on the AWS Cloud :
64
+
58
65
  - Provision the stack :
59
66
 
60
67
  cloud.provision(:resources => [app_server, app_server_2, load_balancer, database], :stack_name => 'TestStack', :description => 'Description of the stack')
@@ -67,10 +74,58 @@ Make a cloud :
67
74
 
68
75
  cloud.delete(:stack_name => 'TestStack')
69
76
 
70
- - Describe the events of a stack using :
77
+ - Describe the events of a stack :
71
78
 
72
79
  cloud.events(:stack_name => 'TestStack')
73
80
 
81
+ ----------------
82
+
83
+ # Contribute !
84
+
85
+ Got some love for Cloudster? That's great!
86
+
87
+ ## Found a bug?
88
+
89
+ Log the bug in the [issue tracker](https://github.com/emilsoman/cloudster/issues). Be sure to include all relevant information, like
90
+ the versions of Cloudster and Ruby you're using. A [gist](http://gist.github.com/)
91
+ of the code that caused the issue as well as any error messages are also very
92
+ helpful.
93
+
94
+ ## Need help?
95
+
96
+ You can use the [Issues](https://github.com/emilsoman/cloudster/issues) page to ask a new question. This is how you do it:
97
+ 1. Click on New Issue
98
+ 2. Type in your question
99
+ 3. Add a "question" label to the issue
100
+
101
+ ## Have a patch?
102
+
103
+ Bugs and feature requests that include patches are much more likely to
104
+ get attention. Here are some guidelines that will help ensure your patch
105
+ can be applied as quickly as possible:
106
+
107
+ 1. **Use [Git](http://git-scm.com) and [GitHub](http://github.com):**
108
+ The easiest way to get setup is to fork the
109
+ [cloudster repo](http://github.com/emilsoman/cloudster/).
110
+
111
+ 2. **Write unit tests:** If you add or modify functionality, it must
112
+ include unit tests. I use RSpec to test cloudster. If you are not an
113
+ RSpec expert, if you let me know, I can help you write the specs.
114
+
115
+ 3. **Update the `README`:** If the patch adds or modifies a major feature,
116
+ modify the `README.md` file to reflect that. Again if you're not an
117
+ expert with Markdown syntax, it's really easy to learn. Check out [Dillinger](http://dillinger.io/) to
118
+ try it out.
119
+
120
+ 4. **Push it:** Once you're ready, push your changes to a topic branch
121
+ and add a note to the ticket with the URL to your branch. Or, say
122
+ something like, "you can find the patch on johndoe/foobranch". I also
123
+ gladly accept Github [pull requests](http://help.github.com/pull-requests/).
124
+
125
+ __NOTE:__ _I will take in whatever I can get._ If you prefer to
126
+ attach diffs in comments on issues, that's fine; but do know
127
+ that _someone_ will need to take the diff through the process described
128
+ above and this can hold things up considerably.
74
129
 
75
130
 
76
131
  ##License
@@ -78,4 +133,3 @@ Make a cloud :
78
133
  MIT
79
134
 
80
135
  *Free Software, Forever . YEAH !*
81
- [![githalytics.com alpha](https://cruel-carlota.pagodabox.com/eafd00c3c74c7362ed850f6a50120c30 "githalytics.com")](http://githalytics.com/emilsoman/cloudster)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.5.0
1
+ 2.6.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "cloudster"
8
- s.version = "2.5.0"
8
+ s.version = "2.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Emil Soman"]
12
- s.date = "2012-11-09"
12
+ s.date = "2012-11-16"
13
13
  s.description = "Cloudster uses the AWS APIs to provision stacks on Amazon Cloud."
14
14
  s.email = "emil.soman@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -5,14 +5,14 @@ module Cloudster
5
5
  # Initialize an ChefClient configuration
6
6
  #
7
7
  # ==== Notes
8
- # options parameter must include values for :instance_name, :validation_key, :server_url and :node_name
8
+ # options parameter must include values for :validation_key, :server_url and :node_name
9
9
  #
10
10
  # ==== Examples
11
11
  # chef_client = Cloudster::ChefClient.new(
12
- # :instance_name => 'AppServer',
13
12
  # :validation_key => 'asd3e33880889098asdnmnnasd8900890a8sdmasdjna9s880808asdnmnasd90-a',
14
13
  # :server_url => 'http://10.50.60.70:4000',
15
- # :node_name => 'project.environment.appserver_1'
14
+ # :node_name => 'project.environment.appserver_1',
15
+ # :interval => 1800
16
16
  # )
17
17
  #
18
18
  # ==== Parameters
@@ -21,11 +21,13 @@ module Cloudster
21
21
  # * :validation_key: String containing the key used for validating this client with the server. This can be taken from the chef-server validation.pem file. Mandatory field
22
22
  # * :server_url: String containing the fully qualified domain name of the chef-server. Mandatory field
23
23
  # * :node_name: String containing the name for the chef node. It has to be unique across all nodes in the particular chef client-server ecosystem. Mandatory field
24
+ # * :interval: Integer containing the interval(in seconds) between chef-client runs. Degault value : 1800 seconds
24
25
  def initialize(options = {})
25
26
  require_options(options, [:validation_key, :server_url, :node_name])
26
27
  @validation_key = options[:validation_key]
27
28
  @server_url = options[:server_url]
28
29
  @node_name = options[:node_name]
30
+ @interval = options[:interval] || 1800
29
31
  end
30
32
 
31
33
  # Merges the required CloudFormation template for installing the Chef Client to the template of the EC2 instance
@@ -97,25 +99,20 @@ module Cloudster
97
99
  "cat << EOF > /etc/chef/solo.rb\n",
98
100
  "file_cache_path \"/tmp/chef-solo\"\n",
99
101
  "cookbook_path \"/tmp/chef-solo/cookbooks\"\n",
102
+ "node_name \"#{@node_name}\"\n",
100
103
  "EOF\n",
101
104
  "cat << EOF > /etc/chef/chef.json\n",
102
105
  "{\n",
103
- "\"chef_server\": {\n",
104
- " \"server_url\": \"http://localhost:4000\",\n",
105
- " \"webui_enabled\": true,\n",
106
- " \"node_name\": \"#{@node_name}\"\n",
106
+ "\"chef_client\": {\n",
107
+ " \"server_url\": \"#{@server_url}\",\n",
108
+ " \"interval\": \"#{@interval}\"\n",
107
109
  "},\n",
108
110
  "\"run_list\": [\"recipe[chef-client::config]\", \"recipe[chef-client]\"]\n",
109
111
  "}\n",
110
112
  "EOF\n",
111
-
113
+ "echo \"#{@validation_key}\" > /etc/chef/validation.pem\n",
112
114
  "# Bootstrap chef\n",
113
- "chef-solo -c /etc/chef/solo.rb -j /etc/chef/chef.json -r http://s3.amazonaws.com/chef-solo/bootstrap-latest.tar.gz > /tmp/chef_solo.log 2>&1 || error_exit 'Failed to bootstrap chef client'\n",
114
-
115
- "# Fixup the server URL in client.rb\n",
116
- "echo \"#{@validation_key}\" > /etc/chef/validation.pem 2>&1 || error_exit 'Failed to get Chef Server validation key'\n",
117
- "sed -i 's|http://localhost:4000|", @server_url , "|g' /etc/chef/client.rb\n",
118
- "chef-client -i 20 > /tmp/chef_client.log 2>&1 || error_exit 'Failed to initialize host via chef client' \n"
115
+ "chef-solo -c /etc/chef/solo.rb -j /etc/chef/chef.json -r http://s3.amazonaws.com/chef-solo/bootstrap-latest.tar.gz > /tmp/chef_solo.log 2>&1 || error_exit 'Failed to bootstrap chef client'\n"
119
116
  ]]}}
120
117
  }
121
118
  }
@@ -10,17 +10,20 @@ module Cloudster
10
10
  # ==== Examples
11
11
  # elb = Cloudster::Elb.new(
12
12
  # :name => 'LoadBalancer',
13
- # :instance_names => ['AppServer1','AppServer2']
13
+ # :instance_names => ['AppServer1','AppServer2'],
14
+ # :listeners => [{:port => 80, :instance_port => 8080, :protocol => 'HTTP'}]
14
15
  # )
15
16
  #
16
17
  # ==== Parameters
17
18
  # * options<~Hash> -
18
19
  # * :name: String containing the name for the Elb resource
19
20
  # * :instance_names: Array containing the names of the Ec2 resources which will be added under the ELB
21
+ # * :listeners: Array of listener hashes. Each listener must be registered for a specific port, and you can not have more than one listener for a given port. Default : {:port => 80, :instance_port => 80, :protocol => 'HTTP'}
20
22
  def initialize(options = {})
21
23
  require_options(options, [:name, :instance_names])
22
24
  @name = options[:name]
23
25
  @instance_names = options[:instance_names]
26
+ @listeners = options[:listeners] || [{:port => 80, :instance_port => 80, :protocol => 'HTTP'}]
24
27
  end
25
28
 
26
29
  # Returns a Ruby hash version of the Cloud Formation template for the resource instance
@@ -35,7 +38,7 @@ module Cloudster
35
38
  # ==== Returns
36
39
  # * Ruby hash version of the Cloud Formation template for the resource instance
37
40
  def template
38
- Elb.template({:name =>@name, :instance_names => @instance_names})
41
+ Elb.template({:name =>@name, :instance_names => @instance_names, :listeners => @listeners})
39
42
  end
40
43
 
41
44
  # Class method that returns a Ruby hash version of the Cloud Formation template
@@ -43,7 +46,8 @@ module Cloudster
43
46
  # ==== Examples
44
47
  # template = Cloudster::Elb.template(
45
48
  # :name => 'LoadBalances',
46
- # :instance_names => ['AppServer1', 'AppServer2']
49
+ # :instance_names => ['AppServer1', 'AppServer2'],
50
+ # :listeners => [{:port => 80, :instance_port => 80, :protocol => 'HTTP'}]
47
51
  # )
48
52
  #
49
53
  # ==== Parameters
@@ -51,16 +55,13 @@ module Cloudster
51
55
  # *Keys:
52
56
  # * :name: String containing the name for the Elb resource
53
57
  # * :instance_names: Array containing the names of the Ec2 resources which will be added under the ELB
54
- #
58
+ # * :listeners: Array of listener hashes. Each listener must be registered for a specific port, and you can not have more than one listener for a given port. Default : {:port => 80, :instance_port => 80, :protocol => 'HTTP'}
55
59
  # ==== Returns
56
60
  # * Ruby hash version of the Cloud Formation template
57
61
  def self.template(options = {})
58
62
  require_options(options, [:name, :instance_names])
59
63
  properties = {"AvailabilityZones" => { "Fn::GetAZs" => "" },
60
- "Listeners" => [{ "LoadBalancerPort" => "80",
61
- "InstancePort" => "80",
62
- "Protocol" => "HTTP"
63
- }],
64
+ "Listeners" => get_listeners_for_template(options[:listeners]),
64
65
  "HealthCheck" => {
65
66
  "Target" => { "Fn::Join" => [ "", ["HTTP:", "80", "/"]]},
66
67
  "HealthyThreshold" => "3",
@@ -90,5 +91,19 @@ module Cloudster
90
91
  return instance_list
91
92
  end
92
93
 
94
+ #Get listeners array for template
95
+ def self.get_listeners_for_template(listeners)
96
+ default_listener = [{:port => 80, :instance_port => 80, :protocol => 'HTTP'}]
97
+ listeners = default_listener if listeners.nil?
98
+ listener_array = []
99
+ listeners.each do |listener|
100
+ listener_array << { "LoadBalancerPort" => "#{listener[:port]}",
101
+ "InstancePort" => "#{listener[:instance_port]}",
102
+ "Protocol" => "#{listener[:protocol]}"
103
+ }
104
+ end
105
+ return listener_array
106
+ end
107
+
93
108
  end
94
109
  end
@@ -12,7 +12,7 @@ describe Cloudster::ChefClient do
12
12
  describe '#add_to' do
13
13
  it "should add chef client configuration to ec2 template" do
14
14
  ec2 = Cloudster::Ec2.new(:key_name => 'testkey', :image_id => 'image_id', :name => 'AppServer', :instance_type => 't1.micro' )
15
- chef_client = Cloudster::ChefClient.new(:validation_key => 'somekey', :server_url => 'testurl', :node_name => 'uniquename')
15
+ chef_client = Cloudster::ChefClient.new(:validation_key => 'somekey', :server_url => 'testurl', :node_name => 'uniquename', :interval => 30)
16
16
  chef_client.add_to ec2
17
17
  ec2.template.should ==
18
18
  {
@@ -26,28 +26,31 @@ describe Cloudster::ChefClient do
26
26
  "UserData"=>{
27
27
  "Fn::Base64"=>{
28
28
  "Fn::Join"=>["", [
29
- "#!/bin/bash -v\n",
30
- "function error_exit\n",
31
- "{\n", " exit 1\n", "}\n",
29
+ "#!/bin/bash -v\n",
30
+ "function error_exit\n",
31
+ "{\n",
32
+ " exit 1\n",
33
+ "}\n",
34
+
32
35
  "mkdir /etc/chef\n",
33
36
  "cat << EOF > /etc/chef/solo.rb\n",
34
37
  "file_cache_path \"/tmp/chef-solo\"\n",
35
38
  "cookbook_path \"/tmp/chef-solo/cookbooks\"\n",
36
- "EOF\n", "cat << EOF > /etc/chef/chef.json\n",
37
- "{\n", "\"chef_server\": {\n",
38
- " \"server_url\": \"http://localhost:4000\",\n",
39
- " \"webui_enabled\": true,\n",
40
- " \"node_name\": \"uniquename\"\n",
39
+ "node_name \"uniquename\"\n",
40
+ "EOF\n",
41
+ "cat << EOF > /etc/chef/chef.json\n",
42
+ "{\n",
43
+ "\"chef_client\": {\n",
44
+ " \"server_url\": \"testurl\",\n",
45
+ " \"interval\": \"30\"\n",
41
46
  "},\n",
42
47
  "\"run_list\": [\"recipe[chef-client::config]\", \"recipe[chef-client]\"]\n",
43
48
  "}\n",
44
49
  "EOF\n",
50
+ "echo \"somekey\" > /etc/chef/validation.pem\n",
45
51
  "# Bootstrap chef\n",
46
- "chef-solo -c /etc/chef/solo.rb -j /etc/chef/chef.json -r http://s3.amazonaws.com/chef-solo/bootstrap-latest.tar.gz > /tmp/chef_solo.log 2>&1 || error_exit 'Failed to bootstrap chef client'\n",
47
- "# Fixup the server URL in client.rb\n",
48
- "echo \"somekey\" > /etc/chef/validation.pem 2>&1 || error_exit 'Failed to get Chef Server validation key'\n",
49
- "sed -i 's|http://localhost:4000|", "testurl", "|g' /etc/chef/client.rb\n",
50
- "chef-client -i 20 > /tmp/chef_client.log 2>&1 || error_exit 'Failed to initialize host via chef client' \n"
52
+ "chef-solo -c /etc/chef/solo.rb -j /etc/chef/chef.json -r http://s3.amazonaws.com/chef-solo/bootstrap-latest.tar.gz > /tmp/chef_solo.log 2>&1 || error_exit 'Failed to bootstrap chef client'\n"
53
+
51
54
  ]]
52
55
  }
53
56
  }
@@ -9,12 +9,18 @@ describe Cloudster::Elb do
9
9
  expect { Cloudster::Elb.new(:name => 'LoadBalancer', :instance_names => ['AppServer', 'AppServer2']) }.to_not raise_error
10
10
  end
11
11
  end
12
- describe '#template' do
13
- it "should return a ruby hash for the resource cloudformation template" do
12
+ describe '#template with default listener' do
13
+ it "should return a ruby hash for the resource cloudformation template with default listener" do
14
14
  elb = Cloudster::Elb.new(:name => 'LoadBalancer', :instance_names => ['AppServer', 'AppServer2'])
15
15
  elb.template.should == {"Resources" => {"LoadBalancer"=>{"Type"=>"AWS::ElasticLoadBalancing::LoadBalancer", "Properties"=>{"AvailabilityZones"=>{"Fn::GetAZs"=>""}, "Listeners"=>[{"LoadBalancerPort"=>"80", "InstancePort"=>"80", "Protocol"=>"HTTP"}], "HealthCheck"=>{"Target"=>{"Fn::Join"=>["", ["HTTP:", "80", "/"]]}, "HealthyThreshold"=>"3", "UnhealthyThreshold"=>"5", "Interval"=>"30", "Timeout"=>"5"}, "Instances"=>[{"Ref"=>"AppServer"}, {"Ref"=>"AppServer2"}]}}}}
16
16
  end
17
17
  end
18
+ describe '#template with custom listener' do
19
+ it "should return a ruby hash for the resource cloudformation template with the custom listener" do
20
+ elb = Cloudster::Elb.new(:name => 'LoadBalancer', :instance_names => ['AppServer', 'AppServer2'], :listeners => [{:port => 80, :instance_port => 3333, :protocol => 'HTTP'}])
21
+ elb.template.should == {"Resources" => {"LoadBalancer"=>{"Type"=>"AWS::ElasticLoadBalancing::LoadBalancer", "Properties"=>{"AvailabilityZones"=>{"Fn::GetAZs"=>""}, "Listeners"=>[{"LoadBalancerPort"=>"80", "InstancePort"=>"3333", "Protocol"=>"HTTP"}], "HealthCheck"=>{"Target"=>{"Fn::Join"=>["", ["HTTP:", "80", "/"]]}, "HealthyThreshold"=>"3", "UnhealthyThreshold"=>"5", "Interval"=>"30", "Timeout"=>"5"}, "Instances"=>[{"Ref"=>"AppServer"}, {"Ref"=>"AppServer2"}]}}}}
22
+ end
23
+ end
18
24
  describe '.template' do
19
25
  it "should raise argument error if no argument is not provided" do
20
26
  expect { Cloudster::Elb.template() }.to raise_error(ArgumentError, 'Missing required argument: name,instance_names')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudster
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.0
4
+ version: 2.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-09 00:00:00.000000000 Z
12
+ date: 2012-11-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
@@ -154,7 +154,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
154
154
  version: '0'
155
155
  segments:
156
156
  - 0
157
- hash: -233709777
157
+ hash: 239227311
158
158
  required_rubygems_version: !ruby/object:Gem::Requirement
159
159
  none: false
160
160
  requirements: