castkit 0.1.2 → 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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec_status +195 -219
  3. data/CHANGELOG.md +42 -0
  4. data/README.md +744 -83
  5. data/castkit.gemspec +1 -0
  6. data/lib/castkit/attribute.rb +6 -24
  7. data/lib/castkit/castkit.rb +61 -10
  8. data/lib/castkit/cli/generate.rb +98 -0
  9. data/lib/castkit/cli/list.rb +200 -0
  10. data/lib/castkit/cli/main.rb +43 -0
  11. data/lib/castkit/cli.rb +24 -0
  12. data/lib/castkit/configuration.rb +116 -46
  13. data/lib/castkit/contract/base.rb +168 -0
  14. data/lib/castkit/contract/data_object.rb +62 -0
  15. data/lib/castkit/contract/result.rb +74 -0
  16. data/lib/castkit/contract/validator.rb +248 -0
  17. data/lib/castkit/contract.rb +67 -0
  18. data/lib/castkit/{data_object_extensions → core}/attribute_types.rb +21 -7
  19. data/lib/castkit/{data_object_extensions → core}/attributes.rb +8 -3
  20. data/lib/castkit/core/config.rb +74 -0
  21. data/lib/castkit/core/registerable.rb +59 -0
  22. data/lib/castkit/data_object.rb +56 -67
  23. data/lib/castkit/error.rb +15 -3
  24. data/lib/castkit/ext/attribute/access.rb +67 -0
  25. data/lib/castkit/ext/attribute/error_handling.rb +63 -0
  26. data/lib/castkit/ext/attribute/options.rb +142 -0
  27. data/lib/castkit/ext/attribute/validation.rb +85 -0
  28. data/lib/castkit/ext/data_object/contract.rb +96 -0
  29. data/lib/castkit/ext/data_object/deserialization.rb +167 -0
  30. data/lib/castkit/ext/data_object/plugins.rb +86 -0
  31. data/lib/castkit/ext/data_object/serialization.rb +61 -0
  32. data/lib/castkit/inflector.rb +47 -0
  33. data/lib/castkit/plugins.rb +82 -0
  34. data/lib/castkit/serializers/base.rb +94 -0
  35. data/lib/castkit/serializers/default_serializer.rb +156 -0
  36. data/lib/castkit/types/base.rb +122 -0
  37. data/lib/castkit/types/boolean.rb +47 -0
  38. data/lib/castkit/types/collection.rb +35 -0
  39. data/lib/castkit/types/date.rb +34 -0
  40. data/lib/castkit/types/date_time.rb +34 -0
  41. data/lib/castkit/types/float.rb +46 -0
  42. data/lib/castkit/types/integer.rb +46 -0
  43. data/lib/castkit/types/string.rb +44 -0
  44. data/lib/castkit/types.rb +15 -0
  45. data/lib/castkit/validators/base.rb +59 -0
  46. data/lib/castkit/validators/boolean_validator.rb +39 -0
  47. data/lib/castkit/validators/collection_validator.rb +29 -0
  48. data/lib/castkit/validators/float_validator.rb +31 -0
  49. data/lib/castkit/validators/integer_validator.rb +31 -0
  50. data/lib/castkit/validators/numeric_validator.rb +2 -2
  51. data/lib/castkit/validators/string_validator.rb +3 -4
  52. data/lib/castkit/version.rb +1 -1
  53. data/lib/castkit.rb +2 -0
  54. data/lib/generators/base.rb +97 -0
  55. data/lib/generators/contract.rb +68 -0
  56. data/lib/generators/data_object.rb +48 -0
  57. data/lib/generators/plugin.rb +25 -0
  58. data/lib/generators/serializer.rb +28 -0
  59. data/lib/generators/templates/contract.rb.tt +24 -0
  60. data/lib/generators/templates/contract_spec.rb.tt +76 -0
  61. data/lib/generators/templates/data_object.rb.tt +15 -0
  62. data/lib/generators/templates/data_object_spec.rb.tt +36 -0
  63. data/lib/generators/templates/plugin.rb.tt +37 -0
  64. data/lib/generators/templates/plugin_spec.rb.tt +18 -0
  65. data/lib/generators/templates/serializer.rb.tt +24 -0
  66. data/lib/generators/templates/serializer_spec.rb.tt +14 -0
  67. data/lib/generators/templates/type.rb.tt +55 -0
  68. data/lib/generators/templates/type_spec.rb.tt +42 -0
  69. data/lib/generators/templates/validator.rb.tt +26 -0
  70. data/lib/generators/templates/validator_spec.rb.tt +23 -0
  71. data/lib/generators/type.rb +29 -0
  72. data/lib/generators/validator.rb +41 -0
  73. metadata +74 -15
  74. data/lib/castkit/attribute_extensions/access.rb +0 -65
  75. data/lib/castkit/attribute_extensions/casting.rb +0 -147
  76. data/lib/castkit/attribute_extensions/error_handling.rb +0 -83
  77. data/lib/castkit/attribute_extensions/options.rb +0 -131
  78. data/lib/castkit/attribute_extensions/serialization.rb +0 -89
  79. data/lib/castkit/attribute_extensions/validation.rb +0 -72
  80. data/lib/castkit/data_object_extensions/config.rb +0 -113
  81. data/lib/castkit/data_object_extensions/deserialization.rb +0 -110
  82. data/lib/castkit/default_serializer.rb +0 -123
  83. data/lib/castkit/serializer.rb +0 -92
  84. data/lib/castkit/validators.rb +0 -4
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor/group"
4
+ require "castkit/inflector"
5
+ require_relative "base"
6
+
7
+ module Castkit
8
+ module Generators
9
+ # Generator for creating Castkit DataObject classes.
10
+ #
11
+ # Generates a DataObject class and an optional spec file with attribute definitions.
12
+ # Accepts a list of field definitions in the form `name:type`.
13
+ #
14
+ # Example:
15
+ # $ castkit generate dataobject User name:string active:boolean
16
+ #
17
+ # This will generate:
18
+ # - lib/castkit/data_objects/user.rb
19
+ # - spec/castkit/data_objects/user_spec.rb
20
+ #
21
+ # @see Castkit::Generators::Base
22
+ class DataObject < Castkit::Generators::Base
23
+ component :data_object
24
+
25
+ argument :fields, type: :array, default: [], desc: "Attribute definitions (e.g., name:string active:boolean)"
26
+
27
+ private
28
+
29
+ # @return [Hash] configuration passed into templates
30
+ def config
31
+ super.merge(
32
+ attributes: parsed_fields,
33
+ default_values: default_values
34
+ )
35
+ end
36
+
37
+ # Parses `name:type` fields into attribute definitions.
38
+ #
39
+ # @return [Array<Hash{Symbol => Object}>] list of parsed attribute hashes
40
+ def parsed_fields
41
+ fields.map do |field|
42
+ name, type = field.split(":")
43
+ { name: name, type: (type || "string").to_sym }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor/group"
4
+ require "castkit/inflector"
5
+ require_relative "base"
6
+
7
+ module Castkit
8
+ module Generators
9
+ # Generator for creating Castkit plugin modules.
10
+ #
11
+ # This generator will produce a module under `Castkit::Plugins::<ClassName>` and an optional spec file.
12
+ #
13
+ # Example:
14
+ # $ castkit generate plugin Oj
15
+ #
16
+ # This will generate:
17
+ # - lib/castkit/plugins/oj.rb
18
+ # - spec/castkit/plugins/oj_spec.rb
19
+ #
20
+ # @see Castkit::Generators::Base
21
+ class Plugin < Castkit::Generators::Base
22
+ component :plugin
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor/group"
4
+ require "castkit/inflector"
5
+ require_relative "base"
6
+
7
+ module Castkit
8
+ module Generators
9
+ # Generator for creating a custom Castkit serializer.
10
+ #
11
+ # Serializers inherit from `Castkit::Serializers::Base` and define a custom `#call` method
12
+ # for rendering a `Castkit::DataObject` into a hash representation.
13
+ #
14
+ # Example usage:
15
+ # $ castkit generate serializer Custom
16
+ #
17
+ # Generates:
18
+ # - lib/castkit/serializers/custom.rb
19
+ # - spec/castkit/serializers/custom_spec.rb
20
+ #
21
+ # These files scaffold a `Castkit::Serializers::Custom` serializer with the correct base class.
22
+ #
23
+ # @see Castkit::Generators::Base
24
+ class Serializer < Castkit::Generators::Base
25
+ component :serializer
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castkit
4
+ module Contracts
5
+ # Contract definition for <%= config[:class_name] %>.
6
+ #
7
+ # This contract can be used to validate structured input like:
8
+ #
9
+ # @example Validating input (soft)
10
+ # result = Castkit::Contracts::<%= config[:class_name] %>.validate(params)
11
+ # puts result.inspect # Castkit::Contract::Result instance
12
+ #
13
+ # @example Validating input (hard)
14
+ # being
15
+ # result = Castkit::Contracts::<%= config[:class_name] %>.validate!(params)
16
+ # rescue Castkit::ContractError => e
17
+ # puts e.errors
18
+ # end
19
+ class <%= config[:class_name] %> < Castkit::Contract::Base<% if config[:attributes].empty? %>
20
+ # string :id<% else %><% config[:attributes].each do |attr| %>
21
+ <%= attr[:type] %> :<%= attr[:name] %><% end %><% end %>
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "castkit/contracts/<%= config[:name] %>"
5
+
6
+ RSpec.describe Castkit::Contracts::<%= config[:class_name] %> do
7
+ subject(:contract) { described_class }
8
+ <% if config[:attributes].empty? %>
9
+ let(:attributes) { {} }
10
+ <% else %>
11
+ let(:attributes) do
12
+ {<% config[:attributes].each do |attr| %>
13
+ <%= attr[:name] %>: <%= config[:default_values].fetch(attr[:type], "nil") %>,<% end %>
14
+ }
15
+ end
16
+ <% end %>
17
+ it "is a Castkit::Contract" do
18
+ expect(contract).to be < Castkit::Contract::Base
19
+ end
20
+ <% if config[:attributes].any? %>
21
+ describe ".validate" do
22
+ it "returns success with valid input" do
23
+ result = contract.validate(attributes)
24
+ expect(result).to be_success
25
+ end
26
+
27
+ it "returns failure with missing required fields" do
28
+ field = contract.attributes.keys.first
29
+ contract.attributes[field].options[:required] = true
30
+
31
+ result = contract.validate(attributes.reject { |k| k == field })
32
+ expect(result).to be_failure
33
+ expect(result.errors).to include({ field => "#{field} is required" })
34
+ end
35
+
36
+ it "returns failure with invalid value types" do
37
+ field = :<%= config[:attributes].first[:name] %>
38
+ field_type = :<%= config[:attributes].first[:type] %>
39
+
40
+ result = contract.validate(attributes.merge({ field => <%= config[:invalid_types].fetch(config[:attributes].first[:type], nil) %> }))
41
+ expect(result).to be_failure
42
+ expect(result.errors).to include({ field => "#{field} must be a #{field_type}" })
43
+ end
44
+ end
45
+
46
+ describe ".validate!" do
47
+ it "returns success with valid input" do
48
+ result = contract.validate!(attributes)
49
+ expect(result).to be_success
50
+ end
51
+
52
+ it "raises an error with missing required fields" do
53
+ field = contract.attributes.keys.first
54
+ contract.attributes[field].options[:required] = true
55
+
56
+ expect do
57
+ contract.validate!(attributes.reject { |k| k == field })
58
+ rescue Castkit::ContractError => e
59
+ expect(e.errors).to include({ field => "#{field} is required" })
60
+ raise e
61
+ end.to raise_error(Castkit::ContractError)
62
+ end
63
+
64
+ it "raises an error with invalid value types" do
65
+ field = :<%= config[:attributes].first[:name] %>
66
+ field_type = :<%= config[:attributes].first[:type] %>
67
+
68
+ expect do
69
+ contract.validate!(attributes.merge({ field => <%= config[:invalid_types].fetch(config[:attributes].first[:type], nil) %> }))
70
+ rescue Castkit::ContractError => e
71
+ expect(e.errors).to include({ field => "#{field} must be a #{field_type}" })
72
+ raise e
73
+ end.to raise_error(Castkit::ContractError)
74
+ end
75
+ end<% end %>
76
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castkit
4
+ module DataObjects
5
+ # Data transfer object for <%= config[:class_name] %>.
6
+ #
7
+ # @example Instantiation
8
+ # <%= config[:class_name] %>.new(id: "123", name: "example")
9
+ class <%= config[:class_name] %> < Castkit::DataObject
10
+ <% config[:attributes].each do |attr| -%>
11
+ <%= attr[:type] %> :<%= attr[:name] %>
12
+ <% end -%>
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "castkit/data_objects/<%= config[:name] %>"
5
+
6
+ RSpec.describe Castkit::DataObjects::<%= config[:class_name] %> do
7
+ subject(:instance) { described_class.new(attributes) }
8
+ <% if config[:attributes].empty? %>
9
+ let(:attributes) { {} }
10
+ <% else %>
11
+ let(:attributes) do
12
+ {<% config[:attributes].each do |attr| %>
13
+ <%= attr[:name] %>: <%= config[:default_values].fetch(attr[:type], nil) %>,<% end %>
14
+ }
15
+ end
16
+ <% end %>
17
+ it "is a Castkit::DataObject" do
18
+ expect(described_class).to be < Castkit::DataObject
19
+ end
20
+ <% config[:attributes].each do |attr| %>
21
+ describe "#<%= attr[:name] %>" do
22
+ it "is defined on the DTO" do
23
+ expect(described_class.attributes.keys).to include(:<%= attr[:name] %>)
24
+ end
25
+
26
+ it "returns the attribute options" do
27
+ # test for options set on the attribute
28
+ # `<%= attr[:type] %> :<%= attr[:name] %>, required: true`
29
+ # expect(described_class.attributes[:<%= attr[:name] %>).to include(required: true)
30
+ end
31
+
32
+ it "returns the expected value" do
33
+ expect(instance.<%= attr[:name] %>).to eq(attributes[:<%= attr[:name] %>])
34
+ end
35
+ end
36
+ <% end %>end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castkit
4
+ module Plugins
5
+ # <%= config[:class_name] %> plugin for Castkit::DataObject.
6
+ #
7
+ # This plugin can be enabled via:
8
+ #
9
+ # class MyDto < Castkit::DataObject
10
+ # enable_plugins :<%= config[:name] %>
11
+ # end
12
+ #
13
+ # Or globally:
14
+ #
15
+ # Castkit.configure do |config|
16
+ # config.register_plugin(:<%= config[:name] %>, Castkit::Plugins::<%= config[:class_name] %>)
17
+ # config.default_plugins << :<%= config[:name] %>
18
+ # end
19
+ module <%= config[:class_name] %>
20
+ # Optional setup hook called during plugin activation
21
+ #
22
+ # @param klass [Class<Castkit::DataObject>]
23
+ # @return [void]
24
+ def self.setup!(klass)
25
+ # Custom setup logic here
26
+ end
27
+
28
+ # Optionally define an Extension module to be included into the DataObject class
29
+ #
30
+ # module Extension
31
+ # def custom_behavior
32
+ # # ...
33
+ # end
34
+ # end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "castkit/plugins/<%= config[:name] %>"
5
+
6
+ RSpec.describe Castkit::Plugins::<%= config[:class_name] %> do
7
+ let(:plugin) { described_class }
8
+
9
+ describe ".setup!" do
10
+ let(:klass) do
11
+ Class.new(Castkit::DataObject)
12
+ end
13
+
14
+ it "can be setup on a dataobject class" do
15
+ expect { plugin.setup!(klass) }.not_to raise_error
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "castkit/serializers/base"
4
+
5
+ module Castkit
6
+ module Serializers
7
+ # Serializer for <%= config[:class_name] %> DTOs.
8
+ #
9
+ # This can be applied to a DataObject with:
10
+ #
11
+ # @example
12
+ # class MyDto < Castkit::DataObject
13
+ # serializer Castkit::Serializers::<%= config[:class_name] %>
14
+ # end
15
+ class <%= config[:class_name] %> < Castkit::Serializers::Base
16
+ # Returns a serialized hash version of the object.
17
+ #
18
+ # @return [Hash]
19
+ def call
20
+ object.to_h
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "castkit/serializers/<%= config[:name] %>"
5
+
6
+ RSpec.describe Castkit::Serializers::<%= config[:class_name] %> do
7
+ let(:object) { double("Castkit::DataObject", to_h: { foo: "bar" }) }
8
+
9
+ subject(:serializer) { described_class.new(object) }
10
+
11
+ it "serializes using #call" do
12
+ expect(serializer.call).to eq({ foo: "bar" })
13
+ end
14
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castkit
4
+ module Types
5
+ # Type definition for <%= config[:class_name] %> (:<%= config[:name] %>) attributes.
6
+ #
7
+ # This class is used internally by Castkit when an attribute is defined with:
8
+ #
9
+ # @example Registering <%= config[:class_name] %> as a valid type
10
+ # Castkit.configure do |config|
11
+ # config.register_type(:<%= config[:name] %>, Castkit::Types::<%= config[:class_name] %>, aliases: %i[custom_alias])
12
+ # end
13
+ #
14
+ # @example Defining an attribute
15
+ # class Data < Castkit::DataObject
16
+ # <%= config[:name] %> :attribute_name
17
+ # attribute :attribute_name, :<%= config[:name] %>
18
+ # end
19
+ #
20
+ # @example Defining an attribute using an alias
21
+ # class Data < Castkit::DataObject
22
+ # custom_alias: :attribute_name
23
+ # attribute :attribute_name, :custom_alias
24
+ # end
25
+ class <%= config[:class_name] %> < Castkit::Types::Base
26
+ # Deserializes the input value to a <%= config[:class_name] %> instance.
27
+ #
28
+ # @param value [Object, nil] the value to deserialize
29
+ # @return [<%= config[:class_name] %>] the deserialized value
30
+ def deserialize(value)
31
+ # deserialization logic
32
+ # value.to_s
33
+ end
34
+
35
+ # Serializes the <%= config[:class_name] %> value.
36
+ #
37
+ # @param value [<%= config[:class_name] %>] the value to serialize
38
+ # @return [Object] the serialized value
39
+ def serialize(value)
40
+ # serialization logic
41
+ # value.to_s
42
+ end
43
+
44
+ # Validates the input value with a custom Castkit::Validators::<%= config[:class_name] %> validator.
45
+ #
46
+ # @param value [Object, nil] the value to validate
47
+ # @param options [Hash] the validation options
48
+ # @param context [Hash] the validation context
49
+ def validate!(value, options = {}, context = {})
50
+ # validation logic
51
+ # Castkit::Validators::<%= config[:class_name] %>.call(value, options: options, context: context)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "castkit/types/<%= config[:name] %>"
5
+
6
+ # Spec for Castkit::Types::<%= config[:class_name] %>
7
+ RSpec.describe Castkit::Types::<%= config[:class_name] %> do
8
+ subject(:type) { described_class.new }
9
+
10
+ it "is a subclass of Castkit::Types::Base" do
11
+ expect(described_class).to be < Castkit::Types::Base
12
+ end
13
+
14
+ describe "#deserialize" do
15
+ let(:input) { "input" }
16
+
17
+ it "converts a valid input" do
18
+ # expect(type.deserialize(input)).to eq(expected_value)
19
+ end
20
+ end
21
+
22
+ describe "#serialize" do
23
+ let(:value) { "value" }
24
+
25
+ it "converts the value to a serializable format" do
26
+ # expect(type.serialize(value)).to eq(expected_output)
27
+ end
28
+ end
29
+
30
+ describe "#validate!" do
31
+ let(:valid_value) { "valid" }
32
+ let(:invalid_value) { nil }
33
+
34
+ it "does not raise for valid input" do
35
+ # expect { type.validate!(valid_value) }.not_to raise_error
36
+ end
37
+
38
+ it "raises for invalid input" do
39
+ # expect { type.validate!(invalid_value) }.to raise_error(Castkit::AttributeError)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Castkit
6
+ module Validators
7
+ # Validator for :<%= config[:name] %> attributes.
8
+ #
9
+ # Subclass of Castkit::Validators::Base. Used automatically by attributes or types that declare it.
10
+ #
11
+ # @example Manual use:
12
+ # Castkit::Validators::<%= config[:class_name] %>.call(value, context: :my_field)
13
+ class <%= config[:class_name] %> < Castkit::Validators::Base
14
+ # Validates the value and raises a Castkit::AttributeError if invalid.
15
+ #
16
+ # @param value [Object] The value to validate
17
+ # @param options [Hash] Optional validation options
18
+ # @param context [Symbol] The attribute or context key for error messages
19
+ def call(value, options: {}, context: nil)
20
+ raise Castkit::AttributeError, "#{context} must be present" if value.nil?
21
+
22
+ value
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "castkit/validators/<%= config[:name] %>"
5
+
6
+ RSpec.describe Castkit::Validators::<%= config[:class_name] %> do
7
+ subject(:validator) { described_class.new }
8
+
9
+ let(:context) { :<%= config[:name] %> }
10
+
11
+ describe "#call" do
12
+ it "returns the value if valid" do
13
+ valid = "example"
14
+ expect(validator.call(valid, context: context)).to eq(valid)
15
+ end
16
+
17
+ it "raises for invalid values" do
18
+ expect {
19
+ validator.call(nil, context: context)
20
+ }.to raise_error(Castkit::AttributeError, /#{context} must be present/)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor/group"
4
+ require "castkit/inflector"
5
+ require_relative "base"
6
+
7
+ module Castkit
8
+ module Generators
9
+ # Generator for creating a new Castkit type.
10
+ #
11
+ # Types define custom deserialization, serialization, and validation behavior
12
+ # for attributes used in `Castkit::DataObject` or `Castkit::Contract`.
13
+ #
14
+ # Example usage:
15
+ # $ castkit generate type Money
16
+ #
17
+ # Generates:
18
+ # - lib/castkit/types/money.rb
19
+ # - spec/castkit/types/money_spec.rb
20
+ #
21
+ # These files scaffold a `Castkit::Types::Money` class inheriting from `Castkit::Types::Base`,
22
+ # along with a basic RSpec test suite.
23
+ #
24
+ # @see Castkit::Generators::Base
25
+ class Type < Castkit::Generators::Base
26
+ component :type
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor/group"
4
+ require "castkit/inflector"
5
+ require_relative "base"
6
+
7
+ module Castkit
8
+ module Generators
9
+ # Generator for creating a new Castkit validator.
10
+ #
11
+ # Validators are responsible for asserting that a given value conforms to a rule.
12
+ # They are typically used inside a type’s `#validate!` method or within custom contract logic.
13
+ #
14
+ # Example usage:
15
+ # $ castkit generate validator Money
16
+ #
17
+ # Generates:
18
+ # - lib/castkit/validators/money.rb
19
+ # - spec/castkit/validators/money_spec.rb
20
+ #
21
+ # These files scaffold a `Castkit::Validators::Money` class with a `#call` method
22
+ # and a corresponding RSpec test suite.
23
+ #
24
+ # @see Castkit::Generators::Base
25
+ class Validator < Castkit::Generators::Base
26
+ component :validator
27
+
28
+ private
29
+
30
+ # Provides extra context used within ERB templates for this generator.
31
+ #
32
+ # @return [Hash]
33
+ def config
34
+ super.merge(
35
+ default_value: "example",
36
+ sample_context: "field_name"
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end