shale-builder 0.8.5 → 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 +7 -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 +53 -53
- 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,13 @@ 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
|
+
|
|
8
15
|
## [0.8.5] - 2025-10-27
|
|
9
16
|
|
|
10
17
|
[Diff](https://github.com/Verseth/ruby-shale-builder/compare/v0.8.3...v0.8.5)
|
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|
|
|
@@ -92,60 +94,64 @@ module Tapioca
|
|
|
92
94
|
|
|
93
95
|
end
|
|
94
96
|
|
|
95
|
-
|
|
97
|
+
#: (untyped type) -> String
|
|
96
98
|
def wrap_nilable_type(type)
|
|
97
99
|
return "T.nilable(#{type})" if type != T.untyped
|
|
98
100
|
|
|
99
101
|
T.unsafe(type).to_s
|
|
100
102
|
end
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
#: (untyped type) -> String
|
|
103
105
|
def wrap_array_type(type)
|
|
104
106
|
"T::Array[#{type}]"
|
|
105
107
|
end
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
params(
|
|
109
|
-
mod: RBI::Scope,
|
|
110
|
-
method_name: String,
|
|
111
|
-
type: Object,
|
|
112
|
-
getter_without_block_type: String,
|
|
113
|
-
comments: T::Array[RBI::Comment],
|
|
114
|
-
).void
|
|
115
|
-
end
|
|
109
|
+
#: (RBI::Scope mod, String method_name, Object type, String getter_without_block_type, Array[RBI::Comment] comments) -> void
|
|
116
110
|
def generate_mapper_getter(mod, method_name, type, getter_without_block_type, comments)
|
|
117
111
|
if mod.respond_to?(:create_sig)
|
|
118
112
|
mod = T.unsafe(mod)
|
|
119
113
|
# for tapioca < 0.16.0
|
|
120
|
-
sigs =
|
|
114
|
+
sigs = [] #: Array[RBI::Sig]
|
|
121
115
|
|
|
122
116
|
# simple getter
|
|
123
117
|
sigs << mod.create_sig(
|
|
124
|
-
parameters: {
|
|
118
|
+
parameters: {
|
|
119
|
+
memoize: 'FalseClass',
|
|
120
|
+
block: 'NilClass',
|
|
121
|
+
},
|
|
125
122
|
return_type: getter_without_block_type,
|
|
126
123
|
)
|
|
127
124
|
# getter with block
|
|
128
125
|
sigs << mod.create_sig(
|
|
129
|
-
parameters: {
|
|
126
|
+
parameters: {
|
|
127
|
+
memoize: 'T::Boolean',
|
|
128
|
+
block: "T.proc.params(arg0: #{type}).void"
|
|
129
|
+
},
|
|
130
130
|
return_type: type.to_s
|
|
131
131
|
)
|
|
132
132
|
mod.create_method_with_sigs(
|
|
133
133
|
method_name,
|
|
134
134
|
sigs: sigs,
|
|
135
135
|
comments: comments,
|
|
136
|
-
parameters: [
|
|
136
|
+
parameters: [
|
|
137
|
+
RBI::KwOptParam.new('memoize', 'false'),
|
|
138
|
+
RBI::BlockParam.new('block'),
|
|
139
|
+
],
|
|
137
140
|
)
|
|
138
141
|
else
|
|
139
142
|
# for tapioca >= 0.16.0
|
|
140
143
|
mod.create_method(method_name, comments: comments) do |method|
|
|
141
144
|
method.add_block_param('block')
|
|
145
|
+
method.add_kw_opt_param('memoize', 'false')
|
|
142
146
|
|
|
143
147
|
method.add_sig do |sig|
|
|
148
|
+
sig.add_param('memoize', 'FalseClass')
|
|
144
149
|
sig.add_param('block', 'NilClass')
|
|
145
150
|
sig.return_type = getter_without_block_type
|
|
146
151
|
end
|
|
147
152
|
|
|
148
153
|
method.add_sig do |sig|
|
|
154
|
+
sig.add_param('memoize', 'T::Boolean')
|
|
149
155
|
sig.add_param('block', "T.proc.params(arg0: #{type}).void")
|
|
150
156
|
sig.return_type = type.to_s
|
|
151
157
|
end
|
|
@@ -155,51 +161,45 @@ module Tapioca
|
|
|
155
161
|
|
|
156
162
|
private
|
|
157
163
|
|
|
158
|
-
|
|
164
|
+
#: (Class klass) -> bool?
|
|
159
165
|
def includes_shale_builder(klass)
|
|
160
166
|
return false unless defined?(::Shale::Builder)
|
|
161
167
|
|
|
162
168
|
klass < ::Shale::Builder
|
|
163
169
|
end
|
|
164
170
|
|
|
165
|
-
|
|
171
|
+
#: -> bool
|
|
166
172
|
def shale_builder_defined? = Boolean(defined?(::Shale::Builder))
|
|
167
173
|
|
|
168
174
|
# Maps Shale return types to Sorbet types
|
|
169
|
-
SHALE_RETURN_TYPES_MAP =
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
end.freeze,
|
|
181
|
-
T::Hash[Class, Object],
|
|
182
|
-
)
|
|
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]
|
|
183
186
|
|
|
184
187
|
# Maps Shale setter types to Sorbet types
|
|
185
|
-
SHALE_SETTER_TYPES_MAP =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
end.freeze,
|
|
199
|
-
T::Hash[Class, Object],
|
|
200
|
-
)
|
|
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]
|
|
201
201
|
|
|
202
|
-
|
|
202
|
+
#: (::Shale::Attribute attribute) -> [Object, bool]
|
|
203
203
|
def shale_type_to_sorbet_return_type(attribute)
|
|
204
204
|
return_type = SHALE_RETURN_TYPES_MAP[attribute.type]
|
|
205
205
|
return complex_shale_type_to_sorbet_return_type(attribute) unless return_type
|
|
@@ -208,7 +208,7 @@ module Tapioca
|
|
|
208
208
|
[return_type, true]
|
|
209
209
|
end
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
#: (::Shale::Attribute attribute) -> [Object, bool]
|
|
212
212
|
def complex_shale_type_to_sorbet_return_type(attribute)
|
|
213
213
|
return attribute.type, true unless attribute.type.respond_to?(:return_type)
|
|
214
214
|
|
|
@@ -216,7 +216,7 @@ module Tapioca
|
|
|
216
216
|
[return_type_string, false]
|
|
217
217
|
end
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
#: (::Shale::Attribute attribute) -> [Object, bool]
|
|
220
220
|
def shale_type_to_sorbet_setter_type(attribute)
|
|
221
221
|
setter_type = SHALE_SETTER_TYPES_MAP[attribute.type]
|
|
222
222
|
return complex_shale_type_to_sorbet_setter_type(attribute) unless setter_type
|
|
@@ -225,7 +225,7 @@ module Tapioca
|
|
|
225
225
|
[setter_type, true]
|
|
226
226
|
end
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
#: (::Shale::Attribute attribute) -> [Object, bool]
|
|
229
229
|
def complex_shale_type_to_sorbet_setter_type(attribute)
|
|
230
230
|
if attribute.type.respond_to?(:setter_type)
|
|
231
231
|
setter_type_string = attribute.type.setter_type
|