dump 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +8 -8
  2. data/.rubocop.yml +53 -0
  3. data/.rubocop_todo.yml +33 -0
  4. data/.travis.yml +12 -3
  5. data/README.markdown +8 -0
  6. data/dump.gemspec +4 -1
  7. data/lib/dump.rb +1 -0
  8. data/lib/dump/capistrano.rb +7 -1
  9. data/lib/dump/capistrano/v2.rb +331 -0
  10. data/lib/dump/railtie.rb +1 -0
  11. data/lib/dump_rake.rb +17 -8
  12. data/lib/dump_rake/archive_tar_minitar_fix.rb +3 -1
  13. data/lib/dump_rake/assets.rb +1 -0
  14. data/lib/dump_rake/continious_timeout.rb +17 -17
  15. data/lib/dump_rake/dump.rb +27 -22
  16. data/lib/dump_rake/dump_reader.rb +62 -63
  17. data/lib/dump_rake/dump_writer.rb +20 -21
  18. data/lib/dump_rake/env.rb +12 -10
  19. data/lib/dump_rake/env/filter.rb +3 -0
  20. data/lib/dump_rake/rails_root.rb +1 -0
  21. data/lib/dump_rake/table_manipulation.rb +23 -20
  22. data/lib/generators/assets_config/assets_config_generator.rb +2 -0
  23. data/lib/tasks/assets.rake +1 -2
  24. data/lib/tasks/dump.rake +5 -6
  25. data/recipes/dump.rb +1 -343
  26. data/script/update_readme +5 -3
  27. data/spec/cycle_spec.rb +33 -30
  28. data/spec/dummy_rails_app.rb +42 -0
  29. data/spec/lib/dump_rake/dump_reader_spec.rb +80 -92
  30. data/spec/lib/dump_rake/dump_spec.rb +56 -58
  31. data/spec/lib/dump_rake/dump_writer_spec.rb +42 -43
  32. data/spec/lib/dump_rake/env/filter_spec.rb +9 -9
  33. data/spec/lib/dump_rake/env_spec.rb +21 -21
  34. data/spec/lib/dump_rake/rails_root_spec.rb +7 -7
  35. data/spec/lib/dump_rake/table_manipulation_spec.rb +57 -63
  36. data/spec/lib/dump_rake_spec.rb +76 -76
  37. data/spec/recipes/dump_spec.rb +241 -217
  38. data/spec/spec_helper.rb +1 -42
  39. data/spec/tasks/assets_spec.rb +18 -18
  40. data/spec/tasks/dump_spec.rb +18 -18
  41. metadata +21 -2
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZGMzMDVmMTc4YzIyN2IyMjBhMTlhODM1NjdlMzIxMjYzNTc5M2EwNg==
4
+ MzE3NmQ3MTRjN2QwOWJlMGNkMTMzNWZjMDcxOTQ5ZGNjYzBkYzJhZg==
5
5
  data.tar.gz: !binary |-
6
- ZmEwODViYTNjMzcxOWM3N2E1YzY2NmQ5MDQ1YTJjM2RlMzFkZWZhZA==
6
+ NzFjNTA5MjgzOTEzMzczMjg4ZDYzYTg5NWVhZDRjN2Y3OTIzZWNiNg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MDZhMGNjY2JjODcwOGI1ZmFjMjJiYWIyYWJmMjkwYzVmZDE3NDU3N2ZhZDBi
10
- NTE1OWJmM2VjNjYxNDNiYTBmZTIxMzk1ZGU4MjMwNDkxMTJlM2YwNDcxNTE5
11
- YzI1YjY3ZWM3ZjY5M2Q3OTg3NjIzNWQ3YjAxOTg0ZTA4MDRkMDk=
9
+ MjkyOWRmMTZmMGVlNDNlZjUzYzAwMzdkZjAyNWE1MmIyZGQ2MzVmZTMxMjgy
10
+ MzY1OTBkZTA0ZWIxNTZlMmU1NTE5YTA3MDZkMjM3MjBjNzM3ZWViZGI1Y2U2
11
+ MDAwNTg4MmE2MzE5YzMwMGY3NDlkMWU2MjY0ZmQyMjkxZjE1OGM=
12
12
  data.tar.gz: !binary |-
