kangaru 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f81016147f4602231fda1758cc6064101872f31d56d65aa830d3917caef5fae
4
- data.tar.gz: 117244d96c1de48b808bcd8a68810d1582d39f60e26969e393057297cb711363
3
+ metadata.gz: db7c4419e9f9d9661c4d12498fcb5450cde2f209958f0bb7249a39dec4a93b54
4
+ data.tar.gz: 43b3237bf249c4c4a3ef59eddbd4328a7b03a0084d9c69296557bab93d0b1492
5
5
  SHA512:
6
- metadata.gz: c4eb18b51418ae6c2286f7091e18335e8d7f710bd405c36fa190ddf5ecbd0d3a55107e1e6ec25fc3dae5be46774e6531b692e2d1c207660a0aef4aa87eefc5f9
7
- data.tar.gz: 0fa8ecd03c0ac6d392c3e4555e0edc62dac602814707e781b6bc971f20ca85cf968937bddb281cb8d7716de521dc0673672852c51a49858ba48a3b31faa8548e
6
+ metadata.gz: c2c5dff9c5ecd0a4401261203f6c89d5837b7824dae3e88d8dd1ad7683f7ae9b65200892d81f4d20d181acca253c5b4dab9b48c24dae32c069dd3ddcea8fd5d9
7
+ data.tar.gz: 6b1c7bbef5ef7a08437b5bd33b7fcfa8d2106bd29b52f8df7e56a228caff297fd22df609b96d8a6828390d6bc503bf4105fab52064efb1b4e146866e81a30162
data/.rubocop.yml CHANGED
@@ -73,3 +73,5 @@ RSpec/NestedGroups:
73
73
  Enabled: false
74
74
  RSpec/SharedExamples:
75
75
  Enabled: false
76
+ RSpec/ExampleLength:
77
+ Max: 7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kangaru (0.1.1)
4
+ kangaru (0.2.0)
5
5
  colorize (~> 1.1)
6
6
  erb (~> 4.0)
7
7
  sequel (~> 5.72)
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Kangaru is an open-source framework written in Ruby for building powerful and efficient command line applications. This project aims to make it easier for developers to create complex CLI programs with minimal effort, by providing a set of useful tools and libraries in a configurable ecosystem.
4
4
 
5
- > Note: This software is currently in beta mode, meaning it may contain bugs and be subject to changes. Please exercise caution when using this software and report any issues you encounter. The first production-ready release of Kangaru will be version 0.2.0.
5
+ > Note: This software is currently in beta mode, meaning it may contain bugs and be subject to changes. Please exercise caution when using this software and report any issues you encounter. The first production-ready release of Kangaru will be version 1.0.0.
6
6
 
7
7
  ## Features
8
8
 
data/kangaru.gemspec CHANGED
@@ -7,6 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.email = ["71787007+apexatoll@users.noreply.github.com"]
8
8
 
9
9
  spec.summary = "A lightweight framework for building command line interfaces"
10
+ spec.homepage = "https://github.com/apexatoll/kangaru"
10
11
  spec.license = "MIT"
11
12
  spec.required_ruby_version = ">= 3.2.0"
12
13
 
@@ -15,6 +16,11 @@ Gem::Specification.new do |spec|
15
16
  f == __FILE__ || f.match?(/\A(bin|spec|\.git|\.github)/)
16
17
  end
17
18
  end
19
+
20
+ spec.metadata["homepage_uri"] = spec.homepage
21
+ spec.metadata["source_code_uri"] = spec.homepage
22
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/releases"
23
+
18
24
  spec.bindir = "bin"
19
25
  spec.executables = []
20
26
  spec.require_paths = ["lib"]
@@ -2,8 +2,12 @@ module Kangaru
2
2
  class Application
3
3
  extend Forwardable
4
4
 
5
+ class InvalidConfigError < StandardError; end
6
+
5
7
  attr_reader :paths, :namespace, :database
6
8
 
9
+ attr_accessor :config_path
10
+
7
11
  def initialize(source:, namespace:)
8
12
  @paths = Paths.new(source:)
9
13
  @namespace = namespace
@@ -34,7 +38,9 @@ module Kangaru
34
38
  def apply_config!
35
39
  raise "config already applied" if configured?
36
40
 
37
- config.import_external_config!
41
+ config.import!(config_path) unless config_path.nil?
42
+
43
+ validate_config!
38
44
 
39
45
  @database = setup_database!
40
46
  @configured = true
@@ -46,6 +52,12 @@ module Kangaru
46
52
  router.resolve(request)
