kangaru 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/.rubocop.yml +75 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +139 -0
- data/LICENSE.txt +21 -0
- data/README.md +49 -0
- data/Rakefile +10 -0
- data/Steepfile +12 -0
- data/kangaru.gemspec +27 -0
- data/lib/kangaru/application.rb +73 -0
- data/lib/kangaru/argument_parser.rb +34 -0
- data/lib/kangaru/components/component.rb +19 -0
- data/lib/kangaru/concerns/attributes_concern.rb +31 -0
- data/lib/kangaru/concerns/concern.rb +33 -0
- data/lib/kangaru/concerns/configurable.rb +22 -0
- data/lib/kangaru/concerns/validatable.rb +46 -0
- data/lib/kangaru/config.rb +45 -0
- data/lib/kangaru/configurators/application_configurator.rb +7 -0
- data/lib/kangaru/configurators/configurator.rb +22 -0
- data/lib/kangaru/configurators/database_configurator.rb +7 -0
- data/lib/kangaru/configurators/external_configurator.rb +6 -0
- data/lib/kangaru/configurators/open_configurator.rb +33 -0
- data/lib/kangaru/configurators/request_configurator.rb +7 -0
- data/lib/kangaru/configurators.rb +12 -0
- data/lib/kangaru/controller.rb +53 -0
- data/lib/kangaru/database.rb +54 -0
- data/lib/kangaru/inflectors/class_inflector.rb +13 -0
- data/lib/kangaru/inflectors/constant_inflector.rb +13 -0
- data/lib/kangaru/inflectors/constantiser.rb +18 -0
- data/lib/kangaru/inflectors/human_inflector.rb +15 -0
- data/lib/kangaru/inflectors/inflector.rb +98 -0
- data/lib/kangaru/inflectors/inflector_macros.rb +33 -0
- data/lib/kangaru/inflectors/path_inflector.rb +21 -0
- data/lib/kangaru/inflectors/screaming_snakecase_inflector.rb +11 -0
- data/lib/kangaru/inflectors/snakecase_inflector.rb +11 -0
- data/lib/kangaru/inflectors/tokeniser.rb +20 -0
- data/lib/kangaru/initialiser.rb +12 -0
- data/lib/kangaru/initialisers/rspec.rb +11 -0
- data/lib/kangaru/injected_methods.rb +23 -0
- data/lib/kangaru/model.rb +7 -0
- data/lib/kangaru/patches/constantise.rb +11 -0
- data/lib/kangaru/patches/inflections.rb +23 -0
- data/lib/kangaru/patches/source.rb +11 -0
- data/lib/kangaru/patches/symboliser.rb +29 -0
- data/lib/kangaru/path_parser.rb +35 -0
- data/lib/kangaru/paths.rb +93 -0
- data/lib/kangaru/renderer.rb +15 -0
- data/lib/kangaru/request.rb +40 -0
- data/lib/kangaru/request_builder.rb +33 -0
- data/lib/kangaru/router.rb +38 -0
- data/lib/kangaru/validation/attribute_validator.rb +32 -0
- data/lib/kangaru/validation/error.rb +32 -0
- data/lib/kangaru/validators/required_validator.rb +17 -0
- data/lib/kangaru/validators/validator.rb +22 -0
- data/lib/kangaru/version.rb +3 -0
- data/lib/kangaru.rb +58 -0
- data/rbs_collection.lock.yaml +83 -0
- data/rbs_collection.yaml +24 -0
- metadata +173 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 04e8f14e4f41534168d56fdc09364a9507553658887f513a1536fe71f6a9c608
|
4
|
+
data.tar.gz: 3af9ce12c3a133964cf66393f0d93339c37ae5329c3bd3ddcd65f53e1f224a28
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6c9ee4ea2cf630d10e52d9a0a549f0ce77c5751c0469ca523baa91e15aa7eaa2e479e6120a683ad6cfb7aed8a6987cd7209d11a51a1503daa4910721718df1d8
|
7
|
+
data.tar.gz: d7c959405a39a4086409f8e1a739643502ef102b662728dc1859e9bc3f8195cc576b9ee81f93135536141dbbd438e37047be08e1f15048fa18d7a32f1887a883
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rspec
|
3
|
+
|
4
|
+
AllCops:
|
5
|
+
NewCops: enable
|
6
|
+
SuggestExtensions: false
|
7
|
+
|
8
|
+
Gemspec/RequireMFA:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
Layout/ExtraSpacing:
|
12
|
+
AllowForAlignment: true
|
13
|
+
Layout/LineLength:
|
14
|
+
Max: 80
|
15
|
+
Layout/MultilineMethodCallIndentation:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Lint/AmbiguousBlockAssociation:
|
19
|
+
AllowedMethods:
|
20
|
+
- change
|
21
|
+
Lint/ConstantDefinitionInBlock:
|
22
|
+
Exclude:
|
23
|
+
- spec/spec_helper.rb
|
24
|
+
Lint/MissingSuper:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Naming/MethodParameterName:
|
28
|
+
AllowedNames:
|
29
|
+
- by
|
30
|
+
- to
|
31
|
+
- id
|
32
|
+
|
33
|
+
Style/ArgumentsForwarding:
|
34
|
+
Enabled: false
|
35
|
+
Style/CharacterLiteral:
|
36
|
+
Enabled: false
|
37
|
+
Style/Documentation:
|
38
|
+
Enabled: false
|
39
|
+
Style/ExplicitBlockArgument:
|
40
|
+
Enabled: false
|
41
|
+
Style/FormatStringToken:
|
42
|
+
EnforcedStyle: template
|
43
|
+
Style/FrozenStringLiteralComment:
|
44
|
+
Enabled: false
|
45
|
+
Style/RescueStandardError:
|
46
|
+
EnforcedStyle: implicit
|
47
|
+
Style/StringLiterals:
|
48
|
+
EnforcedStyle: double_quotes
|
49
|
+
|
50
|
+
RSpec/AnyInstance:
|
51
|
+
Enabled: false
|
52
|
+
RSpec/ContextWording:
|
53
|
+
Prefixes:
|
54
|
+
- when
|
55
|
+
- with
|
56
|
+
- without
|
57
|
+
- if
|
58
|
+
- unless
|
59
|
+
- and
|
60
|
+
- but
|
61
|
+
RSpec/DescribedClass:
|
62
|
+
SkipBlocks: true
|
63
|
+
RSpec/ExpectChange:
|
64
|
+
EnforcedStyle: block
|
65
|
+
RSpec/FilePath:
|
66
|
+
Exclude:
|
67
|
+
- spec/kangaru/components/**/*.rb
|
68
|
+
RSpec/LetSetup:
|
69
|
+
Enabled: false
|
70
|
+
RSpec/MultipleMemoizedHelpers:
|
71
|
+
Max: 15
|
72
|
+
RSpec/NestedGroups:
|
73
|
+
Enabled: false
|
74
|
+
RSpec/SharedExamples:
|
75
|
+
Enabled: false
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.2.0
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
gem "fileutils"
|
6
|
+
gem "forwardable"
|
7
|
+
gem "pathname"
|
8
|
+
gem "rake", "~> 13.0"
|
9
|
+
gem "rspec", "~> 3.0"
|
10
|
+
gem "rubocop", "~> 1.21"
|
11
|
+
gem "rubocop-rspec", "~> 2.23"
|
12
|
+
gem "steep", "~> 1.5"
|
13
|
+
gem "yaml"
|
14
|
+
gem "zirconia", "~> 0.1"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
kangaru (0.1.0)
|
5
|
+
colorize (~> 1.1)
|
6
|
+
erb (~> 4.0)
|
7
|
+
sequel (~> 5.72)
|
8
|
+
sqlite3 (~> 1.6)
|
9
|
+
zeitwerk (~> 2.6)
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: https://rubygems.org/
|
13
|
+
specs:
|
14
|
+
activesupport (7.0.7.2)
|
15
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
16
|
+
i18n (>= 1.6, < 2)
|
17
|
+
minitest (>= 5.1)
|
18
|
+
tzinfo (~> 2.0)
|
19
|
+
ast (2.4.2)
|
20
|
+
base64 (0.1.1)
|
21
|
+
bigdecimal (3.1.4)
|
22
|
+
cgi (0.3.6)
|
23
|
+
colorize (1.1.0)
|
24
|
+
concurrent-ruby (1.2.2)
|
25
|
+
csv (3.2.7)
|
26
|
+
diff-lcs (1.5.0)
|
27
|
+
erb (4.0.3)
|
28
|
+
cgi (>= 0.3.3)
|
29
|
+
ffi (1.15.5)
|
30
|
+
fileutils (1.7.1)
|
31
|
+
forwardable (1.3.3)
|
32
|
+
i18n (1.14.1)
|
33
|
+
concurrent-ruby (~> 1.0)
|
34
|
+
json (2.6.3)
|
35
|
+
language_server-protocol (3.17.0.3)
|
36
|
+
listen (3.8.0)
|
37
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
38
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
39
|
+
logger (1.5.3)
|
40
|
+
minitest (5.19.0)
|
41
|
+
parallel (1.23.0)
|
42
|
+
parser (3.2.2.3)
|
43
|
+
ast (~> 2.4.1)
|
44
|
+
racc
|
45
|
+
pathname (0.2.1)
|
46
|
+
racc (1.7.1)
|
47
|
+
rainbow (3.1.1)
|
48
|
+
rake (13.0.6)
|
49
|
+
rb-fsevent (0.11.2)
|
50
|
+
rb-inotify (0.10.1)
|
51
|
+
ffi (~> 1.0)
|
52
|
+
rbs (3.2.1)
|
53
|
+
regexp_parser (2.8.1)
|
54
|
+
rexml (3.2.6)
|
55
|
+
rspec (3.12.0)
|
56
|
+
rspec-core (~> 3.12.0)
|
57
|
+
rspec-expectations (~> 3.12.0)
|
58
|
+
rspec-mocks (~> 3.12.0)
|
59
|
+
rspec-core (3.12.2)
|
60
|
+
rspec-support (~> 3.12.0)
|
61
|
+
rspec-expectations (3.12.3)
|
62
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
63
|
+
rspec-support (~> 3.12.0)
|
64
|
+
rspec-mocks (3.12.6)
|
65
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
66
|
+
rspec-support (~> 3.12.0)
|
67
|
+
rspec-support (3.12.1)
|
68
|
+
rubocop (1.56.2)
|
69
|
+
base64 (~> 0.1.1)
|
70
|
+
json (~> 2.3)
|
71
|
+
language_server-protocol (>= 3.17.0)
|
72
|
+
parallel (~> 1.10)
|
73
|
+
parser (>= 3.2.2.3)
|
74
|
+
rainbow (>= 2.2.2, < 4.0)
|
75
|
+
regexp_parser (>= 1.8, < 3.0)
|
76
|
+
rexml (>= 3.2.5, < 4.0)
|
77
|
+
rubocop-ast (>= 1.28.1, < 2.0)
|
78
|
+
ruby-progressbar (~> 1.7)
|
79
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
80
|
+
rubocop-ast (1.29.0)
|
81
|
+
parser (>= 3.2.1.0)
|
82
|
+
rubocop-capybara (2.18.0)
|
83
|
+
rubocop (~> 1.41)
|
84
|
+
rubocop-factory_bot (2.23.1)
|
85
|
+
rubocop (~> 1.33)
|
86
|
+
rubocop-rspec (2.23.2)
|
87
|
+
rubocop (~> 1.33)
|
88
|
+
rubocop-capybara (~> 2.17)
|
89
|
+
rubocop-factory_bot (~> 2.22)
|
90
|
+
ruby-progressbar (1.13.0)
|
91
|
+
securerandom (0.2.2)
|
92
|
+
sequel (5.72.0)
|
93
|
+
bigdecimal
|
94
|
+
sqlite3 (1.6.0-arm64-darwin)
|
95
|
+
sqlite3 (1.6.0-x86_64-linux)
|
96
|
+
steep (1.5.3)
|
97
|
+
activesupport (>= 5.1)
|
98
|
+
concurrent-ruby (>= 1.1.10)
|
99
|
+
csv (>= 3.0.9)
|
100
|
+
fileutils (>= 1.1.0)
|
101
|
+
json (>= 2.1.0)
|
102
|
+
language_server-protocol (>= 3.15, < 4.0)
|
103
|
+
listen (~> 3.0)
|
104
|
+
logger (>= 1.3.0)
|
105
|
+
parser (>= 3.1)
|
106
|
+
rainbow (>= 2.2.2, < 4.0)
|
107
|
+
rbs (>= 3.1.0)
|
108
|
+
securerandom (>= 0.1)
|
109
|
+
strscan (>= 1.0.0)
|
110
|
+
terminal-table (>= 2, < 4)
|
111
|
+
strscan (3.0.6)
|
112
|
+
terminal-table (3.0.2)
|
113
|
+
unicode-display_width (>= 1.1.1, < 3)
|
114
|
+
tzinfo (2.0.6)
|
115
|
+
concurrent-ruby (~> 1.0)
|
116
|
+
unicode-display_width (2.4.2)
|
117
|
+
yaml (0.2.1)
|
118
|
+
zeitwerk (2.6.11)
|
119
|
+
zirconia (0.1.2)
|
120
|
+
|
121
|
+
PLATFORMS
|
122
|
+
arm64-darwin-21
|
123
|
+
x86_64-linux
|
124
|
+
|
125
|
+
DEPENDENCIES
|
126
|
+
fileutils
|
127
|
+
forwardable
|
128
|
+
kangaru!
|
129
|
+
pathname
|
130
|
+
rake (~> 13.0)
|
131
|
+
rspec (~> 3.0)
|
132
|
+
rubocop (~> 1.21)
|
133
|
+
rubocop-rspec (~> 2.23)
|
134
|
+
steep (~> 1.5)
|
135
|
+
yaml
|
136
|
+
zirconia (~> 0.1)
|
137
|
+
|
138
|
+
BUNDLED WITH
|
139
|
+
2.4.1
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 Chris Welham
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Kangaru - A Ruby Framework for Building Command Line Applications
|
2
|
+
|
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
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- MVC architecture drawing heavy inspiration from Rails
|
8
|
+
- ERB rendering of view templates with embedded Ruby capabilities
|
9
|
+
- SQLite and Sequel integration allowing databases to be created and used by your CLI
|
10
|
+
- A powerful command parser that routes terminal requests to their controller
|
11
|
+
- Zeitwerk-based autoloading of gem files
|
12
|
+
- Simple installation, integration and configuration
|
13
|
+
|
14
|
+
|
15
|
+
## Quick Setup
|
16
|
+
|
17
|
+
It is recommended to create a new Ruby gem for each Kangaru CLI application. This can be executed through `bundler` with the following command:
|
18
|
+
|
19
|
+
```sh
|
20
|
+
bundle gem gem_name
|
21
|
+
```
|
22
|
+
|
23
|
+
Next add Kangaru to your Gem's Gemfile and bundle install:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
# Gemfile
|
27
|
+
gem "kangaru"
|
28
|
+
```
|
29
|
+
```sh
|
30
|
+
bundle install
|
31
|
+
```
|
32
|
+
|
33
|
+
Kangaru's set up is bundled up into a single Ruby module that must be extended from the entry file for your Gem application. Conventionally this will be the Ruby file at `/gem_name/lib/gem_name.rb`, where `gem_name` is the name of the application specified with the `bundle gem` command.
|
34
|
+
|
35
|
+
For example:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# gem_name/lib/gem_name.rb
|
39
|
+
require "kangaru"
|
40
|
+
|
41
|
+
module GemName
|
42
|
+
extend Kangaru::Initialiser
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
This extension will set up Kangaru in the context of your Gem, including setting up processes such as the autoloader and config.
|
47
|
+
|
48
|
+
## Documentation
|
49
|
+
- TBC
|
data/Rakefile
ADDED
data/Steepfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
target :lib do
|
2
|
+
signature "sig"
|
3
|
+
|
4
|
+
check "lib"
|
5
|
+
|
6
|
+
configure_code_diagnostics do |hash|
|
7
|
+
hash[Steep::Diagnostic::Ruby::FallbackAny] = nil
|
8
|
+
hash[Steep::Diagnostic::Ruby::UnknownConstant] = :error
|
9
|
+
hash[Steep::Diagnostic::Ruby::MethodDefinitionMissing] = nil
|
10
|
+
hash[Steep::Diagnostic::Ruby::UnsupportedSyntax] = :hint
|
11
|
+
end
|
12
|
+
end
|
data/kangaru.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative "lib/kangaru/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "kangaru"
|
5
|
+
spec.version = Kangaru::VERSION
|
6
|
+
spec.authors = ["Chris Welham"]
|
7
|
+
spec.email = ["71787007+apexatoll@users.noreply.github.com"]
|
8
|
+
|
9
|
+
spec.summary = "A lightweight framework for building command line interfaces"
|
10
|
+
spec.license = "MIT"
|
11
|
+
spec.required_ruby_version = ">= 3.2.0"
|
12
|
+
|
13
|
+
spec.files = Dir.chdir(__dir__) do
|
14
|
+
`git ls-files`.split("\n").reject do |f|
|
15
|
+
f == __FILE__ || f.match?(/\A(bin|sig|spec|\.git|\.github)/)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
spec.bindir = "bin"
|
19
|
+
spec.executables = []
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "colorize", "~> 1.1"
|
23
|
+
spec.add_dependency "erb", "~> 4.0"
|
24
|
+
spec.add_dependency "sequel", "~> 5.72"
|
25
|
+
spec.add_dependency "sqlite3", "~> 1.6"
|
26
|
+
spec.add_dependency "zeitwerk", "~> 2.6"
|
27
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Kangaru
|
2
|
+
class Application
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
attr_reader :paths, :namespace, :database
|
6
|
+
|
7
|
+
def initialize(source:, namespace:)
|
8
|
+
@paths = Paths.new(source:)
|
9
|
+
@namespace = namespace
|
10
|
+
|
11
|
+
autoloader.setup
|
12
|
+
end
|
13
|
+
|
14
|
+
# Lazy-loaded to allow defaults to be set after application is created.
|
15
|
+
def config
|
16
|
+
@config ||= Config.new
|
17
|
+
end
|
18
|
+
|
19
|
+
# If called with no env, the config will be applied regardless of current
|
20
|
+
# env. If multiple configure calls matching the current env are made, the
|
21
|
+
# most recent calls will overwrite older changes.
|
22
|
+
def configure(env = nil, &block)
|
23
|
+
block.call(config) if current_env?(env)
|
24
|
+
end
|
25
|
+
|
26
|
+
def configured?
|
27
|
+
@configured == true
|
28
|
+
end
|
29
|
+
|
30
|
+
def apply_config!
|
31
|
+
raise "config already applied" if configured?
|
32
|
+
|
33
|
+
config.import_external_config!
|
34
|
+
|
35
|
+
@database = setup_database!
|
36
|
+
@configured = true
|
37
|
+
end
|
38
|
+
|
39
|
+
def run!(*argv)
|
40
|
+
request = RequestBuilder.new(argv).build
|
41
|
+
|
42
|
+
Router.new(request, namespace:).resolve
|
43
|
+
end
|
44
|
+
|
45
|
+
def_delegators :paths, :view_path
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Returns true if nil as this is represents all envs.
|
50
|
+
def current_env?(env)
|
51
|
+
return true if env.nil?
|
52
|
+
|
53
|
+
Kangaru.env?(env)
|
54
|
+
end
|
55
|
+
|
56
|
+
def autoloader
|
57
|
+
@autoloader ||= Zeitwerk::Loader.new.tap do |loader|
|
58
|
+
loader.inflector = Zeitwerk::GemInflector.new(paths.source.to_s)
|
59
|
+
loader.collapse(paths.collapsed_dirs)
|
60
|
+
loader.push_dir(paths.lib_path.to_s)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def setup_database!
|
65
|
+
return unless config.database.adaptor
|
66
|
+
|
67
|
+
Database.new(**config.database.serialise).tap do |database|
|
68
|
+
database.setup!
|
69
|
+
database.migrate!
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Kangaru
|
2
|
+
class ArgumentParser
|
3
|
+
using Patches::Inflections
|
4
|
+
|
5
|
+
attr_reader :tokens
|
6
|
+
|
7
|
+
def initialize(*tokens)
|
8
|
+
@tokens = tokens
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse
|
12
|
+
grouped_argument_tokens.to_h do |tokens|
|
13
|
+
key = parse_key(tokens.shift || raise)
|
14
|
+
value = parse_value(tokens)
|
15
|
+
|
16
|
+
[key, value]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def grouped_argument_tokens
|
23
|
+
tokens.slice_before(/^-/).to_a
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_key(key)
|
27
|
+
key.gsub(/^-+/, "").to_snakecase.to_sym
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_value(tokens)
|
31
|
+
tokens.empty? || tokens.join(" ")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Kangaru
|
2
|
+
class Component
|
3
|
+
using Patches::Inflections
|
4
|
+
using Patches::Source
|
5
|
+
|
6
|
+
def render
|
7
|
+
Renderer.new(view_file).render(binding)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def view_file
|
13
|
+
dir = File.dirname(self.class.source)
|
14
|
+
name = File.basename(self.class.source, ".rb")
|
15
|
+
|
16
|
+
Pathname.new(dir).join("#{name}.erb")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Kangaru
|
2
|
+
module Concerns
|
3
|
+
module AttributesConcern
|
4
|
+
extend Concern
|
5
|
+
|
6
|
+
class_methods do
|
7
|
+
def attributes
|
8
|
+
instance_methods.grep(/\w=$/).map do |attribute|
|
9
|
+
attribute.to_s.delete_suffix("=").to_sym
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_default(**attributes)
|
14
|
+
defaults.merge!(**attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
def defaults
|
18
|
+
@defaults ||= {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(**attributes)
|
23
|
+
attributes = self.class.defaults.merge(**attributes)
|
24
|
+
|
25
|
+
attributes.slice(*self.class.attributes).each do |attr, value|
|
26
|
+
instance_variable_set(:"@#{attr}", value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Kangaru
|
2
|
+
module Concerns
|
3
|
+
module Concern
|
4
|
+
def append_features(base)
|
5
|
+
super
|
6
|
+
evaluate_concern_blocks!(base)
|
7
|
+
end
|
8
|
+
|
9
|
+
def class_methods(&)
|
10
|
+
if const_defined?(:ClassMethods)
|
11
|
+
const_get(:ClassMethods)
|
12
|
+
else
|
13
|
+
const_set(:ClassMethods, Module.new)
|
14
|
+
end.module_eval(&)
|
15
|
+
end
|
16
|
+
|
17
|
+
def included(base = nil, &block)
|
18
|
+
super base if base
|
19
|
+
return if block.nil?
|
20
|
+
|
21
|
+
@included = block
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def evaluate_concern_blocks!(base)
|
27
|
+
base.extend(const_get(:ClassMethods)) if const_defined?(:ClassMethods)
|
28
|
+
|
29
|
+
base.class_eval(&@included) if instance_variable_defined?(:@included)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Kangaru
|
2
|
+
module Concerns
|
3
|
+
module Configurable
|
4
|
+
extend Concern
|
5
|
+
|
6
|
+
using Patches::Constantise
|
7
|
+
|
8
|
+
class_methods do
|
9
|
+
def configurator_name
|
10
|
+
(name || raise("class name not set"))
|
11
|
+
.gsub(/^(.*?)::/, "\\1::Configurators::")
|
12
|
+
.concat("Configurator")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def config
|
17
|
+
Kangaru.application.config.for(self.class.configurator_name) ||
|
18
|
+
raise("inferred configurator not set by application")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Kangaru
|
2
|
+
module Concerns
|
3
|
+
module Validatable
|
4
|
+
extend Concern
|
5
|
+
|
6
|
+
class_methods do
|
7
|
+
def validation_rules
|
8
|
+
@validation_rules ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def validates(attribute, **validations)
|
12
|
+
validation_rules[attribute] ||= {}
|
13
|
+
validation_rules[attribute].merge!(**validations)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def errors
|
18
|
+
@errors ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
def validate
|
22
|
+
self.class.validation_rules.each do |attribute, validations|
|
23
|
+
validator = validator_for(attribute)
|
24
|
+
|
25
|
+
validations.each do |validator_name, params|
|
26
|
+
params = {} if params == true
|
27
|
+
|
28
|
+
validator.validate(validator_name, **params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def valid?
|
34
|
+
validate
|
35
|
+
|
36
|
+
errors.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def validator_for(attribute)
|
42
|
+
Validation::AttributeValidator.new(model: self, attribute:)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Kangaru
|
2
|
+
class Config
|
3
|
+
attr_reader :configurators
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@configurators = set_configurators!
|
7
|
+
end
|
8
|
+
|
9
|
+
def serialise
|
10
|
+
configurators.transform_values(&:serialise)
|
11
|
+
end
|
12
|
+
|
13
|
+
def import_external_config!
|
14
|
+
return unless external_config_exists?
|
15
|
+
|
16
|
+
@external = Configurators::ExternalConfigurator
|
17
|
+
.from_yaml_file(application.config_path)
|
18
|
+
end
|
19
|
+
|
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
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def set_configurators!
|
30
|
+
Configurators.classes.to_h do |configurator_class|
|
31
|
+
self.class.class_eval { attr_reader configurator_class.key }
|
32
|
+
|
33
|
+
configurator = configurator_class.new
|
34
|
+
|
35
|
+
instance_variable_set(:"@#{configurator_class.key}", configurator)
|
36
|
+
|
37
|
+
[configurator_class.key, configurator]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def external_config_exists?
|
42
|
+
application.config_path && File.exist?(application.config_path)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|