af 0.3.22 → 0.5.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +14 -6
  2. data/LICENSE +1277 -24
  3. data/Rakefile +24 -87
  4. data/bin/af +7 -2
  5. data/lib/af/version.rb +3 -0
  6. data/lib/vmc.rb +7 -2
  7. data/lib/vmc/cli.rb +475 -0
  8. data/lib/vmc/cli/app/app.rb +45 -0
  9. data/lib/vmc/cli/app/apps.rb +105 -0
  10. data/lib/vmc/cli/app/base.rb +82 -0
  11. data/lib/vmc/cli/app/crashes.rb +46 -0
  12. data/lib/vmc/cli/app/delete.rb +95 -0
  13. data/lib/vmc/cli/app/deprecated.rb +11 -0
  14. data/lib/vmc/cli/app/env.rb +78 -0
  15. data/lib/vmc/cli/app/files.rb +137 -0
  16. data/lib/vmc/cli/app/health.rb +26 -0
  17. data/lib/vmc/cli/app/instances.rb +53 -0
  18. data/lib/vmc/cli/app/logs.rb +76 -0
  19. data/lib/vmc/cli/app/push.rb +107 -0
  20. data/lib/vmc/cli/app/push/create.rb +150 -0
  21. data/lib/vmc/cli/app/push/interactions.rb +100 -0
  22. data/lib/vmc/cli/app/push/sync.rb +64 -0
  23. data/lib/vmc/cli/app/rename.rb +39 -0
  24. data/lib/vmc/cli/app/restart.rb +20 -0
  25. data/lib/vmc/cli/app/scale.rb +71 -0
  26. data/lib/vmc/cli/app/start.rb +93 -0
  27. data/lib/vmc/cli/app/stats.rb +67 -0
  28. data/lib/vmc/cli/app/stop.rb +27 -0
  29. data/lib/vmc/cli/domain/base.rb +12 -0
  30. data/lib/vmc/cli/domain/domains.rb +40 -0
  31. data/lib/vmc/cli/domain/map.rb +55 -0
  32. data/lib/vmc/cli/domain/unmap.rb +56 -0
  33. data/lib/vmc/cli/help.rb +16 -0
  34. data/lib/vmc/cli/interactive.rb +105 -0
  35. data/lib/vmc/cli/organization/base.rb +14 -0
  36. data/lib/vmc/cli/organization/create.rb +32 -0
  37. data/lib/vmc/cli/organization/delete.rb +73 -0
  38. data/lib/vmc/cli/organization/org.rb +45 -0
  39. data/lib/vmc/cli/organization/orgs.rb +35 -0
  40. data/lib/vmc/cli/organization/rename.rb +36 -0
  41. data/lib/vmc/cli/route/base.rb +12 -0
  42. data/lib/vmc/cli/route/map.rb +80 -0
  43. data/lib/vmc/cli/route/routes.rb +26 -0
  44. data/lib/vmc/cli/route/unmap.rb +94 -0
  45. data/lib/vmc/cli/service/base.rb +8 -0
  46. data/lib/vmc/cli/service/bind.rb +44 -0
  47. data/lib/vmc/cli/service/create.rb +126 -0
  48. data/lib/vmc/cli/service/delete.rb +86 -0
  49. data/lib/vmc/cli/service/rename.rb +35 -0
  50. data/lib/vmc/cli/service/service.rb +42 -0
  51. data/lib/vmc/cli/service/services.rb +115 -0
  52. data/lib/vmc/cli/service/unbind.rb +38 -0
  53. data/lib/vmc/cli/space/base.rb +21 -0
  54. data/lib/vmc/cli/space/create.rb +56 -0
  55. data/lib/vmc/cli/space/delete.rb +95 -0
  56. data/lib/vmc/cli/space/rename.rb +39 -0
  57. data/lib/vmc/cli/space/space.rb +64 -0
  58. data/lib/vmc/cli/space/spaces.rb +55 -0
  59. data/lib/vmc/cli/space/take.rb +16 -0
  60. data/lib/vmc/cli/start/base.rb +80 -0
  61. data/lib/vmc/cli/start/colors.rb +13 -0
  62. data/lib/vmc/cli/start/info.rb +122 -0
  63. data/lib/vmc/cli/start/login.rb +92 -0
  64. data/lib/vmc/cli/start/logout.rb +13 -0
  65. data/lib/vmc/cli/start/target.rb +64 -0
  66. data/lib/vmc/cli/start/target_interactions.rb +37 -0
  67. data/lib/vmc/cli/start/targets.rb +16 -0
  68. data/lib/vmc/cli/user/base.rb +29 -0
  69. data/lib/vmc/cli/user/create.rb +39 -0
  70. data/lib/vmc/cli/user/delete.rb +25 -0
  71. data/lib/vmc/cli/user/passwd.rb +50 -0
  72. data/lib/vmc/cli/user/register.rb +42 -0
  73. data/lib/vmc/cli/user/users.rb +32 -0
  74. data/lib/vmc/constants.rb +13 -0
  75. data/lib/vmc/detect.rb +134 -0
  76. data/lib/vmc/errors.rb +17 -0
  77. data/lib/vmc/plugin.rb +56 -0
  78. data/lib/vmc/spacing.rb +89 -0
  79. data/lib/vmc/spec_helper.rb +1 -0
  80. data/lib/vmc/test_support.rb +4 -0
  81. data/lib/vmc/test_support/command_helper.rb +32 -0
  82. data/lib/vmc/test_support/common_input_examples.rb +14 -0
  83. data/lib/vmc/test_support/fake_home_dir.rb +16 -0
  84. data/lib/vmc/test_support/interact_helper.rb +29 -0
  85. data/lib/vmc/version.rb +3 -0
  86. data/spec/assets/hello-sinatra/Gemfile +3 -0
  87. data/spec/assets/hello-sinatra/main.rb +6 -0
  88. data/spec/features/new_user_flow_spec.rb +71 -0
  89. data/spec/spec_helper.rb +63 -0
  90. data/spec/vmc/cli/app/base_spec.rb +17 -0
  91. data/spec/vmc/cli/app/delete_spec.rb +188 -0
  92. data/spec/vmc/cli/app/instances_spec.rb +65 -0
  93. data/spec/vmc/cli/app/push/create_spec.rb +571 -0
  94. data/spec/vmc/cli/app/push_spec.rb +369 -0
  95. data/spec/vmc/cli/app/rename_spec.rb +104 -0
  96. data/spec/vmc/cli/app/scale_spec.rb +81 -0
  97. data/spec/vmc/cli/app/stats_spec.rb +62 -0
  98. data/spec/vmc/cli/domain/map_spec.rb +140 -0
  99. data/spec/vmc/cli/domain/unmap_spec.rb +73 -0
  100. data/spec/vmc/cli/organization/orgs_spec.rb +108 -0
  101. data/spec/vmc/cli/organization/rename_spec.rb +113 -0
  102. data/spec/vmc/cli/route/map_spec.rb +138 -0
  103. data/spec/vmc/cli/route/unmap_spec.rb +215 -0
  104. data/spec/vmc/cli/service/bind_spec.rb +25 -0
  105. data/spec/vmc/cli/service/delete_spec.rb +22 -0
  106. data/spec/vmc/cli/service/rename_spec.rb +105 -0
  107. data/spec/vmc/cli/service/service_spec.rb +23 -0
  108. data/spec/vmc/cli/service/unbind_spec.rb +25 -0
  109. data/spec/vmc/cli/space/rename_spec.rb +102 -0
  110. data/spec/vmc/cli/space/spaces_spec.rb +104 -0
  111. data/spec/vmc/cli/start/info_spec.rb +153 -0
  112. data/spec/vmc/cli/start/login_spec.rb +71 -0
  113. data/spec/vmc/cli/user/create_spec.rb +54 -0
  114. data/spec/vmc/cli/user/passwd_spec.rb +102 -0
  115. data/spec/vmc/cli/user/register_spec.rb +148 -0
  116. data/spec/vmc/cli_spec.rb +448 -0
  117. data/spec/vmc/detect_spec.rb +54 -0
  118. metadata +231 -124
  119. data/README.md +0 -155
  120. data/caldecott_helper/Gemfile +0 -10
  121. data/caldecott_helper/Gemfile.lock +0 -48
  122. data/caldecott_helper/server.rb +0 -43
  123. data/config/clients.yml +0 -17
  124. data/config/micro/offline.conf +0 -2
  125. data/config/micro/paths.yml +0 -22
  126. data/config/micro/refresh_ip.rb +0 -20
  127. data/lib/cli.rb +0 -48
  128. data/lib/cli/commands/admin.rb +0 -81
  129. data/lib/cli/commands/apps.rb +0 -1358
  130. data/lib/cli/commands/base.rb +0 -233
  131. data/lib/cli/commands/manifest.rb +0 -56
  132. data/lib/cli/commands/micro.rb +0 -115
  133. data/lib/cli/commands/misc.rb +0 -147
  134. data/lib/cli/commands/services.rb +0 -217
  135. data/lib/cli/commands/user.rb +0 -70
  136. data/lib/cli/config.rb +0 -176
  137. data/lib/cli/console_helper.rb +0 -163
  138. data/lib/cli/core_ext.rb +0 -122
  139. data/lib/cli/errors.rb +0 -19
  140. data/lib/cli/file_helper.rb +0 -123
  141. data/lib/cli/frameworks.rb +0 -265
  142. data/lib/cli/manifest_helper.rb +0 -316
  143. data/lib/cli/runner.rb +0 -633
  144. data/lib/cli/services_helper.rb +0 -104
  145. data/lib/cli/tunnel_helper.rb +0 -336
  146. data/lib/cli/usage.rb +0 -129
  147. data/lib/cli/version.rb +0 -7
  148. data/lib/cli/zip_util.rb +0 -102
  149. data/lib/vmc/client.rb +0 -574
  150. data/lib/vmc/const.rb +0 -27
  151. data/lib/vmc/micro.rb +0 -56
  152. data/lib/vmc/micro/switcher/base.rb +0 -97
  153. data/lib/vmc/micro/switcher/darwin.rb +0 -19
  154. data/lib/vmc/micro/switcher/dummy.rb +0 -15
  155. data/lib/vmc/micro/switcher/linux.rb +0 -16
  156. data/lib/vmc/micro/switcher/windows.rb +0 -31
  157. data/lib/vmc/micro/vmrun.rb +0 -158
