af 0.3.22 → 0.5.0.beta.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 (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