zzsharedlib 0.0.4
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.
- data/LICENSE +1 -0
- data/README.rdoc +10 -0
- data/Rakefile +25 -0
- data/lib/zzsharedlib.rb +11 -0
- data/lib/zzsharedlib/amazon.rb +207 -0
- data/lib/zzsharedlib/deploy_group_simple_db.rb +33 -0
- data/lib/zzsharedlib/monkey_patches.rb +39 -0
- data/lib/zzsharedlib/options.rb +30 -0
- data/lib/zzsharedlib/utilities.rb +64 -0
- metadata +115 -0
data/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Copyright (c) 2011 ZangZing, LLC
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'spec/rake/spectask'
|
|
2
|
+
require 'rake/rdoctask'
|
|
3
|
+
|
|
4
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
|
|
5
|
+
|
|
6
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
|
7
|
+
spec.libs << 'lib' << 'spec'
|
|
8
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
|
12
|
+
spec.libs << 'lib' << 'spec'
|
|
13
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
|
14
|
+
spec.rcov = true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Rake::RDocTask.new do |rdoc|
|
|
18
|
+
require 'zz_deploy'
|
|
19
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
20
|
+
rdoc.title = "zzdeploy #{ZZDeploy::VERSION}"
|
|
21
|
+
rdoc.rdoc_files.include('README*')
|
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
task :default => :spec
|
data/lib/zzsharedlib.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
|
|
4
|
+
require 'right_aws'
|
|
5
|
+
require 'sdb/active_sdb'
|
|
6
|
+
|
|
7
|
+
require 'zzsharedlib/utilities'
|
|
8
|
+
require 'zzsharedlib/monkey_patches'
|
|
9
|
+
require 'zzsharedlib/amazon'
|
|
10
|
+
require 'zzsharedlib/options'
|
|
11
|
+
require 'zzsharedlib/deploy_group_simple_db'
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
|
|
3
|
+
# this class manages global stuff related to the amazon connection
|
|
4
|
+
module ZZSharedLib
|
|
5
|
+
|
|
6
|
+
class Amazon
|
|
7
|
+
attr_reader :ec2, :elb
|
|
8
|
+
|
|
9
|
+
def self.ec2
|
|
10
|
+
@@ec2 ||= RightAws::Ec2.new(access_key, secret_key, :endpoint_url => 'https://ec2.us-east-1.amazonaws.com/', :logger => Amazon.logger)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.elb
|
|
14
|
+
@@elb ||= RightAws::ElbInterface.new(access_key, secret_key, :logger => Amazon.logger)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.secret_key
|
|
18
|
+
Options.get(:secret_key) || ENV['AWS_SECRET_ACCESS_KEY']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.access_key
|
|
22
|
+
Options.get(:access_key) || ENV['AWS_ACCESS_KEY_ID']
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.log_level
|
|
26
|
+
Options.get(:log_level) || Logger::Severity::WARN
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.make_logger
|
|
30
|
+
# need to pass in a logdev for this to work...
|
|
31
|
+
logger = Logger.new(STDOUT)
|
|
32
|
+
logger.level = log_level
|
|
33
|
+
logger
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.logger
|
|
37
|
+
@@logger ||= make_logger
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def initialize
|
|
41
|
+
connection = RightAws::ActiveSdb.establish_connection(Amazon.access_key, Amazon.secret_key, :logger => Amazon.logger)
|
|
42
|
+
@ec2 = Amazon.ec2
|
|
43
|
+
@elb = Amazon.elb
|
|
44
|
+
@force_tags = true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# get the deploy group object from the simple db
|
|
48
|
+
def find_deploy_group(group_name)
|
|
49
|
+
# first see if already exists
|
|
50
|
+
deploy_group = DeployGroupSimpleDB.find_by_zz_object_type_and_group(DeployGroupSimpleDB.object_type, group_name, :auto_load => true)
|
|
51
|
+
|
|
52
|
+
if deploy_group.nil? || deploy_group[:group] != group_name
|
|
53
|
+
raise "Deploy group not found. Make sure you specified the correct deploy group name."
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
deploy_group
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# call this to ensure the tags come from amazon next time
|
|
60
|
+
def flush_tags
|
|
61
|
+
@force_tags = true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# does a describe and returns the map, if we already have it and not doing force
|
|
65
|
+
# return what we already have
|
|
66
|
+
# we filter out any terminated instances
|
|
67
|
+
def describe_tags
|
|
68
|
+
return @describe_tags if @force_tag == false && !@describe_tags.nil?
|
|
69
|
+
@force_tags = false # use from cache next time unless somebody resets flag
|
|
70
|
+
# grab it all and filter later
|
|
71
|
+
all_tags = ec2.describe_tags
|
|
72
|
+
|
|
73
|
+
# filter out any terminated instances
|
|
74
|
+
instances = ec2.describe_instances
|
|
75
|
+
ignore_instances = Set.new
|
|
76
|
+
instances.each do |instance|
|
|
77
|
+
if instance[:aws_state] != 'running'
|
|
78
|
+
ignore_instances << instance[:aws_instance_id]
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
filtered_tags = []
|
|
82
|
+
all_tags.each do |tag|
|
|
83
|
+
resource_id = tag[:resource_id]
|
|
84
|
+
filtered_tags << tag unless ignore_instances.include?(resource_id)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
return @describe_tags = filtered_tags
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# return all the tags that match a given resource id
|
|
91
|
+
def tags_for_resource(id)
|
|
92
|
+
tags = []
|
|
93
|
+
id = id.to_s
|
|
94
|
+
describe_tags.each do |tag|
|
|
95
|
+
tags << tag if id == tag[:resource_id]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
return tags
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# flatten the key/values into a top level hash
|
|
102
|
+
# assumes all tags are for the same resource id
|
|
103
|
+
def flat_tags_for_resource(id)
|
|
104
|
+
tags = tags_for_resource(id)
|
|
105
|
+
flat = {}
|
|
106
|
+
tags.each do |tag|
|
|
107
|
+
key = tag[:key]
|
|
108
|
+
value = tag[:value]
|
|
109
|
+
flat[key.to_sym] = value
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
return flat
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# find an exact match for a given resource type, key, and value
|
|
116
|
+
# returns a list of the resource_ids that matched
|
|
117
|
+
def find_typed_resource(type, key, value)
|
|
118
|
+
ids = []
|
|
119
|
+
type = type.to_s
|
|
120
|
+
key = key.to_s
|
|
121
|
+
value = value.to_s
|
|
122
|
+
describe_tags.each do |tag|
|
|
123
|
+
if type == tag[:resource_type] && key == tag[:key] && value == tag[:value]
|
|
124
|
+
ids << tag[:resource_id]
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
return ids
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# finds instances within a group/app by a role
|
|
132
|
+
def find_by_role(group, role)
|
|
133
|
+
match_role = find_typed_resource("instance", :role, role)
|
|
134
|
+
match_group = find_typed_resource("instance", :group, group)
|
|
135
|
+
# find the ones that match all three
|
|
136
|
+
match = match_role & match_group
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# find with filters and sort by Name, returns
|
|
140
|
+
# array of maps with
|
|
141
|
+
# [{:resource_id => inst_id, :Name => "Instance Name"},...]
|
|
142
|
+
#
|
|
143
|
+
def find_and_sort_named_instances(group = nil, role = nil, ready_only = true)
|
|
144
|
+
instances = {}
|
|
145
|
+
describe_tags.each do |tag|
|
|
146
|
+
if "instance" == tag[:resource_type]
|
|
147
|
+
resource_id = tag[:resource_id]
|
|
148
|
+
inst = instances[resource_id]
|
|
149
|
+
if inst.nil?
|
|
150
|
+
inst = { :resource_id => resource_id }
|
|
151
|
+
instances[resource_id] = inst
|
|
152
|
+
end
|
|
153
|
+
key = tag[:key].to_sym
|
|
154
|
+
value = tag[:value]
|
|
155
|
+
inst[key] = value
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# ok, we've collected the data now we need to filter it
|
|
160
|
+
filtered_instances = []
|
|
161
|
+
need_describe = []
|
|
162
|
+
instances.each_value do |inst|
|
|
163
|
+
next if inst[:group].nil? ||(group.nil? == false && inst[:group] != group.to_s)
|
|
164
|
+
next if inst[:role].nil? || (role.nil? == false && inst[:role] != role.to_s)
|
|
165
|
+
next if ready_only && inst[:state] != "ready"
|
|
166
|
+
filtered_instances << inst
|
|
167
|
+
need_describe << inst[:resource_id]
|
|
168
|
+
end
|
|
169
|
+
filtered_instances.sort! { |a,b| a[:Name] <=> b[:Name] }
|
|
170
|
+
|
|
171
|
+
# last step is to describe the instances we care about to get
|
|
172
|
+
# more info and add that to each instance returned
|
|
173
|
+
az_instances = ec2.describe_instances(need_describe)
|
|
174
|
+
az_hash = {}
|
|
175
|
+
az_instances.each do |instance|
|
|
176
|
+
key = instance[:aws_instance_id]
|
|
177
|
+
az_hash[key] = instance
|
|
178
|
+
end
|
|
179
|
+
# now update the info on filtered instances
|
|
180
|
+
filtered_instances.each do |instance|
|
|
181
|
+
inst_id = instance[:resource_id]
|
|
182
|
+
detail = az_hash[inst_id]
|
|
183
|
+
instance[:public_hostname] = detail[:dns_name]
|
|
184
|
+
instance[:local_hostname] = detail[:private_dns_name]
|
|
185
|
+
end
|
|
186
|
+
return filtered_instances
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# similar to find_and_sort_named_instances but
|
|
190
|
+
# returns a hash with the instance id as the key
|
|
191
|
+
# for each element
|
|
192
|
+
#
|
|
193
|
+
# {:instance_id => { :Name => "Instance Name", :role => role},...}
|
|
194
|
+
#
|
|
195
|
+
def find_named_instances(group = nil, role = nil, ready_only = true)
|
|
196
|
+
remapped = {}
|
|
197
|
+
instances = find_and_sort_named_instances(group, role)
|
|
198
|
+
instances.each do |instance|
|
|
199
|
+
key = instance[:resource_id].to_sym
|
|
200
|
+
remapped[key] = instance
|
|
201
|
+
end
|
|
202
|
+
return remapped
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module ZZSharedLib
|
|
2
|
+
class DeployGroupSimpleDB < RightAws::ActiveSdb::Base
|
|
3
|
+
set_domain_name "deploy"
|
|
4
|
+
|
|
5
|
+
columns do
|
|
6
|
+
zz_object_type
|
|
7
|
+
group
|
|
8
|
+
config_json
|
|
9
|
+
recipes_deploy_tag
|
|
10
|
+
app_deploy_tag
|
|
11
|
+
created_at
|
|
12
|
+
updated_at
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.object_type
|
|
16
|
+
@@object_type ||= 'deploy_group_type'.freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def config
|
|
20
|
+
@config ||= JSON.parse(self.config_json).recursively_symbolize_keys!
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def config=(object)
|
|
24
|
+
self.config_json = JSON.fast_generate(object)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# use strings for time
|
|
28
|
+
def save
|
|
29
|
+
self.updated_at = Time.now.strftime('%Y-%m-%dT%H:%M:%S%z')
|
|
30
|
+
super
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# serialize the data with padding
|
|
2
|
+
class RightAws::ActiveSdb
|
|
3
|
+
class IntegerSerialization
|
|
4
|
+
class << self
|
|
5
|
+
def serialize(int)
|
|
6
|
+
str = int.to_s
|
|
7
|
+
str = str.rjust(12, '0')
|
|
8
|
+
str
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def deserialize(string)
|
|
12
|
+
string.to_i
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Hash
|
|
20
|
+
# A method to recursively symbolize all keys in the Hash class
|
|
21
|
+
def recursively_symbolize_keys!
|
|
22
|
+
self.symbolize_keys!
|
|
23
|
+
self.values.each do |v|
|
|
24
|
+
if v.is_a? Hash
|
|
25
|
+
v.recursively_symbolize_keys!
|
|
26
|
+
elsif v.is_a? Array
|
|
27
|
+
#v.recursively_symbolize_keys!
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def symbolize_keys!
|
|
34
|
+
keys.each do |key|
|
|
35
|
+
self[(key.to_sym rescue key) || key] = delete(key)
|
|
36
|
+
end
|
|
37
|
+
self
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module ZZSharedLib
|
|
2
|
+
# tracks the global and per command options but lets you
|
|
3
|
+
# fetch values without regard to which one. The command
|
|
4
|
+
# is checked before the global
|
|
5
|
+
class Options
|
|
6
|
+
def self.global_options=(options)
|
|
7
|
+
@@global_options = options
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.cmd_options=(options)
|
|
11
|
+
@@cmd_options = options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.cmd_options
|
|
15
|
+
@@cmd_options ||= {}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.global_options
|
|
19
|
+
@@global_options ||= {}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.get(option)
|
|
23
|
+
v = cmd_options[option]
|
|
24
|
+
return v if !v.nil?
|
|
25
|
+
|
|
26
|
+
v = global_options[option]
|
|
27
|
+
return v
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module ZZSharedLib
|
|
2
|
+
class CL
|
|
3
|
+
# run a command line and echo to console
|
|
4
|
+
def self.do_cmd(cmd)
|
|
5
|
+
puts cmd
|
|
6
|
+
Kernel.system(cmd)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# same as above but returns the result code
|
|
10
|
+
# 0 is success, anything else is an error code
|
|
11
|
+
def self.do_cmd_result(cmd)
|
|
12
|
+
do_cmd(cmd)
|
|
13
|
+
$?.exitstatus
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class Utils
|
|
18
|
+
READY = "ready".freeze
|
|
19
|
+
ERROR = "error".freeze
|
|
20
|
+
START = "deploying".freeze
|
|
21
|
+
MAINT = "maint".freeze
|
|
22
|
+
NEVER = "never".freeze
|
|
23
|
+
RESTARTING = "restarting".freeze
|
|
24
|
+
OK_TO_DEPLOY_STATES = [NEVER, READY, ERROR].freeze
|
|
25
|
+
|
|
26
|
+
def initialize(amazon)
|
|
27
|
+
@amazon = amazon
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# mark the deploy state of all unless already marked
|
|
31
|
+
# as errors so we don't overwrite that state
|
|
32
|
+
def mark_deploy_state(instances, state_tag, state, keep_error_state = false)
|
|
33
|
+
to_tag = []
|
|
34
|
+
instances.each do |instance|
|
|
35
|
+
inst_id = instance[:resource_id]
|
|
36
|
+
if keep_error_state
|
|
37
|
+
tags = @amazon.flat_tags_for_resource(inst_id)
|
|
38
|
+
deploy_tag = tags[state_tag]
|
|
39
|
+
if !deploy_tag.nil? && deploy_tag != ERROR
|
|
40
|
+
to_tag << inst_id
|
|
41
|
+
end
|
|
42
|
+
else
|
|
43
|
+
to_tag << inst_id
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
@amazon.ec2.create_tags(to_tag, {state_tag => state })
|
|
47
|
+
@amazon.flush_tags
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def check_deploy_state(instances, state_tags)
|
|
51
|
+
instances.each do |instance|
|
|
52
|
+
inst_id = instance[:resource_id]
|
|
53
|
+
tags = @amazon.flat_tags_for_resource(inst_id)
|
|
54
|
+
state_tags.each do |state_tag|
|
|
55
|
+
deploy_tag = tags[state_tag]
|
|
56
|
+
if !deploy_tag.nil? && !OK_TO_DEPLOY_STATES.include?(deploy_tag)
|
|
57
|
+
raise "One or more instances is still marked as deploying for tag key: #{state_tag}, value: #{deploy_tag}, we will not deploy again. You can use --force to override"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: zzsharedlib
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 23
|
|
5
|
+
prerelease:
|
|
6
|
+
segments:
|
|
7
|
+
- 0
|
|
8
|
+
- 0
|
|
9
|
+
- 4
|
|
10
|
+
version: 0.0.4
|
|
11
|
+
platform: ruby
|
|
12
|
+
authors:
|
|
13
|
+
- Greg Seitz
|
|
14
|
+
autorequire:
|
|
15
|
+
bindir: bin
|
|
16
|
+
cert_chain: []
|
|
17
|
+
|
|
18
|
+
date: 2011-08-05 00:00:00 Z
|
|
19
|
+
dependencies:
|
|
20
|
+
- !ruby/object:Gem::Dependency
|
|
21
|
+
name: right_aws
|
|
22
|
+
prerelease: false
|
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
24
|
+
none: false
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
hash: 27
|
|
29
|
+
segments:
|
|
30
|
+
- 1
|
|
31
|
+
- 3
|
|
32
|
+
- 0
|
|
33
|
+
version: 1.3.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
version_requirements: *id001
|
|
36
|
+
- !ruby/object:Gem::Dependency
|
|
37
|
+
name: json
|
|
38
|
+
prerelease: false
|
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
40
|
+
none: false
|
|
41
|
+
requirements:
|
|
42
|
+
- - ">="
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
hash: 15
|
|
45
|
+
segments:
|
|
46
|
+
- 1
|
|
47
|
+
- 4
|
|
48
|
+
- 4
|
|
49
|
+
version: 1.4.4
|
|
50
|
+
- - <=
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
hash: 7
|
|
53
|
+
segments:
|
|
54
|
+
- 1
|
|
55
|
+
- 5
|
|
56
|
+
- 2
|
|
57
|
+
version: 1.5.2
|
|
58
|
+
type: :runtime
|
|
59
|
+
version_requirements: *id002
|
|
60
|
+
description: Useful utility library
|
|
61
|
+
email:
|
|
62
|
+
executables: []
|
|
63
|
+
|
|
64
|
+
extensions: []
|
|
65
|
+
|
|
66
|
+
extra_rdoc_files: []
|
|
67
|
+
|
|
68
|
+
files:
|
|
69
|
+
- lib/zzsharedlib/amazon.rb
|
|
70
|
+
- lib/zzsharedlib/deploy_group_simple_db.rb
|
|
71
|
+
- lib/zzsharedlib/monkey_patches.rb
|
|
72
|
+
- lib/zzsharedlib/options.rb
|
|
73
|
+
- lib/zzsharedlib/utilities.rb
|
|
74
|
+
- lib/zzsharedlib.rb
|
|
75
|
+
- Rakefile
|
|
76
|
+
- LICENSE
|
|
77
|
+
- README.rdoc
|
|
78
|
+
homepage:
|
|
79
|
+
licenses: []
|
|
80
|
+
|
|
81
|
+
post_install_message:
|
|
82
|
+
rdoc_options:
|
|
83
|
+
- --charset=UTF-8
|
|
84
|
+
require_paths:
|
|
85
|
+
- lib
|
|
86
|
+
- lib/zzsharedlib
|
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
88
|
+
none: false
|
|
89
|
+
requirements:
|
|
90
|
+
- - ">="
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
hash: 31
|
|
93
|
+
segments:
|
|
94
|
+
- 1
|
|
95
|
+
- 8
|
|
96
|
+
version: "1.8"
|
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
|
+
none: false
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
hash: 9
|
|
103
|
+
segments:
|
|
104
|
+
- 1
|
|
105
|
+
- 3
|
|
106
|
+
version: "1.3"
|
|
107
|
+
requirements: []
|
|
108
|
+
|
|
109
|
+
rubyforge_project:
|
|
110
|
+
rubygems_version: 1.8.6
|
|
111
|
+
signing_key:
|
|
112
|
+
specification_version: 3
|
|
113
|
+
summary: ZangZing Utility library
|
|
114
|
+
test_files: []
|
|
115
|
+
|