Compare commits

...
28 Commits
Author SHA1 Message Date
David Dollar bda6e30323 0.20.0 2011-08-22 17:27:50 -04:00
David Dollar ffd77312bb Merge pull request #53 from matth/master
Blank line in Procfile breaks Foreman
2011-08-22 14:27:03 -07:00
David Dollar a24c4ce17b Merge pull request #47 from minhajuddin/master
A little convention over configuration love.
2011-08-22 14:23:43 -07:00
David Dollar cf3d7a0b12 Merge pull request #55 from caos/master
Fix for "..../lib/foreman/engine.rb:117:in `eof?': Input/output error - /dev/pts/n (Errno::EIO)" errors
2011-08-22 14:19:40 -07:00
David Dollar 6ad344d214 consistency 2011-08-18 12:54:17 -04:00
David Dollar 3b8fec463d update manual to mention .env 2011-08-18 12:52:03 -04:00
Mike Javorski 4e015b7436 add Errno::EIO to list of rescued exceptions as underlying pts can close before shutdown is complete
Possibly relates to GitHub Issue #40.
2011-08-11 11:59:51 -07:00
Matt Haynes 2042de7732 Fix bug where blank lines in Procfile break Foreman. 2011-07-28 11:40:31 +01:00
Khaja Minhajuddin 64338c5a09 use a dedicated directory (~/.foreman/templates) to store the templates 2011-06-30 22:23:38 +05:30
Khaja Minhajuddin 8cef71766c tweaked the upstart export code so that it looks for templates in ~/.foreman if no template_root is specified. 2011-06-30 21:50:12 +05:30
David Dollar a2ba079665 0.19.0 2011-06-27 13:17:54 -04:00
David Dollar c8d0dba1cb tweaks to template roots, add testing 2011-06-27 13:17:26 -04:00
Michael van Rooijen e8d2552caa Added the ability to use template configuration files using the '--template [-t]' command line option. This allows you to create a directory on the file system containing your configuration files which Foreman will read from instead of the default templates. 2011-06-26 19:02:51 +02:00
David Dollar 927a57f738 Merge pull request #43 from smith/master
RSpec Warning Fix
2011-06-20 20:35:55 -07:00
Nathan L Smith f65538c1b1 update rspec to 2.6 2011-06-20 21:58:16 -05:00
Nathan L Smith 470c8043af fix rspec warnings 2011-06-20 21:44:35 -05:00
David Dollar 3e69b27f5f Merge pull request #34 from nz/empty-foreman-file
Gracefully handle the 'garbage in' of an empty .foreman file.
2011-06-03 13:51:29 -07:00
Nick Zadrozny f4b1e3701b Gracefully handle the 'garbage in' of an empty .foreman file. 2011-06-03 15:48:13 -05:00
David Dollar fd234b8044 0.18.0 2011-06-03 01:32:14 -04:00
David Dollar f2f09554c8 correct shutdown signals
processes are sent SIGTERM followed 3 seconds later by SIGKILL
2011-06-03 01:31:51 -04:00
David Dollar 6c465b4ef1 remove debug print 2011-06-03 01:31:08 -04:00
David Dollar c419f8213b 0.17.0 2011-06-02 12:10:30 -04:00
David Dollar 7f61fb61ea credits 2011-06-02 12:10:21 -04:00
David Dollar 577a5c7c5c make sure tests run on machines other than mine 2011-06-02 12:09:30 -04:00
David Dollar 56940c56d9 Merge pull request #31 from jayzes/master
Loading the shell environment in the exported upstart configuration
2011-06-02 08:51:45 -07:00
Jay Zeschin f308ad886d Change directories when using su - -c to execute a command since you lose the current working directory 2011-06-01 10:38:07 -06:00
Jay Zeschin 55375b9bde Edited data/export/upstart/process.conf.erb via GitHub 2011-05-31 16:30:45 -07:00
David Dollar 85fcccffa8 more readme touchup 2011-05-13 12:10:38 -04:00
28 changed files with 169 additions and 90 deletions
+11 -13
View File
@@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
foreman (0.16.0) foreman (0.20.0)
term-ansicolor (~> 1.0.5) term-ansicolor (~> 1.0.5)
thor (>= 0.13.6) thor (>= 0.13.6)
@@ -28,17 +28,15 @@ GEM
mustache (>= 0.7.0) mustache (>= 0.7.0)
rdiscount (>= 1.5.8) rdiscount (>= 1.5.8)
rr (1.0.2) rr (1.0.2)
rspec (2.0.1) rspec (2.6.0)
rspec-core (~> 2.0.1) rspec-core (~> 2.6.0)
rspec-expectations (~> 2.0.1) rspec-expectations (~> 2.6.0)
rspec-mocks (~> 2.0.1) rspec-mocks (~> 2.6.0)
rspec-core (2.0.1) rspec-core (2.6.4)
rspec-expectations (2.0.1) rspec-expectations (2.6.0)
diff-lcs (>= 1.1.2) diff-lcs (~> 1.1.2)
rspec-mocks (2.0.1) rspec-mocks (2.6.0)
rspec-core (~> 2.0.1) term-ansicolor (1.0.6)
rspec-expectations (~> 2.0.1)
term-ansicolor (1.0.5)
thor (0.14.6) thor (0.14.6)
PLATFORMS PLATFORMS
@@ -52,4 +50,4 @@ DEPENDENCIES
rcov (~> 0.9.8) rcov (~> 0.9.8)
ronn ronn
rr (~> 1.0.2) rr (~> 1.0.2)
rspec (~> 2.0.0) rspec (~> 2.6.0)
+9 -8
View File
@@ -12,17 +12,18 @@ http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
See the [man page](http://ddollar.github.com/foreman) for usage. See the [man page](http://ddollar.github.com/foreman) for usage.
## Author ## Authorship
David Dollar Created by David Dollar
## Contributors Patches contributed by:
Adam Wiggins * Adam Wiggins
clifff * clifff
Dan Peterson * Dan Peterson
Keith Rarick * Jay Zeschin
Ricardo Chimal, Jr * Keith Rarick
* Ricardo Chimal, Jr
## License ## License
+2 -2
View File
@@ -13,7 +13,7 @@ task :default => :spec
task :release => :man task :release => :man
desc "Run all specs" desc "Run all specs"
Rspec::Core::RakeTask.new(:spec) do |t| RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = 'spec/**/*_spec.rb' t.pattern = 'spec/**/*_spec.rb'
end end
@@ -22,7 +22,7 @@ task :rcov => "rcov:build" do
%x{ open coverage/index.html } %x{ open coverage/index.html }
end end
Rspec::Core::RakeTask.new("rcov:build") do |t| RSpec::Core::RakeTask.new("rcov:build") do |t|
t.pattern = 'spec/**/*_spec.rb' t.pattern = 'spec/**/*_spec.rb'
t.rcov = true t.rcov = true
t.rcov_opts = [ "--exclude", ".bundle", "--exclude", "spec" ] t.rcov_opts = [ "--exclude", ".bundle", "--exclude", "spec" ]
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env ruby
exec ARGV.join(" ")
+6
View File
@@ -1,5 +1,11 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
%w( SIGINT SIGTERM SIGHUP ).each do |signal|
trap(signal) do
puts "received #{signal} but i'm ignoring it!"
end
end
while true while true
puts "tick: #{ARGV.inspect} -- FOO:#{ENV["FOO"]}" puts "tick: #{ARGV.inspect} -- FOO:#{ENV["FOO"]}"
sleep 1 sleep 1
+1 -2
View File
@@ -2,5 +2,4 @@ start on starting <%= app %>-<%= process.name %>
stop on stopping <%= app %>-<%= process.name %> stop on stopping <%= app %>-<%= process.name %>
respawn respawn
chdir <%= engine.directory %> exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>; <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
exec su <%= user %> -c 'export PORT=<%= port %>; <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
+1 -1
View File
@@ -25,5 +25,5 @@ Gem::Specification.new do |gem|
gem.add_development_dependency 'fakefs', '~> 0.2.1' gem.add_development_dependency 'fakefs', '~> 0.2.1'
gem.add_development_dependency 'rcov', '~> 0.9.8' gem.add_development_dependency 'rcov', '~> 0.9.8'
gem.add_development_dependency 'rr', '~> 1.0.2' gem.add_development_dependency 'rr', '~> 1.0.2'
gem.add_development_dependency 'rspec', '~> 2.0.0' gem.add_development_dependency 'rspec', '~> 2.6.0'
end end
+2 -1
View File
@@ -30,6 +30,7 @@ class Foreman::CLI < Thor
method_option :log, :type => :string, :aliases => "-l" method_option :log, :type => :string, :aliases => "-l"
method_option :port, :type => :numeric, :aliases => "-p" method_option :port, :type => :numeric, :aliases => "-p"
method_option :user, :type => :string, :aliases => "-u" method_option :user, :type => :string, :aliases => "-u"
method_option :template, :type => :string, :aliases => "-t"
method_option :concurrency, :type => :string, :aliases => "-c", method_option :concurrency, :type => :string, :aliases => "-c",
:banner => '"alpha=5,bar=3"' :banner => '"alpha=5,bar=3"'
@@ -86,7 +87,7 @@ private ######################################################################
def options def options
original_options = super original_options = super
return original_options unless File.exists?(".foreman") return original_options unless File.exists?(".foreman")
defaults = YAML::load_file(".foreman") defaults = YAML::load_file(".foreman") || {}
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options)) Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
end end
+27 -14
View File
@@ -3,6 +3,7 @@ require "foreman/process"
require "foreman/utils" require "foreman/utils"
require "pty" require "pty"
require "tempfile" require "tempfile"
require "timeout"
require "term/ansicolor" require "term/ansicolor"
require "fileutils" require "fileutils"
@@ -24,7 +25,7 @@ class Foreman::Engine
@processes ||= begin @processes ||= begin
@order = [] @order = []
procfile.split("\n").inject({}) do |hash, line| procfile.split("\n").inject({}) do |hash, line|
next if line.strip == "" next hash if line.strip == ""
name, command = line.split(/ *: +/, 2) name, command = line.split(/ *: +/, 2)
unless command unless command
warn_deprecated_procfile! warn_deprecated_procfile!
@@ -58,8 +59,8 @@ class Foreman::Engine
fork process, options, environment fork process, options, environment
end end
trap("TERM") { puts "SIGTERM received"; kill_all("TERM") } trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
trap("INT") { puts "SIGINT received"; kill_all("TERM") } trap("INT") { puts "SIGINT received"; terminate_gracefully }
watch_for_termination watch_for_termination
end end
@@ -69,8 +70,8 @@ class Foreman::Engine
fork processes[name], options, environment fork processes[name], options, environment
trap("TERM") { puts "SIGTERM received"; kill_all("TERM") } trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
trap("INT") { puts "SIGINT received"; kill_all("TERM") } trap("INT") { puts "SIGINT received"; terminate_gracefully }
watch_for_termination watch_for_termination
end end
@@ -107,18 +108,18 @@ private ######################################################################
def run(process) def run(process)
proctitle "ruby: foreman #{process.name}" proctitle "ruby: foreman #{process.name}"
trap("SIGINT", "IGNORE")
begin begin
Dir.chdir directory do Dir.chdir directory do
command = process.command PTY.spawn(runner, process.command) do |stdin, stdout, pid|
trap("SIGTERM") { Process.kill("SIGTERM", pid) }
PTY.spawn("#{process.command} 2>&1") do |stdin, stdout, pid|
until stdin.eof? until stdin.eof?
info stdin.gets, process info stdin.gets, process
end end
end end
end end
rescue PTY::ChildExited, Interrupt rescue PTY::ChildExited, Interrupt, Errno::EIO
begin begin
info "process exiting", process info "process exiting", process
rescue Interrupt rescue Interrupt
@@ -126,11 +127,9 @@ private ######################################################################
end end
end end
def kill_all(signal="TERM") def kill_all(signal="SIGTERM")
info "terminating"
running_processes.each do |pid, process| running_processes.each do |pid, process|
info "killing #{process.name} in pid #{pid}" Process.kill(signal, pid) rescue Errno::ESRCH
Process.kill(signal, pid)
end end
end end
@@ -179,8 +178,9 @@ private ######################################################################
pid, status = Process.wait2 pid, status = Process.wait2
process = running_processes.delete(pid) process = running_processes.delete(pid)
info "process terminated", process info "process terminated", process
terminate_gracefully
kill_all kill_all
Process.waitall rescue Errno::ECHILD
end end
def running_processes def running_processes
@@ -217,4 +217,17 @@ private ######################################################################
environment environment
end end
def runner
File.expand_path("../../../bin/foreman-runner", __FILE__)
end
def terminate_gracefully
info "sending SIGTERM to all processes"
kill_all "SIGTERM"
Timeout.timeout(3) { Process.waitall }
rescue Timeout::Error
info "sending SIGKILL to all processes"
kill_all "SIGKILL"
end
end end
+8 -2
View File
@@ -23,8 +23,14 @@ private ######################################################################
puts "[foreman export] %s" % message puts "[foreman export] %s" % message
end end
def export_template(name) def export_template(exporter, file, template_root)
File.read(File.expand_path("../../../../data/export/#{name}", __FILE__)) if template_root && File.exist?(file_path = File.join(template_root, file))
File.read(file_path)
elsif File.exist?(file_path = File.join("~/.foreman/templates", file))
File.read(file_path)
else
File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__))
end
end end
def write_file(filename, contents) def write_file(filename, contents)
+4 -3
View File
@@ -11,6 +11,7 @@ class Foreman::Export::Upstart < Foreman::Export::Base
app = options[:app] || File.basename(engine.directory) app = options[:app] || File.basename(engine.directory)
user = options[:user] || app user = options[:user] || app
log_root = options[:log] || "/var/log/#{app}" log_root = options[:log] || "/var/log/#{app}"
template_root = options[:template]
Dir["#{location}/#{app}*.conf"].each do |file| Dir["#{location}/#{app}*.conf"].each do |file|
say "cleaning up: #{file}" say "cleaning up: #{file}"
@@ -19,14 +20,14 @@ class Foreman::Export::Upstart < Foreman::Export::Base
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency]) concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
master_template = export_template("upstart/master.conf.erb") master_template = export_template("upstart", "master.conf.erb", template_root)
master_config = ERB.new(master_template).result(binding) master_config = ERB.new(master_template).result(binding)
write_file "#{location}/#{app}.conf", master_config write_file "#{location}/#{app}.conf", master_config
process_template = export_template("upstart/process.conf.erb") process_template = export_template("upstart", "process.conf.erb", template_root)
engine.processes.values.each do |process| engine.processes.values.each do |process|
process_master_template = export_template("upstart/process_master.conf.erb") process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
process_master_config = ERB.new(process_master_template).result(binding) process_master_config = ERB.new(process_master_template).result(binding)
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
+1 -1
View File
@@ -1,5 +1,5 @@
module Foreman module Foreman
VERSION = "0.16.0" VERSION = "0.20.0"
end end
+16 -2
View File
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3 .\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3
. .
.TH "FOREMAN" "1" "May 2011" "Foreman 0.15.0" "Foreman Manual" .TH "FOREMAN" "1" "August 2011" "Foreman 0.19.0" "Foreman Manual"
. .
.SH "NAME" .SH "NAME"
\fBforeman\fR \- manage Procfile\-based applications \fBforeman\fR \- manage Procfile\-based applications
@@ -69,7 +69,7 @@ These options control all modes of foreman\'s operation\.
. .
.TP .TP
\fB\-e\fR, \fB\-\-env\fR \fB\-e\fR, \fB\-\-env\fR
Specify a file containing the environment that should be set up for each child process\. The file should be key/value pairs separated by \fB=\fR, with one key/value pair per line\. Specify an alternate environment file\.
. .
.TP .TP
\fB\-f\fR, \fB\-\-procfile\fR \fB\-f\fR, \fB\-\-procfile\fR
@@ -141,6 +141,20 @@ $ foreman check
. .
.IP "" 0 .IP "" 0
. .
.SH "ENVIRONMENT"
If a \fB\.env\fR file exists in the current directory, the default environment will be read from it\. This file should contain key/value pairs, separated by \fB=\fR, with one key/value pair per line\.
.
.IP "" 4
.
.nf
FOO=bar
BAZ=qux
.
.fi
.
.IP "" 0
.
.SH "DEFAULT OPTIONS" .SH "DEFAULT OPTIONS"
If a \fB\.foreman\fR file exists in the current directory, default options will be read from it\. This file should be in YAML format with the long option name as keys\. Example: If a \fB\.foreman\fR file exists in the current directory, default options will be read from it\. This file should be in YAML format with the long option name as keys\. Example:
. .
+10 -3
View File
@@ -67,9 +67,7 @@ The following options control how the application is run:
These options control all modes of foreman's operation. These options control all modes of foreman's operation.
* `-e`, `--env`: * `-e`, `--env`:
Specify a file containing the environment that should be set up for each Specify an alternate environment file.
child process. The file should be key/value pairs separated by `=`, with
one key/value pair per line.
* `-f`, `--procfile`: * `-f`, `--procfile`:
Specify an alternate location for the application's Procfile. This file's Specify an alternate location for the application's Procfile. This file's
@@ -116,6 +114,15 @@ You can validate your Procfile format using the `check` command
$ foreman check $ foreman check
## ENVIRONMENT
If a `.env` file exists in the current directory, the default environment will
be read from it. This file should contain key/value pairs, separated by `=`, with
one key/value pair per line.
FOO=bar
BAZ=qux
## DEFAULT OPTIONS ## DEFAULT OPTIONS
If a `.foreman` file exists in the current directory, default options will If a `.foreman` file exists in the current directory, default options will
+40 -7
View File
@@ -1,9 +1,11 @@
require "spec_helper" require "spec_helper"
require "foreman/engine" require "foreman/engine"
require "foreman/export/upstart" require "foreman/export/upstart"
require "tmpdir"
describe Foreman::Export::Upstart do describe Foreman::Export::Upstart do
let(:engine) { Foreman::Engine.new(write_procfile) } let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
let(:engine) { Foreman::Engine.new(procfile) }
let(:upstart) { Foreman::Export::Upstart.new(engine) } let(:upstart) { Foreman::Export::Upstart.new(engine) }
before(:each) { load_export_templates_into_fakefs("upstart") } before(:each) { load_export_templates_into_fakefs("upstart") }
@@ -12,11 +14,42 @@ describe Foreman::Export::Upstart do
it "exports to the filesystem" do it "exports to the filesystem" do
upstart.export("/tmp/init") upstart.export("/tmp/init")
File.read("/tmp/init/foreman.conf").should == example_export_file("upstart/foreman.conf") File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
File.read("/tmp/init/foreman-alpha.conf").should == example_export_file("upstart/foreman-alpha.conf") File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
File.read("/tmp/init/foreman-alpha-1.conf").should == example_export_file("upstart/foreman-alpha-1.conf") File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
File.read("/tmp/init/foreman-alpha-2.conf").should == example_export_file("upstart/foreman-alpha-2.conf") File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("upstart/app-alpha-2.conf")
File.read("/tmp/init/foreman-bravo.conf").should == example_export_file("upstart/foreman.bravo.conf") File.read("/tmp/init/app-bravo.conf").should == example_export_file("upstart/app-bravo.conf")
File.read("/tmp/init/foreman-bravo-1.conf").should == example_export_file("upstart/foreman-bravo-1.conf") File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("upstart/app-bravo-1.conf")
end end
context "with alternate templates" do
let(:template_root) { "/tmp/alternate" }
before do
FileUtils.mkdir_p template_root
File.open("#{template_root}/master.conf.erb", "w") { |f| f.puts "alternate_template" }
end
it "can export with alternate template files" do
upstart.export("/tmp/init", :template => template_root)
File.read("/tmp/init/app.conf").should == "alternate_template\n"
end
end
context "with alternate templates from home dir" do
let(:default_template_root) {File.expand_path("~/.foreman/templates")}
before do
FileUtils.mkdir_p default_template_root
File.open("#{default_template_root}/master.conf.erb", "w") { |f| f.puts "default_alternate_template" }
end
it "can export with alternate template files" do
upstart.export("/tmp/init")
File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
end
end
end end
@@ -0,0 +1,5 @@
start on starting app-alpha
stop on stopping app-alpha
respawn
exec su - app -c 'cd /tmp/app; export PORT=5000; ./alpha >> /var/log/app/alpha-1.log 2>&1'
@@ -0,0 +1,5 @@
start on starting app-alpha
stop on stopping app-alpha
respawn
exec su - app -c 'cd /tmp/app; export PORT=5001; ./alpha >> /var/log/app/alpha-2.log 2>&1'
@@ -0,0 +1,2 @@
start on starting app
stop on stopping app
@@ -0,0 +1,5 @@
start on starting app-bravo
stop on stopping app-bravo
respawn
exec su - app -c 'cd /tmp/app; export PORT=5100; ./bravo >> /var/log/app/bravo-1.log 2>&1'
@@ -0,0 +1,2 @@
start on starting app
stop on stopping app
+8
View File
@@ -0,0 +1,8 @@
pre-start script
bash << "EOF"
mkdir -p /var/log/app
chown -R app /var/log/app
EOF
end script
@@ -1,6 +0,0 @@
start on starting foreman-alpha
stop on stopping foreman-alpha
respawn
chdir /Users/david/Code/foreman
exec su foreman -c 'export PORT=5000; ./alpha >> /var/log/foreman/alpha-1.log 2>&1'
@@ -1,6 +0,0 @@
start on starting foreman-alpha
stop on stopping foreman-alpha
respawn
chdir /Users/david/Code/foreman
exec su foreman -c 'export PORT=5001; ./alpha >> /var/log/foreman/alpha-2.log 2>&1'
@@ -1,2 +0,0 @@
start on starting foreman
stop on stopping foreman
@@ -1,6 +0,0 @@
start on starting foreman-bravo
stop on stopping foreman-bravo
respawn
chdir /Users/david/Code/foreman
exec su foreman -c 'export PORT=5100; ./bravo >> /var/log/foreman/bravo-1.log 2>&1'
@@ -1,2 +0,0 @@
start on starting foreman
stop on stopping foreman
@@ -1,8 +0,0 @@
pre-start script
bash << "EOF"
mkdir -p /var/log/foreman
chown -R foreman /var/log/foreman
EOF
end script
+2 -1
View File
@@ -27,6 +27,7 @@ end
def write_procfile(procfile="Procfile") def write_procfile(procfile="Procfile")
File.open(procfile, "w") do |file| File.open(procfile, "w") do |file|
file.puts "alpha: ./alpha" file.puts "alpha: ./alpha"
file.puts "\n"
file.puts "bravo: ./bravo" file.puts "bravo: ./bravo"
end end
File.expand_path(procfile) File.expand_path(procfile)
@@ -52,7 +53,7 @@ def example_export_file(filename)
data data
end end
Rspec.configure do |config| RSpec.configure do |config|
config.color_enabled = true config.color_enabled = true
config.include FakeFS::SpecHelpers config.include FakeFS::SpecHelpers
config.mock_with :rr config.mock_with :rr