shale-builder 0.8.5 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ff18fc2a346966ffb57504d1391b21bc1ffb0875210149a9fe9bbfaf8f34cbc
4
- data.tar.gz: 0d5b178c4565dbc3471775ae4d3e11c79fc8ddb5c4a80a9d7ae519a43102c7dc
3
+ metadata.gz: a856175f03b1a09320b45d95ce15389523ef1dcc144cd43465ef158ede3d58a5
4
+ data.tar.gz: 8e04cd5334e75d2b01010ec5074b160beb6397f23fb2e17a30fb4bd67b45c57b
5
5
  SHA512:
6
- metadata.gz: 4c63c403ff14f295e3647192ea9f5ae3c6116adbf7c6f0ed6f65a3d635797e51a486788f79bc5f73ee6d3435377b60811aa65e04e37be2b16e26a63febc5fde7
7
- data.tar.gz: c9fcbcfcc5d2f76385df8ba4c8cf07ee6c11be284f80bbb2edc8eebeeb4c532b932748f9fdc624ffd76d7b28d05725c442f46ef19976ef050460dea743d0c7ed
6
+ metadata.gz: 4398f91a2061891d5d4c113fe4bfa23d3cf71e2d37bcc860554390dc6fb05c370c11060fefb3b3f3c54d3d48bddab0664fcc023c2e1cc9c739a8ab5153ac5ea7
7
+ data.tar.gz: e29af058ad0a347ad4afa01332a5aaa6e78af78fa21ea60ef4cbb01184fdc18d061df5327dd21b479977e28a2745e35f451f934f5798ff58e51292e258708bac
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shale-builder (0.8.5)
4
+ shale-builder (0.9.1)
5
5
  booleans (>= 0.1)
6
6
  shale (< 2.0)
7
7
  sorbet-runtime (> 0.5)
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.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Shale
4
4
  module Builder
5
- VERSION = '0.8.5'
5
+ VERSION = '0.9.1'
6
6
  end
7
7
  end
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} # def clients
134
- return super unless block_given? # return super unless block_given?
135
- #
136
- arr = self.#{name} ||= [] # arr = self.clients ||= []
137
- object = #{shale_mapper}.new # object = Client.new
138
- yield(object) # yield(object)
139
- arr << object # arr << object
140
- object # object
141
- end # 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} # def amount
148
- return super unless block_given? # return super unless block_given?
149
- #
150
- object = #{shale_mapper}.new # object = Amount.new
151
- yield(object) # yield(object)
152
- self.#{name} = object # self.amount = object
153
- end # 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
- sig { override.returns(T::Enumerable[Module]) }
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
- sig { override.void }
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
- sig { params(type: T.untyped).returns(String) }
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
- sig { params(type: T.untyped).returns(String) }
104
+ #: (untyped type) -> String
103
105
  def wrap_array_type(type)
104
106
  "T::Array[#{type}]"
105
107
  end
106
108
 
107
- sig do
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 = T.let([], T::Array[RBI::Sig])
114
+ sigs = [] #: Array[RBI::Sig]
121
115
 
122
116
  # simple getter
123
117
  sigs << mod.create_sig(
124
- parameters: { block: 'NilClass' },
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: { block: "T.proc.params(arg0: #{type}).void" },
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: [RBI::BlockParam.new('block')],
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|
144
+ method.add_kw_opt_param('memoize', 'false')
141
145
  method.add_block_param('block')
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
- sig { params(klass: Class).returns(T.nilable(T::Boolean)) }
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
- sig { returns(T::Boolean) }
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 = T.let(
170
- {
171
- ::Shale::Type::Value => T.untyped,
172
- ::Shale::Type::String => String,
173
- ::Shale::Type::Float => Float,
174
- ::Shale::Type::Integer => Integer,
175
- ::Shale::Type::Time => Time,
176
- ::Shale::Type::Date => Date,
177
- ::Shale::Type::Boolean => T::Boolean,
178
- }.tap do |h|
179
- h[::Shale::Type::Decimal] = BigDecimal if defined?(::Shale::Type::Decimal)
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 = T.let(
186
- {
187
- ::Shale::Type::Value => T.untyped,
188
- ::Shale::Type::String => String,
189
- ::Shale::Type::Float => Float,
190
- ::Shale::Type::Integer => Integer,
191
- ::Shale::Type::Time => Time,
192
- ::Shale::Type::Date => Date,
193
- ::Shale::Type::Boolean => Object,
194
- }.tap do |h|
195
- if defined?(::Shale::Type::Decimal)
196
- h[::Shale::Type::Decimal] = T.any(BigDecimal, String, Float, Integer, NilClass)
197
- end
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
- sig { params(attribute: ::Shale::Attribute).returns([Object, T::Boolean]) }
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
- sig { params(attribute: ::Shale::Attribute).returns([Object, T::Boolean]) }
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
- sig { params(attribute: ::Shale::Attribute).returns([Object, T::Boolean]) }
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
- sig { params(attribute: ::Shale::Attribute).returns([Object, T::Boolean]) }
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
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.8.5
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Drewniak