fragrant 0.0.1

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.
Files changed (39) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +6 -0
  3. data/bin/fragrant +14 -0
  4. data/lib/fragrant/address_manager.rb +80 -0
  5. data/lib/fragrant/vagrantfile_generator.rb +64 -0
  6. data/lib/fragrant.rb +323 -0
  7. data/spec/fragrant/environments_spec.rb +18 -0
  8. data/spec/fragrant/vagrantfile_generator_spec.rb +40 -0
  9. data/spec/fragrant/vms_spec.rb +26 -0
  10. data/spec/spec_helper.rb +15 -0
  11. data/vendor/grape/LICENSE +20 -0
  12. data/vendor/grape/lib/grape/api.rb +420 -0
  13. data/vendor/grape/lib/grape/cookies.rb +41 -0
  14. data/vendor/grape/lib/grape/endpoint.rb +377 -0
  15. data/vendor/grape/lib/grape/entity.rb +378 -0
  16. data/vendor/grape/lib/grape/exceptions/base.rb +17 -0
  17. data/vendor/grape/lib/grape/exceptions/validation_error.rb +10 -0
  18. data/vendor/grape/lib/grape/middleware/auth/basic.rb +30 -0
  19. data/vendor/grape/lib/grape/middleware/auth/digest.rb +30 -0
  20. data/vendor/grape/lib/grape/middleware/auth/oauth2.rb +72 -0
  21. data/vendor/grape/lib/grape/middleware/base.rb +154 -0
  22. data/vendor/grape/lib/grape/middleware/error.rb +87 -0
  23. data/vendor/grape/lib/grape/middleware/filter.rb +17 -0
  24. data/vendor/grape/lib/grape/middleware/formatter.rb +81 -0
  25. data/vendor/grape/lib/grape/middleware/prefixer.rb +21 -0
  26. data/vendor/grape/lib/grape/middleware/versioner/header.rb +59 -0
  27. data/vendor/grape/lib/grape/middleware/versioner/param.rb +44 -0
  28. data/vendor/grape/lib/grape/middleware/versioner/path.rb +42 -0
  29. data/vendor/grape/lib/grape/middleware/versioner.rb +29 -0
  30. data/vendor/grape/lib/grape/route.rb +23 -0
  31. data/vendor/grape/lib/grape/util/deep_merge.rb +23 -0
  32. data/vendor/grape/lib/grape/util/hash_stack.rb +100 -0
  33. data/vendor/grape/lib/grape/validations/coerce.rb +61 -0
  34. data/vendor/grape/lib/grape/validations/presence.rb +11 -0
  35. data/vendor/grape/lib/grape/validations/regexp.rb +13 -0
  36. data/vendor/grape/lib/grape/validations.rb +192 -0
  37. data/vendor/grape/lib/grape/version.rb +3 -0
  38. data/vendor/grape/lib/grape.rb +44 -0
  39. metadata +216 -0
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2012 Matt Whiteley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,6 @@
1
+ * Top notes
2
+ - Wrap Vagrant actions in HTTP so we can execute from VMs
3
+ * Middle notes
4
+ - Quick
5
+ * Base notes
6
+ - Dirty
data/bin/fragrant ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ lib_dir = File.expand_path("../../lib", __FILE__)
5
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
6
+ require 'fragrant'
7
+ require 'rack/handler/puma'
8
+ require 'vegas'
9
+
10
+ app = Fragrant::Frontend.new
11
+ def app.server
12
+ "puma"
13
+ end
14
+ Vegas::Runner.new(app, 'fragrant', {:skip_launch => true, :app_dir => Fragrant.env_dir})
@@ -0,0 +1,80 @@
1
+ require 'ipaddr'
2
+ require 'json'
3
+
4
+ module Fragrant
5
+ AddressRangeExhausted = Class.new(StandardError)
6
+
7
+ class AddressManager
8
+ attr_accessor :data_location, :allocated_addresses
9
+ attr_accessor :address_range, :address_map
10
+
11
+ def initialize(data_location, address_range)
12
+ self.data_location = data_location
13
+ self.address_range = address_range
14
+ self.address_map = {}
15
+ self.allocated_addresses = []
16
+ load_address_data
17
+ end
18
+
19
+ def load_address_data
20
+ return unless File.exist?(data_location)
21
+ unless File.writable?(data_location)
22
+ raise "Unable to access IP address config file at #{data_location}"
23
+ end
24
+ File.open(data_location, 'rb') do |f|
25
+ data = JSON.parse(f.read)
26
+ self.address_range = data['address_range']
27
+ self.address_map = data['address_map']
28
+ self.allocated_addresses = data['allocated_addresses']
29
+ end
30
+ end
31
+
32
+ def address_data
33
+ {:address_range => address_range,
34
+ :allocated_addresses => allocated_addresses,
35
+ :address_map => address_map}
36
+ end
37
+
38
+ def first_available_address
39
+ ip = IPAddr.new(address_range).to_range.detect do |ip|
40
+ !allocated_addresses.include?(ip.to_s)
41
+ end
42
+ return ip.to_s if ip
43
+ raise AddressRangeExhausted, "No more addresses available in range #{address_range}"
44
+ end
45
+
46
+ def persist
47
+ dir = File.dirname(data_location)
48
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
49
+ File.open(data_location, 'wb') do |f|
50
+ f.write(address_data.to_json)
51
+ end
52
+ end
53
+
54
+ def claim_address(environment_id)
55
+ if address_map.key?(environment_id)
56
+ raise "#{environment_id} already has an address"
57
+ end
58
+ address = first_available_address
59
+ address_map[environment_id] = address
60
+ allocated_addresses << address
61
+ persist
62
+ address
63
+ end
64
+
65
+ def release_address(environment_id, ip)
66
+ unless address_map.key?(environment_id)
67
+ raise "No addresses registered to environment #{environment_id}"
68
+ end
69
+
70
+ address = address_map[environment_id]
71
+ unless address == ip
72
+ raise "IP #{ip} is not currently assigned to environment #{environment_id} (#{address} is)"
73
+ end
74
+
75
+ address_map.delete environment_id
76
+ allocated_addresses.delete ip
77
+ persist
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,64 @@
1
+ require 'fileutils'
2
+ require 'erb'
3
+
4
+ module Fragrant
5
+ class VagrantfileGenerator
6
+
7
+ attr_accessor :addresses
8
+
9
+ def self.template_path
10
+ File.expand_path('../../templates/Vagrantfile', __FILE__)
11
+ end
12
+
13
+ def initialize(target_directory, opts={})
14
+ @target_directory = target_directory
15
+ @scripts = opts[:scripts] || []
16
+ @addresses = opts[:addresses] || []
17
+ @box_name = opts[:box_name]
18
+ @box_url = opts[:box_url]
19
+ @contents = opts[:contents]
20
+ end
21
+
22
+ def box_name
23
+ @box_name || "precise32"
24
+ end
25
+
26
+ def box_url
27
+ @box_url || "http://files.vagrantup.com/precise32.box"
28
+ end
29
+
30
+ def add_script(contents)
31
+ raise "body set, adding a script not supported" if @contents
32
+ @scripts << contents
33
+ true
34
+ end
35
+
36
+ def writeable_scripts
37
+ retval = {}
38
+ @scripts.each_with_index do |contents, i|
39
+ retval[sprintf("script%03d", i + 1)] = contents
40
+ end
41
+ retval
42
+ end
43
+
44
+ def write
45
+ FileUtils.mkdir_p(@target_directory)
46
+
47
+ writeable_scripts.each do |filename, script_body|
48
+ File.open(File.join(@target_directory, filename), 'w') do |f|
49
+ f.print script_body
50
+ f.chmod 0755
51
+ end
52
+ end
53
+
54
+ @contents ||= Vagrant::Util::TemplateRenderer.render(self.class.template_path,
55
+ :box_name => box_name,
56
+ :box_url => box_url,
57
+ :provision_script_paths => writeable_scripts.keys.sort,
58
+ :addresses => addresses)
59
+ File.open(File.join(@target_directory, 'Vagrantfile'), 'w') { |f| f.print @contents }
60
+
61
+ true
62
+ end
63
+ end
64
+ end
data/lib/fragrant.rb ADDED
@@ -0,0 +1,323 @@
1
+ vendor_dir = File.expand_path('../../vendor', __FILE__)
2
+ $LOAD_PATH.unshift File.join(vendor_dir, 'grape', 'lib')
3
+ require 'grape'
4
+ require 'thread'
5
+ require 'fileutils'
6
+ require 'uuid'
7
+ require 'vagrant'
8
+ require 'fragrant/vagrantfile_generator'
9
+ require 'fragrant/address_manager'
10
+
11
+ module Fragrant
12
+ def self.env_dir
13
+ @env_dir ||= begin
14
+ dir = ENV["FRAGRANT_ENV_DIR"] || File.expand_path("~/.fragrant")
15
+ FileUtils.mkdir_p(dir)
16
+ dir
17
+ end
18
+ end
19
+
20
+ def self.address_manager
21
+ data_location = File.join(Fragrant.env_dir, "addresses.json")
22
+ range = ENV["FRAGRANT_IP_RANGE"] || "172.24.24.128/25"
23
+ @address_manager ||= AddressManager.new(data_location, range)
24
+ end
25
+
26
+ def self.add_task(task)
27
+ background_worker
28
+ tasks.push(task)
29
+ end
30
+
31
+ # Tasks are two-element Arrays of a machine id and a set of vagrant args
32
+ def self.tasks
33
+ @tasks ||= Queue.new
34
+ end
35
+
36
+ def self.create_worker_thread
37
+ thread = Thread.new do
38
+ Thread.current.abort_on_exception = true
39
+ until Thread.current[:shutdown] do
40
+ unless Fragrant.tasks.empty?
41
+ task = Fragrant.tasks.pop
42
+ env = Vagrant::Environment.new({ :cwd => File.join(env_dir, task[:id]) })
43
+ env.cli(task[:args])
44
+ end
45
+ end
46
+ end
47
+
48
+ at_exit do
49
+ $stderr.puts "Waiting for any running Vagrant tasks to complete."
50
+ thread[:shutdown] = true
51
+ thread.join
52
+ end
53
+ thread
54
+ end
55
+
56
+ def self.background_worker
57
+ @background_worker ||= create_worker_thread
58
+ end
59
+
60
+ class Frontend < Grape::API
61
+ version 'v1', :using => :header, :vendor => 'fragrant'
62
+ format :json
63
+
64
+ ENV_REGEX = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/
65
+
66
+ rescue_from :all do |e|
67
+ rack_response({ :message => "Encountered exception: #{e}", :backtrace => e.backtrace }, 500, {"Content-Type" => "application/json"})
68
+ end
69
+
70
+ helpers do
71
+ def add_task(task)
72
+ Fragrant.add_task(task)
73
+ end
74
+
75
+ def box_name
76
+ params[:box_name] || 'precise32'
77
+ end
78
+
79
+ def box_url
80
+ params[:box_url] || "http://files.vagrantup.com/precise32.box"
81
+ end
82
+
83
+ def user_script
84
+ params[:user_data_script]
85
+ end
86
+
87
+ def env_dir
88
+ Fragrant.env_dir
89
+ end
90
+
91
+ def env_glob
92
+ Dir.entries(env_dir).select do |d|
93
+ next if d.start_with?('.')
94
+ File.exists?(File.join(env_dir, d, 'Vagrantfile'))
95
+ end
96
+ end
97
+
98
+ def env_rand
99
+ UUID.generate
100
+ end
101
+
102
+ def v_action
103
+ route.route_path.split(/[\/\(]/)[2]
104
+ end
105
+
106
+ def v_env(id = params[:id])
107
+ Vagrant::Environment.new({ :cwd => File.join(env_dir, id) })
108
+ end
109
+
110
+ def allocate_address(env_id)
111
+ Fragrant.address_manager.claim_address(env_id)
112
+ end
113
+
114
+ def v_file(env_id, directory, contents = nil)
115
+ addresses = [allocate_address(env_id)] unless contents
116
+ VagrantfileGenerator.new(directory, :box_name => box_name,
117
+ :box_url => box_url,
118
+ :scripts => Array(user_script),
119
+ :addresses => addresses,
120
+ :contents => contents).write
121
+ Array(addresses)
122
+ end
123
+
124
+ def make_machine_dir(machine_id)
125
+ machine_dir = File.join(env_dir, machine_id)
126
+ begin
127
+ Dir.mkdir(machine_dir, 0755)
128
+ rescue Errno::EEXIST
129
+ error!({ "error" => "#{machine_dir} already exists!" }, 409)
130
+ end
131
+ machine_dir
132
+ end
133
+ end
134
+
135
+ get do
136
+ {}
137
+ end
138
+
139
+ resource :environments do
140
+
141
+ desc "Destroys a Vagrant environment"
142
+ params do
143
+ requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
144
+ optional :vm_name, :desc => 'single vm to act on'
145
+ end
146
+ delete '/destroy/:id' do
147
+ args = [v_action, params[:vm_name], '--force']
148
+ v_env.cli(args.compact)
149
+ {:id => params[:id]}
150
+ end
151
+
152
+ desc "Lists Vagrant environments"
153
+ get :list do
154
+ env_glob
155
+ end
156
+
157
+ desc "Halts a Vagrant environment"
158
+ params do
159
+ requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
160
+ optional :force, :desc => 'Force shut down (equivalent of pulling power)'
161
+ optional :vm_name, :desc => 'single vm to act on'
162
+ end
163
+ post '/halt/:id' do
164
+ force = params[:force] == true ? '--force' : nil
165
+ args = [v_action, params[:vm_name], force]
166
+ v_env.cli(args.compact)
167
+ {:id => params[:id]}
168
+ end
169
+
170
+ desc "Initializes a Vagrant environment"
171
+ params do
172
+ optional :vagrantfile, :desc => "Vagrant environment configuration", :type => String
173
+ end
174
+ post :init do
175
+ machine = env_rand
176
+ machine_dir = File.join(env_dir, machine)
177
+ begin
178
+ Dir.mkdir(machine_dir, 0755)
179
+ rescue Errno::EEXIST
180
+ error!({ "error" => "#{machine_dir} already exists!" }, 409)
181
+ end
182
+ if params[:vagrantfile].nil?
183
+ v_env(machine).cli(v_action, box_name, box_url)
184
+ else
185
+ File.open(File.join(machine_dir, 'Vagrantfile'), 'w') {|f| f.write(params[:vagrantfile])}
186
+ end
187
+ {:id => machine}
188
+ end
189
+
190
+ desc "Provisions a Vagrant environment"
191
+ params do
192
+ requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
193
+ optional :vm_name, :desc => 'single vm to act on'
194
+ end
195
+ post '/provision/:id' do
196
+ args = [v_action, params[:vm_name]]
197
+ v_env.cli(args.compact)
198
+ {:id => params[:id]}
199
+ end
200
+
201
+ desc "Initialize and provision an environment, returns the environment id"
202
+ params do
203
+ requires :box_name, :desc => 'Name for box, used to lookup already loaded box', :type => String, :regexp => /^[\w_-]+$/
204
+ optional :box_url, :desc => 'URL for box location, optional iff \'box_name\' exists', :type => String
205
+ optional :user_data_script, :desc => 'Script to invoke upon provisioning'
206
+ end
207
+ post :create do
208
+ machine_id = env_rand
209
+ machine_dir = make_machine_dir(machine_id)
210
+ addresses = v_file machine_id, machine_dir
211
+ args = 'up', '--provision'
212
+ add_task(:id => machine_id, :args => args)
213
+ {:id => machine_id, :ips => addresses}
214
+ end
215
+
216
+ desc "Initializes a Vagrant environment"
217
+ params do
218
+ optional :vagrantfile, :desc => "Vagrant environment configuration", :type => String
219
+ end
220
+ post :init do
221
+ machine_id = env_rand
222
+ machine_dir = make_machine_dir(machine_id)
223
+ if params[:vagrantfile].nil?
224
+ v_env(machine_id).cli(v_action, box_name, box_url)
225
+ else
226
+ v_file(machine_id, machine_dir, params[:vagrantfile])
227
+ end
228
+ {:id => machine_id}
229
+ end
230
+
231
+ desc "Purges a Vagrant environment"
232
+ params do
233
+ requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
234
+ end
235
+ post '/purge/:id' do
236
+ if v_env.vms.all? {|vm| vm.last.state == 'not_created'}
237
+ machine_dir = File.join(env_dir, params[:id])
238
+ FileUtils.remove_entry_secure(machine_dir)
239
+ else
240
+ error!({ "error" => "Environment contains undestroyed machines!" }, 409)
241
+ end
242
+ {:id => params[:id]}
243
+ end
244
+
245
+ desc "Reloads a Vagrant environment"
246
+ params do
247
+ requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
248
+ optional :no_provision, :desc => 'disable provisioning'
249
+ optional :vm_name, :desc => 'single vm to act on'
250
+ end
251
+ post '/reload/:id' do
252
+ provision = params[:no_provision] == true ? '--no-provision' : '--provision'
253
+ args = [v_action, params[:vm_name], provision]
254
+ v_env.cli(args.compact)
255
+ {:id => params[:id]}
256
+ end
257
+
258
+ desc "Resumes a Vagrant environment"
259
+ params do
260
+ requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
261
+ optional :vm_name, :desc => 'single vm to act on'
262
+ end
263
+ post '/resume/:id' do
264
+ args = [v_action, params[:vm_name]]
265
+ v_env.cli(args.compact)
266
+ {:id => params[:id]}
267
+ end
268
+
269
+ desc "Prints the status of a Vagrant environment"
270
+ params do
271
+ requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
272
+ end
273
+ get '/status/:id' do
274
+ state = {}
275
+ v_env.vms.each do |vm|
276
+ state[vm.first] = vm.last.state
277
+ end
278
+ {:status => state}
279
+ end
280
+
281
+ desc "Suspends a Vagrant environment"
282
+ params do
283
+ requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
284
+ optional :vm_name, :desc => 'single vm to act on'
285
+ end
286
+ post '/suspend/:id' do
287
+ args = [v_action, params[:vm_name]]
288
+ v_env.cli(args.compact)
289
+ {:id => params[:id]}
290
+ end
291
+
292
+ desc "Boots a Vagrant environment"
293
+ params do
294
+ requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
295
+ optional :no_provision, :desc => 'disable provisioning'
296
+ optional :vm_name, :desc => 'single vm to act on'
297
+ end
298
+ post '/up/:id' do
299
+ provision = params[:no_provision] == true ? '--no-provision' : '--provision'
300
+ args = [v_action, params[:vm_name], provision]
301
+ v_env.cli(args.compact)
302
+ {:id => params[:id]}
303
+ end
304
+
305
+ end
306
+
307
+ resource :vms do
308
+
309
+ desc "Lists registered virtual machines"
310
+ get :registered do
311
+ out = %x{VBoxManage list vms}
312
+ {:vms => out.split('\n')}
313
+ end
314
+
315
+ desc "Lists running virtual machines"
316
+ get :running do
317
+ out = %x{VBoxManage list runningvms}
318
+ {:vms => out.split('\n')}
319
+ end
320
+
321
+ end
322
+ end
323
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fragrant do
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ Fragrant::Frontend
8
+ end
9
+
10
+ describe "GET /environments/list" do
11
+ it "returns an array of Vagrant environments" do
12
+ get "/environments/list"
13
+ last_response.status.should == 200
14
+ JSON.parse(last_response.body).should be_kind_of(Array)
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+ require 'tempfile'
4
+
5
+ describe Fragrant::VagrantfileGenerator do
6
+ before do
7
+ @original_pwd = Dir.pwd
8
+ @working_directory = Dir.mktmpdir('fragrant-')
9
+ Dir.chdir @working_directory
10
+ end
11
+
12
+ after do
13
+ Dir.chdir @original_pwd
14
+ FileUtils.rm_rf @working_directory
15
+ end
16
+
17
+ it 'should create a Vagrantfile' do
18
+ v = Fragrant::VagrantfileGenerator.new(Dir.pwd)
19
+ v.write
20
+ File.exist?(File.join(Dir.pwd, 'Vagrantfile')).should be_true
21
+ end
22
+
23
+ describe 'with a provisioning script passed as an option' do
24
+ before do
25
+ v = Fragrant::VagrantfileGenerator.new(Dir.pwd)
26
+ @custom_script = "#!/not/really/bin/bash\nDo this thing!"
27
+ v.add_script @custom_script
28
+ v.write
29
+ end
30
+
31
+ it 'should write the provisioning script as a separate file' do
32
+ File.read(File.join(Dir.pwd, 'script001')).should == @custom_script
33
+ end
34
+
35
+ it 'should refer to the provisioning script in the Vagrantfile' do
36
+ vagrantfile = File.read(File.join(Dir.pwd, 'Vagrantfile'))
37
+ vagrantfile.split("\n").grep(/provision.*script001/).should_not be_empty
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fragrant do
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ Fragrant::Frontend
8
+ end
9
+
10
+ describe "GET /vms/registered" do
11
+ it "returns an array of registered vms" do
12
+ get "/vms/registered"
13
+ last_response.status.should == 200
14
+ JSON.parse(last_response.body).should be_kind_of(Array)
15
+ end
16
+ end
17
+
18
+ describe "GET /vms/running" do
19
+ it "returns an array of running vms" do
20
+ get "/vms/running"
21
+ last_response.status.should == 200
22
+ JSON.parse(last_response.body).should be_kind_of(Array)
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,15 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ Bundler.setup :default, :test
6
+ require 'fragrant'
7
+ require 'rack/test'
8
+ require 'fileutils'
9
+
10
+ RSpec.configure do |config|
11
+ config.include Rack::Test::Methods
12
+ config.before do
13
+ FileUtils.mkdir_p(Fragrant.env_dir)
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Michael Bleigh and Intridea, Inc.
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.