From adefbb1066e132fb44ab77e01ead3c1b23035e77 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Sat, 30 Oct 2021 09:35:28 +0000 Subject: [PATCH 1/5] - Refactor library handling (internal) --- lib/bashly.rb | 2 +- lib/bashly/commands/add.rb | 39 +++++-------- lib/bashly/commands/generate.rb | 29 +++++----- lib/bashly/libraries.yml | 39 +++++++++++++ lib/bashly/libraries/base.rb | 19 ++++++ lib/bashly/libraries/completions.rb | 14 +++++ .../completions_function.rb | 24 ++++++-- lib/bashly/libraries/completions_script.rb | 29 ++++++++++ lib/bashly/libraries/completions_yaml.rb | 27 +++++++++ lib/bashly/library.rb | 58 +++++++++++++++++++ lib/bashly/library/base.rb | 57 ------------------ lib/bashly/library/colors.rb | 9 --- lib/bashly/library/completions.rb | 28 --------- lib/bashly/library/completions_script.rb | 17 ------ lib/bashly/library/completions_yaml.rb | 15 ----- lib/bashly/library/config.rb | 9 --- lib/bashly/library/sample.rb | 9 --- lib/bashly/library/strings.rb | 12 ---- lib/bashly/library/validations.rb | 9 --- lib/bashly/library/yaml.rb | 9 --- 20 files changed, 232 insertions(+), 222 deletions(-) create mode 100644 lib/bashly/libraries.yml create mode 100644 lib/bashly/libraries/base.rb create mode 100644 lib/bashly/libraries/completions.rb rename lib/bashly/{library => libraries}/completions_function.rb (67%) create mode 100644 lib/bashly/libraries/completions_script.rb create mode 100644 lib/bashly/libraries/completions_yaml.rb create mode 100644 lib/bashly/library.rb delete mode 100644 lib/bashly/library/base.rb delete mode 100644 lib/bashly/library/colors.rb delete mode 100644 lib/bashly/library/completions.rb delete mode 100644 lib/bashly/library/completions_script.rb delete mode 100644 lib/bashly/library/completions_yaml.rb delete mode 100644 lib/bashly/library/config.rb delete mode 100644 lib/bashly/library/sample.rb delete mode 100644 lib/bashly/library/strings.rb delete mode 100644 lib/bashly/library/validations.rb delete mode 100644 lib/bashly/library/yaml.rb diff --git a/lib/bashly.rb b/lib/bashly.rb index cb56c08f..be6046bd 100644 --- a/lib/bashly.rb +++ b/lib/bashly.rb @@ -11,5 +11,5 @@ requires 'bashly/exceptions' requires 'bashly/script/base' requires 'bashly/commands/base' -requires 'bashly/library/base' +requires 'bashly/libraries/base' requires 'bashly' diff --git a/lib/bashly/commands/add.rb b/lib/bashly/commands/add.rb index 91f58ece..0dba7b8c 100644 --- a/lib/bashly/commands/add.rb +++ b/lib/bashly/commands/add.rb @@ -32,27 +32,27 @@ class Add < Base environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]" def strings_command - add_lib Library::Strings.new + add_lib 'strings' end def lib_command - add_lib Library::Sample.new + add_lib 'lib' end def config_command - add_lib Library::Config.new + add_lib 'config' end def colors_command - add_lib Library::Colors.new + add_lib 'colors' end def yaml_command - add_lib Library::YAML.new + add_lib 'yaml' end def validations_command - add_lib Library::Validations.new + add_lib 'validations' end def comp_command @@ -60,35 +60,24 @@ def comp_command output = args['OUTPUT'] case format - when "script" - path = output || "#{Settings.target_dir}/completions.bash" - add_lib Library::CompletionsScript.new(path) - - when "function" - function = output || "send_completions" - path = "#{Settings.source_dir}/lib/#{function}.sh" - add_lib Library::CompletionsFunction.new(path, function: function) - - when "yaml" - path = output || "#{Settings.target_dir}/completions.yml" - add_lib Library::CompletionsYAML.new(path) - - else - raise Error, "Unrecognized format: #{format}" - + when "script" then add_lib 'completions_script', output + when "function" then add_lib 'completions', output + when "yaml" then add_lib 'completions_yaml', output + else raise Error, "Unrecognized format: #{format}" end end private - def add_lib(handler) + def add_lib(name, *args) + library = Bashly::Library.new name, *args files_created = 0 - handler.files.each do |file| + library.files.each do |file| created = safe_write file[:path], file[:content] files_created += 1 if created end - message = handler.post_install_message + message = library.post_install_message say "\n#{message}" if message and files_created > 0 end diff --git a/lib/bashly/commands/generate.rb b/lib/bashly/commands/generate.rb index 21587abb..b2a74f9d 100644 --- a/lib/bashly/commands/generate.rb +++ b/lib/bashly/commands/generate.rb @@ -35,20 +35,8 @@ def upgrade_libs content = File.read file if content =~ /\[@bashly-upgrade (.+)\]/ - lib = $1 - - case lib - when "colors" - upgrade file, Library::Colors.new - when "config" - upgrade file, Library::Config.new - when "yaml" - upgrade file, Library::YAML.new - when "validations" - upgrade file, Library::Validations.new - when /completions (.+)/ - upgrade file, Library::CompletionsFunction.new(file, function: $1) - end + library_name, args = $1.split(' ', 2) + upgrade file, library_name, *args end end end @@ -57,8 +45,17 @@ def generated_files Dir["#{Settings.source_dir}/**/*.*"].sort end - def upgrade(existing_file, handler) - file = handler.files.select { |f| f[:path] == existing_file }.first + def upgrade(existing_file, library_name, *args) + if Library.exist? library_name + upgrade! existing_file, library_name, *args + else + quiet_say "!txtred!warning!txtrst! not upgrading !txtcyn!#{existing_file}!txtrst!, unknown library '#{library_name}'" + end + end + + def upgrade!(existing_file, library_name, *args) + library = Bashly::Library.new library_name, *args + file = library.files.select { |f| f[:path] == existing_file }.first if file File.deep_write file[:path], file[:content] diff --git a/lib/bashly/libraries.yml b/lib/bashly/libraries.yml new file mode 100644 index 00000000..7515e925 --- /dev/null +++ b/lib/bashly/libraries.yml @@ -0,0 +1,39 @@ +colors: + files: + - source: "templates/lib/colors.sh" + target: "%{user_source_dir}/lib/colors.sh" + +config: + files: + - source: "templates/lib/config.sh" + target: "%{user_source_dir}/lib/config.sh" + +yaml: + files: + - source: "templates/lib/yaml.sh" + target: "%{user_source_dir}/lib/yaml.sh" + +lib: + files: + - source: "templates/lib/sample_function.sh" + target: "%{user_source_dir}/lib/sample_function.sh" + +strings: + files: + - source: "templates/strings.yml" + target: "%{user_source_dir}/bashly-strings.yml" + +validations: + files: + - source: "templates/lib/validations/validate_dir_exists.sh" + target: "%{user_source_dir}/lib/validations/validate_dir_exists.sh" + - source: "templates/lib/validations/validate_file_exists.sh" + target: "%{user_source_dir}/lib/validations/validate_file_exists.sh" + - source: "templates/lib/validations/validate_integer.sh" + target: "%{user_source_dir}/lib/validations/validate_integer.sh" + - source: "templates/lib/validations/validate_not_empty.sh" + target: "%{user_source_dir}/lib/validations/validate_not_empty.sh" + +completions: :CompletionsFunction +completions_script: :CompletionsScript +completions_yaml: :CompletionsYAML diff --git a/lib/bashly/libraries/base.rb b/lib/bashly/libraries/base.rb new file mode 100644 index 00000000..4811c967 --- /dev/null +++ b/lib/bashly/libraries/base.rb @@ -0,0 +1,19 @@ +module Bashly + module Libraries + class Base + attr_reader :args + + def initialize(*args) + @args = args + end + + def files + raise NotImplementedError, "Please implement #files" + end + + def post_install_message + nil + end + end + end +end diff --git a/lib/bashly/libraries/completions.rb b/lib/bashly/libraries/completions.rb new file mode 100644 index 00000000..115995be --- /dev/null +++ b/lib/bashly/libraries/completions.rb @@ -0,0 +1,14 @@ +module Bashly + module Libraries + class Completions < Base + protected + def command + @command ||= Script::Command.new config + end + + def config + @config ||= Bashly::Config.new "#{Settings.source_dir}/bashly.yml" + end + end + end +end \ No newline at end of file diff --git a/lib/bashly/library/completions_function.rb b/lib/bashly/libraries/completions_function.rb similarity index 67% rename from lib/bashly/library/completions_function.rb rename to lib/bashly/libraries/completions_function.rb index 7d4b8ef3..4e0d34d9 100644 --- a/lib/bashly/library/completions_function.rb +++ b/lib/bashly/libraries/completions_function.rb @@ -1,11 +1,13 @@ module Bashly - module Library + module Libraries class CompletionsFunction < Completions - def file_content + def files [ - "# [@bashly-upgrade completions #{function_name}]", - command.completion_function(function_name) - ].join "\n" + { + path: "#{Settings.source_dir}/lib/#{function_name}.sh", + content: completions_function_code(function_name) + } + ] end def post_install_message @@ -18,9 +20,19 @@ def post_install_message EOF end + private + def function_name - options[:function] + @function_name ||= args[0] || 'send_completions' end + + def completions_function_code(function_name) + [ + "# [@bashly-upgrade completions #{function_name}]", + command.completion_function(function_name) + ].join "\n" + end + end end end \ No newline at end of file diff --git a/lib/bashly/libraries/completions_script.rb b/lib/bashly/libraries/completions_script.rb new file mode 100644 index 00000000..b6af3d35 --- /dev/null +++ b/lib/bashly/libraries/completions_script.rb @@ -0,0 +1,29 @@ +module Bashly + module Libraries + class CompletionsScript < Completions + def files + [ + { + path: target_path, + content: command.completion_script + } + ] + end + + def post_install_message + <<~EOF + In order to enable completions, run: + + !txtpur!$ source #{target_path} + EOF + end + + private + + def target_path + @target_path ||= args[0] || "#{Settings.target_dir}/completions.bash" + end + + end + end +end \ No newline at end of file diff --git a/lib/bashly/libraries/completions_yaml.rb b/lib/bashly/libraries/completions_yaml.rb new file mode 100644 index 00000000..1b782b61 --- /dev/null +++ b/lib/bashly/libraries/completions_yaml.rb @@ -0,0 +1,27 @@ +module Bashly + module Libraries + class CompletionsYAML < Completions + def files + [ + { + path: target_path, + content: command.completion_data.to_yaml + } + ] + end + + def post_install_message + <<~EOF + This file can be converted to a completions script using the !txtgrn!completely!txtrst! gem. + EOF + end + + private + + def target_path + @target_path ||= args[0] || "#{Settings.target_dir}/completions.yml" + end + + end + end +end \ No newline at end of file diff --git a/lib/bashly/library.rb b/lib/bashly/library.rb new file mode 100644 index 00000000..611961a7 --- /dev/null +++ b/lib/bashly/library.rb @@ -0,0 +1,58 @@ +module Bashly + class Library + class << self + def exist?(name) + config.has_key? name.to_s + end + + def config + @config ||= YAML.load_file(config_path) + end + + def config_path + @config_path ||= File.expand_path 'libraries.yml', __dir__ + end + end + + include AssetHelper + attr_reader :name, :args + + def initialize(name, *args) + @name, @args = name.to_s, args + end + + def files + if config.is_a? Symbol + custom_handler.files + + else + config['files'].map do |file| + { path: file['target'] % target_file_args, + content: asset_content(file['source']) } + end + end + end + + def post_install_message + if config.is_a? Symbol + custom_handler.post_install_message + else + config['post_install_message'] + end + end + + private + + def custom_handler + @custom_handler ||= Bashly::Libraries.const_get(config).new(*args) + end + + def config + @config ||= self.class.config[name] + end + + def target_file_args + { user_source_dir: Settings.source_dir } + end + end +end diff --git a/lib/bashly/library/base.rb b/lib/bashly/library/base.rb deleted file mode 100644 index 8a13c33a..00000000 --- a/lib/bashly/library/base.rb +++ /dev/null @@ -1,57 +0,0 @@ -module Bashly - module Library - class Base - include AssetHelper - - attr_reader :target_path, :options - - def initialize(target_path = nil, options = nil) - @target_path = target_path || Settings.source_dir - @options = options || {} - end - - def files - case content - when String then content_from_string content - when Hash then [content] - else content - end - end - - def post_install_message - nil - end - - def content - raise NotImplementedError, "Please implement either #content" - end - - private - - def content_from_string(string) - if File.directory? asset("templates/lib/#{string}") - content_for_dir string - else - [content_for_file(string)] - end - end - - def content_for_file(file) - { - path: "#{target_path}/lib/#{file}", - content: asset_content("templates/lib/#{file}") - } - end - - def content_for_dir(dir) - Dir[asset("templates/lib/#{dir}/*.sh")].sort.map do |file| - { - path: "#{target_path}/lib/#{dir}/#{File.basename file}", - content: File.read(file) - } - end - end - - end - end -end \ No newline at end of file diff --git a/lib/bashly/library/colors.rb b/lib/bashly/library/colors.rb deleted file mode 100644 index 6b26ff98..00000000 --- a/lib/bashly/library/colors.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Bashly - module Library - class Colors < Base - def content - "colors.sh" - end - end - end -end \ No newline at end of file diff --git a/lib/bashly/library/completions.rb b/lib/bashly/library/completions.rb deleted file mode 100644 index 5c989b9a..00000000 --- a/lib/bashly/library/completions.rb +++ /dev/null @@ -1,28 +0,0 @@ -module Bashly - module Library - class Completions < Base - def content - { path: target_path, content: file_content } - end - - def file_content - raise NotImplementedError, "Please implement #file_content" - end - - protected - - def completions - @completions ||= command.completion_data - end - - def config - @config ||= Bashly::Config.new "#{Settings.source_dir}/bashly.yml" - end - - def command - @command ||= Script::Command.new config - end - - end - end -end \ No newline at end of file diff --git a/lib/bashly/library/completions_script.rb b/lib/bashly/library/completions_script.rb deleted file mode 100644 index 07d608a8..00000000 --- a/lib/bashly/library/completions_script.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Bashly - module Library - class CompletionsScript < Completions - def file_content - command.completion_script - end - - def post_install_message - <<~EOF - In order to enable completions, run: - - !txtpur!$ source #{target_path} - EOF - end - end - end -end \ No newline at end of file diff --git a/lib/bashly/library/completions_yaml.rb b/lib/bashly/library/completions_yaml.rb deleted file mode 100644 index adaddf36..00000000 --- a/lib/bashly/library/completions_yaml.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Bashly - module Library - class CompletionsYAML < Completions - def file_content - completions.to_yaml - end - - def post_install_message - <<~EOF - This file can be converted to a completions script using the !txtgrn!completely!txtrst! gem. - EOF - end - end - end -end \ No newline at end of file diff --git a/lib/bashly/library/config.rb b/lib/bashly/library/config.rb deleted file mode 100644 index 1bfe5c47..00000000 --- a/lib/bashly/library/config.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Bashly - module Library - class Config < Base - def content - "config.sh" - end - end - end -end \ No newline at end of file diff --git a/lib/bashly/library/sample.rb b/lib/bashly/library/sample.rb deleted file mode 100644 index 2dae8c34..00000000 --- a/lib/bashly/library/sample.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Bashly - module Library - class Sample < Base - def content - "sample_function.sh" - end - end - end -end \ No newline at end of file diff --git a/lib/bashly/library/strings.rb b/lib/bashly/library/strings.rb deleted file mode 100644 index 6e673fbd..00000000 --- a/lib/bashly/library/strings.rb +++ /dev/null @@ -1,12 +0,0 @@ -module Bashly - module Library - class Strings < Base - def content - { - path: "#{target_path}/bashly-strings.yml", - content: asset_content("templates/strings.yml") - } - end - end - end -end \ No newline at end of file diff --git a/lib/bashly/library/validations.rb b/lib/bashly/library/validations.rb deleted file mode 100644 index 4b370f95..00000000 --- a/lib/bashly/library/validations.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Bashly - module Library - class Validations < Base - def content - "validations" - end - end - end -end \ No newline at end of file diff --git a/lib/bashly/library/yaml.rb b/lib/bashly/library/yaml.rb deleted file mode 100644 index a978f140..00000000 --- a/lib/bashly/library/yaml.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Bashly - module Library - class YAML < Base - def content - "yaml.sh" - end - end - end -end \ No newline at end of file From 7dccdacd286dcd32c7f81e83bcb84afe2cd28c3f Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Sat, 30 Oct 2021 09:36:00 +0000 Subject: [PATCH 2/5] update specs --- spec/bashly/library/base_spec.rb | 83 ------------------------- spec/bashly/library/completions_spec.rb | 9 --- 2 files changed, 92 deletions(-) delete mode 100644 spec/bashly/library/base_spec.rb delete mode 100644 spec/bashly/library/completions_spec.rb diff --git a/spec/bashly/library/base_spec.rb b/spec/bashly/library/base_spec.rb deleted file mode 100644 index 01488845..00000000 --- a/spec/bashly/library/base_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'spec_helper' - -describe Library::Base do - let(:lib_dir) { 'lib/bashly/templates/lib' } - - describe '#content' do - it "raises a NotImplementedError" do - expect { subject.content }.to raise_error(NotImplementedError) - end - end - - describe '#files' do - it "raises a NotImplementedError" do - expect { subject.files }.to raise_error(NotImplementedError) - end - - context "when #content is a string representing a lib/file" do - subject do - Class.new(described_class) { def content; "colors.sh"; end }.new - end - - it "returns an array of hashes" do - expect(subject.files).to be_an Array - expect(subject.files.first).to be_a Hash - end - - it "returns the content and target paths of the library files" do - matter = subject.files.first - expect(matter.keys).to eq [:path, :content] - expect(matter[:path]).to eq "#{Settings.target_dir}/src/lib/#{subject.content}" - expect(matter[:content]).to eq File.read("#{lib_dir}/#{subject.content}") - end - end - - context "when #content is a string representing a lib/dir" do - subject do - Class.new(described_class) { def content; "validations"; end }.new - end - - it "returns an array of hashes" do - expect(subject.files).to be_an Array - expect(subject.files.first).to be_a Hash - end - - it "returns an array as big as the library folder" do - expect(subject.files.count).to be > 1 - expect(subject.files.count).to eq Dir["#{lib_dir}/#{subject.content}/*.sh"].count - end - - it "returns the content and target paths of the library files" do - expect(subject.files.to_yaml).to match_approval 'library/base/dir' - end - end - - context "when #content is a hash" do - subject do - Class.new(described_class) { def content; { some: 'hash' }; end }.new - end - - it "returns an array with the hash as its first and only element" do - expect(subject.files).to be_an Array - expect(subject.files.count).to eq 1 - expect(subject.files.first).to eq subject.content - end - end - - context "when #content is something else" do - subject do - Class.new(described_class) { def content; ["something else"]; end }.new - end - - it "returns it as is" do - expect(subject.files).to eq subject.content - end - end - end - - describe '#post_install_message' do - it "returns nil" do - expect(subject.post_install_message).to be_nil - end - end -end diff --git a/spec/bashly/library/completions_spec.rb b/spec/bashly/library/completions_spec.rb deleted file mode 100644 index a13cdff8..00000000 --- a/spec/bashly/library/completions_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'spec_helper' - -describe Library::Completions do - describe '#file_content' do - it "raises a NotImplementedError" do - expect { subject.content }.to raise_error(NotImplementedError) - end - end -end From 1d8715c8908e3e1945bb2c6abb90c36bc21a1fd0 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Sat, 30 Oct 2021 11:40:25 +0000 Subject: [PATCH 3/5] update library specs --- .../libraries/completions_function/files | 21 +++++ .../libraries/completions_function/message | 5 ++ .../libraries/completions_script/files | 18 ++++ .../libraries/completions_script/message | 3 + .../libraries/completions_yaml/files | 11 +++ .../libraries/completions_yaml/message | 1 + spec/approvals/library/config-keys | 10 +++ spec/bashly/libraries/base_spec.rb | 15 ++++ .../libraries/completions_function_spec.rb | 32 ++++++++ .../libraries/completions_script_spec.rb | 27 ++++++ .../bashly/libraries/completions_yaml_spec.rb | 27 ++++++ spec/bashly/library_spec.rb | 82 +++++++++++++++++++ 12 files changed, 252 insertions(+) create mode 100644 spec/approvals/libraries/completions_function/files create mode 100644 spec/approvals/libraries/completions_function/message create mode 100644 spec/approvals/libraries/completions_script/files create mode 100644 spec/approvals/libraries/completions_script/message create mode 100644 spec/approvals/libraries/completions_yaml/files create mode 100644 spec/approvals/libraries/completions_yaml/message create mode 100644 spec/approvals/library/config-keys create mode 100644 spec/bashly/libraries/base_spec.rb create mode 100644 spec/bashly/libraries/completions_function_spec.rb create mode 100644 spec/bashly/libraries/completions_script_spec.rb create mode 100644 spec/bashly/libraries/completions_yaml_spec.rb create mode 100644 spec/bashly/library_spec.rb diff --git a/spec/approvals/libraries/completions_function/files b/spec/approvals/libraries/completions_function/files new file mode 100644 index 00000000..451ef6c7 --- /dev/null +++ b/spec/approvals/libraries/completions_function/files @@ -0,0 +1,21 @@ +--- +- :path: spec/tmp/src/lib/send_completions.sh + :content: |- + # [@bashly-upgrade completions send_completions] + send_completions() { + echo $'#!/usr/bin/env bash' + echo $'' + echo $'# This bash completions script was generated by' + echo $'# completely (https://github.com/dannyben/completely)' + echo $'# Modifying it manually is not recommended' + echo $'_download_completions() {' + echo $' local cur=${COMP_WORDS[COMP_CWORD]}' + echo $' local comp_line="${COMP_WORDS[*]:1}"' + echo $'' + echo $' case "$comp_line" in' + echo $' \'\'*) COMPREPLY=($(compgen -W "--force --help --version -f -h -v" -- "$cur")) ;;' + echo $' esac' + echo $'}' + echo $'' + echo $'complete -F _download_completions download' + } diff --git a/spec/approvals/libraries/completions_function/message b/spec/approvals/libraries/completions_function/message new file mode 100644 index 00000000..ae0d183a --- /dev/null +++ b/spec/approvals/libraries/completions_function/message @@ -0,0 +1,5 @@ +In order to enable completions in your script, create a command or a flag (for example: !txtgrn!download completions!txtrst! or !txtgrn!download --completions!txtrst!) that calls the !txtgrn!send_completions!txtrst! function. + +Your users can then run something like this to enable completions: + + !txtpur!$ eval "$(download completions)" diff --git a/spec/approvals/libraries/completions_script/files b/spec/approvals/libraries/completions_script/files new file mode 100644 index 00000000..b82027e1 --- /dev/null +++ b/spec/approvals/libraries/completions_script/files @@ -0,0 +1,18 @@ +--- +- :path: spec/tmp/completions.bash + :content: | + #!/usr/bin/env bash + + # This bash completions script was generated by + # completely (https://github.com/dannyben/completely) + # Modifying it manually is not recommended + _download_completions() { + local cur=${COMP_WORDS[COMP_CWORD]} + local comp_line="${COMP_WORDS[*]:1}" + + case "$comp_line" in + ''*) COMPREPLY=($(compgen -W "--force --help --version -f -h -v" -- "$cur")) ;; + esac + } + + complete -F _download_completions download diff --git a/spec/approvals/libraries/completions_script/message b/spec/approvals/libraries/completions_script/message new file mode 100644 index 00000000..44152ab8 --- /dev/null +++ b/spec/approvals/libraries/completions_script/message @@ -0,0 +1,3 @@ +In order to enable completions, run: + + !txtpur!$ source spec/tmp/completions.bash diff --git a/spec/approvals/libraries/completions_yaml/files b/spec/approvals/libraries/completions_yaml/files new file mode 100644 index 00000000..b3683818 --- /dev/null +++ b/spec/approvals/libraries/completions_yaml/files @@ -0,0 +1,11 @@ +--- +- :path: spec/tmp/completions.yml + :content: | + --- + download: + - "--force" + - "--help" + - "--version" + - "-f" + - "-h" + - "-v" diff --git a/spec/approvals/libraries/completions_yaml/message b/spec/approvals/libraries/completions_yaml/message new file mode 100644 index 00000000..b36d9bd9 --- /dev/null +++ b/spec/approvals/libraries/completions_yaml/message @@ -0,0 +1 @@ +This file can be converted to a completions script using the !txtgrn!completely!txtrst! gem. diff --git a/spec/approvals/library/config-keys b/spec/approvals/library/config-keys new file mode 100644 index 00000000..866b3a3c --- /dev/null +++ b/spec/approvals/library/config-keys @@ -0,0 +1,10 @@ +--- +- colors +- config +- yaml +- lib +- strings +- validations +- completions +- completions_script +- completions_yaml diff --git a/spec/bashly/libraries/base_spec.rb b/spec/bashly/libraries/base_spec.rb new file mode 100644 index 00000000..7fc2d276 --- /dev/null +++ b/spec/bashly/libraries/base_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe Libraries::Base do + describe '#files' do + it "raises a NotImplementedError" do + expect { subject.files }.to raise_error(NotImplementedError) + end + end + + describe '#post_install_message' do + it "returns nil" do + expect(subject.post_install_message).to be_nil + end + end +end diff --git a/spec/bashly/libraries/completions_function_spec.rb b/spec/bashly/libraries/completions_function_spec.rb new file mode 100644 index 00000000..18cd22ca --- /dev/null +++ b/spec/bashly/libraries/completions_function_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe Libraries::CompletionsFunction do + subject { described_class.new *args } + let(:args) { nil } + before { reset_tmp_dir copy_from: 'examples/minimal'} + + describe '#files' do + it "returns an array with a single hash" do + expect(subject.files.to_yaml).to match_approval('libraries/completions_function/files') + end + + context "with an argument" do + let(:args) { ["my_function"] } + + it "uses the first argument in the filename of [:path]" do + expect(subject.files.first[:path]).to eq "#{Settings.source_dir}/lib/#{args.first}.sh" + end + + it "uses the first argument as the function of [:content]" do + expect(subject.files.first[:content]).to include "my_function()" + expect(subject.files.first[:content]).to include "[@bashly-upgrade completions my_function]" + end + end + end + + describe '#post_install_message' do + it "returns a message" do + expect(subject.post_install_message).to match_approval('libraries/completions_function/message') + end + end +end diff --git a/spec/bashly/libraries/completions_script_spec.rb b/spec/bashly/libraries/completions_script_spec.rb new file mode 100644 index 00000000..99d2a10d --- /dev/null +++ b/spec/bashly/libraries/completions_script_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe Libraries::CompletionsScript do + subject { described_class.new *args } + let(:args) { nil } + before { reset_tmp_dir copy_from: 'examples/minimal'} + + describe '#files' do + it "returns an array with a single hash" do + expect(subject.files.to_yaml).to match_approval('libraries/completions_script/files') + end + + context "with an argument" do + let(:args) { ["filename.bash"] } + + it "uses the first argument as a filename" do + expect(subject.files.first[:path]).to eq args.first + end + end + end + + describe '#post_install_message' do + it "returns a message" do + expect(subject.post_install_message).to match_approval('libraries/completions_script/message') + end + end +end diff --git a/spec/bashly/libraries/completions_yaml_spec.rb b/spec/bashly/libraries/completions_yaml_spec.rb new file mode 100644 index 00000000..c73eb0da --- /dev/null +++ b/spec/bashly/libraries/completions_yaml_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe Libraries::CompletionsYAML do + subject { described_class.new *args } + let(:args) { nil } + before { reset_tmp_dir copy_from: 'examples/minimal'} + + describe '#files' do + it "returns an array with a single hash" do + expect(subject.files.to_yaml).to match_approval('libraries/completions_yaml/files') + end + + context "with an argument" do + let(:args) { ["filename.yml"] } + + it "uses the first argument as a filename" do + expect(subject.files.first[:path]).to eq args.first + end + end + end + + describe '#post_install_message' do + it "returns a message" do + expect(subject.post_install_message).to match_approval('libraries/completions_yaml/message') + end + end +end diff --git a/spec/bashly/library_spec.rb b/spec/bashly/library_spec.rb new file mode 100644 index 00000000..23ff3c13 --- /dev/null +++ b/spec/bashly/library_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +describe Library do + subject { described_class.new name, args } + let(:name) { 'colors' } + let(:args) { nil } + let(:lib_dir) { 'lib/bashly/templates/lib' } + + context "eigenclass" do + subject { described_class } + + describe '::exist?' do + it "returns true if the provided library name exists" do + expect(subject).to exist 'colors' + end + + it "returns false if the provided library name does not exist" do + expect(subject).to_not exist 'crypto_mining' + end + end + + describe '::config' do + it "returns the entire libraries configuration data" do + expect(subject.config).to be_a Hash + expect(subject.config.keys.to_yaml).to match_approval('library/config-keys') + end + end + end + + describe '#files' do + context "for basic libraries" do + it "returns an array of hashes" do + expect(subject.files).to be_an Array + expect(subject.files.first).to be_a Hash + end + + it "returns the content and target paths of the library files" do + matter = subject.files.first + expect(matter.keys).to match_array [:path, :content] + expect(matter[:path]).to eq "#{Settings.target_dir}/src/lib/colors.sh" + expect(matter[:content]).to eq File.read("#{lib_dir}/colors.sh") + end + end + + context "for libraries with a custom handler" do + let(:name) { "completions" } + before { reset_tmp_dir copy_from: 'examples/minimal'} + + it "delegaes the request to a custom handler" do + expect(subject.files).to be_an Array + expect(subject.files.first).to be_a Hash + expect(subject.files.first.keys).to match_array [:path, :content] + end + end + end + + describe '#post_install_message' do + context "for basic libraries with no configured message" do + it "returns nil" do + expect(subject.post_install_message).to be_nil + end + end + + context "for basic libraries with configured message" do + before { Library.config['colors']['post_install_message'] = "the message" } + after { Library.config['colors'].delete 'post_install_message' } + + it "returns the message" do + expect(subject.post_install_message).to eq "the message" + end + end + + context "for libraries with a custom handler" do + let(:name) { "completions_yaml" } + before { reset_tmp_dir copy_from: 'examples/minimal'} + + it "returns the message form the handler" do + expect(subject.post_install_message).to include "completely" + end + end + end +end From 188bd9e5f037cc3118c477605c7fa24e00c46d2c Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Sat, 30 Oct 2021 11:47:19 +0000 Subject: [PATCH 4/5] complete test coverage --- .../cli/generate/upgrade-unknown-lib | 14 ++++++++ spec/approvals/fixtures/lib-upgrade | 35 ------------------- spec/approvals/library/base/dir | 25 ------------- spec/bashly/commands/generate_spec.rb | 14 ++++++++ 4 files changed, 28 insertions(+), 60 deletions(-) create mode 100644 spec/approvals/cli/generate/upgrade-unknown-lib delete mode 100644 spec/approvals/fixtures/lib-upgrade delete mode 100644 spec/approvals/library/base/dir diff --git a/spec/approvals/cli/generate/upgrade-unknown-lib b/spec/approvals/cli/generate/upgrade-unknown-lib new file mode 100644 index 00000000..bb713b90 --- /dev/null +++ b/spec/approvals/cli/generate/upgrade-unknown-lib @@ -0,0 +1,14 @@ +creating user files in spec/tmp/src +skipped spec/tmp/src/initialize.sh (exists) +skipped spec/tmp/src/download_command.sh (exists) +skipped spec/tmp/src/upload_command.sh (exists) +warning not upgrading spec/tmp/src/lib/colors.sh, unknown library 'no-such-lib' +updated spec/tmp/src/lib/config.sh +updated spec/tmp/src/lib/send_completions.sh +updated spec/tmp/src/lib/validations/validate_dir_exists.sh +updated spec/tmp/src/lib/validations/validate_file_exists.sh +updated spec/tmp/src/lib/validations/validate_integer.sh +updated spec/tmp/src/lib/validations/validate_not_empty.sh +updated spec/tmp/src/lib/yaml.sh +created spec/tmp/cli +run spec/tmp/cli --help to test your bash script diff --git a/spec/approvals/fixtures/lib-upgrade b/spec/approvals/fixtures/lib-upgrade deleted file mode 100644 index 15928a88..00000000 --- a/spec/approvals/fixtures/lib-upgrade +++ /dev/null @@ -1,35 +0,0 @@ -+ bundle exec bashly add colors --force -created src/lib/colors.sh -+ bundle exec bashly add yaml --force -created src/lib/yaml.sh -+ bundle exec bashly add config --force -created src/lib/config.sh -+ bundle exec bashly add validations --force -created src/lib/validations/validate_dir_exists.sh -created src/lib/validations/validate_file_exists.sh -created src/lib/validations/validate_integer.sh -created src/lib/validations/validate_not_empty.sh -+ bundle exec bashly add comp function --force -created src/lib/send_completions.sh - -In order to enable completions in your script, create a command or a flag (for example: cli completions or cli --completions) that calls the send_completions function. - -Your users can then run something like this to enable completions: - - $ eval "$(cli completions)" - -+ bundle exec bashly generate --upgrade -creating user files in src -created src/initialize.sh -created src/download_command.sh -created src/upload_command.sh -updated src/lib/colors.sh -updated src/lib/config.sh -updated src/lib/send_completions.sh -updated src/lib/validations/validate_dir_exists.sh -updated src/lib/validations/validate_file_exists.sh -updated src/lib/validations/validate_integer.sh -updated src/lib/validations/validate_not_empty.sh -updated src/lib/yaml.sh -created ./cli -run ./cli --help to test your bash script diff --git a/spec/approvals/library/base/dir b/spec/approvals/library/base/dir deleted file mode 100644 index 55ef31ed..00000000 --- a/spec/approvals/library/base/dir +++ /dev/null @@ -1,25 +0,0 @@ ---- -- :path: spec/tmp/src/lib/validations/validate_dir_exists.sh - :content: | - ## [@bashly-upgrade validations] - validate_dir_exists() { - [[ -d "$1" ]] || echo "must be an existing directory" - } -- :path: spec/tmp/src/lib/validations/validate_file_exists.sh - :content: | - ## [@bashly-upgrade validations] - validate_file_exists() { - [[ -f "$1" ]] || echo "must be an existing file" - } -- :path: spec/tmp/src/lib/validations/validate_integer.sh - :content: |- - ## [@bashly-upgrade validations] - validate_integer() { - [[ "$1" =~ ^[0-9]+$ ]] || echo "must be an integer" - } -- :path: spec/tmp/src/lib/validations/validate_not_empty.sh - :content: | - ## [@bashly-upgrade validations] - validate_not_empty() { - [[ -z "$1" ]] && echo "must not be empty" - } diff --git a/spec/bashly/commands/generate_spec.rb b/spec/bashly/commands/generate_spec.rb index 471f8854..74569999 100644 --- a/spec/bashly/commands/generate_spec.rb +++ b/spec/bashly/commands/generate_spec.rb @@ -145,5 +145,19 @@ end end + context "when the upgrade candidate has magic comment with an unknown library" do + let(:file) { lib_files[0] } + let(:patched_content) { file_content.gsub "[@bashly-upgrade colors]", "[@bashly-upgrade no-such-lib]" } + let(:file_content) { File.read file } + + before do + File.write file, patched_content + end + + it "shows a warning" do + expect { subject.run %w[generate -u] }.to output_approval('cli/generate/upgrade-unknown-lib') + end + end + end end From 58856949bc472988e8a9b35084bb04c0f028a098 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Sat, 30 Oct 2021 12:18:20 +0000 Subject: [PATCH 5/5] improvements --- lib/bashly/commands/generate.rb | 5 +++-- lib/bashly/library.rb | 9 +++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/bashly/commands/generate.rb b/lib/bashly/commands/generate.rb index b2a74f9d..5cec3dfb 100644 --- a/lib/bashly/commands/generate.rb +++ b/lib/bashly/commands/generate.rb @@ -35,7 +35,8 @@ def upgrade_libs content = File.read file if content =~ /\[@bashly-upgrade (.+)\]/ - library_name, args = $1.split(' ', 2) + args = $1.split ' ' + library_name = args.shift upgrade file, library_name, *args end end @@ -55,7 +56,7 @@ def upgrade(existing_file, library_name, *args) def upgrade!(existing_file, library_name, *args) library = Bashly::Library.new library_name, *args - file = library.files.select { |f| f[:path] == existing_file }.first + file = library.find_file existing_file if file File.deep_write file[:path], file[:content] diff --git a/lib/bashly/library.rb b/lib/bashly/library.rb index 611961a7..6db49e3d 100644 --- a/lib/bashly/library.rb +++ b/lib/bashly/library.rb @@ -22,7 +22,7 @@ def initialize(name, *args) end def files - if config.is_a? Symbol + if custom_handler custom_handler.files else @@ -34,16 +34,21 @@ def files end def post_install_message - if config.is_a? Symbol + if custom_handler custom_handler.post_install_message else config['post_install_message'] end end + def find_file(path) + files.select { |f| f[:path] == path }.first + end + private def custom_handler + return nil unless config.is_a? Symbol @custom_handler ||= Bashly::Libraries.const_get(config).new(*args) end