13
- Yzc5NzZkYjk3MzlmMWNmYjk0NTYwMTM2ZTEyMWZlMWYzOWJhYTQxYjY5Yzlm
14
- OGJkZDRjOTAwOTBkMWM5ZWM2NzdkYjgwY2I2NWQ0NGM0OTUyNzU3ZmIzYTYx
15
- ZjYwNWNkMTVkZTg3ODhlZTg0ZjVlZjRlMmM0YjZmYWM0NzZmNjc=
13
+ ZTQ0ZmVmOWQ0YzVkNzcyMDdiYzQ4ZDI0YTY0YmYzNjczMjlmNDM3ZDBiZDdm
14
+ YzAzNWI4ODI0OWUzZTE1MGRlMmQ2NWUxYWI2ZWZiMjcyMzY5YzA1OTYwM2Qw
15
+ YzVmMDc5MzA2NzQ3OTc1NjM4MGYzYTE5ZTczMzc1ZDY1ZWY1ZGQ=
@@ -0,0 +1,53 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ Exclude:
5
+ - '*.gemspec'
6
+ - 'spec/dummy-*/**/*'
7
+
8
+ Lint/EndAlignment:
9
+ AlignWith: variable
10
+
11
+ Style/AccessModifierIndentation:
12
+ EnforcedStyle: outdent
13
+
14
+ Style/BracesAroundHashParameters:
15
+ Enabled: false
16
+
17
+ Style/CaseIndentation:
18
+ IndentWhenRelativeTo: end
19
+
20
+ Style/DotPosition:
21
+ EnforcedStyle: trailing
22
+
23
+ Style/DoubleNegation:
24
+ Enabled: false
25
+
26
+ Style/Encoding:
27
+ EnforcedStyle: when_needed
28
+
29
+ Style/HashSyntax:
30
+ EnforcedStyle: hash_rockets
31
+
32
+ Style/IfUnlessModifier:
33
+ MaxLineLength: 40
34
+
35
+ Style/IndentHash:
36
+ EnforcedStyle: consistent
37
+
38
+ Style/PercentLiteralDelimiters:
39
+ PreferredDelimiters:
40
+ '%w': '[]'
41
+ '%W': '[]'
42
+
43
+ Style/Semicolon:
44
+ AllowAsExpressionSeparator: true
45
+
46
+ Style/SpaceBeforeBlockBraces:
47
+ EnforcedStyle: no_space
48
+
49
+ Style/SpaceInsideHashLiteralBraces:
50
+ EnforcedStyle: no_space
51
+
52
+ Style/TrailingComma:
53
+ EnforcedStyleForMultiline: comma
@@ -0,0 +1,33 @@
1
+ # This configuration was generated by `rubocop --auto-gen-config`
2
+ # on 2014-12-08 02:47:19 +0100 using RuboCop version 0.27.1.
3
+ # The point is for the user to remove these configuration records
4
+ # one by one as the offenses are removed from the code base.
5
+ # Note that changes in the inspected code, or installation of new
6
+ # versions of RuboCop, may require this file to be generated again.
7
+
8
+ # Offense count: 19
9
+ Metrics/AbcSize:
10
+ Max: 45
11
+
12
+ # Offense count: 2
13
+ # Configuration parameters: CountComments.
14
+ Metrics/ClassLength:
15
+ Max: 226
16
+
17
+ # Offense count: 6
18
+ Metrics/CyclomaticComplexity:
19
+ Max: 15
20
+
21
+ # Offense count: 386
22
+ # Configuration parameters: AllowURI, URISchemes.
23
+ Metrics/LineLength:
24
+ Max: 209
25
+
26
+ # Offense count: 28
27
+ # Configuration parameters: CountComments.
28
+ Metrics/MethodLength:
29
+ Max: 47
30
+
31
+ # Offense count: 2
32
+ Metrics/PerceivedComplexity:
33
+ Max: 15
@@ -12,7 +12,12 @@ env:
12
12
  - RAILS_VERSION='~> 3.2.0'
13
13
  - RAILS_VERSION='~> 4.0.0'
