pakyow-support 0.11.3 → 1.0.0.rc1

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 (51) hide show
  1. checksums.yaml +5 -5
  2. data/{pakyow-support/CHANGELOG.md → CHANGELOG.md} +4 -0
  3. data/LICENSE +4 -0
  4. data/{pakyow-support/README.md → README.md} +1 -2
  5. data/lib/pakyow/support/aargv.rb +25 -0
  6. data/lib/pakyow/support/bindable.rb +19 -0
  7. data/lib/pakyow/support/class_state.rb +49 -0
  8. data/lib/pakyow/support/cli/runner.rb +106 -0
  9. data/lib/pakyow/support/cli/style.rb +13 -0
  10. data/lib/pakyow/support/configurable/config.rb +153 -0
  11. data/lib/pakyow/support/configurable/setting.rb +52 -0
  12. data/lib/pakyow/support/configurable.rb +103 -0
  13. data/lib/pakyow/support/core_refinements/array/ensurable.rb +25 -0
  14. data/lib/pakyow/support/core_refinements/method/introspection.rb +21 -0
  15. data/lib/pakyow/support/core_refinements/proc/introspection.rb +21 -0
  16. data/lib/pakyow/support/core_refinements/string/normalization.rb +50 -0
  17. data/lib/pakyow/support/deep_dup.rb +61 -0
  18. data/lib/pakyow/support/deep_freeze.rb +82 -0
  19. data/lib/pakyow/support/definable.rb +242 -0
  20. data/lib/pakyow/support/dependencies.rb +61 -0
  21. data/lib/pakyow/support/extension.rb +82 -0
  22. data/lib/pakyow/support/hookable.rb +227 -0
  23. data/lib/pakyow/support/indifferentize.rb +183 -0
  24. data/lib/pakyow/support/inflector.rb +13 -0
  25. data/lib/pakyow/support/inspectable.rb +88 -0
  26. data/lib/pakyow/support/logging.rb +31 -0
  27. data/lib/pakyow/support/makeable/object_maker.rb +30 -0
  28. data/lib/pakyow/support/makeable/object_name.rb +45 -0
  29. data/lib/pakyow/support/makeable/object_namespace.rb +28 -0
  30. data/lib/pakyow/support/makeable.rb +117 -0
  31. data/lib/pakyow/support/message_verifier.rb +74 -0
  32. data/lib/pakyow/support/path_version.rb +21 -0
  33. data/lib/pakyow/support/pipeline/object.rb +41 -0
  34. data/lib/pakyow/support/pipeline.rb +335 -0
  35. data/lib/pakyow/support/safe_string.rb +60 -0
  36. data/lib/pakyow/support/serializer.rb +49 -0
  37. data/lib/pakyow/support/silenceable.rb +21 -0
  38. data/lib/pakyow/support/string_builder.rb +62 -0
  39. data/lib/pakyow/support.rb +1 -0
  40. metadata +107 -26
  41. data/pakyow-support/LICENSE +0 -20
  42. data/pakyow-support/lib/pakyow/support/aargv.rb +0 -15
  43. data/pakyow-support/lib/pakyow/support/array.rb +0 -9
  44. data/pakyow-support/lib/pakyow/support/dir.rb +0 -32
  45. data/pakyow-support/lib/pakyow/support/dup.rb +0 -23
  46. data/pakyow-support/lib/pakyow/support/file.rb +0 -5
  47. data/pakyow-support/lib/pakyow/support/hash.rb +0 -47
  48. data/pakyow-support/lib/pakyow/support/kernel.rb +0 -9
  49. data/pakyow-support/lib/pakyow/support/string.rb +0 -36
  50. data/pakyow-support/lib/pakyow/support.rb +0 -8
  51. data/pakyow-support/lib/pakyow-support.rb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0b9187d1b88df074e68ff932f8fe11dc9dcc004f
4
- data.tar.gz: 80b3d17712390927a743c3ed0db621b89089cc69
2
+ SHA256:
3
+ metadata.gz: 7f55f83e04891fdd69aaafe9d59ab21d250cd6fcdc37aa2fbfcb0a8eaad576be
4
+ data.tar.gz: 980a9b0005d1f5432b995974c3065c4ab2a9484d7ad42c0d7cb2f4b10a3e655d
5
5
  SHA512:
