droxi 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7475e5467fb8a2cce45ca783361e37dcfe2f2361
4
- data.tar.gz: 71a443989f3ef75df19997850f865fef2bac27fe
3
+ metadata.gz: ed8c5f6d9c0df8aff83fc755d298fda9dda25adf
4
+ data.tar.gz: f882a46c0a98a0f4f69cb88f3610fc403abc23e3
5
5
  SHA512:
6
- metadata.gz: 20b0e20d19722a326203d48a5b5232203c88ebeecb809c94e084ebb223de4c97a756de512959e667d6dca5224e71ef82d587f1870d215fc75b795a16ff9edff3
7
- data.tar.gz: 09afb109d29dd70c2d77add8a3557a9442f58cb02deda624731022bf4eb73ad500e9d2ebe2876bf7115b7a690fd16d74e144f8e7f58801e27c187523b6191c9e
6
+ metadata.gz: c5cbb62624375b1515ff9e7de15167ead211bb535dd427a87954c3a585865e5d25ba90236a0f717c4a491b307d6bbb01fdd430dbee3c4edfaf43e8c49a30b3da
7
+ data.tar.gz: 4c9b4bc88008c441cb8f7284be5c90470b147233f134dceb035e9dc681e3ce303168220ecfdea5deb27f2bac94fad43109aeb52b3da18d23bafa730ba8b5580d
data/droxi.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'droxi'
3
- s.version = '0.0.4'
4
- s.date = '2014-06-03'
3
+ s.version = '0.0.5'
4
+ s.date = '2014-06-04'
5
5
  s.summary = 'ftp-like command-line interface to Dropbox'
6
6
  s.description = "A command-line Dropbox interface inspired by GNU \
7
7
  coreutils, GNU ftp, and lftp. Features include smart tab \
@@ -59,12 +59,14 @@ module Commands
59
59
 
60
60
  private
61
61
 
62
+ # Return +true+ if the given number of arguments is acceptable for the
63
+ # command, +false+ otherwise.
62
64
  def num_args_ok?(num_args)
63
65
  args = @usage.split.drop(1)
64
66
  min_args = args.reject { |arg| arg.start_with?('[') }.length
65
67
  if args.empty?
66
68
  max_args = 0
67
- elsif args.last.end_with?('...')
69
+ elsif args.any? { |arg| arg.end_with?('...') }
68
70
  max_args = num_args
69
71
  else
70
72
  max_args = args.length
@@ -96,6 +98,18 @@ module Commands
96
98
  end
97
99
  )
98
100
 
101
+ # Copy remote files.
102
+ CP = Command.new(
103
+ 'cp REMOTE_FILE... REMOTE_FILE',
104
+ "When given two arguments, copies the remote file or folder at the first \
105
+ path to the second path. When given more than two arguments or when the \
106
+ final argument is a directory, copies each remote file or folder into \
107
+ that directory.",
108
+ lambda do |client, state, args, output|
109
+ cp_mv(client, state, args, output, 'cp', :file_copy)
110
+ end
111
+ )
112
+
99
113
  # Terminate the session.
100
114
  EXIT = Command.new(
101
115
  'exit',
@@ -132,14 +146,12 @@ module Commands
132
146
  if path.is_a?(GlobError)
133
147
  output.call("get: #{path}: No such file or directory")
134
148
  else
135
- begin
149
+ try_and_handle(DropboxError, output) do
136
150
  contents = client.get_file(path)
137
151
  File.open(File.basename(path), 'wb') do |file|
138
152
  file.write(contents)
139
153
  end
140
154
  output.call("#{File.basename(path)} <- #{path}")
141
- rescue DropboxError => error
142
- output.call(error.to_s)
143
155
  end
144
156
  end
145
157
  end
@@ -240,11 +252,9 @@ module Commands
240
252
  if path.is_a?(GlobError)
241
253
  output.call("media: #{path}: No such file or directory")
242
254
  else
243
- begin
255
+ try_and_handle(DropboxError, output) do
244
256
  url = client.media(path)['url']
245
257
  output.call("#{File.basename(path)} -> #{url}")
246
- rescue DropboxError => error
247
- output.call(error.to_s)
248
258
  end
249
259
  end
250
260
  end
@@ -257,16 +267,26 @@ module Commands
257
267
  "Create remote directories.",
258
268
  lambda do |client, state, args, output|
259
269
  args.each do |arg|
260
- begin
270
+ try_and_handle(DropboxError, output) do
261
271
  path = state.resolve_path(arg)
262
272
  state.cache[path] = client.file_create_folder(path)
263
- rescue DropboxError => error
264
- output.call(error.to_s)
265
273
  end
266
274
  end
267
275
  end
268
276
  )
