shale-builder 0.8.4 → 0.9.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/CHANGELOG.md +14 -0
- data/Gemfile.lock +1 -1
- data/README.md +41 -0
- data/lib/shale/builder/version.rb +1 -1
- data/lib/shale/builder.rb +16 -16
- data/lib/tapioca/dsl/compilers/shale.rb +66 -54
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 293665a7fae7d93488f9c8f65e0c4d1fed9cc01c629549f4d06f4163ef712b6b
|
|
4
|
+
data.tar.gz: 10bf23ea86288b0d2dc67e8730fa339a63424b1d9f003d6def9d9274007d032b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3ad60377af23996a3a6fca6b186f783c9a693261b3df56ce7a4ecf3ac32048b38b072fa0a3ece2584137767e0e1ca1d9bc5a474a026b9e00873e1d8f9fe4eca1
|
|
7
|
+
data.tar.gz: e5b06e6b2cd4d0ec52b9cfb94063052ce30902855b5b3931850dfe669869d9fc9b5734f3078b6de1dfd793af7c9e2f225c9f9bff926183cc649961ffbac35d97
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ 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.9.0] - 2026-04-24
|
|
9
|
+
|
|
10
|
+
[Diff](https://github.com/Verseth/ruby-shale-builder/compare/v0.8.5...v0.9.0)
|
|
11
|
+
|
|
12
|
+
### Changes
|
|
13
|
+
- Add `memoize:` optional keyword arg to complex getters to preserve the already existing object instead of overriding it
|
|
14
|
+
|
|
15
|
+
## [0.8.5] - 2025-10-27
|
|
16
|
+
|
|
17
|
+
[Diff](https://github.com/Verseth/ruby-shale-builder/compare/v0.8.3...v0.8.5)
|
|
18
|
+
|
|
19
|
+
### Changes
|
|
20
|
+
- Fix the tapioca compiler behaviour with `T.untyped`
|
|
21
|
+
|
|
8
22
|
## [0.8.3] - 2025-10-24
|
|
9
23
|
|
|
10
24
|
[Diff](https://github.com/Verseth/ruby-shale-builder/compare/v0.8.2...v0.8.3)
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -145,6 +145,47 @@ non-primitive types have been overridden to accept blocks.
|
|
|
145
145
|
When a block is given to such a getter, it instantiates an empty object
|
|
146
146
|
of its type and yields it to the block.
|
|
147
147
|
|
|
148
|
+
#### Overriding
|
|
149
|
+
|
|
150
|
+
By default if you call the builder method with a block a second time
|
|
151
|
+
it will override the previous object with a new one
|
|
152
|
+
|
|
153
|
+
```rb
|
|
154
|
+
transaction = Transaction.build do |t|
|
|
155
|
+
t.amount do |a|
|
|
156
|
+
a.value = 2.3
|
|
157
|
+
a.currency = 'PLN'
|
|
158
|
+
end
|
|
159
|
+
t.amount do |a|
|
|
160
|
+
a.value = 10
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
transaction.amount.value #=> 10
|
|
165
|
+
transaction.amount.currency #=> nil
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### Memoization
|
|
169
|
+
|
|
170
|
+
You can change the behaviour of builder methods
|
|
171
|
+
so that they preserve existing objects when called a second time.
|
|
172
|
+
You do that by using the `memoize: true` keyword argument.
|
|
173
|
+
|
|
174
|
+
```rb
|
|
175
|
+
transaction = Transaction.build do |t|
|
|
176
|
+
t.amount do |a|
|
|
177
|
+
a.value = 2.3
|
|
178
|
+
a.currency = 'PLN'
|
|
179
|
+
end
|
|
180
|
+
t.amount(memoize: true) do |a|
|
|
181
|
+
a.value = 10
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
transaction.amount.value #=> 10
|
|
186
|
+
transaction.amount.currency #=> 'PLN'
|
|
187
|
+
```
|
|
188
|
+
|
|
148
189
|
### Collections
|
|
149
190
|
|
|
150
191
|
Whenever you call a getter with a block for a collection attribute, the built object will be appended to the array.
|
data/lib/shale/builder.rb
CHANGED
|
@@ -130,27 +130,27 @@ module Shale
|
|
|
130
130
|
|
|
131
131
|
if collection
|
|
132
132
|
@builder_methods_module.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
133
|
-
def #{name}
|
|
134
|
-
return super unless block_given?
|
|
135
|
-
|
|
136
|
-
arr = self.#{name} ||= []
|
|
137
|
-
object = #{shale_mapper}.new
|
|
138
|
-
yield(object)
|
|
139
|
-
arr << object
|
|
140
|
-
object
|
|
141
|
-
end
|
|
133
|
+
def #{name}
|
|
134
|
+
return super unless block_given?
|
|
135
|
+
|
|
136
|
+
arr = self.#{name} ||= []
|
|
137
|
+
object = #{shale_mapper}.new
|
|
138
|
+
yield(object)
|
|
139
|
+
arr << object
|
|
140
|
+
object
|
|
141
|
+
end
|
|
142
142
|
RUBY
|
|
143
143
|
return
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
@builder_methods_module.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
147
|
-
def #{name}
|
|
148
|
-
return super unless block_given?
|
|
149
|
-
|
|
150
|
-
object = #{shale_mapper}.new
|
|
151
|
-
yield(object)
|
|
152
|
-
self.#{name} = object
|
|
153
|
-
end
|
|
147
|
+
def #{name}(memoize: false)
|
|
148
|
+
return super() unless block_given?
|
|
149
|
+
|
|
150
|
+
object = (memoize && self.#{name}) || #{shale_mapper}.new
|
|
151
|
+
yield(object)
|
|
152
|
+
self.#{name} = object
|
|
153
|
+
end
|
|
154
154
|
RUBY
|
|
155
155
|
end
|
|
156
156
|
|
|
@@ -18,7 +18,8 @@ module Tapioca
|
|
|
18
18
|
class << self
|
|
19
19
|
extend T::Sig
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
# @override
|
|
22
|
+
#: -> T::Enumerable[Module]
|
|
22
23
|
def gather_constants
|
|
23
24
|
# Collect all the classes that inherit from Shale::Mapper
|
|
24
25
|
all_classes.select { |c| c < ::Shale::Mapper }
|
|
@@ -27,7 +28,8 @@ module Tapioca
|
|
|
27
28
|
|
|
28
29
|
SHALE_ATTRIBUTE_MODULE = 'ShaleAttributeMethods'
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
# @override
|
|
32
|
+
#: -> void
|
|
31
33
|
def decorate
|
|
32
34
|
# Create a RBI definition for each class that inherits from Shale::Mapper
|
|
33
35
|
root.create_path(constant) do |klass|
|
|
@@ -50,9 +52,9 @@ module Tapioca
|
|
|
50
52
|
end
|
|
51
53
|
|
|
52
54
|
if attribute.collection?
|
|
53
|
-
getter_without_block_type =
|
|
55
|
+
getter_without_block_type = wrap_nilable_type(wrap_array_type(return_type))
|
|
54
56
|
elsif nilable
|
|
55
|
-
getter_without_block_type =
|
|
57
|
+
getter_without_block_type = wrap_nilable_type(return_type)
|
|
56
58
|
else
|
|
57
59
|
getter_without_block_type = return_type.to_s
|
|
58
60
|
end
|
|
@@ -64,9 +66,9 @@ module Tapioca
|
|
|
64
66
|
setter_type, nilable = shale_type_to_sorbet_setter_type(attribute)
|
|
65
67
|
end
|
|
66
68
|
if attribute.collection?
|
|
67
|
-
setter_type_str =
|
|
69
|
+
setter_type_str = wrap_nilable_type(wrap_array_type(setter_type))
|
|
68
70
|
elsif nilable
|
|
69
|
-
setter_type_str =
|
|
71
|
+
setter_type_str = wrap_nilable_type(setter_type)
|
|
70
72
|
else
|
|
71
73
|
setter_type_str = setter_type.to_s
|
|
72
74
|
end
|
|
@@ -92,48 +94,64 @@ module Tapioca
|
|
|
92
94
|
|
|
93
95
|
end
|
|
94
96
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
#: (untyped type) -> String
|
|
98
|
+
def wrap_nilable_type(type)
|
|
99
|
+
return "T.nilable(#{type})" if type != T.untyped
|
|
100
|
+
|
|
101
|
+
T.unsafe(type).to_s
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
#: (untyped type) -> String
|
|
105
|
+
def wrap_array_type(type)
|
|
106
|
+
"T::Array[#{type}]"
|
|
103
107
|
end
|
|
108
|
+
|
|
109
|
+
#: (RBI::Scope mod, String method_name, Object type, String getter_without_block_type, Array[RBI::Comment] comments) -> void
|
|
104
110
|
def generate_mapper_getter(mod, method_name, type, getter_without_block_type, comments)
|
|
105
111
|
if mod.respond_to?(:create_sig)
|
|
106
112
|
mod = T.unsafe(mod)
|
|
107
113
|
# for tapioca < 0.16.0
|
|
108
|
-
sigs =
|
|
114
|
+
sigs = [] #: Array[RBI::Sig]
|
|
109
115
|
|
|
110
116
|
# simple getter
|
|
111
117
|
sigs << mod.create_sig(
|
|
112
|
-
parameters: {
|
|
118
|
+
parameters: {
|
|
119
|
+
memoize: 'FalseClass',
|
|
120
|
+
block: 'NilClass',
|
|
121
|
+
},
|
|
113
122
|
return_type: getter_without_block_type,
|
|
114
123
|
)
|
|
115
124
|
# getter with block
|
|
116
125
|
sigs << mod.create_sig(
|
|
117
|
-
parameters: {
|
|
126
|
+
parameters: {
|
|
127
|
+
memoize: 'T::Boolean',
|
|
128
|
+
block: "T.proc.params(arg0: #{type}).void"
|
|
129
|
+
},
|
|
118
130
|
return_type: type.to_s
|
|
119
131
|
)
|
|
120
132
|
mod.create_method_with_sigs(
|
|
121
133
|
method_name,
|
|
122
134
|
sigs: sigs,
|
|
123
135
|
comments: comments,
|
|
124
|
-
parameters: [
|
|
136
|
+
parameters: [
|
|
137
|
+
RBI::KwOptParam.new('memoize', 'false'),
|
|
138
|
+
RBI::BlockParam.new('block'),
|
|
139
|
+
],
|
|
125
140
|
)
|
|
126
141
|
else
|
|
127
142
|
# for tapioca >= 0.16.0
|
|
128
143
|
mod.create_method(method_name, comments: comments) do |method|
|
|
129
144
|
method.add_block_param('block')
|
|
145
|
+
method.add_kw_opt_param('memoize', 'false')
|
|
130
146
|
|
|
131
147
|
method.add_sig do |sig|
|
|
148
|
+
sig.add_param('memoize', 'FalseClass')
|
|
132
149
|
sig.add_param('block', 'NilClass')
|
|
133
150
|
sig.return_type = getter_without_block_type
|
|
134
151
|
end
|
|
135
152
|
|
|
136
153
|
method.add_sig do |sig|
|
|
154
|
+
sig.add_param('memoize', 'T::Boolean')
|
|
137
155
|
sig.add_param('block', "T.proc.params(arg0: #{type}).void")
|
|
138
156
|
sig.return_type = type.to_s
|
|
139
157
|
end
|
|
@@ -143,51 +161,45 @@ module Tapioca
|
|
|
143
161
|
|
|
144
162
|
private
|
|
145
163
|
|
|
146
|
-
|
|
164
|
+
#: (Class klass) -> bool?
|
|
147
165
|
def includes_shale_builder(klass)
|
|
148
166
|
return false unless defined?(::Shale::Builder)
|
|
149
167
|
|
|
150
168
|
klass < ::Shale::Builder
|
|
151
169
|
end
|
|
152
170
|
|
|
153
|
-
|
|
171
|
+
#: -> bool
|
|
154
172
|
def shale_builder_defined? = Boolean(defined?(::Shale::Builder))
|
|
155
173
|
|
|
156
174
|
# Maps Shale return types to Sorbet types
|
|
157
|
-
SHALE_RETURN_TYPES_MAP =
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
end.freeze,
|
|
169
|
-
T::Hash[Class, Object],
|
|
170
|
-
)
|
|
175
|
+
SHALE_RETURN_TYPES_MAP = {
|
|
176
|
+
::Shale::Type::Value => T.untyped,
|
|
177
|
+
::Shale::Type::String => String,
|
|
178
|
+
::Shale::Type::Float => Float,
|
|
179
|
+
::Shale::Type::Integer => Integer,
|
|
180
|
+
::Shale::Type::Time => Time,
|
|
181
|
+
::Shale::Type::Date => Date,
|
|
182
|
+
::Shale::Type::Boolean => T::Boolean,
|
|
183
|
+
}.tap do |h|
|
|
184
|
+
h[::Shale::Type::Decimal] = BigDecimal if defined?(::Shale::Type::Decimal)
|
|
185
|
+
end.freeze #: Hash[Class, Object]
|
|
171
186
|
|
|
172
187
|
# Maps Shale setter types to Sorbet types
|
|
173
|
-
SHALE_SETTER_TYPES_MAP =
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
end.freeze,
|
|
187
|
-
T::Hash[Class, Object],
|
|
188
|
-
)
|
|
188
|
+
SHALE_SETTER_TYPES_MAP = {
|
|
189
|
+
::Shale::Type::Value => T.untyped,
|
|
190
|
+
::Shale::Type::String => String,
|
|
191
|
+
::Shale::Type::Float => Float,
|
|
192
|
+
::Shale::Type::Integer => Integer,
|
|
193
|
+
::Shale::Type::Time => Time,
|
|
194
|
+
::Shale::Type::Date => Date,
|
|
195
|
+
::Shale::Type::Boolean => Object,
|
|
196
|
+
}.tap do |h|
|
|
197
|
+
if defined?(::Shale::Type::Decimal)
|
|
198
|
+
h[::Shale::Type::Decimal] = T.any(BigDecimal, String, Float, Integer, NilClass)
|
|
199
|
+
end
|
|
200
|
+
end.freeze #: Hash[Class, Object]
|
|
189
201
|
|
|
190
|
-
|
|
202
|
+
#: (::Shale::Attribute attribute) -> [Object, bool]
|
|
191
203
|
def shale_type_to_sorbet_return_type(attribute)
|
|
192
204
|
return_type = SHALE_RETURN_TYPES_MAP[attribute.type]
|
|
193
205
|
return complex_shale_type_to_sorbet_return_type(attribute) unless return_type
|
|
@@ -196,7 +208,7 @@ module Tapioca
|
|
|
196
208
|
[return_type, true]
|
|
197
209
|
end
|
|
198
210
|
|
|
199
|
-
|
|
211
|
+
#: (::Shale::Attribute attribute) -> [Object, bool]
|
|
200
212
|
def complex_shale_type_to_sorbet_return_type(attribute)
|
|
201
213
|
return attribute.type, true unless attribute.type.respond_to?(:return_type)
|
|
202
214
|
|
|
@@ -204,7 +216,7 @@ module Tapioca
|
|
|
204
216
|
[return_type_string, false]
|
|
205
217
|
end
|
|
206
218
|
|
|
207
|
-
|
|
219
|
+
#: (::Shale::Attribute attribute) -> [Object, bool]
|
|
208
220
|
def shale_type_to_sorbet_setter_type(attribute)
|
|
209
221
|
setter_type = SHALE_SETTER_TYPES_MAP[attribute.type]
|
|
210
222
|
return complex_shale_type_to_sorbet_setter_type(attribute) unless setter_type
|
|
@@ -213,7 +225,7 @@ module Tapioca
|
|
|
213
225
|
[setter_type, true]
|
|
214
226
|
end
|
|
215
227
|
|
|
216
|
-
|
|
228
|
+
#: (::Shale::Attribute attribute) -> [Object, bool]
|
|
217
229
|
def complex_shale_type_to_sorbet_setter_type(attribute)
|
|
218
230
|
if attribute.type.respond_to?(:setter_type)
|
|
219
231
|
setter_type_string = attribute.type.setter_type
|