47
53
  end
48
54
 
55
+ def const_get(const)
56
+ return unless namespace.const_defined?(const)
57
+
58
+ namespace.const_get(const)
59
+ end
60
+
49
61
  def_delegators :paths, :view_path
50
62
 
51
63
  private
@@ -65,6 +77,14 @@ module Kangaru
65
77
  end
66
78
  end
67
79
 
80
+ def validate_config!
81
+ return if config.valid?
82
+
83
+ message = config.errors.map(&:full_message).join(", ")
84
+
85
+ raise InvalidConfigError, message
86
+ end
87
+
68
88
  def setup_database!
69
89
  return unless config.database.adaptor
70
90
 
@@ -22,6 +22,10 @@ module Kangaru
22
22
  def initialize(**attributes)
23
23
  attributes = self.class.defaults.merge(**attributes)
24
24
 
25
+ merge!(**attributes)
26
+ end
27
+
28
+ def merge!(**attributes)
25
29
  attributes.slice(*self.class.attributes).each do |attr, value|
26
30
  instance_variable_set(:"@#{attr}", value)
27
31
  end
@@ -3,18 +3,19 @@ module Kangaru
3
3
  module Configurable
4
4
  extend Concern
5
5
 
6
- using Patches::Constantise
6
+ using Patches::Inflections
7
7
 
8
8
  class_methods do
9
- def configurator_name
9
+ def configurator_key
10
10
  (name || raise("class name not set"))
11
- .gsub(/^(.*?)::/, "\\1::Configurators::")
12
- .concat("Configurator")
11
+ .gsub(/^.*::/, "")
12
+ .to_snakecase
13
+ .to_sym
13
14
  end
14
15
  end
15
16
 
16
17
  def config
17
- Kangaru.application!.config.for(self.class.configurator_name) ||
18
+ Kangaru.application!.config[self.class.configurator_key] ||
18
19
  raise("inferred configurator not set by application")
19
20
  end
20
21
  end
@@ -1,27 +1,38 @@
1
1
  module Kangaru
2
2
  class Config
3
+ using Patches::Symboliser
4
+
3
5
  attr_reader :configurators
4
6
 
5
7
  def initialize
6
8
  @configurators = set_configurators!
7
9
  end
8
10
 
11
+ def import!(path)
12
+ read_external_config(path).each do |key, config|
13
+ configurators[key]&.merge!(**config)
14
+ end
15
+ end
16
+
9
17
  def serialise
10
18
  configurators.transform_values(&:serialise)
11
19
  end
12
20
 
13
- def import_external_config!
14
- return unless external_config_exists?
21
+ def [](key)
22
+ configurators[key.to_sym]
23
+ end
15
24
 
16
- @external = Configurators::ExternalConfigurator
17
- .from_yaml_file(application.config_path)
25
+ def valid?
26
+ validate
27
+ errors.empty?
18
28
  end
19
29
 
20
- # Returns the configurator instance with the given class name.
21
- def for(configurator_name)
22
- configurators.values.find do |configurator|
23
- configurator.class.name == configurator_name # rubocop:disable Style
24
- end
30
+ def validate
31
+ configurators.each_value(&:validate)
32
+ end
33
+
34
+ def errors
35
+ configurators.values.flat_map(&:errors)
25
36
  end
26
37
 
27
38
  private
@@ -38,8 +49,10 @@ module Kangaru
38
49
  end
39
50
  end
40
51
 
41
- def external_config_exists?
42
- application.config_path && File.exist?(application.config_path)
52
+ def read_external_config(path)
53
+ return {} unless File.exist?(path)
54
+
55
+ YAML.load_file(path)&.symbolise || {}
43
56
  end
44
57
  end
45
58
  end
@@ -0,0 +1,21 @@
1
+ module Kangaru
2
+ class Configurator
3
+ include Concerns::AttributesConcern
4
+ include Concerns::Validatable
5
+
6
+ using Patches::Inflections
7
+
8
+ def self.key
9
+ to_s.gsub(/^.*::(?!.*::)/, "")
10
+ .delete_suffix("Configurator")
11
+ .to_snakecase
12
+ .to_sym
13
+ end
14
+
15
+ def serialise
16
+ self.class.attributes.to_h do |setting|
17
+ [setting, send(setting)]
18
+ end.compact
19
+ end
20
+ end
21
+ end
@@ -1,12 +1,21 @@
1
1
  module Kangaru
2
2
  module Configurators
