shale-builder 0.4.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da77acaa0e4c63b3155b114625c4da322066c92447ce1297915eac5e4facef71
4
- data.tar.gz: 043214cb3659bc261089c29306082051e1cf14dc222e8852336b9cf9f4dd102e
3
+ metadata.gz: febc908c436d767296b5869c0f83de5192ebd2dea56649a68840f951ca61210e
4
+ data.tar.gz: 921efc21393a6a01516dc0954efe6f87fe74326d51d1969c00e985f3f8ad9824
5
5
  SHA512:
6
- metadata.gz: 2e00a2bbeb8ba5c240543520d7a881e2b10fc3e79cf941250230c5906e7ba85742b31611a04154f45e953ba8ec16a1c6dbbd5dc7c6a72e895d25f646e01a19b7
7
- data.tar.gz: 1f5462ad104ca4ffdb7f68d895a517d70dd67b7fe666da7baed61bddd2c68de101061d992abad6895045cb6dc8c958ec59a57cc39cbc51a8fb56056e1bafd5ed
6
+ metadata.gz: 315da1873bcba3a08cbf4a6d405b975a8c9be51442726faab0e886138be48b50fa6bec7c5367064d1d0cc731af17ec0c10bbe9cfb04716a5b1d0712de46244a5
7
+ data.tar.gz: 54df3a9708bc1e327ecb7f85338a1420cca5b98d82b754acbecd03417541951472b9f2d005035abe5fe1217bfd5db10d3730a1966043c65956b3cdb83d080299
data/CHANGELOG.md CHANGED
@@ -5,9 +5,25 @@ 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.0] - 2025-10-15
9
+
10
+ [Diff](https://github.com/Verseth/ruby-shale-builder/compare/v0.4.1...v0.5.0)
11
+
12
+ ### Changes
13
+ - Add `Shale::Builder::AssignedAttributes` module which grants a shale mapper class with the ability to record which attributes have been assigned
14
+ - Add `Shale::Builder::Value` which represents a value of a shale attribute.
15
+ - Add `Shale::Builder#attribute_values`, a method that returns an array of `Shale::Builder::Value` objects for each attribute.
16
+
17
+ ## [0.4.1] - 2025-10-14
18
+
19
+ [Diff](https://github.com/Verseth/ruby-shale-builder/compare/v0.4.0...v0.4.1)
20
+
21
+ ### Changes
22
+ - Fix `Shale::Builder::alias_attribute`, improve changelog
23
+
8
24
  ## [0.4.0] - 2025-10-14
9
25
 
10
- [Diff](https://github.com/Verseth/ruby-shale-builder/compare/v0.3.0...v0.2.1)
26
+ [Diff](https://github.com/Verseth/ruby-shale-builder/compare/v0.3.0...v0.4.0)
11
27
 
12
28
  ### Changes
13
29
  - Add `Shale::Builder::NestedValidations`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shale-builder (0.4.0)
4
+ shale-builder (0.5.0)
5
5
  booleans (>= 0.1)
6
6
  shale (< 2.0)
7
7
  sorbet-runtime (> 0.5)
data/README.md CHANGED
@@ -263,50 +263,67 @@ 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
- ### Attribute aliases
266
+ ### Recording Assigned Attributes
267
267
 
268
- You can easily create aliases for attributes using `alias_attribute`
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
+ ```
269
276
 
270
277
  Then you can use it like so
271
278
 
272
279
  ```rb
273
280
  class AmountType < ::Shale::Mapper
274
281
  include ::Shale::Builder
275
- include ::ActiveModel::Validations
276
- include ::Shale::Builder::NestedValidations
282
+ include ::Shale::Builder::AssignedAttributes
277
283
 
278
284
  attribute :value, ::Shale::Type::Float
279
285
  attribute :currency, ::Shale::Type::String
280
-
281
- validates :value, presence: true
282
286
  end
283
287
 
284
- class TransactionType < ::Shale::Mapper
285
- include ::Shale::Builder
286
- include ::ActiveModel::Validations
287
- include ::Shale::Builder::NestedValidations
288
+ obj = AmountType.new
289
+ obj.assigned_attribute_names #=> #<Set: {}>
288
290
 
289
- attribute :cvv_code, ::Shale::Type::String
290
- attribute :amount, AmountType
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
+ ```
291
296
 
292
- validates :cvv_code, presence: true
293
- validates :amount, presence: true
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
+
301
+ ### Attribute aliases
302
+
303
+ You can easily create aliases for attributes using `alias_attribute`
304
+
305
+ Then you can use it like so
306
+
307
+ ```rb
308
+ require 'shale/builder'
309
+
310
+ class Amount < Shale::Mapper
311
+ include Shale::Builder
312
+
313
+ attribute :value, Shale::Type::Float
314
+ attribute :currency, Shale::Type::String
315
+
316
+ alias_attribute :val, :value
294
317
  end
295
318
 
296
- obj = TransactionType.build do |t|
297
- t.amount do |a|
298
- a.currency = 'USD'
299
- end
319
+ a = Amount.build do |a|
320
+ a.val = 3.2
300
321
  end
301
322
 
302
- obj.valid? #=> false
303
- obj.errors #=> #<ActiveModel::Errors [#<ActiveModel::Error attribute=cvv_code, type=blank, options={}>, #<ActiveModel::NestedError attribute=amount.value, type=blank, options={}>]>
304
- obj.errors.messages #=> {cvv_code: ["can't be blank"], "amount.value": ["can't be blank"]}
323
+ a.val #=> 3.2
324
+ a.value #=> 3.2
305
325
  ```
306
326
 
307
- You MUST include `ActiveModel::Validations` before `Shale::Builder::NestedValidations`.
308
-
309
-
310
327
  ### Sorbet support
311
328
 
312
329
  Shale-builder adds support for sorbet and tapioca.
@@ -9,15 +9,15 @@ module Shale
9
9
 
10
10
  # Contains the documentation comment for the shale attribute
11
11
  # in a Ruby String.
12
- sig { returns(T.nilable(String)) }
12
+ #: String?
13
13
  attr_accessor :doc
14
14
 
15
15
  # Contains the documentation comment for the shale attribute
16
16
  # in a Ruby String.
17
- sig { returns(T.nilable(T::Array[Symbol])) }
17
+ #: Array[Symbol]?
18
18
  attr_accessor :aliases
19
19
 
20
- sig { returns(T::Array[Symbol]) }
20
+ #: -> Array[Symbol]
21
21
  def all_names
22
22
  names = [name]
23
23
  aliases = self.aliases
@@ -25,5 +25,12 @@ module Shale
25
25
 
26
26
  names + aliases
27
27
  end
28
+
29
+ # Returns `true` if the attribute is handled by a shale mapper.
30
+ #
31
+ #: -> bool
32
+ def mapper?
33
+ type.is_a?(Class) && type < Shale::Mapper
34
+ end
28
35
  end
29
36
  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 tha already includes `Shale::Builder` to add support
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Shale
4
4
  module Builder
5
- VERSION = '0.4.0'
5
+ VERSION = '0.5.0'
6
6
  end
7
7
  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
- sig { params(mod: Module).void }
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
- sig { params(mod: Module).void }
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
- sig { params(subclass: Class).void }
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
- sig { returns(Module) }
83
+ #: Module
83
84
  attr_reader :builder_methods_module
84
85
 
85
- sig { params(_block: T.proc.params(arg0: T.attached_class).void).returns(T.attached_class) }
86
+ #: { (instance arg0) -> void } -> instance
86
87
  def build(&_block)
87
88
  body = new
88
89
  yield(body)
@@ -96,17 +97,7 @@ module Shale
96
97
  sig { abstract.returns(T::Hash[Symbol, Shale::Attribute]) }
97
98
  def attributes; end
98
99
 
99
- sig do
100
- params(
101
- name: T.any(String, Symbol),
102
- type: Class,
103
- collection: T::Boolean,
104
- default: T.nilable(Proc),
105
- doc: T.nilable(String),
106
- kwargs: Object,
107
- block: T.nilable(T.proc.void),
108
- ).void
109
- end
100
+ #: ((String | Symbol) name, Class type, ?collection: bool, ?default: Proc?, ?doc: String?, **Object kwargs) ?{ -> void } -> void
110
101
  def attribute(name, type, collection: false, default: nil, doc: nil, **kwargs, &block)
111
102
  super(name, type, collection:, default:, **kwargs, &block)
112
103
  if (attr_def = attributes[name.to_sym])
@@ -140,27 +131,46 @@ module Shale
140
131
  RUBY
141
132
  end
142
133
 
143
- end
144
- mixes_in_class_methods(ClassMethods)
134
+ # Create an alias for the getter and setter of an attribute.
135
+ #: (Symbol new_name, Symbol old_name) -> void
136
+ def alias_attribute(new_name, old_name)
137
+ attr = attributes.fetch(old_name)
138
+ (attr.aliases ||= []) << new_name
145
139
 
146
- # Create an alias for the getter and setter of an attribute.
147
- sig { params(new_name: Symbol, old_name: Symbol).void }
148
- def alias_attribute(new_name, old_name)
149
- klass = T.unsafe(self).class
140
+ builder_methods_module.class_eval <<~RUBY, __FILE__, __LINE__ + 1
141
+ def #{new_name}
142
+ #{old_name}
143
+ end
150
144
 
151
- attr = klass.attributes[old_name]
152
- (attr.aliases ||= []) << new_name
145
+ def #{new_name}=(val)
146
+ self.#{old_name} = val
147
+ end
148
+ RUBY
149
+ end
153
150
 
154
- klass.builder_methods_module.class_eval <<~RUBY, __FILE__, __LINE__ + 1
155
- def #{new_name}
156
- #{old_name}
157
- end
151
+ end
152
+ mixes_in_class_methods(ClassMethods)
158
153
 
159
- def #{new_name}=(val)
160
- self.#{old_name} = val
161
- end
162
- RUBY
154
+ def initialize(*args, **kwargs, &block)
155
+ super
156
+ @__initialized = true
157
+ end
158
+
159
+ #: bool?
160
+ attr_reader :__initialized
161
+
162
+ # Returns an array of shale values
163
+ # that have been assigned.
164
+ #
165
+ #: -> Array[Shale::Builder::Value]
166
+ def attribute_values
167
+ klass = self.class #: as untyped
168
+ klass.attributes.map do |name, attr|
169
+ Shale::Builder::Value.new(attr, public_send(name))
170
+ end
163
171
  end
164
172
 
165
173
  end
166
174
  end
175
+
176
+ require_relative 'builder/value'
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.0
4
+ version: 0.5.0
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