shale-builder 0.4.1 → 0.5.1
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/CHANGELOG.md +16 -0
- data/Gemfile.lock +1 -1
- data/README.md +35 -0
- data/lib/shale/attribute.rb +16 -3
- data/lib/shale/builder/assigned_attributes.rb +94 -0
- data/lib/shale/builder/nested_validations.rb +1 -1
- data/lib/shale/builder/value.rb +30 -0
- data/lib/shale/builder/version.rb +1 -1
- data/lib/shale/builder.rb +55 -22
- data/lib/tapioca/dsl/compilers/shale.rb +12 -4
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5129b4e45147ec672d0e23aaf17ad57290bdc9f1f4a81b9d1756ebd44a738df3
|
|
4
|
+
data.tar.gz: 0e47f511519fe4003622c862799ccd34e51bf61bba7bbce3ca6d8e81bdac95d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4682a5bf3469eec462ad9a191434bb761c633cf21117e2443dba85cefc4a2e2641104c4966cd113d76200b95954206b3292fc1182836b550a085cf4d144625c6
|
|
7
|
+
data.tar.gz: 349e8086105a41ee0b5decb2ebbedcb810f7a331ae8b0e2b6a1bdab051eeafb9939e5d36f7d4b841062d7eab3269f85fb0b07d77eb63d3abfdae24ea9b774b7d
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.1] - 2025-10-15
|
|
9
|
+
|
|
10
|
+
[Diff](https://github.com/Verseth/ruby-shale-builder/compare/v0.5.0...v0.5.1)
|
|
11
|
+
|
|
12
|
+
### Changes
|
|
13
|
+
- Add `return_type` and `setter_type` overrides per attribute
|
|
14
|
+
|
|
15
|
+
## [0.5.0] - 2025-10-15
|
|
16
|
+
|
|
17
|
+
[Diff](https://github.com/Verseth/ruby-shale-builder/compare/v0.4.1...v0.5.0)
|
|
18
|
+
|
|
19
|
+
### Changes
|
|
20
|
+
- Add `Shale::Builder::AssignedAttributes` module which grants a shale mapper class with the ability to record which attributes have been assigned
|
|
21
|
+
- Add `Shale::Builder::Value` which represents a value of a shale attribute.
|
|
22
|
+
- Add `Shale::Builder#attribute_values`, a method that returns an array of `Shale::Builder::Value` objects for each attribute.
|
|
23
|
+
|
|
8
24
|
## [0.4.1] - 2025-10-14
|
|
9
25
|
|
|
10
26
|
[Diff](https://github.com/Verseth/ruby-shale-builder/compare/v0.4.0...v0.4.1)
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -263,6 +263,41 @@ obj.errors.messages #=> {cvv_code: ["can't be blank"], "amount.value": ["can't b
|
|
|
263
263
|
|
|
264
264
|
You MUST include `ActiveModel::Validations` before `Shale::Builder::NestedValidations`.
|
|
265
265
|
|
|
266
|
+
### Recording Assigned Attributes
|
|
267
|
+
|
|
268
|
+
There is an additional module `Shale::Builder::AssignedAttributes` that provides
|
|
269
|
+
support for recording which attributes have been assigned in a shale mapper object.
|
|
270
|
+
|
|
271
|
+
In order to load it do:
|
|
272
|
+
|
|
273
|
+
```rb
|
|
274
|
+
require 'shale/builder/assigned_attributes'
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Then you can use it like so
|
|
278
|
+
|
|
279
|
+
```rb
|
|
280
|
+
class AmountType < ::Shale::Mapper
|
|
281
|
+
include ::Shale::Builder
|
|
282
|
+
include ::Shale::Builder::AssignedAttributes
|
|
283
|
+
|
|
284
|
+
attribute :value, ::Shale::Type::Float
|
|
285
|
+
attribute :currency, ::Shale::Type::String
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
obj = AmountType.new
|
|
289
|
+
obj.assigned_attribute_names #=> #<Set: {}>
|
|
290
|
+
|
|
291
|
+
obj.value = 3
|
|
292
|
+
obj.assigned_attribute_names #=> #<Set: {:value}>
|
|
293
|
+
obj.assigned_attributes #=> [#<Shale::Attribute:0x000000011e959b50 @collection=false, @default=nil, @doc=nil, @name=:value, @setter="value=", @type=Shale::Type::Float>]
|
|
294
|
+
obj.assigned_values #=> [#<Shale::Builder::Value:0x000000011d693318 @attribute=#<Shale::Attribute:0x000000011e959b50 @collection=false, @default=nil, @doc=nil, @name=:value, @setter="value=", @type=Shale::Type::Float>, @value=3.0>]
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
- `assigned_attribute_names` returns a set of attribute names
|
|
298
|
+
- `assigned_attributes` returns an array of attribute definitions
|
|
299
|
+
- `assigned_values` returns an array of `Shale::Builder::Value`, which contains the current value of an attribute and a reference to its definition
|
|
300
|
+
|
|
266
301
|
### Attribute aliases
|
|
267
302
|
|
|
268
303
|
You can easily create aliases for attributes using `alias_attribute`
|
data/lib/shale/attribute.rb
CHANGED
|
@@ -7,17 +7,23 @@ module Shale
|
|
|
7
7
|
class Attribute # rubocop:disable Style/Documentation
|
|
8
8
|
extend T::Sig
|
|
9
9
|
|
|
10
|
+
#: untyped
|
|
11
|
+
attr_accessor :setter_type
|
|
12
|
+
|
|
13
|
+
#: untyped
|
|
14
|
+
attr_accessor :return_type
|
|
15
|
+
|
|
10
16
|
# Contains the documentation comment for the shale attribute
|
|
11
17
|
# in a Ruby String.
|
|
12
|
-
|
|
18
|
+
#: String?
|
|
13
19
|
attr_accessor :doc
|
|
14
20
|
|
|
15
21
|
# Contains the documentation comment for the shale attribute
|
|
16
22
|
# in a Ruby String.
|
|
17
|
-
|
|
23
|
+
#: Array[Symbol]?
|
|
18
24
|
attr_accessor :aliases
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
#: -> Array[Symbol]
|
|
21
27
|
def all_names
|
|
22
28
|
names = [name]
|
|
23
29
|
aliases = self.aliases
|
|
@@ -25,5 +31,12 @@ module Shale
|
|
|
25
31
|
|
|
26
32
|
names + aliases
|
|
27
33
|
end
|
|
34
|
+
|
|
35
|
+
# Returns `true` if the attribute is handled by a shale mapper.
|
|
36
|
+
#
|
|
37
|
+
#: -> bool
|
|
38
|
+
def mapper?
|
|
39
|
+
type.is_a?(Class) && type < Shale::Mapper
|
|
40
|
+
end
|
|
28
41
|
end
|
|
29
42
|
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'shale'
|
|
5
|
+
require 'booleans'
|
|
6
|
+
|
|
7
|
+
module Shale
|
|
8
|
+
module Builder
|
|
9
|
+
# Include in a class that already includes `Shale::Builder` to add support
|
|
10
|
+
# for getting a list of attributes that have been assigned.
|
|
11
|
+
#
|
|
12
|
+
# @requires_ancestor: Object
|
|
13
|
+
module AssignedAttributes
|
|
14
|
+
extend T::Sig
|
|
15
|
+
extend T::Helpers
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
extend T::Sig
|
|
19
|
+
|
|
20
|
+
# Gets called after including this module in a module or class.
|
|
21
|
+
#: (Module mod) -> void
|
|
22
|
+
def included(mod)
|
|
23
|
+
mod.extend ClassMethods
|
|
24
|
+
AssignedAttributes.prepare_mod(mod)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Prepares the received module or class
|
|
28
|
+
# for dynamic method definition.
|
|
29
|
+
#: (Module mod) -> void
|
|
30
|
+
def prepare_mod(mod)
|
|
31
|
+
assigned_attributes_methods_module = ::Module.new
|
|
32
|
+
mod.instance_variable_set :@assigned_attributes_methods_module, assigned_attributes_methods_module
|
|
33
|
+
mod.include assigned_attributes_methods_module
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @requires_ancestor: singleton(Shale::Mapper)
|
|
38
|
+
module ClassMethods
|
|
39
|
+
extend T::Sig
|
|
40
|
+
|
|
41
|
+
# Contains overridden getter methods for attributes
|
|
42
|
+
# with complex types (so that they accept a block for building)
|
|
43
|
+
#: Module
|
|
44
|
+
attr_reader :assigned_attributes_methods_module
|
|
45
|
+
|
|
46
|
+
#: (String | Symbol name, Class type, ?collection: bool, ?default: Proc?, ?doc: String?, **untyped kwargs) ?{ -> void } -> void
|
|
47
|
+
def attribute(name, type, collection: false, default: nil, doc: nil, **kwargs, &block)
|
|
48
|
+
super
|
|
49
|
+
|
|
50
|
+
@assigned_attributes_methods_module.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
51
|
+
def #{name}=(val)
|
|
52
|
+
super
|
|
53
|
+
return unless @__initialized
|
|
54
|
+
|
|
55
|
+
self.assigned_attribute_names << #{name.to_sym.inspect}
|
|
56
|
+
end
|
|
57
|
+
RUBY
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
mixes_in_class_methods ClassMethods
|
|
61
|
+
|
|
62
|
+
# Returns a set of names of assigned shale attributes.
|
|
63
|
+
#
|
|
64
|
+
#: -> Set[Symbol]
|
|
65
|
+
def assigned_attribute_names
|
|
66
|
+
@assigned_attribute_names ||= Set.new
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Returns an array of shale attributes
|
|
70
|
+
# that have been assigned.
|
|
71
|
+
#
|
|
72
|
+
#: -> Array[Shale::Attribute]
|
|
73
|
+
def assigned_attributes
|
|
74
|
+
klass = self.class #: as untyped
|
|
75
|
+
assigned_attribute_names.map do |name|
|
|
76
|
+
klass.attributes.fetch(name)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns an array of shale values
|
|
81
|
+
# that have been assigned.
|
|
82
|
+
#
|
|
83
|
+
#: -> Array[Shale::Builder::Value]
|
|
84
|
+
def assigned_values
|
|
85
|
+
klass = self.class #: as untyped
|
|
86
|
+
assigned_attribute_names.map do |name|
|
|
87
|
+
attr = klass.attributes.fetch(name)
|
|
88
|
+
Shale::Builder::Value.new(attr, public_send(attr.name))
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -6,7 +6,7 @@ require 'booleans'
|
|
|
6
6
|
|
|
7
7
|
module Shale
|
|
8
8
|
module Builder
|
|
9
|
-
# Include in a class
|
|
9
|
+
# Include in a class that already includes `Shale::Builder` to add support
|
|
10
10
|
# for nested ActiveModel validations.
|
|
11
11
|
#
|
|
12
12
|
# @requires_ancestor: Object
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'shale'
|
|
5
|
+
|
|
6
|
+
module Shale
|
|
7
|
+
module Builder
|
|
8
|
+
# Represents a value of a particular shale attribute.
|
|
9
|
+
# Hold the value and a reference to the attribute definition.
|
|
10
|
+
class Value
|
|
11
|
+
extend T::Sig
|
|
12
|
+
|
|
13
|
+
# Shale attribute definition
|
|
14
|
+
#
|
|
15
|
+
#: Shale::Attribute
|
|
16
|
+
attr_reader :attribute
|
|
17
|
+
|
|
18
|
+
# Value of the attribute.
|
|
19
|
+
#
|
|
20
|
+
#: untyped
|
|
21
|
+
attr_reader :value
|
|
22
|
+
|
|
23
|
+
#: (Shale::Attribute, untyped) -> void
|
|
24
|
+
def initialize(attribute, value)
|
|
25
|
+
@attribute = attribute
|
|
26
|
+
@value = value
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/shale/builder.rb
CHANGED
|
@@ -39,6 +39,7 @@ module Shale
|
|
|
39
39
|
# end
|
|
40
40
|
# end
|
|
41
41
|
#
|
|
42
|
+
# @requires_ancestor: Object
|
|
42
43
|
module Builder
|
|
43
44
|
extend T::Sig
|
|
44
45
|
extend T::Helpers
|
|
@@ -47,7 +48,7 @@ module Shale
|
|
|
47
48
|
extend T::Sig
|
|
48
49
|
|
|
49
50
|
# Gets called after including this module in a module or class.
|
|
50
|
-
|
|
51
|
+
#: (Module mod) -> void
|
|
51
52
|
def included(mod)
|
|
52
53
|
mod.extend ClassMethods
|
|
53
54
|
Builder.prepare_mod(mod)
|
|
@@ -55,7 +56,7 @@ module Shale
|
|
|
55
56
|
|
|
56
57
|
# Prepares the received module or class
|
|
57
58
|
# for dynamic method definition.
|
|
58
|
-
|
|
59
|
+
#: (Module mod) -> void
|
|
59
60
|
def prepare_mod(mod)
|
|
60
61
|
builder_methods_module = ::Module.new
|
|
61
62
|
mod.instance_variable_set :@builder_methods_module, builder_methods_module
|
|
@@ -71,7 +72,7 @@ module Shale
|
|
|
71
72
|
abstract!
|
|
72
73
|
has_attached_class!
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
#: (Class subclass) -> void
|
|
75
76
|
def inherited(subclass)
|
|
76
77
|
super
|
|
77
78
|
Builder.prepare_mod(subclass)
|
|
@@ -79,10 +80,10 @@ module Shale
|
|
|
79
80
|
|
|
80
81
|
# Contains overridden getter methods for attributes
|
|
81
82
|
# with complex types (so that they accept a block for building)
|
|
82
|
-
|
|
83
|
+
#: Module
|
|
83
84
|
attr_reader :builder_methods_module
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
#: { (instance arg0) -> void } -> instance
|
|
86
87
|
def build(&_block)
|
|
87
88
|
body = new
|
|
88
89
|
yield(body)
|
|
@@ -96,23 +97,34 @@ module Shale
|
|
|
96
97
|
sig { abstract.returns(T::Hash[Symbol, Shale::Attribute]) }
|
|
97
98
|
def attributes; end
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
100
|
+
#: (
|
|
101
|
+
#| String | Symbol name,
|
|
102
|
+
#| Class shale_mapper,
|
|
103
|
+
#| ?collection: bool,
|
|
104
|
+
#| ?default: Proc?,
|
|
105
|
+
#| ?doc: String?,
|
|
106
|
+
#| ?return_type: untyped,
|
|
107
|
+
#| ?setter_type: untyped,
|
|
108
|
+
#| **Object kwargs
|
|
109
|
+
#| ) ?{ -> void } -> void
|
|
110
|
+
def attribute(
|
|
111
|
+
name,
|
|
112
|
+
shale_mapper,
|
|
113
|
+
collection: false,
|
|
114
|
+
default: nil,
|
|
115
|
+
doc: nil,
|
|
116
|
+
return_type: nil,
|
|
117
|
+
setter_type: nil,
|
|
118
|
+
**kwargs,
|
|
119
|
+
&block
|
|
120
|
+
)
|
|
121
|
+
super(name, shale_mapper, collection:, default:, **kwargs, &block)
|
|
112
122
|
if (attr_def = attributes[name.to_sym])
|
|
113
123
|
attr_def.doc = doc
|
|
124
|
+
attr_def.return_type = return_type
|
|
125
|
+
attr_def.setter_type = setter_type
|
|
114
126
|
end
|
|
115
|
-
return unless
|
|
127
|
+
return unless shale_mapper < ::Shale::Mapper
|
|
116
128
|
|
|
117
129
|
if collection
|
|
118
130
|
@builder_methods_module.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
@@ -120,7 +132,7 @@ module Shale
|
|
|
120
132
|
return super unless block_given? # return super unless block_given?
|
|
121
133
|
#
|
|
122
134
|
arr = self.#{name} ||= [] # arr = self.clients ||= []
|
|
123
|
-
object = #{
|
|
135
|
+
object = #{shale_mapper}.new # object = Client.new
|
|
124
136
|
yield(object) # yield(object)
|
|
125
137
|
arr << object # arr << object
|
|
126
138
|
object # object
|
|
@@ -133,7 +145,7 @@ module Shale
|
|
|
133
145
|
def #{name} # def amount
|
|
134
146
|
return super unless block_given? # return super unless block_given?
|
|
135
147
|
#
|
|
136
|
-
object = #{
|
|
148
|
+
object = #{shale_mapper}.new # object = Amount.new
|
|
137
149
|
yield(object) # yield(object)
|
|
138
150
|
self.#{name} = object # self.amount = object
|
|
139
151
|
end # end
|
|
@@ -141,7 +153,7 @@ module Shale
|
|
|
141
153
|
end
|
|
142
154
|
|
|
143
155
|
# Create an alias for the getter and setter of an attribute.
|
|
144
|
-
|
|
156
|
+
#: (Symbol new_name, Symbol old_name) -> void
|
|
145
157
|
def alias_attribute(new_name, old_name)
|
|
146
158
|
attr = attributes.fetch(old_name)
|
|
147
159
|
(attr.aliases ||= []) << new_name
|
|
@@ -160,5 +172,26 @@ module Shale
|
|
|
160
172
|
end
|
|
161
173
|
mixes_in_class_methods(ClassMethods)
|
|
162
174
|
|
|
175
|
+
def initialize(*args, **kwargs, &block)
|
|
176
|
+
super
|
|
177
|
+
@__initialized = true
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
#: bool?
|
|
181
|
+
attr_reader :__initialized
|
|
182
|
+
|
|
183
|
+
# Returns an array of shale values
|
|
184
|
+
# that have been assigned.
|
|
185
|
+
#
|
|
186
|
+
#: -> Array[Shale::Builder::Value]
|
|
187
|
+
def attribute_values
|
|
188
|
+
klass = self.class #: as untyped
|
|
189
|
+
klass.attributes.map do |name, attr|
|
|
190
|
+
Shale::Builder::Value.new(attr, public_send(name))
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
163
194
|
end
|
|
164
195
|
end
|
|
196
|
+
|
|
197
|
+
require_relative 'builder/value'
|
|
@@ -37,9 +37,13 @@ module Tapioca
|
|
|
37
37
|
# For each attribute defined in the class
|
|
38
38
|
attribute_names = constant.attributes.keys.sort
|
|
39
39
|
attribute_names.each do |attribute_name|
|
|
40
|
-
attribute =
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
attribute = constant.attributes[attribute_name] #: ::Shale::Attribute
|
|
41
|
+
if (type = attribute.return_type)
|
|
42
|
+
return_type = type
|
|
43
|
+
else
|
|
44
|
+
return_type, nilable = shale_type_to_sorbet_return_type(attribute)
|
|
45
|
+
end
|
|
46
|
+
comments = [] #: T::Array[RBI::Comment]
|
|
43
47
|
if shale_builder_defined? && attribute.doc
|
|
44
48
|
comments << RBI::Comment.new(T.must(attribute.doc))
|
|
45
49
|
end
|
|
@@ -52,7 +56,11 @@ module Tapioca
|
|
|
52
56
|
getter_without_block_type = return_type.to_s
|
|
53
57
|
end
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
if (type = attribute.return_type || attribute.setter_type)
|
|
60
|
+
setter_type = type
|
|
61
|
+
else
|
|
62
|
+
setter_type, nilable = shale_type_to_sorbet_setter_type(attribute)
|
|
63
|
+
end
|
|
56
64
|
if attribute.collection?
|
|
57
65
|
setter_type_str = "T.nilable(T::Array[#{setter_type}])"
|
|
58
66
|
elsif nilable
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: shale-builder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mateusz Drewniak
|
|
@@ -69,7 +69,9 @@ files:
|
|
|
69
69
|
- Rakefile
|
|
70
70
|
- lib/shale/attribute.rb
|
|
71
71
|
- lib/shale/builder.rb
|
|
72
|
+
- lib/shale/builder/assigned_attributes.rb
|
|
72
73
|
- lib/shale/builder/nested_validations.rb
|
|
74
|
+
- lib/shale/builder/value.rb
|
|
73
75
|
- lib/shale/builder/version.rb
|
|
74
76
|
- lib/shale/mapper.rbi
|
|
75
77
|
- lib/tapioca/dsl/compilers/shale.rb
|