Class: Dbox::DB::DropboxDir

Inherits:
DropboxBlob show all
Defined in:
lib/dbox/db.rb

Instance Attribute Summary collapse

Attributes inherited from DropboxBlob

#modified_at, #path, #revision

Instance Method Summary collapse

Methods inherited from DropboxBlob

#api, #create, #delete, #force_metadata_update_from_server, #local_path, #modified?, #parse_time, #remote_path, #saving_parent_timestamp, #smart_new, #time_to_s, #update, #update_file_timestamp, #update_modification_info

Methods included from Loggable

included, #log

Constructor Details

#initialize(db, res) ⇒ DropboxDir

Returns a new instance of DropboxDir.



290
291
292
293
294
# File 'lib/dbox/db.rb', line 290

def initialize(db, res)
  @contents_hash = nil
  @contents = {}
  super(db, res)
end

Instance Attribute Details

#contentsObject (readonly)

Returns the value of attribute contents.



288
289
290
# File 'lib/dbox/db.rb', line 288

def contents
  @contents
end

#contents_hashObject (readonly)

Returns the value of attribute contents_hash.



288
289
290
# File 'lib/dbox/db.rb', line 288

def contents_hash
  @contents_hash
end

Instance Method Details

#calculate_changes(res) ⇒ Object

Raises:

  • (ArgumentError)


334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/dbox/db.rb', line 334

def calculate_changes(res)
  raise(ArgumentError, "Not a directory: #{res.inspect}") unless res["is_dir"]

  if @contents_hash && res["hash"] && @contents_hash == res["hash"]
    # dir hash hasn't changed -- no need to calculate changes
    []
  elsif res["contents"]
    # dir has changed -- calculate changes on contents
    out = []
    got_paths = []

    remove_dotfiles(res["contents"]).each do |c|
      p = @db.remote_to_relative_path(c["path"])
      c["rel_path"] = p
      got_paths << p

      if @contents.has_key?(p)
        # only update file if it's been modified
        if @contents[p].modified?(c)
          out << [:update, c]
        end
      else
        out << [:create, c]
      end
    end
    out += (@contents.keys.sort - got_paths.sort).map {|p| [:delete, { "rel_path" => p }] }
    out
  else
    raise(RuntimeError, "Trying to calculate dir changes without any contents")
  end
end

#create_localObject



430
431
432
433
434
435
436
# File 'lib/dbox/db.rb', line 430

def create_local
  log.info "Creating #{local_path}"
  saving_parent_timestamp do
    FileUtils.mkdir_p(local_path)
    update_file_timestamp
  end
end

#create_remoteObject



449
450
451
452
# File 'lib/dbox/db.rb', line 449

def create_remote
  api.create_dir(remote_path)
  
end

#delete_localObject



438
439
440
441
442
443
# File 'lib/dbox/db.rb', line 438

def delete_local
  log.info "Deleting #{local_path}"
  saving_parent_timestamp do
    FileUtils.rm_r(local_path)
  end
end

#delete_remoteObject



454
455
456
# File 'lib/dbox/db.rb', line 454

def delete_remote
  api.delete_dir(remote_path)
end

#dir?Boolean

Returns:

  • (Boolean)


426
427
428
# File 'lib/dbox/db.rb', line 426

def dir?
  true
end

#execute_changes(changes, direction) ⇒ Object



366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/dbox/db.rb', line 366

def execute_changes(changes, direction)
  log.debug "executing changes: #{changes.inspect}"
  changelist = { :created => [], :deleted => [], :updated => [] }
  changes.each do |op, c|
    case op
    when :create
      e = smart_new(c)
      e.create(direction)
      @contents[e.path] = e
      changelist[:created] << e.path
    when :update
      e = @contents[c["rel_path"]]
      e.update_modification_info(c) if direction == :down
      e.update(direction)
      changelist[:updated] << e.path
    when :delete
      e = @contents[c["rel_path"]]
      e.delete(direction)
      @contents.delete(e.path)
      changelist[:deleted] << e.path
    else
      raise(RuntimeError, "Unknown operation type: #{op}")
    end
    @db.save
  end
  changelist.keys.each {|k| changelist[k].sort! }
  changelist
end

#gather_local_info(rel, list_contents = true) ⇒ Object



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/dbox/db.rb', line 399

def gather_local_info(rel, list_contents=true)
  full = @db.relative_to_local_path(rel)
  remote = @db.relative_to_remote_path(rel)

  attrs = {
    "path" => remote,
    "is_dir" => File.directory?(full),
    "modified" => File.mtime(full),
    "revision" => @contents[rel] ? @contents[rel].revision : nil
  }

  if attrs["is_dir"] && list_contents
    contents = Dir.entries(full).reject {|s| s == "." || s == ".." }
    attrs["contents"] = contents.map do |s|
      p = File.join(full, s)
      r = @db.local_to_relative_path(p)
      gather_local_info(r, false)
    end
  end

  attrs
end

#merge_changelists(old, new) ⇒ Object



395
396
397
# File 'lib/dbox/db.rb', line 395

def merge_changelists(old, new)
  old.merge(new) {|k, v1, v2| (v1 + v2).sort }
end


466
467
468
469
470
471
472
473
# File 'lib/dbox/db.rb', line 466

def print
  puts
  puts "#{path} (v#{@revision}, #{@modified_at})"
  contents.each do |path, c|
    puts "  #{c.path} (v#{c.revision}, #{c.modified_at})"
  end
  puts
end

#pullObject



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/dbox/db.rb', line 296

def pull
  # calculate changes on this dir
  res = api.(remote_path)
  changes = calculate_changes(res)

  # execute changes on this dir
  changelist = execute_changes(changes, :down)

  # recur on subdirs, expanding changelist as we go
  changelist = subdirs.inject(changelist) {|c, d| merge_changelists(c, d.pull) }

  # only update the modification info on the directory once all descendants are updated
  update_modification_info(res)

  # return changes
  @db.save
  changelist
end

#pushObject



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/dbox/db.rb', line 315

def push
  # calculate changes on this dir
  res = gather_local_info(@path)
  changes = calculate_changes(res)

  # execute changes on this dir
  changelist = execute_changes(changes, :up)

  # recur on subdirs, expanding changelist as we go
  changelist = subdirs.inject(changelist) {|c, d| merge_changelists(c, d.push) }

  # only update the modification info on the directory once all descendants are updated
  update_modification_info(res)

  # return changes
  @db.save
  changelist
end

#remove_dotfiles(contents) ⇒ Object



422
423
424
# File 'lib/dbox/db.rb', line 422

def remove_dotfiles(contents)
  contents.reject {|c| File.basename(c["path"]).start_with?(".") }
end

#subdirsObject



462
463
464
# File 'lib/dbox/db.rb', line 462

def subdirs
  @contents.values.select {|c| c.dir? }
end

#update_localObject



445
446
447
# File 'lib/dbox/db.rb', line 445

def update_local
  update_file_timestamp
end

#update_remoteObject



458
459
460
# File 'lib/dbox/db.rb', line 458

def update_remote
  # do nothing
end