3
- # These are not set as accessors by Config instances as they are abstract.
4
- BASE_CONFIGURATORS = [Configurator, OpenConfigurator].freeze
5
-
6
3
  def self.classes
7
- constants.map { |constant| const_get(constant) }
8
- .select { |constant| constant.is_a?(Class) }
9
- .reject { |constant| BASE_CONFIGURATORS.include?(constant) }
4
+ load_classes(self) + load_classes(application_configurators)
5
+ end
6
+
7
+ def self.application_configurators
8
+ Kangaru.application&.const_get(:Configurators)
10
9
  end
10
+
11
+ def self.load_classes(root)
12
+ return [] if root.nil?
13
+
14
+ root.constants.map { |const| root.const_get(const) }
15
+ .select { |const| const.is_a?(Class) }
16
+ end
17
+
18
+ private_class_method :application_configurators
19
+ private_class_method :load_classes
11
20
  end
12
21
  end
@@ -2,8 +2,6 @@ module Kangaru
2
2
  module Initialisers
3
3
  module RSpec
4
4
  module RequestHelper
5
- extend ::RSpec::Matchers::DSL
6
-
7
5
  attr_reader :request
8
6
 
9
7
  def stub_output(&block)
@@ -28,24 +26,28 @@ module Kangaru
28
26
  Kangaru.application!.view_path(described_class.path, name.to_s)
29
27
  end
30
28
 
31
- matcher :render_template do |name|
32
- supports_block_expectations
29
+ if Object.const_defined?(:RSpec)
30
+ extend ::RSpec::Matchers::DSL
31
+
32
+ matcher :render_template do |name|
33
+ supports_block_expectations
33
34
 
34
- match do |action|
35
- renderer = instance_double(Kangaru::Renderer, render: nil)
36
- allow(Kangaru::Renderer).to receive(:new).and_return(renderer)
37
- expect(Kangaru::Renderer).not_to have_received(:new)
35
+ match do |action|
36
+ renderer = instance_double(Kangaru::Renderer, render: nil)
37
+ allow(Kangaru::Renderer).to receive(:new).and_return(renderer)
38
+ expect(Kangaru::Renderer).not_to have_received(:new)
38
39
 
39
- action.call
40
+ action.call
40
41
 
41
- expect(Kangaru::Renderer)
42
- .to have_received(:new)
43
- .with(view_path(name))
44
- .once
42
+ expect(Kangaru::Renderer)
43
+ .to have_received(:new)
44
+ .with(view_path(name))
45
+ .once
45
46
 
46
- expect(renderer)
47
- .to have_received(:render)
48
- .once
47
+ expect(renderer)
48
+ .to have_received(:render)
49
+ .once
50
+ end
49
51
  end
50
52
  end
51
53
  end
@@ -12,6 +12,10 @@ module Kangaru
12
12
  Kangaru.application!.configure(env, &)
13
13
  end
14
14
 
15
+ def import_config_from!(path)
16
+ Kangaru.application!.config_path = path
17
+ end
18
+
15
19
  def apply_config!
16
20
  Kangaru.application!.apply_config!
17
21
  end
@@ -1,3 +1,3 @@
1
1
  module Kangaru
2
- VERSION = "0.1.1".freeze
2
+ VERSION = "0.2.0".freeze
3
3
  end
@@ -2,10 +2,16 @@ module Kangaru
2
2
  class Application
3
3
  extend Forwardable
4
4
 
5
+ class InvalidConfigError < StandardError
6
+ end
7
+
5
8
  attr_reader paths: Paths
6
9
  attr_reader namespace: Module
7
- attr_reader config: Config
8
10
  attr_reader database: Database?
11
+
12
+ attr_accessor config_path: String
13
+
14
+ attr_reader config: Config
9
15
  attr_reader router: Router
10
16
 
11
17
  def initialize: (source: String, namespace: Module) -> void
@@ -18,6 +24,9 @@ module Kangaru
18
24
 
19
25
  def run!: (*String) -> void
20
26
 
27
+ def const_get: (String | Symbol) -> Module?
28
+ | (String | Symbol) -> Class?
29
+
21
30
  # Delegated to paths
22
31
  def view_path: (*String, ?ext: Symbol?) -> Pathname
23
32
 
@@ -29,6 +38,8 @@ module Kangaru
29
38
 
30
39
  attr_reader autoloader: Zeitwerk::Loader
31
40
 
41
+ def validate_config!: -> void
42
+
32
43
  def setup_database!: -> Database?
33
44
  end