@@ -1,217 +0,0 @@
1
- require "uuidtools"
2
-
3
- module VMC::Cli::Command
4
-
5
- class Services < Base
6
- include VMC::Cli::ServicesHelper
7
- include VMC::Cli::TunnelHelper
8
-
9
- def services
10
- ss = client.services_info
11
- ps = client.services
12
- ps.sort! {|a, b| a[:name] <=> b[:name] }
13
-
14
- if @options[:json]
15
- services = { :system => ss, :provisioned => ps }
16
- return display JSON.pretty_generate(services)
17
- end
18
- display_system_services(ss)
19
- display_provisioned_services(ps)
20
- end
21
-
22
- def create_service(service=nil, name=nil, appname=nil)
23
-
24
- unless no_prompt || service
25
- services = client.services_info
26
- err 'No services available to provision' if services.empty?
27
- service = ask(
28
- "Which service would you like to provision?",
29
- { :indexed => true,
30
- :choices =>
31
- services.values.collect { |type|
32
- type.keys.collect(&:to_s)
33
- }.flatten
34
- }
35
- )
36
- end
37
- name = @options[:name] unless name
38
- unless name
39
- name = random_service_name(service)
40
- picked_name = true
41
- end
42
-
43
- if client.infra_supported?
44
- unless no_prompt || @options[:infra]
45
- @options[:infra] = client.infra_name_for_description(
46
- ask("Select Infrastructure",
47
- :indexed => true, :choices => client.infra_descriptions))
48
- end
49
- end
50
-
51
- create_service_banner(service, name, picked_name, @options[:infra])
52
- appname = @options[:bind] unless appname
53
- bind_service_banner(name, appname) if appname
54
- end
55
-
56
- def delete_service(service=nil)
57
- unless no_prompt || service
58
- user_services = client.services
59
- err 'No services available to delete' if user_services.empty?
60
- service = ask(
61
- "Which service would you like to delete?",
62
- { :indexed => true,
63
- :choices => user_services.collect { |s| s[:name] }
64
- }
65
- )
66
- end
67
- err "Service name required." unless service
68
- display "Deleting service [#{service}]: ", false
69
- client.delete_service(service)
70
- display 'OK'.green
71
- end
72
-
73
- def bind_service(service, appname)
74
- bind_service_banner(service, appname)
75
- end
76
-
77
- def unbind_service(service, appname)
78
- unbind_service_banner(service, appname)
79
- end
80
-
81
- def clone_services(src_app, dest_app)
82
- begin
83
- src = client.app_info(src_app)
84
- dest = client.app_info(dest_app)
85
- rescue
86
- end
87
-
88
- err "Application '#{src_app}' does not exist" unless src
89
- err "Application '#{dest_app}' does not exist" unless dest
90
-
91
- services = src[:services]
92
- err 'No services to clone' unless services && !services.empty?
93
- services.each { |service| bind_service_banner(service, dest_app, false) }
94
- check_app_for_restart(dest_app)
95
- end
96
-
97
- def export_service(service)
98
- display "Exporting data from '#{service}': ", false
99
- export_info = client.export_service(service)
100
- if export_info
101
- display 'OK'.green
102
- puts export_info[:uri]
103
- else
104
- err "Export data from '#{service}': failed"
105
- end
106
- end
107
-
108
- def import_service(service,url)
109
- display "Importing data into '#{service}': ", false
110
- import_info = client.import_service(service,url)
111
- if import_info
112
- display 'OK'.green
113
- else
114
- err "Import data into '#{service}' failed"
115
- end
116
- end
117
-
118
- def tunnel(service=nil, client_name=nil)
119
- unless defined? Caldecott
120
- display "To use `af tunnel', you must first install Caldecott:"
121
- display ""
122
- display "\tgem install caldecott"
123
- display ""
124
- display "Note that you'll need a C compiler. If you're on OS X, Xcode"
125
- display "will provide one. If you're on Windows, try DevKit."
126
- display ""
127
- display "This manual step will be removed in the future."
128
- display ""
129
- err "Caldecott is not installed."
130
- end
131
-
132
- ps = client.services
133
- err "No services available to tunnel to" if ps.empty?
134
-
135
- unless service
136
- choices = ps.collect { |s| s[:name] }.sort
137
- service = ask(
138
- "Which service to tunnel to?",
139
- :choices => choices,
140
- :indexed => true
141
- )
142
- end
143
-
144
- info = ps.select { |s| s[:name] == service }.first
145
-
146
- err "Unknown service '#{service}'" unless info
147
-
148
- port = pick_tunnel_port(@options[:port] || 10000)
149
-
150
- raise VMC::Client::AuthError unless client.logged_in?
151
-
152
- infra_name = nil
153
- if client.infra_supported?
154
- infra_name = info[:infra] ? info[:infra][:name] : default_infra
155
- err "Infra '#{infra_name}' is not valid" unless client.infra_valid?(infra_name)
156
- end
157
-
158
- if not tunnel_pushed?(infra_name)
159
- display "Deploying tunnel application '#{tunnel_appname(infra_name)}'."
160
- auth = UUIDTools::UUID.random_create.to_s
161
- push_caldecott(auth,infra_name)
162
- bind_service_banner(service, tunnel_appname(infra_name), false)
163
- start_caldecott(infra_name)
164
- else
165
- auth = tunnel_auth(infra_name)
166
- end
167
-
168
- if not tunnel_healthy?(auth,infra_name)
169
- display "Redeploying tunnel application '#{tunnel_appname(infra_name)}'."
170
-
171
- # We don't expect caldecott not to be running, so take the
172
- # most aggressive restart method.. delete/re-push
173
- client.delete_app(tunnel_appname(infra_name))
174
- invalidate_tunnel_app_info(infra_name)
175
-
176
- push_caldecott(auth,infra_name)
177
- bind_service_banner(service, tunnel_appname(infra_name), false)
178
- start_caldecott(infra_name)
179
- end
180
-
181
- if not tunnel_bound?(service,infra_name)
182
- bind_service_banner(service, tunnel_appname(infra_name))
183
- end
184
-
185
- conn_info = tunnel_connection_info info[:vendor], service, auth, infra_name
186
- display_tunnel_connection_info(conn_info)
187
- display "Starting tunnel to #{service.bold} on port #{port.to_s.bold}."
188
- start_tunnel(port, conn_info, auth, infra_name)
189
-
190
- clients = get_clients_for(info[:vendor])
191
-
192
- if clients.empty?
193
- client_name ||= "none"
194
- else
195
- client_name ||= ask(
196
- "Which client would you like to start?",
197
- :choices => ["none"] + clients.keys,
198
- :indexed => true
199
- )
200
- end
201
-
202
- if client_name == "none"
203
- wait_for_tunnel_end
204
- else
205
- wait_for_tunnel_start(port)
206
- unless start_local_prog(clients, client_name, conn_info, port)
207
- err "'#{client_name}' execution failed; is it in your $PATH?"
208
- end
209
- end
210
- end
211
-
212
- def get_clients_for(type)
213
- conf = VMC::Cli::Config.clients
214
- conf[type] || {}
215
- end
216
- end
217
- end
@@ -1,70 +0,0 @@
1
- module VMC::Cli::Command
2
-
3
- class User < Base
4
-
5
- # Errors
6
- class InvalidLogin < VMC::Client::TargetError; end
7
-
8
- def info
9
- info = client_info
10
- username = info[:user] || 'N/A'
11
- return display JSON.pretty_generate([username]) if @options[:json]
12
- display "\n[#{username}]"
13
- end
14
-
15
- def login(email=nil)
16
- display "Attempting login to [#{target_url}]" if target_url
17
- begin
18
- email = @options[:email] unless email
19
- password = @options[:password]
20
- tries ||= 0
21
-
22
- unless no_prompt
23
- email ||= ask("Email")
24
- password ||= ask("Password", :echo => "*")
25
- end
26
-
27
- err "Need a valid email" unless email
28
- err "Need a password" unless password
29
- login_and_save_token(email, password)
30
- say "Successfully logged into [#{target_url}]".green
31
- rescue VMC::Client::TargetError
32
- if (tries += 1) < 3 && prompt_ok && !@options[:password]
33
- display "Problem with login, invalid account or password when attempting to login to '#{target_url}'".red
34
- retry
35
- end
36
- raise InvalidLogin, "Problem with login, invalid account or password when attempting to login to '#{target_url}'"
37
- end
38
- end
39
-
40
- def logout
41
- VMC::Cli::Config.remove_token_file
42
- say "Successfully logged out of [#{target_url}]".green
43
- end
44
-
45
- def change_password(password=nil)
46
- info = client_info
47
- email = info[:user]
48
- err "Need to be logged in to change password." unless email
49
- say "Changing password for '#{email}'\n"
50
- unless no_prompt
51
- password = ask "New Password", :echo => "*"
52
- password2 = ask "Verify Password", :echo => "*"
53
- err "Passwords did not match, try again" if password != password2
54
- end
55
- err "Password required" unless password
56
- err "Passwords may not contain braces" if password =~ /[{}]/
57
- client.change_password(password)
58
- say "\nSuccessfully changed password".green
59
- end
60
-
61
- private
62
-
63
- def login_and_save_token(email, password)
64
- token = client.login(email, password)
65
- VMC::Cli::Config.store_token(token, @options[:token_file])
66
- end
67
-
68
- end
69
-
70
- end
@@ -1,176 +0,0 @@
1
- require "yaml"
2
- require 'fileutils'
3
-
4
- require 'rubygems'
5
- require 'json/pure'
6
-
7
- module VMC::Cli
8
- class Config
9
-
10
- DEFAULT_TARGET = 'api.appfog.com'
11
-
12
- TARGET_FILE = '~/.af_target'
13
- TOKEN_FILE = '~/.af_token'
14
- INSTANCES_FILE = '~/.af_instances'
15
- ALIASES_FILE = '~/.af_aliases'
16
- CLIENTS_FILE = '~/.af_clients'
17
- MICRO_FILE = '~/.af_micro'
18
- CRASH_FILE = '~/.af_crash'
19
-
20
- STOCK_CLIENTS = File.expand_path("../../../config/clients.yml", __FILE__)
21
-
22
- class << self
23
- attr_accessor :colorize
24
- attr_accessor :output
25
- attr_accessor :trace
26
- attr_accessor :nozip
27
- attr_accessor :infra
28
-
29
- def target_url
30
- return @target_url if @target_url
31
- target_file = File.expand_path(TARGET_FILE)
32
- if File.exists? target_file
33
- @target_url = lock_and_read(target_file).strip
34
- else
35
- @target_url = DEFAULT_TARGET
36
- end
37
- @target_url = "https://#{@target_url}" unless /^https?/ =~ @target_url
38
- @target_url = @target_url.gsub(/\/+$/, '')
39
- @target_url
40
- end
41
-
42
- def base_of(url)
43
- url.sub(/^[^\.]+\./, "")
44
- end
45
-
46
- def store_target(target_host)
47
- target_file = File.expand_path(TARGET_FILE)
48
- lock_and_write(target_file, target_host)
49
- end
50
-
51
- def store_crash(log)
52
- crash_file = File.expand_path(CRASH_FILE)
53
- lock_and_write(crash_file, log)
54
- end
55
-
56
- def all_tokens(token_file_path=nil)
57
- token_file = File.expand_path(token_file_path || TOKEN_FILE)
58
- return nil unless File.exists? token_file
59
- contents = lock_and_read(token_file).strip
60
- JSON.parse(contents)
61
- end
62
-
63
- alias :targets :all_tokens
64
-
65
- def auth_token(token_file_path=nil)
66
- return @token if @token
67
- tokens = all_tokens(token_file_path)
68
- @token = tokens[target_url] if tokens
69
- end
70
-
71
- def remove_token_file
72
- FileUtils.rm_f(File.expand_path(TOKEN_FILE))
73
- end
74
-
75
- def store_token(token, token_file_path=nil)
76
- tokens = all_tokens(token_file_path) || {}
77
- tokens[target_url] = token
78
- token_file = File.expand_path(token_file_path || TOKEN_FILE)
79
- lock_and_write(token_file, tokens.to_json)
80
- end
81
-
82
- def instances
83
- instances_file = File.expand_path(INSTANCES_FILE)
84
- return nil unless File.exists? instances_file
85
- contents = lock_and_read(instances_file).strip
86
- JSON.parse(contents)
87
- end
88
-
89
- def store_instances(instances)
90
- instances_file = File.expand_path(INSTANCES_FILE)
91
- lock_and_write(instances_file, instances.to_json)
92
- end
93
-
94
- def aliases
95
- aliases_file = File.expand_path(ALIASES_FILE)
96
- # bacward compatible
97
- unless File.exists? aliases_file
98
- old_aliases_file = File.expand_path('~/.vmc-aliases')
99
- FileUtils.mv(old_aliases_file, aliases_file) if File.exists? old_aliases_file
100
- end
101
- aliases = YAML.load_file(aliases_file) rescue {}
102
- end
103
-
104
- def store_aliases(aliases)
105
- aliases_file = File.expand_path(ALIASES_FILE)
106
- File.open(aliases_file, 'wb') {|f| f.write(aliases.to_yaml)}
107
- end
108
-
109
- def micro
110
- micro_file = File.expand_path(MICRO_FILE)
111
- return {} unless File.exists? micro_file
112
- contents = lock_and_read(micro_file).strip
113
- JSON.parse(contents)
114
- end
115
-
116
- def store_micro(micro)
117
- micro_file = File.expand_path(MICRO_FILE)
118
- lock_and_write(micro_file, micro.to_json)
119
- end
120
-
121
- def deep_merge(a, b)
122
- merge = proc do |_, old, new|
123
- if new.is_a?(Hash) and old.is_a?(Hash)
124
- old.merge(new, &merge)
125
- else
126
- new
127
- end
128
- end
129
-
130
- a.merge(b, &merge)
131
- end
132
-
133
- def clients
134
- return @clients if @clients
135
-
136
- stock = YAML.load_file(STOCK_CLIENTS)
137
- clients = File.expand_path CLIENTS_FILE
138
- if File.exists? clients
139
- user = YAML.load_file(clients)
140
- @clients = deep_merge(stock, user)
141
- else
142
- @clients = stock
143
- end
144
- end
145
-
146
- def lock_and_read(file)
147
- File.open(file, File::RDONLY) {|f|
148
- if defined? JRUBY_VERSION
149
- f.flock(File::LOCK_SH)
150
- else
151
- f.flock(File::LOCK_EX)
152
- end
153
- contents = f.read
154
- f.flock(File::LOCK_UN)
155
- contents
156
- }
157
- end
158
-
159
- def lock_and_write(file, contents)
160
- File.open(file, File::RDWR | File::CREAT, 0600) {|f|
161
- f.flock(File::LOCK_EX)
162
- f.rewind
163
- f.puts contents
164
- f.flush
165
- f.truncate(f.pos)
166
- f.flock(File::LOCK_UN)
167
- }
168
- end
169
- end
170
-
171
- def initialize(work_dir = Dir.pwd)
172
- @work_dir = work_dir
173
- end
174
-
175
- end
176
- end