castkit 0.2.0 → 0.3.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
- data/.rspec_status +118 -119
- data/CHANGELOG.md +1 -1
- data/README.md +287 -11
- data/castkit.gemspec +1 -0
- data/lib/castkit/castkit.rb +5 -2
- data/lib/castkit/cli/generate.rb +98 -0
- data/lib/castkit/cli/list.rb +200 -0
- data/lib/castkit/cli/main.rb +43 -0
- data/lib/castkit/cli.rb +24 -0
- data/lib/castkit/configuration.rb +31 -8
- data/lib/castkit/contract/{generic.rb → base.rb} +5 -5
- data/lib/castkit/contract/result.rb +2 -2
- data/lib/castkit/contract.rb +5 -5
- data/lib/castkit/data_object.rb +11 -7
- data/lib/castkit/ext/data_object/contract.rb +1 -1
- data/lib/castkit/ext/data_object/plugins.rb +86 -0
- data/lib/castkit/inflector.rb +1 -1
- data/lib/castkit/plugins.rb +82 -0
- data/lib/castkit/serializers/base.rb +94 -0
- data/lib/castkit/serializers/default_serializer.rb +156 -0
- data/lib/castkit/types/{generic.rb → base.rb} +6 -7
- data/lib/castkit/types/boolean.rb +14 -10
- data/lib/castkit/types/collection.rb +13 -2
- data/lib/castkit/types/date.rb +2 -2
- data/lib/castkit/types/date_time.rb +2 -2
- data/lib/castkit/types/float.rb +5 -5
- data/lib/castkit/types/integer.rb +5 -5
- data/lib/castkit/types/string.rb +2 -2
- data/lib/castkit/types.rb +1 -1
- data/lib/castkit/validators/base.rb +59 -0
- data/lib/castkit/validators/boolean_validator.rb +39 -0
- data/lib/castkit/validators/collection_validator.rb +29 -0
- data/lib/castkit/validators/float_validator.rb +31 -0
- data/lib/castkit/validators/integer_validator.rb +31 -0
- data/lib/castkit/validators/numeric_validator.rb +2 -2
- data/lib/castkit/validators/string_validator.rb +3 -4
- data/lib/castkit/version.rb +1 -1
- data/lib/generators/base.rb +97 -0
- data/lib/generators/contract.rb +68 -0
- data/lib/generators/data_object.rb +48 -0
- data/lib/generators/plugin.rb +25 -0
- data/lib/generators/serializer.rb +28 -0
- data/lib/generators/templates/contract.rb.tt +24 -0
- data/lib/generators/templates/contract_spec.rb.tt +76 -0
- data/lib/generators/templates/data_object.rb.tt +15 -0
- data/lib/generators/templates/data_object_spec.rb.tt +36 -0
- data/lib/generators/templates/plugin.rb.tt +37 -0
- data/lib/generators/templates/plugin_spec.rb.tt +18 -0
- data/lib/generators/templates/serializer.rb.tt +24 -0
- data/lib/generators/templates/serializer_spec.rb.tt +14 -0
- data/lib/generators/templates/type.rb.tt +55 -0
- data/lib/generators/templates/type_spec.rb.tt +42 -0
- data/lib/generators/templates/validator.rb.tt +26 -0
- data/lib/generators/templates/validator_spec.rb.tt +23 -0
- data/lib/generators/type.rb +29 -0
- data/lib/generators/validator.rb +41 -0
- metadata +50 -7
- data/lib/castkit/default_serializer.rb +0 -154
- data/lib/castkit/serializer.rb +0 -92
- data/lib/castkit/validators/base_validator.rb +0 -39
data/README.md
CHANGED
@@ -21,6 +21,8 @@ Castkit is designed to work seamlessly in service-oriented and API-driven archit
|
|
21
21
|
- [DataObjects](#dataobjects)
|
22
22
|
- [Contracts](#contracts)
|
23
23
|
- [Advance Usage](#advanced-usage-coming-soon)
|
24
|
+
- [Plugins](#plugins)
|
25
|
+
- [Castkit CLI](#castkit-cli)
|
24
26
|
- [Testing](#testing)
|
25
27
|
- [Compatibility](#compatibility)
|
26
28
|
- [License](#license)
|
@@ -58,14 +60,14 @@ Castkit comes with built-in support for primitive types and allows registration
|
|
58
60
|
|
59
61
|
```ruby
|
60
62
|
{
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
63
|
+
array: Castkit::Types::Collection,
|
64
|
+
boolean: Castkit::Types::Boolean,
|
65
|
+
date: Castkit::Types::Date,
|
66
|
+
datetime: Castkit::Types::DateTime,
|
67
|
+
float: Castkit::Types::Float,
|
68
|
+
hash: Castkit::Types::Base,
|
69
|
+
integer: Castkit::Types::Integer,
|
70
|
+
string: Castkit::Types::String
|
69
71
|
}
|
70
72
|
```
|
71
73
|
|
@@ -374,7 +376,8 @@ from_contract = Castkit::DataObject.from_contract(contract)
|
|
374
376
|
To override default serialization behavior:
|
375
377
|
|
376
378
|
```ruby
|
377
|
-
|
379
|
+
|
380
|
+
class CustomSerializer < Castkit::Serializers::Base
|
378
381
|
def call
|
379
382
|
{ payload: object.to_h }
|
380
383
|
end
|
@@ -427,7 +430,8 @@ end
|
|
427
430
|
Or subclass directly:
|
428
431
|
|
429
432
|
```ruby
|
430
|
-
|
433
|
+
|
434
|
+
class MyContract < Castkit::Contract::Base
|
431
435
|
string :id
|
432
436
|
integer :count, required: false
|
433
437
|
end
|
@@ -532,7 +536,7 @@ UserContract.validate!(id: "abc", address: { city: "Boston" })
|
|
532
536
|
|
533
537
|
Castkit is designed to be modular and extendable. Future guides will cover:
|
534
538
|
|
535
|
-
- Custom serializers (`Castkit::
|
539
|
+
- Custom serializers (`Castkit::Serializers::Base`)
|
536
540
|
- Integration layers:
|
537
541
|
- `castkit-activerecord` for syncing with ActiveRecord models
|
538
542
|
- `castkit-msgpack` for binary encoding
|
@@ -543,6 +547,278 @@ Castkit is designed to be modular and extendable. Future guides will cover:
|
|
543
547
|
|
544
548
|
---
|
545
549
|
|
550
|
+
## Plugins
|
551
|
+
|
552
|
+
Castkit supports modular extensions through a lightweight plugin system. Plugins can modify or extend the behavior of `Castkit::DataObject` classes, such as adding serialization support, transformation helpers, or framework integrations.
|
553
|
+
|
554
|
+
Plugins are just Ruby modules and can be registered and activated globally or per-class.
|
555
|
+
|
556
|
+
---
|
557
|
+
|
558
|
+
### 📦 Activating Plugins
|
559
|
+
|
560
|
+
Plugins can be activated on any DataObject or at runtime:
|
561
|
+
|
562
|
+
```ruby
|
563
|
+
module MyPlugin
|
564
|
+
def self.setup!(klass)
|
565
|
+
# Optional: called after inclusion
|
566
|
+
klass.string :plugin_id
|
567
|
+
end
|
568
|
+
|
569
|
+
def plugin_feature
|
570
|
+
"Enabled!"
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
Castkit.configure do |config|
|
575
|
+
config.register_plugin(:my_plugin, MyPlugin)
|
576
|
+
end
|
577
|
+
|
578
|
+
class MyDto < Castkit::DataObject
|
579
|
+
Castkit::Plugins.activate(self, :my_plugin)
|
580
|
+
end
|
581
|
+
```
|
582
|
+
|
583
|
+
This includes the `MyPlugin` module into `MyDto` and calls `MyPlugin.setup!(MyDto)` if defined.
|
584
|
+
|
585
|
+
---
|
586
|
+
|
587
|
+
### 🧩 Registering Plugins
|
588
|
+
|
589
|
+
Plugins must be registered before use:
|
590
|
+
|
591
|
+
```ruby
|
592
|
+
Castkit.configure do |config|
|
593
|
+
config.register_plugin(:oj, Castkit::Plugins::Oj)
|
594
|
+
end
|
595
|
+
```
|
596
|
+
|
597
|
+
You can then activate them:
|
598
|
+
|
599
|
+
```ruby
|
600
|
+
Castkit::Plugins.activate(MyDto, :oj)
|
601
|
+
```
|
602
|
+
|
603
|
+
---
|
604
|
+
|
605
|
+
### 🧰 Plugin API
|
606
|
+
|
607
|
+
| Method | Description |
|
608
|
+
|------------------------------|-------------|
|
609
|
+
| `Castkit::Plugins.register(:name, mod)` | Registers a plugin under a custom name. |
|
610
|
+
| `Castkit::Plugins.activate(klass, *names)` | Includes one or more plugins into a class. |
|
611
|
+
| `Castkit::Plugins.lookup!(:name)` | Looks up the plugin by name or constant. |
|
612
|
+
|
613
|
+
---
|
614
|
+
|
615
|
+
### 📁 Plugin Structure
|
616
|
+
|
617
|
+
Castkit looks for plugins under the `Castkit::Plugins` namespace by default:
|
618
|
+
|
619
|
+
```ruby
|
620
|
+
module Castkit
|
621
|
+
module Plugins
|
622
|
+
module Oj
|
623
|
+
def self.setup!(klass)
|
624
|
+
klass.include SerializationSupport
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|
629
|
+
```
|
630
|
+
|
631
|
+
To activate this:
|
632
|
+
|
633
|
+
```ruby
|
634
|
+
Castkit::Plugins.activate(MyDto, :oj)
|
635
|
+
```
|
636
|
+
|
637
|
+
You can also manually register plugins not under this namespace.
|
638
|
+
|
639
|
+
---
|
640
|
+
|
641
|
+
### ✅ Example Use Case
|
642
|
+
|
643
|
+
```ruby
|
644
|
+
module Castkit
|
645
|
+
module Plugins
|
646
|
+
module Timestamps
|
647
|
+
def self.setup!(klass)
|
648
|
+
klass.datetime :created_at
|
649
|
+
klass.datetime :updated_at
|
650
|
+
end
|
651
|
+
end
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
Castkit::Plugins.activate(UserDto, :timestamps)
|
656
|
+
```
|
657
|
+
|
658
|
+
This approach allows reusable, modular feature sets across DTOs with clean setup behavior.
|
659
|
+
|
660
|
+
---
|
661
|
+
|
662
|
+
## Castkit CLI
|
663
|
+
|
664
|
+
Castkit includes a command-line interface to help scaffold and inspect DTO components with ease.
|
665
|
+
|
666
|
+
The CLI is structured around two primary commands:
|
667
|
+
|
668
|
+
- `castkit generate` — scaffolds boilerplate for Castkit components.
|
669
|
+
- `castkit list` — introspects and displays registered or defined components.
|
670
|
+
|
671
|
+
---
|
672
|
+
|
673
|
+
## ✨ Generate Commands
|
674
|
+
|
675
|
+
The `castkit generate` command provides subcommands for creating files for all core Castkit component types.
|
676
|
+
|
677
|
+
### 🧱 DataObject
|
678
|
+
|
679
|
+
```bash
|
680
|
+
castkit generate dataobject User name:string age:integer
|
681
|
+
```
|
682
|
+
|
683
|
+
Creates:
|
684
|
+
|
685
|
+
- `lib/castkit/data_objects/user.rb`
|
686
|
+
- `spec/castkit/data_objects/user_spec.rb`
|
687
|
+
|
688
|
+
### 📄 Contract
|
689
|
+
|
690
|
+
```bash
|
691
|
+
castkit generate contract UserInput id:string email:string
|
692
|
+
```
|
693
|
+
|
694
|
+
Creates:
|
695
|
+
|
696
|
+
- `lib/castkit/contracts/user_input.rb`
|
697
|
+
- `spec/castkit/contracts/user_input_spec.rb`
|
698
|
+
|
699
|
+
### 🔌 Plugin
|
700
|
+
|
701
|
+
```bash
|
702
|
+
castkit generate plugin Oj
|
703
|
+
```
|
704
|
+
|
705
|
+
Creates:
|
706
|
+
|
707
|
+
- `lib/castkit/plugins/oj.rb`
|
708
|
+
- `spec/castkit/plugins/oj_spec.rb`
|
709
|
+
|
710
|
+
### 🧪 Validator
|
711
|
+
|
712
|
+
```bash
|
713
|
+
castkit generate validator Money
|
714
|
+
```
|
715
|
+
|
716
|
+
Creates:
|
717
|
+
|
718
|
+
- `lib/castkit/validators/money.rb`
|
719
|
+
- `spec/castkit/validators/money_spec.rb`
|
720
|
+
|
721
|
+
### 🧬 Type
|
722
|
+
|
723
|
+
```bash
|
724
|
+
castkit generate type money
|
725
|
+
```
|
726
|
+
|
727
|
+
Creates:
|
728
|
+
|
729
|
+
- `lib/castkit/types/money.rb`
|
730
|
+
- `spec/castkit/types/money_spec.rb`
|
731
|
+
|
732
|
+
### 📦 Serializer
|
733
|
+
|
734
|
+
```bash
|
735
|
+
castkit generate serializer Json
|
736
|
+
```
|
737
|
+
|
738
|
+
Creates:
|
739
|
+
|
740
|
+
- `lib/castkit/serializers/json.rb`
|
741
|
+
- `spec/castkit/serializers/json_spec.rb`
|
742
|
+
|
743
|
+
You can disable test generation with `--no-spec`.
|
744
|
+
|
745
|
+
---
|
746
|
+
|
747
|
+
## 📋 List Commands
|
748
|
+
|
749
|
+
The `castkit list` command provides an interface to view internal Castkit definitions or project-registered components.
|
750
|
+
|
751
|
+
### 🧾 List Types
|
752
|
+
|
753
|
+
```bash
|
754
|
+
castkit list types
|
755
|
+
```
|
756
|
+
|
757
|
+
Displays a grouped list of:
|
758
|
+
|
759
|
+
- Native types (defined by Castkit)
|
760
|
+
- Custom types (registered via `Castkit.configure`)
|
761
|
+
|
762
|
+
Example:
|
763
|
+
|
764
|
+
```bash
|
765
|
+
Native Types:
|
766
|
+
Castkit::Types::String - :string, :str, :uuid
|
767
|
+
|
768
|
+
Custom Types:
|
769
|
+
MyApp::Types::Money - :money
|
770
|
+
```
|
771
|
+
|
772
|
+
### 🔍 List Validators
|
773
|
+
|
774
|
+
```bash
|
775
|
+
castkit list validators
|
776
|
+
```
|
777
|
+
|
778
|
+
Displays all validator classes defined in `lib/castkit/validators` or custom-defined under `Castkit::Validators`.
|
779
|
+
|
780
|
+
Castkit validators are tagged `[Castkit]`, and others as `[Custom]`.
|
781
|
+
|
782
|
+
### 📑 List Contracts
|
783
|
+
|
784
|
+
```bash
|
785
|
+
castkit list contracts
|
786
|
+
```
|
787
|
+
|
788
|
+
Lists all contracts in the `Castkit::Contracts` namespace and related files.
|
789
|
+
|
790
|
+
### 📦 List DataObjects
|
791
|
+
|
792
|
+
```bash
|
793
|
+
castkit list dataobjects
|
794
|
+
```
|
795
|
+
|
796
|
+
Lists all DTOs in the `Castkit::DataObjects` namespace.
|
797
|
+
|
798
|
+
### 🧪 List Serializers
|
799
|
+
|
800
|
+
```bash
|
801
|
+
castkit list serializers
|
802
|
+
```
|
803
|
+
|
804
|
+
Lists all serializer classes and their source origin.
|
805
|
+
|
806
|
+
---
|
807
|
+
|
808
|
+
## 🧰 Example Usage
|
809
|
+
|
810
|
+
```bash
|
811
|
+
castkit generate dataobject Product name:string price:float
|
812
|
+
castkit generate contract ProductInput name:string
|
813
|
+
|
814
|
+
castkit list types
|
815
|
+
castkit list validators
|
816
|
+
```
|
817
|
+
|
818
|
+
The CLI is designed to provide a familiar Rails-like generator experience, tailored for Castkit’s data-first architecture.
|
819
|
+
|
820
|
+
---
|
821
|
+
|
546
822
|
## Testing
|
547
823
|
|
548
824
|
You can test DTOs and Contracts by treating them like plain Ruby objects:
|
data/castkit.gemspec
CHANGED
data/lib/castkit/castkit.rb
CHANGED
@@ -20,12 +20,15 @@ require_relative "inflector"
|
|
20
20
|
# @see Castkit::Contract
|
21
21
|
# @see Castkit::DataObject
|
22
22
|
module Castkit
|
23
|
-
# Namespace used for registering
|
23
|
+
# Namespace used for registering generated DataObjects.
|
24
24
|
module DataObjects; end
|
25
25
|
|
26
|
-
# Namespace used for registering
|
26
|
+
# Namespace used for registering generated contracts.
|
27
27
|
module Contracts; end
|
28
28
|
|
29
|
+
# Namespace used for registering generated plugins.
|
30
|
+
module Plugins; end
|
31
|
+
|
29
32
|
class << self
|
30
33
|
# Yields the global configuration object for customization.
|
31
34
|
#
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require_relative "../../generators/contract"
|
5
|
+
require_relative "../../generators/data_object"
|
6
|
+
require_relative "../../generators/plugin"
|
7
|
+
require_relative "../../generators/serializer"
|
8
|
+
require_relative "../../generators/type"
|
9
|
+
require_relative "../../generators/validator"
|
10
|
+
|
11
|
+
module Castkit
|
12
|
+
module CLI
|
13
|
+
# Thor CLI class for generating Castkit components.
|
14
|
+
#
|
15
|
+
# Provides `castkit generate` commands for each major Castkit component, including types,
|
16
|
+
# data objects, contracts, validators, serializers, and plugins.
|
17
|
+
#
|
18
|
+
# All generators support the `--no-spec` flag to skip spec file creation.
|
19
|
+
class Generate < Thor
|
20
|
+
desc "contract NAME", "Generates a new Castkit contract"
|
21
|
+
method_option :spec, type: :boolean, default: true
|
22
|
+
# Generates a new contract class.
|
23
|
+
#
|
24
|
+
# @param name [String] the class name for the contract
|
25
|
+
# @param fields [Array<String>] optional attribute definitions
|
26
|
+
# @return [void]
|
27
|
+
def contract(name, *fields)
|
28
|
+
args = [Castkit::Inflector.pascalize(name), fields]
|
29
|
+
args << "--no-spec" unless options[:spec]
|
30
|
+
Castkit::Generators::Contract.start(args)
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "dataobject NAME", "Generates a new Castkit DataObject"
|
34
|
+
method_option :spec, type: :boolean, default: true
|
35
|
+
# Generates a new DataObject class.
|
36
|
+
#
|
37
|
+
# @param name [String] the class name for the data object
|
38
|
+
# @param fields [Array<String>] optional attribute definitions
|
39
|
+
# @return [void]
|
40
|
+
def dataobject(name, *fields)
|
41
|
+
args = [Castkit::Inflector.pascalize(name), fields]
|
42
|
+
args << "--no-spec" unless options[:spec]
|
43
|
+
Castkit::Generators::DataObject.start(args)
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "plugin NAME", "Generates a new Castkit plugin"
|
47
|
+
method_option :spec, type: :boolean, default: true
|
48
|
+
# Generates a new plugin module.
|
49
|
+
#
|
50
|
+
# @param name [String] the module name for the plugin
|
51
|
+
# @param fields [Array<String>] optional stub fields
|
52
|
+
# @return [void]
|
53
|
+
def plugin(name, *fields)
|
54
|
+
args = [Castkit::Inflector.pascalize(name), fields]
|
55
|
+
args << "--no-spec" unless options[:spec]
|
56
|
+
Castkit::Generators::Plugin.start(args)
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "serializer NAME", "Generates a new Castkit serializer"
|
60
|
+
method_option :spec, type: :boolean, default: true
|
61
|
+
# Generates a new custom serializer class.
|
62
|
+
#
|
63
|
+
# @param name [String] the class name for the serializer
|
64
|
+
# @param fields [Array<String>] optional stub fields
|
65
|
+
# @return [void]
|
66
|
+
def serializer(name, *fields)
|
67
|
+
args = [Castkit::Inflector.pascalize(name), fields]
|
68
|
+
args << "--no-spec" unless options[:spec]
|
69
|
+
Castkit::Generators::Serializer.start(args)
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "type NAME", "Generates a new Castkit type"
|
73
|
+
method_option :spec, type: :boolean, default: true
|
74
|
+
# Generates a new custom type.
|
75
|
+
#
|
76
|
+
# @param name [String] the class name for the type
|
77
|
+
# @return [void]
|
78
|
+
def type(name)
|
79
|
+
args = [Castkit::Inflector.pascalize(name)]
|
80
|
+
args << "--no-spec" unless options[:spec]
|
81
|
+
Castkit::Generators::Type.start(args)
|
82
|
+
end
|
83
|
+
|
84
|
+
desc "validator NAME", "Generates a new Castkit validator"
|
85
|
+
method_option :spec, type: :boolean, default: true
|
86
|
+
# Generates a new validator class.
|
87
|
+
#
|
88
|
+
# @param name [String] the class name for the validator
|
89
|
+
# @param fields [Array<String>] optional stub fields
|
90
|
+
# @return [void]
|
91
|
+
def validator(name, *fields)
|
92
|
+
args = [Castkit::Inflector.pascalize(name), fields]
|
93
|
+
args << "--no-spec" unless options[:spec]
|
94
|
+
Castkit::Generators::Validator.start(args)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require "castkit"
|
5
|
+
require_relative "../../generators/contract"
|
6
|
+
require_relative "../../generators/data_object"
|
7
|
+
require_relative "../../generators/plugin"
|
8
|
+
require_relative "../../generators/serializer"
|
9
|
+
require_relative "../../generators/type"
|
10
|
+
require_relative "../../generators/validator"
|
11
|
+
|
12
|
+
module Castkit
|
13
|
+
module CLI
|
14
|
+
# CLI commands for listing internal Castkit registry components.
|
15
|
+
#
|
16
|
+
# Supports listing:
|
17
|
+
# - Registered types (`castkit list types`)
|
18
|
+
# - Available validators (`castkit list validators`)
|
19
|
+
#
|
20
|
+
# @example Show all available types
|
21
|
+
# $ castkit list types
|
22
|
+
#
|
23
|
+
# @example Show all defined validators
|
24
|
+
# $ castkit list validators
|
25
|
+
class List < Thor
|
26
|
+
desc "types", "Lists registered Castkit types"
|
27
|
+
# Lists registered Castkit types, grouped into native and custom-defined.
|
28
|
+
#
|
29
|
+
# @return [void]
|
30
|
+
def types
|
31
|
+
all_keys = Castkit.configuration.types
|
32
|
+
default_keys = Castkit::Configuration::DEFAULT_TYPES.keys
|
33
|
+
|
34
|
+
native_types(all_keys, default_keys)
|
35
|
+
custom_types(all_keys, default_keys)
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "contracts", "Lists all generated Castkit contracts"
|
39
|
+
# Lists all Castkit contract classes defined in the file system or registered under the Castkit namespace.
|
40
|
+
#
|
41
|
+
# @return [void]
|
42
|
+
def contracts
|
43
|
+
list_files("contracts")
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "dataobjects", "Lists all generated Castkit DataObjects"
|
47
|
+
# Lists all Castkit DataObjects classes defined in the file system or registered under the Castkit namespace.
|
48
|
+
#
|
49
|
+
# @return [void]
|
50
|
+
def dataobjects
|
51
|
+
list_files("data_objects")
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "serializers", "Lists all generated Castkit serializers"
|
55
|
+
# Lists all Castkit serializers classes defined in the file system or registered under the Castkit namespace.
|
56
|
+
#
|
57
|
+
# @return [void]
|
58
|
+
def serializers
|
59
|
+
list_files("serializers")
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "validators", "Lists all generated Castkit validators"
|
63
|
+
# Lists all Castkit validator classes defined in the file system or registered under the Castkit namespace.
|
64
|
+
#
|
65
|
+
# @return [void]
|
66
|
+
def validators
|
67
|
+
list_files("validators")
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Prints all native types and their aliases.
|
73
|
+
#
|
74
|
+
# @param all_types [Hash<Symbol, Object>] all registered types
|
75
|
+
# @param default_keys [Array<Symbol>] predefined native type keys
|
76
|
+
# @return [void]
|
77
|
+
def native_types(all_types, default_keys)
|
78
|
+
alias_map = reverse_grouped(Castkit::Configuration::TYPE_ALIASES)
|
79
|
+
native = all_types.slice(*default_keys)
|
80
|
+
|
81
|
+
say "Native Types:", :green
|
82
|
+
native.each do |name, type|
|
83
|
+
aliases = alias_map[name] || []
|
84
|
+
list_type(type.class, [name, *aliases].map(&:to_sym))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Prints all custom (non-native, non-alias) registered types.
|
89
|
+
#
|
90
|
+
# @param all_types [Hash<Symbol, Object>]
|
91
|
+
# @param default_keys [Array<Symbol>]
|
92
|
+
# @return [void]
|
93
|
+
def custom_types(all_types, default_keys)
|
94
|
+
alias_keys = Castkit::Configuration::TYPE_ALIASES.keys.map(&:to_sym)
|
95
|
+
custom = all_types.except(*default_keys).reject { |k, _| alias_keys.include?(k) }
|
96
|
+
|
97
|
+
say "\nCustom Types:", :green
|
98
|
+
return no_custom_types if custom.empty?
|
99
|
+
|
100
|
+
grouped_custom_types(custom)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Outputs a fallback message if no custom types exist.
|
104
|
+
#
|
105
|
+
# @return [void]
|
106
|
+
def no_custom_types
|
107
|
+
say " No registered types, register with " \
|
108
|
+
"#{set_color("Castkit.configure { |c| c.register_type(:type, Type) }", :yellow)}"
|
109
|
+
end
|
110
|
+
|
111
|
+
# Groups and prints custom types by their class.
|
112
|
+
#
|
113
|
+
# @param types [Hash<Symbol, Object>]
|
114
|
+
# @return [void]
|
115
|
+
def grouped_custom_types(types)
|
116
|
+
types.group_by { |_, inst| inst.class }.each do |klass, group|
|
117
|
+
list_type(klass, group.map(&:first).map(&:to_sym))
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Reverses a hash of alias => type into type => [aliases].
|
122
|
+
#
|
123
|
+
# @param hash [Hash]
|
124
|
+
# @return [Hash{Symbol => Array<Symbol>}]
|
125
|
+
def reverse_grouped(hash)
|
126
|
+
hash.each_with_object(Hash.new { |h, k| h[k] = [] }) do |(k, v), acc|
|
127
|
+
acc[v] << k
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Prints a type or class with all its symbol aliases.
|
132
|
+
#
|
133
|
+
# @param klass [Class]
|
134
|
+
# @param keys [Array<Symbol>]
|
135
|
+
# @return [void]
|
136
|
+
def list_type(klass, keys)
|
137
|
+
types = keys.uniq.sort.map { |k| set_color(":#{k}", :yellow) }.join(", ")
|
138
|
+
say " #{(klass.name || "<AnonymousType>").ljust(34)} - #{types}"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Lists class references for a component (e.g. validators), distinguishing by source (file or custom).
|
142
|
+
#
|
143
|
+
# @param component [String] base namespace (e.g. "validators")
|
144
|
+
# @return [void]
|
145
|
+
def list_files(component)
|
146
|
+
path = "lib/castkit/#{component}"
|
147
|
+
all_classes, file_classes = component_classes(component, path)
|
148
|
+
return say "No registered #{Castkit::Inflector.pascalize(component)} found." if all_classes.empty?
|
149
|
+
|
150
|
+
max_width = all_classes.map(&:length).max + 5
|
151
|
+
say "Castkit #{Castkit::Inflector.pascalize(component)}", :green
|
152
|
+
|
153
|
+
all_classes.each do |klass|
|
154
|
+
tag = file_classes.include?(klass) ? set_color("[Castkit]", :yellow) : set_color("[Custom]", :green)
|
155
|
+
say " #{klass.ljust(max_width)} #{tag}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Gathers all registered and defined constants for a component.
|
160
|
+
#
|
161
|
+
# @param component [String]
|
162
|
+
# @param path [String]
|
163
|
+
# @return [Array<[Array<String>, Set<String>]>]
|
164
|
+
def component_classes(component, path)
|
165
|
+
namespace = Castkit.const_get(Castkit::Inflector.pascalize(component))
|
166
|
+
file_classes = file_classes(namespace, path)
|
167
|
+
defined_classes = defined_classes(namespace)
|
168
|
+
|
169
|
+
all_classes = (file_classes + defined_classes).to_a.sort
|
170
|
+
[all_classes, file_classes]
|
171
|
+
end
|
172
|
+
|
173
|
+
# Converts file names into class names for a given component.
|
174
|
+
#
|
175
|
+
# @param namespace [Module]
|
176
|
+
# @param path [String]
|
177
|
+
# @return [Set<String>]
|
178
|
+
def file_classes(namespace, path)
|
179
|
+
classes = Dir.glob("#{path}/*.rb")
|
180
|
+
.map { |f| File.basename(f, ".rb") }
|
181
|
+
.reject { |f| f.to_s == "base" }
|
182
|
+
.map { |base| "#{namespace}::#{Castkit::Inflector.pascalize(base)}" }
|
183
|
+
|
184
|
+
classes.to_set
|
185
|
+
end
|
186
|
+
|
187
|
+
# Lists actual constants under a namespace, filtering out missing definitions.
|
188
|
+
#
|
189
|
+
# @param namespace [Module]
|
190
|
+
# @return [Set<String>]
|
191
|
+
def defined_classes(namespace)
|
192
|
+
namespace.constants
|
193
|
+
.reject { |const| const.to_s == "Base" }
|
194
|
+
.map { |const| "#{namespace}::#{const}" }
|
195
|
+
.select { |klass| Object.const_defined?(klass) }
|
196
|
+
.to_set
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "generate"
|
4
|
+
require_relative "list"
|
5
|
+
|
6
|
+
module Castkit
|
7
|
+
module CLI
|
8
|
+
# Main CLI entry point for Castkit.
|
9
|
+
#
|
10
|
+
# Provides top-level commands for printing the gem version and generating Castkit components.
|
11
|
+
#
|
12
|
+
# @example Print the version
|
13
|
+
# $ castkit version
|
14
|
+
#
|
15
|
+
# @example Generate a DataObject
|
16
|
+
# $ castkit generate dataobject User name:string age:integer
|
17
|
+
class Main < Thor
|
18
|
+
desc "version", "Prints the version"
|
19
|
+
# Outputs the current Castkit version.
|
20
|
+
#
|
21
|
+
# @return [void]
|
22
|
+
def version
|
23
|
+
puts Castkit::VERSION
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "generate TYPE NAME", "Generate a Castkit component"
|
27
|
+
# Dispatches to the `castkit generate` subcommands.
|
28
|
+
#
|
29
|
+
# Supports generating components like `type`, `dataobject`, `contract`, etc.
|
30
|
+
#
|
31
|
+
# @return [void]
|
32
|
+
subcommand "generate", Castkit::CLI::Generate
|
33
|
+
|
34
|
+
desc "list COMPONENT", "List registered Castkit components"
|
35
|
+
# Dispatches to the `castkit list` subcommands.
|
36
|
+
#
|
37
|
+
# Supports listing components like `type`, `dataobject`, `contract`, etc.
|
38
|
+
#
|
39
|
+
# @return [void]
|
40
|
+
subcommand "list", Castkit::CLI::List
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|