cattri 0.1.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 +7 -0
- data/.idea/workspace.xml +350 -0
- data/.rspec +3 -0
- data/.rspec_status +75 -0
- data/.rubocop.yml +36 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +153 -0
- data/Rakefile +12 -0
- data/cattri.gemspec +40 -0
- data/lib/cattri/class_attributes.rb +246 -0
- data/lib/cattri/error.rb +5 -0
- data/lib/cattri/helpers.rb +75 -0
- data/lib/cattri/instance_attributes.rb +198 -0
- data/lib/cattri/introspection.rb +70 -0
- data/lib/cattri/version.rb +5 -0
- data/lib/cattri.rb +36 -0
- data/sig/cattri.rbs +4 -0
- metadata +150 -0
@@ -0,0 +1,198 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cattri
|
4
|
+
# Provides a DSL for defining instance-level attributes with support for:
|
5
|
+
#
|
6
|
+
# - Default values (static or callable)
|
7
|
+
# - Optional reader/writer method generation
|
8
|
+
# - Custom coercion logic for writers
|
9
|
+
# - Full attribute metadata access and reset capabilities
|
10
|
+
#
|
11
|
+
# This module is designed for mixin into classes or modules that want to offer
|
12
|
+
# configurable instance attributes (e.g., plugin systems, DTOs, DSLs).
|
13
|
+
#
|
14
|
+
# Example:
|
15
|
+
#
|
16
|
+
# class MyObject
|
17
|
+
# extend Cattri::InstanceAttributes
|
18
|
+
#
|
19
|
+
# iattr :name, default: "anonymous"
|
20
|
+
# iattr_writer :age, default: 0 do |v|
|
21
|
+
# Integer(v)
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# obj = MyObject.new
|
26
|
+
# obj.name # => "anonymous"
|
27
|
+
# obj.age = "42"
|
28
|
+
# obj.instance_variable_get(:@age) # => 42
|
29
|
+
module InstanceAttributes
|
30
|
+
include Cattri::Helpers
|
31
|
+
|
32
|
+
def self.included(base)
|
33
|
+
base.extend(ClassMethods)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Class-level methods for defining instance attributes
|
37
|
+
module ClassMethods
|
38
|
+
include Cattri::Helpers
|
39
|
+
|
40
|
+
# Default options for all instance attributes
|
41
|
+
DEFAULT_OPTIONS = { default: nil, reader: true, writer: true }.freeze
|
42
|
+
|
43
|
+
# Defines a new instance-level attribute with optional default and coercion.
|
44
|
+
#
|
45
|
+
# @param name [Symbol, String] the name of the attribute
|
46
|
+
# @param options [Hash] additional options like `:default`, `:reader`, `:writer`
|
47
|
+
# @option options [Object, Proc] :default the default value or proc returning the value
|
48
|
+
# @option options [Boolean] :reader whether to define a reader method (default: true)
|
49
|
+
# @option options [Boolean] :writer whether to define a writer method (default: true)
|
50
|
+
# @yield [value] optional coercion logic for the writer
|
51
|
+
# @return [void]
|
52
|
+
def instance_attribute(name, **options, &block)
|
53
|
+
name, definition = define_attribute(name, options, block, DEFAULT_OPTIONS)
|
54
|
+
__cattri_instance_attributes[name] = definition
|
55
|
+
|
56
|
+
define_reader(name, definition) if options.fetch(:reader, true)
|
57
|
+
define_writer(name, definition) if options.fetch(:writer, true)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Defines a read-only instance-level attribute.
|
61
|
+
#
|
62
|
+
# @param name [Symbol, String]
|
63
|
+
# @param options [Hash]
|
64
|
+
# @return [void]
|
65
|
+
def instance_attribute_reader(name, **options)
|
66
|
+
instance_attribute(name, writer: false, **options)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Defines a write-only instance-level attribute.
|
70
|
+
#
|
71
|
+
# @param name [Symbol, String]
|
72
|
+
# @param options [Hash]
|
73
|
+
# @yield [value] coercion logic
|
74
|
+
# @return [void]
|
75
|
+
def instance_attribute_writer(name, **options, &block)
|
76
|
+
instance_attribute(name, reader: false, **options, &block)
|
77
|
+
end
|
78
|
+
|
79
|
+
def __cattri_instance_attributes
|
80
|
+
@__cattri_instance_attributes ||= {}
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns all defined instance-level attribute names.
|
84
|
+
#
|
85
|
+
# @return [Array<Symbol>]
|
86
|
+
def instance_attributes
|
87
|
+
__cattri_instance_attributes.keys
|
88
|
+
end
|
89
|
+
|
90
|
+
# Checks whether an instance attribute is defined.
|
91
|
+
#
|
92
|
+
# @param name [Symbol, String]
|
93
|
+
# @return [Boolean]
|
94
|
+
def instance_attribute_defined?(name)
|
95
|
+
__cattri_instance_attributes.key?(name.to_sym)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Fetches the full definition hash for a specific attribute.
|
99
|
+
#
|
100
|
+
# @param name [Symbol, String]
|
101
|
+
# @return [Hash, nil]
|
102
|
+
def instance_attribute_definition(name)
|
103
|
+
__cattri_instance_attributes[name.to_sym]
|
104
|
+
end
|
105
|
+
|
106
|
+
# @!method iattr(name, **options, &block)
|
107
|
+
# Alias for {.instance_attribute}
|
108
|
+
# @see .instance_attribute
|
109
|
+
alias iattr instance_attribute
|
110
|
+
|
111
|
+
# @!method iattr_accessor(name, **options, &block)
|
112
|
+
# Alias for {.instance_attribute}
|
113
|
+
# @see .instance_attribute
|
114
|
+
alias iattr_accessor instance_attribute
|
115
|
+
|
116
|
+
# @!method iattr_reader(name, **options)
|
117
|
+
# Alias for {.instance_attribute_reader}
|
118
|
+
# @see .instance_attribute_reader
|
119
|
+
alias iattr_reader instance_attribute_reader
|
120
|
+
|
121
|
+
# @!method iattr_writer(name, **options, &block)
|
122
|
+
# Alias for {.instance_attribute_writer}
|
123
|
+
# @see .instance_attribute_writer
|
124
|
+
alias iattr_writer instance_attribute_writer
|
125
|
+
|
126
|
+
# @!method iattrs
|
127
|
+
# @return [Hash<Symbol, Hash>] all defined attributes
|
128
|
+
# @see .instance_attributes
|
129
|
+
alias iattrs instance_attributes
|
130
|
+
|
131
|
+
# @!method iattr_defined?(name)
|
132
|
+
# @return [Boolean]
|
133
|
+
# @see .instance_attribute_defined?
|
134
|
+
alias iattr_defined? instance_attribute_defined?
|
135
|
+
|
136
|
+
# @!method iattr_for(name)
|
137
|
+
# @return [Hash, nil]
|
138
|
+
# @see .instance_attribute_definition
|
139
|
+
alias iattr_definition instance_attribute_definition
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
# Defines the reader method for an instance attribute.
|
144
|
+
#
|
145
|
+
# @param name [Symbol] attribute name
|
146
|
+
# @param definition [Hash] full attribute definition
|
147
|
+
def define_reader(name, definition)
|
148
|
+
ivar = definition[:ivar]
|
149
|
+
|
150
|
+
define_method(name) do
|
151
|
+
return instance_variable_get(ivar) if instance_variable_defined?(ivar)
|
152
|
+
|
153
|
+
value = definition[:default].call
|
154
|
+
instance_variable_set(ivar, value)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Defines the writer method for an instance attribute.
|
159
|
+
#
|
160
|
+
# @param name [Symbol] attribute name
|
161
|
+
# @param definition [Hash] full attribute definition
|
162
|
+
def define_writer(name, definition)
|
163
|
+
define_method("#{name}=") do |value|
|
164
|
+
coerced_value = definition[:setter].call(value)
|
165
|
+
instance_variable_set(definition[:ivar], coerced_value)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Resets all defined attributes to their default values.
|
171
|
+
#
|
172
|
+
# @return [void]
|
173
|
+
def reset_instance_attributes!
|
174
|
+
reset_attributes!(self, self.class.__cattri_instance_attributes.values)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Resets a specific attribute to its default value.
|
178
|
+
#
|
179
|
+
# @param name [Symbol, String]
|
180
|
+
# @return [void]
|
181
|
+
def reset_instance_attribute!(name)
|
182
|
+
definition = self.class.__cattri_instance_attributes[name]
|
183
|
+
return unless definition
|
184
|
+
|
185
|
+
reset_attributes!(self, [definition])
|
186
|
+
end
|
187
|
+
|
188
|
+
# @!method reset_iattrs!
|
189
|
+
# @return [void]
|
190
|
+
# @see .reset_instance_attributes!
|
191
|
+
alias reset_iattrs! reset_instance_attributes!
|
192
|
+
|
193
|
+
# @!method reset_iattr!(name)
|
194
|
+
# @return [void]
|
195
|
+
# @see .reset_instance_attribute!
|
196
|
+
alias reset_iattr! reset_instance_attribute!
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cattri
|
4
|
+
# Adds debugging and inspection helpers for reading the current values of
|
5
|
+
# class- and instance-level attributes defined via Cattri.
|
6
|
+
#
|
7
|
+
# This module is intended for use in development and test environments to
|
8
|
+
# help capture attribute state at a given moment.
|
9
|
+
#
|
10
|
+
# It can be included in any class or module that uses {Cattri::ClassAttributes}
|
11
|
+
# or {Cattri::InstanceAttributes}.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# class MyConfig
|
15
|
+
# extend Cattri::ClassAttributes
|
16
|
+
# include Cattri::Introspection
|
17
|
+
#
|
18
|
+
# cattr :items, default: []
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# MyConfig.items << :a
|
22
|
+
# MyConfig.snapshot_class_attributes #=> { items: [:a] }
|
23
|
+
module Introspection
|
24
|
+
# Hook called when the module is included. Extends the base with class methods.
|
25
|
+
#
|
26
|
+
# @param base [Class, Module]
|
27
|
+
# @return [void]
|
28
|
+
def self.included(base)
|
29
|
+
base.extend(ClassMethods)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Class-level methods for introspection.
|
33
|
+
module ClassMethods
|
34
|
+
# Returns a hash of current class attribute values.
|
35
|
+
#
|
36
|
+
# @return [Hash<Symbol, Object>] a snapshot of each defined class attribute
|
37
|
+
def snapshot_class_attributes
|
38
|
+
return {} unless respond_to?(:class_attributes)
|
39
|
+
|
40
|
+
class_attributes.each_with_object({}) do |attribute, hash|
|
41
|
+
hash[attribute] = send(attribute)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @!method snapshot_cattrs
|
46
|
+
# Alias for {.snapshot_class_attributes}
|
47
|
+
# @see .snapshot_class_attributes
|
48
|
+
alias snapshot_cattrs snapshot_class_attributes
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns a hash of current instance attribute values for this object.
|
52
|
+
#
|
53
|
+
# @return [Hash<Symbol, Object>] a snapshot of each defined instance attribute
|
54
|
+
def snapshot_instance_attributes
|
55
|
+
return {} unless self.class.respond_to?(:instance_attributes)
|
56
|
+
|
57
|
+
self.class.instance_attributes.each_with_object({}) do |attribute, hash|
|
58
|
+
hash[attribute] = send(attribute)
|
59
|
+
rescue NoMethodError
|
60
|
+
# Catch for write-only methods
|
61
|
+
hash[attribute] = instance_variable_get(:"@#{attribute}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @!method snapshot_iattrs
|
66
|
+
# Alias for {#snapshot_instance_attributes}
|
67
|
+
# @see #snapshot_instance_attributes
|
68
|
+
alias snapshot_iattrs snapshot_instance_attributes
|
69
|
+
end
|
70
|
+
end
|
data/lib/cattri.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "cattri/version"
|
4
|
+
require_relative "cattri/class_attributes"
|
5
|
+
require_relative "cattri/instance_attributes"
|
6
|
+
require_relative "cattri/introspection"
|
7
|
+
|
8
|
+
# The primary entry point for the Cattri gem.
|
9
|
+
#
|
10
|
+
# When included, it adds support for both class-level and instance-level
|
11
|
+
# attribute declarations using `cattr` and `iattr` style methods.
|
12
|
+
#
|
13
|
+
# This module does **not** include introspection helpers by default —
|
14
|
+
# use `include Cattri::Introspection` explicitly if needed.
|
15
|
+
#
|
16
|
+
# @example Using both class and instance attributes
|
17
|
+
# class MyConfig
|
18
|
+
# include Cattri
|
19
|
+
#
|
20
|
+
# cattr :enabled, default: true
|
21
|
+
# iattr :name, default: "anonymous"
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# MyConfig.enabled # => true
|
25
|
+
# MyConfig.new.name # => "anonymous"
|
26
|
+
module Cattri
|
27
|
+
# Hook triggered when `include Cattri` is called.
|
28
|
+
# Adds class and instance attribute support to the target.
|
29
|
+
#
|
30
|
+
# @param base [Class, Module] the receiving class or module
|
31
|
+
# @return [void]
|
32
|
+
def self.included(base)
|
33
|
+
base.extend(Cattri::ClassAttributes)
|
34
|
+
base.include(Cattri::InstanceAttributes)
|
35
|
+
end
|
36
|
+
end
|
data/sig/cattri.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cattri
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nathan Lucas
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-04-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: simplecov
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov-cobertura
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov-html
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: yard
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Cattri provides a clean DSL for defining class-level and instance-level
|
98
|
+
attributes with optional defaults, coercion, accessors, and inheritance support.
|
99
|
+
email:
|
100
|
+
- bnlucas@outlook.com
|
101
|
+
executables: []
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".idea/workspace.xml"
|
106
|
+
- ".rspec"
|
107
|
+
- ".rspec_status"
|
108
|
+
- ".rubocop.yml"
|
109
|
+
- CHANGELOG.md
|
110
|
+
- CODE_OF_CONDUCT.md
|
111
|
+
- LICENSE.txt
|
112
|
+
- README.md
|
113
|
+
- Rakefile
|
114
|
+
- cattri.gemspec
|
115
|
+
- lib/cattri.rb
|
116
|
+
- lib/cattri/class_attributes.rb
|
117
|
+
- lib/cattri/error.rb
|
118
|
+
- lib/cattri/helpers.rb
|
119
|
+
- lib/cattri/instance_attributes.rb
|
120
|
+
- lib/cattri/introspection.rb
|
121
|
+
- lib/cattri/version.rb
|
122
|
+
- sig/cattri.rbs
|
123
|
+
homepage: https://github.com/bnlucas/cattri
|
124
|
+
licenses:
|
125
|
+
- MIT
|
126
|
+
metadata:
|
127
|
+
homepage_uri: https://github.com/bnlucas/cattri
|
128
|
+
source_code_uri: https://github.com/bnlucas/cattri
|
129
|
+
changelog_uri: https://github.com/bnlucas/cattri/blob/main/CHANGELOG.md
|
130
|
+
rubygems_mfa_required: 'true'
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: 2.7.0
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
requirements: []
|
146
|
+
rubygems_version: 3.5.4
|
147
|
+
signing_key:
|
148
|
+
specification_version: 4
|
149
|
+
summary: Simple class and instance attribute DSL for Ruby.
|
150
|
+
test_files: []
|