269
277
 
278
+ # Move/rename remote files.
279
+ MV = Command.new(
280
+ 'mv REMOTE_FILE... REMOTE_FILE',
281
+ "When given two arguments, moves the remote file or folder at the first \
282
+ path to the second path. When given more than two arguments or when the \
283
+ final argument is a directory, moves each remote file or folder into \
284
+ that directory.",
285
+ lambda do |client, state, args, output|
286
+ cp_mv(client, state, args, output, 'mv', :file_move)
287
+ end
288
+ )
289
+
270
290
  # Upload a local file.
271
291
  PUT = Command.new(
272
292
  'put LOCAL_FILE [REMOTE_FILE]',
@@ -283,14 +303,12 @@ module Commands
283
303
  end
284
304
  to_path = state.resolve_path(to_path)
285
305
 
286
- begin
306
+ try_and_handle(Exception, output) do
287
307
  File.open(File.expand_path(from_path), 'rb') do |file|
288
308
  data = client.put_file(to_path, file)
289
309
  state.cache[data['path']] = data
290
310
  output.call("#{from_path} -> #{data['path']}")
291
311
  end
292
- rescue Exception => error
293
- output.call(error.to_s)
294
312
  end
295
313
  end
296
314
  )
@@ -304,14 +322,13 @@ module Commands
304
322
  if path.is_a?(GlobError)
305
323
  output.call("rm: #{path}: No such file or directory")
306
324
  else
307
- begin
325
+ try_and_handle(DropboxError, output) do
308
326
  client.file_delete(path)
309
- state.cache.delete(path)
310
- rescue DropboxError => error
311
- output.call(error.to_s)
327
+ state.cache_remove(path)
312
328
  end
313
329
  end
314
330
  end
331
+ check_pwd(state)
315
332
  end
316
333
  )
317
334
 
@@ -327,11 +344,9 @@ module Commands
327
344
  if path.is_a?(GlobError)
328
345
  output.call("share: #{path}: No such file or directory")
329
346
  else
330
- begin
347
+ try_and_handle(DropboxError, output) do
331
348
  url = client.shares(path)['url']
332
349
  output.call("#{File.basename(path)} -> #{url}")
333
- rescue DropboxError => error
334
- output.call(error.to_s)
335
350
  end
336
351
  end
337
352
  end
@@ -356,6 +371,16 @@ module Commands
356
371
 
357
372
  private
358
373
 
374
+ # Attempt to run the associated block, handling the given type of +Exception+
375
+ # by passing its +String+ representation to an output +Proc+.
376
+ def self.try_and_handle(exception_class, output)
377
+ yield
378
+ rescue exception_class => error
379
+ output.call(error.to_s)
380
+ end
381
+
382
+ # Run a command with the given name, or print an error message if usage is
383
+ # incorrect or no such command exists.
359
384
  def self.try_command(command_name, args, client, state)
360
385
  if NAMES.include?(command_name)
361
386
  begin
@@ -369,6 +394,8 @@ module Commands
369
394
  end
370
395
  end
371
396
 
397
+ # Split a +String+ into tokens, allowing for backslash-escaped spaces, and
398
+ # return the resulting +Array+.
372
399
  def self.tokenize(string)
373
400
  string.split.reduce([]) do |list, token|
374
401
  list << if !list.empty? && list.last.end_with?('\\')
@@ -379,6 +406,7 @@ module Commands
379
406
  end
380
407
  end
381
408
 
409
+ # Return a +String+ of information about a remote file for ls -l.
382
410
  def self.long_info(state, path, name)
383
411
  meta = state.metadata(state.resolve_path(path), false)
384
412
  is_dir = meta['is_dir'] ? 'd' : '-'
@@ -388,6 +416,8 @@ module Commands
388
416
  "#{is_dir} #{size} #{mtime.strftime(format_str)} #{name}"
389
417
  end
390
418
 
419
+ # Yield lines of output for the ls command executed on the given file paths
420
+ # and names.
391
421
  def self.list(state, paths, names, long)
392
422
  if long
393
423
  paths.zip(names).each { |path, name| yield long_info(state, path, name) }
@@ -396,6 +426,7 @@ module Commands
396
426
  end