14
14
  - RAILS_VERSION='~> 4.1.0'
15
- script: "bundle exec rspec"
15
+ script:
16
+ if [ -n "$RUBOCOP" ]; then
17
+ bundle exec rubocop
18
+ ; else
19
+ bundle exec rspec
20
+ ; fi
16
21
  before_install:
17
22
  - sudo /etc/init.d/postgresql stop
18
23
  - sudo /etc/init.d/postgresql start
@@ -20,8 +25,10 @@ before_install:
20
25
  - psql -c 'create database myapp_test;' -U postgres
21
26
  - cp .travis.database.yml spec/db/database.yml
22
27
  matrix:
23
- allow_failures:
24
- - rvm: jruby-19mode
28
+ fast_finish: true
29
+ include:
30
+ - env: RUBOCOP=true
31
+ rvm: default
25
32
  exclude:
26
33
  - rvm: 1.8.7 # not supported by rails 4
27
34
  env: RAILS_VERSION='~> 4.0.0'
@@ -31,3 +38,5 @@ matrix:
31
38
  env: RAILS_VERSION='~> 2.3.0'
32
39
  - rvm: 2.1.0
33
40
  env: RAILS_VERSION='~> 2.3.0'
41
+ allow_failures:
42
+ - rvm: jruby-19mode
@@ -249,6 +249,14 @@ restore backup (upload dump and restore on remote):
249
249
 
250
250
  cap dump:backup:restore
251
251
 
252
+ #### If you have separate db server
253
+
254
+ by default, dump's capistrano tasks use remote server with role :db, :primary => true. If you have separate db server, and want to use dump with assets, you should run cap tasks as following:
255
+
256
+ cap dump:mirror:down HOSTS=myappserver.com
257
+
258
+ where myappserver.com is application server carrying assets with database access.
259
+
252
260
  ## Copyright
253
261
 
254
262
  Copyright (c) 2008-2014 Ivan Kuchin. See LICENSE.txt for details.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'dump'
5
- s.version = '1.0.4'
5
+ s.version = '1.0.5'
6
6
  s.summary = %q{Rails app rake and capistrano tasks to create and restore dumps of database and assets}
7
7
  s.homepage = "http://github.com/toy/#{s.name}"
8
8
  s.authors = ['Ivan Kuchin']
@@ -18,4 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.add_dependency 'archive-tar-minitar', '= 0.5.2'
19
19
  s.add_dependency 'progress', '~> 2.4.0'
20
20
  s.add_development_dependency 'rspec', '~> 3.0'
21
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9.3')
22
+ s.add_development_dependency 'rubocop', '~> 0.27'
23
+ end
21
24
  end
@@ -1,3 +1,4 @@
1
+ # Namespace for railtie
1
2
  module Dump
2
3
  require 'dump/railtie' if defined?(Rails)
3
4
  end
