Class: Dbox::Syncer::Push
Instance Attribute Summary
Attributes inherited from Operation
Instance Method Summary collapse
- #calculate_changes(dir) ⇒ Object
- #create_dir(dir) ⇒ Object
- #delete_dir(dir) ⇒ Object
- #delete_file(file) ⇒ Object
- #execute ⇒ Object
- #force_metadata_update_from_server(entry) ⇒ Object
-
#initialize(database, api) ⇒ Push
constructor
A new instance of Push.
- #is_dir(path) ⇒ Object
- #list_contents(dir) ⇒ Object
- #modified?(entry, res) ⇒ Boolean
- #mtime(path) ⇒ Object
- #practice ⇒ Object
- #upload_file(file) ⇒ Object
Methods inherited from Operation
#api, #current_dir_entries_as_hash, #gather_remote_info, #generate_tmpfilename, #local_path, #lookup_id_by_path, #metadata, #process_basic_remote_props, #remote_path, #remove_dotfiles, #remove_tmpfiles, #saving_parent_timestamp, #saving_timestamp, #sort_changelist, #update_file_timestamp
Methods included from Utils
#calculate_hash, #case_insensitive_difference, #case_insensitive_equal, #case_insensitive_join, #case_insensitive_resolve, #find_nonconflicting_path, #local_to_relative_path, #parse_time, #relative_to_local_path, #relative_to_remote_path, #remote_to_relative_path, #time_to_s, #times_equal?
Methods included from Loggable
Constructor Details
#initialize(database, api) ⇒ Push
Returns a new instance of Push.
419 420 421 |
# File 'lib/dbox/syncer.rb', line 419 def initialize(database, api) super(database, api) end |
Instance Method Details
#calculate_changes(dir) ⇒ Object
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
# File 'lib/dbox/syncer.rb', line 520 def calculate_changes(dir) raise(ArgumentError, "Not a directory: #{dir.inspect}") unless dir[:is_dir] out = [] recur_dirs = [] existing_entries = current_dir_entries_as_hash(dir) child_paths = list_contents(dir).sort child_paths.each do |p| local_path = relative_to_local_path(p) remote_path = relative_to_remote_path(p) c = { :path => p, :local_path => local_path, :remote_path => remote_path, :modified => mtime(local_path), :is_dir => is_dir(local_path), :parent_path => dir[:path], :local_hash => calculate_hash(local_path) } if entry = existing_entries[p] c[:id] = entry[:id] recur_dirs << c if c[:is_dir] # queue dir for later out << [:update, c] if modified?(entry, c) # update iff modified else # create out << [:create, c] recur_dirs << c if c[:is_dir] end end # add any deletions out += case_insensitive_difference(existing_entries.keys, child_paths).map do |p| [:delete, existing_entries[p]] end # recursively process new & existing subdirectories recur_dirs.each do |dir| out += calculate_changes(dir) end out end |
#create_dir(dir) ⇒ Object
593 594 595 596 597 |
# File 'lib/dbox/syncer.rb', line 593 def create_dir(dir) remote_path = dir[:remote_path] log.info "Creating #{remote_path}" api.create_dir(remote_path) end |
#delete_dir(dir) ⇒ Object
599 600 601 602 |
# File 'lib/dbox/syncer.rb', line 599 def delete_dir(dir) remote_path = dir[:remote_path] api.delete_dir(remote_path) end |
#delete_file(file) ⇒ Object
604 605 606 607 |
# File 'lib/dbox/syncer.rb', line 604 def delete_file(file) remote_path = file[:remote_path] api.delete_file(remote_path) end |
#execute ⇒ Object
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 |
# File 'lib/dbox/syncer.rb', line 429 def execute dir = database.root_dir changes = calculate_changes(dir) log.debug "Executing changes:\n" + changes.map {|c| c.inspect }.join("\n") changelist = { :created => [], :deleted => [], :updated => [], :failed => [] } changes.each do |op, c| case op when :create c[:parent_id] ||= lookup_id_by_path(c[:parent_path]) if c[:is_dir] # create the remote directiory create_dir(c) database.add_entry(c[:path], true, c[:parent_id], nil, nil, nil, nil) (c) changelist[:created] << c[:path] else # upload a new file begin local_hash = calculate_hash(c[:local_path]) res = upload_file(c) database.add_entry(c[:path], false, c[:parent_id], nil, nil, nil, local_hash) if case_insensitive_equal(c[:path], res[:path]) (c) changelist[:created] << c[:path] else log.warn "#{c[:path]} had a conflict and was renamed to #{res[:path]} on the server" changelist[:conflicts] ||= [] changelist[:conflicts] << { :original => c[:path], :renamed => res[:path] } end rescue => e log.error "Error while uploading #{c[:path]}: #{e.inspect}\n#{e.backtrace.join("\n")}" changelist[:failed] << { :operation => :create, :path => c[:path], :error => e } end end when :update existing = database.find_by_path(c[:path]) unless existing[:is_dir] == c[:is_dir] raise(RuntimeError, "Mode on #{c[:path]} changed between file and dir -- not supported yet") end # only update files -- nothing to do to update a dir if !c[:is_dir] # upload changes to a file begin local_hash = calculate_hash(c[:local_path]) res = upload_file(c) database.update_entry_by_path(c[:path], :local_hash => local_hash) if case_insensitive_equal(c[:path], res[:path]) (c) changelist[:updated] << c[:path] else log.warn "#{c[:path]} had a conflict and was renamed to #{res[:path]} on the server" changelist[:conflicts] ||= [] changelist[:conflicts] << { :original => c[:path], :renamed => res[:path] } end rescue => e log.error "Error while uploading #{c[:path]}: #{e.inspect}\n#{e.backtrace.join("\n")}" changelist[:failed] << { :operation => :update, :path => c[:path], :error => e } end end when :delete # delete a remote file/directory begin begin if c[:is_dir] delete_dir(c) else delete_file(c) end rescue Dbox::RemoteMissing # safe to delete even if remote is already gone end database.delete_entry_by_path(c[:path]) changelist[:deleted] << c[:path] rescue => e log.error "Error while deleting #{c[:path]}: #{e.inspect}\n#{e.backtrace.join("\n")}" changelist[:failed] << { :operation => :delete, :path => c[:path], :error => e } end when :failed changelist[:failed] << { :operation => c[:operation], :path => c[:path], :error => c[:error] } else raise(RuntimeError, "Unknown operation type: #{op}") end end # sort & return output sort_changelist(changelist) end |
#force_metadata_update_from_server(entry) ⇒ Object
618 619 620 621 622 623 624 |
# File 'lib/dbox/syncer.rb', line 618 def (entry) res = gather_remote_info(entry) unless res == :not_modified database.update_entry_by_path(entry[:path], :modified => res[:modified], :revision => res[:revision], :remote_hash => res[:remote_hash]) end (database.find_by_path(entry[:path])) end |
#is_dir(path) ⇒ Object
569 570 571 |
# File 'lib/dbox/syncer.rb', line 569 def is_dir(path) File.directory?(path) end |
#list_contents(dir) ⇒ Object
587 588 589 590 591 |
# File 'lib/dbox/syncer.rb', line 587 def list_contents(dir) local_path = dir[:local_path] paths = Dir.entries(local_path).reject {|s| s == "." || s == ".." || s.start_with?(".") } paths.map {|p| local_to_relative_path(File.join(local_path, p)) } end |
#modified?(entry, res) ⇒ Boolean
573 574 575 576 577 578 579 580 581 582 583 584 585 |
# File 'lib/dbox/syncer.rb', line 573 def modified?(entry, res) out = true if entry[:is_dir] out = !times_equal?(entry[:modified], res[:modified]) log.debug "#{entry[:path]} modified? t#{time_to_s(entry[:modified])} vs. t#{time_to_s(res[:modified])} => #{out}" else eh = entry[:local_hash] rh = res[:local_hash] out = !(eh && rh && eh == rh) log.debug "#{entry[:path]} modified? #{eh} vs. #{rh} => #{out}" end out end |
#mtime(path) ⇒ Object
565 566 567 |
# File 'lib/dbox/syncer.rb', line 565 def mtime(path) File.mtime(path) end |
#practice ⇒ Object
423 424 425 426 427 |
# File 'lib/dbox/syncer.rb', line 423 def practice dir = database.root_dir changes = calculate_changes(dir) log.debug "Changes that would be executed:\n" + changes.map {|c| c.inspect }.join("\n") end |
#upload_file(file) ⇒ Object
609 610 611 612 613 614 615 616 |
# File 'lib/dbox/syncer.rb', line 609 def upload_file(file) local_path = file[:local_path] remote_path = file[:remote_path] db_entry = database.find_by_path(file[:path]) last_revision = db_entry ? db_entry[:revision] : nil res = api.put_file(remote_path, local_path, last_revision) process_basic_remote_props(res) end |