6
- metadata.gz: 1ccdcd7d1049dab1831833765ea44cb76790b033660a47de5f2ff79bbe9dee409a11a577cf0a178673e4a522b942d086c937558d086923d472dc426b28e9ee11
7
- data.tar.gz: a692eb81c1cb1b5b9488f7e90179c2b402df348ec4bc1495be0649959b384365ef525e06347b44e8990da6d08512e87e9cb342ae9bb0b2f4a5feb9459e8a99aa
6
+ metadata.gz: 6563a073ab30d40996accbae5e9aa5f8f4dcd6adb3a63b02dc38ffbd51a604aaccc0608d82edc7dd74571fd7b6c44b580c29c70dffc1171249cecabf9f9936f6
7
+ data.tar.gz: 30c696d99b2afa619298d7ea5bac825442f9578ed738c3180d4973f94058d0bf6033b3341cc5a476da2ec65071ddda0436b39bd40c32d2440d6c17649aa9de36
@@ -1,3 +1,7 @@
1
+ # 1.0.0
2
+
3
+ * ADDED `Hookable` module
4
+
1
5
  # 0.11.0
2
6
 
3
7
  * Moves everything into the Pakyow namespace
data/LICENSE ADDED
@@ -0,0 +1,4 @@
1
+ Copyright (c) Metabahn, LLC
2
+
3
+ Pakyow Support is an open-source project licensed under the terms of the LGPLv3 license.
4
+ See <https://choosealicense.com/licenses/lgpl-3.0/> for license text.
@@ -16,8 +16,7 @@ Source code can be downloaded as part of the Pakyow project on Github:
16
16
 
17
17
  # License
18
18
 
