pennyworth 10.0.1 → 11.0.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.adoc +209 -30
- data/bin/pennyworth +1 -3
- data/lib/pennyworth.rb +11 -2
- data/lib/pennyworth/cli/actions/config.rb +35 -0
- data/lib/pennyworth/cli/actions/encodings.rb +23 -0
- data/lib/pennyworth/cli/actions/git_hub.rb +27 -0
- data/lib/pennyworth/cli/actions/http_statuses.rb +23 -0
- data/lib/pennyworth/cli/actions/ruby_gems.rb +27 -0
- data/lib/pennyworth/cli/actions/system/errors.rb +25 -0
- data/lib/pennyworth/cli/actions/system/signals.rb +25 -0
- data/lib/pennyworth/cli/actions/text.rb +23 -0
- data/lib/pennyworth/cli/configuration/content.rb +28 -0
- data/lib/pennyworth/cli/configuration/defaults.yml +25 -0
- data/lib/pennyworth/cli/configuration/loader.rb +37 -0
- data/lib/pennyworth/cli/parsers.rb +11 -0
- data/lib/pennyworth/cli/parsers/assembler.rb +32 -0
- data/lib/pennyworth/cli/parsers/core.rb +100 -0
- data/lib/pennyworth/cli/parsers/git_hub.rb +51 -0
- data/lib/pennyworth/cli/parsers/ruby_gems.rb +35 -0
- data/lib/pennyworth/cli/shell.rb +82 -0
- data/lib/pennyworth/container.rb +40 -0
- data/lib/pennyworth/identity.rb +2 -1
- data/lib/pennyworth/inflector.rb +18 -0
- data/lib/pennyworth/integrations/git_hub/client.rb +59 -0
- data/lib/pennyworth/integrations/git_hub/response.rb +33 -0
- data/lib/pennyworth/integrations/ruby_gems/client.rb +29 -0
- data/lib/pennyworth/loaders/encodings.rb +31 -0
- data/lib/pennyworth/loaders/http_statuses.rb +28 -0
- data/lib/pennyworth/loaders/projects.rb +22 -0
- data/lib/pennyworth/loaders/system/errors.rb +33 -0
- data/lib/pennyworth/loaders/system/signals.rb +21 -0
- data/lib/pennyworth/loaders/text.rb +27 -0
- data/lib/pennyworth/models/encoding.rb +8 -0
- data/lib/pennyworth/models/http_status.rb +8 -0
- data/lib/pennyworth/models/system/error.rb +10 -0
- data/lib/pennyworth/models/system/signal.rb +10 -0
- data/lib/pennyworth/models/text.rb +8 -0
- data/lib/pennyworth/presenters/encoding.rb +22 -0
- data/lib/pennyworth/presenters/gem.rb +48 -0
- data/lib/pennyworth/presenters/http_status.rb +41 -0
- data/lib/pennyworth/presenters/repository.rb +35 -0
- data/lib/pennyworth/presenters/system/error.rb +28 -0
- data/lib/pennyworth/presenters/system/signal.rb +22 -0
- data/lib/pennyworth/presenters/text.rb +25 -0
- data/lib/pennyworth/processor.rb +68 -0
- data/lib/pennyworth/serializers/encoding.rb +33 -0
- data/lib/pennyworth/serializers/http_status.rb +41 -0
- data/lib/pennyworth/serializers/project.rb +52 -0
- data/lib/pennyworth/serializers/system/error.rb +46 -0
- data/lib/pennyworth/serializers/system/signal.rb +40 -0
- data/lib/pennyworth/serializers/text.rb +31 -0
- metadata +110 -19
- metadata.gz.sig +2 -2
- data/lib/pennyworth/cli.rb +0 -140
- data/lib/pennyworth/templates/workflows/user.workflow.B59B22A2-1880-4765-9358-412791BE9202/icon.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.B59B22A2-1880-4765-9358-412791BE9202/info.plist +0 -159
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/icon.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/info.plist +0 -272
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-camelcase.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-capitalize.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-lowercase.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-size.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-snakecase.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-titleize.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-uppercase.png +0 -0
@@ -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
|
data/lib/pennyworth/identity.rb
CHANGED
@@ -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) { |missing_key| missing_key }
|
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
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "refinements/arrays"
|
4
|
+
|
5
|
+
module Pennyworth
|
6
|
+
module Loaders
|
7
|
+
module System
|
8
|
+
# Loads an array of system error records.
|
9
|
+
class Errors
|
10
|
+
using Refinements::Arrays
|
11
|
+
|
12
|
+
def initialize errno: Errno, model: Models::System::Error
|
13
|
+
@errno = errno
|
14
|
+
@model = model
|
15
|
+
end
|
16
|
+
|
17
|
+
def call _omit = nil
|
18
|
+
errno.constants
|
19
|
+
.map { |name| errno.const_get name }
|
20
|
+
.uniq
|
21
|
+
.sort_by { |error| error::Errno }
|
22
|
+
.map do |error|
|
23
|
+
model[id: error::Errno, name: error.name, description: error.exception.message]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :errno, :model
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pennyworth
|
4
|
+
module Loaders
|
5
|
+
module System
|
6
|
+
# Loads an array of signal records.
|
7
|
+
class Signals
|
8
|
+
def initialize list: Signal.list, model: Models::System::Signal
|
9
|
+
@list = list
|
10
|
+
@model = model
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(_omit = nil) = list.map { |name, number| model[number: number, name: name] }
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :list, :model
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "refinements/strings"
|
4
|
+
|
5
|
+
module Pennyworth
|
6
|
+
module Loaders
|
7
|
+
# Loads an array of text records.
|
8
|
+
class Text
|
9
|
+
using Refinements::Strings
|
10
|
+
|
11
|
+
TRANSFORMS = %w[camelcase capitalize downcase size snakecase titleize upcase].freeze
|
12
|
+
|
13
|
+
def initialize transforms: TRANSFORMS, model: Models::Text
|
14
|
+
@transforms = transforms
|
15
|
+
@model = model
|
16
|
+
end
|
17
|
+
|
18
|
+
def call content
|
19
|
+
transforms.map { |kind| model[id: kind, content: content.public_send(kind)] }
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :transforms, :model
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pennyworth
|
4
|
+
module Presenters
|
5
|
+
# Renders encoding into a compatible format for use in Alfred script filters.
|
6
|
+
class Encoding
|
7
|
+
def initialize record
|
8
|
+
@record = record
|
9
|
+
end
|
10
|
+
|
11
|
+
def id = record.name
|
12
|
+
|
13
|
+
def label = record.name
|
14
|
+
|
15
|
+
def aliases = record.aliases
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :record
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|