pennyworth 10.0.1 → 11.1.1

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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.adoc +352 -31
  5. data/bin/pennyworth +1 -3
  6. data/lib/pennyworth.rb +11 -2
  7. data/lib/pennyworth/cli/actions/config.rb +35 -0
  8. data/lib/pennyworth/cli/actions/encodings.rb +23 -0
  9. data/lib/pennyworth/cli/actions/git_hub.rb +27 -0
  10. data/lib/pennyworth/cli/actions/http_statuses.rb +23 -0
  11. data/lib/pennyworth/cli/actions/ruby_gems.rb +27 -0
  12. data/lib/pennyworth/cli/actions/system/errors.rb +25 -0
  13. data/lib/pennyworth/cli/actions/system/signals.rb +25 -0
  14. data/lib/pennyworth/cli/actions/text.rb +23 -0
  15. data/lib/pennyworth/cli/configuration/content.rb +28 -0
  16. data/lib/pennyworth/cli/configuration/defaults.yml +25 -0
  17. data/lib/pennyworth/cli/configuration/loader.rb +37 -0
  18. data/lib/pennyworth/cli/parsers.rb +11 -0
  19. data/lib/pennyworth/cli/parsers/assembler.rb +32 -0
  20. data/lib/pennyworth/cli/parsers/core.rb +100 -0
  21. data/lib/pennyworth/cli/parsers/git_hub.rb +51 -0
  22. data/lib/pennyworth/cli/parsers/ruby_gems.rb +35 -0
  23. data/lib/pennyworth/cli/shell.rb +81 -0
  24. data/lib/pennyworth/container.rb +40 -0
  25. data/lib/pennyworth/identity.rb +2 -1
  26. data/lib/pennyworth/inflector.rb +18 -0
  27. data/lib/pennyworth/integrations/git_hub/client.rb +59 -0
  28. data/lib/pennyworth/integrations/git_hub/response.rb +33 -0
  29. data/lib/pennyworth/integrations/ruby_gems/client.rb +29 -0
  30. data/lib/pennyworth/loaders/encodings.rb +31 -0
  31. data/lib/pennyworth/loaders/http_statuses.rb +28 -0
  32. data/lib/pennyworth/loaders/projects.rb +22 -0
  33. data/lib/pennyworth/loaders/system/errors.rb +33 -0
  34. data/lib/pennyworth/loaders/system/signals.rb +21 -0
  35. data/lib/pennyworth/loaders/text.rb +27 -0
  36. data/lib/pennyworth/models/encoding.rb +8 -0
  37. data/lib/pennyworth/models/http_status.rb +8 -0
  38. data/lib/pennyworth/models/system/error.rb +10 -0
  39. data/lib/pennyworth/models/system/signal.rb +10 -0
  40. data/lib/pennyworth/models/text.rb +8 -0
  41. data/lib/pennyworth/presenters/encoding.rb +22 -0
  42. data/lib/pennyworth/presenters/gem.rb +48 -0
  43. data/lib/pennyworth/presenters/http_status.rb +41 -0
  44. data/lib/pennyworth/presenters/repository.rb +35 -0
  45. data/lib/pennyworth/presenters/system/error.rb +28 -0
  46. data/lib/pennyworth/presenters/system/signal.rb +22 -0
  47. data/lib/pennyworth/presenters/text.rb +25 -0
  48. data/lib/pennyworth/processor.rb +68 -0
  49. data/lib/pennyworth/serializers/encoding.rb +33 -0
  50. data/lib/pennyworth/serializers/http_status.rb +41 -0
  51. data/lib/pennyworth/serializers/project.rb +52 -0
  52. data/lib/pennyworth/serializers/system/error.rb +46 -0
  53. data/lib/pennyworth/serializers/system/signal.rb +40 -0
  54. data/lib/pennyworth/serializers/text.rb +31 -0
  55. metadata +110 -19
  56. metadata.gz.sig +0 -0
  57. data/lib/pennyworth/cli.rb +0 -140
  58. data/lib/pennyworth/templates/workflows/user.workflow.B59B22A2-1880-4765-9358-412791BE9202/icon.png +0 -0
  59. data/lib/pennyworth/templates/workflows/user.workflow.B59B22A2-1880-4765-9358-412791BE9202/info.plist +0 -159
  60. data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/icon.png +0 -0
  61. data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/info.plist +0 -272
  62. data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-camelcase.png +0 -0
  63. data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-capitalize.png +0 -0
  64. data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-lowercase.png +0 -0
  65. data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-size.png +0 -0
  66. data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-snakecase.png +0 -0
  67. data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-titleize.png +0 -0
  68. data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-uppercase.png +0 -0
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/structs"
4
+
5
+ module Pennyworth
6
+ module CLI
7
+ module Parsers
8
+ # Handles parsing of Command Line Interface (CLI) GitHub options.
9
+ class GitHub
10
+ using Refinements::Structs
11
+
12
+ def self.call(...) = new(...).call
13
+
14
+ def initialize configuration = Configuration::Loader.call, client: CLIENT
15
+ @configuration = configuration
16
+ @client = client
17
+ end
18
+
19
+ def call arguments = []
20
+ client.separator "\nGITHUB OPTIONS:\n"
21
+ private_methods.sort.grep(/add_/).each { |method| __send__ method }
22
+ arguments.empty? ? arguments : client.parse!(arguments)
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :configuration, :client
28
+
29
+ def add_organization
30
+ client.on(
31
+ "--organization [HANDLE]",
32
+ %(Set organization. Default: "#{organization}".)
33
+ ) do |handle|
34
+ configuration.merge! action_git_hub: :organization,
35
+ git_hub_organization: handle || organization
36
+ end
37
+ end
38
+
39
+ def add_user
40
+ client.on "--user [HANDLE]", %(Set user. Default: "#{user}".) do |handle|
41
+ configuration.merge! action_git_hub: :user, git_hub_user: handle || user
42
+ end
43
+ end
44
+
45
+ def organization = configuration.git_hub_organization
46
+
47
+ def user = configuration.git_hub_user
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pennyworth
4
+ module CLI
5
+ module Parsers
6
+ # Handles parsing of Command Line Interface (CLI) RubyGems options.
7
+ class RubyGems
8
+ def self.call(...) = new(...).call
9
+
10
+ def initialize configuration = Configuration::Loader.call, client: CLIENT
11
+ @configuration = configuration
12
+ @client = client
13
+ end
14
+
15
+ def call arguments = []
16
+ client.separator "\nRUBYGEMS OPTIONS:\n"
17
+ add_owner
18
+ arguments.empty? ? arguments : client.parse!(arguments)
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :configuration, :client
24
+
25
+ def add_owner
26
+ client.on "--owner [HANDLE]", %(Set owner. Default: "#{owner}".) do |handle|
27
+ configuration.ruby_gems_owner = handle || owner
28
+ end
29
+ end
30
+
31
+ def owner = configuration.ruby_gems_owner
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pennyworth
4
+ module CLI
5
+ # The main Command Line Interface (CLI) object.
6
+ class Shell
7
+ ACTIONS = {
8
+ config: Actions::Config.new,
9
+ encodings: Actions::Encodings.new,
10
+ git_hub: Actions::GitHub.new,
11
+ http_statuses: Actions::HTTPStatuses.new,
12
+ ruby_gems: Actions::RubyGems.new,
13
+ system_errors: Actions::System::Errors.new,
14
+ system_signals: Actions::System::Signals.new,
15
+ text: Actions::Text.new
16
+ }.freeze
17
+
18
+ def initialize parser: Parsers::Assembler.new, actions: ACTIONS, container: Container
19
+ @parser = parser
20
+ @actions = actions
21
+ @container = container
22
+ end
23
+
24
+ def call arguments = []
25
+ perform parser.call(arguments)
26
+ rescue OptionParser::ParseError, KeyError => error
27
+ logger.error error.message
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :parser, :actions, :container
33
+
34
+ # rubocop:disable Metrics/MethodLength
35
+ def perform configuration
36
+ case configuration
37
+ in action_config: Symbol => action then config action
38
+ in action_encodings: true then encodings
39
+ in action_git_hub: :organization then git_hub_organization configuration
40
+ in action_git_hub: :user then git_hub_user configuration
41
+ in action_http_statuses: true then http_statuses
42
+ in action_ruby_gems: true then ruby_gems configuration
43
+ in action_system_signals: true then system_signals
44
+ in action_system_errors: true then system_errors
45
+ in action_text: String => content then text content
46
+ in action_version: String => version then logger.info version
47
+ else usage
48
+ end
49
+ end
50
+ # rubocop:enable Metrics/MethodLength
51
+
52
+ def config(action) = actions.fetch(__method__).call(action)
53
+
54
+ def encodings = actions.fetch(__method__).call
55
+
56
+ def git_hub_organization configuration
57
+ actions.fetch(:git_hub).call("orgs/#{configuration.git_hub_organization}/repos")
58
+ end
59
+
60
+ def git_hub_user configuration
61
+ actions.fetch(:git_hub).call("users/#{configuration.git_hub_user}/repos")
62
+ end
63
+
64
+ def http_statuses = actions.fetch(__method__).call
65
+
66
+ def ruby_gems configuration
67
+ actions.fetch(__method__).call("owners/#{configuration.ruby_gems_owner}/gems.json")
68
+ end
69
+
70
+ def system_errors = actions.fetch(__method__).call
71
+
72
+ def system_signals = actions.fetch(__method__).call
73
+
74
+ def text(content) = actions.fetch(__method__).call(content)
75
+
76
+ def usage = logger.unknown { parser.to_s }
77
+
78
+ def logger = container[__method__]
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-container"
4
+ require "logger"
5
+ require "http"
6
+ require "pastel"
7
+
8
+ module Pennyworth
9
+ # Provides a global gem container for injection into other objects.
10
+ module Container
11
+ extend Dry::Container::Mixin
12
+
13
+ register(:configuration) { CLI::Configuration::Loader.call }
14
+ register(:environment) { ENV }
15
+ register(:kernel) { Kernel }
16
+ register(:http) { HTTP }
17
+ register(:colorizer) { Pastel.new enabled: $stdout.tty? }
18
+
19
+ register :log_colors do
20
+ {
21
+ "DEBUG" => self[:colorizer].white.detach,
22
+ "INFO" => self[:colorizer].green.detach,
23
+ "WARN" => self[:colorizer].yellow.detach,
24
+ "ERROR" => self[:colorizer].red.detach,
25
+ "FATAL" => self[:colorizer].white.bold.on_red.detach,
26
+ "ANY" => self[:colorizer].white.bold.detach
27
+ }
28
+ end
29
+
30
+ register :logger do
31
+ Logger.new $stdout,
32
+ level: Logger.const_get(ENV.fetch("LOG_LEVEL", "INFO")),
33
+ formatter: (
34
+ lambda do |severity, _at, _name, message|
35
+ self[:log_colors][severity].call "#{message}\n"
36
+ end
37
+ )
38
+ end
39
+ end
40
+ end
@@ -5,7 +5,8 @@ module Pennyworth
5
5
  module Identity
