castkit 0.2.0 → 0.3.1
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/CHANGELOG.md +40 -1
- data/README.md +297 -13
- data/castkit.gemspec +3 -0
- data/lib/castkit/attribute.rb +82 -59
- data/lib/castkit/attributes/definition.rb +64 -0
- data/lib/castkit/attributes/options.rb +214 -0
- data/lib/castkit/castkit.rb +18 -5
- data/lib/castkit/cli/generate.rb +112 -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 -17
- data/lib/castkit/contract/result.rb +2 -2
- data/lib/castkit/contract/validator.rb +5 -1
- data/lib/castkit/contract.rb +5 -5
- data/lib/castkit/core/attributes.rb +87 -44
- data/lib/castkit/data_object.rb +11 -30
- data/lib/castkit/{ext → dsl}/attribute/access.rb +1 -1
- data/lib/castkit/{ext → dsl}/attribute/error_handling.rb +1 -1
- data/lib/castkit/{ext → dsl}/attribute/options.rb +1 -1
- data/lib/castkit/{ext → dsl}/attribute/validation.rb +3 -3
- data/lib/castkit/dsl/attribute.rb +47 -0
- data/lib/castkit/{ext → dsl}/data_object/contract.rb +2 -2
- data/lib/castkit/{ext → dsl}/data_object/deserialization.rb +6 -2
- data/lib/castkit/dsl/data_object/plugins.rb +86 -0
- data/lib/castkit/{ext → dsl}/data_object/serialization.rb +1 -1
- data/lib/castkit/dsl/data_object.rb +61 -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} +30 -10
- 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/castkit.rb +1 -4
- data/lib/generators/attribute.rb +39 -0
- 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/attribute.rb.tt +21 -0
- data/lib/generators/templates/attribute_spec.rb.tt +41 -0
- data/lib/generators/templates/contract.rb.tt +26 -0
- data/lib/generators/templates/contract_spec.rb.tt +76 -0
- data/lib/generators/templates/data_object.rb.tt +17 -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 +57 -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 +92 -16
- data/.rspec_status +0 -196
- data/lib/castkit/core/registerable.rb +0 -59
- data/lib/castkit/default_serializer.rb +0 -154
- data/lib/castkit/serializer.rb +0 -92
- data/lib/castkit/validators/base_validator.rb +0 -39
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f048458a9f967984b6b590c1e6bf2348a43c90936374b4d8755c6f7151057fe8
|
|
4
|
+
data.tar.gz: 182e73cec494a329bc670999cceab4a3c1e057eb6108097c759989102232ec94
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4d8bc7bdd0ed6c26f7fb7a621306995b4fbfbbe6d1d331ffa813ffc4c13a2d2795b72af9f977bc618e950c8dfdc5b017bb95563a436520e4e545efdf1a9a0b50
|
|
7
|
+
data.tar.gz: 196ed35dd105ad854ac74fa4540cbfbb04de30885069719ab80bb84d5a41b8ea8e64df1e085753477dab3bf54f998db20684b5cd6460af4c87447594c320edd3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.3.0] - 2025-04-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **CLI System (`castkit`)**:
|
|
8
|
+
- Introduced a full-featured CLI for generating and inspecting Castkit components.
|
|
9
|
+
- Supports:
|
|
10
|
+
- `castkit generate [component]` for scaffolding types, data objects, contracts, serializers, validators, and plugins.
|
|
11
|
+
- `castkit list [types|validators|contracts|dataobjects|serializers]` for inspecting the internal registry and available classes.
|
|
12
|
+
- Enables developer productivity and exploration through a single entry point.
|
|
13
|
+
|
|
14
|
+
- **Plugin System**:
|
|
15
|
+
- Added `Castkit::Plugins` for modular runtime extensions.
|
|
16
|
+
- Plugins can be registered and activated on DTO classes via:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
Castkit.configure do |config|
|
|
20
|
+
config.register_plugin(:oj, MyOjPlugin)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class MyDto < Castkit::DataObject
|
|
24
|
+
enable_plugins :oj
|
|
25
|
+
end
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- Plugins support `setup!(klass)` for optional initialization logic.
|
|
29
|
+
|
|
30
|
+
- **Base Class Renames**:
|
|
31
|
+
- Introduced consistent naming conventions with `Castkit::Types::Base`, `Castkit::Serializers::Base`, `Castkit::Validators::Base`, etc.
|
|
32
|
+
- These will be excluded from list output automatically in CLI commands.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
3
36
|
## [0.2.0] - 2025-04-14
|
|
4
37
|
|
|
5
38
|
### Added
|
|
@@ -10,7 +43,7 @@
|
|
|
10
43
|
- Developers may register custom types via `Castkit.configure`:
|
|
11
44
|
|
|
12
45
|
```ruby
|
|
13
|
-
class CustomType < Castkit::Types::
|
|
46
|
+
class CustomType < Castkit::Types::Base
|
|
14
47
|
def deserialize(value) ... end
|
|
15
48
|
def validate!(value, **) ... end
|
|
16
49
|
end
|
|
@@ -42,6 +75,8 @@
|
|
|
42
75
|
|
|
43
76
|
- **Union type validation support** for both `Castkit::DataObject` and contracts, allowing attributes to accept multiple types (e.g., `[:string, :integer]`).
|
|
44
77
|
|
|
78
|
+
---
|
|
79
|
+
|
|
45
80
|
## [0.1.2] - 2025-04-14
|
|
46
81
|
|
|
47
82
|
### Added
|
|
@@ -71,6 +106,8 @@
|
|
|
71
106
|
end
|
|
72
107
|
```
|
|
73
108
|
|
|
109
|
+
---
|
|
110
|
+
|
|
74
111
|
## [0.1.1] - 2025-04-13
|
|
75
112
|
|
|
76
113
|
### Added
|
|
@@ -87,6 +124,8 @@
|
|
|
87
124
|
|
|
88
125
|
Useful when working with nested DataObject collections (e.g., for integration with ActiveRecord or serialization logic).
|
|
89
126
|
|
|
127
|
+
---
|
|
128
|
+
|
|
90
129
|
## [0.1.0] - 2025-04-12
|
|
91
130
|
|
|
92
131
|
- Initial release
|
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
|
|
|
@@ -213,7 +215,7 @@ class Metadata < Castkit::DataObject
|
|
|
213
215
|
end
|
|
214
216
|
|
|
215
217
|
class PageDto < Castkit::DataObject
|
|
216
|
-
dataobject :metadata, unwrapped: true, prefix: "meta"
|
|
218
|
+
dataobject :metadata, Metadata, unwrapped: true, prefix: "meta"
|
|
217
219
|
end
|
|
218
220
|
|
|
219
221
|
# Serializes as:
|
|
@@ -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
|
|
@@ -519,7 +523,7 @@ end
|
|
|
519
523
|
|
|
520
524
|
class UserDto < Castkit::DataObject
|
|
521
525
|
string :id
|
|
522
|
-
dataobject :address,
|
|
526
|
+
dataobject :address, AddressDto
|
|
523
527
|
end
|
|
524
528
|
|
|
525
529
|
UserContract = Castkit::Contract.from_dataobject(UserDto)
|
|
@@ -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,286 @@ 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
|
+
enable_plugins :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
|
+
Or by using the `enable_plugins` helper method in `Castkit::DataObject`:
|
|
604
|
+
|
|
605
|
+
```ruby
|
|
606
|
+
class MyDto < Castkit::DataObject
|
|
607
|
+
enable_plugins :oj, :yaml
|
|
608
|
+
end
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
### 🧰 Plugin API
|
|
614
|
+
|
|
615
|
+
| Method | Description |
|
|
616
|
+
|------------------------------|-------------|
|
|
617
|
+
| `Castkit::Plugins.register(:name, mod)` | Registers a plugin under a custom name. |
|
|
618
|
+
| `Castkit::Plugins.activate(klass, *names)` | Includes one or more plugins into a class. |
|
|
619
|
+
| `Castkit::Plugins.lookup!(:name)` | Looks up the plugin by name or constant. |
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
### 📁 Plugin Structure
|
|
624
|
+
|
|
625
|
+
Castkit looks for plugins under the `Castkit::Plugins` namespace by default:
|
|
626
|
+
|
|
627
|
+
```ruby
|
|
628
|
+
module Castkit
|
|
629
|
+
module Plugins
|
|
630
|
+
module Oj
|
|
631
|
+
def self.setup!(klass)
|
|
632
|
+
klass.include SerializationSupport
|
|
633
|
+
end
|
|
634
|
+
end
|
|
635
|
+
end
|
|
636
|
+
end
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
To activate this:
|
|
640
|
+
|
|
641
|
+
```ruby
|
|
642
|
+
Castkit::Plugins.activate(MyDto, :oj)
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
You can also manually register plugins not under this namespace.
|
|
646
|
+
|
|
647
|
+
---
|
|
648
|
+
|
|
649
|
+
### ✅ Example Use Case
|
|
650
|
+
|
|
651
|
+
```ruby
|
|
652
|
+
module Castkit
|
|
653
|
+
module Plugins
|
|
654
|
+
module Timestamps
|
|
655
|
+
def self.setup!(klass)
|
|
656
|
+
klass.datetime :created_at
|
|
657
|
+
klass.datetime :updated_at
|
|
658
|
+
end
|
|
659
|
+
end
|
|
660
|
+
end
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
Castkit::Plugins.activate(UserDto, :timestamps)
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
This approach allows reusable, modular feature sets across DTOs with clean setup behavior.
|
|
667
|
+
|
|
668
|
+
---
|
|
669
|
+
|
|
670
|
+
## Castkit CLI
|
|
671
|
+
|
|
672
|
+
Castkit includes a command-line interface to help scaffold and inspect DTO components with ease.
|
|
673
|
+
|
|
674
|
+
The CLI is structured around two primary commands:
|
|
675
|
+
|
|
676
|
+
- `castkit generate` — scaffolds boilerplate for Castkit components.
|
|
677
|
+
- `castkit list` — introspects and displays registered or defined components.
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
## ✨ Generate Commands
|
|
682
|
+
|
|
683
|
+
The `castkit generate` command provides subcommands for creating files for all core Castkit component types.
|
|
684
|
+
|
|
685
|
+
### 🧱 DataObject
|
|
686
|
+
|
|
687
|
+
```bash
|
|
688
|
+
castkit generate dataobject User name:string age:integer
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
Creates:
|
|
692
|
+
|
|
693
|
+
- `lib/castkit/data_objects/user.rb`
|
|
694
|
+
- `spec/castkit/data_objects/user_spec.rb`
|
|
695
|
+
|
|
696
|
+
### 📄 Contract
|
|
697
|
+
|
|
698
|
+
```bash
|
|
699
|
+
castkit generate contract UserInput id:string email:string
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Creates:
|
|
703
|
+
|
|
704
|
+
- `lib/castkit/contracts/user_input.rb`
|
|
705
|
+
- `spec/castkit/contracts/user_input_spec.rb`
|
|
706
|
+
|
|
707
|
+
### 🔌 Plugin
|
|
708
|
+
|
|
709
|
+
```bash
|
|
710
|
+
castkit generate plugin Oj
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
Creates:
|
|
714
|
+
|
|
715
|
+
- `lib/castkit/plugins/oj.rb`
|
|
716
|
+
- `spec/castkit/plugins/oj_spec.rb`
|
|
717
|
+
|
|
718
|
+
### 🧪 Validator
|
|
719
|
+
|
|
720
|
+
```bash
|
|
721
|
+
castkit generate validator Money
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
Creates:
|
|
725
|
+
|
|
726
|
+
- `lib/castkit/validators/money.rb`
|
|
727
|
+
- `spec/castkit/validators/money_spec.rb`
|
|
728
|
+
|
|
729
|
+
### 🧬 Type
|
|
730
|
+
|
|
731
|
+
```bash
|
|
732
|
+
castkit generate type money
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
Creates:
|
|
736
|
+
|
|
737
|
+
- `lib/castkit/types/money.rb`
|
|
738
|
+
- `spec/castkit/types/money_spec.rb`
|
|
739
|
+
|
|
740
|
+
### 📦 Serializer
|
|
741
|
+
|
|
742
|
+
```bash
|
|
743
|
+
castkit generate serializer Json
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
Creates:
|
|
747
|
+
|
|
748
|
+
- `lib/castkit/serializers/json.rb`
|
|
749
|
+
- `spec/castkit/serializers/json_spec.rb`
|
|
750
|
+
|
|
751
|
+
You can disable test generation with `--no-spec`.
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
755
|
+
## 📋 List Commands
|
|
756
|
+
|
|
757
|
+
The `castkit list` command provides an interface to view internal Castkit definitions or project-registered components.
|
|
758
|
+
|
|
759
|
+
### 🧾 List Types
|
|
760
|
+
|
|
761
|
+
```bash
|
|
762
|
+
castkit list types
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
Displays a grouped list of:
|
|
766
|
+
|
|
767
|
+
- Native types (defined by Castkit)
|
|
768
|
+
- Custom types (registered via `Castkit.configure`)
|
|
769
|
+
|
|
770
|
+
Example:
|
|
771
|
+
|
|
772
|
+
```bash
|
|
773
|
+
Native Types:
|
|
774
|
+
Castkit::Types::String - :string, :str, :uuid
|
|
775
|
+
|
|
776
|
+
Custom Types:
|
|
777
|
+
MyApp::Types::Money - :money
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
### 🔍 List Validators
|
|
781
|
+
|
|
782
|
+
```bash
|
|
783
|
+
castkit list validators
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
Displays all validator classes defined in `lib/castkit/validators` or custom-defined under `Castkit::Validators`.
|
|
787
|
+
|
|
788
|
+
Castkit validators are tagged `[Castkit]`, and others as `[Custom]`.
|
|
789
|
+
|
|
790
|
+
### 📑 List Contracts
|
|
791
|
+
|
|
792
|
+
```bash
|
|
793
|
+
castkit list contracts
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
Lists all contracts in the `Castkit::Contracts` namespace and related files.
|
|
797
|
+
|
|
798
|
+
### 📦 List DataObjects
|
|
799
|
+
|
|
800
|
+
```bash
|
|
801
|
+
castkit list dataobjects
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
Lists all DTOs in the `Castkit::DataObjects` namespace.
|
|
805
|
+
|
|
806
|
+
### 🧪 List Serializers
|
|
807
|
+
|
|
808
|
+
```bash
|
|
809
|
+
castkit list serializers
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
Lists all serializer classes and their source origin.
|
|
813
|
+
|
|
814
|
+
---
|
|
815
|
+
|
|
816
|
+
## 🧰 Example Usage
|
|
817
|
+
|
|
818
|
+
```bash
|
|
819
|
+
castkit generate dataobject Product name:string price:float
|
|
820
|
+
castkit generate contract ProductInput name:string
|
|
821
|
+
|
|
822
|
+
castkit list types
|
|
823
|
+
castkit list validators
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
The CLI is designed to provide a familiar Rails-like generator experience, tailored for Castkit’s data-first architecture.
|
|
827
|
+
|
|
828
|
+
---
|
|
829
|
+
|
|
546
830
|
## Testing
|
|
547
831
|
|
|
548
832
|
You can test DTOs and Contracts by treating them like plain Ruby objects:
|
data/castkit.gemspec
CHANGED
|
@@ -33,9 +33,12 @@ Gem::Specification.new do |spec|
|
|
|
33
33
|
spec.require_paths = ["lib"]
|
|
34
34
|
|
|
35
35
|
# Runtime dependencies
|
|
36
|
+
spec.add_dependency "thor"
|
|
36
37
|
|
|
37
38
|
# Development dependencies
|
|
38
39
|
spec.add_development_dependency "rspec"
|
|
39
40
|
spec.add_development_dependency "rubocop"
|
|
41
|
+
spec.add_development_dependency "simplecov"
|
|
42
|
+
spec.add_development_dependency "simplecov-cobertura"
|
|
40
43
|
spec.add_development_dependency "yard"
|
|
41
44
|
end
|
data/lib/castkit/attribute.rb
CHANGED
|
@@ -1,46 +1,103 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "castkit"
|
|
3
4
|
require_relative "error"
|
|
4
|
-
require_relative "
|
|
5
|
-
require_relative "
|
|
6
|
-
require_relative "ext/attribute/validation"
|
|
5
|
+
require_relative "attributes/options"
|
|
6
|
+
require_relative "dsl/attribute"
|
|
7
7
|
|
|
8
8
|
module Castkit
|
|
9
|
-
# Represents a typed attribute on a Castkit::DataObject
|
|
9
|
+
# Represents a typed attribute on a `Castkit::DataObject`.
|
|
10
10
|
#
|
|
11
|
-
#
|
|
11
|
+
# This class is responsible for:
|
|
12
|
+
# - Type normalization (symbol, class, or data object)
|
|
13
|
+
# - Default and option resolution
|
|
14
|
+
# - Validation hooks
|
|
15
|
+
# - Access and serialization control
|
|
16
|
+
#
|
|
17
|
+
# Attributes are created automatically when using the DSL in `DataObject`, but
|
|
18
|
+
# can also be created manually or through reusable definitions.
|
|
19
|
+
#
|
|
20
|
+
# @see Castkit::Attributes::Definition
|
|
21
|
+
# @see Castkit::DSL::Attribute::Options
|
|
22
|
+
# @see Castkit::DSL::Attribute::Access
|
|
23
|
+
# @see Castkit::DSL::Attribute::Validation
|
|
12
24
|
class Attribute
|
|
13
|
-
include Castkit::
|
|
14
|
-
|
|
15
|
-
|
|
25
|
+
include Castkit::DSL::Attribute
|
|
26
|
+
|
|
27
|
+
class << self
|
|
28
|
+
# Defines a reusable attribute definition via a DSL wrapper.
|
|
29
|
+
#
|
|
30
|
+
# @param type [Symbol, Class] The base type to define.
|
|
31
|
+
# @param options [Hash] Additional attribute options.
|
|
32
|
+
# @yield The block to configure options or transformations.
|
|
33
|
+
# @return [Array<(Symbol, Hash)>] a tuple of the final type and options hash
|
|
34
|
+
def define(type, **options, &block)
|
|
35
|
+
normalized_type = normalize_type(type)
|
|
36
|
+
Castkit::Attributes::Definition.define(normalized_type, **options, &block)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Normalizes a declared type (symbol, class, or array) for internal usage.
|
|
40
|
+
#
|
|
41
|
+
# @param type [Symbol, Class, Array] the input type
|
|
42
|
+
# @return [Symbol, Class<Castkit::DataObject>] the normalized form
|
|
43
|
+
def normalize_type(type)
|
|
44
|
+
return type.map { |t| normalize_type(t) } if type.is_a?(Array)
|
|
45
|
+
return type if Castkit.dataobject?(type)
|
|
46
|
+
|
|
47
|
+
process_type(type).to_sym
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Converts a raw type into a normalized symbol.
|
|
51
|
+
#
|
|
52
|
+
# Recognized forms:
|
|
53
|
+
# - `TrueClass`/`FalseClass` → `:boolean`
|
|
54
|
+
# - Class → `class.name.downcase.to_sym`
|
|
55
|
+
# - Symbol → passed through
|
|
56
|
+
#
|
|
57
|
+
# @param type [Symbol, Class] the type to convert
|
|
58
|
+
# @return [Symbol] normalized type symbol
|
|
59
|
+
# @raise [Castkit::AttributeError] if the type is invalid
|
|
60
|
+
def process_type(type)
|
|
61
|
+
case type
|
|
62
|
+
when Class
|
|
63
|
+
return :boolean if [TrueClass, FalseClass].include?(type)
|
|
64
|
+
|
|
65
|
+
type.name.downcase.to_sym
|
|
66
|
+
when Symbol
|
|
67
|
+
type
|
|
68
|
+
else
|
|
69
|
+
raise Castkit::AttributeError, "Unknown type: #{type.inspect}"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
16
73
|
|
|
17
74
|
# @return [Symbol] the attribute name
|
|
18
75
|
attr_reader :field
|
|
19
76
|
|
|
20
|
-
# @return [Symbol, Class, Array] the declared
|
|
77
|
+
# @return [Symbol, Class, Array] the declared or normalized type
|
|
21
78
|
attr_reader :type
|
|
22
79
|
|
|
23
|
-
# @return [Hash]
|
|
80
|
+
# @return [Hash] full option hash, including merged defaults
|
|
24
81
|
attr_reader :options
|
|
25
82
|
|
|
26
83
|
# Initializes a new attribute definition.
|
|
27
84
|
#
|
|
28
|
-
# @param field [Symbol] the name
|
|
29
|
-
# @param type [Symbol, Class, Array] the type or
|
|
30
|
-
# @param default [Object, Proc] optional default
|
|
31
|
-
# @param options [Hash] additional
|
|
85
|
+
# @param field [Symbol] the attribute name
|
|
86
|
+
# @param type [Symbol, Class, Array<Symbol, Class>] the type (or list of types)
|
|
87
|
+
# @param default [Object, Proc, nil] optional static or callable default
|
|
88
|
+
# @param options [Hash] additional attribute options
|
|
32
89
|
def initialize(field, type, default: nil, **options)
|
|
33
90
|
@field = field
|
|
34
|
-
@type = normalize_type(type)
|
|
91
|
+
@type = self.class.normalize_type(type)
|
|
35
92
|
@default = default
|
|
36
93
|
@options = populate_options(options)
|
|
37
94
|
|
|
38
95
|
validate!
|
|
39
96
|
end
|
|
40
97
|
|
|
41
|
-
#
|
|
98
|
+
# Converts the attribute definition to a serializable hash.
|
|
42
99
|
#
|
|
43
|
-
# @return [Hash]
|
|
100
|
+
# @return [Hash] the full attribute metadata
|
|
44
101
|
def to_hash
|
|
45
102
|
{
|
|
46
103
|
field: field,
|
|
@@ -55,56 +112,22 @@ module Castkit
|
|
|
55
112
|
|
|
56
113
|
private
|
|
57
114
|
|
|
58
|
-
# Populates default values and
|
|
115
|
+
# Populates default values and prepares internal options.
|
|
59
116
|
#
|
|
60
|
-
# @param options [Hash]
|
|
61
|
-
# @return [Hash]
|
|
117
|
+
# @param options [Hash] the user-provided options
|
|
118
|
+
# @return [Hash] the merged and normalized options
|
|
62
119
|
def populate_options(options)
|
|
63
|
-
options =
|
|
120
|
+
options = Castkit::Attributes::Options::DEFAULTS.merge(options)
|
|
64
121
|
options[:aliases] = Array(options[:aliases] || [])
|
|
65
|
-
options[:of] = normalize_type(options[:of]) if options[:of]
|
|
122
|
+
options[:of] = self.class.normalize_type(options[:of]) if options[:of]
|
|
66
123
|
|
|
67
124
|
options
|
|
68
125
|
end
|
|
69
126
|
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
# @param type [Symbol, Class, Array]
|
|
73
|
-
# @return [Symbol, Class, Array]
|
|
74
|
-
# @raise [Castkit::AttributeError] if the type is not valid
|
|
75
|
-
def normalize_type(type)
|
|
76
|
-
return type.map { |t| normalize_type(t) } if type.is_a?(Array)
|
|
77
|
-
return type if Castkit.dataobject?(type)
|
|
78
|
-
|
|
79
|
-
process_type(type)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Converts a single type value into a normalized internal representation.
|
|
83
|
-
#
|
|
84
|
-
# - Maps `TrueClass`/`FalseClass` to `:boolean`
|
|
85
|
-
# - Converts class names (e.g., `String`, `Integer`) to lowercase symbols
|
|
86
|
-
# - Accepts already-symbolized types (e.g., `:string`)
|
|
87
|
-
#
|
|
88
|
-
# @param type [Class, Symbol] the declared type to process
|
|
89
|
-
# @return [Symbol] the normalized type
|
|
90
|
-
# @raise [Castkit::AttributeError] if the type is not a recognized form
|
|
91
|
-
def process_type(type)
|
|
92
|
-
case type
|
|
93
|
-
when Class
|
|
94
|
-
return :boolean if [TrueClass, FalseClass].include?(type)
|
|
95
|
-
|
|
96
|
-
type.name.downcase.to_sym
|
|
97
|
-
when Symbol
|
|
98
|
-
type
|
|
99
|
-
else
|
|
100
|
-
raise_error!("Unknown type: #{type.inspect}")
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# Raises a Castkit::AttributeError with optional context.
|
|
127
|
+
# Raises a standardized attribute error with context.
|
|
105
128
|
#
|
|
106
|
-
# @param message [String]
|
|
107
|
-
# @param context [Hash, nil]
|
|
129
|
+
# @param message [String] the error message
|
|
130
|
+
# @param context [Hash, nil] optional override for context payload
|
|
108
131
|
# @raise [Castkit::AttributeError]
|
|
109
132
|
def raise_error!(message, context: nil)
|
|
110
133
|
raise Castkit::AttributeError.new(message, context: context || to_h)
|