ecology 0.0.1 → 0.0.11
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/.gitignore +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +25 -0
- data/LICENSE +19 -0
- data/README.md +221 -0
- data/Rakefile +20 -0
- data/TODO +5 -0
- data/ecology.gemspec +36 -0
- data/ecology.gemspec~ +36 -0
- data/lib/ecology/version.rb +3 -0
- data/lib/ecology/version.rb~ +3 -0
- data/lib/ecology.rb +264 -0
- data/test/ecology_test.rb +41 -0
- data/test/environment_test.rb +78 -0
- data/test/environment_var_test.rb +34 -0
- data/test/override_properties_test.rb +77 -0
- data/test/path_test.rb +44 -0
- data/test/property_test.rb +76 -0
- data/test/test_helper.rb +18 -0
- data/test/trigger_test.rb +127 -0
- metadata +121 -26
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ecology (0.0.11)
|
5
|
+
multi_json
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
minitest (2.3.0)
|
11
|
+
mocha (0.9.12)
|
12
|
+
multi_json (1.0.3)
|
13
|
+
rake (0.9.2)
|
14
|
+
scope (0.2.1)
|
15
|
+
minitest
|
16
|
+
|
17
|
+
PLATFORMS
|
18
|
+
ruby
|
19
|
+
|
20
|
+
DEPENDENCIES
|
21
|
+
bundler (~> 1.0.10)
|
22
|
+
ecology!
|
23
|
+
mocha
|
24
|
+
rake
|
25
|
+
scope (~> 0.2.1)
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 Ooyala, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
Ecology
|
2
|
+
=======
|
3
|
+
|
4
|
+
Ecology is a gem to handle configuration variables. At Ooyala, we use
|
5
|
+
it for setting application metadata about logging, monitoring,
|
6
|
+
testing, deployment and other "outside the application"
|
7
|
+
infrastructure. So it's the application's ecology, right?
|
8
|
+
|
9
|
+
Installing
|
10
|
+
==========
|
11
|
+
|
12
|
+
"gem install ecology" works pretty well. You can also specify Ecology
|
13
|
+
from a Gemfile if you're using Bundler.
|
14
|
+
|
15
|
+
Ooyalans should make sure that "gems.sv2" is listed as a gem source in
|
16
|
+
your Gemfile or on your gem command line.
|
17
|
+
|
18
|
+
Finding Your Ecology
|
19
|
+
====================
|
20
|
+
|
21
|
+
By default an application called "bob.sh" will have an ecology file in
|
22
|
+
the same directory called "bob.ecology". Ecology just strips off the
|
23
|
+
final file extension, replaces it with ".ecology", and looks there.
|
24
|
+
|
25
|
+
You can also specify a different location in your Ecology.read call,
|
26
|
+
or set the ECOLOGY_SPEC environment variable to a different location.
|
27
|
+
|
28
|
+
An Ecology is a JSON file of roughly this structure:
|
29
|
+
|
30
|
+
{
|
31
|
+
"application": "MyApp",
|
32
|
+
"environment-from": "RACK_ENV",
|
33
|
+
"logging": {
|
34
|
+
"default_component": "SplodgingLib",
|
35
|
+
"extra_json_fields": {
|
36
|
+
"app_group": "SuperSpiffyGroup",
|
37
|
+
"precedence": 7
|
38
|
+
},
|
39
|
+
"console_print": "off",
|
40
|
+
"filename": "/tmp/bobo.txt",
|
41
|
+
"stderr_level": "fatal"
|
42
|
+
},
|
43
|
+
"monitoring": {
|
44
|
+
"zookeeper-host": "zookeeper-dev.sv2"
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
Absolutely every part of it is optional, including the presence of the file at all.
|
49
|
+
|
50
|
+
You can override the application name, as shown above.
|
51
|
+
|
52
|
+
Paths
|
53
|
+
=====
|
54
|
+
|
55
|
+
If you have a configurable per-environment path, you probably want it in the "paths"
|
56
|
+
section of your ecology. For instance:
|
57
|
+
|
58
|
+
{
|
59
|
+
"application": "SomeApp",
|
60
|
+
"paths": {
|
61
|
+
"pid_location": "/pid_dir/",
|
62
|
+
"app1_location": "$app/../dir1",
|
63
|
+
"app1_log_path": "$cwd/logs"
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
You can then access these paths with Ecology.path("app1_location") and
|
68
|
+
similar. In the paths, "$app" will be replaced by the directory the
|
69
|
+
application is run from, "$cwd" will be replaced by the current
|
70
|
+
working directory, "$env" will be replaced by the current environment,
|
71
|
+
and "$pid" will be replaced by the current process ID.
|
72
|
+
|
73
|
+
Reading Data
|
74
|
+
============
|
75
|
+
|
76
|
+
If your library is configured via Ecology, you'll likely want to read data
|
77
|
+
from it. For instance, let's look at the Termite logging library's method
|
78
|
+
of configuration:
|
79
|
+
|
80
|
+
{
|
81
|
+
"application": "SomeApp",
|
82
|
+
"logging": {
|
83
|
+
"level": "info",
|
84
|
+
"stderr_level": "warn",
|
85
|
+
"stdout_level": 4,
|
86
|
+
"file_path": "$app/../log_to",
|
87
|
+
"extra_json_fields": {
|
88
|
+
"app_tag": "splodging_apps",
|
89
|
+
"precedence": 9
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
Termite can read the level via Ecology.property("logging:level"), which will
|
95
|
+
give it in whatever form it appears in the JSON.
|
96
|
+
|
97
|
+
Ecology.property("logging:extra_json_fields") would be returned as a Hash.
|
98
|
+
You can return it as a String, Symbol, Array, Fixnum or Hash by supplying
|
99
|
+
the :as option:
|
100
|
+
|
101
|
+
Ecology.property("logging:info", :as => Symbol) # :info
|
102
|
+
Ecology.property("logging:stdout_level", :as => String) # "4"
|
103
|
+
Ecology.property("logging:extra_json_fields", :as => Symbol) # error!
|
104
|
+
Ecology.property("logging:file_path", :as => :path) # "/home/theuser/sub/log_to"
|
105
|
+
|
106
|
+
Environment-Specific Data
|
107
|
+
=========================
|
108
|
+
|
109
|
+
Often you'll want to supply a different path, hostname or other
|
110
|
+
configuration variable depending on what environment you're
|
111
|
+
currently deployed to - staging may want a different MemCacheD
|
112
|
+
server than development, say.
|
113
|
+
|
114
|
+
Here's another logging example:
|
115
|
+
|
116
|
+
{
|
117
|
+
"application": "Ooyala Rails",
|
118
|
+
"environment-from": ["RAILS_ENV", "RACK_ENV"],
|
119
|
+
"logging": {
|
120
|
+
"console_out": {
|
121
|
+
"env:development": true,
|
122
|
+
"env:*": false
|
123
|
+
},
|
124
|
+
"stderr_level": {
|
125
|
+
"env:development": "fatal",
|
126
|
+
"env:production": "warn"
|
127
|
+
},
|
128
|
+
"stdout_level": "info"
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
In this case, data can be converted from a Hash into a Fixnum
|
133
|
+
or String automatically:
|
134
|
+
|
135
|
+
Ecology.property("logging:stderr_level", :as => String)
|
136
|
+
|
137
|
+
Ecology returns "fatal" or "warn" here, depending on the value
|
138
|
+
of RAILS_ENV or RACK_ENV.
|
139
|
+
|
140
|
+
Using Other Ecologies
|
141
|
+
=====================
|
142
|
+
|
143
|
+
The data in a given Ecology file can build on one or more
|
144
|
+
other Ecology files.
|
145
|
+
|
146
|
+
{
|
147
|
+
"application": "SomeApp",
|
148
|
+
"environment-from": [ "APP_ENV", "RACK_ENV" ],
|
149
|
+
"uses": [ "ecologies/logging.ecology", "ecologies/monitoring.ecology" ]
|
150
|
+
}
|
151
|
+
|
152
|
+
Each field will be overridden by the "latest" value -- the top-level
|
153
|
+
Ecology overrides the Ecologies that it uses, and so on. If multiple
|
154
|
+
Ecologies are used, the earlier Ecologies in the list override the
|
155
|
+
later Ecologies.
|
156
|
+
|
157
|
+
This can be used to set up Ecology "modules" for common functionality,
|
158
|
+
or to override certain settings in certain environments from a common
|
159
|
+
base template.
|
160
|
+
|
161
|
+
Events
|
162
|
+
======
|
163
|
+
|
164
|
+
You often want to set your ecology-related properties when the ecology
|
165
|
+
is initialized, but no earlier. You may not know exactly when the
|
166
|
+
earliest call to Ecology.read will be. In that case, you want to use
|
167
|
+
the on_initialize event hook:
|
168
|
+
|
169
|
+
Ecology.on_initialize do
|
170
|
+
@my_property = Ecology.property("my:property")
|
171
|
+
end
|
172
|
+
|
173
|
+
If the ecology was already initialized before you set the
|
174
|
+
on_initialize hook, then the hook will run immediately.
|
175
|
+
|
176
|
+
There is also an on_reset hook. Read "Testing with an Ecology" to
|
177
|
+
find out why you'd ever care about that.
|
178
|
+
|
179
|
+
Testing with an Ecology
|
180
|
+
=======================
|
181
|
+
|
182
|
+
In production use, you'll probably never reset the ecology. However,
|
183
|
+
in testing you may frequently want to, especially if you're testing a
|
184
|
+
library that ties closely into the ecology.
|
185
|
+
|
186
|
+
There are two basic approaches your library can take, and they affect
|
187
|
+
testing.
|
188
|
+
|
189
|
+
Termite, our logging library, copies settings from the ecology into
|
190
|
+
its instance. Then, when you reset the ecology, you can also discard
|
191
|
+
old logger objects with old settings.
|
192
|
+
|
193
|
+
Glowworm, our feature flags library, is basically a big singleton and
|
194
|
+
uses ecology data, so it needs to reset its internal state when the
|
195
|
+
ecology is reset, and then re-read that state when the ecology is next
|
196
|
+
initialized.
|
197
|
+
|
198
|
+
Code for that for your library might look something like:
|
199
|
+
|
200
|
+
MyLib.on_reset do
|
201
|
+
@myvar1 = nil
|
202
|
+
@myvar2 = nil
|
203
|
+
end
|
204
|
+
|
205
|
+
MyLib.on_initialize do
|
206
|
+
@myvar1 = Ecology.property("mylib:property1", :as => :string)
|
207
|
+
@myvar2 = Ecology.property("mylib:property2", :as => :path)
|
208
|
+
end
|
209
|
+
|
210
|
+
Hooks persist across resets. That is, your on_reset hook will be
|
211
|
+
called on every reset until you explicitly remove it.
|
212
|
+
|
213
|
+
Releasing within Ooyala
|
214
|
+
=======================
|
215
|
+
|
216
|
+
Ooyalans, to release Ecology to gems.sv2, use the following:
|
217
|
+
|
218
|
+
gem build
|
219
|
+
rake _0.8.7_ -f ../ooyala_gems.rake gem:push ecology-0.0.1.gem
|
220
|
+
|
221
|
+
Change the version to the actual version you'd like to push.
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "bundler"
|
2
|
+
require "rake/testtask"
|
3
|
+
|
4
|
+
require File.join(File.dirname(__FILE__), "lib", "ecology", "version")
|
5
|
+
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.test_files = Dir.glob("test/**/*test.rb")
|
9
|
+
t.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Builds the gem'
|
13
|
+
task :build do
|
14
|
+
sh "gem build ecology.gemspec"
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Builds and installs the gem'
|
18
|
+
task :install => :build do
|
19
|
+
sh "gem install ecology-#{Ecology::VERSION}"
|
20
|
+
end
|
data/TODO
ADDED
data/ecology.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require "ecology/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "ecology"
|
8
|
+
s.version = Ecology::VERSION
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = ["Noah Gibbs"]
|
11
|
+
s.email = ["noah@ooyala.com"]
|
12
|
+
s.homepage = "http://www.ooyala.com"
|
13
|
+
s.summary = %q{Ruby config variable management}
|
14
|
+
s.description = <<EOS
|
15
|
+
Ecology sets configuration data for an application based
|
16
|
+
on environment variables and other factors. It is meant
|
17
|
+
to unify configuration data for logging, testing, monitoring
|
18
|
+
and deployment.
|
19
|
+
EOS
|
20
|
+
|
21
|
+
s.rubyforge_project = "ecology"
|
22
|
+
|
23
|
+
ignores = File.readlines(".gitignore").grep(/\S+/).map {|pattern| pattern.chomp }
|
24
|
+
dotfiles = Dir[".*"]
|
25
|
+
s.files = Dir["**/*"].reject {|f| File.directory?(f) || ignores.any? {|i| File.fnmatch(i, f) } } + dotfiles
|
26
|
+
s.test_files = s.files.grep(/^test\//)
|
27
|
+
|
28
|
+
s.require_paths = ["lib"]
|
29
|
+
|
30
|
+
s.add_dependency "multi_json"
|
31
|
+
|
32
|
+
s.add_development_dependency "bundler", "~> 1.0.10"
|
33
|
+
s.add_development_dependency "scope", "~> 0.2.1"
|
34
|
+
s.add_development_dependency "mocha"
|
35
|
+
s.add_development_dependency "rake"
|
36
|
+
end
|
data/ecology.gemspec~
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require "ecology/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "ecology"
|
8
|
+
s.version = Ecology::VERSION
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = ["Noah Gibbs"]
|
11
|
+
s.email = ["noah@ooyala.com"]
|
12
|
+
s.homepage = "http://www.ooyala.com"
|
13
|
+
s.summary = %q{Ruby config variable management}
|
14
|
+
s.description = <<EOS
|
15
|
+
Ecology sets configuration data for an application based
|
16
|
+
on environment variables and other factors. It is meant
|
17
|
+
to unify configuration data for logging, testing, monitoring
|
18
|
+
and deployment.
|
19
|
+
EOS
|
20
|
+
|
21
|
+
s.rubyforge_project = "ecology"
|
22
|
+
|
23
|
+
ignores = File.readlines(".gitignore").grep(/\S+/).map {|pattern| pattern.chomp }
|
24
|
+
dotfiles = [".gemtest", ".gitignore", ".rspec", ".yardopts"]
|
25
|
+
s.files = Dir["**/*"].reject {|f| File.directory?(f) || ignores.any? {|i| File.fnmatch(i, f) } } + dotfiles
|
26
|
+
s.test_files = s.files.grep(/^spec\//)
|
27
|
+
|
28
|
+
s.require_paths = ["lib"]
|
29
|
+
|
30
|
+
s.add_dependency "multi_json"
|
31
|
+
|
32
|
+
s.add_development_dependency "bundler", "~> 1.0.10"
|
33
|
+
s.add_development_dependency "scope", "~> 0.2.1"
|
34
|
+
s.add_development_dependency "mocha"
|
35
|
+
s.add_development_dependency "rake"
|
36
|
+
end
|
data/lib/ecology.rb
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
require "multi_json"
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
module Ecology
|
5
|
+
class << self
|
6
|
+
attr_reader :application
|
7
|
+
attr_reader :data
|
8
|
+
attr_reader :environment
|
9
|
+
attr_accessor :mutex
|
10
|
+
end
|
11
|
+
|
12
|
+
ECOLOGY_EXTENSION = ".ecology"
|
13
|
+
|
14
|
+
Ecology.mutex = Mutex.new
|
15
|
+
|
16
|
+
class << self
|
17
|
+
# Normally this is only for testing.
|
18
|
+
def reset
|
19
|
+
# Preserve triggers across resets by default
|
20
|
+
@triggers ||= {}
|
21
|
+
|
22
|
+
@application = nil
|
23
|
+
@environment = nil
|
24
|
+
@data = nil
|
25
|
+
@ecology_initialized = nil
|
26
|
+
|
27
|
+
publish_event :reset
|
28
|
+
end
|
29
|
+
|
30
|
+
def clear_triggers
|
31
|
+
@triggers = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def read(ecology_pathname = nil)
|
35
|
+
return if @ecology_initialized
|
36
|
+
|
37
|
+
should_publish_event = false
|
38
|
+
|
39
|
+
mutex.synchronize do
|
40
|
+
return if @ecology_initialized
|
41
|
+
|
42
|
+
file_path = ENV['ECOLOGY_SPEC'] || ecology_pathname || default_ecology_name
|
43
|
+
if File.exist?(file_path)
|
44
|
+
@data = {}
|
45
|
+
contents = merge_with_overrides(file_path)
|
46
|
+
end
|
47
|
+
|
48
|
+
@application ||= File.basename($0)
|
49
|
+
@environment ||= ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development"
|
50
|
+
|
51
|
+
should_publish_event = true
|
52
|
+
|
53
|
+
@ecology_initialized = true
|
54
|
+
end
|
55
|
+
|
56
|
+
# Do this outside the mutex to reduce the likelihood
|
57
|
+
# of deadlocks.
|
58
|
+
publish_event(:initialize) if should_publish_event
|
59
|
+
end
|
60
|
+
|
61
|
+
def on_initialize(token = nil, &block)
|
62
|
+
on_event(:initialize, token, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def on_reset(token = nil, &block)
|
66
|
+
on_event(:reset, token, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
def remove_trigger(token)
|
70
|
+
@triggers ||= {}
|
71
|
+
@triggers.each do |event, trigger_list|
|
72
|
+
@triggers[event].delete(token)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def on_event(event, token = nil, &block)
|
79
|
+
mutex.synchronize do
|
80
|
+
@token_offset ||= 0
|
81
|
+
token ||= "token#{@token_offset}"
|
82
|
+
|
83
|
+
@triggers ||= {}
|
84
|
+
@triggers[event] ||= {}
|
85
|
+
@triggers[event][token] = block
|
86
|
+
|
87
|
+
if event == :initialize && @ecology_initialized
|
88
|
+
block.call
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def publish_event(event)
|
94
|
+
@triggers ||= {}
|
95
|
+
|
96
|
+
# This doesn't lock the mutex, because there's too high
|
97
|
+
# a chance of somebody calling Ecology.read or on_event
|
98
|
+
# or something while we're doing this. That would
|
99
|
+
# deadlock, which is no good.
|
100
|
+
(@triggers[event] || {}).each do |token, event_block|
|
101
|
+
event_block.call
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def merge_with_overrides(file_path)
|
106
|
+
contents = File.read(file_path)
|
107
|
+
file_data = MultiJson.decode(contents);
|
108
|
+
|
109
|
+
return unless file_data
|
110
|
+
|
111
|
+
# First, try to set @application and @environment from the file data
|
112
|
+
|
113
|
+
@application ||= file_data["application"]
|
114
|
+
@environment ||= file_data["environment"]
|
115
|
+
|
116
|
+
if !@environment && file_data["environment-from"]
|
117
|
+
from = file_data["environment-from"]
|
118
|
+
if from.respond_to?(:map)
|
119
|
+
@environment ||= from.map {|v| ENV[v]}.compact.first
|
120
|
+
else
|
121
|
+
@environment = ENV[from] ? ENV[from].to_s : nil
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Next, filter the data by the current environment
|
126
|
+
file_data = environmentize_data(file_data)
|
127
|
+
|
128
|
+
# Merge the file data into @data
|
129
|
+
@data = deep_merge(@data, file_data)
|
130
|
+
|
131
|
+
# Finally, process any inheritance/overrides
|
132
|
+
if file_data["uses"]
|
133
|
+
if file_data["uses"].respond_to?(:map)
|
134
|
+
file_data["uses"].map { |file| merge_with_overrides(file) }
|
135
|
+
else
|
136
|
+
merge_with_overrides(file_data["uses"])
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def deep_merge(hash1, hash2)
|
142
|
+
all_keys = hash1.keys | hash2.keys
|
143
|
+
ret = {}
|
144
|
+
|
145
|
+
all_keys.each do |key|
|
146
|
+
if hash1.has_key?(key) && hash2.has_key?(key)
|
147
|
+
if hash1[key].is_a?(Hash) && hash2[key].is_a?(Hash)
|
148
|
+
ret[key] = deep_merge(hash1[key], hash2[key])
|
149
|
+
else
|
150
|
+
ret[key] = hash1[key]
|
151
|
+
end
|
152
|
+
elsif hash1.has_key?(key)
|
153
|
+
ret[key] = hash1[key]
|
154
|
+
else
|
155
|
+
ret[key] = hash2[key]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
ret
|
160
|
+
end
|
161
|
+
|
162
|
+
def environmentize_data(data_in)
|
163
|
+
if data_in.is_a?(Array)
|
164
|
+
data_in.map { |subdata| environmentize_data(subdata) }
|
165
|
+
elsif data_in.is_a?(Hash)
|
166
|
+
if data_in.keys.any? { |k| k =~ /^env:/ }
|
167
|
+
value = data_in["env:#{@environment}"] || data_in["env:*"]
|
168
|
+
return nil unless value
|
169
|
+
environmentize_data(value)
|
170
|
+
else
|
171
|
+
data_out = {}
|
172
|
+
data_in.each { |k, v| data_out[k] = environmentize_data(v) }
|
173
|
+
data_out
|
174
|
+
end
|
175
|
+
else
|
176
|
+
data_in
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
public
|
181
|
+
|
182
|
+
def property(param, options = {})
|
183
|
+
components = param.split(":").compact.select {|s| s != ""}
|
184
|
+
|
185
|
+
value = components.inject(@data) do |data, component|
|
186
|
+
if data
|
187
|
+
data[component]
|
188
|
+
else
|
189
|
+
nil
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
return nil unless value
|
194
|
+
return value unless options[:as]
|
195
|
+
|
196
|
+
unless value.is_a?(Hash)
|
197
|
+
if [String, :string].include?(options[:as])
|
198
|
+
return value.to_s
|
199
|
+
elsif [Symbol, :symbol].include?(options[:as])
|
200
|
+
return value.to_s.to_sym
|
201
|
+
elsif [Fixnum, :int, :integer, :fixnum].include?(options[:as])
|
202
|
+
return value.to_i
|
203
|
+
elsif [Hash, :hash].include?(options[:as])
|
204
|
+
raise "Cannot convert scalar value to Hash!"
|
205
|
+
elsif [:path].include?(options[:as])
|
206
|
+
return string_to_path(value.to_s)
|
207
|
+
elsif [:json].include?(options[:as])
|
208
|
+
raise "JSON return type not yet supported!"
|
209
|
+
else
|
210
|
+
raise "Unknown type #{options[:as].inspect} passed to Ecology.data(:as) for property #{property}!"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
return value if options[:as] == Hash
|
215
|
+
raise "Couldn't convert JSON fields to #{options[:as].inspect} for property #{property}!"
|
216
|
+
end
|
217
|
+
|
218
|
+
PATH_SUBSTITUTIONS = {
|
219
|
+
"$env" => proc { Ecology.environment },
|
220
|
+
"$cwd" => proc { Dir.getwd },
|
221
|
+
"$app" => proc { File.dirname($0) },
|
222
|
+
"$pid" => proc { Process.pid.to_s },
|
223
|
+
}
|
224
|
+
|
225
|
+
def path(path_name)
|
226
|
+
path_data = @data ? @data["paths"] : nil
|
227
|
+
return nil unless path_data && path_data[path_name]
|
228
|
+
|
229
|
+
string_to_path path_data[path_name]
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
def string_to_path(path)
|
235
|
+
PATH_SUBSTITUTIONS.each do |key, value|
|
236
|
+
path.gsub! key, value.call
|
237
|
+
end
|
238
|
+
|
239
|
+
path
|
240
|
+
end
|
241
|
+
|
242
|
+
public
|
243
|
+
|
244
|
+
def default_ecology_name(executable = $0)
|
245
|
+
suffix = File.extname(executable)
|
246
|
+
executable[0..(executable.length - 1 - suffix.size)] +
|
247
|
+
ECOLOGY_EXTENSION
|
248
|
+
end
|
249
|
+
|
250
|
+
# This is a convenience function because the Ruby
|
251
|
+
# thread API has no accessor for the thread ID,
|
252
|
+
# but includes it in "to_s" (buh?)
|
253
|
+
def thread_id(thread)
|
254
|
+
return "main" if thread == Thread.main
|
255
|
+
|
256
|
+
str = thread.to_s
|
257
|
+
|
258
|
+
match = nil
|
259
|
+
match = str.match /(0x\d+)/
|
260
|
+
return nil unless match
|
261
|
+
match[1]
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
class EcologyTest < Scope::TestCase
|
5
|
+
context "with ecology" do
|
6
|
+
setup do
|
7
|
+
Ecology.reset
|
8
|
+
end
|
9
|
+
|
10
|
+
should "correctly determine default ecology names" do
|
11
|
+
assert_equal "/path/to/bob.txt.ecology", Ecology.default_ecology_name("/path/to/bob.txt.rb")
|
12
|
+
assert_equal "relative/path/to/app.ecology", Ecology.default_ecology_name("relative/path/to/app.rb")
|
13
|
+
assert_equal "/path/to/bob.ecology", Ecology.default_ecology_name("/path/to/bob.sh")
|
14
|
+
assert_equal "\\path\\to\\bob.ecology", Ecology.default_ecology_name("\\path\\to\\bob.EXE")
|
15
|
+
end
|
16
|
+
|
17
|
+
should "respect the ECOLOGY_SPEC environment variable" do
|
18
|
+
ENV['ECOLOGY_SPEC'] = '/tmp/bobo.txt'
|
19
|
+
File.expects(:exist?).with('/tmp/bobo.txt').returns(true)
|
20
|
+
File.expects(:read).with('/tmp/bobo.txt').returns('{ "application": "foo_app" }')
|
21
|
+
Ecology.read
|
22
|
+
|
23
|
+
assert_equal "foo_app", Ecology.application
|
24
|
+
end
|
25
|
+
|
26
|
+
should "recognize that this is the main thread" do
|
27
|
+
assert_equal "main", Ecology.thread_id(Thread.current)
|
28
|
+
end
|
29
|
+
|
30
|
+
should "work without an ECOLOGY_SPEC" do
|
31
|
+
$0 = "whatever_app.rb"
|
32
|
+
|
33
|
+
ENV['ECOLOGY_SPEC'] = nil
|
34
|
+
File.expects(:exist?).with("whatever_app.ecology").returns(false)
|
35
|
+
|
36
|
+
Ecology.read
|
37
|
+
|
38
|
+
assert_equal "whatever_app.rb", Ecology.application
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
|
3
|
+
class EnvironmentTest < Scope::TestCase
|
4
|
+
setup do
|
5
|
+
Ecology.reset
|
6
|
+
end
|
7
|
+
|
8
|
+
context "with environment-from in your ecology" do
|
9
|
+
setup do
|
10
|
+
set_up_ecology <<ECOLOGY_CONTENTS
|
11
|
+
{
|
12
|
+
"application": "SomeApp",
|
13
|
+
"environment-from": ["SOME_ENV_VAR", "VAR2"]
|
14
|
+
}
|
15
|
+
ECOLOGY_CONTENTS
|
16
|
+
|
17
|
+
ENV["SOME_ENV_VAR"] = ENV["VAR2"] = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
should "default to the development environment" do
|
21
|
+
Ecology.read
|
22
|
+
assert_equal "development", Ecology.environment
|
23
|
+
end
|
24
|
+
|
25
|
+
should "use the environment variables to determine environment" do
|
26
|
+
ENV["SOME_ENV_VAR"] = "staging"
|
27
|
+
Ecology.read
|
28
|
+
assert_equal "staging", Ecology.environment
|
29
|
+
end
|
30
|
+
|
31
|
+
should "use secondary environment variables when the primary isn't set" do
|
32
|
+
ENV["VAR2"] = "daily-staging"
|
33
|
+
Ecology.read
|
34
|
+
assert_equal "daily-staging", Ecology.environment
|
35
|
+
end
|
36
|
+
|
37
|
+
should "use primary environment variables in preference to secondary" do
|
38
|
+
ENV["SOME_ENV_VAR"] = "theatrical staging"
|
39
|
+
ENV["VAR2"] = "daily-staging"
|
40
|
+
Ecology.read
|
41
|
+
assert_equal "theatrical staging", Ecology.environment
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "with an environment override in your ecology" do
|
46
|
+
setup do
|
47
|
+
set_up_ecology <<ECOLOGY_CONTENTS
|
48
|
+
{
|
49
|
+
"application": "SomeApp",
|
50
|
+
"environment": "not really staging",
|
51
|
+
"environment-from": "SOME_ENV_VAR"
|
52
|
+
}
|
53
|
+
ECOLOGY_CONTENTS
|
54
|
+
end
|
55
|
+
|
56
|
+
should "use the environment override" do
|
57
|
+
Ecology.read
|
58
|
+
assert_equal "not really staging", Ecology.environment
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "with a single environment-from in your ecology" do
|
63
|
+
setup do
|
64
|
+
set_up_ecology <<ECOLOGY_CONTENTS
|
65
|
+
{
|
66
|
+
"application": "SomeApp",
|
67
|
+
"environment-from": "SOME_ENV_VAR"
|
68
|
+
}
|
69
|
+
ECOLOGY_CONTENTS
|
70
|
+
end
|
71
|
+
|
72
|
+
should "use the environment override" do
|
73
|
+
ENV['SOME_ENV_VAR'] = "bob's pants"
|
74
|
+
Ecology.read
|
75
|
+
assert_equal "bob's pants", Ecology.environment
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
|
3
|
+
class EnvironmentVarTest < Scope::TestCase
|
4
|
+
setup do
|
5
|
+
Ecology.reset
|
6
|
+
end
|
7
|
+
|
8
|
+
context "with environments in your ecology" do
|
9
|
+
setup do
|
10
|
+
set_up_ecology <<ECOLOGY_CONTENTS
|
11
|
+
{
|
12
|
+
"application": "SomeApp",
|
13
|
+
"environment-from": ["RACK_ENV"],
|
14
|
+
"domain": {
|
15
|
+
"property1" : {
|
16
|
+
"env:staging": "value1",
|
17
|
+
"env:development": "value2",
|
18
|
+
"env:*": "value3"
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
ECOLOGY_CONTENTS
|
23
|
+
|
24
|
+
ENV["RACK_ENV"] = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
should "select the right environment value for a property" do
|
28
|
+
ENV["RACK_ENV"] = "staging"
|
29
|
+
Ecology.read
|
30
|
+
assert_equal "value1", Ecology.property("domain::property1", :as => String)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
|
3
|
+
class OverridePropertiesTest < Scope::TestCase
|
4
|
+
setup do
|
5
|
+
Ecology.reset
|
6
|
+
end
|
7
|
+
|
8
|
+
context "with environment-from in your ecology" do
|
9
|
+
setup do
|
10
|
+
set_up_ecology <<ECOLOGY_GRANDPARENT1_CONTENTS, "grandparent1.ecology"
|
11
|
+
{
|
12
|
+
"testing": {
|
13
|
+
"capabilities": [ "rails", "rvm" ],
|
14
|
+
"othertag": 9
|
15
|
+
},
|
16
|
+
"monitoring": {
|
17
|
+
"property9": 71
|
18
|
+
}
|
19
|
+
}
|
20
|
+
ECOLOGY_GRANDPARENT1_CONTENTS
|
21
|
+
|
22
|
+
set_up_ecology <<ECOLOGY_GRANDPARENT2_CONTENTS, "grandparent2.ecology"
|
23
|
+
{
|
24
|
+
"monitoring": {
|
25
|
+
"property3": 7,
|
26
|
+
"property9": 134
|
27
|
+
}
|
28
|
+
}
|
29
|
+
ECOLOGY_GRANDPARENT2_CONTENTS
|
30
|
+
|
31
|
+
set_up_ecology <<ECOLOGY_PARENT_CONTENTS, "parent.ecology"
|
32
|
+
{
|
33
|
+
"uses": ["grandparent1.ecology", "grandparent2.ecology"],
|
34
|
+
"logging": {
|
35
|
+
"property1": "foo"
|
36
|
+
},
|
37
|
+
"monitoring": {
|
38
|
+
"property1": "burgers",
|
39
|
+
"property2": "bar",
|
40
|
+
"property3": "quux"
|
41
|
+
}
|
42
|
+
}
|
43
|
+
ECOLOGY_PARENT_CONTENTS
|
44
|
+
|
45
|
+
set_up_ecology <<ECOLOGY_CONTENTS
|
46
|
+
{
|
47
|
+
"application": "SomeApp",
|
48
|
+
"uses": "parent.ecology",
|
49
|
+
"monitoring": {
|
50
|
+
"property2": "baz"
|
51
|
+
}
|
52
|
+
}
|
53
|
+
ECOLOGY_CONTENTS
|
54
|
+
Ecology.read
|
55
|
+
end
|
56
|
+
|
57
|
+
should "get overridden properties correctly" do
|
58
|
+
assert_equal "baz", Ecology.property("monitoring::property2")
|
59
|
+
end
|
60
|
+
|
61
|
+
should "get inherited properties correctly" do
|
62
|
+
assert_equal "foo", Ecology.property("logging::property1")
|
63
|
+
end
|
64
|
+
|
65
|
+
should "get properties in an overridden hash" do
|
66
|
+
assert_equal "burgers", Ecology.property("monitoring::property1")
|
67
|
+
end
|
68
|
+
|
69
|
+
should "get grandparent properties" do
|
70
|
+
assert_equal 9, Ecology.property("testing::othertag")
|
71
|
+
end
|
72
|
+
|
73
|
+
should "have first parent override second parent properties" do
|
74
|
+
assert_equal 71, Ecology.property("monitoring::property9")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/test/path_test.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
|
3
|
+
class PathTest < Scope::TestCase
|
4
|
+
setup do
|
5
|
+
Ecology.reset
|
6
|
+
end
|
7
|
+
|
8
|
+
context "with paths in your ecology" do
|
9
|
+
setup do
|
10
|
+
set_up_ecology <<ECOLOGY_CONTENTS
|
11
|
+
{
|
12
|
+
"application": "SomeApp",
|
13
|
+
"paths": {
|
14
|
+
"pid_location": "/pid_dir/",
|
15
|
+
"whozit_location": "$app/../dir1",
|
16
|
+
"whatsit_path": "$cwd/logs",
|
17
|
+
"some_other_location": "dir/to/there.$pid"
|
18
|
+
}
|
19
|
+
}
|
20
|
+
ECOLOGY_CONTENTS
|
21
|
+
Ecology.read
|
22
|
+
end
|
23
|
+
|
24
|
+
should "find an absolute path" do
|
25
|
+
assert_equal "/pid_dir/", Ecology.path("pid_location")
|
26
|
+
end
|
27
|
+
|
28
|
+
should "find an application-relative path" do
|
29
|
+
$0 = "some/path/my_app.rb"
|
30
|
+
assert_equal "some/path/../dir1", Ecology.path("whozit_location")
|
31
|
+
end
|
32
|
+
|
33
|
+
should "find a cwd-relative path" do
|
34
|
+
Dir.expects(:getwd).returns("some/path")
|
35
|
+
assert_equal "some/path/logs", Ecology.path("whatsit_path")
|
36
|
+
end
|
37
|
+
|
38
|
+
should "substitute correctly for a PID path" do
|
39
|
+
Process.expects(:pid).returns(379)
|
40
|
+
assert_equal "dir/to/there.379", Ecology.path("some_other_location")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
|
3
|
+
class PropertyTest < Scope::TestCase
|
4
|
+
setup do
|
5
|
+
Ecology.reset
|
6
|
+
end
|
7
|
+
|
8
|
+
context "with environments in your ecology" do
|
9
|
+
setup do
|
10
|
+
set_up_ecology <<ECOLOGY_CONTENTS
|
11
|
+
{
|
12
|
+
"application": "SomeApp",
|
13
|
+
"environment-from": ["RACK_ENV"],
|
14
|
+
"domain": {
|
15
|
+
"property1" : "strval1",
|
16
|
+
"property2" : "374",
|
17
|
+
"property3" : 1987
|
18
|
+
}
|
19
|
+
}
|
20
|
+
ECOLOGY_CONTENTS
|
21
|
+
Ecology.read
|
22
|
+
end
|
23
|
+
|
24
|
+
should "get a top-level property" do
|
25
|
+
assert_equal "SomeApp", Ecology.property("application")
|
26
|
+
end
|
27
|
+
|
28
|
+
should "get a string property without a typecast" do
|
29
|
+
assert_equal "strval1", Ecology.property("domain::property1")
|
30
|
+
end
|
31
|
+
|
32
|
+
should "get a string property with a typecast" do
|
33
|
+
assert_equal "strval1", Ecology.property("domain::property1", :as => String)
|
34
|
+
end
|
35
|
+
|
36
|
+
should "get a string-number property with a String typecast" do
|
37
|
+
assert_equal "374", Ecology.property("domain::property2", :as => String)
|
38
|
+
end
|
39
|
+
|
40
|
+
should "get a string-number property with a Fixnum typecast" do
|
41
|
+
assert_equal 374, Ecology.property("domain::property2", :as => Fixnum)
|
42
|
+
end
|
43
|
+
|
44
|
+
should "get a string-number property with no typecast" do
|
45
|
+
assert_equal "374", Ecology.property("domain::property2")
|
46
|
+
end
|
47
|
+
|
48
|
+
should "get an integer property with a String typecast" do
|
49
|
+
assert_equal "1987", Ecology.property("domain::property3", :as => String)
|
50
|
+
end
|
51
|
+
|
52
|
+
should "get an integer property with a Fixnum typecast" do
|
53
|
+
assert_equal 1987, Ecology.property("domain::property3", :as => Fixnum)
|
54
|
+
end
|
55
|
+
|
56
|
+
should "get an integer property with no typecast" do
|
57
|
+
assert_equal 1987, Ecology.property("domain::property3")
|
58
|
+
end
|
59
|
+
|
60
|
+
should "be able to use :integer for a Fixnum typecast" do
|
61
|
+
assert_equal 374, Ecology.property("domain::property2", :as => :integer)
|
62
|
+
end
|
63
|
+
|
64
|
+
should "be able to use :int for a Fixnum typecast" do
|
65
|
+
assert_equal 374, Ecology.property("domain::property2", :as => :int)
|
66
|
+
end
|
67
|
+
|
68
|
+
should "be able to use :string for a String typecast" do
|
69
|
+
assert_equal "1987", Ecology.property("domain::property3", :as => :string)
|
70
|
+
end
|
71
|
+
|
72
|
+
should "be able to use :symbol for a Symbol typecast" do
|
73
|
+
assert_equal "1987".to_sym, Ecology.property("domain::property3", :as => :symbol)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
3
|
+
Bundler.require(:default, :development)
|
4
|
+
require "minitest/autorun"
|
5
|
+
|
6
|
+
# For testing Ecology itself, use the local version *first*.
|
7
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
8
|
+
|
9
|
+
require "ecology"
|
10
|
+
|
11
|
+
class Scope::TestCase
|
12
|
+
def set_up_ecology(file_contents, filename = "some.ecology")
|
13
|
+
ENV["ECOLOGY_SPEC"] = filename
|
14
|
+
File.stubs(:exist?).with(filename).returns(true)
|
15
|
+
File.expects(:read).with(filename).returns(file_contents).at_least_once
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
|
3
|
+
class EnvironmentTest < Scope::TestCase
|
4
|
+
setup do
|
5
|
+
Ecology.reset
|
6
|
+
end
|
7
|
+
|
8
|
+
teardown do
|
9
|
+
Ecology.clear_triggers
|
10
|
+
end
|
11
|
+
|
12
|
+
context "without an ecology" do
|
13
|
+
should "call on_initialize events at initialize" do
|
14
|
+
callee_mock = mock("object that gets called")
|
15
|
+
callee_mock.expects(:method)
|
16
|
+
|
17
|
+
Ecology.on_initialize("test_on_init") { callee_mock.method }
|
18
|
+
Ecology.read
|
19
|
+
end
|
20
|
+
|
21
|
+
should "call on_initialize events when called after initialize" do
|
22
|
+
callee_mock = mock("object that gets called")
|
23
|
+
callee_mock.expects(:method)
|
24
|
+
|
25
|
+
Ecology.read
|
26
|
+
Ecology.on_initialize("test_on_init") { callee_mock.method }
|
27
|
+
end
|
28
|
+
|
29
|
+
should "call on_initialize events with no token" do
|
30
|
+
callee_mock = mock("object that gets called")
|
31
|
+
callee_mock.expects(:method)
|
32
|
+
|
33
|
+
Ecology.read
|
34
|
+
Ecology.on_initialize { callee_mock.method }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with an ecology" do
|
39
|
+
setup do
|
40
|
+
set_up_ecology <<ECOLOGY_CONTENTS
|
41
|
+
{
|
42
|
+
"application": "SomeApp"
|
43
|
+
}
|
44
|
+
ECOLOGY_CONTENTS
|
45
|
+
end
|
46
|
+
|
47
|
+
should "call on_initialize events at initialize" do
|
48
|
+
callee_mock = mock("object that gets called")
|
49
|
+
callee_mock.expects(:method)
|
50
|
+
|
51
|
+
Ecology.on_initialize("test_on_init") { callee_mock.method }
|
52
|
+
Ecology.read
|
53
|
+
end
|
54
|
+
|
55
|
+
should "call on_initialize events when called after initialize" do
|
56
|
+
callee_mock = mock("object that gets called")
|
57
|
+
callee_mock.expects(:method)
|
58
|
+
|
59
|
+
Ecology.read
|
60
|
+
Ecology.on_initialize("test_on_init") { callee_mock.method }
|
61
|
+
end
|
62
|
+
|
63
|
+
should "call on_initialize events again after reset" do
|
64
|
+
callee_mock = mock("object that gets called")
|
65
|
+
callee_mock.expects(:method).twice
|
66
|
+
|
67
|
+
Ecology.on_initialize("test_on_init") { callee_mock.method }
|
68
|
+
Ecology.read
|
69
|
+
Ecology.reset
|
70
|
+
Ecology.read
|
71
|
+
end
|
72
|
+
|
73
|
+
should "call tokenless on_initialize events again after reset" do
|
74
|
+
callee_mock = mock("object that gets called")
|
75
|
+
callee_mock.expects(:method).twice
|
76
|
+
|
77
|
+
Ecology.on_initialize { callee_mock.method }
|
78
|
+
Ecology.read
|
79
|
+
Ecology.reset
|
80
|
+
Ecology.read
|
81
|
+
end
|
82
|
+
|
83
|
+
should "call on_reset events across multiple resets" do
|
84
|
+
callee_mock = mock("object that gets called")
|
85
|
+
callee_mock.expects(:method).twice
|
86
|
+
|
87
|
+
Ecology.read
|
88
|
+
Ecology.on_reset("test_on_reset") { callee_mock.method }
|
89
|
+
Ecology.reset
|
90
|
+
Ecology.reset
|
91
|
+
end
|
92
|
+
|
93
|
+
should "call on_reset events with no token across multiple resets" do
|
94
|
+
callee_mock = mock("object that gets called")
|
95
|
+
callee_mock.expects(:method).twice
|
96
|
+
|
97
|
+
Ecology.read
|
98
|
+
Ecology.on_reset { callee_mock.method }
|
99
|
+
Ecology.reset
|
100
|
+
Ecology.reset
|
101
|
+
end
|
102
|
+
|
103
|
+
should "remove on_reset events after remove_trigger" do
|
104
|
+
callee_mock = mock("object that gets called")
|
105
|
+
callee_mock.expects(:method).once # Not three times...
|
106
|
+
|
107
|
+
Ecology.read
|
108
|
+
Ecology.on_reset("on_reset_remove") { callee_mock.method }
|
109
|
+
Ecology.reset
|
110
|
+
Ecology.remove_trigger("on_reset_remove")
|
111
|
+
Ecology.reset
|
112
|
+
Ecology.reset
|
113
|
+
end
|
114
|
+
|
115
|
+
should "repeat on_initialize events even when called after initialize" do
|
116
|
+
callee_mock = mock("object that gets called")
|
117
|
+
callee_mock.expects(:method).twice
|
118
|
+
|
119
|
+
Ecology.read
|
120
|
+
Ecology.on_initialize("test_on_init") { callee_mock.method }
|
121
|
+
Ecology.reset
|
122
|
+
Ecology.read
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
metadata
CHANGED
@@ -1,45 +1,140 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: ecology
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
5
|
+
version: 0.0.11
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
8
|
-
-
|
7
|
+
authors:
|
8
|
+
- Noah Gibbs
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
|
13
|
+
date: 2011-10-06 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: multi_json
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: bundler
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.0.10
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: scope
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.2.1
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id003
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: mocha
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id004
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: rake
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
type: :development
|
69
|
+
version_requirements: *id005
|
70
|
+
description: |
|
71
|
+
Ecology sets configuration data for an application based
|
72
|
+
on environment variables and other factors. It is meant
|
73
|
+
to unify configuration data for logging, testing, monitoring
|
74
|
+
and deployment.
|
75
|
+
|
76
|
+
email:
|
77
|
+
- noah@ooyala.com
|
17
78
|
executables: []
|
79
|
+
|
18
80
|
extensions: []
|
81
|
+
|
19
82
|
extra_rdoc_files: []
|
20
|
-
|
21
|
-
|
83
|
+
|
84
|
+
files:
|
85
|
+
- ecology.gemspec
|
86
|
+
- ecology.gemspec~
|
87
|
+
- Gemfile
|
88
|
+
- Gemfile.lock
|
89
|
+
- lib/ecology/version.rb
|
90
|
+
- lib/ecology/version.rb~
|
91
|
+
- lib/ecology.rb
|
92
|
+
- LICENSE
|
93
|
+
- Rakefile
|
94
|
+
- README.md
|
95
|
+
- test/ecology_test.rb
|
96
|
+
- test/environment_test.rb
|
97
|
+
- test/environment_var_test.rb
|
98
|
+
- test/override_properties_test.rb
|
99
|
+
- test/path_test.rb
|
100
|
+
- test/property_test.rb
|
101
|
+
- test/test_helper.rb
|
102
|
+
- test/trigger_test.rb
|
103
|
+
- TODO
|
104
|
+
- .gitignore
|
105
|
+
homepage: http://www.ooyala.com
|
22
106
|
licenses: []
|
107
|
+
|
23
108
|
post_install_message:
|
24
109
|
rdoc_options: []
|
25
|
-
|
110
|
+
|
111
|
+
require_paths:
|
26
112
|
- lib
|
27
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
28
114
|
none: false
|
29
|
-
requirements:
|
30
|
-
- -
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version:
|
33
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: "0"
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
34
120
|
none: false
|
35
|
-
requirements:
|
36
|
-
- -
|
37
|
-
- !ruby/object:Gem::Version
|
38
|
-
version:
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: "0"
|
39
125
|
requirements: []
|
126
|
+
|
40
127
|
rubyforge_project: ecology
|
41
|
-
rubygems_version: 1.8.
|
128
|
+
rubygems_version: 1.8.10
|
42
129
|
signing_key:
|
43
130
|
specification_version: 3
|
44
|
-
summary:
|
45
|
-
test_files:
|
131
|
+
summary: Ruby config variable management
|
132
|
+
test_files:
|
133
|
+
- test/ecology_test.rb
|
134
|
+
- test/environment_test.rb
|
135
|
+
- test/environment_var_test.rb
|
136
|
+
- test/override_properties_test.rb
|
137
|
+
- test/path_test.rb
|
138
|
+
- test/property_test.rb
|
139
|
+
- test/test_helper.rb
|
140
|
+
- test/trigger_test.rb
|