Top Level Namespace

Defined Under Namespace

Modules: Atk, AtkToolbox, FileSystem, Git, OS Classes: AtkPackage, AtkPaths, Code, CommandResult, ConsoleCode, Info, RubyCode, String, Version

Constant Summary collapse

MSWINDOWS =
false
WIN32EXTS =
'.{exe,com,bat}'
Console =

Console

Class.new do
    
    CACHE = Class.new do
        attr_accessor :prompt
    end.new
    
    # 
    # prompt properties
    # 
    # see https://github.com/piotrmurach/tty-prompt
    def _load_prompt
        require "tty-prompt"
        CACHE::prompt = TTY::Prompt.new
    end
    # generate interface for TTY prompt with lazy require
    for each in [ :ask, :keypress, :multiline, :mask, :yes?, :no?, :select, :multi_select, :enum_select, :expand, :collect, :suggest, :slider, :say, :warn, :error ]
        eval("            def \#{each}(*args, **kwargs)\n                self._load_prompt() if CACHE::prompt == nil\n                if block_given?\n                    CACHE::prompt.\#{each}(*args, **kwargs) do |*block_args, **block_kwargs|\n                        yield(*block_args, **block_kwargs)\n                    end\n                else\n                    CACHE::prompt.\#{each}(*args, **kwargs)\n                end\n            end\n        HEREDOC\n    end\n    \n    def ok(message)\n        puts message.green + \"\\n[press enter to continue]\".light_black\n        gets\n    end\n    alias :ok? :ok\n    \n    attr_accessor :verbose\n    \n    def _save_args\n        if @args == nil\n            @args = []\n            for each in ARGV\n                @args << each\n            end\n        end\n    end\n    \n    def args\n        self._save_args()\n        return @args\n    end\n    \n    def stdin\n        # save arguments before clearing them\n        self._save_args()\n        # must clear arguments in order to get stdin\n        ARGV.clear\n        # check if there is a stdin\n        if !(STDIN.tty?)\n            @stdin = $stdin.read\n        end\n        return @stdin\n    end\n    \n    #\n    # returns the command object, ignores errors\n    #\n    def run!(command, **keyword_arguments)\n        if command.is_a?(String)\n            # by default return a string with stderr included\n            begin\n                command_info = IO.popen(command, err: [:child, :out])\n                Process.wait(command_info.pid)\n                process_info = $?\n                result = CommandResult.new(command_info, process_info)\n                if keyword_arguments[:silent] != true\n                    puts result.read\n                end\n            rescue\n                process_info = $?\n                result = CommandResult.new(nil, process_info)\n            end\n            return result\n        else\n            raise <<-HEREDOC.remove_indent\n                \n                \n                The argument for run!() must be a string\n                this restriction will be lifted in the future\n            HEREDOC\n        end\n    end\n\n    # \n    # returns true if successful, false/nil on error\n    # \n    def run?(command, **keyword_arguments)\n        if command.is_a?(String)\n            return system(command)\n        else\n            raise <<-HEREDOC.remove_indent\n                \n                \n                The argument for run?() must be a string\n                this restriction will be lifted in the future\n            HEREDOC\n        end\n    end\n\n    # \n    # returns process info if successful, raises error if command failed\n    # \n    def run(command, **keyword_arguments)\n        if command.is_a?(String)\n            # by default return a string with stderr included\n            begin\n                command_info = IO.popen(command, err: [:child, :out])\n                Process.wait(command_info.pid)\n                process_info = $?\n                result = CommandResult.new(command_info, process_info)\n                if keyword_arguments[:silent] != true\n                    puts result.read\n                end\n            rescue\n                process_info = $?\n                result = CommandResult.new(nil, process_info)\n            end\n            # if ended with error\n            if !process_info.success?\n                # then raise an error\n                raise CommandResult::Error.new(<<-HEREDOC.remove_indent, result)\n                    \n                    \n                    From run(command)\n                    The command: \#{command.color_as :code}\n                    Failed with a exitcode of: \#{process_info.exitstatus}\n                    \n                    \#{\"This likely means the command could not be found\" if process_info.exitstatus == 127}\n                    \#{\"Hopefully there is additional error info above\" if process_info.exitstatus != 127}\n                HEREDOC\n            end\n            return result\n        else\n            raise <<-HEREDOC.remove_indent\n                \n                \n                The argument for run() must be a string\n                this restriction will be lifted in the future\n            HEREDOC\n        end\n    end\n    \n    def path_for(name_of_executable)\n        return OS.path_for_executable(name_of_executable)\n    end\n    \n    def has_command(name_of_executable)\n        return OS.has_command(name_of_executable)\n    end\n    alias :has_command? :has_command\n    \n    def as_shell_argument(argument)\n        argument = argument.to_s\n        if OS.is?(:unix)\n            # use single quotes to perfectly escape any string\n            return \" '\"+argument.gsub(/'/, \"'\\\"'\\\"'\")+\"'\"\n        else\n            # *sigh* Windows\n            # this problem is unsovleable\n            # see: https://superuser.com/questions/182454/using-backslash-to-escape-characters-in-cmd-exe-runas-command-as-example\n            #       \"The fact is, there's nothing that will escape \" within quotes for argument passing. \n            #        You can brood over this for a couple of years and arrive at no solution. \n            #        This is just some of the inherent limitations of cmd scripting.\n            #        However, the good news is that you'll most likely never come across a situation whereby you need to do so.\n            #        Sure, there's no way to get echo \"\"\" & echo 1 to work, but that's not such a big deal because it's simply\n            #        a contrived problem which you'll likely never encounter.\n            #        For example, consider runas. It works fine without needing to escape \" within quotes\n            #        because runas knew that there's no way to do so and made internal adjustments to work around it.\n            #        runas invented its own parsing rules (runas /flag \"anything even including quotes\") and does not\n            #        interpret cmd arguments the usual way.\n            #        Official documentation for these special syntax is pretty sparse (or non-existent).\n            #        Aside from /? and help, it's mostly trial-and-error.\"\n            # \n            \n            \n            # according to Microsoft see: https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way\n            # the best possible (but still broken) implementation is to quote things \n            # in accordance with their default C++ argument parser\n            # so thats what this function does\n            \n            # users are going to have to manually escape things like ^, !, % etc depending on the context they're used in\n            \n            simple_char = \"[a-zA-Z0-9_.,;`=\\\\-*?\\\\/\\\\[\\\\]]\"\n            \n            # if its a simple argument just pass it on\n            if argument =~ /\\A(\#{simple_char})*\\z/\n                return \" \#{argument}\"\n            # if it is complicated, then quote it and escape quotes\n            else\n                # find any backslashes that come before a double quote or the ending of the argument\n                # then double the number of slashes\n                escaped = argument.gsub(/(\\/+)(?=\"|\\z)/) do |each_match|\n                    \"\\/\" * ($1.size * 2)\n                end\n                \n                # then find all the double quotes and escape them\n                escaped.gsub!(/\"/, '\\\\\"')\n                \n                # all of the remaining escapes are up to Windows user's/devs\n\n                return \" \\\"\#{escaped}\\\"\"\n            end\n        end\n    end\n    \n    def make_arguments_appendable(arguments)\n        safe_arguments = arguments.map do |each|\n            Console.as_shell_argument(each)\n        end\n        return safe_arguments.join('')\n    end\n    \n    # returns the locations where commands are stored from highest to lowest priority\n    def command_sources()\n        if OS.is?('unix')\n            return ENV['PATH'].split(':')\n        else\n            return ENV['PATH'].split(';')\n        end\n    end\n    \n    def require_superuser()\n        if OS.is?('unix')\n            system(\"sudo echo 'permissions acquired'\")\n        else\n            # check if already admin\n            # $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())\n            # $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)\n            # FUTURE: add a check here and raise an error if not admin\n            puts \"(in the future this will be an automatic check)\"\n            puts \"(if you're unsure, then the answer is probably no)\"\n            if Console.yes?(\"Are you running this \\\"as an Administrator\\\"?\\n(caution: incorrectly saying 'yes' can cause broken systems)\")\n                puts \"assuming permissions are acquired\"\n            else\n                puts <<-HEREDOC.remove_indent\n                    \n                    You'll need to \n                    - close the current program\n                    - reopen it \"as Administrator\"\n                    - redo whatever steps you did to get here\n                    \n                HEREDOC\n                Console.keypress(\"Press enter to end the current process\", keys: [:return])\n                exit\n            end\n        end\n    end\n    \n    def set_command(name, code)\n        require_relative './file_system'\n        require_relative './os'\n        require_relative './atk_info'\n        if OS.is?(\"unix\")\n            exec_path = \"\#{Atk.paths[:commands]}/\#{name}\"\n            local_place = Atk.temp_path(name)\n            # add the hash bang\n            hash_bang = \"#!\#{Atk.paths[:ruby]}\\n\"\n            # create the file\n            FS.write(hash_bang+code, to: local_place)\n            # copy to command folder\n            system(\"sudo\", \"cp\", local_place, exec_path)\n            system(\"sudo\", \"chmod\", \"ugo+x\", exec_path)\n        elsif OS.is?(\"windows\")\n            # check for invalid file paths, see https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file\n            if name =~ /[><:\"\\/\\\\|?*]/\n                raise <<-HEREDOC.remove_indent\n                    \n                    \n                    When using the ATK Console.set_command(name)\n                    The name: \#{name}\n                    is not a valid file path on windows\n                    which means it cannot be a command\n                HEREDOC\n            end\n            exec_path = \"\#{Atk.paths[:commands]}\\\\\#{name}\"\n            \n            # create the code\n            IO.write(exec_path+\".rb\", code)\n            # create an executable to call the code\n            IO.write(exec_path+\".bat\", \"@echo off\\nruby \\\"\#{exec_path}.rb\\\" %*\")\n        end\n    end\nend.new\n")
VERSION_OF_RUBY =
Version.new(RUBY_VERSION)
ATK =
Atk
HOME =

windows

"C:"+`echo %HOMEPATH%`.chomp
FS =

create an FS singleton_class.send(:alias_method, :FS = :FileSystem)

FileSystem

Instance Method Summary collapse

Instance Method Details

#log(*args) ⇒ Object



347
348
349
350
351
# File 'lib/atk/console.rb', line 347

def log(*args)
    if Console.verbose
        puts(*args)
    end
end

#register_tag(tag_name, class_value) ⇒ Object

Create loaders for ruby code literal and console code literal



38
39
40
41
# File 'lib/atk/info.rb', line 38

def register_tag(tag_name, class_value)
    YAML.add_tag(tag_name, class_value)
    Code.tags[tag_name] = class_value
end