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.
- checksums.yaml +4 -4
- data/.rspec_status +195 -219
- data/CHANGELOG.md +42 -0
- data/README.md +744 -83
- data/castkit.gemspec +1 -0
- data/lib/castkit/attribute.rb +6 -24
- data/lib/castkit/castkit.rb +61 -10
- 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 +116 -46
- data/lib/castkit/contract/base.rb +168 -0
- data/lib/castkit/contract/data_object.rb +62 -0
- data/lib/castkit/contract/result.rb +74 -0
- data/lib/castkit/contract/validator.rb +248 -0
- data/lib/castkit/contract.rb +67 -0
- data/lib/castkit/{data_object_extensions → core}/attribute_types.rb +21 -7
- data/lib/castkit/{data_object_extensions → core}/attributes.rb +8 -3
- data/lib/castkit/core/config.rb +74 -0
- data/lib/castkit/core/registerable.rb +59 -0
- data/lib/castkit/data_object.rb +56 -67
- data/lib/castkit/error.rb +15 -3
- data/lib/castkit/ext/attribute/access.rb +67 -0
- data/lib/castkit/ext/attribute/error_handling.rb +63 -0
- data/lib/castkit/ext/attribute/options.rb +142 -0
- data/lib/castkit/ext/attribute/validation.rb +85 -0
- data/lib/castkit/ext/data_object/contract.rb +96 -0
- data/lib/castkit/ext/data_object/deserialization.rb +167 -0
- data/lib/castkit/ext/data_object/plugins.rb +86 -0
- data/lib/castkit/ext/data_object/serialization.rb +61 -0
- data/lib/castkit/inflector.rb +47 -0
- 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/base.rb +122 -0
- data/lib/castkit/types/boolean.rb +47 -0
- data/lib/castkit/types/collection.rb +35 -0
- data/lib/castkit/types/date.rb +34 -0
- data/lib/castkit/types/date_time.rb +34 -0
- data/lib/castkit/types/float.rb +46 -0
- data/lib/castkit/types/integer.rb +46 -0
- data/lib/castkit/types/string.rb +44 -0
- data/lib/castkit/types.rb +15 -0
- 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 +2 -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/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 +74 -15
- data/lib/castkit/attribute_extensions/access.rb +0 -65
- data/lib/castkit/attribute_extensions/casting.rb +0 -147
- data/lib/castkit/attribute_extensions/error_handling.rb +0 -83
- data/lib/castkit/attribute_extensions/options.rb +0 -131
- data/lib/castkit/attribute_extensions/serialization.rb +0 -89
- data/lib/castkit/attribute_extensions/validation.rb +0 -72
- data/lib/castkit/data_object_extensions/config.rb +0 -113
- data/lib/castkit/data_object_extensions/deserialization.rb +0 -110
- data/lib/castkit/default_serializer.rb +0 -123
- data/lib/castkit/serializer.rb +0 -92
- 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
|