397
427
  end
398
428
 
429
+ # Run a command in the system shell and yield lines of output.
399
430
  def self.shell(cmd)
400
431
  IO.popen(cmd) do |pipe|
401
432
  pipe.each_line { |line| yield line.chomp if block_given? }
@@ -405,4 +436,54 @@ module Commands
405
436
  yield error.to_s if block_given?
406
437
  end
407
438
 
439
+ # Return an +Array+ of paths from an +Array+ of globs, passing error messages
440
+ # to the output +Proc+ for non-matches.
441
+ def self.expand(state, paths, preserve_root, output, cmd_name)
442
+ state.expand_patterns(paths, true).map do |item|
443
+ if item.is_a?(GlobError)
444
+ output.call("#{cmd_name}: #{item}: no such file or directory")
445
+ nil
446
+ else
447
+ item
448
+ end
449
+ end.compact
450
+ end
451
+
452
+ # Copies or moves the file at +source+ to +dest+ and passes a description of
453
+ # the operation to the output +Proc+.
454
+ def self.copy_move(method, source, dest, client, state, output)
455
+ from_path, to_path = [source, dest].map { |p| state.resolve_path(p) }
456
+ try_and_handle(DropboxError, output) do
457
+ metadata = client.send(method, from_path, to_path)
458
+ state.cache_remove(from_path) if method == :file_move
459
+ state.cache_add(metadata)
460
+ output.call("#{source} -> #{dest}")
461
+ end
462
+ end
463
+
464
+ # Execute a 'mv' or 'cp' operation depending on arguments given.
465
+ def self.cp_mv(client, state, args, output, cmd, method)
466
+ sources = expand(state, args.take(args.length - 1), true, output, cmd)
467
+ dest = state.resolve_path(args.last)
468
+
469
+ if sources.length == 1 && !state.directory?(dest)
470
+ copy_move(method, sources[0], args.last, client, state, output)
471
+ else
472
+ if state.metadata(dest)
473
+ sources.each do |source|
474
+ to_path = args.last.chomp('/') + '/' + File.basename(source)
475
+ copy_move(method, source, to_path, client, state, output)
476
+ end
477
+ else
478
+ output.call("#{cmd}: #{args.last}: no such directory")
479
+ end
480
+ end
481
+ end
482
+
483
+ # If the remote working directory does not exist, move up the directory
484
+ # tree until at a real location.
485
+ def self.check_pwd(state)
486
+ state.pwd = File.dirname(state.pwd) until state.metadata(state.pwd)
487
+ end
488
+
408
489
  end
@@ -11,7 +11,7 @@ module Complete
11
11
  end
12
12
  end
13
13
 
14
- # Returns an +Array+ of potential local tab-completions for a +String+.
14
+ # Return an +Array+ of potential local tab-completions for a +String+.
15
15
  def self.local(string)
16
16
  dir = local_search_path(string)
17
17
  name = string.end_with?('/') ? '' : File.basename(string)
@@ -24,7 +24,7 @@ module Complete
24
24
  end
25
25
  end
26
26
 
27
- # Returns an +Array+ of potential local tab-completions for a +String+,
27
+ # Return an +Array+ of potential local tab-completions for a +String+,
28
28
  # including only directories.
29
29
  def self.local_dir(string)
30
30
  local(string).select { |result| result.end_with?('/') }
@@ -42,7 +42,7 @@ module Complete
42
42
  strip_filename(collapse(path))
43
43
  end
44
44
 
45
- # Returns an +Array+ of potential remote tab-completions for a +String+.
45
+ # Return an +Array+ of potential remote tab-completions for a +String+.
46
46
  def self.remote(string, state)
47
47
  dir = remote_search_path(string, state)
48
48
  name = string.end_with?('/') ? '' : File.basename(string)
@@ -57,7 +57,7 @@ module Complete
57
57
  end
58
58
  end
59
59
 
60
- # Returns an +Array+ of potential remote tab-completions for a +String+,
60
+ # Return an +Array+ of potential remote tab-completions for a +String+,
61
61
  # including only directories.
62
62
  def self.remote_dir(string, state)
63
63
  remote(string, state).select { |result| result.end_with?('/') }
@@ -65,6 +65,7 @@ module Complete
65
65
 
66
66
  private
67
67
 
68
+ # Return the name of the directory indicated by a path.
68
69
  def self.strip_filename(path)
69
70
  if path != '/'