@@ -1 +1,7 @@
1
- require File.join(File.dirname(__FILE__), '../../recipes/dump.rb')
1
+ require 'capistrano/version'
2
+
3
+ if defined?(Capistrano::Version) && Capistrano::Version::MAJOR == 2
4
+ require 'dump/capistrano/v2'
5
+ else
6
+ fail 'Capistrano 3 is not yet supported'
7
+ end
@@ -0,0 +1,331 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'fileutils'
4
+ require 'shellwords'
5
+
6
+ require 'dump_rake/continious_timeout'
7
+ require 'dump_rake/env'
8
+
9
+ require 'active_support/core_ext/object/blank'
10
+
11
+ require 'English'
12
+
13
+ Capistrano::Configuration.instance(:i_need_this!).load do
14
+ namespace :dump do
15
+ def dump_command(command, env = {})
16
+ rake = env.delete(:rake) || 'rake'
17
+
18
+ # stringify_keys! from activesupport
19
+ DumpRake::Env.stringify!(env)
20
+
21
+ env.update(DumpRake::Env.for_command(command, true))
22
+
23
+ cmd = %W[-s dump:#{command}]
24
+ cmd += env.sort.map{ |key, value| "#{key}=#{value}" }
25
+ "#{rake} #{cmd.shelljoin}"
26
+ end
27
+
28
+ def fetch_rails_env
29
+ fetch(:rails_env, 'production')
30
+ end
31
+
32
+ def got_rsync?
33
+ `which rsync`
34
+ $CHILD_STATUS.success?
35
+ end
36
+
37
+ def do_transfer_via(via, direction, from, to)
38
+ case via
39
+ when :rsync
40
+ if run_local('which rsync').present? && $CHILD_STATUS.success?
41
+ execute_on_servers do |servers|
42
+ commands = servers.map do |server|
43
+ target = sessions[server]
44
+ user = target.options[:user] || fetch(:user, nil)
45
+ host = target.host
46
+ port = target.options[:port]
47
+ full_host = "#{"#{user}@" if user.present?}#{host}"
48
+
49
+ ssh = port.present? ? "ssh -p #{port}" : 'ssh'
50
+ cmd = %W[rsync -P -e #{ssh} --timeout=15]
51
+ case direction
52
+ when :up
53
+ cmd << from << "#{full_host}:#{to}"
54
+ when :down
55
+ cmd << "#{full_host}:#{from}" << to
56
+ else
57
+ fail "Don't know how to transfer in direction #{direction}"
58
+ end
59
+ cmd.shelljoin
60
+ end
61
+ commands.each do |cmd|
62
+ logger.info cmd if logger
63
+
64
+ 3.times do
65
+ break if system(cmd)
66
+ break unless [10, 11, 12, 23, 30, 35].include?($CHILD_STATUS.exitstatus)
67
+ end
68
+ fail "rsync returned #{$CHILD_STATUS.exitstatus}" unless $CHILD_STATUS.success?
69
+ end
70
+ end
71
+ end
72
+ when :sftp, :scp
73
+ DumpRake::ContiniousTimeout.timeout 15 do |thread|
74
+ transfer(direction, from, to, :via => via) do |_channel, _path, transfered, total|
75
+ thread.defer
76
+ progress = if transfered < total
77
+ format("\e[1m%5.1f%%\e[0m", transfered * 100.0 / total)
78
+ else
79
+ '100%'
80
+ end
81
+ $stderr << "\rTransfering: #{progress}"
82
+ end
83
+ end
84
+ else
85
+ fail "Unknown transfer method #{via}"
86
+ end
87
+ end
88
+
89
+ def do_transfer(direction, from, to)
90
+ via = DumpRake::Env[:transfer_via]
91
+ case via && via.downcase
92
+ when nil
93
+ if got_rsync?
94
+ do_transfer_via(:rsync, direction, from, to)
95
+ else
96
+ $stderr.puts 'To transfer using rsync — make rsync binary accessible and verify that remote host can work with rsync through ssh'
97
+ begin
98
+ do_transfer_via(:sftp, direction, from, to)
99
+ rescue => e
100
+ $stderr.puts e
101
+ do_transfer_via(:scp, direction, from, to)
102
+ end
103
+ end
104
+ when 'rsync'
105
+ do_transfer_via(:rsync, direction, from, to)
106
+ when 'sftp'
107
+ do_transfer_via(:sftp, direction, from, to)
108
+ when 'scp'
109
+ do_transfer_via(:scp, direction, from, to)
110
+ else
111
+ fail "Unknown transfer method #{via}"
112
+ end
113
+ end
114
+
115
+ def with_additional_tags(*tags)
116
+ tags = [tags, DumpRake::Env[:tags]].flatten.select(&:present?).join(',')
117
+ DumpRake::Env.with_env(:tags => tags) do
118
+ yield
119
+ end
120
+ end
121
+
122
+ def print_and_return_or_fail
123
+ out = yield
124
+ fail 'Failed creating dump' if out.blank?
125
+ print out
126
+ out.strip
127
+ end
128
+
129
+ def run_local(cmd)
130
+ `#{cmd}`
131
+ end
132
+
133
+ def run_remote(cmd)
134
+ output = ''
135
+ run(cmd) do |_channel, io, data|
136
+ case io
137
+ when :out
138
+ output << data
139
+ when :err
140
+ $stderr << data
141
+ end
142
+ end
143
+ output
144
+ end
145
+
146
+ def last_part_of_last_line(out)
147
+ line = out.strip.split(/\s*[\n\r]\s*/).last
148
+ line.split("\t").last if line
149
+ end
150
+
151
+ def fetch_rake
152
+ fetch(:rake, nil)
153
+ end
154
+
155
+ def auto_backup?
156
+ !DumpRake::Env.no?(:backup)
157
+ end
158
+
159
+ namespace :local do
160
+ desc 'Shorthand for dump:local:create' << DumpRake::Env.explain_variables_for_command(:create)
161
+ task :default, :roles => :db, :only => {:primary => true} do
162
+ local.create
163
+ end
164
+
165
+ desc 'Create local dump' << DumpRake::Env.explain_variables_for_command(:create)
166
+ task :create, :roles => :db, :only => {:primary => true} do
167
+ print_and_return_or_fail do
168
+ with_additional_tags('local') do
169
+ run_local(dump_command(:create))
170
+ end
171
+ end
172
+ end
173
+
174
+ desc 'Restore local dump' << DumpRake::Env.explain_variables_for_command(:restore)
175
+ task :restore, :roles => :db, :only => {:primary => true} do
176
+ run_local(dump_command(:restore))
177
+ end
178
+
179
+ desc 'Versions of local dumps' << DumpRake::Env.explain_variables_for_command(:versions)
180
+ task :versions, :roles => :db, :only => {:primary => true} do
181
+ print run_local(dump_command(:versions, :show_size => true))
182
+ end
183
+
184
+ desc 'Cleanup local dumps' << DumpRake::Env.explain_variables_for_command(:cleanup)
185
+ task :cleanup, :roles => :db, :only => {:primary => true} do
186
+ print run_local(dump_command(:cleanup))
187
+ end
188
+
189
+ desc 'Upload dump' << DumpRake::Env.explain_variables_for_command(:transfer)
190
+ task :upload, :roles => :db, :only => {:primary => true} do
191
+ file = DumpRake::Env.with_env(:summary => nil) do
192
+ last_part_of_last_line(run_local(dump_command(:versions)))
193
+ end
194
+ if file
195
+ do_transfer :up, "dump/#{file}", "#{current_path}/dump/#{file}"
196
+ end
197
+ end
198
+ end
199
+
200
+ namespace :remote do
201
+ desc 'Shorthand for dump:remote:create' << DumpRake::Env.explain_variables_for_command(:create)
202
+ task :default, :roles => :db, :only => {:primary => true} do
203
+ remote.create
204
+ end
205
+
206
+ desc 'Create remote dump' << DumpRake::Env.explain_variables_for_command(:create)
207
+ task :create, :roles => :db, :only => {:primary => true} do
208
+ print_and_return_or_fail do
209
+ with_additional_tags('remote') do
210
+ run_remote("cd #{current_path}; #{dump_command(:create, :rake => fetch_rake, :RAILS_ENV => fetch_rails_env, :PROGRESS_TTY => '+')}")
211
+ end
212
+ end
213
+ end
214
+
215
+ desc 'Restore remote dump' << DumpRake::Env.explain_variables_for_command(:restore)
216
+ task :restore, :roles => :db, :only => {:primary => true} do
217
+ run_remote("cd #{current_path}; #{dump_command(:restore, :rake => fetch_rake, :RAILS_ENV => fetch_rails_env, :PROGRESS_TTY => '+')}")
218
+ end
219
+
220
+ desc 'Versions of remote dumps' << DumpRake::Env.explain_variables_for_command(:versions)
221
+ task :versions, :roles => :db, :only => {:primary => true} do
222
+ print run_remote("cd #{current_path}; #{dump_command(:versions, :rake => fetch_rake, :RAILS_ENV => fetch_rails_env, :PROGRESS_TTY => '+', :show_size => true)}")
223
+ end
224
+
225
+ desc 'Cleanup of remote dumps' << DumpRake::Env.explain_variables_for_command(:cleanup)
226
+ task :cleanup, :roles => :db, :only => {:primary => true} do
227
+ print run_remote("cd #{current_path}; #{dump_command(:cleanup, :rake => fetch_rake, :RAILS_ENV => fetch_rails_env, :PROGRESS_TTY => '+')}")
228
+ end
229
+
230
+ desc 'Download dump' << DumpRake::Env.explain_variables_for_command(:transfer)
231
+ task :download, :roles => :db, :only => {:primary => true} do
232
+ file = DumpRake::Env.with_env(:summary => nil) do
233
+ last_part_of_last_line(run_remote("cd #{current_path}; #{dump_command(:versions, :rake => fetch_rake, :RAILS_ENV => fetch_rails_env, :PROGRESS_TTY => '+')}"))
234
+ end
235
+ if file
236
+ FileUtils.mkpath('dump')
237
+ do_transfer :down, "#{current_path}/dump/#{file}", "dump/#{file}"
238
+ end
239
+ end
240
+ end
241
+
242
+ desc 'Shorthand for dump:local:upload' << DumpRake::Env.explain_variables_for_command(:transfer)
243
+ task :upload, :roles => :db, :only => {:primary => true} do
244
+ local.upload
245
+ end
246
+
247
+ desc 'Shorthand for dump:remote:download' << DumpRake::Env.explain_variables_for_command(:transfer)
248
+ task :download, :roles => :db, :only => {:primary => true} do
249
+ remote.download
250
+ end
251
+
252
+ namespace :mirror do
253
+ desc 'Creates local dump, uploads and restores on remote' << DumpRake::Env.explain_variables_for_command(:mirror)
254
+ task :up, :roles => :db, :only => {:primary => true} do
255
+ auto_backup = if auto_backup?
256
+ with_additional_tags('auto-backup') do
257
+ remote.create
258
+ end
259
+ end
260
+ if !auto_backup? || auto_backup.present?
261
+ file = with_additional_tags('mirror') do
262
+ local.create
263
+ end
264
+ if file.present?
265
+ DumpRake::Env.with_clean_env(:like => file) do
266
+ local.upload
267
+ remote.restore
268
+ end
269
+ end
270
+ end
271
+ end
272
+
273
+ desc 'Creates remote dump, downloads and restores on local' << DumpRake::Env.explain_variables_for_command(:mirror)
274
+ task :down, :roles => :db, :only => {:primary => true} do
275
+ auto_backup = if auto_backup?
276
+ with_additional_tags('auto-backup') do
277
+ local.create
278
+ end
279
+ end
280
+ if !auto_backup? || auto_backup.present?
281
+ file = with_additional_tags('mirror') do
282
+ remote.create
283
+ end
284
+ if file.present?
285
+ DumpRake::Env.with_clean_env(:like => file) do
286
+ remote.download
287
+ local.restore
288
+ end
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ namespace :backup do
295
+ desc 'Shorthand for dump:backup:create' << DumpRake::Env.explain_variables_for_command(:backup)
296
+ task :default, :roles => :db, :only => {:primary => true} do
297
+ backup.create
298
+ end
299
+
300
+ desc "Creates remote dump and downloads to local (desc defaults to 'backup')" << DumpRake::Env.explain_variables_for_command(:backup)
301
+ task :create, :roles => :db, :only => {:primary => true} do
302
+ file = with_additional_tags('backup') do
303
+ remote.create
304
+ end
305
+ if file.present?
306
+ DumpRake::Env.with_clean_env(:like => file) do
307
+ remote.download
308
+ end
309
+ end
310
+ end
311
+
312
+ desc 'Uploads dump with backup tag and restores it on remote' << DumpRake::Env.explain_variables_for_command(:backup_restore)
313
+ task :restore, :roles => :db, :only => {:primary => true} do
314
+ file = with_additional_tags('backup') do
315
+ last_part_of_last_line(run_local(dump_command(:versions)))
316
+ end
317
+ if file.present?
318
+ DumpRake::Env.with_clean_env(:like => file) do
319
+ local.upload
320
+ remote.restore
321
+ end
322
+ end
323
+ end
324
+ end
325
+ end
326
+
327
+ after 'deploy:update_code' do
328
+ from, to = %W[#{shared_path}/dump #{release_path}/dump]
329
+ run "mkdir -p #{from}; rm -rf #{to}; ln -s #{from} #{to}"
330
+ end
331
+ end