From f9c057fccb1bba223ec85d7ef2ad3fe125af8a1f Mon Sep 17 00:00:00 2001 From: "Tom \"Ravi\" Hale" Date: Thu, 13 Oct 2016 17:00:16 +0700 Subject: [PATCH 01/10] Support specified config directory and XDG Base Dirs Spec Resolve Issue #462 - "Tmuxinator config files in a different location" Resolve Issue #360 - "XDG base directory support" Mentioned in Issue #427 - "Introduce Analytics Module" Allow `$TMUXINATOR_CONFIG` to specify a configuration directory, which will be created if it doesn't exist. This directory takes preference over honouring the XDG specification (below). If specified, this will be the only configuration location used. Honour the [XDG Base Directories Specification] (https://specifications.freedesktop.org/basedir-spec/latest/) with regards to user specific configuration files: Look for `tmuxinator` configuration files under `$XDG_CONFIG_HOME/tmuxinator`. For existing setups with no `$XDG_CONFIG_HOME` directory, continue to create new projects in `~/.tmuxinator` for backwards compatibility. If `$XDG_CONFIG_HOME/tmuxinator` exists, create new projects there. If neither `~/.tmuxinator` nor `$XDG_CONFIG_HOME/tmuxinator` exist, use `$XDG_CONFIG_HOME/tmuxinator` for new projects. When loading a project, search `$XDG_CONFIG_HOME/tmuxinator` before `~/.tmuxinator`. Document in comments and tests the existing behaviour of returning only the first project file found in a recursive search of a configuration directory. Add Config#directories: an array of the configuration director{y,ies} which exist. Update commands `implode` and `list` to operate upon this array. Rename `Config#root` -> `Config#directory` This directory is the only one used when creating new project files, and may be either `$TMUXINATOR_CONFIG`, `$XDG_CONFIG_HOME/tmuxinator` or `~/.tmuxinator`. Implementing XDG Base Dirs support with backwards compatibility means that two directories may contain project files, making the `root` nomenclature misleading as it implies a single directory. Rename `Config#project_in_root` -> `Config#global_project` See comment on `root` terminology above. `global_project` is also shorter and corresponds to the naming of `default_project` Rename `Config#project_in_local` -> `Config#local_project` Fit with the naming of both `global_project` and `default_project` --- CHANGELOG.md | 1 + lib/tmuxinator.rb | 3 +- lib/tmuxinator/cli.rb | 4 +- lib/tmuxinator/config.rb | 78 ++++++++-- .../TMUXINATOR_CONFIG/TMUXINATOR_CONFIG.yml | 0 spec/fixtures/dot-tmuxinator/both.yml | 0 .../fixtures/dot-tmuxinator/dup/local-dup.yml | 0 spec/fixtures/dot-tmuxinator/home.yml | 0 spec/fixtures/dot-tmuxinator/local-dup.yml | 0 spec/fixtures/xdg-tmuxinator/both.yml | 0 spec/fixtures/xdg-tmuxinator/xdg.yml | 0 spec/lib/tmuxinator/cli_spec.rb | 36 ++++- spec/lib/tmuxinator/config_spec.rb | 140 ++++++++++++++---- spec/spec_helper.rb | 1 + tmuxinator.gemspec | 3 +- 15 files changed, 213 insertions(+), 53 deletions(-) create mode 100644 spec/fixtures/TMUXINATOR_CONFIG/TMUXINATOR_CONFIG.yml create mode 100644 spec/fixtures/dot-tmuxinator/both.yml create mode 100644 spec/fixtures/dot-tmuxinator/dup/local-dup.yml create mode 100644 spec/fixtures/dot-tmuxinator/home.yml create mode 100644 spec/fixtures/dot-tmuxinator/local-dup.yml create mode 100644 spec/fixtures/xdg-tmuxinator/both.yml create mode 100644 spec/fixtures/xdg-tmuxinator/xdg.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b913ac7..6cc76c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - Removed support for Ruby 1.9.3 - Move gem dependencies from Gemfile to tmuxinator.gemspec - Add tmux 2.2 and 2.3 the TravisCI test matrix +- Support user-specified and XDG Base Dirs configuration directories ## 0.9.0 ### Misc diff --git a/lib/tmuxinator.rb b/lib/tmuxinator.rb index f3829d9b..2091b392 100644 --- a/lib/tmuxinator.rb +++ b/lib/tmuxinator.rb @@ -1,8 +1,9 @@ -require "yaml" require "erubis" require "shellwords" require "thor" require "thor/version" +require "xdg" +require "yaml" require "tmuxinator/util" require "tmuxinator/deprecations" diff --git a/lib/tmuxinator/cli.rb b/lib/tmuxinator/cli.rb index 56e3325a..75f3aa37 100644 --- a/lib/tmuxinator/cli.rb +++ b/lib/tmuxinator/cli.rb @@ -231,7 +231,9 @@ def delete(*projects) def implode if yes?("Are you sure you want to delete all tmuxinator configs?", :red) - FileUtils.remove_dir(Tmuxinator::Config.root) + Tmuxinator::Config.directories.each do |directory| + FileUtils.remove_dir(directory) + end say "Deleted all tmuxinator projects." end end diff --git a/lib/tmuxinator/config.rb b/lib/tmuxinator/config.rb index e71e7e82..d3f7f2d8 100644 --- a/lib/tmuxinator/config.rb +++ b/lib/tmuxinator/config.rb @@ -4,10 +4,26 @@ class Config NO_LOCAL_FILE_MSG = "Project file at ./.tmuxinator.yml doesn't exist." class << self - def root - root_dir = File.expand_path("#{ENV['HOME']}/.tmuxinator") - Dir.mkdir(root_dir) unless File.directory?(root_dir) - root_dir + # The directory (created if needed) in which to store new projects + def directory + environment = ENV['TMUXINATOR_CONFIG'] + if !environment.nil? && !environment.empty? + Dir.mkdir(environment) unless File.directory?(environment) + return environment + end + return xdg if File.directory?(xdg) + return home if File.directory?(home) + # No project directory specified or existant, default to XDG: + Dir.mkdir(xdg) + xdg + end + + def home + ENV['HOME'] + "/.tmuxinator" + end + + def xdg + XDG['CONFIG'].to_s + "/tmuxinator" end def sample @@ -15,7 +31,7 @@ def sample end def default - "#{ENV['HOME']}/.tmuxinator/default.yml" + "#{directory}/default.yml" end def default? @@ -46,25 +62,32 @@ def exists?(name) File.exist?(project(name)) end - def project_in_root(name) - projects = Dir.glob("#{root}/**/*.yml") - projects.detect { |project| File.basename(project, ".yml") == name } + # The first project found matching 'name' + def global_project(name) + project_in(ENV['TMUXINATOR_CONFIG'], name) || + project_in(xdg, name) || + project_in(home, name) end def local? - project_in_local + local_project end - def project_in_local + def local_project [LOCAL_DEFAULT].detect { |f| File.exist?(f) } end def default_project(name) - "#{root}/#{name}.yml" + "#{directory}/#{name}.yml" end + # Pathname of project file def project(name) - project_in_root(name) || project_in_local || default_project(name) + project_in(ENV['TMUXINATOR_CONFIG'], name) || + project_in(xdg, name) || + project_in(home, name) || + local_project || # refactor? + default_project(name) end def template @@ -75,9 +98,26 @@ def wemux_template asset_path "wemux_template.erb" end + # Sorted list of all .yml files, including duplicates def configs - Dir["#{Tmuxinator::Config.root}/**/*.yml"].sort.map do |path| - path.gsub("#{Tmuxinator::Config.root}/", "").gsub(".yml", "") + configs = [] + directories.each do |directory| + configs += Dir["#{directory}/**/*.yml"].collect do |path| + path.gsub("#{directory}/", "").gsub(".yml", "") + end + end + configs.sort + end + + # Existant directories which may contain project files + # Listed in search order + # Used by `implode` and `list` commands + def directories + environment = ENV['TMUXINATOR_CONFIG'] + if !environment.nil? && !environment.empty? + [environment] + else + [xdg, home].select { |d| File.directory? d } end end @@ -89,7 +129,7 @@ def validate(options = {}) project_file = if name.nil? raise NO_LOCAL_FILE_MSG \ unless Tmuxinator::Config.local? - project_in_local + local_project else raise "Project #{name} doesn't exist." \ unless Tmuxinator::Config.exists?(name) @@ -103,6 +143,14 @@ def validate(options = {}) def asset_path(asset) "#{File.dirname(__FILE__)}/assets/#{asset}" end + + # The first pathname of the project named 'name' found while + # recursively searching 'directory' + def project_in(directory, name) + return nil if String(directory).empty? + projects = Dir.glob("#{directory}/**/*.yml") + projects.detect { |project| File.basename(project, ".yml") == name } + end end end end diff --git a/spec/fixtures/TMUXINATOR_CONFIG/TMUXINATOR_CONFIG.yml b/spec/fixtures/TMUXINATOR_CONFIG/TMUXINATOR_CONFIG.yml new file mode 100644 index 00000000..e69de29b diff --git a/spec/fixtures/dot-tmuxinator/both.yml b/spec/fixtures/dot-tmuxinator/both.yml new file mode 100644 index 00000000..e69de29b diff --git a/spec/fixtures/dot-tmuxinator/dup/local-dup.yml b/spec/fixtures/dot-tmuxinator/dup/local-dup.yml new file mode 100644 index 00000000..e69de29b diff --git a/spec/fixtures/dot-tmuxinator/home.yml b/spec/fixtures/dot-tmuxinator/home.yml new file mode 100644 index 00000000..e69de29b diff --git a/spec/fixtures/dot-tmuxinator/local-dup.yml b/spec/fixtures/dot-tmuxinator/local-dup.yml new file mode 100644 index 00000000..e69de29b diff --git a/spec/fixtures/xdg-tmuxinator/both.yml b/spec/fixtures/xdg-tmuxinator/both.yml new file mode 100644 index 00000000..e69de29b diff --git a/spec/fixtures/xdg-tmuxinator/xdg.yml b/spec/fixtures/xdg-tmuxinator/xdg.yml new file mode 100644 index 00000000..e69de29b diff --git a/spec/lib/tmuxinator/cli_spec.rb b/spec/lib/tmuxinator/cli_spec.rb index 7eb6c6d6..2814e1f0 100644 --- a/spec/lib/tmuxinator/cli_spec.rb +++ b/spec/lib/tmuxinator/cli_spec.rb @@ -1,4 +1,5 @@ require "spec_helper" + describe Tmuxinator::Cli do let(:cli) { Tmuxinator::Cli } @@ -231,16 +232,16 @@ end end - context "files exists" do - let(:root_path) { "#{ENV['HOME']}\/\.tmuxinator\/#{name}\.yml" } + context "file exists" do + let(:project_path) { "#{Tmuxinator::Config.project(name)}" } before do allow(File).to receive(:exist?).with(anything).and_return(false) - expect(File).to receive(:exist?).with(root_path).and_return(true) + expect(File).to receive(:exist?).with(project_path).and_return(true) end it "just opens the file" do - expect(Kernel).to receive(:system).with(%r{#{root_path}}) + expect(Kernel).to receive(:system).with(%r{#{project_path}}) capture_io { cli.start } end end @@ -264,7 +265,7 @@ end end - context "files exists" do + context "file exists" do let(:path) { Tmuxinator::Config::LOCAL_DEFAULT } before do expect(File).to receive(:exist?).with(path) { true } @@ -444,10 +445,29 @@ capture_io { cli.start } end - it "deletes all projects" do - expect(FileUtils).to receive(:remove_dir) + it "deletes the configuration directory(s)" do + # allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg) { true } + # allow(File).to receive(:directory?).with(Tmuxinator::Config.home) { true } + allow(Tmuxinator::Config).to receive(:directories) { \ + [Tmuxinator::Config.xdg, Tmuxinator::Config.home] } + expect(FileUtils).to receive(:remove_dir).once + .with(Tmuxinator::Config.xdg) + expect(FileUtils).to receive(:remove_dir).once + .with(Tmuxinator::Config.home) + expect(FileUtils).to receive(:remove_dir).never capture_io { cli.start } end + + context "$TMUXINATOR_CONFIG specified" do + it "only deletes projects in that directory" do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('TMUXINATOR_CONFIG').and_return "dir" + allow(File).to receive(:directory?).with("dir").and_return true + expect(FileUtils).to receive(:remove_dir).once.with("dir") + expect(FileUtils).to receive(:remove_dir).never + capture_io { cli.start } + end + end end describe "#list" do @@ -571,7 +591,7 @@ subject { described_class.new.create_project(params) } before do - allow(Tmuxinator::Config).to receive_messages(root: path) + allow(Tmuxinator::Config).to receive_messages(directory: path) end it_should_behave_like :a_proper_project diff --git a/spec/lib/tmuxinator/config_spec.rb b/spec/lib/tmuxinator/config_spec.rb index 6316f5fb..f716bfef 100644 --- a/spec/lib/tmuxinator/config_spec.rb +++ b/spec/lib/tmuxinator/config_spec.rb @@ -1,9 +1,83 @@ require "spec_helper" describe Tmuxinator::Config do - describe "#root" do - it "is ~/.tmuxintaor" do - expect(Tmuxinator::Config.root).to eq "#{ENV['HOME']}/.tmuxinator" + let(:fixtures_dir) { File.expand_path("../../../fixtures/", __FILE__) } + let(:xdg_config_dir) { "#{fixtures_dir}/xdg-tmuxinator" } + let(:home_config_dir) { "#{fixtures_dir}/dot-tmuxinator" } + + describe "#directory" do + context 'environment variable $TMUXINATOR_CONFIG set' do + it "is $TMUXINATOR_CONFIG" do + allow(ENV).to receive(:[]).with('TMUXINATOR_CONFIG') + .and_return 'expected' + allow(File).to receive(:directory?).and_return true + expect(Tmuxinator::Config.directory).to eq 'expected' + end + end + + context "only ~/.tmuxinator exists" do + it "is ~/.tmuxinator" do + allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg) + .and_return false + allow(File).to receive(:directory?).with(Tmuxinator::Config.home) + .and_return true + expect(Tmuxinator::Config.directory).to eq Tmuxinator::Config.home + end + end + + context "only $XDG_CONFIG_HOME/tmuxinator exists" do + it "is #xdg" do + allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg) + .and_return true + allow(File).to receive(:directory?).with(Tmuxinator::Config.home) + .and_return false + expect(Tmuxinator::Config.directory).to eq Tmuxinator::Config.xdg + end + end + + context "both $XDG_CONFIG_HOME/tmuxinator and ~/.tmuxinator exist" do + it "is #xdg" do + allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg) + .and_return true + allow(File).to receive(:directory?).with(Tmuxinator::Config.home) + .and_return true + expect(Tmuxinator::Config.directory).to eq Tmuxinator::Config.xdg + end + end + end + + describe "#directories" do + it "is empty if no configuration directories exist" do + allow(File).to receive(:directory?).and_return false + expect(Tmuxinator::Config.directories).to eq [] + end + + it "is only [$TMUXINATOR_CONFIG] if set" do + allow(ENV).to receive(:[]).with('TMUXINATOR_CONFIG') + .and_return 'expected' + allow(File).to receive(:directory?).and_return true + expect(Tmuxinator::Config.directories).to eq ['expected'] + end + + it "contains #xdg before #home" do + allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg) + .and_return true + allow(File).to receive(:directory?).with(Tmuxinator::Config.home) + .and_return true + expect(Tmuxinator::Config.directories).to eq \ + [Tmuxinator::Config.xdg, Tmuxinator::Config.home] + end + end + + describe "#home" do + it "is ~/.tmuxinator" do + expect(Tmuxinator::Config.home).to eq "#{ENV['HOME']}/.tmuxinator" + end + end + + describe "#xdg" do + it "is $XDG_CONFIG_HOME/tmuxinator" do + expect(Tmuxinator::Config.xdg).to eq "#{XDG['CONFIG_HOME']}/tmuxinator" end end @@ -42,7 +116,7 @@ end describe "#default?" do - let(:root) { Tmuxinator::Config.root } + let(:directory) { Tmuxinator::Config.directory } let(:local_default) { Tmuxinator::Config::LOCAL_DEFAULT } let(:proj_default) { Tmuxinator::Config.default } @@ -71,11 +145,18 @@ describe "#configs" do before do - allow(Dir).to receive_messages(:[] => ["test.yml"]) + allow(Tmuxinator::Config).to receive_messages(xdg: xdg_config_dir) + allow(Tmuxinator::Config).to receive_messages(home: home_config_dir) + end + + it "gets a sorted list of all projects" do + expect(Tmuxinator::Config.configs).to eq ["both", "both", "dup/local-dup", "home", "local-dup", "xdg"] end - it "gets a list of all projects" do - expect(Tmuxinator::Config.configs).to include("test") + it "lists only projects in $TMUXINATOR_CONFIG when set" do + allow(ENV).to receive(:[]).with('TMUXINATOR_CONFIG') + .and_return "#{fixtures_dir}/TMUXINATOR_CONFIG" + expect(Tmuxinator::Config.configs).to eq ["TMUXINATOR_CONFIG"] end end @@ -156,24 +237,31 @@ end end - describe "#project_in_root" do - let(:root) { Tmuxinator::Config.root } - let(:base) { "#{root}/sample.yml" } + describe "#global_project" do + let(:directory) { Tmuxinator::Config.directory } + let(:base) { "#{directory}/sample.yml" } + let(:first_dup) { "#{home_config_dir}/dup/local-dup.yml" } before do - path = File.expand_path("../../../fixtures/", __FILE__) - allow(Tmuxinator::Config).to receive_messages(root: path) + allow(Tmuxinator::Config).to receive_messages(xdg: fixtures_dir) + allow(Tmuxinator::Config).to receive_messages(home: fixtures_dir) end context "with project yml" do it "gets the project as path to the yml file" do - expect(Tmuxinator::Config.project_in_root("sample")).to eq base + expect(Tmuxinator::Config.global_project("sample")).to eq base end end context "without project yml" do it "gets the project as path to the yml file" do - expect(Tmuxinator::Config.project_in_root("new-project")).to be_nil + expect(Tmuxinator::Config.global_project("new-project")).to be_nil + end + end + + context "with duplicate project files" do + it "is the first .yml file found" do + expect(Tmuxinator::Config.global_project("local-dup")).to eq first_dup end end end @@ -186,39 +274,38 @@ end end - describe "#project_in_local" do + describe "#local_project" do let(:default) { Tmuxinator::Config::LOCAL_DEFAULT } context "with a project yml" do it "gets the project as path to the yml file" do expect(File).to receive(:exist?).with(default) { true } - expect(Tmuxinator::Config.project_in_local).to eq default + expect(Tmuxinator::Config.local_project).to eq default end end context "without project yml" do it "gets the project as path to the yml file" do - expect(Tmuxinator::Config.project_in_local).to be_nil + expect(Tmuxinator::Config.local_project).to be_nil end end end describe "#project" do - let(:root) { Tmuxinator::Config.root } - let(:path) { File.expand_path("../../../fixtures/", __FILE__) } + let(:directory) { Tmuxinator::Config.directory } let(:default) { Tmuxinator::Config::LOCAL_DEFAULT } - context "with project yml in the root directory" do + context "with an non-local project yml" do before do - allow(Tmuxinator::Config).to receive_messages(root: path) + allow(Tmuxinator::Config).to receive_messages(directory: fixtures_dir) end it "gets the project as path to the yml file" do - expect(Tmuxinator::Config.project("sample")).to eq "#{root}/sample.yml" + expect(Tmuxinator::Config.project("sample")).to eq "#{directory}/sample.yml" end end - context "with a local project, but no project in root" do + context "with a local project, but no global project" do it "gets the project as path to the yml file" do expect(File).to receive(:exist?).with(default) { true } expect(Tmuxinator::Config.project("sample")).to eq "./.tmuxinator.yml" @@ -226,7 +313,7 @@ end context "without project yml" do - let(:expected) { "#{root}/new-project.yml" } + let(:expected) { "#{directory}/new-project.yml" } it "gets the project as path to the yml file" do expect(Tmuxinator::Config.project("new-project")).to eq expected end @@ -234,7 +321,6 @@ end describe "#validate" do - let(:path) { File.expand_path("../../../fixtures", __FILE__) } let(:default) { Tmuxinator::Config::LOCAL_DEFAULT } context "when a project name is provided" do @@ -245,7 +331,7 @@ end it "should load and validate the project" do - expect(Tmuxinator::Config).to receive_messages(root: path) + expect(Tmuxinator::Config).to receive_messages(directory: fixtures_dir) expect(Tmuxinator::Config.validate(name: "sample")).to \ be_a Tmuxinator::Project end @@ -260,7 +346,7 @@ end it "should load and validate the project" do - content = File.read(File.join(path, "sample.yml")) + content = File.read(File.join(fixtures_dir, "sample.yml")) expect(File).to receive(:exist?).with(default).at_least(:once) { true } expect(File).to receive(:read).with(default).and_return(content) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b558cdaf..4c16ee53 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,7 @@ require "coveralls" require "simplecov" require "pry" +require "xdg" formatters = [ SimpleCov::Formatter::HTMLFormatter, diff --git a/tmuxinator.gemspec b/tmuxinator.gemspec index a055ac5e..60fb6a0b 100644 --- a/tmuxinator.gemspec +++ b/tmuxinator.gemspec @@ -38,8 +38,9 @@ Gem::Specification.new do |s| s.required_rubygems_version = ">= 1.8.23" s.required_ruby_version = ">= 2.0.0" - s.add_dependency "thor", "~> 0.19", ">= 0.15.0" s.add_dependency "erubis", "~> 2.6" + s.add_dependency "thor", "~> 0.19", ">= 0.15.0" + s.add_dependency "xdg", "~>2.2", ">= 2.2.3" s.add_development_dependency "bundler", "~> 1.3" s.add_development_dependency "rake", "~> 10.4" From 7a85a44ab2b35e99797196975f1f7ceebe8ee897 Mon Sep 17 00:00:00 2001 From: "Tom \"Ravi\" Hale" Date: Tue, 18 Oct 2016 14:10:12 +0700 Subject: [PATCH 02/10] Sort dependency lists --- spec/spec_helper.rb | 2 +- tmuxinator.gemspec | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4c16ee53..13f5154b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,6 @@ require "coveralls" -require "simplecov" require "pry" +require "simplecov" require "xdg" formatters = [ diff --git a/tmuxinator.gemspec b/tmuxinator.gemspec index 60fb6a0b..a725bcc2 100644 --- a/tmuxinator.gemspec +++ b/tmuxinator.gemspec @@ -42,15 +42,15 @@ Gem::Specification.new do |s| s.add_dependency "thor", "~> 0.19", ">= 0.15.0" s.add_dependency "xdg", "~>2.2", ">= 2.2.3" + s.add_development_dependency "activesupport", "< 5.0.0" # Please see issue #432 + s.add_development_dependency "awesome_print", "~> 1.2" s.add_development_dependency "bundler", "~> 1.3" - s.add_development_dependency "rake", "~> 10.4" - s.add_development_dependency "rspec", "~> 3.3" - s.add_development_dependency "simplecov", "~> 0.11.0" s.add_development_dependency "coveralls", "~> 0.7" - s.add_development_dependency "awesome_print", "~> 1.2" - s.add_development_dependency "pry", "~> 0.10" s.add_development_dependency "factory_girl", "~> 4.5" - s.add_development_dependency "activesupport", "< 5.0.0" # Please see issue #432 + s.add_development_dependency "pry", "~> 0.10" + s.add_development_dependency "rake", "~> 10.4" + s.add_development_dependency "rspec", "~> 3.3" s.add_development_dependency "rubocop", "~> 0.35.1" + s.add_development_dependency "simplecov", "~> 0.11.0" end From 93f58424cd59ba250c3ae263dd72a510fe930060 Mon Sep 17 00:00:00 2001 From: "Tom \"Ravi\" Hale" Date: Tue, 18 Oct 2016 14:50:36 +0700 Subject: [PATCH 03/10] Add aliases for deprecated methods Allow existing code to work with renamed methods Deprecated methods: ignore the 1st, use the 2nd alias :root :directory alias :project_in_root :global_project alias :project_in_local :local_project --- lib/tmuxinator/config.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/tmuxinator/config.rb b/lib/tmuxinator/config.rb index d3f7f2d8..71138fa6 100644 --- a/lib/tmuxinator/config.rb +++ b/lib/tmuxinator/config.rb @@ -138,6 +138,11 @@ def validate(options = {}) Tmuxinator::Project.load(project_file, options).validate! end + # Deprecated methods: ignore the 1st, use the 2nd + alias :root :directory + alias :project_in_root :global_project + alias :project_in_local :local_project + private def asset_path(asset) From a69ee4367fc1a665d8f5a7def8ca41c3070709c0 Mon Sep 17 00:00:00 2001 From: "Tom \"Ravi\" Hale" Date: Tue, 8 Nov 2016 14:20:40 +0700 Subject: [PATCH 04/10] Add failing test with non-existant parent --- spec/lib/tmuxinator/config_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spec/lib/tmuxinator/config_spec.rb b/spec/lib/tmuxinator/config_spec.rb index f716bfef..baf356ed 100644 --- a/spec/lib/tmuxinator/config_spec.rb +++ b/spec/lib/tmuxinator/config_spec.rb @@ -13,7 +13,22 @@ allow(File).to receive(:directory?).and_return true expect(Tmuxinator::Config.directory).to eq 'expected' end + context 'parent directory does not exist' do + it 'creates parent directories if required' do + non_existant = '/does-not-exist' # a directory which does not exist + allow(Tmuxinator::Config).to receive(:xdg) \ + .and_return non_existant + '/tmuxinator' + # allow(ENV).to receive(:[]).and_call_original + # allow(ENV).to receive(:[]).with('XDG_CONFIG_HOME') + # .and_return non_existant + # allow(Dir).to receive(:mkdir).with(non_existant) + allow(File).to receive(:directory?).and_return false + expect(Dir).to receive(:mkdir).with(non_existant) + allow(Dir).to receive(:mkdir).with(non_existant + '/tmuxinator') + expect(Tmuxinator::Config.directory).to eq(non_existant + '/tmuxinator') + end end + end context "only ~/.tmuxinator exists" do it "is ~/.tmuxinator" do From 3e3b1bc0c82d6ee2ccc240b000929f301b49ace7 Mon Sep 17 00:00:00 2001 From: "Tom \"Ravi\" Hale" Date: Tue, 8 Nov 2016 14:41:06 +0700 Subject: [PATCH 05/10] Refactor Config#project --- lib/tmuxinator/config.rb | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/tmuxinator/config.rb b/lib/tmuxinator/config.rb index 71138fa6..58c8d37f 100644 --- a/lib/tmuxinator/config.rb +++ b/lib/tmuxinator/config.rb @@ -62,17 +62,17 @@ def exists?(name) File.exist?(project(name)) end - # The first project found matching 'name' + def local? + local_project + end + + # Pathname of given project searching only global directories def global_project(name) project_in(ENV['TMUXINATOR_CONFIG'], name) || project_in(xdg, name) || project_in(home, name) end - def local? - local_project - end - def local_project [LOCAL_DEFAULT].detect { |f| File.exist?(f) } end @@ -81,12 +81,10 @@ def default_project(name) "#{directory}/#{name}.yml" end - # Pathname of project file + # Pathname of the given project def project(name) - project_in(ENV['TMUXINATOR_CONFIG'], name) || - project_in(xdg, name) || - project_in(home, name) || - local_project || # refactor? + global_project(name) || + local_project || default_project(name) end From bfe338b70dec0969bc183d0b25b6be977667b237 Mon Sep 17 00:00:00 2001 From: "Tom \"Ravi\" Hale" Date: Tue, 8 Nov 2016 16:10:09 +0700 Subject: [PATCH 06/10] Fix test and code --- lib/tmuxinator.rb | 1 + lib/tmuxinator/config.rb | 4 ++-- spec/lib/tmuxinator/config_spec.rb | 29 ++++++++++++++--------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/tmuxinator.rb b/lib/tmuxinator.rb index 2091b392..21fb2c68 100644 --- a/lib/tmuxinator.rb +++ b/lib/tmuxinator.rb @@ -1,4 +1,5 @@ require "erubis" +require "fileutils" require "shellwords" require "thor" require "thor/version" diff --git a/lib/tmuxinator/config.rb b/lib/tmuxinator/config.rb index 58c8d37f..d8d8032f 100644 --- a/lib/tmuxinator/config.rb +++ b/lib/tmuxinator/config.rb @@ -8,13 +8,13 @@ class << self def directory environment = ENV['TMUXINATOR_CONFIG'] if !environment.nil? && !environment.empty? - Dir.mkdir(environment) unless File.directory?(environment) + FileUtils::mkpath(environment) unless File.directory?(environment) return environment end return xdg if File.directory?(xdg) return home if File.directory?(home) # No project directory specified or existant, default to XDG: - Dir.mkdir(xdg) + FileUtils::mkpath(xdg) xdg end diff --git a/spec/lib/tmuxinator/config_spec.rb b/spec/lib/tmuxinator/config_spec.rb index baf356ed..3ac5807d 100644 --- a/spec/lib/tmuxinator/config_spec.rb +++ b/spec/lib/tmuxinator/config_spec.rb @@ -13,22 +13,7 @@ allow(File).to receive(:directory?).and_return true expect(Tmuxinator::Config.directory).to eq 'expected' end - context 'parent directory does not exist' do - it 'creates parent directories if required' do - non_existant = '/does-not-exist' # a directory which does not exist - allow(Tmuxinator::Config).to receive(:xdg) \ - .and_return non_existant + '/tmuxinator' - # allow(ENV).to receive(:[]).and_call_original - # allow(ENV).to receive(:[]).with('XDG_CONFIG_HOME') - # .and_return non_existant - # allow(Dir).to receive(:mkdir).with(non_existant) - allow(File).to receive(:directory?).and_return false - expect(Dir).to receive(:mkdir).with(non_existant) - allow(Dir).to receive(:mkdir).with(non_existant + '/tmuxinator') - expect(Tmuxinator::Config.directory).to eq(non_existant + '/tmuxinator') - end end - end context "only ~/.tmuxinator exists" do it "is ~/.tmuxinator" do @@ -59,6 +44,20 @@ expect(Tmuxinator::Config.directory).to eq Tmuxinator::Config.xdg end end + + context 'parent directory(s) do not exist' do + it 'creates parent directories if required' do + allow(File).to receive(:directory?).and_call_original + allow(File).to receive(:directory?).with(Tmuxinator::Config.home) \ + .and_return false + Dir.mktmpdir do |dir| + config_parent = "#{dir}/non_existant_parent/s" + allow(XDG).to receive(:[]).with('CONFIG').and_return config_parent + expect(Tmuxinator::Config.directory).to eq "#{config_parent}/tmuxinator" + expect(File.directory? "#{config_parent}/tmuxinator").to be true + end + end + end end describe "#directories" do From 9b5e2caa4ce04ebac06790f2834a94dd5fcad9b9 Mon Sep 17 00:00:00 2001 From: "Tom \"Ravi\" Hale" Date: Tue, 8 Nov 2016 17:11:06 +0700 Subject: [PATCH 07/10] Silence rubocop --- lib/tmuxinator/config.rb | 20 ++++----- spec/lib/tmuxinator/cli_spec.rb | 16 +++---- spec/lib/tmuxinator/config_spec.rb | 69 ++++++++++++++++-------------- 3 files changed, 53 insertions(+), 52 deletions(-) diff --git a/lib/tmuxinator/config.rb b/lib/tmuxinator/config.rb index d8d8032f..aee52ad0 100644 --- a/lib/tmuxinator/config.rb +++ b/lib/tmuxinator/config.rb @@ -6,7 +6,7 @@ class Config class << self # The directory (created if needed) in which to store new projects def directory - environment = ENV['TMUXINATOR_CONFIG'] + environment = ENV["TMUXINATOR_CONFIG"] if !environment.nil? && !environment.empty? FileUtils::mkpath(environment) unless File.directory?(environment) return environment @@ -19,11 +19,11 @@ def directory end def home - ENV['HOME'] + "/.tmuxinator" + ENV["HOME"] + "/.tmuxinator" end def xdg - XDG['CONFIG'].to_s + "/tmuxinator" + XDG["CONFIG"].to_s + "/tmuxinator" end def sample @@ -68,9 +68,9 @@ def local? # Pathname of given project searching only global directories def global_project(name) - project_in(ENV['TMUXINATOR_CONFIG'], name) || - project_in(xdg, name) || - project_in(home, name) + project_in(ENV["TMUXINATOR_CONFIG"], name) || + project_in(xdg, name) || + project_in(home, name) end def local_project @@ -84,8 +84,8 @@ def default_project(name) # Pathname of the given project def project(name) global_project(name) || - local_project || - default_project(name) + local_project || + default_project(name) end def template @@ -100,7 +100,7 @@ def wemux_template def configs configs = [] directories.each do |directory| - configs += Dir["#{directory}/**/*.yml"].collect do |path| + configs += Dir["#{directory}/**/*.yml"].map do |path| path.gsub("#{directory}/", "").gsub(".yml", "") end end @@ -111,7 +111,7 @@ def configs # Listed in search order # Used by `implode` and `list` commands def directories - environment = ENV['TMUXINATOR_CONFIG'] + environment = ENV["TMUXINATOR_CONFIG"] if !environment.nil? && !environment.empty? [environment] else diff --git a/spec/lib/tmuxinator/cli_spec.rb b/spec/lib/tmuxinator/cli_spec.rb index 2814e1f0..5911cbdd 100644 --- a/spec/lib/tmuxinator/cli_spec.rb +++ b/spec/lib/tmuxinator/cli_spec.rb @@ -446,14 +446,12 @@ end it "deletes the configuration directory(s)" do - # allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg) { true } - # allow(File).to receive(:directory?).with(Tmuxinator::Config.home) { true } - allow(Tmuxinator::Config).to receive(:directories) { \ - [Tmuxinator::Config.xdg, Tmuxinator::Config.home] } - expect(FileUtils).to receive(:remove_dir).once - .with(Tmuxinator::Config.xdg) - expect(FileUtils).to receive(:remove_dir).once - .with(Tmuxinator::Config.home) + allow(Tmuxinator::Config).to receive(:directories) \ + { [Tmuxinator::Config.xdg, Tmuxinator::Config.home] } + expect(FileUtils).to receive(:remove_dir).once. + with(Tmuxinator::Config.xdg) + expect(FileUtils).to receive(:remove_dir).once. + with(Tmuxinator::Config.home) expect(FileUtils).to receive(:remove_dir).never capture_io { cli.start } end @@ -461,7 +459,7 @@ context "$TMUXINATOR_CONFIG specified" do it "only deletes projects in that directory" do allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:[]).with('TMUXINATOR_CONFIG').and_return "dir" + allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG").and_return "dir" allow(File).to receive(:directory?).with("dir").and_return true expect(FileUtils).to receive(:remove_dir).once.with("dir") expect(FileUtils).to receive(:remove_dir).never diff --git a/spec/lib/tmuxinator/config_spec.rb b/spec/lib/tmuxinator/config_spec.rb index 3ac5807d..8f8c6f8a 100644 --- a/spec/lib/tmuxinator/config_spec.rb +++ b/spec/lib/tmuxinator/config_spec.rb @@ -6,54 +6,55 @@ let(:home_config_dir) { "#{fixtures_dir}/dot-tmuxinator" } describe "#directory" do - context 'environment variable $TMUXINATOR_CONFIG set' do + context "environment variable $TMUXINATOR_CONFIG set" do it "is $TMUXINATOR_CONFIG" do - allow(ENV).to receive(:[]).with('TMUXINATOR_CONFIG') - .and_return 'expected' + allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG"). + and_return "expected" allow(File).to receive(:directory?).and_return true - expect(Tmuxinator::Config.directory).to eq 'expected' + expect(Tmuxinator::Config.directory).to eq "expected" end end context "only ~/.tmuxinator exists" do it "is ~/.tmuxinator" do - allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg) - .and_return false - allow(File).to receive(:directory?).with(Tmuxinator::Config.home) - .and_return true + allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg). + and_return false + allow(File).to receive(:directory?).with(Tmuxinator::Config.home). + and_return true expect(Tmuxinator::Config.directory).to eq Tmuxinator::Config.home end end context "only $XDG_CONFIG_HOME/tmuxinator exists" do it "is #xdg" do - allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg) - .and_return true - allow(File).to receive(:directory?).with(Tmuxinator::Config.home) - .and_return false + allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg). + and_return true + allow(File).to receive(:directory?).with(Tmuxinator::Config.home). + and_return false expect(Tmuxinator::Config.directory).to eq Tmuxinator::Config.xdg end end context "both $XDG_CONFIG_HOME/tmuxinator and ~/.tmuxinator exist" do it "is #xdg" do - allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg) - .and_return true - allow(File).to receive(:directory?).with(Tmuxinator::Config.home) - .and_return true + allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg). + and_return true + allow(File).to receive(:directory?).with(Tmuxinator::Config.home). + and_return true expect(Tmuxinator::Config.directory).to eq Tmuxinator::Config.xdg end end - context 'parent directory(s) do not exist' do - it 'creates parent directories if required' do + context "parent directory(s) do not exist" do + it "creates parent directories if required" do allow(File).to receive(:directory?).and_call_original - allow(File).to receive(:directory?).with(Tmuxinator::Config.home) \ - .and_return false + allow(File).to receive(:directory?).with(Tmuxinator::Config.home). + and_return false Dir.mktmpdir do |dir| config_parent = "#{dir}/non_existant_parent/s" - allow(XDG).to receive(:[]).with('CONFIG').and_return config_parent - expect(Tmuxinator::Config.directory).to eq "#{config_parent}/tmuxinator" + allow(XDG).to receive(:[]).with("CONFIG").and_return config_parent + expect(Tmuxinator::Config.directory). + to eq "#{config_parent}/tmuxinator" expect(File.directory? "#{config_parent}/tmuxinator").to be true end end @@ -67,17 +68,17 @@ end it "is only [$TMUXINATOR_CONFIG] if set" do - allow(ENV).to receive(:[]).with('TMUXINATOR_CONFIG') - .and_return 'expected' + allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG"). + and_return "expected" allow(File).to receive(:directory?).and_return true - expect(Tmuxinator::Config.directories).to eq ['expected'] + expect(Tmuxinator::Config.directories).to eq ["expected"] end it "contains #xdg before #home" do - allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg) - .and_return true - allow(File).to receive(:directory?).with(Tmuxinator::Config.home) - .and_return true + allow(File).to receive(:directory?).with(Tmuxinator::Config.xdg). + and_return true + allow(File).to receive(:directory?).with(Tmuxinator::Config.home). + and_return true expect(Tmuxinator::Config.directories).to eq \ [Tmuxinator::Config.xdg, Tmuxinator::Config.home] end @@ -164,12 +165,13 @@ end it "gets a sorted list of all projects" do - expect(Tmuxinator::Config.configs).to eq ["both", "both", "dup/local-dup", "home", "local-dup", "xdg"] + expect(Tmuxinator::Config.configs). + to eq ["both", "both", "dup/local-dup", "home", "local-dup", "xdg"] end it "lists only projects in $TMUXINATOR_CONFIG when set" do - allow(ENV).to receive(:[]).with('TMUXINATOR_CONFIG') - .and_return "#{fixtures_dir}/TMUXINATOR_CONFIG" + allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG"). + and_return "#{fixtures_dir}/TMUXINATOR_CONFIG" expect(Tmuxinator::Config.configs).to eq ["TMUXINATOR_CONFIG"] end end @@ -315,7 +317,8 @@ end it "gets the project as path to the yml file" do - expect(Tmuxinator::Config.project("sample")).to eq "#{directory}/sample.yml" + expect(Tmuxinator::Config.project("sample")). + to eq "#{directory}/sample.yml" end end From f86a065bf30479793f1583d8ed3e4bf2ff657e6c Mon Sep 17 00:00:00 2001 From: "Tom \"Ravi\" Hale" Date: Tue, 8 Nov 2016 17:35:15 +0700 Subject: [PATCH 08/10] Save 2 lines --- lib/tmuxinator/config.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/tmuxinator/config.rb b/lib/tmuxinator/config.rb index aee52ad0..b83d80a1 100644 --- a/lib/tmuxinator/config.rb +++ b/lib/tmuxinator/config.rb @@ -83,9 +83,7 @@ def default_project(name) # Pathname of the given project def project(name) - global_project(name) || - local_project || - default_project(name) + global_project(name) || local_project || default_project(name) end def template From 69c9cb73e09fe424529bba59d0d905c611d22838 Mon Sep 17 00:00:00 2001 From: "Tom \"Ravi\" Hale" Date: Mon, 21 Nov 2016 16:35:59 +0700 Subject: [PATCH 09/10] Use map instead of += --- lib/tmuxinator/config.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/tmuxinator/config.rb b/lib/tmuxinator/config.rb index b83d80a1..000522e8 100644 --- a/lib/tmuxinator/config.rb +++ b/lib/tmuxinator/config.rb @@ -96,13 +96,11 @@ def wemux_template # Sorted list of all .yml files, including duplicates def configs - configs = [] - directories.each do |directory| - configs += Dir["#{directory}/**/*.yml"].map do |path| + directories.map do |directory| + Dir["#{directory}/**/*.yml"].map do |path| path.gsub("#{directory}/", "").gsub(".yml", "") end - end - configs.sort + end.flatten.sort end # Existant directories which may contain project files From 0c0f20ca123b0c6c272f02db46d740beae093733 Mon Sep 17 00:00:00 2001 From: "Tom \"Ravi\" Hale" Date: Mon, 21 Nov 2016 16:42:55 +0700 Subject: [PATCH 10/10] Add helper environment for ENV["TMUXINATOR_CONFIG"] --- lib/tmuxinator/config.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/tmuxinator/config.rb b/lib/tmuxinator/config.rb index 000522e8..ea436913 100644 --- a/lib/tmuxinator/config.rb +++ b/lib/tmuxinator/config.rb @@ -6,7 +6,6 @@ class Config class << self # The directory (created if needed) in which to store new projects def directory - environment = ENV["TMUXINATOR_CONFIG"] if !environment.nil? && !environment.empty? FileUtils::mkpath(environment) unless File.directory?(environment) return environment @@ -22,10 +21,15 @@ def home ENV["HOME"] + "/.tmuxinator" end + # Is ~/.config/tmuxinator unless $XDG_CONFIG_DIR is set def xdg XDG["CONFIG"].to_s + "/tmuxinator" end + def environment + ENV["TMUXINATOR_CONFIG"] + end + def sample asset_path "sample.yml" end @@ -68,7 +72,7 @@ def local? # Pathname of given project searching only global directories def global_project(name) - project_in(ENV["TMUXINATOR_CONFIG"], name) || + project_in(environment, name) || project_in(xdg, name) || project_in(home, name) end @@ -107,7 +111,6 @@ def configs # Listed in search order # Used by `implode` and `list` commands def directories - environment = ENV["TMUXINATOR_CONFIG"] if !environment.nil? && !environment.empty? [environment] else