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
data/castkit.gemspec
CHANGED
data/lib/castkit/attribute.rb
CHANGED
@@ -1,23 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "error"
|
4
|
-
require_relative "
|
5
|
-
require_relative "
|
6
|
-
require_relative "
|
7
|
-
require_relative "attribute_extensions/access"
|
8
|
-
require_relative "attribute_extensions/validation"
|
9
|
-
require_relative "attribute_extensions/serialization"
|
4
|
+
require_relative "ext/attribute/options"
|
5
|
+
require_relative "ext/attribute/access"
|
6
|
+
require_relative "ext/attribute/validation"
|
10
7
|
|
11
8
|
module Castkit
|
12
9
|
# Represents a typed attribute on a Castkit::DataObject.
|
13
10
|
#
|
14
11
|
# Provides casting, validation, access control, and serialization behavior.
|
15
12
|
class Attribute
|
16
|
-
include Castkit::
|
17
|
-
include Castkit::
|
18
|
-
include Castkit::
|
19
|
-
include Castkit::AttributeExtensions::Validation
|
20
|
-
include Castkit::AttributeExtensions::Serialization
|
13
|
+
include Castkit::Ext::Attribute::Options
|
14
|
+
include Castkit::Ext::Attribute::Access
|
15
|
+
include Castkit::Ext::Attribute::Validation
|
21
16
|
|
22
17
|
# @return [Symbol] the attribute name
|
23
18
|
attr_reader :field
|
@@ -106,19 +101,6 @@ module Castkit
|
|
106
101
|
end
|
107
102
|
end
|
108
103
|
|
109
|
-
# Validates the final value against a validator if required.
|
110
|
-
#
|
111
|
-
# @param value [Object]
|
112
|
-
# @param context [Symbol, String]
|
113
|
-
# @return [void]
|
114
|
-
def validate_value!(value, context:)
|
115
|
-
return if value.nil? && optional?
|
116
|
-
return if type.is_a?(Array) || dataobject?
|
117
|
-
|
118
|
-
validator = options[:validator] || Castkit.configuration.validator_for(type)
|
119
|
-
validator&.call(value, options: options, context: context)
|
120
|
-
end
|
121
|
-
|
122
104
|
# Raises a Castkit::AttributeError with optional context.
|
123
105
|
#
|
124
106
|
# @param message [String]
|
data/lib/castkit/castkit.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "configuration"
|
4
|
+
require_relative "inflector"
|
4
5
|
|
5
6
|
# Castkit is a lightweight, type-safe data object system for Ruby.
|
6
7
|
#
|
@@ -15,42 +16,92 @@ require_relative "configuration"
|
|
15
16
|
#
|
16
17
|
# user = UserDto.new(name: "Alice", age: 30)
|
17
18
|
# user.to_h #=> { name: "Alice", age: 30 }
|
19
|
+
#
|
20
|
+
# @see Castkit::Contract
|
21
|
+
# @see Castkit::DataObject
|
18
22
|
module Castkit
|
23
|
+
# Namespace used for registering generated DataObjects.
|
24
|
+
module DataObjects; end
|
25
|
+
|
26
|
+
# Namespace used for registering generated contracts.
|
27
|
+
module Contracts; end
|
28
|
+
|
29
|
+
# Namespace used for registering generated plugins.
|
30
|
+
module Plugins; end
|
31
|
+
|
19
32
|
class << self
|
20
33
|
# Yields the global configuration object for customization.
|
21
34
|
#
|
22
|
-
# @example
|
35
|
+
# @example Disabling array enforcement
|
23
36
|
# Castkit.configure do |config|
|
24
|
-
# config.
|
37
|
+
# config.enforce_typing = false
|
25
38
|
# end
|
26
39
|
#
|
27
|
-
# @yieldparam config [Castkit::Configuration]
|
40
|
+
# @yieldparam config [Castkit::Configuration] the mutable config object
|
28
41
|
# @return [void]
|
29
42
|
def configure
|
30
43
|
yield(configuration)
|
31
44
|
end
|
32
45
|
|
33
|
-
# Retrieves the global Castkit configuration.
|
46
|
+
# Retrieves the global Castkit configuration instance.
|
34
47
|
#
|
35
|
-
# @return [Castkit::Configuration] the configuration
|
48
|
+
# @return [Castkit::Configuration] the configuration object
|
36
49
|
def configuration
|
37
50
|
@configuration ||= Configuration.new
|
38
51
|
end
|
39
52
|
|
40
|
-
#
|
53
|
+
# Emits a warning to STDERR if `enable_warnings` is enabled in config.
|
41
54
|
#
|
42
|
-
# @param message [String]
|
55
|
+
# @param message [String] the warning message
|
43
56
|
# @return [void]
|
44
57
|
def warning(message)
|
45
58
|
warn message if configuration.enable_warnings
|
46
59
|
end
|
47
60
|
|
48
|
-
#
|
61
|
+
# Checks whether a given object is a subclass of Castkit::DataObject.
|
49
62
|
#
|
50
|
-
# @param obj [Object]
|
51
|
-
# @return [Boolean]
|
63
|
+
# @param obj [Object] the object to test
|
64
|
+
# @return [Boolean] true if obj is a Castkit::DataObject class
|
52
65
|
def dataobject?(obj)
|
53
66
|
obj.is_a?(Class) && obj.ancestors.include?(Castkit::DataObject)
|
54
67
|
end
|
68
|
+
|
69
|
+
# Returns a type caster lambda for the given type.
|
70
|
+
#
|
71
|
+
# Type casting performs both validation and deserialization on the provided value.
|
72
|
+
#
|
73
|
+
# @param type [Symbol] the registered type (e.g. :string)
|
74
|
+
# @return [Proc] a lambda that accepts a value and options and returns a casted result
|
75
|
+
def type_caster(type)
|
76
|
+
type_definition = configuration.fetch_type(type)
|
77
|
+
|
78
|
+
lambda do |value, validator: nil, options: {}, context: nil|
|
79
|
+
type_definition.class.cast!(value, validator: validator, options: options, context: context)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns a serializer lambda for the given type.
|
84
|
+
#
|
85
|
+
# @param type [Symbol] the registered type (e.g. :string)
|
86
|
+
# @return [Proc] a lambda that calls `.serialize` on the type
|
87
|
+
def type_serializer(type)
|
88
|
+
->(value) { configuration.fetch_type(type).serialize(value) }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns a deserializer lambda for the given type.
|
92
|
+
#
|
93
|
+
# @param type [Symbol] the registered type (e.g. :string)
|
94
|
+
# @return [Proc] a lambda that calls `.deserialize` on the type
|
95
|
+
def type_deserializer(type)
|
96
|
+
->(value) { configuration.fetch_type(type).deserialize(value) }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns a validator lambda for the given type.
|
100
|
+
#
|
101
|
+
# @param type [Symbol] the registered type (e.g. :string)
|
102
|
+
# @return [Proc] a lambda that calls `.validate!` on the type
|
103
|
+
def type_validator(type)
|
104
|
+
->(value) { configuration.fetch_type(type).validate!(value) }
|
105
|
+
end
|
55
106
|
end
|
56
107
|
end
|
@@ -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
|
data/lib/castkit/cli.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require_relative "cli/main"
|
5
|
+
|
6
|
+
module Castkit
|
7
|
+
# Entrypoint for Castkit’s command-line interface.
|
8
|
+
#
|
9
|
+
# Delegates to the `Castkit::CLI::Main` Thor class, which defines all CLI commands.
|
10
|
+
#
|
11
|
+
# @example Executing from a binstub
|
12
|
+
# Castkit::CLI.start(ARGV)
|
13
|
+
#
|
14
|
+
module CLI
|
15
|
+
# Starts the Castkit CLI.
|
16
|
+
#
|
17
|
+
# @param args [Array<String>] the command-line arguments
|
18
|
+
# @param kwargs [Hash] additional keyword arguments passed to Thor
|
19
|
+
# @return [void]
|
20
|
+
def self.start(*args, **kwargs)
|
21
|
+
Castkit::CLI::Main.start(*args, **kwargs)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|