upm 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
 - data/.gemspec +9 -18
 - data/.gitignore +1 -0
 - data/README.md +77 -43
 - data/Rakefile +1 -1
 - data/TODO.md +38 -0
 - data/VERSION +1 -1
 - data/bin/upm +14 -0
 - data/lib/upm/colored.rb +320 -0
 - data/lib/upm/core_ext.rb +131 -0
 - data/lib/upm/lesspipe.rb +57 -0
 - data/lib/upm/log_parser.rb +56 -0
 - data/lib/upm/tool.rb +180 -0
 - data/lib/upm/tools/apt.rb +68 -0
 - data/lib/upm/tools/pacman.rb +105 -0
 - data/lib/upm/tools/xbps.rb +20 -0
 - data/lib/upm.rb +12 -0
 - metadata +17 -5
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 4da92ce63688e75043c91e8d40bf9a1829687b974a4bc21bc40e23c06ce108f9
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 0eb4815df308f4dfaffdcc9fee5a877c65dda0d65f85ed3149fdb338bc34517b
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: d8ca05eff0cd5037aa9a30b9206d79933fdd7fe6bc8a7d225185278c0696965416ba5d0315fec9cc25a1ecb1ed566ed80704b213c9fee1e37b515596a1d50dc0
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 7a0341ee388207404b5a785d7d27fd5a563f886a8638165539f1a790520e9c097b8d33bbbd2f79d046183a6515e73d5d1cb08ab44f0e3a0d33fb7d4e58f6df94
         
     | 
    
        data/.gemspec
    CHANGED
    
    | 
         @@ -2,26 +2,17 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
            # -*- coding: utf-8 -*-
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            Gem::Specification.new do |s|
         
     | 
| 
       5 
     | 
    
         
            -
              s.name 
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
              s. 
     | 
| 
       8 
     | 
    
         
            -
              s. 
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
              s.summary = "Universal Package Manager"
         
     | 
| 
      
 5 
     | 
    
         
            +
              s.name        = "upm"
         
     | 
| 
      
 6 
     | 
    
         
            +
              s.version     = File.read "VERSION"
         
     | 
| 
      
 7 
     | 
    
         
            +
              s.date        = File.mtime("VERSION").strftime("%Y-%m-%d")
         
     | 
| 
      
 8 
     | 
    
         
            +
              s.summary     = "Universal Package Manager"
         
     | 
| 
       11 
9 
     | 
    
         
             
              s.description = "Wrap all known command-line package tools with a consistent and pretty interface."
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
              s. 
     | 
| 
       14 
     | 
    
         
            -
              s. 
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
              s. 
     | 
| 
       17 
     | 
    
         
            -
              s.authors = ["epitron"]
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
              # s.executables = ["upm", "up", "u"]
         
     | 
| 
      
 10 
     | 
    
         
            +
              s.homepage    = "http://github.com/epitron/upm/"
         
     | 
| 
      
 11 
     | 
    
         
            +
              s.licenses    = ["WTFPL"]
         
     | 
| 
      
 12 
     | 
    
         
            +
              s.email       = "chris@ill-logic.com"
         
     | 
| 
      
 13 
     | 
    
         
            +
              s.authors     = ["epitron"]
         
     | 
| 
      
 14 
     | 
    
         
            +
              s.executables = ["upm"]
         
     | 
| 
       20 
15 
     | 
    
         | 
| 
       21 
16 
     | 
    
         
             
              s.files = `git ls`.lines.map(&:strip)
         
     | 
| 
       22 
17 
     | 
    
         
             
              s.extra_rdoc_files = ["README.md", "LICENSE"]
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
              # s.require_paths = %w[lib]
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
              # s.add_dependency "slop", "~> 3.0"
         
     | 
| 
       27 
18 
     | 
    
         
             
            end
         
     | 
    
        data/.gitignore
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            *.gem
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,5 +1,11 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # upm: Universal Package Manager
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## Concept:
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Wraps all known package managers to provide a consistent and pretty interface, along with advanced features not supported by all tools, such as rollback and pinning.
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            All tools will give you modern, pretty, colourful, piped-to-less output, and you'll only have to remember one consistent set of commands. It'll also prompt you with a text UI whenever faced with ambiguity.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
       3 
9 
     | 
    
         
             
            ## Usage:
         
     | 
| 
       4 
10 
     | 
    
         | 
| 
       5 
11 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -8,35 +14,36 @@ up <command> <pkg> 
     | 
|
| 
       8 
14 
     | 
    
         
             
            u <command> <pkg>
         
     | 
| 
       9 
15 
     | 
    
         
             
            ```
         
     | 
| 
       10 
16 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
            ## Commands
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
            *  
     | 
| 
       14 
     | 
    
         
            -
            *  
     | 
| 
       15 
     | 
    
         
            -
            * install
         
     | 
| 
       16 
     | 
    
         
            -
            *  
     | 
| 
       17 
     | 
    
         
            -
            *  
     | 
| 
       18 
     | 
    
         
            -
            *  
     | 
| 
       19 
     | 
    
         
            -
            *  
     | 
| 
       20 
     | 
    
         
            -
            *  
     | 
| 
       21 
     | 
    
         
            -
            *  
     | 
| 
       22 
     | 
    
         
            -
            *  
     | 
| 
       23 
     | 
    
         
            -
            *  
     | 
| 
       24 
     | 
    
         
            -
            *  
     | 
| 
       25 
     | 
    
         
            -
            *  
     | 
| 
       26 
     | 
    
         
            -
            *  
     | 
| 
       27 
     | 
    
         
            -
            *  
     | 
| 
       28 
     | 
    
         
            -
            *  
     | 
| 
       29 
     | 
    
         
            -
            *  
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
      
 17 
     | 
    
         
            +
            ## Commands:
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            * `install`
         
     | 
| 
      
 20 
     | 
    
         
            +
            * `remove`
         
     | 
| 
      
 21 
     | 
    
         
            +
            * `build` - compile a package from source and install it
         
     | 
| 
      
 22 
     | 
    
         
            +
            * `search` - using the fastest known API or service
         
     | 
| 
      
 23 
     | 
    
         
            +
            * `list` - show all packages, or the contents of a specific package
         
     | 
| 
      
 24 
     | 
    
         
            +
            * `info` - show metadata about a package
         
     | 
| 
      
 25 
     | 
    
         
            +
            * `sync`/`update` - retrieve the latest package list or manifest
         
     | 
| 
      
 26 
     | 
    
         
            +
            * `upgrade` - install new versions of all packages
         
     | 
| 
      
 27 
     | 
    
         
            +
            * `pin` - pinning a package means it won't be automatically upgraded
         
     | 
| 
      
 28 
     | 
    
         
            +
            * `rollback` - revert to an earlier version of a package (including its dependencies)
         
     | 
| 
      
 29 
     | 
    
         
            +
            * `log` - show history of package installs 
         
     | 
| 
      
 30 
     | 
    
         
            +
            * `packagers` - detect installed package managers, and pick which ones upm should wrap
         
     | 
| 
      
 31 
     | 
    
         
            +
            * `sources`/`mirrors` - select remote repositories and mirrors
         
     | 
| 
      
 32 
     | 
    
         
            +
            * `verfiy` - verifies the integrity of installed files
         
     | 
| 
      
 33 
     | 
    
         
            +
            * `clean` - clear out the local package cache
         
     | 
| 
      
 34 
     | 
    
         
            +
            * `monitor` - ad-hoc package manager for custom installations (like instmon)
         
     | 
| 
      
 35 
     | 
    
         
            +
            * `keys` - keyrings and package authentication
         
     | 
| 
      
 36 
     | 
    
         
            +
            * `default` - configure the action to take when no arguments are passed to "upm" (defaults to "os:update")
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            ### Any command that takes a package name can be prefixed with the package tool's namespace:
         
     | 
| 
       32 
39 
     | 
    
         | 
| 
       33 
40 
     | 
    
         
             
            ```
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
            deb:<pkg> (or d:)
         
     | 
| 
       36 
     | 
    
         
            -
            rpm:<pkg>
         
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
      
 41 
     | 
    
         
            +
            os:<pkg> -- automatically select the package manager for the current unix distribution
         
     | 
| 
      
 42 
     | 
    
         
            +
            deb:<pkg> (or d: u:)
         
     | 
| 
      
 43 
     | 
    
         
            +
            rpm:<pkg> (or yum: y:)
         
     | 
| 
      
 44 
     | 
    
         
            +
            bsd:<pkg> (or b:)
         
     | 
| 
      
 45 
     | 
    
         
            +
            ruby:<pkg> (or r: gem:)
         
     | 
| 
      
 46 
     | 
    
         
            +
            python:<pkg>,<pkg> (or py: p: pip:)
         
     | 
| 
       40 
47 
     | 
    
         
             
            ```
         
     | 
| 
       41 
48 
     | 
    
         | 
| 
       42 
49 
     | 
    
         
             
            ### ...or suffixed with its file extension:
         
     | 
| 
         @@ -48,25 +55,52 @@ pip:<pkg> 
     | 
|
| 
       48 
55 
     | 
    
         
             
            <pkg>.pip
         
     | 
| 
       49 
56 
     | 
    
         
             
            ```
         
     | 
| 
       50 
57 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
            ##  
     | 
| 
      
 58 
     | 
    
         
            +
            ## Package tools to wrap:
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            * Arch: `pacman`/`aur`/`abs` (svn mirror)
         
     | 