19
- Pakyow Support is released free and open-source under the [MIT
20
- License](http://opensource.org/licenses/MIT).
19
+ Pakyow Support is free and open-source under the [LGPLv3 license](https://choosealicense.com/licenses/lgpl-3.0/).
21
20
 
22
21
  # Support
23
22
 
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pakyow/support/core_refinements/array/ensurable"
4
+
5
+ module Pakyow
6
+ module Support
7
+ class Aargv
8
+ using Refinements::Array::Ensurable
9
+
10
+ def self.normalize(args, opts)
11
+ Hash[opts.map { |opt_name, opt_types|
12
+ [opt_name, value_of_type(args, Array.ensure(opt_types))]
13
+ }.reject { |pair| pair[1].nil? }]
14
+ end
15
+
16
+ def self.value_of_type(values, types)
17
+ if match = values.find { |value| types.find { |type| value.is_a?(type) } }
18
+ values.delete(match)
19
+ else
20
+ nil
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pakyow
4
+ module Support
5
+ # Makes an object bindable.
6
+ #
7
+ module Bindable
8
+ def include?(key)
9
+ respond_to?(key.to_s.to_sym)
10
+ end
11
+
12
+ def [](key)
13
+ if include?(key)
14
+ public_send(key.to_s.to_sym)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pakyow/support/deep_dup"
4
+
5
+ module Pakyow
6
+ module Support
7
+ module ClassState
8
+ using DeepDup
9
+
10
+ def class_state(name, default: nil, inheritable: false, getter: true)
11
+ ivar = :"@#{name}"
12
+ @__class_state[ivar] = {
13
+ default: default,
14
+ inheritable: inheritable
15
+ }
16
+
17
+ instance_variable_set(ivar, default.deep_dup)
18
+
19
+ if getter
20
+ define_singleton_method name do
21
+ instance_variable_get(ivar)
22
+ end
23
+ end
24
+ end
25
+
26
+ def self.extended(base)
27
+ unless base.instance_variable_defined?(:@__class_state)
28
+ base.instance_variable_set(:@__class_state, {})
29
+ end
30
+ end
31
+
32
+ def inherited(subclass)
33
+ subclass.instance_variable_set(:@__class_state, @__class_state.deep_dup)
34
+
35
+ @__class_state.each do |ivar, options|
36
+ if options[:inheritable]
37
+ subclass.instance_variable_set(ivar, instance_variable_get(ivar).deep_dup)
38
+ elsif @__class_state[ivar][:default]
39
+ subclass.instance_variable_set(ivar, @__class_state[ivar][:default].deep_dup)
40
+ else
41
+ subclass.instance_variable_set(ivar, nil)
42
+ end
43
+ end
44
+
45
+ super
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-command"
4
+ require "tty-spinner"
5
+
6
+ require "pakyow/support/cli/style"
7
+
8
+ module Pakyow
9
+ module Support
10
+ # Runs a command, or block of code, with consistent command-line messaging.
11
+ #
12
+ module CLI
13
+ class Runner
14
+ SPINNER = :dots
15
+ FAILURE_MARK = "✕"
16
+ SUCCESS_MARK = "✓"
17
+ FAILURE_MESSAGE = "failed"
18
+ SUCCESS_MESSAGE = ""
19
+
20
+ def initialize(message:)
21
+ @spinner = TTY::Spinner.new(
22
+ Support::CLI.style.bold(":spinner #{message}"),
23
+ format: SPINNER,
24
+ success_mark: SUCCESS_MARK,
25
+ error_mark: FAILURE_MARK
26
+ )
27
+
28
+ @succeeded = @failed = false
29
+ end
30
+
31
+ # Runs a command or block of code. If a value for `command` is passed with
32
+ # the block, the result will be yielded to the block on success.
33
+ #
34
+ def run(*command)
35
+ @spinner.auto_spin
36
+
37
+ if command.empty? && block_given?
38
+ yield self
39
+ succeeded
40
+ else
41
+ result = TTY::Command.new(printer: :null, pty: true).run!(*command)
42
+
43
+ if result.failure?
44
+ failed(result.err)
45
+ else
46
+ succeeded
47
+
48
+ if block_given?
49
+ yield result
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ # Called when the command succeeds.
56
+ #
57
+ def succeeded(output = "")
58
+ unless completed?
59
+ @succeeded = true
60
+ @spinner.success(Support::CLI.style.green(SUCCESS_MESSAGE))
61
+ puts indent_output(output) unless output.empty?
62
+ end
63
+ end
64
+
65
+ # Called when the command fails.
66
+ #
67
+ def failed(output = "")
68
+ unless completed?
69
+ @failed = true
70
+ @spinner.error(Support::CLI.style.red(FAILURE_MESSAGE))
71
+ puts indent_output(output) unless output.empty?
72
+ end
73
+ end
74
+
75
+ # Returns `true` if the command has completed.
76
+ #
77
+ def completed?
78
+ succeeded? || failed?
79
+ end
80
+
81
+ # Returns `true` if the command has completed successfully.
82
+ #
83
+ def succeeded?
84
+ @succeeded == true
85
+ end
86
+
87
+ # Returns `true` if the command has completed unsuccessfully.
88
+ #
89
+ def failed?
90
+ @failed == true
91
+ end
92
+
93
+ private
94
+
95
+ ANSI_REGEX = /\x1B\[[0-9;]*[a-zA-Z]/
96
+
97
+ def indent_output(output)
98
+ output.split("\n").map { |line|
99
+ first_real_string = line.split(ANSI_REGEX).reject(&:empty?).first
100
+ line.sub(first_real_string.to_s, " #{first_real_string}")
101
+ }.join("\n")
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pastel"
4
+
5
+ module Pakyow
6
+ module Support
7
+ module CLI
8
+ def self.style
9
+ @style ||= Pastel.new
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/hash"
4
+
5
+ require "pakyow/support/deep_dup"
6
+ require "pakyow/support/deep_freeze"
7
+
8
+ require "pakyow/support/configurable/setting"
9
+
10
+ module Pakyow
11
+ module Support
12
+ module Configurable
13
+ # @api private
14
+ class Config
15
+ using DeepDup
16
+
17
+ extend DeepFreeze
18
+ unfreezable :configurable
19
+
20
+ # @api private
21
+ attr_reader :__settings, :__defaults, :__groups
22
+
23
+ def initialize(configurable)
24
+ @configurable = configurable
25
+
26
+ @__settings = Concurrent::Hash.new
27
+ @__defaults = Concurrent::Hash.new
28
+ @__groups = Concurrent::Hash.new
29
+ end
30
+
31
+ def initialize_copy(_)
32
+ @__defaults = @__defaults.deep_dup
33
+ @__settings = @__settings.deep_dup
34
+ @__groups = @__groups.deep_dup
35
+
36
+ @__settings.each do |key, _|
37
+ define_setting_methods(key)
38
+ end
39
+
40
+ @__groups.each do |key, _|
41
+ define_group_methods(key)
42
+ end
43
+
44
+ super
45
+ end
46
+
47
+ def setting(name, default = default_omitted = true, &block)
48
+ tap do
49
+ name = name.to_sym
50
+ default = nil if default_omitted
51
+
52
+ unless @__settings.include?(name)
53
+ define_setting_methods(name)
54
+ end
55
+
56
+ @__settings[name] = Setting.new(default: default, configurable: @configurable, &block)
57
+ end
58
+ end
59
+
60
+ def defaults(environment, &block)
61
+ @__defaults[environment] = block
62
+ end
63
+
64
+ def configurable(group, &block)
65
+ group = group.to_sym
66
+ config = Config.new(@configurable)
67
+ config.instance_eval(&block)
68
+
69
+ unless @__groups.include?(group)
70
+ define_group_methods(group)
71
+ end
72
+
73
+ @__groups[group] = config
74
+ end
75
+
76
+ def configure_defaults!(configured_environment)
77
+ if defaults = @__defaults[configured_environment.to_s.to_sym]
78
+ instance_eval(&defaults)
79
+ end
80
+
81
+ @__groups.values.each do |group|
82
+ group.configure_defaults!(configured_environment)
83
+ end
84
+ end
85
+
86
+ def update_configurable(configurable)
87
+ @configurable = configurable
88
+
89
+ @__settings.values.each do |setting|
90
+ setting.update_configurable(configurable)
91
+ end
92
+
93
+ @__groups.values.each do |group|
94
+ group.update_configurable(configurable)
95
+ end
96
+ end
97
+
98
+ def to_h
99
+ hash = {}
100
+
101
+ @__settings.each_with_object(hash) { |(name, setting), h|
102
+ h[name] = setting.value
103
+ }
104
+
105
+ @__groups.each_with_object(hash) { |(name, group), h|
106
+ h[name] = group.to_h
107
+ }
108
+
109
+ hash
110
+ end
111
+
112
+ def eval(setting, context)
113
+ value = public_send(setting)
114
+ if value.is_a?(Proc)
115
+ context.instance_eval(&value)
116
+ else
117
+ value
118
+ end
119
+ end
120
+
121
+ private
122
+
123
+ def find_setting(name)
124
+ @__settings[name.to_sym]
125
+ end
126
+
127
+ def find_group(name)
128
+ @__groups[name.to_sym]
129
+ end
130
+
131
+ def define_setting_methods(name)
132
+ singleton_class.define_method name do |&block|
133
+ if block
134
+ find_setting(name).set(block)
135
+ else
136
+ find_setting(name).value
137
+ end
138
+ end
139
+
140
+ singleton_class.define_method :"#{name}=" do |value|
141
+ find_setting(name).set(value)
142
+ end
143
+ end
144
+
145
+ def define_group_methods(name)
146
+ singleton_class.define_method name do
147
+ find_group(name)
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pakyow/support/deep_dup"
4
+ require "pakyow/support/deep_freeze"
5
+
6
+ module Pakyow
7
+ module Support
8
+ module Configurable
9
+ # @api private
10
+ class Setting
11
+ using DeepDup
12
+
13
+ extend DeepFreeze
14
+ unfreezable :configurable, :value
15
+
16
+ def initialize(default:, configurable:, &block)
17
+ @default, @block, @configurable = default, block, configurable
18
+ end
19
+
20
+ def initialize_copy(_)
21
+ @default = @default.deep_dup
22
+ super
23
+ end
24
+
25
+ def freeze
26
+ value
27
+ super
28
+ end
29
+
30
+ def set(value)
31
+ @value = value
32
+ end
33
+
34
+ def value
35
+ if instance_variable_defined?(:@value)
36
+ @value
37
+ else
38
+ @value = if @block
39
+ @configurable.instance_eval(&@block)
40
+ else
41
+ @default
42
+ end
43
+ end
44
+ end
45
+
46
+ def update_configurable(configurable)
47
+ @configurable = configurable
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/hash"
4
+
5
+ require "pakyow/support/class_state"
6
+
7
+ require "pakyow/support/configurable/config"
8
+
9
+ module Pakyow
10
+ module Support
11
+ # Makes an object configurable.
12
+ #
13
+ # class ConfigurableObject
14
+ # include Configurable
15
+ #
16
+ # setting :foo, "default"
17
+ # setting :bar
18
+ #
19
+ # defaults :development do
20
+ # setting :bar, "bar"
21
+ # end
22
+ # end
23
+ #
24
+ # class ConfigurableSubclass < ConfigurableObject
25
+ # configure :development do
26
+ # config.foo = "development"
27
+ # end
28
+ #
29
+ # configure :production do
30
+ # config.foo = "production"
31
+ # end
32
+ # end
33
+ #
34
+ # instance = ConfigurableSubclass.new
35
+ # instance.configure! :development
36
+ #
37
+ # instance.config.foo
38
+ # # => "development"
39
+ # instance.config.bar
40
+ # # => "bar"
41
+ #
42
+ module Configurable
43
+ # @api private
44
+ def self.included(base)
45
+ base.extend ClassState
46
+ base.class_state :__config, default: Config.new(base), inheritable: true
47
+ base.class_state :__config_environments, default: Concurrent::Hash.new, inheritable: true
48
+
49
+ base.prepend Initializer
50
+ base.include CommonMethods
51
+ base.extend ClassMethods, CommonMethods
52
+ end
53
+
54
+ private def __config_environments
55
+ self.class.__config_environments
56
+ end
57
+
58
+ module Initializer
59
+ # @api private
60
+ def initialize(*)
61
+ @__config = self.class.__config.dup
62
+ @__config.update_configurable(self)
63
+ super
64
+ end
65
+ end
66
+
67
+ module ClassMethods
68
+ # Define configuration to be applied when configuring for an environment.
69
+ #
70
+ def configure(environment = :__global, &block)
71
+ @__config_environments[environment] = block
72
+ end
73
+
74
+ # @api private
75
+ def inherited(subclass)
76
+ super
77
+ subclass.config.update_configurable(subclass)
78
+ end
79
+ end
80
+
81
+ module CommonMethods
82
+ extend Forwardable
83
+ def_delegators :@__config, :setting, :defaults, :configurable
84
+
85
+ def config
86
+ @__config
87
+ end
88
+
89
+ # Configures the object for an environment.
90
+ #
91
+ def configure!(configured_environment = nil)
92
+ @__config.configure_defaults!(configured_environment)
93
+
94
+ [:__global, configured_environment].compact.map(&:to_sym).select { |environment|
95
+ __config_environments.key?(environment)
96
+ }.each do |environment|
97
+ instance_eval(&__config_environments[environment])
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pakyow
4
+ module Support
5
+ module Refinements
6
+ module Array
7
+ module Ensurable
8
+ refine ::Array.singleton_class do
9
+ # Ensures that +object+ is an array, converting it if necessary. This
10
+ # was added to safely wrap hashes, because +Array(hash)+ converts
11
+ # into an array of key/value pairs.
12
+ #
13
+ def ensure(object)
14
+ if object.respond_to?(:to_ary)
15
+ object.to_ary
16
+ else
17
+ [object]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pakyow
4
+ module Support
5
+ module Refinements
6
+ module Method
7
+ module Introspection
8
+ refine ::Method do
9
+ # Returns true if +argument_name+ is defined as a keyword argument.
10
+ #
11
+ def keyword_argument?(argument_name)
12
+ parameters.any? { |(parameter_type, parameter_name)|
13
+ (parameter_type == :key || parameter_type == :keyreq) && parameter_name == argument_name
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pakyow
4
+ module Support
5
+ module Refinements
6
+ module Proc
7
+ module Introspection
8
+ refine ::Proc do
9
+ # Returns true if +argument_name+ is defined as a keyword argument.
10
+ #
11
+ def keyword_argument?(argument_name)
12
+ parameters.any? { |(parameter_type, parameter_name)|
13
+ (parameter_type == :key || parameter_type == :keyreq) && parameter_name == argument_name
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pakyow
4
+ module Support
5
+ module Refinements
6
+ module String
7
+ module Normalization
8
+ refine ::String.singleton_class do
9
+ # Normalizes a string into a predictable path.
10
+ #
11
+ # String.normalize_path("foo//bar/")
12
+ # # => "/foo/bar"
13
+ #
14
+ def normalize_path(path)
15
+ path = path.to_s
16
+
17
+ unless path.start_with?("/")
18
+ path = "/#{path}"
19
+ end
20
+
21
+ if path.include?("//")
22
+ path = path.to_s.gsub("//", "/")
23
+ end
24
+
25
+ unless path == "/"
26
+ path = path.chomp("/")
27
+ end
28
+
29
+ path
30
+ end
31
+
32
+ # Collapses a string into a version without tokens.
33
+ #
34
+ # String.collapse_path("/foo/:bar/baz")
35
+ # # => "/foo/baz"
36
+ def collapse_path(path)
37
+ if path == "/"
38
+ return path
39
+ end
40
+
41
+ path.to_s.split("/").keep_if { |part|
42
+ part[0] != ":"
43
+ }.join("/")
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end