diff --git a/.gitignore b/.gitignore index 3b129b0..595d7b9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,24 @@ +*.gem +*.rbc +*.log +/.config/ +/coverage/ +/pkg/ +/test/tmp/ +/test/version_tmp/ +/tmp/ +/log/ + +# documentation +/doc/ + +# editor *~ -*.swo *.swp -doc/ -coverage/ -pkg/ -.rvmrc -.DS_Store -.gem -.byebug_history -vendor/ +*.swo + +# environment normalization +/.gem/ +/.bundle/ +/vendor/bundle/ +/lib/bundler/man/ diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..157b5f0 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,104 @@ +require: + - rubocop-md + - rubocop-minitest + - rubocop-packaging + - rubocop-performance + - rubocop-rake + - rubocop-thread_safety + +AllCops: + NewCops: enable + Exclude: + - 'coverage/**/*' + - 'doc/**/*' + - 'pkg/**/*' + - 'tmp/**/*' + - '*.gemspec' + - 'vendor/bundle/**/*' + +Layout/ExtraSpacing: + AllowBeforeTrailingComments: true + +Layout/LineLength: + Max: 175 + +Layout/SpaceInsideHashLiteralBraces: + EnforcedStyle: space + +Lint/DuplicateBranch: + IgnoreLiteralBranches: true + IgnoreConstantBranches: true + +Metrics/AbcSize: + CountRepeatedAttributes: false + Max: 25 + Exclude: + - 'spec/**/*' + - 'tasks/**/*' + +Metrics/BlockLength: + CountAsOne: + - heredoc + Max: 30 + Exclude: + - 'spec/**/*' + - 'tasks/**/*' + +Metrics/ClassLength: + Max: 175 + CountAsOne: + - heredoc + - array + +Metrics/ModuleLength: + Max: 175 + CountAsOne: + - heredoc + - array + +Metrics/CyclomaticComplexity: + Max: 10 + +Metrics/MethodLength: + Max: 30 + CountAsOne: + - heredoc + - array + Exclude: + - 'spec/**/*' + +Metrics/ParameterLists: + CountKeywordArgs: false + +Naming/PredicateName: + Enabled: false + +Style/Documentation: + Exclude: + - 'spec/**/*' + +Style/StringLiterals: + Enabled: true + EnforcedStyle: double_quotes + ConsistentQuotesInMultiline: false + +Style/SafeNavigation: + Enabled: false + +# this gem outputs to stderr on purpose +Style/StderrPuts: + Enabled: false + +Style/TernaryParentheses: + EnforcedStyle: require_parentheses_when_complex + +Style/TrailingCommaInArrayLiteral: + EnforcedStyleForMultiline: consistent_comma +Style/TrailingCommaInHashLiteral: + EnforcedStyleForMultiline: consistent_comma +Style/TrivialAccessors: + Exclude: + - lib/launchy.rb + +ThreadSafety/InstanceVariableInClassMethod: + Enabled: false diff --git a/.ruby-version b/.ruby-version index 15a2799..bea438e 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.3.0 +3.3.1 diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index eb21d06..4c18b13 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -28,49 +28,48 @@ blocks: matrix: - env_var: RUBY_VERSION values: - - 3.0.6 - - 3.1.4 - - 3.2.3 - - 3.3.0 + - 3.0.7 + - 3.1.5 + - 3.2.4 + - 3.3.1 - jruby-9.4.5.0 - - truffleruby-23.1.2 + - truffleruby-24.0.1 commands: - source .semaphore/ensure-ruby-version.sh - source .semaphore/ensure-bundle.sh - mkdir -p tmp/test-results/ + - bundle exec rake rubocop - export TEST_RESULTS_FILE=tmp/test-results/${RUBY_VERSION}.xml - A="--junit --junit-filename=${TEST_RESULTS_FILE}" bundle exec rake test - - name: Run tests in macOS environment + + - name: Run MacOS Tests dependencies: [] task: - prologue: - commands: - - checkout - - git -C ${HOME}/.rbenv/plugins/ruby-build pull - epilogue: - always: - commands: - - test-results publish --name ${RUBY_VERSION} ${TEST_RESULTS_FILE} - agent: machine: type: a1-standard-4 os_image: macos-xcode14 + prologue: + commands: + - checkout + - git -C ${HOME}/.rbenv/plugins/ruby-build pull jobs: - name: macos matrix test matrix: - env_var: RUBY_VERSION values: - - 3.0.6 - - 3.1.4 - - 3.2.3 - - 3.3.0 + - 3.0.7 + - 3.1.5 + - 3.2.4 + - 3.3.1 commands: - source .semaphore/ensure-ruby-version.sh - source .semaphore/ensure-bundle.sh - mkdir -p tmp/test-results/ + - bundle exec rake rubocop - export TEST_RESULTS_FILE=tmp/test-results/${RUBY_VERSION}.xml - - A="--junit --junit-filename=${TEST_RESULTS_FILE}" bundle exec rake test + - bundle exec rake test + after_pipeline: task: diff --git a/Gemfile b/Gemfile index dc4515d..b386164 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,26 @@ -# DO NOT EDIT - This file is automatically generated -# Make changes to Manifest.txt and/or Rakefile and regenerate +# frozen_string_literal: true + source "https://rubygems.org" + gemspec + +# gem "debug", "~> 1.0", require: false + +gem "minitest", "~> 5.11" +gem "minitest-focus", "~> 1.2" +gem "minitest-junit", "~> 1.0" + +gem "rake" +gem "rdoc", "~> 6.3", require: false +gem "reek", require: false + +gem "rubocop", "~> 1.63", require: false +gem "rubocop-minitest", "~> 0.35", require: false +gem "rubocop-packaging", "~> 0.5", require: false +gem "rubocop-performance", "~> 1.21", require: false +gem "rubocop-rake", "~> 0.6", require: false +gem "rubocop-thread_safety", "~> 0.5", require: false + +gem "rubocop-md", "~> 1.2", require: false + +gem "simplecov", "~> 0.22", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 7a6db18..eb39476 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,22 +10,100 @@ GEM specs: addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) + ast (2.4.2) + bigdecimal (3.1.7) + bigdecimal (3.1.7-java) builder (3.2.4) childprocess (5.0.0) + concurrent-ruby (1.2.3) docile (1.4.0) + dry-configurable (1.1.0) + dry-core (~> 1.0, < 2) + zeitwerk (~> 2.6) + dry-core (1.0.1) + concurrent-ruby (~> 1.0) + zeitwerk (~> 2.6) + dry-inflector (1.0.0) + dry-initializer (3.1.1) + dry-logic (1.5.0) + concurrent-ruby (~> 1.0) + dry-core (~> 1.0, < 2) + zeitwerk (~> 2.6) + dry-schema (1.13.3) + concurrent-ruby (~> 1.0) + dry-configurable (~> 1.0, >= 1.0.1) + dry-core (~> 1.0, < 2) + dry-initializer (~> 3.0) + dry-logic (>= 1.4, < 2) + dry-types (>= 1.7, < 2) + zeitwerk (~> 2.6) + dry-types (1.7.2) + bigdecimal (~> 3.0) + concurrent-ruby (~> 1.0) + dry-core (~> 1.0) + dry-inflector (~> 1.0) + dry-logic (~> 1.4) + zeitwerk (~> 2.6) jar-dependencies (0.4.1) - minitest (5.21.2) + json (2.7.2) + json (2.7.2-java) + language_server-protocol (3.17.0.3) + minitest (5.22.3) + minitest-focus (1.4.0) + minitest (>= 4, < 6) minitest-junit (1.1.0) builder (~> 3.2) minitest (~> 5.11) + parallel (1.24.0) + parser (3.3.1.0) + ast (~> 2.4.1) + racc psych (5.1.2) stringio psych (5.1.2-java) jar-dependencies (>= 0.1.7) - public_suffix (5.0.4) - rake (13.1.0) - rdoc (6.6.2) + public_suffix (5.0.5) + racc (1.7.3) + racc (1.7.3-java) + rainbow (3.1.1) + rake (13.2.1) + rdoc (6.6.3.1) psych (>= 4.0.0) + reek (6.3.0) + dry-schema (~> 1.13.0) + parser (~> 3.3.0) + rainbow (>= 2.0, < 4.0) + rexml (~> 3.1) + regexp_parser (2.9.0) + rexml (3.2.6) + rubocop (1.63.4) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.31.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) + rubocop-md (1.2.2) + rubocop (>= 1.0) + rubocop-minitest (0.35.0) + rubocop (>= 1.61, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-packaging (0.5.2) + rubocop (>= 1.33, < 2.0) + rubocop-performance (1.21.0) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rake (0.6.0) + rubocop (~> 1.0) + rubocop-thread_safety (0.5.1) + rubocop (>= 0.90.0) + ruby-progressbar (1.13.0) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) @@ -33,18 +111,31 @@ GEM simplecov-html (0.12.3) simplecov_json_formatter (0.1.4) stringio (3.1.0) + unicode-display_width (2.5.0) + zeitwerk (2.6.13) PLATFORMS + arm64-darwin-23 java ruby + x86_64-linux DEPENDENCIES launchy! - minitest (~> 5.21) + minitest (~> 5.11) + minitest-focus (~> 1.2) minitest-junit (~> 1.0) - rake (~> 13.0) - rdoc (~> 6.6) + rake + rdoc (~> 6.3) + reek + rubocop (~> 1.63) + rubocop-md (~> 1.2) + rubocop-minitest (~> 0.35) + rubocop-packaging (~> 0.5) + rubocop-performance (~> 1.21) + rubocop-rake (~> 0.6) + rubocop-thread_safety (~> 0.5) simplecov (~> 0.22) BUNDLED WITH - 2.5.5 + 2.5.9 diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/Manifest.txt b/Manifest.txt index 31f1354..9d3d23d 100644 --- a/Manifest.txt +++ b/Manifest.txt @@ -1,10 +1,10 @@ CONTRIBUTING.md HISTORY.md -LICENSE +LICENSE.txt Manifest.txt README.md -Rakefile -bin/launchy +exe/launchy +launchy.gemspec lib/launchy.rb lib/launchy/application.rb lib/launchy/applications/browser.rb @@ -19,16 +19,3 @@ lib/launchy/error.rb lib/launchy/os_family.rb lib/launchy/runner.rb lib/launchy/version.rb -spec/application_spec.rb -spec/applications/browser_spec.rb -spec/cli_spec.rb -spec/detect/host_os_family_spec.rb -spec/detect/host_os_spec.rb -spec/detect/nix_desktop_environment_spec.rb -spec/launchy_spec.rb -spec/mock_application.rb -spec/spec_helper.rb -spec/tattle-host-os.yaml -spec/version_spec.rb -tasks/default.rake -tasks/this.rb diff --git a/Rakefile b/Rakefile index a61587e..b9b9345 100644 --- a/Rakefile +++ b/Rakefile @@ -1,29 +1,25 @@ +# frozen_string_literal: true + # vim: syntax=ruby -load 'tasks/this.rb' +load "tasks/this.rb" This.name = "launchy" This.author = "Jeremy Hinegardner" This.email = "jeremy@copiousfreetime.org" -This.homepage = "https://github.com/copiousfreetime/#{ This.name }" +This.homepage = "https://github.com/copiousfreetime/#{This.name}" This.ruby_gemspec do |spec| - spec.add_dependency( 'addressable', '~> 2.8') - spec.add_dependency( 'childprocess', '~> 5.0') - - spec.add_development_dependency( 'rake' , '~> 13.0') - spec.add_development_dependency( 'minitest' , '~> 5.21' ) - spec.add_development_dependency( 'minitest-junit' , '~> 1.0' ) - spec.add_development_dependency( 'rdoc' , '~> 6.6' ) - spec.add_development_dependency( 'simplecov', '~> 0.22' ) + spec.add_dependency("addressable", "~> 2.8") + spec.add_dependency("childprocess", "~> 5.0") - spec.licenses = ['ISC'] + spec.licenses = ["ISC"] spec.metadata = { "bug_tracker_uri" => "https://github.com/copiousfreetime/launchy/issues", - "changelog_uri" => "https://github.com/copiousfreetime/launchy/blob/master/README.md", - "homepage_uri" => "https://github.com/copiousfreetime/launchy", + "changelog_uri" => "https://github.com/copiousfreetime/launchy/blob/master/HISTORY.md", + "homepage_uri" => "https://github.com/copiousfreetime/launchy", "source_code_uri" => "https://github.com/copiousfreetime/launchy", } end -load 'tasks/default.rake' +load "tasks/default.rake" diff --git a/bin/launchy b/bin/launchy deleted file mode 100755 index 082f1dd..0000000 --- a/bin/launchy +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env ruby - -require 'launchy' -Launchy::Cli.new.run( ARGV, ENV ) diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..6dd3b3d --- /dev/null +++ b/bin/setup @@ -0,0 +1,12 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "pathname" +require "fileutils" + +PROJECT_ROOT = Pathname.new(__dir__).parent.expand_path + +FileUtils.chdir(PROJECT_ROOT) do + puts "Installing dependencies..." + system("bundle install") +end diff --git a/exe/launchy b/exe/launchy new file mode 100755 index 0000000..d92b26f --- /dev/null +++ b/exe/launchy @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "launchy" +Launchy::Cli.new.run(ARGV, ENV) diff --git a/launchy.gemspec b/launchy.gemspec index 22a868e..c2df6d1 100644 --- a/launchy.gemspec +++ b/launchy.gemspec @@ -8,30 +8,25 @@ Gem::Specification.new do |s| s.version = "3.0.0".freeze s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= - s.metadata = { "bug_tracker_uri" => "https://github.com/copiousfreetime/launchy/issues", "changelog_uri" => "https://github.com/copiousfreetime/launchy/blob/master/README.md", "homepage_uri" => "https://github.com/copiousfreetime/launchy", "source_code_uri" => "https://github.com/copiousfreetime/launchy" } if s.respond_to? :metadata= + s.metadata = { "bug_tracker_uri" => "https://github.com/copiousfreetime/launchy/issues", "changelog_uri" => "https://github.com/copiousfreetime/launchy/blob/master/HISTORY.md", "homepage_uri" => "https://github.com/copiousfreetime/launchy", "source_code_uri" => "https://github.com/copiousfreetime/launchy" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Jeremy Hinegardner".freeze] - s.date = "2024-02-04" + s.bindir = "exe".freeze + s.date = "2024-05-01" s.description = "Launchy is helper class for launching cross-platform applications in a fire and forget manner. There are application concepts (browser, email client, etc) that are common across all platforms, and they may be launched differently on each platform. Launchy is here to make a common approach to launching external applications from within ruby programs.".freeze s.email = "jeremy@copiousfreetime.org".freeze s.executables = ["launchy".freeze] - s.extra_rdoc_files = ["CONTRIBUTING.md".freeze, "HISTORY.md".freeze, "Manifest.txt".freeze, "README.md".freeze] - s.files = ["CONTRIBUTING.md".freeze, "HISTORY.md".freeze, "LICENSE".freeze, "Manifest.txt".freeze, "README.md".freeze, "Rakefile".freeze, "bin/launchy".freeze, "lib/launchy.rb".freeze, "lib/launchy/application.rb".freeze, "lib/launchy/applications/browser.rb".freeze, "lib/launchy/argv.rb".freeze, "lib/launchy/cli.rb".freeze, "lib/launchy/descendant_tracker.rb".freeze, "lib/launchy/detect.rb".freeze, "lib/launchy/detect/host_os.rb".freeze, "lib/launchy/detect/host_os_family.rb".freeze, "lib/launchy/detect/nix_desktop_environment.rb".freeze, "lib/launchy/error.rb".freeze, "lib/launchy/os_family.rb".freeze, "lib/launchy/runner.rb".freeze, "lib/launchy/version.rb".freeze, "spec/application_spec.rb".freeze, "spec/applications/browser_spec.rb".freeze, "spec/cli_spec.rb".freeze, "spec/detect/host_os_family_spec.rb".freeze, "spec/detect/host_os_spec.rb".freeze, "spec/detect/nix_desktop_environment_spec.rb".freeze, "spec/launchy_spec.rb".freeze, "spec/mock_application.rb".freeze, "spec/spec_helper.rb".freeze, "spec/tattle-host-os.yaml".freeze, "spec/version_spec.rb".freeze, "tasks/default.rake".freeze, "tasks/this.rb".freeze] + s.extra_rdoc_files = ["CONTRIBUTING.md".freeze, "HISTORY.md".freeze, "LICENSE.txt".freeze, "Manifest.txt".freeze, "README.md".freeze] + s.files = ["CONTRIBUTING.md".freeze, "HISTORY.md".freeze, "LICENSE.txt".freeze, "Manifest.txt".freeze, "README.md".freeze, "exe/launchy".freeze, "launchy.gemspec".freeze, "lib/launchy.rb".freeze, "lib/launchy/application.rb".freeze, "lib/launchy/applications/browser.rb".freeze, "lib/launchy/argv.rb".freeze, "lib/launchy/cli.rb".freeze, "lib/launchy/descendant_tracker.rb".freeze, "lib/launchy/detect.rb".freeze, "lib/launchy/detect/host_os.rb".freeze, "lib/launchy/detect/host_os_family.rb".freeze, "lib/launchy/detect/nix_desktop_environment.rb".freeze, "lib/launchy/error.rb".freeze, "lib/launchy/os_family.rb".freeze, "lib/launchy/runner.rb".freeze, "lib/launchy/version.rb".freeze] s.homepage = "https://github.com/copiousfreetime/launchy".freeze s.licenses = ["ISC".freeze] s.rdoc_options = ["--main".freeze, "README.md".freeze, "--markup".freeze, "tomdoc".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze) - s.rubygems_version = "3.5.3".freeze + s.rubygems_version = "3.5.9".freeze s.summary = "Launchy is helper class for launching cross-platform applications in a fire and forget manner.".freeze - s.test_files = ["spec/application_spec.rb".freeze, "spec/applications/browser_spec.rb".freeze, "spec/cli_spec.rb".freeze, "spec/detect/host_os_family_spec.rb".freeze, "spec/detect/host_os_spec.rb".freeze, "spec/detect/nix_desktop_environment_spec.rb".freeze, "spec/launchy_spec.rb".freeze, "spec/mock_application.rb".freeze, "spec/spec_helper.rb".freeze, "spec/tattle-host-os.yaml".freeze, "spec/version_spec.rb".freeze] s.specification_version = 4 s.add_runtime_dependency(%q.freeze, ["~> 2.8".freeze]) s.add_runtime_dependency(%q.freeze, ["~> 5.0".freeze]) - s.add_development_dependency(%q.freeze, ["~> 13.0".freeze]) - s.add_development_dependency(%q.freeze, ["~> 5.21".freeze]) - s.add_development_dependency(%q.freeze, ["~> 1.0".freeze]) - s.add_development_dependency(%q.freeze, ["~> 6.6".freeze]) - s.add_development_dependency(%q.freeze, ["~> 0.22".freeze]) end diff --git a/lib/launchy.rb b/lib/launchy.rb index 8dd1761..032fb44 100644 --- a/lib/launchy.rb +++ b/lib/launchy.rb @@ -1,6 +1,9 @@ -require 'addressable/uri' -require 'shellwords' -require 'stringio' +# frozen_string_literal: true + +require "English" +require "addressable/uri" +require "shellwords" +require "stringio" # # The entry point into Launchy. This is the sole supported public API. @@ -19,59 +22,62 @@ # application class # module Launchy - class << self # # Launch an application for the given uri string # - def open(uri_s, options = {}, &error_block ) - leftover = extract_global_options( options ) - uri = string_to_uri( uri_s ) - if name = options[:application] then - app = app_for_name( name ) + def open(uri_s, options = {}) + leftover = extract_global_options(options) + uri = string_to_uri(uri_s) + if (name = options[:application]) + app = app_for_name(name) end - if app.nil? then - app = app_for_uri( uri ) - end + app = app_for_uri(uri) if app.nil? - app.new.open( uri, leftover ) - rescue Launchy::Error => le - raise le - rescue Exception => e + app.new.open(uri, leftover) + rescue Launchy::Error => e + raise e + rescue StandardError => e msg = "Failure in opening uri #{uri_s.inspect} with options #{options.inspect}: #{e}" raise Launchy::Error, msg ensure - if $! && block_given? then - yield $! - return # explicitly swallow the errors + if $ERROR_INFO && block_given? + yield $ERROR_INFO + + # explicitly return here to swallow the errors if there was an error + # and we yielded to the block + # rubocop:disable Lint/EnsureReturn + return + # rubocop:enable Lint/EnsureReturn end end - def app_for_uri( uri ) - Launchy::Application.handling( uri ) + def app_for_uri(uri) + Launchy::Application.handling(uri) end - def app_for_name( name ) - Launchy::Application.for_name( name ) + def app_for_name(name) + Launchy::Application.for_name(name) rescue Launchy::ApplicationNotFoundError nil end - def app_for_uri_string( s ) - app_for_uri( string_to_uri( s ) ) + def app_for_uri_string(str) + app_for_uri(string_to_uri(str)) end - def string_to_uri( s ) - s = s.to_s - uri = Addressable::URI.parse( s ) - Launchy.log "URI parsing pass 1 : #{s} -> #{uri.to_hash}" - if not uri.scheme then - uri = Addressable::URI.heuristic_parse( s ) - Launchy.log "URI parsing pass 2 : #{s} -> #{uri.to_hash}" + def string_to_uri(str) + str = str.to_s + uri = Addressable::URI.parse(str) + Launchy.log "URI parsing pass 1 : #{str} -> #{uri.to_hash}" + unless uri.scheme + uri = Addressable::URI.heuristic_parse(str) + Launchy.log "URI parsing pass 2 : #{str} -> #{uri.to_hash}" end - raise Launchy::ArgumentError, "Invalid URI given: #{s.inspect}" unless uri - return uri + raise Launchy::ArgumentError, "Invalid URI given: #{str.inspect}" unless uri + + uri end def reset_global_options @@ -79,49 +85,49 @@ def reset_global_options Launchy.application = nil Launchy.host_os = nil Launchy.dry_run = false - Launchy.path = ENV['PATH'] + Launchy.path = ENV.fetch("PATH", nil) end - def extract_global_options( options ) + def extract_global_options(options) leftover = options.dup - Launchy.debug = leftover.delete( :debug ) || ENV['LAUNCHY_DEBUG'] - Launchy.application = leftover.delete( :application ) || ENV['LAUNCHY_APPLICATION'] - Launchy.host_os = leftover.delete( :host_os ) || ENV['LAUNCHY_HOST_OS'] - Launchy.dry_run = leftover.delete( :dry_run ) || ENV['LAUNCHY_DRY_RUN'] + Launchy.debug = leftover.delete(:debug) || ENV.fetch("LAUNCHY_DEBUG", nil) + Launchy.application = leftover.delete(:application) || ENV.fetch("LAUNCHY_APPLICATION", nil) + Launchy.host_os = leftover.delete(:host_os) || ENV.fetch("LAUNCHY_HOST_OS", nil) + Launchy.dry_run = leftover.delete(:dry_run) || ENV.fetch("LAUNCHY_DRY_RUN", nil) end - def debug=( d ) - @debug = to_bool( d ) + def debug=(enabled) + @debug = to_bool(enabled) end # we may do logging before a call to 'open', hence the need to check # LAUNCHY_DEBUG here def debug? - @debug || to_bool( ENV['LAUNCHY_DEBUG'] ) + @debug || to_bool(ENV.fetch("LAUNCHY_DEBUG", nil)) end - def application=( app ) + def application=(app) @application = app end def application - @application || ENV['LAUNCHY_APPLICATION'] + @application || ENV.fetch("LAUNCHY_APPLICATION", nil) end - def host_os=( host_os ) + def host_os=(host_os) @host_os = host_os end def host_os - @host_os || ENV['LAUNCHY_HOST_OS'] + @host_os || ENV.fetch("LAUNCHY_HOST_OS", nil) end - def dry_run=( dry_run ) - @dry_run = to_bool( dry_run ) + def dry_run=(dry_run) + @dry_run = to_bool(dry_run) end def dry_run? - @dry_run || to_bool( ENV['LAUNCHY_DRY_RUN'] ) + @dry_run || to_bool(ENV.fetch("LAUNCHY_DRY_RUN", nil)) end def bug_report_message @@ -140,15 +146,13 @@ def path=(path) @path = path end - private - def to_bool( arg ) + private + + def to_bool(arg) if arg.is_a? String - arg == 'true' - elsif arg.is_a? TrueClass - true + arg == "true" else - # All other values mapped to false. - false + arg.is_a? TrueClass end end end @@ -157,11 +161,11 @@ def to_bool( arg ) Launchy.reset_global_options end -require 'launchy/version' -require 'launchy/argv' -require 'launchy/cli' -require 'launchy/descendant_tracker' -require 'launchy/error' -require 'launchy/application' -require 'launchy/detect' -require 'launchy/runner' +require "launchy/version" +require "launchy/argv" +require "launchy/cli" +require "launchy/descendant_tracker" +require "launchy/error" +require "launchy/application" +require "launchy/detect" +require "launchy/runner" diff --git a/lib/launchy/application.rb b/lib/launchy/application.rb index faa40fc..f188d4a 100644 --- a/lib/launchy/application.rb +++ b/lib/launchy/application.rb @@ -1,4 +1,6 @@ -require 'set' +# frozen_string_literal: true + +require "set" module Launchy # # Application is the base class of all the application types that launchy may @@ -18,60 +20,61 @@ class << self # Find the application that handles the given uri. # # returns the Class that can handle the uri - def handling( uri ) - klass = find_child( :handles?, uri ) + def handling(uri) + klass = find_child(:handles?, uri) return klass if klass + raise ApplicationNotFoundError, "No application found to handle '#{uri}'" end # Find the application with the given name # # returns the Class that has the given name - def for_name( name ) - klass = find_child( :has_name?, name ) + def for_name(name) + klass = find_child(:has_name?, name) return klass if klass + raise ApplicationNotFoundError, "No application found named '#{name}'" end # Find the given executable in the available paths # # returns the path to the executable or nil if not found - def find_executable( bin, *paths ) - paths = Launchy.path.split( File::PATH_SEPARATOR ) if paths.empty? + def find_executable(bin, *paths) + paths = Launchy.path.split(File::PATH_SEPARATOR) if paths.empty? paths.each do |path| - file = File.join( path, bin ) - if File.executable?( file ) then - Launchy.log "#{self.name} : found executable #{file}" + file = File.join(path, bin) + if File.executable?(file) + Launchy.log "#{name} : found executable #{file}" return file end end - Launchy.log "#{self.name} : Unable to find `#{bin}' in #{paths.join(", ")}" - return nil + Launchy.log "#{name} : Unable to find `#{bin}' in #{paths.join(', ')}" + nil end # Does this class have the given name-like string? # # returns true if the class has the given name - def has_name?( qname ) - qname.to_s.downcase == self.name.split("::").last.downcase + def has_name?(qname) + qname.to_s.downcase == name.split("::").last.downcase end end - attr_reader :host_os_family - attr_reader :runner + attr_reader :host_os_family, :runner def initialize @host_os_family = Launchy::Detect::HostOsFamily.detect @runner = Launchy::Runner.new end - def find_executable( bin, *paths ) - Application.find_executable( bin, *paths ) + def find_executable(bin, *paths) + Application.find_executable(bin, *paths) end - def run( cmd, *args ) - runner.run( cmd, *args ) + def run(cmd, *args) + runner.run(cmd, *args) end end end -require 'launchy/applications/browser' +require "launchy/applications/browser" diff --git a/lib/launchy/applications/browser.rb b/lib/launchy/applications/browser.rb index 72c733c..73a5b31 100644 --- a/lib/launchy/applications/browser.rb +++ b/lib/launchy/applications/browser.rb @@ -1,82 +1,87 @@ -class Launchy::Application - # - # The class handling the browser application and all of its schemes - # - class Browser < Launchy::Application - def self.schemes - %w[ http https ftp file ] - end +# frozen_string_literal: true - def self.handles?( uri ) - return true if schemes.include?( uri.scheme ) - return true if File.exist?( uri.path ) - end +module Launchy + class Application + # + # The class handling the browser application and all of its schemes + # + class Browser < Launchy::Application + def self.schemes + %w[http https ftp file] + end - def windows_app_list - [ 'start "launchy" /b' ] - end + def self.handles?(uri) + return true if schemes.include?(uri.scheme) - def cygwin_app_list - [ 'cmd /C start "launchy" /b' ] - end + true if File.exist?(uri.path) + end - # hardcode this to open? - def darwin_app_list - [ find_executable( "open" ) ] - end + def windows_app_list + ['start "launchy" /b'] + end - def nix_app_list - nix_de = Launchy::Detect::NixDesktopEnvironment.detect - list = nix_de.browsers - list.find_all { |argv| argv.valid? } - end + def cygwin_app_list + ['cmd /C start "launchy" /b'] + end - # use a call back mechanism to get the right app_list that is decided by the - # host_os_family class. - def app_list - host_os_family.app_list( self ) - end + # hardcode this to open? + def darwin_app_list + [find_executable("open")] + end - def browser_env - return [] unless ENV['BROWSER'] - browser_env = ENV['BROWSER'].split( File::PATH_SEPARATOR ) - browser_env.flatten! - browser_env.delete_if { |b| b.nil? || (b.strip.size == 0) } - return browser_env - end + def nix_app_list + nix_de = Launchy::Detect::NixDesktopEnvironment.detect + list = nix_de.browsers + list.find_all(&:valid?) + end - # Get the full commandline of what we are going to add the uri to - def browser_cmdline - browser_env.each do |p| - Launchy.log "#{self.class.name} : possibility from BROWSER environment variable : #{p}" + # use a call back mechanism to get the right app_list that is decided by the + # host_os_family class. + def app_list + host_os_family.app_list(self) end - app_list.each do |p| - Launchy.log "#{self.class.name} : possibility from app_list : #{p}" + + def browser_env + return [] unless ENV["BROWSER"] + + browser_env = ENV["BROWSER"].split(File::PATH_SEPARATOR) + browser_env.flatten! + browser_env.delete_if { |b| b.nil? || b.strip.empty? } + browser_env end - possibilities = (browser_env + app_list).flatten + # Get the full commandline of what we are going to add the uri to + def browser_cmdline + browser_env.each do |p| + Launchy.log "#{self.class.name} : possibility from BROWSER environment variable : #{p}" + end + app_list.each do |p| + Launchy.log "#{self.class.name} : possibility from app_list : #{p}" + end + + possibilities = (browser_env + app_list).flatten - if browser = possibilities.shift then - Launchy.log "#{self.class.name} : Using browser value '#{browser}'" - return browser + if (browser = possibilities.shift) + Launchy.log "#{self.class.name} : Using browser value '#{browser}'" + return browser + end + raise Launchy::CommandNotFoundError, + "Unable to find a browser command. If this is unexpected, #{Launchy.bug_report_message}" end - raise Launchy::CommandNotFoundError, "Unable to find a browser command. If this is unexpected, #{Launchy.bug_report_message}" - end - def cmd_and_args( uri, options = {} ) - cmd = browser_cmdline.to_s - args = [ uri.to_s ] - if cmd =~ /%s/ then - cmd.gsub!( /%s/, args.shift ) + def cmd_and_args(uri, _options = {}) + cmd = browser_cmdline.to_s + args = [uri.to_s] + cmd.gsub!("%s", args.shift) if cmd.include?("%s") + [cmd, args] end - [cmd, args] - end - # final assembly of the command and do %s substitution - # http://www.catb.org/~esr/BROWSER/index.html - def open( uri, options = {} ) - cmd, args = cmd_and_args( uri, options ) - run( cmd, args ) + # final assembly of the command and do %s substitution + # http://www.catb.org/~esr/BROWSER/index.html + def open(uri, options = {}) + cmd, args = cmd_and_args(uri, options) + run(cmd, args) + end end end end diff --git a/lib/launchy/argv.rb b/lib/launchy/argv.rb index d998246..693dede 100644 --- a/lib/launchy/argv.rb +++ b/lib/launchy/argv.rb @@ -1,12 +1,17 @@ +# frozen_string_literal: true + module Launchy + # Internal: Ecapsulate the commandline argumens passed to Launchy + # class Argv attr_reader :argv - def initialize( *args ) + + def initialize(*args) @argv = args.flatten end def to_s - @argv.join(' ') + @argv.join(" ") end def to_str @@ -18,18 +23,18 @@ def [](idx) end def valid? - (not blank?) && executable? + !blank? && executable? end def blank? - @argv.empty? || (@argv.first.strip.size == 0) + @argv.empty? || @argv.first.strip.empty? end def executable? - ::Launchy::Application.find_executable( @argv.first ) + ::Launchy::Application.find_executable(@argv.first) end - def ==( other ) + def ==(other) @argv == other.argv end end diff --git a/lib/launchy/cli.rb b/lib/launchy/cli.rb index e8afeef..d44cc24 100644 --- a/lib/launchy/cli.rb +++ b/lib/launchy/cli.rb @@ -1,9 +1,13 @@ -require 'optparse' +# frozen_string_literal: true + +require "optparse" module Launchy + # Internal: Command line interface for Launchy + # class Cli - attr_reader :options + def initialize @options = {} end @@ -15,70 +19,66 @@ def parser op.separator "" op.separator "Launch Options:" - op.on( "-a", "--application APPLICATION", - "Explicitly specify the application class to use in the launch") do |app| + op.on("-a", "--application APPLICATION", + "Explicitly specify the application class to use in the launch") do |app| @options[:application] = app end - op.on( "-d", "--debug", - "Force debug. Output lots of information.") do |d| + op.on("-d", "--debug", + "Force debug. Output lots of information.") do |_d| @options[:debug] = true end - op.on( "-n", "--dry-run", "Don't launchy, print the command to be executed on stdout" ) do |x| + op.on("-n", "--dry-run", "Don't launchy, print the command to be executed on stdout") do |_x| @options[:dry_run] = true end - op.on( "-o", "--host-os HOST_OS", - "Force launchy to behave as if it was on a particular host os.") do |os| + op.on("-o", "--host-os HOST_OS", + "Force launchy to behave as if it was on a particular host os.") do |os| @options[:host_os] = os end - op.separator "" op.separator "Standard Options:" - op.on( "-h", "--help", "Print this message.") do |h| + op.on("-h", "--help", "Print this message.") do |_h| $stdout.puts op.to_s exit 0 end - op.on( "-v", "--version", "Output the version of Launchy") do |v| + op.on("-v", "--version", "Output the version of Launchy") do |_v| $stdout.puts "Launchy version #{Launchy::VERSION}" exit 0 end - end end - def parse( argv, env ) - parser.parse!( argv ) - return true - rescue ::OptionParser::ParseError => pe - error_output( pe ) + def parse(argv, _env) + parser.parse!(argv) + true + rescue ::OptionParser::ParseError => e + error_output(e) end - def good_run( argv, env ) - if parse( argv, env ) then - Launchy.open( argv.shift, options ) { |e| error_output( e ) } - return true - else - return false - end + def good_run(argv, env) + return false unless parse(argv, env) + + Launchy.open(argv.shift, options) { |e| error_output(e) } + true end - def error_output( error ) + def error_output(error) $stderr.puts "ERROR: #{error}" Launchy.log "ERROR: #{error}" error.backtrace.each do |bt| Launchy.log bt end $stderr.puts "Try `#{parser.program_name} --help' for more information." - return false + false end - def run( argv = ARGV, env = ENV ) - exit 1 unless good_run( argv, env ) + def run(argv = ARGV, env = ENV) + exit 1 unless good_run(argv, env) end end end diff --git a/lib/launchy/descendant_tracker.rb b/lib/launchy/descendant_tracker.rb index 6f2eb72..c60c53a 100644 --- a/lib/launchy/descendant_tracker.rb +++ b/lib/launchy/descendant_tracker.rb @@ -1,4 +1,6 @@ -require 'set' +# frozen_string_literal: true + +require "set" module Launchy # @@ -9,7 +11,7 @@ module Launchy # end # # or - # + # # class Foo # class << self # include DescendantTracker @@ -20,29 +22,29 @@ module Launchy # them in a Set that is available via the 'children' method. # module DescendantTracker - def inherited( klass ) - return unless klass.instance_of?( Class ) - self.children << klass + def inherited(klass) + super + return unless klass.instance_of?(Class) + + children << klass end # # The list of children that are registered # def children - unless defined? @children - @children = Array.new - end - return @children + @children = [] unless defined? @children + @children end # # Find one of the child classes by calling the given method - # and passing all the rest of the parameters to that method in + # and passing all the rest of the parameters to that method in # each child - def find_child( method, *args ) + def find_child(method, *args) children.find do |child| Launchy.log "Checking if class #{child} is the one for #{method}(#{args.join(', ')})}" - child.send( method, *args ) + child.send(method, *args) end end end diff --git a/lib/launchy/detect.rb b/lib/launchy/detect.rb index 6ec64a8..7c84618 100644 --- a/lib/launchy/detect.rb +++ b/lib/launchy/detect.rb @@ -1,8 +1,12 @@ +# frozen_string_literal: true + module Launchy + # Internal: Namespace for detecting the environment that Launchy is running in + # module Detect end end -require 'launchy/detect/host_os' -require 'launchy/detect/host_os_family' -require 'launchy/detect/nix_desktop_environment' +require "launchy/detect/host_os" +require "launchy/detect/host_os_family" +require "launchy/detect/nix_desktop_environment" diff --git a/lib/launchy/detect/host_os.rb b/lib/launchy/detect/host_os.rb index 8913111..7e74a7e 100644 --- a/lib/launchy/detect/host_os.rb +++ b/lib/launchy/detect/host_os.rb @@ -1,32 +1,35 @@ -require 'rbconfig' +# frozen_string_literal: true -module Launchy::Detect - class HostOs +require "rbconfig" - attr_reader :host_os - alias to_s host_os - alias to_str host_os +module Launchy + module Detect + # Internal: Determine the host operating system that Launchy is running on + # + class HostOs + attr_reader :host_os + alias to_s host_os + alias to_str host_os - def initialize( host_os = nil ) - @host_os = host_os + def initialize(host_os = nil) + @host_os = host_os - if not @host_os then - if @host_os = override_host_os then + return if @host_os + + if (@host_os = override_host_os) Launchy.log "Using LAUNCHY_HOST_OS override value of '#{Launchy.host_os}'" else @host_os = default_host_os end end - end - def default_host_os - ::RbConfig::CONFIG['host_os'].downcase - end + def default_host_os + ::RbConfig::CONFIG["host_os"].downcase + end - def override_host_os - Launchy.host_os + def override_host_os + Launchy.host_os + end end - end - end diff --git a/lib/launchy/detect/host_os_family.rb b/lib/launchy/detect/host_os_family.rb index 024a826..ce57132 100644 --- a/lib/launchy/detect/host_os_family.rb +++ b/lib/launchy/detect/host_os_family.rb @@ -1,71 +1,112 @@ -module Launchy::Detect - # Detect the current host os family - # - # If the current host familiy cannot be detected then return - # HostOsFamily::Unknown - class HostOsFamily - class NotFoundError < Launchy::Error; end - extend ::Launchy::DescendantTracker - - class << self - - def detect( host_os = HostOs.new ) - found = find_child( :matches?, host_os ) - return found.new( host_os ) if found - raise NotFoundError, "Unknown OS family for host os '#{host_os}'. #{Launchy.bug_report_message}" +# frozen_string_literal: true + +module Launchy + module Detect + # Detect the current host os family + # + # If the current host familiy cannot be detected then return + # HostOsFamily::Unknown + class HostOsFamily + class NotFoundError < Launchy::Error; end + extend ::Launchy::DescendantTracker + + class << self + def detect(host_os = HostOs.new) + found = find_child(:matches?, host_os) + return found.new(host_os) if found + + raise NotFoundError, "Unknown OS family for host os '#{host_os}'. #{Launchy.bug_report_message}" + end + + def matches?(host_os) + matching_regex.match(host_os.to_s) + end + + def windows? + self == Windows + end + + def darwin? + self == Darwin + end + + def nix? + self == Nix + end + + def cygwin? + self == Cygwin + end end - def matches?( host_os ) - matching_regex.match( host_os.to_s ) + attr_reader :host_os + + def initialize(host_os = HostOs.new) + @host_os = host_os end - def windows?() self == Windows; end - def darwin?() self == Darwin; end - def nix?() self == Nix; end - def cygwin?() self == Cygwin; end - end + def windows? + self.class.windows? + end + def darwin? + self.class.darwin? + end - attr_reader :host_os - def initialize( host_os = HostOs.new ) - @host_os = host_os - end + def nix? + self.class.nix? + end - def windows?() self.class.windows?; end - def darwin?() self.class.darwin?; end - def nix?() self.class.nix?; end - def cygwin?() self.class.cygwin?; end + def cygwin? + self.class.cygwin? + end - #--------------------------- - # All known host os families - #--------------------------- - # - class Windows < HostOsFamily - def self.matching_regex - /(mingw|mswin|msys|windows)/i + #--------------------------- + # All known host os families + #--------------------------- + # + class Windows < HostOsFamily + def self.matching_regex + /(mingw|mswin|msys|windows)/i + end + + def app_list(app) + app.windows_app_list + end end - def app_list( app ) app.windows_app_list; end - end - class Darwin < HostOsFamily - def self.matching_regex - /(darwin|mac os)/i + # Mac OS X family + class Darwin < HostOsFamily + def self.matching_regex + /(darwin|mac os)/i + end + + def app_list(app) + app.darwin_app_list + end end - def app_list( app ) app.darwin_app_list; end - end - class Nix < HostOsFamily - def self.matching_regex - /(linux|bsd|aix|solaris|sunos|dragonfly)/i + # All the *nix family of operating systems, and BSDs + class Nix < HostOsFamily + def self.matching_regex + /(linux|bsd|aix|solaris|sunos|dragonfly)/i + end + + def app_list(app) + app.nix_app_list + end end - def app_list( app ) app.nix_app_list; end - end - class Cygwin < HostOsFamily - def self.matching_regex - /cygwin/i + # Cygwin - if anyone is still using that + class Cygwin < HostOsFamily + def self.matching_regex + /cygwin/i + end + + def app_list(app) + app.cygwin_app_list + end end - def app_list( app ) app.cygwin_app_list; end end end end diff --git a/lib/launchy/detect/nix_desktop_environment.rb b/lib/launchy/detect/nix_desktop_environment.rb index 7269c72..8458785 100644 --- a/lib/launchy/detect/nix_desktop_environment.rb +++ b/lib/launchy/detect/nix_desktop_environment.rb @@ -1,93 +1,98 @@ -module Launchy::Detect - # - # Detect the current desktop environment for *nix machines - # Currently this is Linux centric. The detection is based upon the detection - # used by xdg-open from http://portland.freedesktop.org/ - class NixDesktopEnvironment - class NotFoundError < Launchy::Error; end +# frozen_string_literal: true - extend ::Launchy::DescendantTracker - - # Detect the current *nix desktop environment +module Launchy + module Detect # - # If the current dekstop environment be detected, the return - # NixDekstopEnvironment::Unknown - def self.detect - found = find_child( :is_current_desktop_environment? ) - Launchy.log("Current Desktop environment not found. #{Launchy.bug_report_message}") unless found - return found - end + # Detect the current desktop environment for *nix machines + # Currently this is Linux centric. The detection is based upon the detection + # used by xdg-open from http://portland.freedesktop.org/ + class NixDesktopEnvironment + class NotFoundError < Launchy::Error; end + + extend ::Launchy::DescendantTracker + + # Detect the current *nix desktop environment + # + # If the current dekstop environment be detected, the return + # NixDekstopEnvironment::Unknown + def self.detect + found = find_child(:is_current_desktop_environment?) + Launchy.log("Current Desktop environment not found. #{Launchy.bug_report_message}") unless found + found + end - def self.fallback_browsers - %w[ firefox iceweasel seamonkey opera mozilla netscape galeon links lynx ].map { |x| ::Launchy::Argv.new( x ) } - end + def self.fallback_browsers + %w[firefox iceweasel seamonkey opera mozilla netscape galeon links lynx].map { |x| ::Launchy::Argv.new(x) } + end - def self.browsers - [ browser, fallback_browsers ].flatten - end + def self.browsers + [browser, fallback_browsers].flatten + end - #--------------------------------------- - # The list of known desktop environments - #--------------------------------------- + #--------------------------------------- + # The list of known desktop environments + #--------------------------------------- - class Kde < NixDesktopEnvironment - def self.is_current_desktop_environment? - ENV['KDE_FULL_SESSION'] && - Launchy::Application.find_executable( 'kde-open' ) - end + # KDE desktop environment + class Kde < NixDesktopEnvironment + def self.is_current_desktop_environment? + ENV.fetch("KDE_FULL_SESSION", nil) && + Launchy::Application.find_executable("kde-open") + end - def self.browser - ::Launchy::Argv.new( 'kde-open' ) + def self.browser + ::Launchy::Argv.new("kde-open") + end end - end - class Gnome < NixDesktopEnvironment - def self.is_current_desktop_environment? - ENV['GNOME_DESKTOP_SESSION_ID'] && - Launchy::Application.find_executable( 'gnome-open' ) - end + # Gnome desktop environment + class Gnome < NixDesktopEnvironment + def self.is_current_desktop_environment? + ENV.fetch("GNOME_DESKTOP_SESSION_ID", nil) && + Launchy::Application.find_executable("gnome-open") + end - def self.browser - ::Launchy::Argv.new( 'gnome-open' ) + def self.browser + ::Launchy::Argv.new("gnome-open") + end end - end - class Xfce < NixDesktopEnvironment - def self.is_current_desktop_environment? - if Launchy::Application.find_executable( 'xprop' ) then - %x[ xprop -root _DT_SAVE_MODE].include?("xfce") - else - false + # Xfce desktop environment + class Xfce < NixDesktopEnvironment + def self.is_current_desktop_environment? + if Launchy::Application.find_executable("xprop") + `xprop -root _DT_SAVE_MODE`.include?("xfce") + else + false + end end - end - def self.browser - ::Launchy::Argv.new( %w[ exo-open --launch WebBrowser ] ) + def self.browser + ::Launchy::Argv.new(%w[exo-open --launch WebBrowser]) + end end - end - # Fall back environment as the last case - class Xdg < NixDesktopEnvironment - def self.is_current_desktop_environment? - Launchy::Application.find_executable( browser ) - end + # Fall back environment as the last case + class Xdg < NixDesktopEnvironment + def self.is_current_desktop_environment? + Launchy::Application.find_executable(browser) + end - def self.browser - ::Launchy::Argv.new( 'xdg-open' ) + def self.browser + ::Launchy::Argv.new("xdg-open") + end end - end - # The one that is found when all else fails. And this must be declared last - class NotFound < NixDesktopEnvironment - def self.is_current_desktop_environment? - true - end + # The one that is found when all else fails. And this must be declared last + class NotFound < NixDesktopEnvironment + def self.is_current_desktop_environment? + true + end - def self.browser - ::Launchy::Argv.new + def self.browser + ::Launchy::Argv.new + end end end - end end - diff --git a/lib/launchy/error.rb b/lib/launchy/error.rb index fc1dda9..00299cd 100644 --- a/lib/launchy/error.rb +++ b/lib/launchy/error.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Launchy class Error < ::StandardError; end class ApplicationNotFoundError < Error; end diff --git a/lib/launchy/os_family.rb b/lib/launchy/os_family.rb index 2accf54..297af45 100644 --- a/lib/launchy/os_family.rb +++ b/lib/launchy/os_family.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Launchy # # Model all the Operating system families that can exist. diff --git a/lib/launchy/runner.rb b/lib/launchy/runner.rb index 737c7c1..4674b06 100644 --- a/lib/launchy/runner.rb +++ b/lib/launchy/runner.rb @@ -1,19 +1,27 @@ -require 'childprocess' +# frozen_string_literal: true + +require "childprocess" module Launchy + # Internal: Run a command in a child process + # class Runner - def run( cmd, *args ) - raise Launchy::CommandNotFoundError, "No command found to run with args '#{args.join(' ')}'. If this is unexpected, #{Launchy.bug_report_message}" unless cmd - if Launchy.dry_run? then - $stdout.puts dry_run( cmd, *args ) + def run(cmd, *args) + unless cmd + raise Launchy::CommandNotFoundError, + "No command found to run with args '#{args.join(' ')}'. If this is unexpected, #{Launchy.bug_report_message}" + end + + if Launchy.dry_run? + $stdout.puts dry_run(cmd, *args) else - wet_run( cmd, *args ) + wet_run(cmd, *args) end end - def wet_run( cmd, *args ) - argv = [ cmd, *args ].flatten + def wet_run(cmd, *args) + argv = [cmd, *args].flatten Launchy.log "ChildProcess: argv => #{argv.inspect}" - process = ChildProcess.build( *argv ) + process = ChildProcess.build(*argv) process.io.inherit! process.leader = true @@ -21,7 +29,7 @@ def wet_run( cmd, *args ) process.start end - def dry_run( cmd, *args ) + def dry_run(cmd, *args) shell_commands(cmd, args).join(" ") end @@ -30,17 +38,17 @@ def dry_run( cmd, *args ) # args are not escaped because the whole set is passed to system as *args # and in that case system shell escaping rules are not done. # - def shell_commands( cmd, args ) - cmdline = [ cmd.to_s.shellsplit ] - cmdline << args.flatten.collect{ |a| a.to_s } - return commandline_normalize( cmdline ) + def shell_commands(cmd, args) + cmdline = [cmd.to_s.shellsplit] + cmdline << args.flatten.collect(&:to_s) + commandline_normalize(cmdline) end - def commandline_normalize( cmdline ) + def commandline_normalize(cmdline) c = cmdline.flatten! - c = c.find_all { |a| (not a.nil?) and ( a.size > 0 ) } + c = c.find_all { |a| !a.nil? and a.size.positive? } Launchy.log "commandline_normalized => #{c.join(' ')}" - return c + c end end end diff --git a/lib/launchy/version.rb b/lib/launchy/version.rb index 4700043..a243d1f 100644 --- a/lib/launchy/version.rb +++ b/lib/launchy/version.rb @@ -1,11 +1,13 @@ +# frozen_string_literal: true + module Launchy VERSION = "3.0.0" + # Internal: Version number of Launchy module Version - - MAJOR = Integer(VERSION.split('.')[0]) - MINOR = Integer(VERSION.split('.')[1]) - PATCH = Integer(VERSION.split('.')[2]) + MAJOR = Integer(VERSION.split(".")[0]) + MINOR = Integer(VERSION.split(".")[1]) + PATCH = Integer(VERSION.split(".")[2]) def self.to_a [MAJOR, MINOR, PATCH] diff --git a/spec/application_spec.rb b/spec/application_spec.rb index 27c8fe0..7078b14 100644 --- a/spec/application_spec.rb +++ b/spec/application_spec.rb @@ -1,43 +1,47 @@ -require 'spec_helper' -require 'mock_application' +# frozen_string_literal: true + +require "spec_helper" +require "mock_application" class JunkApp < Launchy::Application - def self.handles?( uri ) + def self.handles?(uri) uri.scheme == "junk" end end describe Launchy::Application do - it 'registers inherited classes' do + it "registers inherited classes" do + # rubocop:disable Lint/ConstantDefinitionInBlock class Junk2App < Launchy::Application - def self.handles?( uri ) + def self.handles?(uri) uri.scheme == "junk2" end end - _(Launchy::Application.children).must_include( Junk2App ) - Launchy::Application.children.delete( Junk2App ) + # rubocop:enable Lint/ConstantDefinitionInBlock + _(Launchy::Application.children).must_include(Junk2App) + Launchy::Application.children.delete(Junk2App) end it "can find an app" do - _(Launchy::Application.children).must_include( JunkApp ) + _(Launchy::Application.children).must_include(JunkApp) _(Launchy::Application.children.size).must_equal 3 - uri = Addressable::URI.parse( "junk:///foo" ) - _(Launchy::Application.handling( uri )).must_equal( JunkApp ) + uri = Addressable::URI.parse("junk:///foo") + _(Launchy::Application.handling(uri)).must_equal(JunkApp) end it "raises an error if an application cannot be found for the given scheme" do - uri = Addressable::URI.parse( "foo:///bar" ) - _(lambda { Launchy::Application.handling( uri ) }).must_raise( Launchy::ApplicationNotFoundError ) + uri = Addressable::URI.parse("foo:///bar") + _(-> { Launchy::Application.handling(uri) }).must_raise(Launchy::ApplicationNotFoundError) end it "can find open or curl or xdg-open" do - found = %w[ open curl xdg-open ].any? do |app| - Launchy::Application.find_executable( app ) + found = %w[open curl xdg-open].any? do |app| + Launchy::Application.find_executable(app) end _(found).must_equal true end it "does not find xyzzy" do - _(Launchy::Application.find_executable( "xyzzy" )).must_be_nil + _(Launchy::Application.find_executable("xyzzy")).must_be_nil end end diff --git a/spec/applications/browser_spec.rb b/spec/applications/browser_spec.rb index 6247cfd..8501a21 100644 --- a/spec/applications/browser_spec.rb +++ b/spec/applications/browser_spec.rb @@ -1,23 +1,24 @@ -require 'spec_helper' +# frozen_string_literal: true + +require "spec_helper" describe Launchy::Application::Browser do before do Launchy.reset_global_options - ENV['KDE_FULL_SESSION'] = "launchy" + ENV["KDE_FULL_SESSION"] = "launchy" @test_url = "http://example.com/" end after do Launchy.reset_global_options - ENV.delete( 'KDE_FULL_SESSION' ) - ENV.delete( 'BROWSER' ) + ENV.delete("KDE_FULL_SESSION") + ENV.delete("BROWSER") end - { 'windows' => 'windows_app_list', - 'darwin' => 'darwin_app_list', - 'cygwin' => 'cygwin_app_list', - 'linux' => 'nix_app_list', - }.each do |host_os, called_method| + { "windows" => "windows_app_list", + "darwin" => "darwin_app_list", + "cygwin" => "cygwin_app_list", + "linux" => "nix_app_list", }.each do |host_os, called_method| it "when host_os is '#{host_os}' the '#{called_method}' method is called" do Launchy.host_os = host_os browser = Launchy::Application::Browser.new @@ -28,27 +29,27 @@ end end - %w[ linux windows darwin cygwin ].each do |host_os| + %w[linux windows darwin cygwin].each do |host_os| it "the BROWSER environment variable overrides any host defaults on '#{host_os}'" do - ENV['BROWSER'] = "my_special_browser --new-tab '%s'" + ENV["BROWSER"] = "my_special_browser --new-tab '%s'" Launchy.host_os = host_os browser = Launchy::Application::Browser.new - cmd, args = browser.cmd_and_args( @test_url ) + cmd, args = browser.cmd_and_args(@test_url) _(cmd).must_equal "my_special_browser --new-tab 'http://example.com/'" _(args).must_equal [] end end it "handles a file on the file system when there is no file:// scheme" do - uri = Addressable::URI.parse( __FILE__ ) - _(Launchy::Application::Browser.handles?( uri )).must_equal true + uri = Addressable::URI.parse(__FILE__) + _(Launchy::Application::Browser.handles?(uri)).must_equal true end it "handles the case where $BROWSER is set and no *nix desktop environment is found" do - ENV.delete( "KDE_FULL_SESSION" ) - ENV.delete( "GNOME_DESKTOP_SESSION_ID" ) - ENV['BROWSER'] = "do-this-instead" - Launchy.host_os = 'linux' + ENV.delete("KDE_FULL_SESSION") + ENV.delete("GNOME_DESKTOP_SESSION_ID") + ENV["BROWSER"] = "do-this-instead" + Launchy.host_os = "linux" browser = Launchy::Application::Browser.new _(browser.browser_cmdline).must_equal "do-this-instead" end @@ -56,11 +57,11 @@ # NOTE: Unable to figure out how capture the stderr from the child which has # moved it at least once. This test just serves the purpose of noting why # something happens, and the problem we are attempting to fix. - #it "When BROWSER is set to something that is not executable, error still appears on stderr" do + # it "When BROWSER is set to something that is not executable, error still appears on stderr" do # ENV['BROWSER'] = "not-an-app" # url = "http://example.com/" - # _, err = capture_subprocess_io do + # _, err = capture_subprocess_io do # begin # Launchy.open( url ) # rescue => nil @@ -68,6 +69,5 @@ # end # #_(err).must_match( /wibble/m ) # err # something - #end + # end end - diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb index 6641f64..44d7774 100644 --- a/spec/cli_spec.rb +++ b/spec/cli_spec.rb @@ -1,7 +1,8 @@ -require 'spec_helper' +# frozen_string_literal: true -describe Launchy::Cli do +require "spec_helper" +describe Launchy::Cli do before do @old_stderr = $stderr $stderr = StringIO.new @@ -17,53 +18,52 @@ $stdout = @old_stdout end - def cli_test( argv, env, exit_val, stderr_regex, stdout_regex ) - begin - Launchy::Cli.new.run( argv, env ) - rescue SystemExit => se - _(se.status).must_equal exit_val - _($stderr.string).must_match stderr_regex if stderr_regex - _($stdout.string).must_match stdout_regex if stdout_regex - end + def cli_test(argv, env, exit_val, stderr_regex, stdout_regex) + Launchy::Cli.new.run(argv, env) + rescue SystemExit => e + _(e.status).must_equal exit_val + _($stderr.string).must_match stderr_regex if stderr_regex + _($stdout.string).must_match stdout_regex if stdout_regex end it "exits 1 when invalid options are given" do - cli_test( %w[ -z foo ], {}, 1, /invalid option/, nil ) + cli_test(%w[-z foo], {}, 1, /invalid option/, nil) end - %w[ -h --help ].each do |opt| + %w[-h --help].each do |opt| it "output help and exits 0 when using #{opt}" do - cli_test( [ opt ], {}, 0, nil, /Print this message/m ) + cli_test([opt], {}, 0, nil, /Print this message/m) end end - %w[ -v --version ].each do |opt| + %w[-v --version].each do |opt| it "outputs version and exits 0 when using #{opt}" do - cli_test( [ opt ], {}, 0, nil, /Launchy version/ ) + cli_test([opt], {}, 0, nil, /Launchy version/) end end it "leaves the url on argv after parsing" do l = Launchy::Cli.new - argv = %w[ --debug --dry-run https://github.com/copiousfreetime/launchy ] - l.parse( argv , {} ) + argv = %w[--debug --dry-run https://github.com/copiousfreetime/launchy] + l.parse(argv, {}) _(argv.size).must_equal 1 - _(argv[0]).must_equal "https://github.com/copiousfreetime/launchy" + _(argv[0]).must_equal "https://github.com/copiousfreetime/launchy" end it "prints the command on stdout when using --dry-run" do - argv = %w[ --debug --dry-run https://github.com/copiousfreetime/launchy ] - Launchy::Cli.new.good_run( argv, {} ) - _($stdout.string).must_match %r[github.com] + argv = %w[--debug --dry-run https://github.com/copiousfreetime/launchy] + Launchy::Cli.new.good_run(argv, {}) + _($stdout.string).must_match(/github.com/) end { - '--application' => [ :application, 'Browser'], - '--host-os' => [ :host_os, 'cygwin'] }.each_pair do |opt, val| + "--application" => [:application, "Browser"], + "--host-os" => [:host_os, "cygwin"], + }.each_pair do |opt, val| it "the commandline option #{opt} sets the program option #{val[0]}" do - argv = [ opt, val[1], "https://github.com/copiousfreetime/launchy" ] + argv = [opt, val[1], "https://github.com/copiousfreetime/launchy"] l = Launchy::Cli.new - rc = l.parse( argv, {} ) + rc = l.parse(argv, {}) _(rc).must_equal true _(argv.size).must_equal 1 _(argv[0]).must_equal "https://github.com/copiousfreetime/launchy" @@ -71,4 +71,3 @@ def cli_test( argv, env, exit_val, stderr_regex, stdout_regex ) end end end - diff --git a/spec/detect/host_os_family_spec.rb b/spec/detect/host_os_family_spec.rb index 33ff056..a7a3cc0 100644 --- a/spec/detect/host_os_family_spec.rb +++ b/spec/detect/host_os_family_spec.rb @@ -1,8 +1,9 @@ -require 'spec_helper' -require 'yaml' +# frozen_string_literal: true -describe Launchy::Detect::HostOsFamily do +require "spec_helper" +require "yaml" +describe Launchy::Detect::HostOsFamily do before do Launchy.reset_global_options end @@ -11,32 +12,30 @@ Launchy.reset_global_options end - YAML::load( IO.read( File.expand_path( "../../tattle-host-os.yaml", __FILE__ ) ) )['host_os'].keys.sort.each do |os| + YAML.load_file(File.expand_path("../tattle-host-os.yaml", __dir__))["host_os"].keys.sort.each do |os| it "OS family of #{os} is detected" do - os_family = Launchy::Detect::HostOsFamily.detect( os ) + os_family = Launchy::Detect::HostOsFamily.detect(os) _(os_family).must_be_kind_of Launchy::Detect::HostOsFamily end end - { 'mswin' => :windows?, - 'darwin' => :darwin?, - 'linux' => :nix?, - 'cygwin' => :cygwin? }.each_pair do |os, method| + { "mswin" => :windows?, + "darwin" => :darwin?, + "linux" => :nix?, + "cygwin" => :cygwin?, }.each_pair do |os, method| it "#{method} returns true for #{os} " do - r = Launchy::Detect::HostOsFamily.detect( os ).send( method ) + r = Launchy::Detect::HostOsFamily.detect(os).send(method) _(r).must_equal true end end it "uses the global host_os overrides" do - ENV['LAUNCHY_HOST_OS'] = "fake-os-2" - _(lambda { Launchy::Detect::HostOsFamily.detect }).must_raise Launchy::Detect::HostOsFamily::NotFoundError - ENV.delete('LAUNCHY_HOST_OS') + ENV["LAUNCHY_HOST_OS"] = "fake-os-2" + _(-> { Launchy::Detect::HostOsFamily.detect }).must_raise Launchy::Detect::HostOsFamily::NotFoundError + ENV.delete("LAUNCHY_HOST_OS") end - it "does not find an os of 'dos'" do - _(lambda { Launchy::Detect::HostOsFamily.detect( 'dos' ) }).must_raise Launchy::Detect::HostOsFamily::NotFoundError + _(-> { Launchy::Detect::HostOsFamily.detect("dos") }).must_raise Launchy::Detect::HostOsFamily::NotFoundError end - end diff --git a/spec/detect/host_os_spec.rb b/spec/detect/host_os_spec.rb index 53ffcff..4c61f2c 100644 --- a/spec/detect/host_os_spec.rb +++ b/spec/detect/host_os_spec.rb @@ -1,19 +1,19 @@ -require 'spec_helper' +# frozen_string_literal: true -describe Launchy::Detect::HostOs do +require "spec_helper" +describe Launchy::Detect::HostOs do it "uses the defult host os from ruby's config" do - _(Launchy::Detect::HostOs.new.host_os).must_equal RbConfig::CONFIG['host_os'] + _(Launchy::Detect::HostOs.new.host_os).must_equal RbConfig::CONFIG["host_os"] end it "uses the passed in value as the host os" do - _(Launchy::Detect::HostOs.new( "fake-os-1").host_os).must_equal "fake-os-1" + _(Launchy::Detect::HostOs.new("fake-os-1").host_os).must_equal "fake-os-1" end it "uses the environment variable LAUNCHY_HOST_OS to override ruby's config" do - ENV['LAUNCHY_HOST_OS'] = "fake-os-2" + ENV["LAUNCHY_HOST_OS"] = "fake-os-2" _(Launchy::Detect::HostOs.new.host_os).must_equal "fake-os-2" - ENV.delete('LAUNCHY_HOST_OS') + ENV.delete("LAUNCHY_HOST_OS") end - end diff --git a/spec/detect/nix_desktop_environment_spec.rb b/spec/detect/nix_desktop_environment_spec.rb index 42472fa..7bf233a 100644 --- a/spec/detect/nix_desktop_environment_spec.rb +++ b/spec/detect/nix_desktop_environment_spec.rb @@ -1,7 +1,8 @@ -require 'spec_helper' +# frozen_string_literal: true -describe Launchy::Detect::NixDesktopEnvironment do +require "spec_helper" +describe Launchy::Detect::NixDesktopEnvironment do before do Launchy.reset_global_options end @@ -12,16 +13,16 @@ it "returns false for XFCE if xprop is not found" do Launchy.host_os = "linux" - _(Launchy::Detect::NixDesktopEnvironment::Xfce.is_current_desktop_environment?).must_equal( false ) + _(Launchy::Detect::NixDesktopEnvironment::Xfce.is_current_desktop_environment?).must_equal(false) end it "returns NotFound if it cannot determine the *nix desktop environment" do Launchy.host_os = "linux" - ENV.delete( "KDE_FULL_SESSION" ) - ENV.delete( "GNOME_DESKTOP_SESSION_ID" ) - Launchy.path = %w[ / /tmp ].join(File::PATH_SEPARATOR) + ENV.delete("KDE_FULL_SESSION") + ENV.delete("GNOME_DESKTOP_SESSION_ID") + Launchy.path = %w[/ /tmp].join(File::PATH_SEPARATOR) not_found = Launchy::Detect::NixDesktopEnvironment.detect - _(not_found).must_equal( Launchy::Detect::NixDesktopEnvironment::NotFound ) - _(not_found.browser).must_equal( Launchy::Argv.new ) + _(not_found).must_equal(Launchy::Detect::NixDesktopEnvironment::NotFound) + _(not_found.browser).must_equal(Launchy::Argv.new) end end diff --git a/spec/launchy_spec.rb b/spec/launchy_spec.rb index dc3398f..b14f6ac 100644 --- a/spec/launchy_spec.rb +++ b/spec/launchy_spec.rb @@ -1,16 +1,17 @@ -require 'spec_helper' -require 'pathname' -require 'mock_application' +# frozen_string_literal: true -describe Launchy do +require "spec_helper" +require "pathname" +require "mock_application" +describe Launchy do before do Launchy.reset_global_options - @stderr = $stderr + @stderr = $stderr $stderr = StringIO.new @stdout = $stdout $stdout = StringIO.new - @invalid_url = 'blah://example.com/invalid' + @invalid_url = "blah://example.com/invalid" end after do @@ -20,7 +21,7 @@ end it "logs to stderr when LAUNCHY_DEBUG environment variable is set" do - ENV["LAUNCHY_DEBUG"] = 'true' + ENV["LAUNCHY_DEBUG"] = "true" old_stderr = $stderr $stderr = StringIO.new Launchy.log "This is a test log message" @@ -30,59 +31,50 @@ end it "sets the global option :dry_run to true if LAUNCHY_DRY_RUN environment variable is 'true'" do - ENV['LAUNCHY_DRY_RUN'] = 'true' + ENV["LAUNCHY_DRY_RUN"] = "true" Launchy.extract_global_options({}) _(Launchy.dry_run?).must_equal true - ENV['LAUNCHY_DRY_RUN'] = nil + ENV["LAUNCHY_DRY_RUN"] = nil end it "sets the global option :debug to true if LAUNCHY_DEBUG environment variable is 'true'" do - ENV['LAUNCHY_DEBUG'] = 'true' + ENV["LAUNCHY_DEBUG"] = "true" Launchy.extract_global_options({}) _(Launchy.debug?).must_equal true - ENV['LAUNCHY_DEBUG'] = nil + ENV["LAUNCHY_DEBUG"] = nil end it "has the global option :debug" do - Launchy.extract_global_options( { :debug => 'true' } ) + Launchy.extract_global_options({ debug: "true" }) _(Launchy.debug?).must_equal true - Launchy.extract_global_options( { :debug => true } ) + Launchy.extract_global_options({ debug: true }) _(Launchy.debug?).must_equal true end it "has the global option :dry_run" do - Launchy.extract_global_options( { :dry_run => 'true' } ) + Launchy.extract_global_options({ dry_run: "true" }) _(Launchy.dry_run?).must_equal true - Launchy.extract_global_options( { :dry_run => true } ) + Launchy.extract_global_options({ dry_run: true }) _(Launchy.dry_run?).must_equal true end it "has the global option :application" do - Launchy.extract_global_options( { :application => "wibble" } ) - _(Launchy.application).must_equal 'wibble' + Launchy.extract_global_options({ application: "wibble" }) + _(Launchy.application).must_equal "wibble" end it "has the global option :host_os" do - Launchy.extract_global_options( { :host_os => "my-special-os-v2" } ) - _(Launchy.host_os).must_equal 'my-special-os-v2' + Launchy.extract_global_options({ host_os: "my-special-os-v2" }) + _(Launchy.host_os).must_equal "my-special-os-v2" end it "raises an exception if no scheme is found for the given uri" do - _(lambda { Launchy.open( @invalid_url ) }).must_raise Launchy::ApplicationNotFoundError - end - - it "raises an exepction if the browser failed to launch" do - skip("because headless CI") if ENV["CI"] == "true" - caught = nil - Launchy.open( @invalid_url, application: "browser") do |exception| - caught = exception - end - _(caught).must_be_kind_of Launchy::Error + _(-> { Launchy.open(@invalid_url) }).must_raise Launchy::ApplicationNotFoundError end it "asssumes we open a local file if we have an exception if we have an invalid scheme and a valid path" do uri = "blah://example.com/#{__FILE__}" - Launchy.open( uri , :dry_run => true ) + Launchy.open(uri, dry_run: true) parts = $stdout.string.strip.split _(parts.size).must_be :>, 1 _(parts.last).must_equal uri @@ -90,53 +82,55 @@ it "opens a local file if we have a drive letter and a valid path on windows" do uri = "C:#{__FILE__}" - Launchy.open( uri, :dry_run => true, :host_os => 'windows' ) - _($stdout.string.strip).must_equal 'start launchy /b ' + uri + Launchy.open(uri, dry_run: true, host_os: "windows") + _($stdout.string.strip).must_equal "start launchy /b #{uri}" end it "opens a data url with a forced browser application" do uri = "data:text/html,hello%20world" - Launchy.open( uri, :dry_run => true, :application => "browser" ) + Launchy.open(uri, dry_run: true, application: "browser") _($stdout.string.strip).must_match(/open/) # /usr/bin/open or xdg-open end it "calls the block if instead of raising an exception if there is an error" do - Launchy.open( @invalid_url ) { $stderr.puts "oops had an error opening #{@invalid_url}" } + Launchy.open(@invalid_url) { $stderr.puts "oops had an error opening #{@invalid_url}" } _($stderr.string.strip).must_equal "oops had an error opening #{@invalid_url}" end it "calls the block with the values passed to launchy and the error" do - options = { :dry_run => true } - Launchy.open( @invalid_url, :dry_run => true ) { |e| $stderr.puts "had an error opening #{@invalid_url} with options #{options}: #{e}" } + options = { dry_run: true } + Launchy.open(@invalid_url, dry_run: true) do |e| + $stderr.puts "had an error opening #{@invalid_url} with options #{options}: #{e}" + end _($stderr.string.strip).must_equal "had an error opening #{@invalid_url} with options #{options}: No application found to handle '#{@invalid_url}'" end it "raises the error in the called block" do - _(lambda { Launchy.open( @invalid_url ) { raise StandardError, "KABOOM!" } }).must_raise StandardError + _(-> { Launchy.open(@invalid_url) { raise StandardError, "KABOOM!" } }).must_raise StandardError end it "can force a specific application to be used" do - result = Launchy.open( "http://example.com", :application => "mockapplication" ) + result = Launchy.open("http://example.com", application: "mockapplication") _(result).must_equal "MockApplication opened http://example.com" end - [ 'www.example.com', 'www.example.com/foo/bar', "C:#{__FILE__}" ].each do |x| + ["www.example.com", "www.example.com/foo/bar", "C:#{__FILE__}"].each do |x| it "picks a Browser for #{x}" do - app = Launchy.app_for_uri_string( x ) - _(app).must_equal( Launchy::Application::Browser ) + app = Launchy.app_for_uri_string(x) + _(app).must_equal(Launchy::Application::Browser) end end it "can use a Pathname as the URI" do - path = Pathname.new( Dir.pwd ) - app = Launchy.app_for_uri_string( path ) - _(app).must_equal( Launchy::Application::Browser ) + path = Pathname.new(Dir.pwd) + app = Launchy.app_for_uri_string(path) + _(app).must_equal(Launchy::Application::Browser) end - [ "BROWSER", "bRoWsEr", "browser", "Browser" ].each do |x| + %w[BROWSER bRoWsEr browser Browser].each do |x| it "can find the browser by name #{x}" do - app = Launchy.app_for_name( x ) - _(app).must_equal( Launchy::Application::Browser ) + app = Launchy.app_for_name(x) + _(app).must_equal(Launchy::Application::Browser) end end end diff --git a/spec/mock_application.rb b/spec/mock_application.rb index e694d70..233373a 100644 --- a/spec/mock_application.rb +++ b/spec/mock_application.rb @@ -1,13 +1,15 @@ +# frozen_string_literal: true + class MockApplication < Launchy::Application def self.schemes - %w[ mock mockother ] + %w[mock mockother] end - def self.handles?( uri ) - schemes.include?( uri.scheme ) + def self.handles?(uri) + schemes.include?(uri.scheme) end - def open( uri, options = {} ) - return "MockApplication opened #{uri}" + def open(uri, _options = {}) + "MockApplication opened #{uri}" end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 510bc44..468cb77 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,6 @@ -require 'simplecov' -SimpleCov.start if ENV['COVERAGE'] +# frozen_string_literal: true -gem 'minitest' -require 'launchy' -require 'stringio' -require 'minitest/autorun' -require 'minitest/pride' +require "launchy" +require "stringio" +require "minitest/autorun" +require "minitest/pride" diff --git a/spec/version_spec.rb b/spec/version_spec.rb index 291e2ad..9155f52 100644 --- a/spec/version_spec.rb +++ b/spec/version_spec.rb @@ -1,9 +1,11 @@ -require 'spec_helper' +# frozen_string_literal: true -describe 'Launchy::VERSION' do +require "spec_helper" + +describe "Launchy::VERSION" do it "should have a #.#.# format" do - _(Launchy::VERSION).must_match( /\d+\.\d+\.\d+/ ) - _(Launchy::Version.to_s).must_match( /\d+\.\d+\.\d+/ ) + _(Launchy::VERSION).must_match(/\d+\.\d+\.\d+/) + _(Launchy::Version.to_s).must_match(/\d+\.\d+\.\d+/) Launchy::Version.to_a.each do |n| _(n.to_i).must_be :>=, 0 end diff --git a/tasks/default.rake b/tasks/default.rake index 93f5683..8299f21 100644 --- a/tasks/default.rake +++ b/tasks/default.rake @@ -1,55 +1,44 @@ +# frozen_string_literal: true + # vim: syntax=ruby -require 'rake/clean' -require 'digest' -#------------------------------------------------------------------------------ -# If you want to Develop on this project just run 'rake develop' and you'll -# have all you need to get going. If you want to use bundler for development, -# then run 'rake develop:using_bundler' -#------------------------------------------------------------------------------ -namespace :develop do - - # Install all the development and runtime dependencies of this gem using the - # gemspec. - task :default => 'Gemfile' do - require 'rubygems/dependency_installer' - installer = ::Gem::DependencyInstaller.new - puts "Installing bundler..." - installer.install 'bundler' - sh 'bundle install' - puts "\n\nNow run 'rake test'" +require "rake/clean" +require "digest" +#------------------------------------------------------------------------------ +# Minitest - standard TestTask +#------------------------------------------------------------------------------ +begin + require "minitest/test_task" + Minitest::TestTask.create(:test) do |t| + t.libs << "spec" + t.warning = true + t.test_globs = "{test,spec}/**/{test_*,*_spec}.rb" end - # Create a Gemfile that just references the gemspec - file 'Gemfile' => :gemspec do - File.open( "Gemfile", "w+" ) do |f| - f.puts "# DO NOT EDIT - This file is automatically generated" - f.puts "# Make changes to Manifest.txt and/or Rakefile and regenerate" - f.puts 'source "https://rubygems.org"' - f.puts 'gemspec' - end - end + desc "Run the requirements for the tests" + task "test:requirements" + + desc "and the requirements" + task test: "test:requirements" + task default: :test +rescue LoadError + This.task_warning("test") end -desc "Bootstrap development" -task :develop => "develop:default" #------------------------------------------------------------------------------ -# Minitest - standard TestTask +# Coverage - integrated with minitest #------------------------------------------------------------------------------ begin - require 'minitest/test_task' - Minitest::TestTask.create( :test) do |t| - t.libs << "lib" + require "simplecov" + desc "Run tests with code coverage" + Minitest::TestTask.create(:coverage) do |t| + t.test_prelude = 'require "simplecov"; SimpleCov.start;' t.libs << "spec" - t.libs << "test" t.warning = true t.test_globs = "{test,spec}/**/{test_*,*_spec}.rb" end - - task :test_requirements - task :test => :test_requirements - task :default => :test + CLOBBER << "coverage" if File.directory?("coverage") rescue LoadError - This.task_warning( 'test' ) + This.task_warning("simplecov") end #------------------------------------------------------------------------------ @@ -57,48 +46,58 @@ end # recent version of rdoc since it is the one that has 'tomdoc' markup #------------------------------------------------------------------------------ begin - gem 'rdoc' # otherwise we get the wrong task from stdlib - require 'rdoc/task' + gem "rdoc" # otherwise we get the wrong task from stdlib + require "rdoc/task" RDoc::Task.new do |t| - t.markup = 'tomdoc' - t.rdoc_dir = 'doc' - t.main = 'README.md' + t.markup = "tomdoc" + t.rdoc_dir = "doc" + t.main = "README.md" t.title = "#{This.name} #{This.version}" - t.rdoc_files.include( FileList['*.{rdoc,md,txt}'], FileList['ext/**/*.c'], - FileList['lib/**/*.rb'] ) + t.rdoc_files.include(FileList["*.{rdoc,md,txt}"], FileList["ext/**/*.c"], + FileList["lib/**/*.rb"]) end rescue StandardError, LoadError - This.task_warning( 'rdoc' ) + This.task_warning("rdoc") end #------------------------------------------------------------------------------ -# Coverage - optional code coverage, rcov for 1.8 and simplecov for 1.9, so -# for the moment only rcov is listed. +# Reek - static code analysis #------------------------------------------------------------------------------ begin - require 'simplecov' - desc 'Run tests with code coverage' - task :coverage do - ENV['COVERAGE'] = 'true' - Rake::Task[:test].execute - end - CLOBBER << 'coverage' if File.directory?( 'coverage' ) + require "reek/rake/task" + Reek::Rake::Task.new rescue LoadError - This.task_warning( 'simplecov' ) + This.task_warning("reek") +end + +#------------------------------------------------------------------------------ +# Rubocop - static code analysis +#------------------------------------------------------------------------------ +begin + require "rubocop/rake_task" + RuboCop::RakeTask.new +rescue LoadError + This.task_warning("rubocop") +end + +def git_files + IO.popen(%w[git ls-files -z], chdir: This.project_root, err: IO::NULL) do |ls| + ls.readlines("\x0", chomp: true) + end end #------------------------------------------------------------------------------ # Manifest - We want an explicit list of thos files that are to be packaged in # the gem. Most of this is from Hoe. #------------------------------------------------------------------------------ -namespace 'manifest' do +namespace "manifest" do desc "Check the manifest" - task :check => :clean do - files = FileList["**/*", ".*"].exclude( This.exclude_from_manifest ).to_a.sort - files = files.select{ |f| File.file?( f ) } + task check: :clean do + files = FileList["**/*", ".*"].to_a.sort + files = files.select { |f| (f =~ This.include_in_manifest) && File.file?(f) } tmp = "Manifest.tmp" - File.open( tmp, 'w' ) do |f| + File.open(tmp, "w") do |f| f.puts files.join("\n") end @@ -111,10 +110,10 @@ namespace 'manifest' do end desc "Generate the manifest" - task :generate => :clean do - files = %x[ git ls-files ].split("\n").sort - files.reject! { |f| f =~ This.exclude_from_manifest } - File.open( "Manifest.txt", "w" ) do |f| + task generate: :clean do + files = git_files.grep(This.include_in_manifest) + files.sort! + File.open("Manifest.txt", "w") do |f| f.puts files.join("\n") end end @@ -123,52 +122,73 @@ end #------------------------------------------------------------------------------ # Fixme - look for fixmes and report them #------------------------------------------------------------------------------ -namespace :fixme do - task :default => 'manifest:check' do - This.manifest.each do |file| - next if file == __FILE__ - next unless file =~ %r/(txt|rb|md|rdoc|css|html|xml|css)\Z/ - puts "FIXME: Rename #{file}" if file =~ /fixme/i - IO.readlines( file ).each_with_index do |line, idx| - prefix = "FIXME: #{file}:#{idx+1}".ljust(42) - puts "#{prefix} => #{line.strip}" if line =~ /fixme/i +def fixme_project_root + This.project_path("../fixme") +end + +def fixme_project_path(subtree) + fixme_project_root.join(subtree) +end + +def local_fixme_files + local_files = Dir.glob("tasks/**/*") + local_files.concat(Dir.glob(".semaphore/*")) + local_files.concat(Dir.glob(".rubocop.yml")) + local_files.concat(Dir.glob("bin/*")) +end + +def upstream_fixme_files + fixme_project_root.glob("{tasks/**/*,.semaphore/*,.rubocop.yml,bin/*}") +end + +def outdated_fixme_files + local_fixme_files.select do |local| + upstream = fixme_project_path(local) + if upstream.exist? + if File.exist?(local) + (Digest::SHA256.file(local) != Digest::SHA256.file(upstream)) + else + true end end end +end - def fixme_project_root - This.project_path( '../fixme' ) - end +def fixme_up_to_date? + outdated_fixme_files.empty? +end - def fixme_project_path( subtree ) - fixme_project_root.join( subtree ) - end +namespace :fixme do + task default: "manifest:check" do + This.manifest.each do |file| + next if file == __FILE__ + next unless /(txt|rb|md|rdoc|css|html|xml|css)\Z/.match?(file) - def local_fixme_files - local_files = This.manifest.select { |p| p =~ %r|^tasks/| } - local_files.concat( Dir.glob( ".semaphore/*" ) ) + puts "FIXME: Rename #{file}" if /fixme/i.match?(file) + File.readlines(file).each_with_index do |line, idx| + prefix = "FIXME: #{file}:#{idx + 1}".ljust(42) + puts "#{prefix} => #{line.strip}" if /fixme/i.match?(line) + end + end end - def outdated_fixme_files - local_fixme_files.select do |local| - upstream = fixme_project_path( local ) - if upstream.exist? then - if File.exist?( local ) then - ( Digest::SHA256.file( local ) != Digest::SHA256.file( upstream ) ) - else - true - end - end + desc "See the local fixme files" + task :local_files do + local_fixme_files.each do |f| + puts f end end - def fixme_up_to_date? - outdated_fixme_files.empty? + desc "See the upstream fixme files" + task :upstream_files do + upstream_fixme_files.each do |f| + puts f + end end desc "See if the fixme tools are outdated" task :outdated do - if fixme_up_to_date? then + if fixme_up_to_date? puts "Fixme files are up to date." else outdated_fixme_files.each do |f| @@ -177,23 +197,35 @@ namespace :fixme do end end + desc "Show the diff between the local and upstream fixme files" + task :diff do + outdated_fixme_files.each do |f| + upstream = fixme_project_path(f) + puts "===> Start Diff for #{f}" + output = `diff -du #{upstream} #{f}` + puts output unless output.empty? + puts "===> End Diff for #{f}" + puts + end + end + desc "Update outdated fixme files" task :update do - if fixme_up_to_date? then + if fixme_up_to_date? puts "Fixme files are already up to date." else puts "Updating fixme files:" outdated_fixme_files.each do |local| - upstream = fixme_project_path( local ) + upstream = fixme_project_path(local) puts " * #{local}" - FileUtils.cp( upstream, local ) + FileUtils.cp(upstream, local) end puts "Use your git commands as appropriate." end end end desc "Look for fixmes and report them" -task :fixme => "fixme:default" +task fixme: "fixme:default" #------------------------------------------------------------------------------ # Gem Specification @@ -201,7 +233,7 @@ task :fixme => "fixme:default" # Really this is only here to support those who use bundler desc "Build the #{This.name}.gemspec file" task :gemspec do - File.open( This.gemspec_file, "wb+" ) do |f| + File.open(This.gemspec_file, "wb+") do |f| f.puts "# DO NOT EDIT - This file is automatically generated" f.puts "# Make changes to Manifest.txt and/or Rakefile and regenerate" f.write This.platform_gemspec.to_ruby @@ -212,8 +244,8 @@ end CLOBBER << "**/*.rbc" # The standard gem packaging task, everyone has it. -require 'rubygems/package_task' -::Gem::PackageTask.new( This.platform_gemspec ) do +require "rubygems/package_task" +Gem::PackageTask.new(This.platform_gemspec) do # nothing end @@ -231,17 +263,14 @@ end # 8) push the tag # 7) pus the gem #------------------------------------------------------------------------------ +desc "Check to make sure we are ready to release" task :release_check do - unless `git branch` =~ /^\* main/ - abort "You must be on the main branch to release!" - end - unless `git status` =~ /^nothing to commit/m - abort "Nope, sorry, you have unfinished business" - end + abort "You must be on the main branch to release!" unless /^\* main/.match?(`git branch`) + abort "Nope, sorry, you have unfinished business" unless /^nothing to commit/m.match?(`git status`) end desc "Create tag v#{This.version}, build and push #{This.platform_gemspec.full_name} to rubygems.org" -task :release => [ :release_check, 'manifest:check', :gem ] do +task release: [:release_check, "manifest:check", :gem] do sh "git commit --allow-empty -a -m 'Release #{This.version}'" sh "git tag -a -m 'v#{This.version}' v#{This.version}" sh "git push origin main" diff --git a/tasks/this.rb b/tasks/this.rb index 6b1045d..e7bb72a 100644 --- a/tasks/this.rb +++ b/tasks/this.rb @@ -1,4 +1,6 @@ -require 'pathname' +# frozen_string_literal: true + +require "pathname" # Public: A Class containing all the metadata and utilities needed to manage a # ruby project. @@ -15,22 +17,22 @@ class ThisProject # The homepage of this project attr_accessor :homepage - # The regex of files to exclude from the manifest - attr_accessor :exclude_from_manifest + # The regex of files to include in the manifest + attr_accessor :include_in_manifest # The hash of Gem::Specifications keyed' by platform attr_accessor :gemspecs + # List of cross platforms to build the gem for + attr_accessor :cross_platforms + # Public: Initialize ThisProject # # Yields self - def initialize(&block) - @exclude_from_manifest = Regexp.union(/\.(git|DS_Store|semaphore)/, - /^(doc|coverage|pkg|tmp|Gemfile(\.lock)?)/, - /^[^\/]+\.gemspec/, - /\.(swp|jar|bundle|so|rvmrc|travis.yml|byebug_history|fossa.yml|ruby-version)$/, - /~$/) - @gemspecs = Hash.new + def initialize + @include_in_manifest = Regexp.union(/\Alib/, /\Aexe/, /\Aext/, + %r{\A[^/]+\.(gemspec|txt|md|rdoc|adoc)\Z}) + @gemspecs = {} yield self if block_given? end @@ -42,12 +44,10 @@ def initialize(&block) # # Returns a String version def version - [ "lib/#{ name }.rb", "lib/#{ name }/version.rb" ].each do |v| - path = project_path( v ) + ["lib/#{name}.rb", "lib/#{name}/version.rb"].each do |v| + path = project_path(v) line = path.read[/^\s*VERSION\s*=\s*.*/] - if line then - return line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1] - end + return line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1] if line end end @@ -57,22 +57,22 @@ def version # section_name - the section out of the file from which to parse data # # Retuns the text of the section as an array of paragrphs. - def section_of( file, section_name ) - re = /^[=#]+ (.*)$/ - sectional = project_path( file ) - parts = sectional.read.split( re )[1..-1] - parts.map! { |p| p.strip } - - sections = Hash.new - Hash[*parts].each do |k,v| + def section_of(file, section_name) + re = /^[=#]+ (.*)$/ + sectional = project_path(file) + parts = sectional.read.split(re)[1..] + parts.map!(&:strip) + + sections = {} + Hash[*parts].each do |k, v| sections[k] = v.split("\n\n") end - return sections[section_name] + sections[section_name] end # Internal: print out a warning about the give task - def task_warning( task ) - warn "WARNING: '#{task}' tasks are not defined. Please run 'rake develop'" + def task_warning(task) + warn "WARNING: '#{task}' tasks are not defined. Please run 'bin/setup'" end # Internal: Return the full path to the file that is relative to the project @@ -81,15 +81,15 @@ def task_warning( task ) # path - the relative path of the file from the project root # # Returns the Pathname of the file - def project_path( *relative_path ) - project_root.join( *relative_path ) + def project_path(*relative_path) + project_root.join(*relative_path) end # Internal: The absolute path of this file # # Returns the Pathname of this file. def this_file_path - Pathname.new( __FILE__ ).expand_path + Pathname.new(__FILE__).expand_path end # Internal: The root directory of this project @@ -100,7 +100,7 @@ def this_file_path # Returns the Pathname of the directory def project_root this_file_path.ascend do |p| - rakefile = p.join( 'Rakefile' ) + rakefile = p.join("Rakefile") return p if rakefile.exist? end end @@ -109,16 +109,16 @@ def project_root # # Returns an Array of strings def manifest - manifest_file = project_path( "Manifest.txt" ) + manifest_file = project_path("Manifest.txt") abort "You need a Manifest.txt" unless manifest_file.readable? - manifest_file.readlines.map { |l| l.strip } + manifest_file.readlines.map(&:strip) end # Internal: Return the files that define the extensions # # Returns an Array def extension_conf_files - manifest.grep( /extconf.rb\Z/ ) + manifest.grep(/extconf.rb\Z/) end # Internal: Returns the gemspace associated with the current ruby platform @@ -139,25 +139,26 @@ def core_gemspec spec.license = license spec.files = manifest - spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) } - spec.test_files = spec.files.grep(/^spec/) + spec.bindir = "exe" + spec.executables = spec.files.grep(/^exe/) { |f| File.basename(f) } + spec.test_files = [] spec.extra_rdoc_files += spec.files.grep(/(txt|rdoc|md)$/) - spec.rdoc_options = [ "--main" , 'README.md', - "--markup", "tomdoc" ] + spec.rdoc_options = ["--main", "README.md", + "--markup", "tomdoc",] - spec.required_ruby_version = '>= 2.3.0' + spec.required_ruby_version = ">= 2.3.0" end end # Internal: Return the gemspec for the ruby platform - def ruby_gemspec( core = core_gemspec, &block ) - yielding_gemspec( 'ruby', core, &block ) + def ruby_gemspec(core = core_gemspec, &block) + yielding_gemspec("ruby", core, &block) end # Internal: Return the gemspec for the jruby platform - def java_gemspec( core = core_gemspec, &block ) - yielding_gemspec( 'java', core, &block ) + def java_gemspec(core = core_gemspec, &block) + yielding_gemspec("java", core, &block) end # Internal: give an initial spec and a key, create a new gemspec based off of @@ -166,21 +167,21 @@ def java_gemspec( core = core_gemspec, &block ) # This will force the new gemspecs 'platform' to be that of the key, since the # only reason you would have multiple gemspecs at this point is to deal with # different platforms. - def yielding_gemspec( key, core ) + def yielding_gemspec(key, core) spec = gemspecs[key] ||= core.dup spec.platform = key yield spec if block_given? - return spec + spec end # Internal: Return the platform of ThisProject at the current moment in time. def platform - (RUBY_PLATFORM == "java") ? 'java' : Gem::Platform::RUBY + (RUBY_PLATFORM == "java") ? "java" : Gem::Platform::RUBY end # Internal: Return the DESCRIPTION section of the README.rdoc file def description_section - section_of( 'README.md', 'DESCRIPTION') + section_of("README.md", "DESCRIPTION") end # Internal: Return the summary text from the README @@ -190,18 +191,18 @@ def summary # Internal: Return the full description text from the README def description - description_section.join(" ").tr("\n", ' ').gsub(/[{}]/,'').gsub(/\[[^\]]+\]/,'') # strip rdoc + description_section.join(" ").tr("\n", " ").gsub(/[{}]/, "").gsub(/\[[^\]]+\]/, "") # strip rdoc end def license - license_file = project_path("LICENSE") + license_file = project_path("LICENSE.txt") line = license_file.readlines.first line.split(/\s+/).first end # Internal: The path to the gemspec file def gemspec_file - project_path( "#{ name }.gemspec" ) + project_path("#{name}.gemspec") end end