cattri 0.1.3 → 0.2.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/.github/workflows/main.yml +34 -0
- data/.gitignore +72 -0
- data/.rubocop.yml +6 -3
- data/CHANGELOG.md +41 -0
- data/Gemfile +12 -0
- data/README.md +163 -151
- data/Steepfile +6 -0
- data/bin/console +33 -0
- data/bin/setup +8 -0
- data/cattri.gemspec +5 -5
- data/lib/cattri/attribute.rb +119 -155
- data/lib/cattri/attribute_compiler.rb +104 -0
- data/lib/cattri/attribute_options.rb +183 -0
- data/lib/cattri/attribute_registry.rb +155 -0
- data/lib/cattri/context.rb +124 -106
- data/lib/cattri/context_registry.rb +36 -0
- data/lib/cattri/deferred_attributes.rb +73 -0
- data/lib/cattri/dsl.rb +54 -0
- data/lib/cattri/error.rb +17 -90
- data/lib/cattri/inheritance.rb +35 -0
- data/lib/cattri/initializer_patch.rb +37 -0
- data/lib/cattri/internal_store.rb +104 -0
- data/lib/cattri/introspection.rb +56 -49
- data/lib/cattri/version.rb +3 -1
- data/lib/cattri.rb +38 -99
- data/sig/lib/cattri/attribute.rbs +105 -0
- data/sig/lib/cattri/attribute_compiler.rbs +61 -0
- data/sig/lib/cattri/attribute_options.rbs +150 -0
- data/sig/lib/cattri/attribute_registry.rbs +95 -0
- data/sig/lib/cattri/context.rbs +130 -0
- data/sig/lib/cattri/context_registry.rbs +31 -0
- data/sig/lib/cattri/deferred_attributes.rbs +53 -0
- data/sig/lib/cattri/dsl.rbs +55 -0
- data/sig/lib/cattri/error.rbs +28 -0
- data/sig/lib/cattri/inheritance.rbs +21 -0
- data/sig/lib/cattri/initializer_patch.rbs +26 -0
- data/sig/lib/cattri/internal_store.rbs +75 -0
- data/sig/lib/cattri/introspection.rbs +61 -0
- data/sig/lib/cattri/types.rbs +19 -0
- data/sig/lib/cattri/visibility.rbs +55 -0
- data/sig/lib/cattri.rbs +37 -0
- data/spec/cattri/attribute_compiler_spec.rb +179 -0
- data/spec/cattri/attribute_options_spec.rb +267 -0
- data/spec/cattri/attribute_registry_spec.rb +257 -0
- data/spec/cattri/attribute_spec.rb +297 -0
- data/spec/cattri/context_registry_spec.rb +45 -0
- data/spec/cattri/context_spec.rb +346 -0
- data/spec/cattri/deferred_attrributes_spec.rb +117 -0
- data/spec/cattri/dsl_spec.rb +69 -0
- data/spec/cattri/error_spec.rb +37 -0
- data/spec/cattri/inheritance_spec.rb +60 -0
- data/spec/cattri/initializer_patch_spec.rb +35 -0
- data/spec/cattri/internal_store_spec.rb +139 -0
- data/spec/cattri/introspection_spec.rb +90 -0
- data/spec/cattri/visibility_spec.rb +68 -0
- data/spec/cattri_spec.rb +54 -0
- data/spec/simplecov_helper.rb +21 -0
- data/spec/spec_helper.rb +16 -0
- metadata +79 -6
- data/lib/cattri/attribute_definer.rb +0 -143
- data/lib/cattri/class_attributes.rb +0 -277
- data/lib/cattri/instance_attributes.rb +0 -276
- data/sig/cattri.rbs +0 -4
@@ -1,276 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "attribute_definer"
|
4
|
-
|
5
|
-
module Cattri
|
6
|
-
# Mixin that provides support for defining instance-level attributes.
|
7
|
-
#
|
8
|
-
# This module is included into a class (via `include Cattri`) and exposes
|
9
|
-
# a DSL similar to `attr_accessor`, with enhancements:
|
10
|
-
#
|
11
|
-
# - Lazy or static default values
|
12
|
-
# - Coercion via custom setter blocks
|
13
|
-
# - Visibility control (`:public`, `:protected`, `:private`)
|
14
|
-
# - Read-only or write-only support
|
15
|
-
#
|
16
|
-
# Each defined attribute is stored as metadata and linked to a reader and/or writer.
|
17
|
-
# Values are accessed and stored via standard instance variables.
|
18
|
-
module InstanceAttributes
|
19
|
-
# Hook called when this module is included into a class.
|
20
|
-
#
|
21
|
-
# @param base [Class]
|
22
|
-
# @return [void]
|
23
|
-
def self.included(base)
|
24
|
-
base.extend(ClassMethods)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Defines instance-level attribute DSL methods.
|
28
|
-
module ClassMethods
|
29
|
-
# Defines one or more instance-level attributes with optional default and coercion.
|
30
|
-
#
|
31
|
-
# This method supports defining multiple attributes at once, provided they share the same options.
|
32
|
-
# If a block is given, only one attribute may be defined to avoid ambiguity.
|
33
|
-
#
|
34
|
-
# @example Define multiple attributes with shared defaults
|
35
|
-
# iattr :foo, :bar, default: []
|
36
|
-
#
|
37
|
-
# @example Define a single attribute with coercion
|
38
|
-
# iattr :level do |val|
|
39
|
-
# Integer(val)
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# @param names [Array<Symbol | String>] the names of the attributes to define
|
43
|
-
# @param options [Hash] additional options like `:default`, `:reader`, `:writer`
|
44
|
-
# @option options [Object, Proc] :default the default value or lambda
|
45
|
-
# @option options [Boolean] :reader whether to define a reader method (default: true)
|
46
|
-
# @option options [Boolean] :writer whether to define a writer method (default: true)
|
47
|
-
# @option options [Symbol] :access method visibility (:public, :protected, :private)
|
48
|
-
# @option options [Boolean] :predicate whether to define a predicate-style alias method
|
49
|
-
# (e.g., `foo?`) for the attribute
|
50
|
-
# @yieldparam value [Object] optional custom coercion logic for the setter
|
51
|
-
# @raise [Cattri::AttributeError] or its subclasses, including `Cattri::AttributeDefinedError` or
|
52
|
-
# `Cattri::AttributeDefinitionError` if defining the attribute fails (e.g., if the attribute is
|
53
|
-
# already defined or an error occurs while defining methods)
|
54
|
-
# @return [void]
|
55
|
-
def instance_attribute(*names, **options, &block)
|
56
|
-
raise Cattri::AmbiguousBlockError if names.size > 1 && block_given?
|
57
|
-
|
58
|
-
names.each do |name|
|
59
|
-
if name.end_with?("?")
|
60
|
-
raise Cattri::AttributeError,
|
61
|
-
"Attribute names ending in '?' are not allowed. Use `predicate: true` or `iattr_alias` instead."
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
define_instance_attribute(name, options, block)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Defines a read-only instance-level attribute.
|
70
|
-
#
|
71
|
-
# Equivalent to `instance_attribute(..., writer: false)`
|
72
|
-
#
|
73
|
-
# @param names [Array<Symbol | String>] the names of the attributes to define
|
74
|
-
# @param options [Hash] additional options like `:default`, `:reader`, `:writer`
|
75
|
-
# @option options [Object, Proc] :default the default value or lambda
|
76
|
-
# @option options [Boolean] :reader whether to define a reader method (default: true)
|
77
|
-
# @option options [Symbol] :access method visibility (:public, :protected, :private)
|
78
|
-
# @option options [Boolean] :predicate whether to define a predicate-style alias method
|
79
|
-
# (e.g., `foo?`) for the attribute
|
80
|
-
# @yieldparam value [Object] optional custom coercion logic for the setter
|
81
|
-
# @raise [Cattri::AttributeError] or its subclasses, including `Cattri::AttributeDefinedError` or
|
82
|
-
# `Cattri::AttributeDefinitionError` if defining the attribute fails (e.g., if the attribute is
|
83
|
-
# already defined or an error occurs while defining methods)
|
84
|
-
# @return [void]
|
85
|
-
def instance_attribute_reader(*names, **options)
|
86
|
-
instance_attribute(*names, **options, writer: false)
|
87
|
-
end
|
88
|
-
|
89
|
-
# Defines a write-only instance-level attribute.
|
90
|
-
#
|
91
|
-
# Equivalent to `instance_attribute(..., reader: false)`. The predicate: option is not allowed
|
92
|
-
# when defining writer methods.
|
93
|
-
#
|
94
|
-
# @param names [Array<Symbol | String>] the names of the attributes to define
|
95
|
-
# @param options [Hash] additional options like `:default`, `:reader`, `:writer`
|
96
|
-
# @option options [Object, Proc] :default the default value or lambda
|
97
|
-
# @option options [Boolean] :writer whether to define a writer method (default: true)
|
98
|
-
# @option options [Symbol] :access method visibility (:public, :protected, :private)
|
99
|
-
# @yieldparam value [Object] optional custom coercion logic for the setter
|
100
|
-
# @raise [Cattri::AttributeError] or its subclasses, including `Cattri::AttributeDefinedError` or
|
101
|
-
# `Cattri::AttributeDefinitionError` if defining the attribute fails (e.g., if the attribute is
|
102
|
-
# already defined or an error occurs while defining methods)
|
103
|
-
# @return [void]
|
104
|
-
def instance_attribute_writer(*names, **options, &block)
|
105
|
-
instance_attribute(*names, **options.merge(reader: false, predicate: false), &block)
|
106
|
-
end
|
107
|
-
|
108
|
-
# Updates the setter behavior of an existing instance-level attribute.
|
109
|
-
#
|
110
|
-
# This allows coercion logic to be defined or overridden after the attribute
|
111
|
-
# has been declared using `iattr`, as long as the writer method exists.
|
112
|
-
#
|
113
|
-
# @example Add coercion to an existing attribute
|
114
|
-
# iattr :format
|
115
|
-
# iattr_setter :format do |val|
|
116
|
-
# val.to_s.downcase.to_sym
|
117
|
-
# end
|
118
|
-
#
|
119
|
-
# @param name [Symbol, String] the name of the attribute
|
120
|
-
# @yieldparam value [Object] the value passed to the setter
|
121
|
-
# @yieldreturn [Object] the coerced value to be assigned
|
122
|
-
# @raise [Cattri::AttributeNotDefinedError] if the attribute is not defined or the writer method does not exist
|
123
|
-
# @raise [Cattri::AttributeDefinitionError] if method redefinition fails
|
124
|
-
# @return [void]
|
125
|
-
def instance_attribute_setter(name, &block)
|
126
|
-
attribute = __cattri_instance_attributes[name.to_sym]
|
127
|
-
|
128
|
-
raise Cattri::AttributeNotDefinedError.new(:instance, name) if attribute.nil?
|
129
|
-
raise Cattri::AttributeError, "Cannot define setter for readonly attribute :#{name}" unless attribute[:writer]
|
130
|
-
|
131
|
-
attribute.instance_variable_set(:@setter, attribute.send(:normalize_setter, block))
|
132
|
-
Cattri::AttributeDefiner.define_writer!(attribute, context)
|
133
|
-
end
|
134
|
-
|
135
|
-
# Defines an alias method for an existing instance-level attribute.
|
136
|
-
#
|
137
|
-
# This does **not** register a new attribute; it simply defines a method
|
138
|
-
# (e.g., a predicate-style alias like `foo?`) that delegates to an existing one.
|
139
|
-
#
|
140
|
-
# The alias method inherits the visibility of the original attribute.
|
141
|
-
#
|
142
|
-
# @param alias_name [Symbol, String] the new method name (e.g., `:foo?`)
|
143
|
-
# @param original [Symbol, String] the name of the existing attribute to delegate to (e.g., `:foo`)
|
144
|
-
# @raise [Cattri::AttributeNotDefinedError] if the original attribute is not defined
|
145
|
-
# @return [void]
|
146
|
-
def instance_attribute_alias(alias_name, original)
|
147
|
-
attribute = __cattri_instance_attributes[original.to_sym]
|
148
|
-
raise Cattri::AttributeNotDefinedError.new(:instance, original) if attribute.nil?
|
149
|
-
|
150
|
-
context.define_method(attribute, name: alias_name) { public_send(original) }
|
151
|
-
end
|
152
|
-
|
153
|
-
# Returns a list of defined instance-level attribute names.
|
154
|
-
#
|
155
|
-
# @return [Array<Symbol>]
|
156
|
-
def instance_attributes
|
157
|
-
([self] + ancestors + singleton_class.included_modules)
|
158
|
-
.uniq
|
159
|
-
.select { |mod| mod.respond_to?(:__cattri_instance_attributes, true) }
|
160
|
-
.flat_map { |mod| mod.send(:__cattri_instance_attributes).keys }
|
161
|
-
.uniq
|
162
|
-
end
|
163
|
-
|
164
|
-
# Checks if an instance-level attribute has been defined.
|
165
|
-
#
|
166
|
-
# @param name [Symbol, String]
|
167
|
-
# @return [Boolean]
|
168
|
-
def instance_attribute_defined?(name)
|
169
|
-
__cattri_instance_attributes.key?(name.to_sym)
|
170
|
-
end
|
171
|
-
|
172
|
-
# Returns the full attribute definition for a given name.
|
173
|
-
#
|
174
|
-
# @param name [Symbol, String]
|
175
|
-
# @return [Cattri::Attribute, nil]
|
176
|
-
def instance_attribute_definition(name)
|
177
|
-
__cattri_instance_attributes[name.to_sym]
|
178
|
-
end
|
179
|
-
|
180
|
-
# @!method iattr(name, **options, &block)
|
181
|
-
# Alias for {#instance_attribute}
|
182
|
-
# @see #instance_attribute
|
183
|
-
alias iattr instance_attribute
|
184
|
-
|
185
|
-
# @!method iattr_accessor(name, **options, &block)
|
186
|
-
# Alias for {#instance_attribute}
|
187
|
-
# @see #instance_attribute
|
188
|
-
alias iattr_accessor instance_attribute
|
189
|
-
|
190
|
-
# @!method iattr_reader(name, **options)
|
191
|
-
# Alias for {#instance_attribute_reader}
|
192
|
-
# @see #instance_attribute_reader
|
193
|
-
alias iattr_reader instance_attribute_reader
|
194
|
-
|
195
|
-
# @!method iattr_writer(name, **options, &block)
|
196
|
-
# Alias for {#instance_attribute_writer}
|
197
|
-
# @see #instance_attribute_writer
|
198
|
-
alias iattr_writer instance_attribute_writer
|
199
|
-
|
200
|
-
# @!method iattr_setter(name, &block)
|
201
|
-
# Alias for {#instance_attribute_setter}
|
202
|
-
# @see #instance_attribute_setter
|
203
|
-
alias iattr_setter instance_attribute_setter
|
204
|
-
|
205
|
-
# @!method iattr_alias(name, &block)
|
206
|
-
# Alias for {#instance_attribute_alias}
|
207
|
-
# @see #instance_attribute_alias
|
208
|
-
alias iattr_alias instance_attribute_alias
|
209
|
-
|
210
|
-
# @!method iattrs
|
211
|
-
# Alias for {#instance_attributes}
|
212
|
-
# @see #instance_attributes
|
213
|
-
alias iattrs instance_attributes
|
214
|
-
|
215
|
-
# @!method iattr_defined?(name)
|
216
|
-
# Alias for {#instance_attribute_defined?}
|
217
|
-
# @see #instance_attribute_defined?
|
218
|
-
alias iattr_defined? instance_attribute_defined?
|
219
|
-
|
220
|
-
# @!method iattr_definition(name)
|
221
|
-
# Alias for {#instance_attribute_definition}
|
222
|
-
# @see #instance_attribute_definition
|
223
|
-
alias iattr_definition instance_attribute_definition
|
224
|
-
|
225
|
-
private
|
226
|
-
|
227
|
-
# Defines a single instance-level attribute.
|
228
|
-
#
|
229
|
-
# This is the internal implementation used by {.instance_attribute} and its aliases.
|
230
|
-
# It creates a `Cattri::Attribute`, registers it, and defines the appropriate
|
231
|
-
# reader and/or writer methods on the class.
|
232
|
-
#
|
233
|
-
# @param name [Symbol, String] the attribute name
|
234
|
-
# @param options [Hash] additional options for the attribute
|
235
|
-
# @param block [Proc, nil] optional setter coercion logic
|
236
|
-
#
|
237
|
-
# @raise [Cattri::AttributeDefinedError] if the attribute has already been defined
|
238
|
-
# @raise [Cattri::AttributeDefinitionError] if method definition fails
|
239
|
-
#
|
240
|
-
# @return [void]
|
241
|
-
def define_instance_attribute(name, options, block) # rubocop:disable Metrics/AbcSize
|
242
|
-
options[:access] ||= __cattri_visibility
|
243
|
-
attribute = Cattri::Attribute.new(name, :instance, options, block)
|
244
|
-
|
245
|
-
raise Cattri::AttributeDefinedError.new(:instance, name) if instance_attribute_defined?(attribute.name)
|
246
|
-
|
247
|
-
begin
|
248
|
-
__cattri_instance_attributes[name.to_sym] = attribute
|
249
|
-
Cattri::AttributeDefiner.define_accessor(attribute, context)
|
250
|
-
rescue StandardError => e
|
251
|
-
raise Cattri::AttributeDefinitionError.new(self, attribute, e)
|
252
|
-
end
|
253
|
-
|
254
|
-
context.define_method(attribute, name: :"#{name}?") { !!send(attribute.name) } if options[:predicate]
|
255
|
-
end
|
256
|
-
|
257
|
-
# Internal registry of instance attributes defined on the class.
|
258
|
-
#
|
259
|
-
# @return [Hash{Symbol => Cattri::Attribute}]
|
260
|
-
def __cattri_instance_attributes
|
261
|
-
@__cattri_instance_attributes ||= {}
|
262
|
-
end
|
263
|
-
|
264
|
-
# Returns the context used to define methods for this class.
|
265
|
-
#
|
266
|
-
# Used internally to encapsulate method definition and visibility rules.
|
267
|
-
#
|
268
|
-
# @return [Cattri::Context]
|
269
|
-
# :nocov:
|
270
|
-
def context
|
271
|
-
@context ||= Context.new(self)
|
272
|
-
end
|
273
|
-
# :nocov:
|
274
|
-
end
|
275
|
-
end
|
276
|
-
end
|
data/sig/cattri.rbs
DELETED