| 
      
 61 
     | 
    
         
            +
            * Debian/Ubuntu: `apt-get`/`dpkg` (+ curated list of ppa's)
         
     | 
| 
      
 62 
     | 
    
         
            +
            * RedHat/Fedora/Centos: `yum`/`rpm`
         
     | 
| 
      
 63 
     | 
    
         
            +
            * Mac OSX: `brew`/`fink`/`ports`
         
     | 
| 
      
 64 
     | 
    
         
            +
            * FreeBSD: `pkg`/`ports`
         
     | 
| 
      
 65 
     | 
    
         
            +
            * OpenBSD: `pkg_add`/`ports`
         
     | 
| 
      
 66 
     | 
    
         
            +
            * NetBSD: `pkgin`/`ports`
         
     | 
| 
      
 67 
     | 
    
         
            +
            * SmartOS/Illumos: `pkgin`
         
     | 
| 
      
 68 
     | 
    
         
            +
            * Windows: `apt-cyg`/`mingw-get`/`nuget`/`Windows Update`/(as-yet-not-created package manager, "winget")
         
     | 
| 
      
 69 
     | 
    
         
            +
            * Wine: `winetricks`
         
     | 
| 
      
 70 
     | 
    
         
            +
            * Ruby: `rubygems`
         
     | 
| 
      
 71 
     | 
    
         
            +
            * Python: `pip`/`easy_install`
         
     | 
| 
      
 72 
     | 
    
         
            +
            * Javascript: `npm`
         
     | 
| 
      
 73 
     | 
    
         
            +
            * Clojure: `leiningen`
         
     | 
| 
      
 74 
     | 
    
         
            +
            * Java: `gradle`
         
     | 
| 
      
 75 
     | 
    
         
            +
            * Erlang: `rebar`
         
     | 
| 
      
 76 
     | 
    
         
            +
            * Scala: `sbt`
         
     | 
| 
      
 77 
     | 
    
         
            +
            * Rust: `cargo`
         
     | 
| 
      
 78 
     | 
    
         
            +
            * R: `cran`
         
     | 
| 
      
 79 
     | 
    
         
            +
            * Lua: `rocks`
         
     | 
| 
      
 80 
     | 
    
         
            +
            * Julia: `Pkg`
         
     | 
| 
      
 81 
     | 
    
         
            +
            * Haskell: `cabal`
         
     | 
| 
      
 82 
     | 
    
         
            +
            * Perl: `cpan`
         
     | 
| 
      
 83 
     | 
    
         
            +
            * go: `go-get`
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
            ...[and many more!](https://en.wikipedia.org/wiki/List_of_software_package_management_systems)
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            ## What it might look like:
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
            Info:
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
            
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
            Log:
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
            
         
     | 
| 
       52 
97 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
            * FreeBSD: pkg
         
     | 
| 
       55 
     | 
    
         
            -
            * RedHat/Fedora/Centos: yum/rpm
         
     | 
| 
       56 
     | 
    
         
            -
            * Debian/Ubuntu: apt-get/dpkg (+ curated list of ppa's)
         
     | 
| 
       57 
     | 
    
         
            -
            * Windows: apt-cyg/nuget/"winget" (new package manager)
         
     | 
| 
       58 
     | 
    
         
            -
            * Arch: pacman/aur/abs (svn mirror)
         
     | 
| 
       59 
     | 
    
         
            -
            * Mac OSX: brew/fink
         
     | 
| 
       60 
     | 
    
         
            -
            * Python: pip/easy_install
         
     | 
| 
       61 
     | 
    
         
            -
            * Ruby: rubygems
         
     | 
| 
       62 
     | 
    
         
            -
            * Haskell: cabal
         
     | 
| 
       63 
     | 
    
         
            -
            * Perl: cpan
         
     | 
| 
       64 
     | 
    
         
            -
            * go: go-get
         
     | 
| 
       65 
     | 
    
         
            -
            * Java: ?
         
     | 
| 
      
 98 
     | 
    
         
            +
            Rollback:
         
     | 
| 
       66 
99 
     | 
    
         | 
| 
      
 100 
     | 
    
         
            +
            
         
     | 
| 
       67 
101 
     | 
    
         | 
| 
       68 
102 
     | 
    
         
             
            ## TODOs:
         
     | 
| 
       69 
103 
     | 
    
         | 
| 
       70 
     | 
    
         
            -
            *  
     | 
| 
      
 104 
     | 
    
         
            +
            * Use the pretty text-mode UI that passenger-install uses
         
     | 
| 
       71 
105 
     | 
    
         
             
            * Context-dependent operation
         
     | 
| 
       72 
     | 
    
         
            -
              * if you're in a ruby project,  
     | 
| 
      
 106 
     | 
    
         
            +
              * eg: if you're in a ruby project's directory, set the 'ruby' namespace to highest priority
         
     | 
    
        data/Rakefile
    CHANGED
    
    
    
        data/TODO.md
    ADDED
    
    | 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # TODO
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            ## More package managers
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Currently missing:
         
     | 
| 
      
 6 
     | 
    
         
            +
            * RedHat/Fedora/CentOS
         
     | 
| 
      
 7 
     | 
    
         
            +
            * OSX
         
     | 
| 
      
 8 
     | 
    
         
            +
            * FreeBSD
         
     | 
| 
      
 9 
     | 
    
         
            +
            * OpenBSD
         
     | 
| 
      
 10 
     | 
    
         
            +
            * SuSE
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            ## fzf
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            Use fzf for "list" output (or other commands that require selecting, like "remove")
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            ## Proper ARGV parser
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            * Something better than "command, *args = ARGV" (with "--help" available, at the very least.)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            ## Streaming pipes
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            * Make the `run` command able to grep the output while streaming the results to the screen.
         
     | 
| 
      
 23 
     | 
    
         
            +
            * Make run pretend to be a tty, so I don't need `--color=always`.
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            ## Figure out how to integrate language package managers
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            * The packages that you can get through gem/pip/luarocks/etc. are often duplicated in the OS-level package managers. Should there be a preference?
         
     | 
| 
      
 28 
     | 
    
         
            +
            * Should the search command show matches from all available package tools? (There could be a configure step where the user says which package managers should be included, and which have preference)
         
     | 
| 
      
 29 
     | 
    
         
            +
            * Possibilites: 
         
     | 
| 
      
 30 
     | 
    
         
            +
                * upm install --ruby <pkg>
         
     | 
| 
      
 31 
     | 
    
         
            +
                * upm install ruby:<pkg>,<pkg>
         
     | 
| 
      
 32 
     | 
    
         
            +
            * Add detectors for language-specific package-managers
         
     | 
| 
      
 33 
     | 
    
         
            +
            * Help screen needs to display language-specific package managers
         
     | 
| 
      
 34 
     | 
    
         
            +
            * `upm help --ruby` should show available ruby commands
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            ## Mirror Selector
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            * Do a ping test on available mirrors, and use fzf to select
         
     | 
    
        data/VERSION
    CHANGED
    
    | 
         @@ -1 +1 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            0. 
     | 
| 
      
 1 
     | 
    
         
            +
            0.1.0
         
     | 
    
        data/bin/upm
    ADDED
    
    | 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            bin_dir = File.dirname(File.realpath(__FILE__))
         
     | 
| 
      
 3 
     | 
    
         
            +
            $LOAD_PATH.unshift(File.expand_path(File.join('..', 'lib'), bin_dir))
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'upm'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            tool = UPM::Tool.for_os
         
     | 
| 
      
 8 
     | 
    
         
            +
            command, *args = ARGV
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            if command.nil?
         
     | 
| 
      
 11 
     | 
    
         
            +
              tool.help
         
     | 
| 
      
 12 
     | 
    
         
            +
            else
         
     | 
| 
      
 13 
     | 
    
         
            +
              tool.call_command command, args
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/upm/colored.rb
    ADDED
    
    | 
         @@ -0,0 +1,320 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #
         
     | 
| 
      
 2 
     | 
    
         
            +
            # ANSI Colour-coding (for terminals that support it.)
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            # Originally by defunkt (Chris Wanstrath)
         
     | 
| 
      
 5 
     | 
    
         
            +
            #   Enhanced by epitron (Chris Gahan)
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
      
 7 
     | 
    
         
            +
            # It adds methods to String to allow easy coloring.
         
     | 
| 
      
 8 
     | 
    
         
            +
            #
         
     | 
| 
      
 9 
     | 
    
         
            +
            # (Note: Colors are automatically disabled if your program is piped to another program,
         
     | 
| 
      
 10 
     | 
    
         
            +
            #        ie: if STDOUT is not a TTY)
         
     | 
| 
      
 11 
     | 
    
         
            +
            #
         
     | 
| 
      
 12 
     | 
    
         
            +
            # Basic examples:
         
     | 
| 
      
 13 
     | 
    
         
            +
            #
         
     | 
| 
      
 14 
     | 
    
         
            +
            #   >> "this is red".red
         
     | 
| 
      
 15 
     | 
    
         
            +
            #   >> "this is red with a blue background (read: ugly)".red_on_blue
         
     | 
| 
      
 16 
     | 
    
         
            +
            #   >> "this is light blue".light_blue
         
     | 
| 
      
 17 
     | 
    
         
            +
            #   >> "this is red with an underline".red.underline
         
     | 
| 
      
 18 
     | 
    
         
            +
            #   >> "this is really bold and really blue".bold.blue
         
     | 
| 
      
 19 
     | 
    
         
            +
            #
         
     | 
| 
      
 20 
     | 
    
         
            +
            # Color tags:
         
     | 
| 
      
 21 
     | 
    
         
            +
            # (Note: You don't *need* to close color tags, but you can!)
         
     | 
| 
      
 22 
     | 
    
         
            +
            #
         
     | 
| 
      
 23 
     | 
    
         
            +
            #   >> "<yellow>This is using <green>color tags</green> to colorize.".colorize
         
     | 
| 
      
 24 
     | 
    
         
            +
            #   >> "<1>N<9>u<11>m<15>eric tags!".colorize
         
     | 
| 
      
 25 
     | 
    
         
            +
            #   (For those who still remember the DOS color palette and want more terse tagged-colors.)
         
     | 
| 
      
 26 
     | 
    
         
            +
            #
         
     | 
| 
      
 27 
     | 
    
         
            +
            # Highlight search results:
         
     | 
| 
      
 28 
     | 
    
         
            +
            #
         
     | 
| 
      
 29 
     | 
    
         
            +
            #   >> string.gsub(pattern) { |match| "<yellow>#{match}</yellow>" }.colorize
         
     | 
| 
      
 30 
     | 
    
         
            +
            #
         
     | 
| 
      
 31 
     | 
    
         
            +
            # Forcing colors:
         
     | 
| 
      
 32 
     | 
    
         
            +
            #
         
     | 
| 
      
 33 
     | 
    
         
            +
            # Since the presence of a terminal is detected automatically, the colors will be
         
     | 
| 
      
 34 
     | 
    
         
            +
            # disabled when you pipe your program to another program. However, if you want to
         
     | 
| 
      
 35 
     | 
    
         
            +
            # show colors when piped (eg: when you pipe to `less -R`), you can force it:
         
     | 
| 
      
 36 
     | 
    
         
            +
            #
         
     | 
| 
      
 37 
     | 
    
         
            +
            #   >> Colored.enable!
         
     | 
| 
      
 38 
     | 
    
         
            +
            #   >> Colored.disable!
         
     | 
| 
      
 39 
     | 
    
         
            +
            #   >> Colored.enable_temporarily { puts "whee!".red }
         
     | 
| 
      
 40 
     | 
    
         
            +
            #
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            require 'set'
         
     | 
| 
      
 43 
     | 
    
         
            +
            require 'rbconfig'
         
     | 
| 
      
 44 
     | 
    
         
            +
            require 'Win32/Console/ANSI' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
         
     | 
| 
      
 45 
     | 
    
         
            +
            #require 'Win32/Console/ANSI' if RUBY_PLATFORM =~ /win32/
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
            module Colored
         
     | 
| 
      
 48 
     | 
    
         
            +
              extend self
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
              @@is_tty = STDOUT.isatty
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
              COLORS = {
         
     | 
| 
      
 53 
     | 
    
         
            +
                'black'   => 30,
         
     | 
| 
      
 54 
     | 
    
         
            +
                'red'     => 31,
         
     | 
| 
      
 55 
     | 
    
         
            +
                'green'   => 32,
         
     | 
| 
      
 56 
     | 
    
         
            +
                'yellow'  => 33,
         
     | 
| 
      
 57 
     | 
    
         
            +
                'blue'    => 34,
         
     | 
| 
      
 58 
     | 
    
         
            +
                'magenta' => 35,
         
     | 
| 
      
 59 
     | 
    
         
            +
                'purple'  => 35,
         
     | 
| 
      
 60 
     | 
    
         
            +
                'cyan'    => 36,
         
     | 
| 
      
 61 
     | 
    
         
            +
                'white'   => 37
         
     | 
| 
      
 62 
     | 
    
         
            +
              }
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
              EXTRAS = {
         
     | 
| 
      
 65 
     | 
    
         
            +
                'clear'     => 0,
         
     | 
| 
      
 66 
     | 
    
         
            +
                'bold'      => 1,
         
     | 
| 
      
 67 
     | 
    
         
            +
                'light'     => 1,
         
     | 
| 
      
 68 
     | 
    
         
            +
                'underline' => 4,
         
     | 
| 
      
 69 
     | 
    
         
            +
                'reversed'  => 7
         
     | 
| 
      
 70 
     | 
    
         
            +
              }
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
              #
         
     | 
| 
      
 73 
     | 
    
         
            +
              # BBS-style numeric color codes.
         
     | 
| 
      
 74 
     | 
    
         
            +
              #
         
     | 
| 
      
 75 
     | 
    
         
            +
              BBS_COLOR_TABLE = {
         
     | 
| 
      
 76 
     | 
    
         
            +
                0   => :black,
         
     | 
| 
      
 77 
     | 
    
         
            +
                1   => :blue,
         
     | 
| 
      
 78 
     | 
    
         
            +
                2   => :green,
         
     | 
| 
      
 79 
     | 
    
         
            +
                3   => :cyan,
         
     | 
| 
      
 80 
     | 
    
         
            +
                4   => :red,
         
     | 
| 
      
 81 
     | 
    
         
            +
                5   => :magenta,
         
     | 
| 
      
 82 
     | 
    
         
            +
                6   => :yellow,
         
     | 
| 
      
 83 
     | 
    
         
            +
                7   => :white,
         
     | 
| 
      
 84 
     | 
    
         
            +
                8   => :light_black,
         
     | 
| 
      
 85 
     | 
    
         
            +
                9   => :light_blue,
         
     | 
| 
      
 86 
     | 
    
         
            +
                10  => :light_green,
         
     | 
| 
      
 87 
     | 
    
         
            +
                11  => :light_cyan,
         
     | 
| 
      
 88 
     | 
    
         
            +
                12  => :light_red,
         
     | 
| 
      
 89 
     | 
    
         
            +
                13  => :light_magenta,
         
     | 
| 
      
 90 
     | 
    
         
            +
                14  => :light_yellow,
         
     | 
| 
      
 91 
     | 
    
         
            +
                15  => :light_white,
         
     | 
| 
      
 92 
     | 
    
         
            +
              }
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
              VALID_COLORS = begin
         
     | 
| 
      
 95 
     | 
    
         
            +
                normal         = COLORS.keys
         
     | 
| 
      
 96 
     | 
    
         
            +
                lights         = normal.map { |fore| "light_#{fore}" }
         
     | 
| 
      
 97 
     | 
    
         
            +
                brights        = normal.map { |fore| "bright_#{fore}" }
         
     | 
| 
      
 98 
     | 
    
         
            +
                on_backgrounds = normal.map { |fore| normal.map { |back| "#{fore}_on_#{back}" } }.flatten
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                Set.new(normal + lights + brights + on_backgrounds + ["grey", "gray"])
         
     | 
| 
      
 101 
     | 
    
         
            +
              end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
              COLORS.each do |color, value|
         
     | 
| 
      
 104 
     | 
    
         
            +
                define_method(color) do
         
     | 
| 
      
 105 
     | 
    
         
            +
                  colorize(self, :foreground => color)
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                define_method("on_#{color}") do
         
     | 
| 
      
 109 
     | 
    
         
            +
                  colorize(self, :background => color)
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                define_method("light_#{color}") do
         
     | 
| 
      
 113 
     | 
    
         
            +
                  colorize(self, :foreground => color, :extra => 'bold')
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                define_method("bright_#{color}") do
         
     | 
| 
      
 117 
     | 
    
         
            +
                  colorize(self, :foreground => color, :extra => 'bold')
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                COLORS.each do |highlight, value|
         
     | 
| 
      
 121 
     | 
    
         
            +
                  next if color == highlight
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                  define_method("#{color}_on_#{highlight}") do
         
     | 
| 
      
 124 
     | 
    
         
            +
                    colorize(self, :foreground => color, :background => highlight)
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  define_method("light_#{color}_on_#{highlight}") do
         
     | 
| 
      
 128 
     | 
    
         
            +
                    colorize(self, :foreground => color, :background => highlight, :extra => 'bold')
         
     | 
| 
      
 129 
     | 
    
         
            +
                  end
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                end
         
     | 
| 
      
 132 
     | 
    
         
            +
              end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
              alias_method :gray, :light_black
         
     | 
| 
      
 135 
     | 
    
         
            +
              alias_method :grey, :light_black
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
              EXTRAS.each do |extra, value|
         
     | 
| 
      
 138 
     | 
    
         
            +
                next if extra == 'clear'
         
     | 
| 
      
 139 
     | 
    
         
            +
                define_method(extra) do
         
     | 
| 
      
 140 
     | 
    
         
            +
                  colorize(self, :extra => extra)
         
     | 
| 
      
 141 
     | 
    
         
            +
                end
         
     | 
| 
      
 142 
     | 
    
         
            +
              end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
              define_method(:to_eol) do
         
     | 
| 
      
 145 
     | 
    
         
            +
                tmp = sub(/^(\e\[[\[\e0-9;m]+m)/, "\\1\e[2K")
         
     | 
| 
      
 146 
     | 
    
         
            +
                if tmp == self
         
     | 
| 
      
 147 
     | 
    
         
            +
                  return "\e[2K" << self
         
     | 
| 
      
 148 
     | 
    
         
            +
                end
         
     | 
| 
      
 149 
     | 
    
         
            +
                tmp
         
     | 
| 
      
 150 
     | 
    
         
            +
              end
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
              #
         
     | 
| 
      
 153 
     | 
    
         
            +
              # Colorize a string (this method is called by #red, #blue, #red_on_green, etc.)
         
     | 
| 
      
 154 
     | 
    
         
            +
              #
         
     | 
| 
      
 155 
     | 
    
         
            +
              # Accepts options:
         
     | 
| 
      
 156 
     | 
    
         
            +
              #   :foreground
         
     | 
| 
      
 157 
     | 
    
         
            +
              #       The name of the foreground color as a string.
         
     | 
| 
      
 158 
     | 
    
         
            +
              #   :background
         
     | 
| 
      
 159 
     | 
    
         
            +
              #       The name of the background color as a string.
         
     | 
| 
      
 160 
     | 
    
         
            +
              #   :extra
         
     | 
| 
      
 161 
     | 
    
         
            +
              #       Extra styling, like 'bold', 'light', 'underline', 'reversed', or 'clear'.
         
     | 
| 
      
 162 
     | 
    
         
            +
              #
         
     | 
| 
      
 163 
     | 
    
         
            +
              #
         
     | 
| 
      
 164 
     | 
    
         
            +
              # With no options, it uses tagged colors:
         
     | 
| 
      
 165 
     | 
    
         
            +
              #
         
     | 
| 
      
 166 
     | 
    
         
            +
              #    puts "<light_green><magenta>*</magenta> Hey mom! I am <light_blue>SO</light_blue> colored right now.</light_green>".colorize
         
     | 
| 
      
 167 
     | 
    
         
            +
              #
         
     | 
| 
      
 168 
     | 
    
         
            +
              # Or numeric ANSI tagged colors (from the BBS days):
         
     | 
| 
      
 169 
     | 
    
         
            +
              #    puts "<10><5>*</5> Hey mom! I am <9>SO</9> colored right now.</10>".colorize
         
     | 
| 
      
 170 
     | 
    
         
            +
              #
         
     | 
| 
      
 171 
     | 
    
         
            +
              #
         
     | 
| 
      
 172 
     | 
    
         
            +
              def colorize(string=nil, options = {})
         
     | 
| 
      
 173 
     | 
    
         
            +
                if string == nil
         
     | 
| 
      
 174 
     | 
    
         
            +
                  return self.tagged_colors
         
     | 
| 
      
 175 
     | 
    
         
            +
                end
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                if @@is_tty
         
     | 
| 
      
 178 
     | 
    
         
            +
                  colored = [color(options[:foreground]), color("on_#{options[:background]}"), extra(options[:extra])].compact * ''
         
     | 
| 
      
 179 
     | 
    
         
            +
                  colored << string
         
     | 
| 
      
 180 
     | 
    
         
            +
                  colored << extra(:clear)
         
     | 
| 
      
 181 
     | 
    
         
            +
                else
         
     | 
| 
      
 182 
     | 
    
         
            +
                  string
         
     | 
| 
      
 183 
     | 
    
         
            +
                end
         
     | 
| 
      
 184 
     | 
    
         
            +
              end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
              #
         
     | 
| 
      
 187 
     | 
    
         
            +
              # Find all occurrences of "pattern" in the string and highlight them
         
     | 
| 
      
 188 
     | 
    
         
            +
              # with the specified color. (defaults to light_yellow)
         
     | 
| 
      
 189 
     | 
    
         
            +
              #
         
     | 
| 
      
 190 
     | 
    
         
            +
              # The pattern can be a string or a regular expression.
         
     | 
| 
      
 191 
     | 
    
         
            +
              #
         
     | 
| 
      
 192 
     | 
    
         
            +
              def highlight(pattern, color=:light_yellow, &block)
         
     | 
| 
      
 193 
     | 
    
         
            +
                pattern = Regexp.new(Regexp.escape(pattern)) if pattern.is_a? String
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
                if block_given?
         
     | 
| 
      
 196 
     | 
    
         
            +
                  gsub(pattern, &block)
         
     | 
| 
      
 197 
     | 
    
         
            +
                else
         
     | 
| 
      
 198 
     | 
    
         
            +
                  gsub(pattern) { |match| match.send(color) }
         
     | 
| 
      
 199 
     | 
    
         
            +
                end
         
     | 
| 
      
 200 
     | 
    
         
            +
              end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
              #
         
     | 
| 
      
 203 
     | 
    
         
            +
              # An array of all possible colors.
         
     | 
| 
      
 204 
     | 
    
         
            +
              #
         
     | 
| 
      
 205 
     | 
    
         
            +
              def colors
         
     | 
| 
      
 206 
     | 
    
         
            +
                @@colors ||= COLORS.keys.sort
         
     | 
| 
      
 207 
     | 
    
         
            +
              end
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
              #
         
     | 
| 
      
 210 
     | 
    
         
            +
              # Returns the terminal code for one of the extra styling options.
         
     | 
| 
      
 211 
     | 
    
         
            +
              #
         
     | 
| 
      
 212 
     | 
    
         
            +
              def extra(extra_name)
         
     | 
| 
      
 213 
     | 
    
         
            +
                extra_name = extra_name.to_s
         
     | 
| 
      
 214 
     | 
    
         
            +
                "\e[#{EXTRAS[extra_name]}m" if EXTRAS[extra_name]
         
     | 
| 
      
 215 
     | 
    
         
            +
              end
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
              #
         
     | 
| 
      
 218 
     | 
    
         
            +
              # Returns the terminal code for a specified color.
         
     | 
| 
      
 219 
     | 
    
         
            +
              #
         
     | 
| 
      
 220 
     | 
    
         
            +
              def color(color_name)
         
     | 
| 
      
 221 
     | 
    
         
            +
                background = color_name.to_s =~ /on_/
         
     | 
| 
      
 222 
     | 
    
         
            +
                color_name = color_name.to_s.sub('on_', '')
         
     | 
| 
      
 223 
     | 
    
         
            +
                return unless color_name && COLORS[color_name]
         
     | 
| 
      
 224 
     | 
    
         
            +
                "\e[#{COLORS[color_name] + (background ? 10 : 0)}m"
         
     | 
| 
      
 225 
     | 
    
         
            +
              end
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
              #
         
     | 
| 
      
 228 
     | 
    
         
            +
              # Will color commands actually modify the strings?
         
     | 
| 
      
 229 
     | 
    
         
            +
              #
         
     | 
| 
      
 230 
     | 
    
         
            +
              def enabled?
         
     | 
| 
      
 231 
     | 
    
         
            +
                @@is_tty
         
     | 
| 
      
 232 
     | 
    
         
            +
              end
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
              alias_method :is_tty?, :enabled?
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
              #
         
     | 
| 
      
 237 
     | 
    
         
            +
              # Color commands will always produce colored strings, regardless
         
     | 
| 
      
 238 
     | 
    
         
            +
              # of whether the script is being run in a terminal.
         
     | 
| 
      
 239 
     | 
    
         
            +
              #
         
     | 
| 
      
 240 
     | 
    
         
            +
              def enable!
         
     | 
| 
      
 241 
     | 
    
         
            +
                @@is_tty = true
         
     | 
| 
      
 242 
     | 
    
         
            +
              end
         
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
      
 244 
     | 
    
         
            +
              alias_method :force!, :enable!
         
     | 
| 
      
 245 
     | 
    
         
            +
             
     | 
| 
      
 246 
     | 
    
         
            +
              #
         
     | 
| 
      
 247 
     | 
    
         
            +
              # Enable Colored just for this block.
         
     | 
| 
      
 248 
     | 
    
         
            +
              #
         
     | 
| 
      
 249 
     | 
    
         
            +
              def enable_temporarily(&block)
         
     | 
| 
      
 250 
     | 
    
         
            +
                last_state = @@is_tty
         
     | 
| 
      
 251 
     | 
    
         
            +
             
     | 
| 
      
 252 
     | 
    
         
            +
                @@is_tty = true
         
     | 
| 
      
 253 
     | 
    
         
            +
                block.call
         
     | 
| 
      
 254 
     | 
    
         
            +
                @@is_tty = last_state
         
     | 
| 
      
 255 
     | 
    
         
            +
              end
         
     | 
| 
      
 256 
     | 
    
         
            +
             
     | 
| 
      
 257 
     | 
    
         
            +
              #
         
     | 
| 
      
 258 
     | 
    
         
            +
              # Color commands will do nothing.
         
     | 
| 
      
 259 
     | 
    
         
            +
              #
         
     | 
| 
      
 260 
     | 
    
         
            +
              def disable!
         
     | 
| 
      
 261 
     | 
    
         
            +
                @@is_tty = false
         
     | 
| 
      
 262 
     | 
    
         
            +
              end
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
              #
         
     | 
| 
      
 265 
     | 
    
         
            +
              # Is this string legal?
         
     | 
| 
      
 266 
     | 
    
         
            +
              #
         
     | 
| 
      
 267 
     | 
    
         
            +
              def valid_tag?(tag)
         
     | 
| 
      
 268 
     | 
    
         
            +
                VALID_COLORS.include?(tag) or
         
     | 
| 
      
 269 
     | 
    
         
            +
                  (tag =~ /^\d+$/ and BBS_COLOR_TABLE.include?(tag.to_i) )
         
     | 
| 
      
 270 
     | 
    
         
            +
              end
         
     | 
| 
      
 271 
     | 
    
         
            +
             
     | 
| 
      
 272 
     | 
    
         
            +
              #
         
     | 
| 
      
 273 
     | 
    
         
            +
              # Colorize a string that has "color tags".
         
     | 
| 
      
 274 
     | 
    
         
            +
              #
         
     | 
| 
      
 275 
     | 
    
         
            +
              def tagged_colors
         
     | 
| 
      
 276 
     | 
    
         
            +
                stack = []
         
     | 
| 
      
 277 
     | 
    
         
            +
             
     | 
| 
      
 278 
     | 
    
         
            +
                # split the string into tags and literal strings
         
     | 
| 
      
 279 
     | 
    
         
            +
                tokens          = self.split(/(<\/?[\w\d_]+>)/)
         
     | 
| 
      
 280 
     | 
    
         
            +
                tokens.delete_if { |token| token.size == 0 }
         
     | 
| 
      
 281 
     | 
    
         
            +
             
     | 
| 
      
 282 
     | 
    
         
            +
                result        = ""
         
     | 
| 
      
 283 
     | 
    
         
            +
             
     | 
| 
      
 284 
     | 
    
         
            +
                tokens.each do |token|
         
     | 
| 
      
 285 
     | 
    
         
            +
             
     | 
| 
      
 286 
     | 
    
         
            +
                  # token is an opening tag!
         
     | 
| 
      
 287 
     | 
    
         
            +
             
     | 
| 
      
 288 
     | 
    
         
            +
                  if /<([\w\d_]+)>/ =~ token and valid_tag?($1)
         
     | 
| 
      
 289 
     | 
    
         
            +
                    stack.push $1
         
     | 
| 
      
 290 
     | 
    
         
            +
             
     | 
| 
      
 291 
     | 
    
         
            +
                  # token is a closing tag!
         
     | 
| 
      
 292 
     | 
    
         
            +
             
     | 
| 
      
 293 
     | 
    
         
            +
                  elsif /<\/([\w\d_]+)>/ =~ token and valid_tag?($1)
         
     | 
| 
      
 294 
     | 
    
         
            +
             
     | 
| 
      
 295 
     | 
    
         
            +
                    # if this color is on the stack somwehere...
         
     | 
| 
      
 296 
     | 
    
         
            +
                    if pos = stack.rindex($1)
         
     | 
| 
      
 297 
     | 
    
         
            +
                      # close the tag by removing it from the stack
         
     | 
| 
      
 298 
     | 
    
         
            +
                      stack.delete_at pos
         
     | 
| 
      
 299 
     | 
    
         
            +
                    else
         
     | 
| 
      
 300 
     | 
    
         
            +
                      raise "Error: tried to close an unopened color tag -- #{token}"
         
     | 
| 
      
 301 
     | 
    
         
            +
                    end
         
     | 
| 
      
 302 
     | 
    
         
            +
             
     | 
| 
      
 303 
     | 
    
         
            +
                  # token is a literal string!
         
     | 
| 
      
 304 
     | 
    
         
            +
             
     | 
| 
      
 305 
     | 
    
         
            +
                  else
         
     | 
| 
      
 306 
     | 
    
         
            +
             
     | 
| 
      
 307 
     | 
    
         
            +
                    color = (stack.last || "white")
         
     | 
| 
      
 308 
     | 
    
         
            +
                    color = BBS_COLOR_TABLE[color.to_i] if color =~ /^\d+$/
         
     | 
| 
      
 309 
     | 
    
         
            +
                    result << token.send(color)
         
     | 
| 
      
 310 
     | 
    
         
            +
             
     | 
| 
      
 311 
     | 
    
         
            +
                  end
         
     | 
| 
      
 312 
     | 
    
         
            +
             
     | 
| 
      
 313 
     | 
    
         
            +
                end
         
     | 
| 
      
 314 
     | 
    
         
            +
             
     | 
| 
      
 315 
     | 
    
         
            +
                result
         
     | 
| 
      
 316 
     | 
    
         
            +
              end
         
     | 
| 
      
 317 
     | 
    
         
            +
             
     | 
| 
      
 318 
     | 
    
         
            +
            end unless Object.const_defined? :Colored
         
     | 
| 
      
 319 
     | 
    
         
            +
             
     | 
| 
      
 320 
     | 
    
         
            +
            String.send(:include, Colored)
         
     | 
    
        data/lib/upm/core_ext.rb
    ADDED
    
    | 
         @@ -0,0 +1,131 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'date'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class DateTime
         
     | 
| 
      
 4 
     | 
    
         
            +
              def to_i; to_time.to_i; end
         
     | 
| 
      
 5 
     | 
    
         
            +
            end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module Enumerable
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              # Split this enumerable into chunks, given some boundary condition. (Returns an array of arrays.)
         
     | 
| 
      
 10 
     | 
    
         
            +
              #
         
     | 
| 
      
 11 
     | 
    
         
            +
              # Options:
         
     | 
| 
      
 12 
     | 
    
         
            +
              #   :include_boundary => true  #=> include the element that you're splitting at in the results
         
     | 
| 
      
 13 
     | 
    
         
            +
              #                                  (default: false)
         
     | 
| 
      
 14 
     | 
    
         
            +
              #   :after => true             #=> split after the matched element (only has an effect when used with :include_boundary)
         
     | 
| 
      
 15 
     | 
    
         
            +
              #                                  (default: false)
         
     | 
| 
      
 16 
     | 
    
         
            +
              #   :once => flase             #=> only perform one split (default: false)
         
     | 
| 
      
 17 
     | 
    
         
            +
              #
         
     | 
| 
      
 18 
     | 
    
         
            +
              # Examples:
         
     | 
| 
      
 19 
     | 
    
         
            +
              #   [1,2,3,4,5].split{ |e| e == 3 }
         
     | 
| 
      
 20 
     | 
    
         
            +
              #   #=> [ [1,2], [4,5] ]
         
     | 
| 
      
 21 
     | 
    
         
            +
              #
         
     | 
| 
      
 22 
     | 
    
         
            +
              #   "hello\n\nthere\n".each_line.split_at("\n").to_a
         
     | 
| 
      
 23 
     | 
    
         
            +
              #   #=> [ ["hello\n"], ["there\n"] ]
         
     | 
| 
      
 24 
     | 
    
         
            +
              #
         
     | 
| 
      
 25 
     | 
    
         
            +
              #   [1,2,3,4,5].split(:include_boundary=>true) { |e| e == 3 }
         
     | 
| 
      
 26 
     | 
    
         
            +
              #   #=> [ [1,2], [3,4,5] ]
         
     | 
| 
      
 27 
     | 
    
         
            +
              #
         
     | 
| 
      
 28 
     | 
    
         
            +
              #   chapters = File.read("ebook.txt").split(/Chapter \d+/, :include_boundary=>true)
         
     | 
| 
      
 29 
     | 
    
         
            +
              #   #=> [ ["Chapter 1", ...], ["Chapter 2", ...], etc. ]
         
     | 
| 
      
 30 
     | 
    
         
            +
              #
         
     | 
| 
      
 31 
     | 
    
         
            +
              def split_at(matcher=nil, options={}, &block)
         
     | 
| 
      
 32 
     | 
    
         
            +
                include_boundary = options[:include_boundary] || false
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                if matcher.nil?
         
     | 
| 
      
 35 
     | 
    
         
            +
                  boundary_test_proc = block
         
     | 
| 
      
 36 
     | 
    
         
            +
                else
         
     | 
| 
      
 37 
     | 
    
         
            +
                  if matcher.is_a? Regexp
         
     | 
| 
      
 38 
     | 
    
         
            +
                    boundary_test_proc = proc { |element| element =~ matcher }
         
     | 
| 
      
 39 
     | 
    
         
            +
                  else
         
     | 
| 
      
 40 
     | 
    
         
            +
                    boundary_test_proc = proc { |element| element == matcher }
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                Enumerator.new do |yielder|
         
     | 
| 
      
 45 
     | 
    
         
            +
                  current_chunk = []
         
     | 
| 
      
 46 
     | 
    
         
            +
                  splits        = 0
         
     | 
| 
      
 47 
     | 
    
         
            +
                  max_splits    = options[:once] == true ? 1 : options[:max_splits]
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  each do |e|
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    if boundary_test_proc.call(e) and (max_splits == nil or splits < max_splits)
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                      if current_chunk.empty? and not include_boundary
         
     | 
| 
      
 54 
     | 
    
         
            +
                        next # hit 2 boundaries in a row... just keep moving, people!
         
     | 
| 
      
 55 
     | 
    
         
            +
                      end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                      if options[:after]
         
     | 
| 
      
 58 
     | 
    
         
            +
                        # split after boundary
         
     | 
| 
      
 59 
     | 
    
         
            +
                        current_chunk << e        if include_boundary   # include the boundary, if necessary
         
     | 
| 
      
 60 
     | 
    
         
            +
                        yielder << current_chunk                         # shift everything after the boundary into the resultset
         
     | 
| 
      
 61 
     | 
    
         
            +
                        current_chunk = []                              # start a new result
         
     | 
| 
      
 62 
     | 
    
         
            +
                      else
         
     | 
| 
      
 63 
     | 
    
         
            +
                        # split before boundary
         
     | 
| 
      
 64 
     | 
    
         
            +
                        yielder << current_chunk                         # shift before the boundary into the resultset
         
     | 
| 
      
 65 
     | 
    
         
            +
                        current_chunk = []                              # start a new result
         
     | 
| 
      
 66 
     | 
    
         
            +
                        current_chunk << e        if include_boundary   # include the boundary, if necessary
         
     | 
| 
      
 67 
     | 
    
         
            +
                      end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                      splits += 1
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                    else
         
     | 
| 
      
 72 
     | 
    
         
            +
                      current_chunk << e
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  yielder << current_chunk if current_chunk.any?
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
              end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
              #
         
     | 
| 
      
 83 
     | 
    
         
            +
              # Split the array into chunks, cutting between the matched element and the next element.
         
     | 
| 
      
 84 
     | 
    
         
            +
              #
         
     | 
| 
      
 85 
     | 
    
         
            +
              # Example:
         
     | 
| 
      
 86 
     | 
    
         
            +
              #   [1,2,3,4].split_after{|e| e == 3 } #=> [ [1,2,3], [4] ]
         
     | 
| 
      
 87 
     | 
    
         
            +
              #
         
     | 
| 
      
 88 
     | 
    
         
            +
              def split_after(matcher=nil, options={}, &block)
         
     | 
| 
      
 89 
     | 
    
         
            +
                options[:after]             ||= true
         
     | 
| 
      
 90 
     | 
    
         
            +
                options[:include_boundary]  ||= true
         
     | 
| 
      
 91 
     | 
    
         
            +
                split_at(matcher, options, &block)
         
     | 
| 
      
 92 
     | 
    
         
            +
              end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
              #
         
     | 
| 
      
 95 
     | 
    
         
            +
              # Split the array into chunks, cutting before each matched element.
         
     | 
| 
      
 96 
     | 
    
         
            +
              #
         
     | 
| 
      
 97 
     | 
    
         
            +
              # Example:
         
     | 
| 
      
 98 
     | 
    
         
            +
              #   [1,2,3,4].split_before{|e| e == 3 } #=> [ [1,2], [3,4] ]
         
     | 
| 
      
 99 
     | 
    
         
            +
              #
         
     | 
| 
      
 100 
     | 
    
         
            +
              def split_before(matcher=nil, options={}, &block)
         
     | 
| 
      
 101 
     | 
    
         
            +
                options[:include_boundary]  ||= true
         
     | 
| 
      
 102 
     | 
    
         
            +
                split_at(matcher, options, &block)
         
     | 
| 
      
 103 
     | 
    
         
            +
              end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
              #
         
     | 
| 
      
 106 
     | 
    
         
            +
              # Split the array into chunks, cutting between two elements.
         
     | 
| 
      
 107 
     | 
    
         
            +
              #
         
     | 
| 
      
 108 
     | 
    
         
            +
              # Example:
         
     | 
| 
      
 109 
     | 
    
         
            +
              #   [1,1,2,2].split_between{|a,b| a != b } #=> [ [1,1], [2,2] ]
         
     | 
| 
      
 110 
     | 
    
         
            +
              #
         
     | 
| 
      
 111 
     | 
    
         
            +
              def split_between(&block)
         
     | 
| 
      
 112 
     | 
    
         
            +
                Enumerator.new do |yielder|
         
     | 
| 
      
 113 
     | 
    
         
            +
                  current = []
         
     | 
| 
      
 114 
     | 
    
         
            +
                  last    = nil
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  each_cons(2) do |a,b|
         
     | 
| 
      
 117 
     | 
    
         
            +
                    current << a
         
     | 
| 
      
 118 
     | 
    
         
            +
                    if yield(a,b)
         
     | 
| 
      
 119 
     | 
    
         
            +
                      yielder << current
         
     | 
| 
      
 120 
     | 
    
         
            +
                      current = []
         
     | 
| 
      
 121 
     | 
    
         
            +
                    end
         
     | 
| 
      
 122 
     | 
    
         
            +
                    last = b
         
     | 
| 
      
 123 
     | 
    
         
            +
                  end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                  current << last unless last.nil?
         
     | 
| 
      
 126 
     | 
    
         
            +
                  yielder << current
         
     | 
| 
      
 127 
     | 
    
         
            +
                end
         
     | 
| 
      
 128 
     | 
    
         
            +
              end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
              alias_method :cut_between, :split_between
         
     | 
| 
      
 131 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/upm/lesspipe.rb
    ADDED
    
    | 
         @@ -0,0 +1,57 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Create scrollable output via less!
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            # This command runs `less` in a subprocess, and gives you the IO to its STDIN pipe
         
     | 
| 
      
 5 
     | 
    
         
            +
            # so that you can communicate with it.
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
      
 7 
     | 
    
         
            +
            # Example:
         
     | 
| 
      
 8 
     | 
    
         
            +
            #
         
     | 
| 
      
 9 
     | 
    
         
            +
            #   lesspipe do |less|
         
     | 
| 
      
 10 
     | 
    
         
            +
            #     50.times { less.puts "Hi mom!" }
         
     | 
| 
      
 11 
     | 
    
         
            +
            #   end
         
     | 
| 
      
 12 
     | 
    
         
            +
            #
         
     | 
| 
      
 13 
     | 
    
         
            +
            # The default less parameters are:
         
     | 
| 
      
 14 
     | 
    
         
            +
            # * Allow colour
         
     | 
| 
      
 15 
     | 
    
         
            +
            # * Don't wrap lines longer than the screen
         
     | 
| 
      
 16 
     | 
    
         
            +
            # * Quit immediately (without paging) if there's less than one screen of text.
         
     | 
| 
      
 17 
     | 
    
         
            +
            #
         
     | 
| 
      
 18 
     | 
    
         
            +
            # You can change these options by passing a hash to `lesspipe`, like so:
         
     | 
| 
      
 19 
     | 
    
         
            +
            #
         
     | 
| 
      
 20 
     | 
    
         
            +
            #   lesspipe(:wrap=>false) { |less| less.puts essay.to_s }
         
     | 
| 
      
 21 
     | 
    
         
            +
            #
         
     | 
| 
      
 22 
     | 
    
         
            +
            # It accepts the following boolean options:
         
     | 
| 
      
 23 
     | 
    
         
            +
            #    :color  => Allow ANSI colour codes?
         
     | 
| 
      
 24 
     | 
    
         
            +
            #    :wrap   => Wrap long lines?
         
     | 
| 
      
 25 
     | 
    
         
            +
            #    :always => Always page, even if there's less than one page of text?
         
     | 
| 
      
 26 
     | 
    
         
            +
            #
         
     | 
| 
      
 27 
     | 
    
         
            +
            def lesspipe(*args)
         
     | 
| 
      
 28 
     | 
    
         
            +
              if args.any? and args.last.is_a?(Hash)
         
     | 
| 
      
 29 
     | 
    
         
            +
                options = args.pop
         
     | 
| 
      
 30 
     | 
    
         
            +
              else
         
     | 
| 
      
 31 
     | 
    
         
            +
                options = {}
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              output = args.first if args.any?
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              params = []
         
     | 
| 
      
 37 
     | 
    
         
            +
              params << "-R" unless options[:color] == false
         
     | 
| 
      
 38 
     | 
    
         
            +
              params << "-S" unless options[:wrap] == true
         
     | 
| 
      
 39 
     | 
    
         
            +
              params << "-F" unless options[:always] == true
         
     | 
| 
      
 40 
     | 
    
         
            +
              if options[:tail] == true
         
     | 
| 
      
 41 
     | 
    
         
            +
                params << "+\\>"
         
     | 
| 
      
 42 
     | 
    
         
            +
                $stderr.puts "Seeking to end of stream..."
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
              params << "-X"
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              IO.popen("less #{params * ' '}", "w") do |less|
         
     | 
| 
      
 47 
     | 
    
         
            +
                if output
         
     | 
| 
      
 48 
     | 
    
         
            +
                  less.puts output
         
     | 
| 
      
 49 
     | 
    
         
            +
                else
         
     | 
| 
      
 50 
     | 
    
         
            +
                  yield less
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
            rescue Errno::EPIPE, Interrupt
         
     | 
| 
      
 55 
     | 
    
         
            +
              # less just quit -- eat the exception.
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module UPM
         
     | 
| 
      
 2 
     | 
    
         
            +
              class LogParser
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                def initialize(klass, log_glob)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @klass = klass
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @log_glob = log_glob
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def log_events
         
     | 
| 
      
 10 
     | 
    
         
            +
                  return to_enum(:log_events) unless block_given?
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  yielder = proc do |io|
         
     | 
| 
      
 13 
     | 
    
         
            +
                    io.each_line do |line|
         
     | 
| 
      
 14 
     | 
    
         
            +
                      if e = @klass.from_line(line.strip)
         
     | 
| 
      
 15 
     | 
    
         
            +
                        yield e
         
     | 
| 
      
 16 
     | 
    
         
            +
                      end
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  logs = Dir[@log_glob].sort_by { |path| File.mtime(path) } 
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  logs.each do |log|
         
     | 
| 
      
 23 
     | 
    
         
            +
                    if log =~ /\.gz$/
         
     | 
| 
      
 24 
     | 
    
         
            +
                      IO.popen(["zcat", log], &yielder)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    else
         
     | 
| 
      
 26 
     | 
    
         
            +
                      open(log, &yielder)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end 
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def display
         
     | 
| 
      
 32 
     | 
    
         
            +
                  lesspipe(tail: true) do |less|
         
     | 
| 
      
 33 
     | 
    
         
            +
                    groups = log_events.split_between { |a,b| (b.date.to_i - a.date.to_i) > 60 }
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    groups.each do |group|
         
     | 
| 
      
 36 
     | 
    
         
            +
                      first, last = group.first.date, group.last.date
         
     | 
| 
      
 37 
     | 
    
         
            +
                      elapsed     = (last.to_i - first.to_i) / 60
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                      empty_group = true
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                      group.each do |ev|
         
     | 
| 
      
 42 
     | 
    
         
            +
                        # Print the header only if the query matched something in this group
         
     | 
| 
      
 43 
     | 
    
         
            +
                        if empty_group
         
     | 
| 
      
 44 
     | 
    
         
            +
                          empty_group = false
         
     | 
| 
      
 45 
     | 
    
         
            +
                          less.puts
         
     | 
| 
      
 46 
     | 
    
         
            +
                          less.puts "<8>== <11>#{first.strftime("<10>%Y-%m-%d <7>at <2>%l:%M %p")} <7>(<9>#{elapsed} <7>minute session) <8>========".colorize
         
     | 
| 
      
 47 
     | 
    
         
            +
                        end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                        less.puts ev
         
     | 
| 
      
 50 
     | 
    
         
            +
                      end
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end # lesspipe
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              end # LogParser
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/upm/tool.rb
    ADDED
    
    | 
         @@ -0,0 +1,180 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
      
 2 
     | 
    
         
            +
            # os:<pkg> -- automatically select the package manager for the current unix distribution
         
     | 
| 
      
 3 
     | 
    
         
            +
            # deb:<pkg> (or d: u:)
         
     | 
| 
      
 4 
     | 
    
         
            +
            # rpm:<pkg> (or yum: y:)
         
     | 
| 
      
 5 
     | 
    
         
            +
            # bsd:<pkg> (or b:)
         
     | 
| 
      
 6 
     | 
    
         
            +
            # ruby:<pkg> (or r: gem:)
         
     | 
| 
      
 7 
     | 
    
         
            +
            # python:<pkg>,<pkg> (or py: p: pip:)
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module UPM
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              class Tool
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                COMMAND_HELP = {
         
     | 
| 
      
 14 
     | 
    
         
            +
                  "install"          => "install a package",
         
     | 
| 
      
 15 
     | 
    
         
            +
                  "remove/uninstall" => "remove a package",
         
     | 
| 
      
 16 
     | 
    
         
            +
                  "build"            => "compile a package from source and install it",
         
     | 
| 
      
 17 
     | 
    
         
            +
                  "search"           => "using the fastest known API or service",
         
     | 
| 
      
 18 
     | 
    
         
            +
                  "list"             => "list installed packages (or search their names if extra arguments are supplied)",
         
     | 
| 
      
 19 
     | 
    
         
            +
                  "info"             => "show metadata about a package",
         
     | 
| 
      
 20 
     | 
    
         
            +
                  "sync/update"      => "retrieve the latest package list or manifest",
         
     | 
| 
      
 21 
     | 
    
         
            +
                  "upgrade"          => "install new versions of all packages",
         
     | 
| 
      
 22 
     | 
    
         
            +
                  "pin"              => "pinning a package means it won't be automatically upgraded",
         
     | 
| 
      
 23 
     | 
    
         
            +
                  "rollback"         => "revert to an earlier version of a package (including its dependencies)",
         
     | 
| 
      
 24 
     | 
    
         
            +
                  "log"              => "show history of package installs",
         
     | 
| 
      
 25 
     | 
    
         
            +
                  "packagers"        => "detect installed package managers, and pick which ones upm should wrap",
         
     | 
| 
      
 26 
     | 
    
         
            +
                  "mirrors/sources"  => "manage remote repositories and mirrors",
         
     | 
| 
      
 27 
     | 
    
         
            +
                  "verfiy"           => "verify the integrity of installed files",
         
     | 
| 
      
 28 
     | 
    
         
            +
                  "clean"            => "clear out the local package cache",
         
     | 
| 
      
 29 
     | 
    
         
            +
                  "monitor"          => "ad-hoc package manager for custom installations (like instmon)",
         
     | 
| 
      
 30 
     | 
    
         
            +
                  "keys"             => "keyrings and package authentication",
         
     | 
| 
      
 31 
     | 
    
         
            +
                  "default"          => "configure the action to take when no arguments are passed to 'upm' (defaults to 'os:update')",
         
     | 
| 
      
 32 
     | 
    
         
            +
                }
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                ALIASES = {
         
     | 
| 
      
 35 
     | 
    
         
            +
                  "file"    => "files",
         
     | 
| 
      
 36 
     | 
    
         
            +
                  "sync"    => "update",
         
     | 
| 
      
 37 
     | 
    
         
            +
                  "sources" => "mirrors",
         
     | 
| 
      
 38 
     | 
    
         
            +
                  "show"    => "info",
         
     | 
| 
      
 39 
     | 
    
         
            +
                }
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                @@tools = {}
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def self.register_tools!
         
     | 
| 
      
 44 
     | 
    
         
            +
                  Dir["#{__dir__}/tools/*.rb"].each { |lib| require_relative(lib) }
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                def self.os_release
         
     | 
| 
      
 48 
     | 
    
         
            +
                  @os_release ||= open("/etc/os-release") do |io|
         
     | 
| 
      
 49 
     | 
    
         
            +
                    io.read.scan(/^(\w+)="?(.+?)"?$/)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end.to_h
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                def self.current_os_names
         
     | 
| 
      
 54 
     | 
    
         
            +
                  # ID=ubuntu
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # ID_LIKE=debian
         
     | 
| 
      
 56 
     | 
    
         
            +
                  os_release.values_at("ID", "ID_LIKE")
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                def self.nice_os_name
         
     | 
| 
      
 60 
     | 
    
         
            +
                  os_release.values_at("PRETTY_NAME", "NAME", "ID", "ID_LIKE").first
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                def self.for_os(os_names=nil)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  os_names = os_names ? [os_names].flatten : current_os_names
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @@tools.find { |name, tool| os_names.any? { |name| tool.os.include? name } }.last
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                def self.tools
         
     | 
| 
      
 69 
     | 
    
         
            +
                  @@tools
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                def initialize(name, &block)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 74 
     | 
    
         
            +
                  instance_eval(&block)
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  @@tools[name] = self
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                def call_command(name, args)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  if block = (@cmds[name] || @cmds[ALIASES[name]])
         
     | 
| 
      
 81 
     | 
    
         
            +
                    block.call args
         
     | 
| 
      
 82 
     | 
    
         
            +
                  else
         
     | 
| 
      
 83 
     | 
    
         
            +
                    puts "Command #{name} not supported in #{@name}"
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                def help
         
     | 
| 
      
 88 
     | 
    
         
            +
                  puts "    Detected OS: #{Tool.nice_os_name}"
         
     | 
| 
      
 89 
     | 
    
         
            +
                  puts "Package manager: #{@name}"
         
     | 
| 
      
 90 
     | 
    
         
            +
                  puts
         
     | 
| 
      
 91 
     | 
    
         
            +
                  puts "Available commands:"
         
     | 
| 
      
 92 
     | 
    
         
            +
                  available = COMMAND_HELP.select do |name, desc|
         
     | 
| 
      
 93 
     | 
    
         
            +
                    names = name.split("/")
         
     | 
| 
      
 94 
     | 
    
         
            +
                    names.any? { |name| @cmds[name] }
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  max_width = available.map(&:first).map(&:size).max
         
     | 
| 
      
 98 
     | 
    
         
            +
                  available.each do |name, desc|
         
     | 
| 
      
 99 
     | 
    
         
            +
                    puts "  #{name.rjust(max_width)} | #{desc}"
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
                end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                def print_files(*paths, include: nil, exclude: nil)
         
     | 
| 
      
 104 
     | 
    
         
            +
                  lesspipe do |less|
         
     | 
| 
      
 105 
     | 
    
         
            +
                    paths.each do |path|
         
     | 
| 
      
 106 
     | 
    
         
            +
                      less.puts "<8>=== <11>#{path} <8>========".colorize
         
     | 
| 
      
 107 
     | 
    
         
            +
                      open(path) do |io|
         
     | 
| 
      
 108 
     | 
    
         
            +
                        enum = io.each_line
         
     | 
| 
      
 109 
     | 
    
         
            +
                        enum = enum.grep(include) if include
         
     | 
| 
      
 110 
     | 
    
         
            +
                        enum = enum.reject { |line| line[exclude] } if exclude
         
     | 
| 
      
 111 
     | 
    
         
            +
                        enum.each { |line| less.puts line }
         
     | 
| 
      
 112 
     | 
    
         
            +
                      end
         
     | 
| 
      
 113 
     | 
    
         
            +
                      less.puts
         
     | 
| 
      
 114 
     | 
    
         
            +
                    end
         
     | 
| 
      
 115 
     | 
    
         
            +
                  end
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                ## DSL methods
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                def prefix(name)
         
     | 
| 
      
 121 
     | 
    
         
            +
                  @prefix = name
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                def command(name, shell_command=nil, root: false, paged: false, &block)
         
     | 
| 
      
 125 
     | 
    
         
            +
                  @cmds ||= {}
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  if block_given?
         
     | 
| 
      
 128 
     | 
    
         
            +
                    @cmds[name] = block
         
     | 
| 
      
 129 
     | 
    
         
            +
                  elsif shell_command
         
     | 
| 
      
 130 
     | 
    
         
            +
                    if shell_command.is_a? String
         
     | 
| 
      
 131 
     | 
    
         
            +
                      shell_command = shell_command.split
         
     | 
| 
      
 132 
     | 
    
         
            +
                    elsif not shell_command.is_a? Array
         
     | 
| 
      
 133 
     | 
    
         
            +
                      raise "Error: command argument must be a String or an Array; it was a #{cmd.class}"
         
     | 
| 
      
 134 
     | 
    
         
            +
                    end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                    shell_command.unshift "sudo" if root
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                    @cmds[name] = proc { |args| run(*shell_command, *args, paged: paged) }
         
     | 
| 
      
 139 
     | 
    
         
            +
                  end
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                def os(*names)
         
     | 
| 
      
 143 
     | 
    
         
            +
                  names.any? ? @os = names : @os
         
     | 
| 
      
 144 
     | 
    
         
            +
                end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                ## Helpers
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                def run(*args, paged: false, grep: nil)
         
     | 
| 
      
 149 
     | 
    
         
            +
                  if !paged and !grep
         
     | 
| 
      
 150 
     | 
    
         
            +
                    system(*args)
         
     | 
| 
      
 151 
     | 
    
         
            +
                  else
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                    IO.popen(args, err: [:child, :out]) do |command_io|
         
     | 
| 
      
 154 
     | 
    
         
            +
                      
         
     | 
| 
      
 155 
     | 
    
         
            +
                      if grep
         
     | 
| 
      
 156 
     | 
    
         
            +
                        pattern = grep.is_a?(Regexp) ? grep.source : grep.to_s 
         
     | 
| 
      
 157 
     | 
    
         
            +
                        grep_io = IO.popen(["grep", "--color=always", "-Ei", pattern], "w+")
         
     | 
| 
      
 158 
     | 
    
         
            +
                        IO.copy_stream(command_io, grep_io)
         
     | 
| 
      
 159 
     | 
    
         
            +
                        grep_io.close_write
         
     | 
| 
      
 160 
     | 
    
         
            +
                        command_io = grep_io
         
     | 
| 
      
 161 
     | 
    
         
            +
                      end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                      if paged
         
     | 
| 
      
 164 
     | 
    
         
            +
                        lesspipe do |less|
         
     | 
| 
      
 165 
     | 
    
         
            +
                          IO.copy_stream(command_io, less)
         
     | 
| 
      
 166 
     | 
    
         
            +
                        end
         
     | 
| 
      
 167 
     | 
    
         
            +
                      else
         
     | 
| 
      
 168 
     | 
    
         
            +
                        IO.copy_stream(command_io, STDOUT)
         
     | 
| 
      
 169 
     | 
    
         
            +
                      end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                    end
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                    $?.to_i == 0
         
     | 
| 
      
 174 
     | 
    
         
            +
                  end
         
     | 
| 
      
 175 
     | 
    
         
            +
                end
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
              end # class Tool
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
            end # module UPM
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,68 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            UPM::Tool.new "apt" do
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              os "debian", "ubuntu"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              command "install",  "apt install", root: true
         
     | 
| 
      
 6 
     | 
    
         
            +
              command "update",   "apt update",  root: true
         
     | 
| 
      
 7 
     | 
    
         
            +
              command "upgrade",  "apt upgrade", root: true
         
     | 
| 
      
 8 
     | 
    
         
            +
              command "remove",   "apt remove",  root: true
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              command "files",  "dpkg-query -L", paged: true
         
     | 
| 
      
 11 
     | 
    
         
            +
              command "search", "apt search",    paged: true
         
     | 
| 
      
 12 
     | 
    
         
            +
              command "info",   "apt show",      paged: true
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              command "list" do |args|
         
     | 
| 
      
 15 
     | 
    
         
            +
                if args.any?
         
     | 
| 
      
 16 
     | 
    
         
            +
                  query = args.join
         
     | 
| 
      
 17 
     | 
    
         
            +
                  run("dpkg", "-l", grep: query, paged: true)
         
     | 
| 
      
 18 
     | 
    
         
            +
                else
         
     | 
| 
      
 19 
     | 
    
         
            +
                  run("dpkg", "-l", paged: true)
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
              
         
     | 
| 
      
 23 
     | 
    
         
            +
              command "mirrors" do
         
     | 
| 
      
 24 
     | 
    
         
            +
                print_files("/etc/apt/sources.list", *Dir["/etc/apt/sources.list.d/*"], exclude: /^(#|$)/)
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              command "log" do
         
     | 
| 
      
 28 
     | 
    
         
            +
                UPM::LogParser.new(DpkgEvent, "/var/log/dpkg*").display
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              class DpkgEvent < Struct.new(:datestr, :date, :cmd, :name, :v1, :v2)
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                # 2010-12-03 01:36:56 remove gir1.0-mutter-2.31 2.31.5-0ubuntu9 2.31.5-0ubuntu9
         
     | 
| 
      
 34 
     | 
    
         
            +
                # 2010-12-03 01:36:58 install gir1.0-mutter-2.91 <none> 2.91.2+git20101114.982a10ac-0ubuntu1~11.04~ricotz0  
         
     | 
| 
      
 35 
     | 
    
         
            +
                #LINE_RE = /^(.+ .+) (status \w+|\w+) (.+) (.+)$/
         
     | 
| 
      
 36 
     | 
    
         
            +
                LINE_RE = /^(.+ .+) (remove|install|upgrade) (.+) (.+) (.+)$/
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                CMD_COLORS = {
         
     | 
| 
      
 39 
     | 
    
         
            +
                  'remove' => :light_red,
         
     | 
| 
      
 40 
     | 
    
         
            +
                  'install' => :light_yellow,
         
     | 
| 
      
 41 
     | 
    
         
            +
                  'upgrade' => :light_green,
         
     | 
| 
      
 42 
     | 
    
         
            +
                  nil => :white,
         
     | 
| 
      
 43 
     | 
    
         
            +
                }
         
     | 
| 
      
 44 
     | 
    
         
            +
                
         
     | 
| 
      
 45 
     | 
    
         
            +
                def self.parse_date(date)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  DateTime.strptime(date, "%Y-%m-%d %H:%M:%S")
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
               
         
     | 
| 
      
 49 
     | 
    
         
            +
                def self.from_line(line)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  if line =~ LINE_RE
         
     | 
| 
      
 51 
     | 
    
         
            +
                    new($1, parse_date($1), $2, $3, $4, $5)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  else
         
     | 
| 
      
 53 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
                end    
         
     | 
| 
      
 56 
     | 
    
         
            +
                
         
     | 
| 
      
 57 
     | 
    
         
            +
                def cmd_color
         
     | 
| 
      
 58 
     | 
    
         
            +
                  CMD_COLORS[cmd]
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 62 
     | 
    
         
            +
                  date, time = datestr.split
         
     | 
| 
      
 63 
     | 
    
         
            +
                  "<grey>[<white>#{date} #{time}<grey>] <#{cmd_color}>#{cmd} <light_cyan>#{name} <light_white>#{v2} <white>(#{v1})".colorize
         
     | 
| 
      
 64 
     | 
    
         
            +
                end  
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  
         
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,105 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            UPM::Tool.new "pacman" do
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              os "arch"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              bin = ["pacman", "--color=always"]
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              command "install", [*bin, "-S"],   root: true
         
     | 
| 
      
 8 
     | 
    
         
            +
              command "update",  [*bin, "-Sy"],  root: true
         
     | 
| 
      
 9 
     | 
    
         
            +
              command "upgrade", [*bin, "-Syu"], root: true
         
     | 
| 
      
 10 
     | 
    
         
            +
              command "remove",  [*bin, "-R"],   root: true
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              command "files",  [*bin, "-Ql"], paged: true
         
     | 
| 
      
 13 
     | 
    
         
            +
              command "search", [*bin, "-Ss"], paged: true
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              command "info" do |args|
         
     | 
| 
      
 16 
     | 
    
         
            +
                run(*bin, "-Qi", *args) || run(*bin, "-Si", *args)
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              command "list" do |args|
         
     | 
| 
      
 20 
     | 
    
         
            +
                if args.any?
         
     | 
| 
      
 21 
     | 
    
         
            +
                  query = args.join
         
     | 
| 
      
 22 
     | 
    
         
            +
                  run(*bin, "-Q", grep: query, paged: true)
         
     | 
| 
      
 23 
     | 
    
         
            +
                else
         
     | 
| 
      
 24 
     | 
    
         
            +
                  run(*bin, "-Q", paged: true)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              command "mirrors" do
         
     | 
| 
      
 29 
     | 
    
         
            +
                print_files("/etc/pacman.d/mirrorlist", exclude: /^(#|$)/)
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              command "depends" do |args|
         
     | 
| 
      
 33 
     | 
    
         
            +
                packages_that_depend_on = proc do |package|
         
     | 
| 
      
 34 
     | 
    
         
            +
                  result = []
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  [`pacman -Sii #{package}`, `pacman -Qi #{package}`].each do |output|
         
     | 
| 
      
 37 
     | 
    
         
            +
                    output.each_line do |l|
         
     | 
| 
      
 38 
     | 
    
         
            +
                      if l =~ /Required By\s+: (.+)/
         
     | 
| 
      
 39 
     | 
    
         
            +
                        result += $1.strip.split unless $1["None"]
         
     | 
| 
      
 40 
     | 
    
         
            +
                        break
         
     | 
| 
      
 41 
     | 
    
         
            +
                      end
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  result
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                args.each do |arg|
         
     | 
| 
      
 49 
     | 
    
         
            +
                  puts "=== Packages which depend on: #{arg} ============"
         
     | 
| 
      
 50 
     | 
    
         
            +
                  packages = packages_that_depend_on.call(arg)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  puts
         
     | 
| 
      
 52 
     | 
    
         
            +
                  run *bin, "-Ss", "^(#{packages.join '|'})$" # upstream packages
         
     | 
| 
      
 53 
     | 
    
         
            +
                  run *bin, "-Qs", "^(#{packages.join '|'})$" # packages that are only installed locally
         
     | 
| 
      
 54 
     | 
    
         
            +
                  puts
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
              command "log" do
         
     | 
| 
      
 59 
     | 
    
         
            +
                UPM::LogParser.new(PacmanEvent, "/var/log/pacman.log*").display
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              class PacmanEvent < Struct.new(:datestr, :date, :cmd, :name, :v1, :v2)
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                # [2015-01-04 04:21] [PACMAN] installed lib32-libidn (1.29-1)
         
     | 
| 
      
 65 
     | 
    
         
            +
                # [2015-01-04 04:21] [PACMAN] upgraded lib32-curl (7.38.0-1 -> 7.39.0-1)
         
     | 
| 
      
 66 
     | 
    
         
            +
                # [2015-01-07 04:39] [ALPM] upgraded intel-tbb (4.3_20141023-1 -> 4.3_20141204-1)
         
     | 
| 
      
 67 
     | 
    
         
            +
                # [2015-01-07 04:39] [ALPM] upgraded iso-codes (3.54-1 -> 3.57-1)
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                DATE_RE = /[\d:-]+/
         
     | 
| 
      
 70 
     | 
    
         
            +
                LINE_RE = /^\[(#{DATE_RE} #{DATE_RE})\](?: \[(?:PACMAN|ALPM)\])? (removed|installed|upgraded) (.+) \((.+)(?: -> (.+))?\)$/
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                CMD_COLORS = {
         
     | 
| 
      
 73 
     | 
    
         
            +
                  'removed' => :light_red,
         
     | 
| 
      
 74 
     | 
    
         
            +
                  'installed' => :light_yellow,
         
     | 
| 
      
 75 
     | 
    
         
            +
                  'upgraded' => :light_green,
         
     | 
| 
      
 76 
     | 
    
         
            +
                  nil => :white,
         
     | 
| 
      
 77 
     | 
    
         
            +
                }
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                def self.parse_date(date)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  DateTime.strptime(date, "%Y-%m-%d %H:%M")
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                def self.from_line(line)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  if line =~ LINE_RE
         
     | 
| 
      
 85 
     | 
    
         
            +
                    new($1, parse_date($1), $2, $3, $4, $5)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  else
         
     | 
| 
      
 87 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
                end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                def cmd_color
         
     | 
| 
      
 92 
     | 
    
         
            +
                  CMD_COLORS[cmd]
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 96 
     | 
    
         
            +
                  date, time = datestr.split
         
     | 
| 
      
 97 
     | 
    
         
            +
                  "<grey>[<white>#{date} #{time}<grey>] <#{cmd_color}>#{cmd} <light_cyan>#{name} #{"<light_white>#{v2} " if v2}<white>(#{v1})".colorize
         
     | 
| 
      
 98 
     | 
    
         
            +
                end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
            end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            UPM::Tool.new "xbps" do
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              os "void"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              command "install", "xbps-install", root: true
         
     | 
| 
      
 6 
     | 
    
         
            +
              command "update", "xbps-install -S", root: true
         
     | 
| 
      
 7 
     | 
    
         
            +
              command "upgrade", "xbps-install -Su", root: true
         
     | 
| 
      
 8 
     | 
    
         
            +
              command "files", "xbps-query -f"
         
     | 
| 
      
 9 
     | 
    
         
            +
              command "search", "xbps-query --regex -Rs"
         
     | 
| 
      
 10 
     | 
    
         
            +
              # command "info", ""
         
     | 
| 
      
 11 
     | 
    
         
            +
              
         
     | 
| 
      
 12 
     | 
    
         
            +
              command "list" do |args|
         
     | 
| 
      
 13 
     | 
    
         
            +
                if args.any?
         
     | 
| 
      
 14 
     | 
    
         
            +
                  run "xbps-query", "-f", *args
         
     | 
| 
      
 15 
     | 
    
         
            +
                else
         
     | 
| 
      
 16 
     | 
    
         
            +
                  run "xbps-query", "-l"
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/upm.rb
    ADDED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,29 +1,42 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: upm
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - epitron
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date:  
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2018-05-04 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies: []
         
     | 
| 
       13 
13 
     | 
    
         
             
            description: Wrap all known command-line package tools with a consistent and pretty
         
     | 
| 
       14 
14 
     | 
    
         
             
              interface.
         
     | 
| 
       15 
15 
     | 
    
         
             
            email: chris@ill-logic.com
         
     | 
| 
       16 
     | 
    
         
            -
            executables: 
     | 
| 
      
 16 
     | 
    
         
            +
            executables:
         
     | 
| 
      
 17 
     | 
    
         
            +
            - upm
         
     | 
| 
       17 
18 
     | 
    
         
             
            extensions: []
         
     | 
| 
       18 
19 
     | 
    
         
             
            extra_rdoc_files:
         
     | 
| 
       19 
20 
     | 
    
         
             
            - README.md
         
     | 
| 
       20 
21 
     | 
    
         
             
            - LICENSE
         
     | 
| 
       21 
22 
     | 
    
         
             
            files:
         
     | 
| 
       22 
23 
     | 
    
         
             
            - ".gemspec"
         
     | 
| 
      
 24 
     | 
    
         
            +
            - ".gitignore"
         
     | 
| 
       23 
25 
     | 
    
         
             
            - LICENSE
         
     | 
| 
       24 
26 
     | 
    
         
             
            - README.md
         
     | 
| 
       25 
27 
     | 
    
         
             
            - Rakefile
         
     | 
| 
      
 28 
     | 
    
         
            +
            - TODO.md
         
     | 
| 
       26 
29 
     | 
    
         
             
            - VERSION
         
     | 
| 
      
 30 
     | 
    
         
            +
            - bin/upm
         
     | 
| 
      
 31 
     | 
    
         
            +
            - lib/upm.rb
         
     | 
| 
      
 32 
     | 
    
         
            +
            - lib/upm/colored.rb
         
     | 
| 
      
 33 
     | 
    
         
            +
            - lib/upm/core_ext.rb
         
     | 
| 
      
 34 
     | 
    
         
            +
            - lib/upm/lesspipe.rb
         
     | 
| 
      
 35 
     | 
    
         
            +
            - lib/upm/log_parser.rb
         
     | 
| 
      
 36 
     | 
    
         
            +
            - lib/upm/tool.rb
         
     | 
| 
      
 37 
     | 
    
         
            +
            - lib/upm/tools/apt.rb
         
     | 
| 
      
 38 
     | 
    
         
            +
            - lib/upm/tools/pacman.rb
         
     | 
| 
      
 39 
     | 
    
         
            +
            - lib/upm/tools/xbps.rb
         
     | 
| 
       27 
40 
     | 
    
         
             
            homepage: http://github.com/epitron/upm/
         
     | 
| 
       28 
41 
     | 
    
         
             
            licenses:
         
     | 
| 
       29 
42 
     | 
    
         
             
            - WTFPL
         
     | 
| 
         @@ -44,9 +57,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       44 
57 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       45 
58 
     | 
    
         
             
            requirements: []
         
     | 
| 
       46 
59 
     | 
    
         
             
            rubyforge_project: 
         
     | 
| 
       47 
     | 
    
         
            -
            rubygems_version: 2. 
     | 
| 
      
 60 
     | 
    
         
            +
            rubygems_version: 2.7.6
         
     | 
| 
       48 
61 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       49 
62 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       50 
63 
     | 
    
         
             
            summary: Universal Package Manager
         
     | 
| 
       51 
64 
     | 
    
         
             
            test_files: []
         
     | 
| 
       52 
     | 
    
         
            -
            has_rdoc: 
         
     |