6
6
  NAME = "pennyworth"
7
7
  LABEL = "Pennyworth"
8
- VERSION = "10.0.1"
8
+ SUMMARY = "A command line interface that augments Alfred workflows."
9
+ VERSION = "11.1.1"
9
10
  VERSION_LABEL = "#{LABEL} #{VERSION}"
10
11
  end
11
12
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pennyworth
4
+ # Overrides any string to desired form if matched, otherwise answers the original string.
5
+ class Inflector
6
+ DEFAULTS = Array(CLI::Configuration::Loader.call.inflections).reduce({}, :merge)
7
+
8
+ def initialize overrides = DEFAULTS
9
+ @overrides = overrides
10
+ end
11
+
12
+ def call(key) = overrides.fetch(key) { |fallback| fallback }
13
+
14
+ private
15
+
16
+ attr_reader :overrides
17
+ end
18
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/arrays"
4
+
5
+ module Pennyworth
6
+ module Integrations
7
+ module GitHub
8
+ # Provides low-level API access to the GitHub API.
9
+ class Client
10
+ using Refinements::Arrays
11
+
12
+ HEADERS = {
13
+ accept: "application/vnd.github.v3+json",
14
+ content_type: "Content-Type: application/json; charset=utf-8"
15
+ }.freeze
16
+
17
+ def initialize headers: HEADERS, container: Container
18
+ @headers = headers
19
+ @container = container
20
+ end
21
+
22
+ def get endpoint, parameters: {}
23
+ paginate ->(page) { sole_get endpoint, parameters: parameters.merge(page: page) }
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :headers, :container
29
+
30
+ # :reek:FeatureEnvy
31
+ def paginate request, records: [], page: 1
32
+ response = request.call page
33
+
34
+ records.including(response.records)
35
+ .then do |updated_records|
36
+ return updated_records if response.last_page.zero?
37
+
38
+ paginate request, records: updated_records, page: response.next_page
39
+ end
40
+ end
41
+
42
+ def sole_get endpoint, parameters: {}
43
+ http.headers(headers)
44
+ .basic_auth(user: api_login, pass: api_token)
45
+ .get("#{api_url}/#{endpoint}", params: parameters)
46
+ .then { |response| Response.new response }
47
+ end
48
+
49
+ def http = container[__method__]
50
+
51
+ def api_url = container[:configuration].git_hub_api_url
52
+
53
+ def api_login = container[:environment].fetch("GITHUB_API_LOGIN")
54
+
55
+ def api_token = container[:environment].fetch("GITHUB_API_TOKEN")
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pennyworth
4
+ module Integrations
5
+ module GitHub
6
+ # Represents a generic GitHub API response.
7
+ class Response
8
+ def initialize raw
9
+ @raw = raw
10
+ end
11
+
12
+ def next_page = page("next")
13
+
14
+ def last_page = page("last")
15
+
16
+ def records = success? ? JSON(raw.body, symbolize_names: true) : []
17
+
18
+ def success? = raw.status.success?
19
+
20
+ private
21
+
22
+ attr_reader :raw
23
+
24
+ def page reference
25
+ links.find { |link| link.include? reference }
26
+ .then { |link| String(link)[/page=(?<page>\d+)/, :page].to_i }
27
+ end
28
+
29
+ def links = String(raw.headers["Link"]).split(", ")
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pennyworth
4
+ module Integrations
5
+ module RubyGems
6
+ # Provides low-level API access to the RubyGems API.
7
+ class Client
8
+ def initialize container: Container
9
+ @container = container
10
+ end
11
+
12
+ def get endpoint
13
+ http.get("#{api_url}/#{endpoint}")
14
+ .then do |response|
15
+ response.status.success? ? JSON(response.body.to_s, symbolize_names: true) : {}
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :container
22
+
23
+ def http = container[__method__]
24
+
25
+ def api_url = container[:configuration].ruby_gems_api_url
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/arrays"
4
+
5
+ module Pennyworth
6
+ module Loaders
7
+ # Loads an array of encoding records.
8
+ class Encodings
9
+ using Refinements::Arrays
10
+
11
+ def initialize encoding: Encoding, model: Models::Encoding
12
+ @encoding = encoding
13
+ @model = model
14
+ end
15
+
16
+ def call _omit = nil
17
+ encoding.aliases
18
+ .map { |_second, first| first }
19
+ .uniq
20
+ .sort
21
+ .map do |first|
22
+ model[name: first, aliases: encoding.find(first).names.sort.excluding(first)]
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :encoding, :model
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+
5
+ module Pennyworth
6
+ module Loaders
7
+ # Loads an array of HTTP status records.
8
+ class HTTPStatuses
9
+ def initialize codes: Rack::Utils::HTTP_STATUS_CODES,
10
+ model: Models::HTTPStatus,
11
+ configuration: CLI::Configuration::Loader.call
12
+ @codes = codes
13
+ @model = model
14
+ @configuration = configuration
15
+ end
16
+
17
+ def call _omit = nil
18
+ codes.map { |(code, label)| model[code: code, label: label, url: "#{url}/#{code}"] }
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :codes, :model, :configuration
24
+
25
+ def url = configuration.http_statuses_url
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pennyworth
4
+ module Loaders
5
+ # Loads projects from API requests for further processing.
6
+ class Projects
7
+ def self.for_git_hub = new(client: Integrations::GitHub::Client.new)
8
+
9
+ def self.for_ruby_gems = new(client: Integrations::RubyGems::Client.new)
10
+
11
+ def initialize client:
12
+ @client = client
13
+ end
14
+
15
+ def call(endpoint) = client.get(endpoint)
16
+
17
+ private
18
+
19
+ attr_reader :client
20
+ end
21
+ end
22
+ end