magicmonkey 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 pioz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,17 @@
1
+ = magic_monkey
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 pioz. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "magicmonkey"
8
+ gem.version = File.exist?('VERSION') ? File.read('VERSION').strip : ''
9
+ gem.summary = %Q{Manage your Rails applications: different Ruby versions and different application servers}
10
+ gem.description = %Q{Manage your Rails applications: different Ruby versions and different application servers}
11
+ gem.email = "enrico@megiston.it"
12
+ gem.homepage = "http://github.com/pioz/magicmonkey"
13
+ gem.authors = ["pioz"]
14
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
15
+ #gem.add_dependency 'nokogiri'
16
+ #gem.add_dependency 'activerecord'
17
+ #gem.add_dependency 'sqlite3-ruby'
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
+ end
23
+
24
+ require 'rake/testtask'
25
+ Rake::TestTask.new(:test) do |test|
26
+ test.libs << 'lib' << 'test'
27
+ test.pattern = 'test/**/test_*.rb'
28
+ test.verbose = true
29
+ end
30
+
31
+ begin
32
+ require 'rcov/rcovtask'
33
+ Rcov::RcovTask.new do |test|
34
+ test.libs << 'test'
35
+ test.pattern = 'test/**/test_*.rb'
36
+ test.verbose = true
37
+ end
38
+ rescue LoadError
39
+ task :rcov do
40
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
41
+ end
42
+ end
43
+
44
+ task :test => :check_dependencies
45
+
46
+ task :default => :test
47
+
48
+ require 'rake/rdoctask'
49
+ Rake::RDocTask.new do |rdoc|
50
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "magicmonkey #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.10
data/bin/magicmonkey ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $APP_PATH = "#{File.dirname(__FILE__)}/.."
4
+ require "#{$APP_PATH}/lib/magic_monkey.rb"
5
+
6
+ MagicMonkey.main(ARGV)
@@ -0,0 +1,68 @@
1
+ require 'singleton'
2
+ require 'yaml'
3
+
4
+ class Conf
5
+ include Singleton
6
+ attr_reader :config
7
+
8
+ def initialize
9
+ load
10
+ end
11
+
12
+ def load
13
+ @file = "#{Etc.getpwuid.dir}/.magicmonkey.yml"
14
+ if File.exist?(@file)
15
+ @config = YAML.load_file(@file)
16
+ else
17
+ vht = ""
18
+ vht << "<VirtualHost tagi:80>\n"
19
+ vht << " ServerName $SERVER_NAME\n"
20
+ vht << " PassengerEnabled off\n"
21
+ vht << " ProxyPass / http://127.0.0.1:$PORT/\n"
22
+ vht << " ProxyPassReverse / http://127.0.0.1:$PORT/\n"
23
+ vht << "</VirtualHost>\n"
24
+ @config = {:vhost_template => vht}
25
+ self.save
26
+ end
27
+ end
28
+
29
+ def save
30
+ File.open(@file, 'w') { |f| f.write(@config.to_yaml) }
31
+ self.load
32
+ end
33
+
34
+ def self.[](key)
35
+ Conf.instance.config[key.to_sym]
36
+ end
37
+
38
+ def self.[]=(key, value)
39
+ Conf.instance.config[key.to_sym] = value
40
+ end
41
+
42
+ def self.delete(key)
43
+ Conf.instance.config.delete(key.to_sym)
44
+ end
45
+
46
+ def self.save
47
+ Conf.instance.save
48
+ end
49
+
50
+ def self.applications
51
+ app = {}
52
+ Conf.instance.config.each do |k, v|
53
+ if k != :vhost_template && k != :uid
54
+ app[k] = v
55
+ end
56
+ end
57
+ return app
58
+ end
59
+
60
+ def self.ports
61
+ p = []
62
+ Conf.instance.config.each do |k, v|
63
+ p << v[:port] if v.class == Hash && v[:port]
64
+ end
65
+ return p
66
+ end
67
+
68
+ end
@@ -0,0 +1,277 @@
1
+ require 'optparse'
2
+ require 'pp'
3
+ require 'etc'
4
+ require "#{$APP_PATH}/lib/configuration"
5
+
6
+ module MagicMonkey
7
+ COMMANDS = [:start, :stop, :restart, :add, :remove, :show]
8
+
9
+ def self.main(argv)
10
+ raise 'You cannot do this as root' if Process.uid == 0
11
+ Process::UID.change_privilege(Conf[:uid] || Process.uid)
12
+ command = argv[0]
13
+ if command == '-v' || command == '--version'
14
+ puts File.exist?("#{$APP_PATH}/VERSION") ? File.read("#{$APP_PATH}/VERSION").strip : ''
15
+ exit
16
+ elsif command.nil? || command == '-h' || command == '--help' || !COMMANDS.include?(command.to_sym)
17
+ main_help
18
+ exit
19
+ else
20
+ send(command, argv[1..-1])
21
+ end
22
+ end
23
+
24
+ def self.main_help
25
+ puts 'Description here'
26
+ puts
27
+ puts 'Available commands:'
28
+ puts
29
+ COMMANDS.each do |c|
30
+ puts " magicmonkey #{c}\t\tdesc"
31
+ end
32
+ puts
33
+ puts "Special options:"
34
+ puts
35
+ puts " magicmonkey --help\t\tDisplay this help message."
36
+ puts " magicmonkey --version\t\tDisplay version number."
37
+ puts
38
+ puts "For more information about a specific command, please type"
39
+ puts "'magicmonkey <COMMAND> --help', e.g. 'magicmonkey add --help'."
40
+ end
41
+
42
+ def self.show(argv)
43
+ applications = argv
44
+ applications = Conf.applications.keys if argv.empty?
45
+ applications.each do |app_name|
46
+ if Conf[app_name]
47
+ puts app_name
48
+ puts '-'*app_name.to_s.size
49
+ pp Conf[app_name]
50
+ puts
51
+ else
52
+ puts "Application '#{app_name}' not found."
53
+ end
54
+ end
55
+ end
56
+
57
+ def self.start(argv)
58
+ v, help = common_options(argv)
59
+ if help
60
+ puts 'Start a web application added with ADD command. If no params are given start all web applications.'
61
+ exit
62
+ end
63
+ applications = argv
64
+ applications = Conf.applications.keys if argv.empty?
65
+ applications.each do |app_name|
66
+ if Conf[app_name]
67
+ commands = []
68
+ commands << "source '#{Etc.getpwuid.dir}/.rvm/scripts/rvm'"
69
+ commands << "cd '#{Conf[app_name][:app_path]}'"
70
+ commands << "rvm #{v ? 'use ' : ''}'#{Conf[app_name][:ruby]}'"
71
+ case Conf[app_name][:app_server]
72
+ when 'passenger'
73
+ commands << "passenger start -e production -p #{Conf[app_name][:port]} #{Conf[app_name][:app_server_options]} -d"
74
+ when 'thin'
75
+ commands << "thin start -e production -p #{Conf[app_name][:port]} -d"
76
+ end
77
+ print "Starting '#{app_name}' application..."
78
+ STDOUT.flush
79
+ output = `bash -c "#{commands.join(' && ')}"`
80
+ puts ' done.'
81
+ print output if v
82
+ end
83
+ end
84
+ end
85
+
86
+ def self.stop(argv)
87
+ v, help = common_options(argv)
88
+ if help
89
+ puts 'Stop a web application added with ADD command. If no params are given stop all web applications.'
90
+ exit
91
+ end
92
+ applications = argv
93
+ applications = Conf.applications.keys if argv.empty?
94
+ applications.each do |app_name|
95
+ if Conf[app_name]
96
+ commands = []
97
+ commands << "source '#{Etc.getpwuid.dir}/.rvm/scripts/rvm'"
98
+ commands << "cd '#{Conf[app_name][:app_path]}'"
99
+ commands << "rvm #{v ? 'use ' : ''}'#{Conf[app_name][:ruby]}'"
100
+ case Conf[app_name][:app_server]
101
+ when 'passenger'
102
+ commands << "passenger stop -p #{Conf[app_name][:port]}"
103
+ when 'thin'
104
+ commands << "thin stop -p #{Conf[app_name][:port]}"
105
+ end
106
+ print "Stopping '#{app_name}' application..."
107
+ STDOUT.flush
108
+ output = `bash -c "#{commands.join(' && ')}"`
109
+ puts ' done.'
110
+ print output if v
111
+ end
112
+ end
113
+ end
114
+
115
+ def self.restart(argv)
116
+ applications = argv
117
+ applications = Conf.applications.keys if argv.empty?
118
+ applications.each do |app_name|
119
+ self.stop([app_name])
120
+ self.start([app_name])
121
+ end
122
+ end
123
+
124
+ def self.add(argv)
125
+ options = {}
126
+ tmp = argv.join('$$').split(/\$\$--\$\$/)
127
+ argv = tmp[0].split('$$')
128
+ options[:app_server_options] = tmp[1] ? tmp[1].split('$$').join(' ') : ''
129
+ servers = ['passenger', 'thin']
130
+ ports = (3000..4000).to_a.collect{|p| p.to_s}
131
+ rubies = ['default', '1.9.2', '1.8.7', 'ree']
132
+ options[:app_server] = servers.first
133
+ options[:app_path] = '/var/sites/APP_NAME/current'
134
+ options[:port] = nil
135
+ options[:ruby] = rubies.first
136
+ options[:vhost_path] = '/etc/apache2/sites-available'
137
+ vhost_template = "#{Etc.getpwuid.dir}/.magicmonkey.yml"
138
+ force = false
139
+ create_vhost = true
140
+ enable_site = true
141
+ reload_apache = false
142
+ server_name = nil
143
+
144
+ parser = OptionParser.new do |opts|
145
+ opts.banner = 'Usage: magicmonkey add APP_NAME [options] [-- application_server_options]'
146
+ opts.separator ''
147
+ opts.separator 'Options:'
148
+
149
+ opts.on('-s', '--app-server APP_SERVER', servers, "Use the given application server: #{servers.join(', ')} (default: #{options[:app_server]}).") do |s|
150
+ options[:app_server] = s
151
+ end
152
+ opts.on('--app-path APP_PATH', "Use the given application path (default: '#{options[:app_path]}').") do |path|
153
+ options[:app_path] = path
154
+ end
155
+ opts.on('--vhost-path VHOST_PATH', "Use the given virtual host path (default: '#{options[:vhost_path]}').") do |path|
156
+ options[:vhost_path] = path
157
+ end
158
+ opts.on('--vhost-template TEMPLATE', "Use the given virtual host template file (default: #{vhost_template}).") do |template|
159
+ vhost_template = template
160
+ end
161
+ opts.on('-p', '--port NUMBER', ports, "Use the given port number (min: #{ports.first}, max: #{ports.last}).") do |p|
162
+ options[:port] = p.to_i
163
+ end
164
+ opts.on('-r', '--ruby RUBY_VERSION', rubies, "Use the given Ruby version: #{rubies.join(', ')} (default: #{options[:ruby]}).") do |r|
165
+ options[:ruby] = r
166
+ end
167
+ opts.on('-f', '--[no-]force', "Force mode: replace exist files (default: #{force}).") do |f|
168
+ force = f
169
+ end
170
+ opts.on('--[no-]create-vhost', "Create virtual host file from template (default: #{create_vhost}).") do |c|
171
+ create_vhost = c
172
+ end
173
+ opts.on('--[no-]enable-site', "Enable Apache virtual host (default: #{enable_site}).") do |e|
174
+ enable_site = e
175
+ end
176
+ opts.on('--[no-]reload-apache', "Reload apache to load virtual host (default: #{reload_apache}).") do |r|
177
+ reload_apache = r
178
+ end
179
+ opts.on('--server-name SERVER_NAME', "Set ServerName on virtual host (default: APP_NAME).") do |name|
180
+ server_name = name
181
+ end
182
+ opts.on_tail('-h', '--help', 'Show this help message.') do
183
+ puts opts
184
+ exit
185
+ end
186
+ end
187
+ begin
188
+ argv = parser.parse!(argv)
189
+ rescue
190
+ puts parser.help
191
+ exit
192
+ end
193
+ if argv.size != 1
194
+ puts parser.help
195
+ exit
196
+ end
197
+ app_name = argv[0]
198
+ #setting up default values
199
+ options[:app_path].gsub!('APP_NAME', app_name)
200
+ port = get_port(options[:port])
201
+ if port
202
+ options[:port] = port
203
+ else
204
+ puts 'This port is busy'
205
+ exit
206
+ end
207
+ #start
208
+ if Conf[app_name].nil?
209
+ Conf[app_name] = options
210
+ puts "Configuration for application '#{app_name}' is:"
211
+ pp Conf[app_name]
212
+ print 'Add this application? [Y/n]'
213
+ input = STDIN.gets
214
+ if input.upcase == "Y\n" || input == "\n"
215
+ if create_vhost
216
+ vh = YAML.load_file(vhost_template)[:vhost_template]
217
+ vh.gsub!('$SERVER_NAME', server_name || app_name)
218
+ #vh.gsub!('$DOCUMENT_ROOT', Conf[app_name][:app_path])
219
+ vh.gsub!('$PORT', Conf[app_name][:port].to_s)
220
+ vh_file = "#{Conf[app_name][:vhost_path]}/#{app_name}"
221
+ if !File.exist?(vh_file) || force
222
+ #File.open(vh_file, 'w') { |f| f.write(vh) }
223
+ print `sudo bash -c "echo '#{vh}' > #{vh_file}"`
224
+ else
225
+ puts "Virtual host file '#{vh_file}' already exist. Use option '-f' to replace it."
226
+ exit
227
+ end
228
+ print `sudo a2ensite '#{app_name}'` if enable_site
229
+ print `sudo /etc/init.d/apache2 reload` if enable_site && reload_apache
230
+ end
231
+ Conf.save
232
+ puts 'Application added.'
233
+ else
234
+ puts 'Application rejected.'
235
+ end
236
+ else
237
+ puts "Application '#{app_name}' already added. You can remove it with 'remove' command."
238
+ end
239
+ end
240
+
241
+ def self.remove(argv)
242
+ argv.each do |app_name|
243
+ if Conf[app_name]
244
+ vh_file = "#{Conf[app_name][:vhost_path]}/#{app_name}"
245
+ if File.exist?(vh_file)
246
+ print `sudo a2dissite '#{app_name}'`
247
+ print `sudo rm -f #{vh_file}`
248
+ end
249
+ Conf.delete(app_name)
250
+ Conf.save
251
+ else
252
+ puts "Application '#{app_name}' does not exist. You can add it with 'add' command."
253
+ end
254
+ end
255
+ end
256
+
257
+ private
258
+
259
+ def self.get_port(port)
260
+ ports = Conf.ports
261
+ return 3000 if ports.nil? || ports.empty?
262
+ return false if ports.include?(port)
263
+ return ports.max + 1 if port.nil?
264
+ return port
265
+ end
266
+
267
+ def self.common_options(argv)
268
+ verbose = argv.include?('-v') || argv.include?('--version')
269
+ help = argv.include?('-h') || argv.include?('--help')
270
+ argv.delete('-v')
271
+ argv.delete('--version')
272
+ argv.delete('-h')
273
+ argv.delete('--help')
274
+ return verbose, help
275
+ end
276
+
277
+ end
@@ -0,0 +1,57 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{magicmonkey}
8
+ s.version = "0.0.10"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["pioz"]
12
+ s.date = %q{2010-10-28}
13
+ s.default_executable = %q{magicmonkey}
14
+ s.description = %q{Manage your Rails applications: different Ruby versions and different application servers}
15
+ s.email = %q{enrico@megiston.it}
16
+ s.executables = ["magicmonkey"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".gitignore",
23
+ "LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "bin/magicmonkey",
28
+ "lib/configuration.rb",
29
+ "lib/magic_monkey.rb",
30
+ "magicmonkey.gemspec",
31
+ "test/helper.rb",
32
+ "test/test_magic_monkey.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/pioz/magicmonkey}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.7}
38
+ s.summary = %q{Manage your Rails applications: different Ruby versions and different application servers}
39
+ s.test_files = [
40
+ "test/helper.rb",
41
+ "test/test_magic_monkey.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
50
+ else
51
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
52
+ end
53
+ else
54
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
55
+ end
56
+ end
57
+
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'magic_monkey'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestMagicMonkey < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: magicmonkey
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 10
9
+ version: 0.0.10
10
+ platform: ruby
11
+ authors:
12
+ - pioz
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-10-28 00:00:00 +02:00
18
+ default_executable: magicmonkey
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: thoughtbot-shoulda
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :development
32
+ version_requirements: *id001
33
+ description: "Manage your Rails applications: different Ruby versions and different application servers"
34
+ email: enrico@megiston.it
35
+ executables:
36
+ - magicmonkey
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - LICENSE
41
+ - README.rdoc
42
+ files:
43
+ - .gitignore
44
+ - LICENSE
45
+ - README.rdoc
46
+ - Rakefile
47
+ - VERSION
48
+ - bin/magicmonkey
49
+ - lib/configuration.rb
50
+ - lib/magic_monkey.rb
51
+ - magicmonkey.gemspec
52
+ - test/helper.rb
53
+ - test/test_magic_monkey.rb
54
+ has_rdoc: true
55
+ homepage: http://github.com/pioz/magicmonkey
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --charset=UTF-8
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.3.7
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: "Manage your Rails applications: different Ruby versions and different application servers"
86
+ test_files:
87
+ - test/helper.rb
88
+ - test/test_magic_monkey.rb