34
45
  end
@@ -20,6 +20,8 @@ module Kangaru
20
20
  def instance_methods: -> Array[Symbol]
21
21
 
22
22
  def initialize: (**untyped) -> void
23
+
24
+ def merge!: (**untyped) -> void
23
25
  end
24
26
  end
25
27
  end
@@ -5,11 +5,11 @@ module Kangaru
5
5
  extend ClassMethods
6
6
 
7
7
  module ClassMethods
8
- def configurator_name: -> String
8
+ def configurator_key: -> Symbol
9
9
  end
10
10
 
11
11
  def name: -> String
12
- def config: [C < Configurators::Configurator] -> C
12
+ def config: -> untyped
13
13
  end
14
14
  end
15
15
  end
@@ -1,27 +1,29 @@
1
1
  module Kangaru
2
2
  class Config
3
- type configurator = Configurators::Configurator
4
-
5
- attr_reader configurators: Hash[Symbol, configurator]
3
+ attr_reader configurators: Hash[Symbol, Configurator]
6
4
 
7
5
  def initialize: -> void
8
6
 
7
+ def import!: (String) -> void
8
+
9
9
  def serialise: -> Hash[Symbol, Hash[Symbol, untyped]]
10
10
 
11
- def import_external_config!: -> void
11
+ def []: (Symbol) -> Configurator?
12
+
13
+ def valid?: -> bool
14
+
15
+ def validate: -> void
12
16
 
13
- def for: (String) -> untyped
17
+ def errors: -> Array[Validation::Error]
14
18
 
15
- # Configurators
16
- attr_reader application: Configurators::ApplicationConfigurator
17
- attr_reader database: Configurators::DatabaseConfigurator
18
- attr_reader external: Configurators::ExternalConfigurator
19
- attr_reader request: Configurators::RequestConfigurator
19
+ # Native Configurators
20
+ attr_reader database: Configurators::DatabaseConfigurator
21
+ attr_reader request: Configurators::RequestConfigurator
20
22
 
21
23
  private
22
24
 
23
- def set_configurators!: -> Hash[Symbol, configurator]
25
+ def set_configurators!: -> Hash[Symbol, Configurator]
24
26
 
25
- def external_config_exists?: -> bool
27
+ def read_external_config: (String) -> Hash[Symbol, untyped]
26
28
  end
27
29
  end
@@ -0,0 +1,15 @@
1
+ module Kangaru
2
+ class Configurator
3
+ include Concerns::AttributesConcern
4
+ extend Concerns::AttributesConcern::ClassMethods
5
+
6
+ include Concerns::Validatable
7
+ extend Concerns::Validatable::ClassMethods
8
+
9
+ def self.key: -> Symbol
10
+
11
+ def self.name: -> String
12
+
13
+ def serialise: -> Hash[Symbol, untyped]
14
+ end
15
+ end
@@ -1,7 +1,11 @@
1
1
  module Kangaru
2
2
  module Configurators
3
- BASE_CONFIGURATORS: Array[singleton(Configurator)]
4
-
5
3
  def self.classes: -> Array[singleton(Configurator)]
4
+
5
+ private
6
+
7
+ def self.application_configurators: -> Module?
8
+
9
+ def self.load_classes: (Module?) -> Array[singleton(Configurator)]
6
10
  end
7
11
  end
@@ -9,6 +9,8 @@ module Kangaru
9
9
 
10
10
  def apply_config!: -> void
11
11
 
12
+ def import_config_from!: (String) -> void
13
+
12
14
  def database: -> Database?
13
15
  end
14
16
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kangaru
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Welham
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-08 00:00:00.000000000 Z
11
+ date: 2023-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -107,12 +107,9 @@ files:
107
107
  - lib/kangaru/concerns/configurable.rb
108
108
  - lib/kangaru/concerns/validatable.rb
109
109
  - lib/kangaru/config.rb
110
+ - lib/kangaru/configurator.rb
110
111
  - lib/kangaru/configurators.rb
111
- - lib/kangaru/configurators/application_configurator.rb
112
- - lib/kangaru/configurators/configurator.rb
113
112
  - lib/kangaru/configurators/database_configurator.rb
114
- - lib/kangaru/configurators/external_configurator.rb
115
- - lib/kangaru/configurators/open_configurator.rb
116
113
  - lib/kangaru/configurators/request_configurator.rb
117
114
  - lib/kangaru/controller.rb
118
115
  - lib/kangaru/database.rb
