hosties 1.0.0
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 +7 -0
- data/lib/hosties.rb +14 -0
- data/lib/hosties/Definitions.rb +137 -0
- data/lib/hosties/Reification.rb +127 -0
- data/spec/definitions_spec.rb +40 -0
- data/spec/hosties_spec.rb +114 -0
- data/spec/reification_spec.rb +18 -0
- data/spec/spec_helper.rb +7 -0
- metadata +70 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d0ddfeda2e90489df21afb44cb70c5560fd24b12
|
4
|
+
data.tar.gz: 7307934a3183c4fcb74d1a4fe3e4c6401ecf7ba0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e8c90640a12836827bc7c0df71a7496389833b5fafcfb495b35355efc650215032ae6e39b8b10870c40b9e36c9f62ed45025347f1d8d051175fe50c04813b7d4
|
7
|
+
data.tar.gz: e17ba1f3cb0e4bfa34740edf9491124a97ef467cdd993dd4e478e257cfcf4d556dcdc2e34270962239b05a3114e604a6a8c45d59635bf4e532d55ed743360c1b
|
data/lib/hosties.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'hosties/definitions'
|
2
|
+
require 'hosties/reification'
|
3
|
+
|
4
|
+
# A library to provide easily readable environment definitions.
|
5
|
+
module Hosties
|
6
|
+
# Environment definitions, keyed by type
|
7
|
+
EnvironmentDefinitions = {}
|
8
|
+
# Host definitions, keyed by type
|
9
|
+
HostDefinitions = {}
|
10
|
+
# Environment instances, definition type => array of instances
|
11
|
+
Environments = Hash.new{|h,k| h[k] = []}
|
12
|
+
# Maps type => hash of specified 'grouped_by' value to array of matches
|
13
|
+
GroupedEnvironments = {}
|
14
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
#######################################################################
|
2
|
+
# Provide some classes to give us friendly, easy on the eyes syntax #
|
3
|
+
# for defining what different types of hosts and environments should #
|
4
|
+
# have in order to be valid. #
|
5
|
+
#######################################################################
|
6
|
+
|
7
|
+
# Constrains a named attribute to a provided set of values. This is good
|
8
|
+
# for things like describing environments that a set of hosts can be in,
|
9
|
+
# for instance Dev, QA, etc
|
10
|
+
class AttributeConstraint
|
11
|
+
attr_reader :name, :possible_vals
|
12
|
+
|
13
|
+
def initialize(name)
|
14
|
+
@name = name
|
15
|
+
@possible_vals = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def can_be(val, *more)
|
19
|
+
@possible_vals += (more << val)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Superclass for the host and environment requirement types. This
|
24
|
+
# class handles the plumbing of tracking, constraining, and eventually
|
25
|
+
# reifying attributes.
|
26
|
+
class HasAttributes
|
27
|
+
attr_accessor :constraints
|
28
|
+
attr_accessor :attributes
|
29
|
+
def initialize
|
30
|
+
@constraints = {}
|
31
|
+
@attributes = []
|
32
|
+
end
|
33
|
+
|
34
|
+
# Specify symbols that will later be reified into attributes
|
35
|
+
def have_attributes(attr, *more)
|
36
|
+
@attributes += (more << attr)
|
37
|
+
end
|
38
|
+
|
39
|
+
alias_method :have_attribute, :have_attributes
|
40
|
+
|
41
|
+
# Helpful method to define constraints
|
42
|
+
def where(name)
|
43
|
+
# Must define the attributes before constraining them
|
44
|
+
raise ArgumentError, "Unknown attribute: #{name}" unless @attributes.include? name
|
45
|
+
@constraints[name] = AttributeConstraint.new(name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Check if a given name-value pair is valid given the constraints
|
49
|
+
def valid?(name, value)
|
50
|
+
if @constraints.include? name then
|
51
|
+
constraints[name].possible_vals.include? value
|
52
|
+
else true end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Defines what a host of a certain type looks like
|
57
|
+
class HostRequirement < HasAttributes
|
58
|
+
attr_reader :type, :services
|
59
|
+
def initialize(type)
|
60
|
+
super()
|
61
|
+
@type = type
|
62
|
+
@services = []
|
63
|
+
end
|
64
|
+
|
65
|
+
# Services will be provided with a host definition. In order for
|
66
|
+
# a host definition to be valid, it must provide service details
|
67
|
+
# for all of the services specified by its matching
|
68
|
+
# HostRequirement
|
69
|
+
def have_services(service, *more)
|
70
|
+
@services += (more << service)
|
71
|
+
end
|
72
|
+
|
73
|
+
alias_method :have_service, :have_services
|
74
|
+
|
75
|
+
def finished
|
76
|
+
Hosties::HostDefinitions[@type] = self
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Builder method
|
81
|
+
def host_type(symbol, &block)
|
82
|
+
builder = HostRequirement.new(symbol)
|
83
|
+
begin
|
84
|
+
builder.instance_eval(&block)
|
85
|
+
builder.finished
|
86
|
+
rescue ArgumentError => ex
|
87
|
+
# TODO: There must be a better way!
|
88
|
+
# I'd like to provide some feedback in this case, but I don't
|
89
|
+
# like having this show up in test output.
|
90
|
+
#puts "Problem defining host \"#{symbol}\": #{ex}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Used to describe an environment.
|
95
|
+
class EnvironmentRequirement < HasAttributes
|
96
|
+
attr_reader :type, :hosts, :grouping
|
97
|
+
def initialize(type)
|
98
|
+
super()
|
99
|
+
@type = type
|
100
|
+
@hosts = []
|
101
|
+
end
|
102
|
+
|
103
|
+
# Define the hosts that an environment needs to be valid,
|
104
|
+
# for instance, maybe you need a :logger host and a
|
105
|
+
# :service host at a minimum.
|
106
|
+
def need(host1, *more)
|
107
|
+
sum = more << host1
|
108
|
+
# Array doesn't have an 'exists' method, so behold - map reduce wankery!
|
109
|
+
unless sum.map { |x| Hosties::HostDefinitions.include? x }.reduce(true) { |xs, x| x & xs }
|
110
|
+
raise ArgumentError, "Unrecognized host type"
|
111
|
+
end
|
112
|
+
@hosts += sum
|
113
|
+
end
|
114
|
+
|
115
|
+
# Optionally specify an attribute to group by when registering
|
116
|
+
# environments of this type.
|
117
|
+
def grouped_by(attr)
|
118
|
+
raise ArgumentError, "Unknown attribute #{attr}" unless @attributes.include?(attr)
|
119
|
+
@grouping = attr
|
120
|
+
end
|
121
|
+
|
122
|
+
def finished
|
123
|
+
Hosties::EnvironmentDefinitions[@type] = self
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Builder method
|
128
|
+
def environment_type(symbol, &block)
|
129
|
+
builder = EnvironmentRequirement.new(symbol)
|
130
|
+
begin
|
131
|
+
builder.instance_eval(&block)
|
132
|
+
builder.finished
|
133
|
+
rescue ArgumentError => ex
|
134
|
+
# TODO: Same as above, find a better way to get this information out
|
135
|
+
#puts "Problem describing environment \"#{builder.type}\": #{ex}"
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# Fancy words, fancy words.
|
2
|
+
# Provide some classes to turn a declarative host definition into something
|
3
|
+
# more useful in code, applying rules from the definition files to ensure we
|
4
|
+
# only get valid stuff.
|
5
|
+
class UsesAttributes
|
6
|
+
# Oh this old thing...
|
7
|
+
def metaclass
|
8
|
+
class << self
|
9
|
+
self
|
10
|
+
end
|
11
|
+
end
|
12
|
+
def initialize(has_attributes)
|
13
|
+
@attributes = has_attributes.attributes
|
14
|
+
# Magic.
|
15
|
+
has_attributes.attributes.each do |attr|
|
16
|
+
# Add in the attribute
|
17
|
+
self.metaclass.send(:attr_accessor, attr)
|
18
|
+
# Define a constrained setter
|
19
|
+
self.metaclass.send(:define_method, attr) do |val|
|
20
|
+
raise ArgumentError, "Invalid value" unless has_attributes.valid?(attr, val)
|
21
|
+
instance_variable_set "@#{attr}", val
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return a hash after verifying everything was set correctly
|
27
|
+
def finish
|
28
|
+
retval = {}
|
29
|
+
# Ensure all required attributes have been set
|
30
|
+
@attributes.each do |attr|
|
31
|
+
val = instance_variable_get "@#{attr}"
|
32
|
+
raise ArgumentError, "Missing attribute #{attr}" if val.nil?
|
33
|
+
retval[attr] = val
|
34
|
+
end
|
35
|
+
retval
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class HostBuilder < UsesAttributes
|
40
|
+
def initialize(type, hostname)
|
41
|
+
if Hosties::HostDefinitions[type].nil? then
|
42
|
+
raise ArgumentError, "Unrecognized host type"
|
43
|
+
end
|
44
|
+
@type = type
|
45
|
+
@definition = Hosties::HostDefinitions[@type]
|
46
|
+
@hostname = hostname
|
47
|
+
@service_ports = {} # Map of service type to port
|
48
|
+
super(@definition) # Creates attribute code
|
49
|
+
# Services are really just a special kind of attribute, but for now I'll
|
50
|
+
# keep them separate. I'm thinking maybe I could add a new type of attribute
|
51
|
+
# constraint that let's a user specify that an attribute must be numeric, or
|
52
|
+
# a string for instance.
|
53
|
+
@definition.services.each do |service_type|
|
54
|
+
self.metaclass.send(:attr_accessor, service_type)
|
55
|
+
self.metaclass.send(:define_method, service_type) do |port|
|
56
|
+
raise ArgumentError, "Port number required" unless port.is_a? Integer
|
57
|
+
@service_ports[service_type] = port
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def finish
|
63
|
+
# Ensure all services have been set
|
64
|
+
@definition.services.each do |svc|
|
65
|
+
raise ArgumentError, "Missing service #{svc}" if @service_ports[svc].nil?
|
66
|
+
end
|
67
|
+
# TODO: More clever data repackaging
|
68
|
+
super.merge({ :hostname => @hostname }).merge(@service_ports)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Turn a description into a useful data structure - and it's validated!
|
73
|
+
class EnvironmentBuilder < UsesAttributes
|
74
|
+
def initialize(type)
|
75
|
+
if Hosties::EnvironmentDefinitions[type].nil? then
|
76
|
+
raise ArgumentError, "Unrecognized environment type"
|
77
|
+
end
|
78
|
+
@hosts = {} # host type => array of hosts' data
|
79
|
+
@type = type
|
80
|
+
@definition = Hosties::EnvironmentDefinitions[@type]
|
81
|
+
super(@definition) # Creates attribute code
|
82
|
+
# More magic, this time create a parameterized host builder based
|
83
|
+
# on the type of hosts this environment wants. Poor man's currying
|
84
|
+
@definition.hosts.each do |host_type|
|
85
|
+
self.metaclass.send(:define_method, host_type) do |hostname, &block|
|
86
|
+
begin
|
87
|
+
builder = HostBuilder.new(host_type, hostname)
|
88
|
+
builder.instance_eval(&block)
|
89
|
+
if @hosts[host_type].nil? then @hosts[host_type] = [] end
|
90
|
+
@hosts[host_type] << builder.finish
|
91
|
+
rescue ArgumentError => ex
|
92
|
+
#puts "Problem declaring host: #{ex}"
|
93
|
+
raise ex
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def finish
|
100
|
+
# Verify all of the required hosts were set
|
101
|
+
@definition.hosts.each do |host_type|
|
102
|
+
raise ArgumentError, "Missing #{host_type} host" unless @hosts.include? host_type
|
103
|
+
end
|
104
|
+
retval = super.merge({ :hosts => @hosts })
|
105
|
+
Hosties::Environments[@type] << retval
|
106
|
+
# Add ourselves into the grouped listing if necessary
|
107
|
+
attr = @definition.grouping
|
108
|
+
unless attr.nil? then # TODO: This is really ugly
|
109
|
+
if Hosties::GroupedEnvironments[@type].nil? then
|
110
|
+
Hosties::GroupedEnvironments[@type] = Hash.new{|h,k| h[k] = []}
|
111
|
+
end
|
112
|
+
Hosties::GroupedEnvironments[@type][retval[attr]] << retval
|
113
|
+
end
|
114
|
+
retval
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def environment_for(type, &block)
|
119
|
+
begin
|
120
|
+
builder = EnvironmentBuilder.new(type)
|
121
|
+
builder.instance_eval(&block)
|
122
|
+
data = builder.finish
|
123
|
+
rescue ArgumentError => ex
|
124
|
+
puts "Problem declaring environment: #{ex}"
|
125
|
+
raise ex
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HasAttributes do
|
4
|
+
it 'rejects definitions with constraints on nonexistent attributes' do
|
5
|
+
instance = HasAttributes.new
|
6
|
+
instance.have_attributes :foo, :bar
|
7
|
+
expect { instance.where(:baz).can_be("anything") }.to raise_error(ArgumentError)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe HostRequirement do
|
12
|
+
it 'defines host types' do
|
13
|
+
# Declare a host type
|
14
|
+
host_type :logger do
|
15
|
+
have_services :jmx, :rest, :http, :https
|
16
|
+
have_attributes :control_mbean, :default_user
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe EnvironmentRequirement do
|
22
|
+
it 'defines environments with host and attribute requirements' do
|
23
|
+
host_type :mutant_maker do end
|
24
|
+
host_type :turkey_blaster do end
|
25
|
+
environment_type :weird_thanksgiving do
|
26
|
+
need :mutant_maker, :turkey_blaster
|
27
|
+
have_attribute :weirdness_factor
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'rejects environment definitions that need undefined host types' do
|
32
|
+
builder = EnvironmentRequirement.new(:failure)
|
33
|
+
expect { builder.need(:nonexistent) }.to raise_error(ArgumentError)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'rejects groupings for unknown attributes' do
|
37
|
+
builder = EnvironmentRequirement.new(:failure)
|
38
|
+
expect { builder.grouped_by(:nonexistent) }.to raise_error(ArgumentError)
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hosties do
|
4
|
+
it 'can declare a host' do
|
5
|
+
host_type :special_host do
|
6
|
+
end
|
7
|
+
instance = HostBuilder.new(:special_host, "0.0.0.0")
|
8
|
+
expect(instance.finish).to eq({ :hostname => "0.0.0.0"})
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'catches missing services' do
|
12
|
+
host_type :web_host do
|
13
|
+
have_service :http
|
14
|
+
end
|
15
|
+
instance = HostBuilder.new(:web_host, "0.0.0.0")
|
16
|
+
expect { instance.finish }.to raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'catches missing attributes' do
|
20
|
+
host_type :mud_server do
|
21
|
+
have_attribute :version
|
22
|
+
end
|
23
|
+
instance = HostBuilder.new(:mud_server, "0.0.0.0")
|
24
|
+
expect { instance.finish }.to raise_error(ArgumentError)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'catches non-integral service ports' do
|
28
|
+
host_type :web_host do
|
29
|
+
have_service :http
|
30
|
+
end
|
31
|
+
instance = HostBuilder.new(:web_host, "0.0.0.0")
|
32
|
+
expect { instance.http 10.4 }.to raise_error(ArgumentError)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'catches missing host requirements' do
|
36
|
+
host_type :type_a do
|
37
|
+
end
|
38
|
+
host_type :type_b do
|
39
|
+
end
|
40
|
+
environment_type :needy_environment do
|
41
|
+
need :type_a, :type_b
|
42
|
+
end
|
43
|
+
builder = EnvironmentBuilder.new(:needy_environment)
|
44
|
+
builder.type_a "0.0.0.0" do end
|
45
|
+
# No type_b specified
|
46
|
+
expect { builder.finish }.to raise_error(ArgumentError)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'can fully declare an environment' do
|
50
|
+
# Declare the host types
|
51
|
+
host_type :monitoring do
|
52
|
+
have_services :logging, :http
|
53
|
+
end
|
54
|
+
host_type :service_host do
|
55
|
+
have_services :service_port, :rest, :jmx
|
56
|
+
have_attribute :uuid
|
57
|
+
end
|
58
|
+
# Declare this product's environment makeup
|
59
|
+
environment_type :typical_product do
|
60
|
+
need :service_host, :monitoring
|
61
|
+
have_attribute :environment
|
62
|
+
where(:environment).can_be(:dev, :qa, :live)
|
63
|
+
end
|
64
|
+
# make one!
|
65
|
+
environment_for :typical_product do
|
66
|
+
environment :qa
|
67
|
+
monitoring "192.168.0.1" do
|
68
|
+
logging 8888
|
69
|
+
http 80
|
70
|
+
end
|
71
|
+
monitoring "192.168.0.2" do
|
72
|
+
logging 8888
|
73
|
+
http 80
|
74
|
+
end
|
75
|
+
service_host "192.168.0.3" do
|
76
|
+
service_port 1234
|
77
|
+
rest 8080
|
78
|
+
jmx 9876
|
79
|
+
uuid "81E3C1D4-C040-4D59-A56F-4273384D576B"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
expect(Hosties::Environments[:typical_product].nil?).to eq(false)
|
83
|
+
data = Hosties::Environments[:typical_product].first
|
84
|
+
expect(data[:environment]).to eq(:qa)
|
85
|
+
expect(data[:hosts][:monitoring].size).to eq(2) # Two monitoring hosts
|
86
|
+
expect(data[:hosts][:service_host].size).to eq(1)
|
87
|
+
service_host = data[:hosts][:service_host].first
|
88
|
+
expect(service_host[:service_port]).to eq(1234)
|
89
|
+
expect(service_host[:uuid]).to eq("81E3C1D4-C040-4D59-A56F-4273384D576B")
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'can group environments by attribute' do
|
93
|
+
host_type :foo do end
|
94
|
+
host_type :bar do end
|
95
|
+
environment_type :foobar do
|
96
|
+
need :foo, :bar
|
97
|
+
have_attribute :env_type
|
98
|
+
where(:env_type).can_be :dev, :qa, :live
|
99
|
+
grouped_by :env_type
|
100
|
+
end
|
101
|
+
environment_for :foobar do
|
102
|
+
foo "0.0.0.0" do end
|
103
|
+
bar "0.0.0.0" do end
|
104
|
+
env_type :dev
|
105
|
+
end
|
106
|
+
environment_for :foobar do
|
107
|
+
foo "0.0.0.0" do end
|
108
|
+
bar "0.0.0.0" do end
|
109
|
+
env_type :qa
|
110
|
+
end
|
111
|
+
expect(Hosties::GroupedEnvironments[:foobar][:dev].size).to eq(1)
|
112
|
+
expect(Hosties::GroupedEnvironments[:foobar][:qa].size).to eq(1)
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe UsesAttributes do
|
4
|
+
it 'can enforce attribute constraints' do
|
5
|
+
definition = HasAttributes.new
|
6
|
+
definition.have_attributes(:x)
|
7
|
+
definition.where(:x).can_be("hello")
|
8
|
+
instance = UsesAttributes.new(definition)
|
9
|
+
instance.x "hello"
|
10
|
+
expect { instance.x 31 }.to raise_error(ArgumentError)
|
11
|
+
end
|
12
|
+
it 'catches missing attributes' do
|
13
|
+
definition = HasAttributes.new
|
14
|
+
definition.have_attributes(:x)
|
15
|
+
instance = UsesAttributes.new(definition)
|
16
|
+
expect { instance.finish }.to raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hosties
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ron Dahlgren
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-05-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.5'
|
27
|
+
description: |2
|
28
|
+
Hosties provides an expressive way to define environments, which are in turn
|
29
|
+
comprised of lists of roles and the hosts that fill those roles.
|
30
|
+
email: ronald.dahlgren@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- lib/hosties/Definitions.rb
|
36
|
+
- lib/hosties/Reification.rb
|
37
|
+
- lib/hosties.rb
|
38
|
+
- spec/definitions_spec.rb
|
39
|
+
- spec/hosties_spec.rb
|
40
|
+
- spec/reification_spec.rb
|
41
|
+
- spec/spec_helper.rb
|
42
|
+
homepage: https://github.com/influenza/hosties
|
43
|
+
licenses:
|
44
|
+
- BSD-new
|
45
|
+
metadata: {}
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 2.0.3
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: Easy environment description
|
66
|
+
test_files:
|
67
|
+
- spec/definitions_spec.rb
|
68
|
+
- spec/hosties_spec.rb
|
69
|
+
- spec/reification_spec.rb
|
70
|
+
- spec/spec_helper.rb
|