From 705a416d74deaa6150aa468d6e9f77e71eb25f74 Mon Sep 17 00:00:00 2001 From: Nicolas McCurdy Date: Wed, 16 Apr 2014 17:20:05 -0400 Subject: [PATCH 1/6] Extract the CLI into a new Homesick::CLI class, while making Homesick a module --- bin/homesick | 2 +- lib/homesick.rb | 334 +---------------- lib/homesick/actions.rb | 2 +- lib/homesick/cli.rb | 335 ++++++++++++++++++ lib/homesick/shell.rb | 2 +- lib/homesick/utils.rb | 2 +- lib/homesick/version.rb | 2 +- ...{homesick_spec.rb => homesick_cli_spec.rb} | 6 +- 8 files changed, 346 insertions(+), 339 deletions(-) create mode 100644 lib/homesick/cli.rb rename spec/{homesick_spec.rb => homesick_cli_spec.rb} (99%) diff --git a/bin/homesick b/bin/homesick index aef6100..db98f1b 100755 --- a/bin/homesick +++ b/bin/homesick @@ -6,4 +6,4 @@ $LOAD_PATH.unshift lib.to_s require 'homesick' -Homesick.start +Homesick::CLI.start diff --git a/lib/homesick.rb b/lib/homesick.rb index f9a1359..bfd789c 100644 --- a/lib/homesick.rb +++ b/lib/homesick.rb @@ -1,343 +1,15 @@ # -*- encoding : utf-8 -*- -require 'thor' -# Homesick's command line interface (with some helper methods) -class Homesick < Thor +# Homesick's top-level module +module Homesick autoload :Shell, 'homesick/shell' autoload :Actions, 'homesick/actions' autoload :Version, 'homesick/version' autoload :Utils, 'homesick/utils' - - include Thor::Actions - include Homesick::Actions - include Homesick::Version - include Homesick::Utils - - add_runtime_options! + autoload :CLI, 'homesick/cli' GITHUB_NAME_REPO_PATTERN = /\A([A-Za-z0-9_-]+\/[A-Za-z0-9_-]+)\Z/ SUBDIR_FILENAME = '.homesick_subdir' DEFAULT_CASTLE_NAME = 'dotfiles' - - map '-v' => :version - map '--version' => :version - # Retain a mapped version of the symlink command for compatibility. - map symlink: :link - - def initialize(args = [], options = {}, config = {}) - super - self.shell = Homesick::Shell.new - end - - desc 'clone URI', 'Clone +uri+ as a castle for homesick' - def clone(uri) - inside repos_dir do - destination = nil - if File.exist?(uri) - uri = Pathname.new(uri).expand_path - fail "Castle already cloned to #{uri}" if uri.to_s.start_with?(repos_dir.to_s) - - destination = uri.basename - - ln_s uri, destination - elsif uri =~ GITHUB_NAME_REPO_PATTERN - destination = Pathname.new(uri).basename - git_clone "https://github.com/#{Regexp.last_match[1]}.git", - destination: destination - elsif uri =~ /%r([^%r]*?)(\.git)?\Z/ || uri =~ /[^:]+:([^:]+)(\.git)?\Z/ - destination = Pathname.new(Regexp.last_match[1]) - git_clone uri - else - fail "Unknown URI format: #{uri}" - end - - setup_castle(destination) - end - end - - desc 'rc CASTLE', 'Run the .homesickrc for the specified castle' - def rc(name = DEFAULT_CASTLE_NAME) - inside repos_dir do - destination = Pathname.new(name) - homesickrc = destination.join('.homesickrc').expand_path - if homesickrc.exist? - proceed = shell.yes?("#{name} has a .homesickrc. Proceed with evaling it? (This could be destructive)") - if proceed - shell.say_status 'eval', homesickrc - inside destination do - eval homesickrc.read, binding, homesickrc.expand_path.to_s - end - else - shell.say_status 'eval skip', - "not evaling #{homesickrc}, #{destination} may need manual configuration", - :blue - end - end - end - end - - desc 'pull CASTLE', 'Update the specified castle' - method_option :all, - type: :boolean, - default: false, - required: false, - desc: 'Update all cloned castles' - def pull(name = DEFAULT_CASTLE_NAME) - if options[:all] - inside_each_castle do |castle| - shell.say castle.to_s.gsub(repos_dir.to_s + '/', '') + ':' - update_castle castle - end - else - update_castle name - end - end - - desc 'commit CASTLE MESSAGE', "Commit the specified castle's changes" - def commit(name = DEFAULT_CASTLE_NAME, message = nil) - commit_castle name, message - end - - desc 'push CASTLE', 'Push the specified castle' - def push(name = DEFAULT_CASTLE_NAME) - push_castle name - end - - desc 'unlink CASTLE', 'Unsymlinks all dotfiles from the specified castle' - def unlink(name = DEFAULT_CASTLE_NAME) - check_castle_existance(name, 'symlink') - - inside castle_dir(name) do - subdirs = subdirs(name) - - # unlink files - unsymlink_each(name, castle_dir(name), subdirs) - - # unlink files in subdirs - subdirs.each do |subdir| - unsymlink_each(name, subdir, subdirs) - end - end - end - - desc 'link CASTLE', 'Symlinks all dotfiles from the specified castle' - method_option :force, - default: false, - desc: 'Overwrite existing conflicting symlinks without prompting.' - def link(name = DEFAULT_CASTLE_NAME) - check_castle_existance(name, 'symlink') - - inside castle_dir(name) do - subdirs = subdirs(name) - - # link files - symlink_each(name, castle_dir(name), subdirs) - - # link files in subdirs - subdirs.each do |subdir| - symlink_each(name, subdir, subdirs) - end - end - end - - desc 'track FILE CASTLE', 'add a file to a castle' - def track(file, castle = DEFAULT_CASTLE_NAME) - castle = Pathname.new(castle) - file = Pathname.new(file.chomp('/')) - check_castle_existance(castle, 'track') - - absolute_path = file.expand_path - relative_dir = absolute_path.relative_path_from(home_dir).dirname - castle_path = Pathname.new(castle_dir(castle)).join(relative_dir) - FileUtils.mkdir_p castle_path - - # Are we already tracking this or anything inside it? - target = Pathname.new(castle_path.join(file.basename)) - if target.exist? - if absolute_path.directory? - move_dir_contents(target, absolute_path) - absolute_path.rmtree - subdir_remove(castle, relative_dir + file.basename) - - elsif more_recent? absolute_path, target - target.delete - mv absolute_path, castle_path - else - shell.say_status(:track, - "#{target} already exists, and is more recent than #{file}. Run 'homesick SYMLINK CASTLE' to create symlinks.", - :blue) unless options[:quiet] - end - else - mv absolute_path, castle_path - end - - inside home_dir do - absolute_path = castle_path + file.basename - home_path = home_dir + relative_dir + file.basename - ln_s absolute_path, home_path - end - - inside castle_path do - git_add absolute_path - end - - # are we tracking something nested? Add the parent dir to the manifest - subdir_add(castle, relative_dir) unless relative_dir.eql?(Pathname.new('.')) - end - - desc 'list', 'List cloned castles' - def list - inside_each_castle do |castle| - say_status castle.relative_path_from(repos_dir).to_s, - `git config remote.origin.url`.chomp, - :cyan - end - end - - desc 'status CASTLE', 'Shows the git status of a castle' - def status(castle = DEFAULT_CASTLE_NAME) - check_castle_existance(castle, 'status') - inside repos_dir.join(castle) do - git_status - end - end - - desc 'diff CASTLE', 'Shows the git diff of uncommitted changes in a castle' - def diff(castle = DEFAULT_CASTLE_NAME) - check_castle_existance(castle, 'diff') - inside repos_dir.join(castle) do - git_diff - end - end - - desc 'show_path CASTLE', 'Prints the path of a castle' - def show_path(castle = DEFAULT_CASTLE_NAME) - check_castle_existance(castle, 'show_path') - say repos_dir.join(castle) - end - - desc 'generate PATH', 'generate a homesick-ready git repo at PATH' - def generate(castle) - castle = Pathname.new(castle).expand_path - - github_user = `git config github.user`.chomp - github_user = nil if github_user == '' - github_repo = castle.basename - - empty_directory castle - inside castle do - git_init - if github_user - url = "git@github.com:#{github_user}/#{github_repo}.git" - git_remote_add 'origin', url - end - - empty_directory 'home' - end - end - - desc 'destroy CASTLE', 'Delete all symlinks and remove the cloned repository' - def destroy(name) - check_castle_existance name, 'destroy' - - if shell.yes?('This will destroy your castle irreversible! Are you sure?') - unlink(name) - rm_rf repos_dir.join(name) - end - end - - desc 'cd CASTLE', 'Open a new shell in the root of the given castle' - def cd(castle = DEFAULT_CASTLE_NAME) - check_castle_existance castle, 'cd' - castle_dir = repos_dir.join(castle) - say_status "cd #{castle_dir.realpath}", - "Opening a new shell in castle '#{castle}'. To return to the original one exit from the new shell.", - :green - inside castle_dir do - system(ENV['SHELL']) - end - end - - desc 'open CASTLE', - 'Open your default editor in the root of the given castle' - def open(castle = DEFAULT_CASTLE_NAME) - unless ENV['EDITOR'] - say_status :error, - 'The $EDITOR environment variable must be set to use this command', - :red - - exit(1) - end - check_castle_existance castle, 'open' - castle_dir = repos_dir.join(castle) - say_status "#{ENV['EDITOR']} #{castle_dir.realpath}", - "Opening the root directory of castle '#{castle}' in editor '#{ENV['EDITOR']}'.", - :green - inside castle_dir do - system(ENV['EDITOR']) - end - end - - desc 'exec CASTLE COMMAND', - 'Execute a single shell command inside the root of a castle' - method_option :pretend, - type: :boolean, - default: false, - required: false, - desc: 'Perform a dry run of the command' - method_option :quiet, - type: :boolean, - default: false, - required: false, - desc: 'Run a command without any output from Homesick' - def exec(castle, *args) - check_castle_existance castle, 'exec' - unless args.count > 0 - say_status :error, - 'You must pass a shell command to execute', - :red - exit(1) - end - full_command = args.join(' ') - say_status "exec '#{full_command}'", - "#{options[:pretend] ? 'Would execute' : 'Executing command'} '#{full_command}' in castle '#{castle}'", - :green unless options[:quiet] - inside repos_dir.join(castle) do - system(full_command) unless options[:pretend] - end - end - - desc 'exec_all COMMAND', - 'Execute a single shell command inside the root of every cloned castle' - method_option :pretend, - type: :boolean, - default: false, - required: false, - desc: 'Perform a dry run of the command' - method_option :quiet, - type: :boolean, - default: false, - required: false, - desc: 'Run a command without any output from Homesick' - def exec_all(*args) - unless args.count > 0 - say_status :error, - 'You must pass a shell command to execute', - :red - exit(1) - end - full_command = args.join(' ') - inside_each_castle do |castle| - say_status "exec '#{full_command}'", - "#{options[:pretend] ? 'Would execute' : 'Executing command'} '#{full_command}' in castle '#{castle}'", - :green unless options[:quiet] - system(full_command) unless options[:pretend] - end - end - - desc 'version', 'Display the current version of homesick' - def version - say Homesick::Version::STRING - end end diff --git a/lib/homesick/actions.rb b/lib/homesick/actions.rb index edd2ec0..c2b996e 100644 --- a/lib/homesick/actions.rb +++ b/lib/homesick/actions.rb @@ -1,5 +1,5 @@ # -*- encoding : utf-8 -*- -class Homesick +module Homesick # Git-related and file-related helper methods for the Homesick class module Actions # TODO: move this to be more like thor's template, empty_directory, etc diff --git a/lib/homesick/cli.rb b/lib/homesick/cli.rb new file mode 100644 index 0000000..f23e1c4 --- /dev/null +++ b/lib/homesick/cli.rb @@ -0,0 +1,335 @@ +# -*- encoding : utf-8 -*- +require 'thor' + +module Homesick + # Homesick's command line interface + class CLI < Thor + include Thor::Actions + include Homesick::Actions + include Homesick::Version + include Homesick::Utils + + add_runtime_options! + + map '-v' => :version + map '--version' => :version + # Retain a mapped version of the symlink command for compatibility. + map symlink: :link + + def initialize(args = [], options = {}, config = {}) + super + self.shell = Homesick::Shell.new + end + + desc 'clone URI', 'Clone +uri+ as a castle for homesick' + def clone(uri) + inside repos_dir do + destination = nil + if File.exist?(uri) + uri = Pathname.new(uri).expand_path + fail "Castle already cloned to #{uri}" if uri.to_s.start_with?(repos_dir.to_s) + + destination = uri.basename + + ln_s uri, destination + elsif uri =~ GITHUB_NAME_REPO_PATTERN + destination = Pathname.new(uri).basename + git_clone "https://github.com/#{Regexp.last_match[1]}.git", + destination: destination + elsif uri =~ /%r([^%r]*?)(\.git)?\Z/ || uri =~ /[^:]+:([^:]+)(\.git)?\Z/ + destination = Pathname.new(Regexp.last_match[1]) + git_clone uri + else + fail "Unknown URI format: #{uri}" + end + + setup_castle(destination) + end + end + + desc 'rc CASTLE', 'Run the .homesickrc for the specified castle' + def rc(name = DEFAULT_CASTLE_NAME) + inside repos_dir do + destination = Pathname.new(name) + homesickrc = destination.join('.homesickrc').expand_path + if homesickrc.exist? + proceed = shell.yes?("#{name} has a .homesickrc. Proceed with evaling it? (This could be destructive)") + if proceed + shell.say_status 'eval', homesickrc + inside destination do + eval homesickrc.read, binding, homesickrc.expand_path.to_s + end + else + shell.say_status 'eval skip', + "not evaling #{homesickrc}, #{destination} may need manual configuration", + :blue + end + end + end + end + + desc 'pull CASTLE', 'Update the specified castle' + method_option :all, + type: :boolean, + default: false, + required: false, + desc: 'Update all cloned castles' + def pull(name = DEFAULT_CASTLE_NAME) + if options[:all] + inside_each_castle do |castle| + shell.say castle.to_s.gsub(repos_dir.to_s + '/', '') + ':' + update_castle castle + end + else + update_castle name + end + end + + desc 'commit CASTLE MESSAGE', "Commit the specified castle's changes" + def commit(name = DEFAULT_CASTLE_NAME, message = nil) + commit_castle name, message + end + + desc 'push CASTLE', 'Push the specified castle' + def push(name = DEFAULT_CASTLE_NAME) + push_castle name + end + + desc 'unlink CASTLE', 'Unsymlinks all dotfiles from the specified castle' + def unlink(name = DEFAULT_CASTLE_NAME) + check_castle_existance(name, 'symlink') + + inside castle_dir(name) do + subdirs = subdirs(name) + + # unlink files + unsymlink_each(name, castle_dir(name), subdirs) + + # unlink files in subdirs + subdirs.each do |subdir| + unsymlink_each(name, subdir, subdirs) + end + end + end + + desc 'link CASTLE', 'Symlinks all dotfiles from the specified castle' + method_option :force, + default: false, + desc: 'Overwrite existing conflicting symlinks without prompting.' + def link(name = DEFAULT_CASTLE_NAME) + check_castle_existance(name, 'symlink') + + inside castle_dir(name) do + subdirs = subdirs(name) + + # link files + symlink_each(name, castle_dir(name), subdirs) + + # link files in subdirs + subdirs.each do |subdir| + symlink_each(name, subdir, subdirs) + end + end + end + + desc 'track FILE CASTLE', 'add a file to a castle' + def track(file, castle = DEFAULT_CASTLE_NAME) + castle = Pathname.new(castle) + file = Pathname.new(file.chomp('/')) + check_castle_existance(castle, 'track') + + absolute_path = file.expand_path + relative_dir = absolute_path.relative_path_from(home_dir).dirname + castle_path = Pathname.new(castle_dir(castle)).join(relative_dir) + FileUtils.mkdir_p castle_path + + # Are we already tracking this or anything inside it? + target = Pathname.new(castle_path.join(file.basename)) + if target.exist? + if absolute_path.directory? + move_dir_contents(target, absolute_path) + absolute_path.rmtree + subdir_remove(castle, relative_dir + file.basename) + + elsif more_recent? absolute_path, target + target.delete + mv absolute_path, castle_path + else + shell.say_status(:track, + "#{target} already exists, and is more recent than #{file}. Run 'homesick SYMLINK CASTLE' to create symlinks.", + :blue) unless options[:quiet] + end + else + mv absolute_path, castle_path + end + + inside home_dir do + absolute_path = castle_path + file.basename + home_path = home_dir + relative_dir + file.basename + ln_s absolute_path, home_path + end + + inside castle_path do + git_add absolute_path + end + + # are we tracking something nested? Add the parent dir to the manifest + subdir_add(castle, relative_dir) unless relative_dir.eql?(Pathname.new('.')) + end + + desc 'list', 'List cloned castles' + def list + inside_each_castle do |castle| + say_status castle.relative_path_from(repos_dir).to_s, + `git config remote.origin.url`.chomp, + :cyan + end + end + + desc 'status CASTLE', 'Shows the git status of a castle' + def status(castle = DEFAULT_CASTLE_NAME) + check_castle_existance(castle, 'status') + inside repos_dir.join(castle) do + git_status + end + end + + desc 'diff CASTLE', 'Shows the git diff of uncommitted changes in a castle' + def diff(castle = DEFAULT_CASTLE_NAME) + check_castle_existance(castle, 'diff') + inside repos_dir.join(castle) do + git_diff + end + end + + desc 'show_path CASTLE', 'Prints the path of a castle' + def show_path(castle = DEFAULT_CASTLE_NAME) + check_castle_existance(castle, 'show_path') + say repos_dir.join(castle) + end + + desc 'generate PATH', 'generate a homesick-ready git repo at PATH' + def generate(castle) + castle = Pathname.new(castle).expand_path + + github_user = `git config github.user`.chomp + github_user = nil if github_user == '' + github_repo = castle.basename + + empty_directory castle + inside castle do + git_init + if github_user + url = "git@github.com:#{github_user}/#{github_repo}.git" + git_remote_add 'origin', url + end + + empty_directory 'home' + end + end + + desc 'destroy CASTLE', 'Delete all symlinks and remove the cloned repository' + def destroy(name) + check_castle_existance name, 'destroy' + + if shell.yes?('This will destroy your castle irreversible! Are you sure?') + unlink(name) + rm_rf repos_dir.join(name) + end + end + + desc 'cd CASTLE', 'Open a new shell in the root of the given castle' + def cd(castle = DEFAULT_CASTLE_NAME) + check_castle_existance castle, 'cd' + castle_dir = repos_dir.join(castle) + say_status "cd #{castle_dir.realpath}", + "Opening a new shell in castle '#{castle}'. To return to the original one exit from the new shell.", + :green + inside castle_dir do + system(ENV['SHELL']) + end + end + + desc 'open CASTLE', + 'Open your default editor in the root of the given castle' + def open(castle = DEFAULT_CASTLE_NAME) + unless ENV['EDITOR'] + say_status :error, + 'The $EDITOR environment variable must be set to use this command', + :red + + exit(1) + end + check_castle_existance castle, 'open' + castle_dir = repos_dir.join(castle) + say_status "#{ENV['EDITOR']} #{castle_dir.realpath}", + "Opening the root directory of castle '#{castle}' in editor '#{ENV['EDITOR']}'.", + :green + inside castle_dir do + system(ENV['EDITOR']) + end + end + + desc 'exec CASTLE COMMAND', + 'Execute a single shell command inside the root of a castle' + method_option :pretend, + type: :boolean, + default: false, + required: false, + desc: 'Perform a dry run of the command' + method_option :quiet, + type: :boolean, + default: false, + required: false, + desc: 'Run a command without any output from Homesick' + def exec(castle, *args) + check_castle_existance castle, 'exec' + unless args.count > 0 + say_status :error, + 'You must pass a shell command to execute', + :red + exit(1) + end + full_command = args.join(' ') + say_status "exec '#{full_command}'", + "#{options[:pretend] ? 'Would execute' : 'Executing command'} '#{full_command}' in castle '#{castle}'", + :green unless options[:quiet] + inside repos_dir.join(castle) do + system(full_command) unless options[:pretend] + end + end + + desc 'exec_all COMMAND', + 'Execute a single shell command inside the root of every cloned castle' + method_option :pretend, + type: :boolean, + default: false, + required: false, + desc: 'Perform a dry run of the command' + method_option :quiet, + type: :boolean, + default: false, + required: false, + desc: 'Run a command without any output from Homesick' + def exec_all(*args) + unless args.count > 0 + say_status :error, + 'You must pass a shell command to execute', + :red + exit(1) + end + full_command = args.join(' ') + inside_each_castle do |castle| + say_status "exec '#{full_command}'", + "#{options[:pretend] ? 'Would execute' : 'Executing command'} '#{full_command}' in castle '#{castle}'", + :green unless options[:quiet] + system(full_command) unless options[:pretend] + end + end + + desc 'version', 'Display the current version of homesick' + def version + say Homesick::Version::STRING + end + end +end diff --git a/lib/homesick/shell.rb b/lib/homesick/shell.rb index 7bf1a65..a8e6141 100644 --- a/lib/homesick/shell.rb +++ b/lib/homesick/shell.rb @@ -1,4 +1,4 @@ -class Homesick +module Homesick # Hack in support for diffing symlinks class Shell < Thor::Shell::Color def show_diff(destination, content) diff --git a/lib/homesick/utils.rb b/lib/homesick/utils.rb index 37244f9..4a5a6c8 100644 --- a/lib/homesick/utils.rb +++ b/lib/homesick/utils.rb @@ -1,5 +1,5 @@ # -*- encoding : utf-8 -*- -class Homesick +module Homesick # Various utility methods that are used by Homesick module Utils protected diff --git a/lib/homesick/version.rb b/lib/homesick/version.rb index 5c5dfcb..1e1c25c 100644 --- a/lib/homesick/version.rb +++ b/lib/homesick/version.rb @@ -1,5 +1,5 @@ # -*- encoding : utf-8 -*- -class Homesick +module Homesick # A representation of Homesick's version number in constants, including a # String of the entire version number module Version diff --git a/spec/homesick_spec.rb b/spec/homesick_cli_spec.rb similarity index 99% rename from spec/homesick_spec.rb rename to spec/homesick_cli_spec.rb index 26657d6..41f3dfe 100644 --- a/spec/homesick_spec.rb +++ b/spec/homesick_cli_spec.rb @@ -2,13 +2,13 @@ require 'spec_helper' require 'capture-output' -describe 'homesick' do +describe Homesick::CLI do let(:home) { create_construct } after { home.destroy! } let(:castles) { home.directory('.homesick/repos') } - let(:homesick) { Homesick.new } + let(:homesick) { Homesick::CLI.new } before { allow(homesick).to receive(:repos_dir).and_return(castles) } @@ -176,7 +176,7 @@ end context 'when forced' do - let(:homesick) { Homesick.new [], force: true } + let(:homesick) { Homesick::CLI.new [], force: true } it 'can override symlinks to directories' do somewhere_else = create_construct From f6c4e5e42e27b1f357eb97b60572202fe3a35929 Mon Sep 17 00:00:00 2001 From: Nicolas McCurdy Date: Wed, 16 Apr 2014 17:29:17 -0400 Subject: [PATCH 2/6] Require Thor in Homesick::Shell --- lib/homesick/shell.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/homesick/shell.rb b/lib/homesick/shell.rb index a8e6141..d53dbda 100644 --- a/lib/homesick/shell.rb +++ b/lib/homesick/shell.rb @@ -1,3 +1,5 @@ +require 'thor' + module Homesick # Hack in support for diffing symlinks class Shell < Thor::Shell::Color From 03d87807e01161ed12658685e1f4d1a3116d7a60 Mon Sep 17 00:00:00 2001 From: Nicolas McCurdy Date: Wed, 16 Apr 2014 17:48:36 -0400 Subject: [PATCH 3/6] Use require instead of autoload (fix #108) --- lib/homesick.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/homesick.rb b/lib/homesick.rb index bfd789c..4780645 100644 --- a/lib/homesick.rb +++ b/lib/homesick.rb @@ -1,13 +1,12 @@ # -*- encoding : utf-8 -*- +require 'homesick/shell' +require 'homesick/actions' +require 'homesick/version' +require 'homesick/utils' +require 'homesick/cli' # Homesick's top-level module module Homesick - autoload :Shell, 'homesick/shell' - autoload :Actions, 'homesick/actions' - autoload :Version, 'homesick/version' - autoload :Utils, 'homesick/utils' - autoload :CLI, 'homesick/cli' - GITHUB_NAME_REPO_PATTERN = /\A([A-Za-z0-9_-]+\/[A-Za-z0-9_-]+)\Z/ SUBDIR_FILENAME = '.homesick_subdir' From 1c3403064ec05ace8e568ab43aa0bb7595a2cc9e Mon Sep 17 00:00:00 2001 From: Nicolas McCurdy Date: Wed, 16 Apr 2014 17:57:00 -0400 Subject: [PATCH 4/6] Don't refer to Homesick as a class --- lib/homesick/actions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/homesick/actions.rb b/lib/homesick/actions.rb index c2b996e..3f5de84 100644 --- a/lib/homesick/actions.rb +++ b/lib/homesick/actions.rb @@ -1,6 +1,6 @@ # -*- encoding : utf-8 -*- module Homesick - # Git-related and file-related helper methods for the Homesick class + # Git-related and file-related helper methods for Homesick module Actions # TODO: move this to be more like thor's template, empty_directory, etc def git_clone(repo, config = {}) From 214869786489ff3313aae04ec16eb2f93cf3f080 Mon Sep 17 00:00:00 2001 From: Nicolas McCurdy Date: Wed, 16 Apr 2014 13:46:08 -0400 Subject: [PATCH 5/6] Separate Actions into two new modules: FileActions and GitActions --- lib/homesick.rb | 3 +- lib/homesick/cli.rb | 3 +- lib/homesick/file_actions.rb | 89 +++++++++++++++++++++ lib/homesick/{actions.rb => git_actions.rb} | 88 +------------------- 4 files changed, 95 insertions(+), 88 deletions(-) create mode 100644 lib/homesick/file_actions.rb rename lib/homesick/{actions.rb => git_actions.rb} (51%) diff --git a/lib/homesick.rb b/lib/homesick.rb index 4780645..09f1358 100644 --- a/lib/homesick.rb +++ b/lib/homesick.rb @@ -1,6 +1,7 @@ # -*- encoding : utf-8 -*- require 'homesick/shell' -require 'homesick/actions' +require 'homesick/file_actions' +require 'homesick/git_actions' require 'homesick/version' require 'homesick/utils' require 'homesick/cli' diff --git a/lib/homesick/cli.rb b/lib/homesick/cli.rb index f23e1c4..83045e7 100644 --- a/lib/homesick/cli.rb +++ b/lib/homesick/cli.rb @@ -5,7 +5,8 @@ module Homesick # Homesick's command line interface class CLI < Thor include Thor::Actions - include Homesick::Actions + include Homesick::FileActions + include Homesick::GitActions include Homesick::Version include Homesick::Utils diff --git a/lib/homesick/file_actions.rb b/lib/homesick/file_actions.rb new file mode 100644 index 0000000..7108a25 --- /dev/null +++ b/lib/homesick/file_actions.rb @@ -0,0 +1,89 @@ +# -*- encoding : utf-8 -*- +module Homesick + # File-related helper methods for Homesick + module FileActions + def mv(source, destination, config = {}) + source = Pathname.new(source) + destination = Pathname.new(destination + source.basename) + + if destination.exist? + say_status :conflict, "#{destination} exists", :red unless options[:quiet] + + FileUtils.mv source, destination if (options[:force] || shell.file_collision(destination) { source }) && !options[:pretend] + else + # this needs some sort of message here. + FileUtils.mv source, destination unless options[:pretend] + end + end + + def rm_rf(dir) + say_status "rm -rf #{dir}", '', :green unless options[:quiet] + FileUtils.rm_r dir, force: true + end + + def rm_link(target) + target = Pathname.new(target) + + if target.symlink? + say_status :unlink, "#{target.expand_path}", :green unless options[:quiet] + FileUtils.rm_rf target + else + say_status :conflict, "#{target} is not a symlink", :red unless options[:quiet] + end + end + + def rm(file) + say_status "rm #{file}", '', :green unless options[:quiet] + FileUtils.rm file, force: true + end + + def rm_r(dir) + say_status "rm -r #{dir}", '', :green unless options[:quiet] + FileUtils.rm_r dir + end + + def ln_s(source, destination, config = {}) + source = Pathname.new(source) + destination = Pathname.new(destination) + FileUtils.mkdir_p destination.dirname + + action = if destination.symlink? && destination.readlink == source + :identical + elsif destination.symlink? + :symlink_conflict + elsif destination.exist? + :conflict + else + :success + end + + handle_symlink_action action, source, destination + end + + def handle_symlink_action(action, source, destination) + case action + when :identical + say_status :identical, destination.expand_path, :blue unless options[:quiet] + when :symlink_conflict + say_status :conflict, + "#{destination} exists and points to #{destination.readlink}", + :red unless options[:quiet] + + FileUtils.rm destination + FileUtils.ln_s source, destination, force: true unless options[:pretend] + when :conflict + say_status :conflict, "#{destination} exists", :red unless options[:quiet] + + if collision_accepted? + FileUtils.rm_r destination, force: true unless options[:pretend] + FileUtils.ln_s source, destination, force: true unless options[:pretend] + end + else + say_status :symlink, + "#{source.expand_path} to #{destination.expand_path}", + :green unless options[:quiet] + FileUtils.ln_s source, destination unless options[:pretend] + end + end + end +end diff --git a/lib/homesick/actions.rb b/lib/homesick/git_actions.rb similarity index 51% rename from lib/homesick/actions.rb rename to lib/homesick/git_actions.rb index 3f5de84..201dbd5 100644 --- a/lib/homesick/actions.rb +++ b/lib/homesick/git_actions.rb @@ -1,7 +1,7 @@ # -*- encoding : utf-8 -*- module Homesick - # Git-related and file-related helper methods for Homesick - module Actions + # Git-related helper methods for Homesick + module GitActions # TODO: move this to be more like thor's template, empty_directory, etc def git_clone(repo, config = {}) config ||= {} @@ -88,89 +88,5 @@ def git_diff(config = {}) say_status 'git diff', '', :green unless options[:quiet] system 'git diff' unless options[:pretend] end - - def mv(source, destination, config = {}) - source = Pathname.new(source) - destination = Pathname.new(destination + source.basename) - - if destination.exist? - say_status :conflict, "#{destination} exists", :red unless options[:quiet] - - FileUtils.mv source, destination if (options[:force] || shell.file_collision(destination) { source }) && !options[:pretend] - else - # this needs some sort of message here. - FileUtils.mv source, destination unless options[:pretend] - end - end - - def rm_rf(dir) - say_status "rm -rf #{dir}", '', :green unless options[:quiet] - FileUtils.rm_r dir, force: true - end - - def rm_link(target) - target = Pathname.new(target) - - if target.symlink? - say_status :unlink, "#{target.expand_path}", :green unless options[:quiet] - FileUtils.rm_rf target - else - say_status :conflict, "#{target} is not a symlink", :red unless options[:quiet] - end - end - - def rm(file) - say_status "rm #{file}", '', :green unless options[:quiet] - FileUtils.rm file, force: true - end - - def rm_r(dir) - say_status "rm -r #{dir}", '', :green unless options[:quiet] - FileUtils.rm_r dir - end - - def ln_s(source, destination, config = {}) - source = Pathname.new(source) - destination = Pathname.new(destination) - FileUtils.mkdir_p destination.dirname - - action = if destination.symlink? && destination.readlink == source - :identical - elsif destination.symlink? - :symlink_conflict - elsif destination.exist? - :conflict - else - :success - end - - handle_symlink_action action, source, destination - end - - def handle_symlink_action(action, source, destination) - case action - when :identical - say_status :identical, destination.expand_path, :blue unless options[:quiet] - when :symlink_conflict - say_status :conflict, - "#{destination} exists and points to #{destination.readlink}", - :red unless options[:quiet] - - FileUtils.rm destination - FileUtils.ln_s source, destination, force: true unless options[:pretend] - when :conflict - say_status :conflict, "#{destination} exists", :red unless options[:quiet] - - if collision_accepted? - FileUtils.rm_r destination, force: true unless options[:pretend] - FileUtils.ln_s source, destination, force: true unless options[:pretend] - end - else - say_status :symlink, - "#{source.expand_path} to #{destination.expand_path}", - :green unless options[:quiet] - FileUtils.ln_s source, destination unless options[:pretend] - end - end end end From d9ee74bf142f82d8d57baf3197d72410d93652b3 Mon Sep 17 00:00:00 2001 From: Nicolas McCurdy Date: Wed, 16 Apr 2014 20:54:26 -0400 Subject: [PATCH 6/6] Move GitActions and FileActions into a new Actions module --- lib/homesick.rb | 4 +- lib/homesick/actions/file_actions.rb | 91 +++++++++++++++++++++++++++ lib/homesick/actions/git_actions.rb | 94 ++++++++++++++++++++++++++++ lib/homesick/cli.rb | 4 +- lib/homesick/file_actions.rb | 89 -------------------------- lib/homesick/git_actions.rb | 92 --------------------------- 6 files changed, 189 insertions(+), 185 deletions(-) create mode 100644 lib/homesick/actions/file_actions.rb create mode 100644 lib/homesick/actions/git_actions.rb delete mode 100644 lib/homesick/file_actions.rb delete mode 100644 lib/homesick/git_actions.rb diff --git a/lib/homesick.rb b/lib/homesick.rb index 09f1358..bd7d6cb 100644 --- a/lib/homesick.rb +++ b/lib/homesick.rb @@ -1,7 +1,7 @@ # -*- encoding : utf-8 -*- require 'homesick/shell' -require 'homesick/file_actions' -require 'homesick/git_actions' +require 'homesick/actions/file_actions' +require 'homesick/actions/git_actions' require 'homesick/version' require 'homesick/utils' require 'homesick/cli' diff --git a/lib/homesick/actions/file_actions.rb b/lib/homesick/actions/file_actions.rb new file mode 100644 index 0000000..e4d4a37 --- /dev/null +++ b/lib/homesick/actions/file_actions.rb @@ -0,0 +1,91 @@ +# -*- encoding : utf-8 -*- +module Homesick + module Actions + # File-related helper methods for Homesick + module FileActions + def mv(source, destination, config = {}) + source = Pathname.new(source) + destination = Pathname.new(destination + source.basename) + + if destination.exist? + say_status :conflict, "#{destination} exists", :red unless options[:quiet] + + FileUtils.mv source, destination if (options[:force] || shell.file_collision(destination) { source }) && !options[:pretend] + else + # this needs some sort of message here. + FileUtils.mv source, destination unless options[:pretend] + end + end + + def rm_rf(dir) + say_status "rm -rf #{dir}", '', :green unless options[:quiet] + FileUtils.rm_r dir, force: true + end + + def rm_link(target) + target = Pathname.new(target) + + if target.symlink? + say_status :unlink, "#{target.expand_path}", :green unless options[:quiet] + FileUtils.rm_rf target + else + say_status :conflict, "#{target} is not a symlink", :red unless options[:quiet] + end + end + + def rm(file) + say_status "rm #{file}", '', :green unless options[:quiet] + FileUtils.rm file, force: true + end + + def rm_r(dir) + say_status "rm -r #{dir}", '', :green unless options[:quiet] + FileUtils.rm_r dir + end + + def ln_s(source, destination, config = {}) + source = Pathname.new(source) + destination = Pathname.new(destination) + FileUtils.mkdir_p destination.dirname + + action = if destination.symlink? && destination.readlink == source + :identical + elsif destination.symlink? + :symlink_conflict + elsif destination.exist? + :conflict + else + :success + end + + handle_symlink_action action, source, destination + end + + def handle_symlink_action(action, source, destination) + case action + when :identical + say_status :identical, destination.expand_path, :blue unless options[:quiet] + when :symlink_conflict + say_status :conflict, + "#{destination} exists and points to #{destination.readlink}", + :red unless options[:quiet] + + FileUtils.rm destination + FileUtils.ln_s source, destination, force: true unless options[:pretend] + when :conflict + say_status :conflict, "#{destination} exists", :red unless options[:quiet] + + if collision_accepted? + FileUtils.rm_r destination, force: true unless options[:pretend] + FileUtils.ln_s source, destination, force: true unless options[:pretend] + end + else + say_status :symlink, + "#{source.expand_path} to #{destination.expand_path}", + :green unless options[:quiet] + FileUtils.ln_s source, destination unless options[:pretend] + end + end + end + end +end diff --git a/lib/homesick/actions/git_actions.rb b/lib/homesick/actions/git_actions.rb new file mode 100644 index 0000000..5907f89 --- /dev/null +++ b/lib/homesick/actions/git_actions.rb @@ -0,0 +1,94 @@ +# -*- encoding : utf-8 -*- +module Homesick + module Actions + # Git-related helper methods for Homesick + module GitActions + # TODO: move this to be more like thor's template, empty_directory, etc + def git_clone(repo, config = {}) + config ||= {} + destination = config[:destination] || File.basename(repo, '.git') + + destination = Pathname.new(destination) unless destination.kind_of?(Pathname) + FileUtils.mkdir_p destination.dirname + + if destination.directory? + say_status :exist, destination.expand_path, :blue unless options[:quiet] + else + say_status 'git clone', + "#{repo} to #{destination.expand_path}", + :green unless options[:quiet] + system "git clone -q --config push.default=upstream --recursive #{repo} #{destination}" unless options[:pretend] + end + end + + def git_init(path = '.') + path = Pathname.new(path) + + inside path do + if path.join('.git').exist? + say_status 'git init', 'already initialized', :blue unless options[:quiet] + else + say_status 'git init', '' unless options[:quiet] + system 'git init >/dev/null' unless options[:pretend] + end + end + end + + def git_remote_add(name, url) + existing_remote = `git config remote.#{name}.url`.chomp + existing_remote = nil if existing_remote == '' + + if existing_remote + say_status 'git remote', "#{name} already exists", :blue unless options[:quiet] + else + say_status 'git remote', "add #{name} #{url}" unless options[:quiet] + system "git remote add #{name} #{url}" unless options[:pretend] + end + end + + def git_submodule_init(config = {}) + say_status 'git submodule', 'init', :green unless options[:quiet] + system 'git submodule --quiet init' unless options[:pretend] + end + + def git_submodule_update(config = {}) + say_status 'git submodule', 'update', :green unless options[:quiet] + system 'git submodule --quiet update --init --recursive >/dev/null 2>&1' unless options[:pretend] + end + + def git_pull(config = {}) + say_status 'git pull', '', :green unless options[:quiet] + system 'git pull --quiet' unless options[:pretend] + end + + def git_push(config = {}) + say_status 'git push', '', :green unless options[:quiet] + system 'git push' unless options[:pretend] + end + + def git_commit_all(config = {}) + say_status 'git commit all', '', :green unless options[:quiet] + if config[:message] + system "git commit -a -m '#{config[:message]}'" unless options[:pretend] + else + system 'git commit -v -a' unless options[:pretend] + end + end + + def git_add(file, config = {}) + say_status 'git add file', '', :green unless options[:quiet] + system "git add '#{file}'" unless options[:pretend] + end + + def git_status(config = {}) + say_status 'git status', '', :green unless options[:quiet] + system 'git status' unless options[:pretend] + end + + def git_diff(config = {}) + say_status 'git diff', '', :green unless options[:quiet] + system 'git diff' unless options[:pretend] + end + end + end +end diff --git a/lib/homesick/cli.rb b/lib/homesick/cli.rb index 83045e7..debb29a 100644 --- a/lib/homesick/cli.rb +++ b/lib/homesick/cli.rb @@ -5,8 +5,8 @@ module Homesick # Homesick's command line interface class CLI < Thor include Thor::Actions - include Homesick::FileActions - include Homesick::GitActions + include Homesick::Actions::FileActions + include Homesick::Actions::GitActions include Homesick::Version include Homesick::Utils diff --git a/lib/homesick/file_actions.rb b/lib/homesick/file_actions.rb deleted file mode 100644 index 7108a25..0000000 --- a/lib/homesick/file_actions.rb +++ /dev/null @@ -1,89 +0,0 @@ -# -*- encoding : utf-8 -*- -module Homesick - # File-related helper methods for Homesick - module FileActions - def mv(source, destination, config = {}) - source = Pathname.new(source) - destination = Pathname.new(destination + source.basename) - - if destination.exist? - say_status :conflict, "#{destination} exists", :red unless options[:quiet] - - FileUtils.mv source, destination if (options[:force] || shell.file_collision(destination) { source }) && !options[:pretend] - else - # this needs some sort of message here. - FileUtils.mv source, destination unless options[:pretend] - end - end - - def rm_rf(dir) - say_status "rm -rf #{dir}", '', :green unless options[:quiet] - FileUtils.rm_r dir, force: true - end - - def rm_link(target) - target = Pathname.new(target) - - if target.symlink? - say_status :unlink, "#{target.expand_path}", :green unless options[:quiet] - FileUtils.rm_rf target - else - say_status :conflict, "#{target} is not a symlink", :red unless options[:quiet] - end - end - - def rm(file) - say_status "rm #{file}", '', :green unless options[:quiet] - FileUtils.rm file, force: true - end - - def rm_r(dir) - say_status "rm -r #{dir}", '', :green unless options[:quiet] - FileUtils.rm_r dir - end - - def ln_s(source, destination, config = {}) - source = Pathname.new(source) - destination = Pathname.new(destination) - FileUtils.mkdir_p destination.dirname - - action = if destination.symlink? && destination.readlink == source - :identical - elsif destination.symlink? - :symlink_conflict - elsif destination.exist? - :conflict - else - :success - end - - handle_symlink_action action, source, destination - end - - def handle_symlink_action(action, source, destination) - case action - when :identical - say_status :identical, destination.expand_path, :blue unless options[:quiet] - when :symlink_conflict - say_status :conflict, - "#{destination} exists and points to #{destination.readlink}", - :red unless options[:quiet] - - FileUtils.rm destination - FileUtils.ln_s source, destination, force: true unless options[:pretend] - when :conflict - say_status :conflict, "#{destination} exists", :red unless options[:quiet] - - if collision_accepted? - FileUtils.rm_r destination, force: true unless options[:pretend] - FileUtils.ln_s source, destination, force: true unless options[:pretend] - end - else - say_status :symlink, - "#{source.expand_path} to #{destination.expand_path}", - :green unless options[:quiet] - FileUtils.ln_s source, destination unless options[:pretend] - end - end - end -end diff --git a/lib/homesick/git_actions.rb b/lib/homesick/git_actions.rb deleted file mode 100644 index 201dbd5..0000000 --- a/lib/homesick/git_actions.rb +++ /dev/null @@ -1,92 +0,0 @@ -# -*- encoding : utf-8 -*- -module Homesick - # Git-related helper methods for Homesick - module GitActions - # TODO: move this to be more like thor's template, empty_directory, etc - def git_clone(repo, config = {}) - config ||= {} - destination = config[:destination] || File.basename(repo, '.git') - - destination = Pathname.new(destination) unless destination.kind_of?(Pathname) - FileUtils.mkdir_p destination.dirname - - if destination.directory? - say_status :exist, destination.expand_path, :blue unless options[:quiet] - else - say_status 'git clone', - "#{repo} to #{destination.expand_path}", - :green unless options[:quiet] - system "git clone -q --config push.default=upstream --recursive #{repo} #{destination}" unless options[:pretend] - end - end - - def git_init(path = '.') - path = Pathname.new(path) - - inside path do - if path.join('.git').exist? - say_status 'git init', 'already initialized', :blue unless options[:quiet] - else - say_status 'git init', '' unless options[:quiet] - system 'git init >/dev/null' unless options[:pretend] - end - end - end - - def git_remote_add(name, url) - existing_remote = `git config remote.#{name}.url`.chomp - existing_remote = nil if existing_remote == '' - - if existing_remote - say_status 'git remote', "#{name} already exists", :blue unless options[:quiet] - else - say_status 'git remote', "add #{name} #{url}" unless options[:quiet] - system "git remote add #{name} #{url}" unless options[:pretend] - end - end - - def git_submodule_init(config = {}) - say_status 'git submodule', 'init', :green unless options[:quiet] - system 'git submodule --quiet init' unless options[:pretend] - end - - def git_submodule_update(config = {}) - say_status 'git submodule', 'update', :green unless options[:quiet] - system 'git submodule --quiet update --init --recursive >/dev/null 2>&1' unless options[:pretend] - end - - def git_pull(config = {}) - say_status 'git pull', '', :green unless options[:quiet] - system 'git pull --quiet' unless options[:pretend] - end - - def git_push(config = {}) - say_status 'git push', '', :green unless options[:quiet] - system 'git push' unless options[:pretend] - end - - def git_commit_all(config = {}) - say_status 'git commit all', '', :green unless options[:quiet] - if config[:message] - system "git commit -a -m '#{config[:message]}'" unless options[:pretend] - else - system 'git commit -v -a' unless options[:pretend] - end - end - - def git_add(file, config = {}) - say_status 'git add file', '', :green unless options[:quiet] - system "git add '#{file}'" unless options[:pretend] - end - - def git_status(config = {}) - say_status 'git status', '', :green unless options[:quiet] - system 'git status' unless options[:pretend] - end - - def git_diff(config = {}) - say_status 'git diff', '', :green unless options[:quiet] - system 'git diff' unless options[:pretend] - end - end -end