70
71
  path.end_with?('/') ? path.sub(/\/$/, '') : File.dirname(path)
@@ -73,10 +74,13 @@ module Complete
73
74
  end
74
75
  end
75
76
 
77
+ # Return a version of a path with .. and . resolved to appropriate
78
+ # directories.
76
79
  def self.collapse(path)
77
- nil while path.sub!(/[^\/]+\/\.\.\//, '/')
78
- nil while path.sub!('./', '')
79
- path
80
+ new_path = path.dup
81
+ nil while new_path.sub!(/[^\/]+\/\.\.\//, '/')
82
+ nil while new_path.sub!('./', '')
83
+ new_path
80
84
  end
81
85
 
82
86
  end
@@ -38,6 +38,7 @@ class Settings
38
38
  FileUtils.mkdir_p(File.dirname(CONFIG_FILE_PATH))
39
39
  File.open(CONFIG_FILE_PATH, 'w') do |file|
40
40
  @@settings.each_pair { |k, v| file.write("#{k}=#{v}\n") }
41
+ file.chmod(0600)
41
42
  end
42
43
  end
43
44
  nil
@@ -45,11 +46,15 @@ class Settings
45
46
 
46
47
  private
47
48
 
49
+ # Print a warning for an invalid setting and return an empty +Hash+ (the
50
+ # result of an invalid setting).
48
51
  def Settings.warn_invalid(line)
49
52
  warn "invalid setting: #{line}"
50
53
  {}
51
54
  end
52
55
 
56
+ # Parse a line of the rc file and return a +Hash+ containing the resulting
57
+ # setting data.
53
58
  def Settings.parse(line)
54
59
  if /^(.+?)=(.+)$/ =~ line
55
60
  key, value = $1.to_sym, $2
@@ -63,6 +68,7 @@ class Settings
63
68
  end
64
69
  end
65
70
 
71
+ # Read and parse the rc file.
66
72
  def Settings.read
67
73
  if File.exists?(CONFIG_FILE_PATH)
68
74
  File.open(CONFIG_FILE_PATH) do |file|
data/lib/droxi/state.rb CHANGED
@@ -33,6 +33,28 @@ class State
33
33
  @local_oldpwd = Dir.pwd
34
34
  end
35
35
 
36
+ # Adds a metadata +Hash+ and its contents to the metadata cache.
37
+ def cache_add(metadata)
38
+ @cache[metadata['path']] = metadata
39
+ if metadata.include?('contents')
40
+ metadata['contents'].each { |content| cache_add(content) }
41
+ end
42
+ end
43
+
44
+ # Removes a path from the metadata cache.
45
+ def cache_remove(path)
46
+ if directory?(path) && @cache[path].include?('contents')
47
+ @cache[path]['contents'].each { |item| cache_remove(item['path']) }
48
+ end
49
+
50
+ @cache.delete(path)
51
+
52
+ dir = File.dirname(path)
53
+ if @cache.include?(dir) && @cache[dir].include?('contents')
54
+ @cache[dir]['contents'].delete_if { |item| item['path'] == path }
55
+ end
56
+ end
57
+
36
58
  # Return a +Hash+ of the Dropbox metadata for a file, or +nil+ if the file
37
59
  # does not exist.
38
60
  def metadata(path, require_contents=true)
@@ -42,15 +64,18 @@ class State
42
64
  partial_path = '/' + tokens.take(i).join('/')
43
65
  unless have_all_info_for(partial_path, require_contents)
44
66
  begin
45
- data = @cache[partial_path] = @client.metadata(partial_path)
67
+ data = @client.metadata(partial_path)
68
+ if !data['is_deleted']
69
+ @cache[partial_path] = data
70
+ if data.include?('contents')
71
+ data['contents'].each do |datum|
72
+ @cache[datum['path']] = datum
73
+ end
74
+ end
75
+ end
46
76
  rescue DropboxError
47
77
  return nil
48
78
  end
49
- if data.include?('contents')
50
- data['contents'].each do |datum|
51
- @cache[datum['path']] = datum
52
- end
53
- end
54
79
  end
55
80
  end
56
81
 
@@ -122,6 +147,8 @@ class State
122
147
 
123
148
  private
124
149
 
150
+ # Return an +Array+ of file paths matching a glob pattern, or a GlobError if
151
+ # no files were matched.
125
152
  def get_matches(pattern, path, preserve_root)
126
153
  dir = File.dirname(path)
127
154
  matches = contents(dir).select { |entry| File.fnmatch(path, entry) }
@@ -135,6 +162,7 @@ class State
135
162
  end
136
163
  end
137
164
 
165
+ # Return +true+ if the path's information is cached, +false+ otherwise.
138
166
  def have_all_info_for(path, require_contents=true)
139
167
  @cache.include?(path) && (
140
168
  !require_contents ||
data/lib/droxi/text.rb CHANGED
@@ -33,6 +33,7 @@ module Text
33
33
 
34
34
  private
35
35
 
36
+ # Return the width of the terminal in columns.
36
37
  def self.get_columns
37
38
  require 'readline'
38
39
  begin
@@ -43,6 +44,7 @@ module Text
43
44
  end
44
45
  end
45
46
 
47
+ # Return an +Array+ of lines of the given items formatted as a table.
46
48
  def self.format_table(items, item_width, items_per_line, num_lines)
47
49
  num_lines.times.map do |i|
48
50
  items[i * items_per_line, items_per_line].map do |item|
@@ -51,6 +53,7 @@ module Text
51
53
  end
52
54
  end
53
55
 
56
+ # Return a wrapped line of output from the start of the given text.
54
57
  def self.get_wrap_segment(text, columns)
55
58
  segment, sep, text = text.partition(' ')
56
59
  while !text.empty? && segment.length < columns
data/lib/droxi.rb CHANGED
@@ -28,8 +28,7 @@ module Droxi
28
28
 
29
29
  private
30
30
 
31
- # Attempt to authorize the user for app usage. Return +true+ if
32
- # authorization was successful, +false+ otherwise.
31
+ # Attempt to authorize the user for app usage.
33
32
  def self.authorize
34
33
  app_key = '5sufyfrvtro9zp7'
35
34
  app_secret = 'h99ihzv86jyypho'
@@ -46,8 +45,8 @@ module Droxi
46
45
  end
47
46
  end
48
47
 
49
- # Get the access token for the user, requesting authorization if no token
50
- # exists.
48
+ # Return the access token for the user, requesting authorization if no saved
49
+ # token exists.
51
50
  def self.get_access_token
52
51
  authorize() until Settings.include?(:access_token)
53
52
  Settings[:access_token]
@@ -55,7 +54,7 @@ module Droxi
55
54
 
56
55
  # Return a prompt message reflecting the current state of the application.
57
56
  def self.prompt(info, state)
58
- "droxi #{info['email']}:#{state.pwd}> "
57
+ "\rdroxi #{info['email']}:#{state.pwd}> "
59
58
  end
60
59
 
61
60
  # Run the client in interactive mode.
@@ -66,10 +65,11 @@ module Droxi
66
65
  init_readline(state)
67
66
  with_interrupt_handling { do_interaction_loop(client, state, info) }
68
67
 
69
- # Set pwd so that the oldpwd setting is saved to pwd
68
+ # Set pwd before exiting so that the oldpwd setting is saved to pwd
70
69
  state.pwd = '/'
71
70
  end
72
71
 
72
+ # Set up the Readline library's completion capabilities.
73
73
  def self.init_readline(state)
74
74
  Readline.completion_proc = proc do |word|
75
75
  words = Readline.line_buffer.split
@@ -103,12 +103,16 @@ module Droxi
103
103
  end
104
104
  end
105
105
 
106
+ # Run the associated block, handling Interrupt errors by printing a blank
107
+ # line.
106
108
  def self.with_interrupt_handling
107
109
  yield
108
110
  rescue Interrupt
109
111
  puts
110
112
  end
111
113
 
114
+ # Run the main loop of the program, getting user input and executing it as a
115
+ # command until an getting input fails or an exit is requested.
112
116
  def self.do_interaction_loop(client, state, info)
113
117
  while !state.exit_requested &&
114
118
  line = Readline.readline(prompt(info, state), true)
@@ -117,6 +121,8 @@ module Droxi
117
121
  puts if !line
118
122
  end
119
123
 
124
+ # Instruct the user to enter an authorization code and return the code. If
125
+ # the user gives EOF, exit the program.
120
126
  def self.get_auth_code(url)
121
127
  puts '1. Go to: ' + url
122
128
  puts '2. Click "Allow" (you might have to log in first)'
@@ -90,6 +90,38 @@ describe Commands do
90
90
  end
91
91
  end
92
92
 
93
+ describe 'when executing the cp command' do
94
+ before do
95
+ state.pwd = '/testing'
96
+ end
97
+
98
+ after do
99
+ Commands::RM.exec(client, state, '*')
100
+ end
101
+
102
+ it 'must copy source to dest when given 2 args and last arg is non-dir' do
103
+ Commands::MKDIR.exec(client, state, 'source')
104
+ Commands::CP.exec(client, state, 'source', 'dest')
105
+ ['source', 'dest'].all? do |dir|
106
+ client.metadata("/testing/#{dir}")
107
+ end.must_equal true
108
+ end
109
+
110
+ it 'must copy source into dest when given 2 args and last arg is dir' do
111
+ Commands::MKDIR.exec(client, state, 'source', 'dest')
112
+ Commands::CP.exec(client, state, 'source', 'dest')
113
+ client.metadata('/testing/dest/source').wont_equal nil
114
+ end
115
+
116
+ it 'must copy sources into dest when given 3 or more args' do
117
+ Commands::MKDIR.exec(client, state, 'source1', 'source2', 'dest')
118
+ Commands::CP.exec(client, state, 'source1', 'source2', 'dest')
119
+ ['source2', 'source2'].all? do |dir|
120
+ client.metadata("/testing/dest/#{dir}")
121
+ end.must_equal true
122
+ end
123
+ end
124
+
93
125
  describe 'when executing the forget command' do
94
126
  it 'must clear entire cache when given no arguments' do
95
127
  Commands::LS.exec(client, state, '/')
@@ -206,6 +238,39 @@ describe Commands do
206
238
  end
207
239
  end
208
240
 
241
+ describe 'when executing the mv command' do
242
+ before do
243
+ state.pwd = '/testing'
244
+ end
245
+
246
+ after do
247
+ Commands::RM.exec(client, state, '*')
248
+ end
249
+
250
+ it 'must move source to dest when given 2 args and last arg is non-dir' do
251
+ Commands::MKDIR.exec(client, state, 'source')
252
+ Commands::MV.exec(client, state, 'source', 'dest')
253
+ client.metadata('/testing/source')['is_deleted'].must_equal true
254
+ client.metadata('/testing/dest').wont_equal nil
255
+ end
256
+
257
+ it 'must move source into dest when given 2 args and last arg is dir' do
258
+ Commands::MKDIR.exec(client, state, 'source', 'dest')
259
+ Commands::MV.exec(client, state, 'source', 'dest')
260
+ client.metadata('/testing/source')['is_deleted'].must_equal true
261
+ client.metadata('/testing/dest/source').wont_equal nil
262
+ end
263
+
264
+ it 'must move sources into dest when given 3 or more args' do
265
+ Commands::MKDIR.exec(client, state, 'source1', 'source2', 'dest')
266
+ Commands::MV.exec(client, state, 'source1', 'source2', 'dest')
267
+ ['source2', 'source2'].all? do |dir|
268
+ client.metadata("/testing/#{dir}")['is_deleted'].must_equal true
269
+ client.metadata("/testing/dest/#{dir}")
270
+ end.must_equal true
271
+ end
272
+ end
273
+
209
274
  describe 'when executing the put command' do
210
275
  it 'must put a file of the same name when given 1 arg' do
211
276
  state.pwd = '/testing'
@@ -239,10 +304,17 @@ describe Commands do
239
304
 
240
305
  describe 'when executing the rm command' do
241
306
  it 'must remove the remote file when given args' do
242
- # FIXME: This test fails and I don't know why
243
- #Commands::MKDIR.exec(client, state, '/testing/test')
244
- #Commands::RM.exec(client, state, '/testing/test')
245
- #client.metadata('/testing/test')['is_deleted'].must_equal true
307
+ Commands::MKDIR.exec(client, state, '/testing/test')
308
+ Commands::RM.exec(client, state, '/testing/test')
309
+ client.metadata('/testing/test')['is_deleted'].must_equal true
310
+ end
311
+
312
+ it 'must change pwd to existing dir if the current one is removed' do
313
+ Commands::MKDIR.exec(client, state, '/testing/one')
314
+ Commands::MKDIR.exec(client, state, '/testing/one/two')
315
+ Commands::CD.exec(client, state, '/testing/one/two')
316
+ Commands::RM.exec(client, state, '..')
317
+ state.pwd.must_equal('/testing')
246
318
  end
247
319
  end
248
320
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: droxi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Mulcahy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-03 00:00:00.000000000 Z
11
+ date: 2014-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dropbox-sdk