@@ -159,12 +156,9 @@ files:
159
156
  - sig/kangaru/concerns/configurable.rbs
160
157
  - sig/kangaru/concerns/validatable.rbs
161
158
  - sig/kangaru/config.rbs
159
+ - sig/kangaru/configurator.rbs
162
160
  - sig/kangaru/configurators.rbs
163
- - sig/kangaru/configurators/application_configurator.rbs
164
- - sig/kangaru/configurators/configurator.rbs
165
161
  - sig/kangaru/configurators/database_configurator.rbs
166
- - sig/kangaru/configurators/external_configurator.rbs
167
- - sig/kangaru/configurators/open_configurator.rbs
168
162
  - sig/kangaru/configurators/request_configurator.rbs
169
163
  - sig/kangaru/controller.rbs
170
164
  - sig/kangaru/database.rbs
@@ -196,10 +190,13 @@ files:
196
190
  - sig/kangaru/validation/error.rbs
197
191
  - sig/kangaru/validators/required_validator.rbs
198
192
  - sig/kangaru/validators/validator.rbs
199
- homepage:
193
+ homepage: https://github.com/apexatoll/kangaru
200
194
  licenses:
201
195
  - MIT
202
- metadata: {}
196
+ metadata:
197
+ homepage_uri: https://github.com/apexatoll/kangaru
198
+ source_code_uri: https://github.com/apexatoll/kangaru
199
+ changelog_uri: https://github.com/apexatoll/kangaru/releases
203
200
  post_install_message:
204
201
  rdoc_options: []
205
202
  require_paths:
@@ -1,7 +0,0 @@
1
- module Kangaru
2
- module Configurators
3
- class ApplicationConfigurator < Configurator
4
- attr_accessor :config_path
5
- end
6
- end
7
- end
@@ -1,22 +0,0 @@
1
- module Kangaru
2
- module Configurators
3
- class Configurator
4
- include Concerns::AttributesConcern
5
-
6
- using Patches::Inflections
7
-
8
- def self.key
9
- to_s.gsub(/^.*::(?!.*::)/, "")
10
- .delete_suffix("Configurator")
11
- .to_snakecase
12
- .to_sym
13
- end
14
-
15
- def serialise
16
- self.class.attributes.to_h do |setting|
17
- [setting, send(setting)]
18
- end.compact
19
- end
20
- end
21
- end
22
- end
@@ -1,6 +0,0 @@
1
- module Kangaru
2
- module Configurators
3
- class ExternalConfigurator < OpenConfigurator
4
- end
5
- end
6
- end
@@ -1,33 +0,0 @@
1
- # Similar to a standard configurator, except on initialisation, it will set
2
- # accessors for every attribute specified. This means that the super call will
3
- # lead to each value being set as if the accessor was defined in the class.
4
- module Kangaru
5
- module Configurators
6
- class OpenConfigurator < Configurator
7
- using Patches::Symboliser
8
-
9
- def initialize(**)
10
- set_accessors!(**)
11
-
12
- super
13
- end
14
-
15
- # Import contents of a yaml file
16
- def self.from_yaml_file(path)
17
- raise "path does not exist" unless File.exist?(path)
18
-
19
- attributes = YAML.load_file(path).symbolise
20
-
21
- new(**attributes)
22
- end
23
-
24
- private
25
-
26
- def set_accessors!(**attributes)
27
- attributes.each_key do |key|
28
- self.class.class_eval { attr_accessor key }
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,7 +0,0 @@
1
- module Kangaru
2
- module Configurators
3
- class ApplicationConfigurator < Configurator
4
- attr_accessor config_path: String
5
- end
6
- end
7
- end
@@ -1,14 +0,0 @@
1
- module Kangaru
2
- module Configurators
3
- class Configurator
4
- include Concerns::AttributesConcern
5
- extend Concerns::AttributesConcern::ClassMethods
6
-
7
- def self.key: -> Symbol
8
-
9
- def self.name: -> String
10
-
11
- def serialise: -> Hash[Symbol, untyped]
12
- end
13
- end
14
- end
@@ -1,6 +0,0 @@
1
- module Kangaru
2
- module Configurators
3
- class ExternalConfigurator < OpenConfigurator
4
- end
5
- end
6
- end
@@ -1,11 +0,0 @@
1
- module Kangaru
2
- module Configurators
3
- class OpenConfigurator < Configurator
4
- def self.from_yaml_file: (String) -> instance
5
-
6
- private
7
-
8
- def set_accessors!: (**untyped) -> void
9
- end
10
- end
11
- end