hosties 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|