ecology 0.0.1 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|