inheritance-helper 0.2.6 → 0.3.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: 31680a3ae280b81619d4eef1777f9821a4c47ef390225e56a4805b9686747fb9
4
- data.tar.gz: 998ec788e4497c7e90870ece2d008dc76226344d7d24c0be408b7e6d4b859673
3
+ metadata.gz: 5d1e8987c76ed0af5063986813c435c9e755bce6f0d145ab7d63e9f0a35ba0f9
4
+ data.tar.gz: f1c481bd51c45d74e3e36a6e95fe656e29457fb489a9d99a1760539554577462
5
5
  SHA512:
6
- metadata.gz: 47fc914a0bae8fedf35b01b0c9f4db8db002b1b1368d9bdaaf9b13a9ad537a5fc94a84900d652515874e6597acbd76f6d366bf50e2da6c798f9b756f89059333
7
- data.tar.gz: 96f5b2c69b69ef9a8720b9c5db7f64aeb6cfb578516c2586a4bc57085b91700e393090872f986ebfbbd598ba26eb1578f7ec182fede9272e5bb887fa0362de8c
6
+ metadata.gz: c67b0fb5d1bbdcb65186c689c9e088739132de2cda3cc3d44ef51f131b868c64f73a4df073dfe8f4171257dedd4d8770af3dac7e47b451ca605574766c7add21
7
+ data.tar.gz: 7ef84095e953df92ee231e364ee9446e2020ee9f2d1fe9063c847bf389e13e594abe153c428669ef9f7e1f0ac00dd62b564790592fae2628622ffa80ee6cf386
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0](https://github.com/dougyouch/inheritance-helper/compare/v0.2.6...v0.3.0) (2026-10-05)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * get_class_name no longer calls String#classify when ActiveSupport is loaded, so names are no longer singularized there (:line_items now gives LineItems, not LineItem). Class names are now the same whether or not ActiveSupport is loaded.
9
+
10
+ ### 1.0
11
+
12
+ * clearer nil error, consistent class names, Ruby 3.3 ([#5](https://github.com/dougyouch/inheritance-helper/issues/5)) ([11bb5a8](https://github.com/dougyouch/inheritance-helper/commit/11bb5a8d056d7b6c2794895bd01c9d9bce14805d))
13
+
3
14
  ## [0.2.6](https://github.com/dougyouch/inheritance-helper/compare/v0.2.5...v0.2.6) (2026-10-05)
4
15
 
5
16
 
data/README.md CHANGED
@@ -20,7 +20,7 @@ attribute definitions.
20
20
 
21
21
  ## Installation
22
22
 
23
- Requires Ruby 3.2 or newer. Add this line to your application's Gemfile:
23
+ Requires Ruby 3.3 or newer. Add this line to your application's Gemfile:
24
24
 
25
25
  ```ruby
26
26
  gem 'inheritance-helper'
@@ -149,10 +149,38 @@ method keeps the visibility of the one it replaces, so a private class method st
149
149
  `InheritanceHelper::Methods.redefine_class_method(klass, method, value)` does the same for a class that
150
150
  doesn't extend the module.
151
151
 
152
+ ### Copy nested values before changing them
153
+
154
+ Once a value is set with these helpers, every call returns the same object, and a subclass starts with its
155
+ parent's object. Freezing covers only the top-level value: a hash or array *inside* it is shared by the parent
156
+ and the subclass, so changing it in place changes both:
157
+
158
+ ```ruby
159
+ class Base
160
+ extend InheritanceHelper::Methods
161
+
162
+ def self.config = {}.freeze
163
+
164
+ add_value_to_class_method :config, skip: []
165
+ end
166
+
167
+ class Child < Base
168
+ # wrong: appends to Base.config[:skip] too
169
+ config[:skip] << :logs
170
+
171
+ # right: build a new nested value and replace the key
172
+ add_value_to_class_method :config, skip: config[:skip] + [:logs]
173
+ end
174
+ ```
175
+
176
+ The same applies to a value from `redefine_class_method`: `dup` the value (and any nested values you change)
177
+ before changing it, then pass the copy to `redefine_class_method`.
178
+
152
179
  ### Notes
153
180
 
154
181
  - The class method must already return a value: `add_value_to_class_method` and
155
- `append_value_to_class_method` call it to get the current value.
182
+ `append_value_to_class_method` call it to get the current value, and raise a `TypeError` naming the method
183
+ when it returns `nil`.
156
184
  - Each call defines a method on the class's singleton class. Classes declared at load time (the usual DSL
157
185
  case) are fine; redefining class methods from several threads at once is not synchronized.
158
186
 
@@ -179,9 +207,9 @@ InheritanceHelper::ClassBuilder::Utils.get_class_name(:line_item, 'Has', 'Class'
179
207
  `nil`), assigns it to the constant in `base_module`, and evaluates the block in the new class. If the constant
180
208
  already exists it is replaced, with Ruby's "already initialized constant" warning.
181
209
 
182
- `get_class_name` uses `String#classify` when ActiveSupport is loaded, which also singularizes the name
183
- (`line_items` becomes `LineItem`). Without ActiveSupport the name is split on underscores and each part is
184
- capitalized (`line_items` becomes `LineItems`).
210
+ `get_class_name` splits the name on underscores and capitalizes the first letter of each part, so
211
+ `line_items` becomes `LineItems`. The result is the same whether or not ActiveSupport is loaded (before 1.0 it
212
+ used `String#classify`, which singularized the name).
185
213
 
186
214
  ## Development
187
215
 
@@ -12,8 +12,8 @@ module InheritanceHelper
12
12
 
13
13
  # Builds a class name from `name` with an optional prefix and suffix.
14
14
  #
15
- # `name` is converted with `String#classify` when ActiveSupport is loaded (which also singularizes it).
16
- # Otherwise it is split on underscores and each part is capitalized.
15
+ # `name` is split on underscores and the first letter of each part is capitalized. The result doesn't depend
16
+ # on whether ActiveSupport is loaded, and the name isn't singularized (`:line_items` gives `"LineItems"`).
17
17
  #
18
18
  # @param name [String, Symbol] the base name, such as `:line_item`
19
19
  # @param prefix_class_name [String, nil] text to put before the converted name
@@ -22,15 +22,7 @@ module InheritanceHelper
22
22
  # @example
23
23
  # get_class_name(:line_item, 'Schema', 'Class') # => "SchemaLineItemClass"
24
24
  def get_class_name(name, prefix_class_name, suffix_class_name)
25
- name = name.to_s
26
-
27
- class_name =
28
- if name.respond_to?(:classify)
29
- name.classify
30
- else
31
- name.split('_').map { |part| part.sub(/\A./, &:upcase) }.join
32
- end
33
-
25
+ class_name = name.to_s.split('_').map { |part| part.sub(/\A./, &:upcase) }.join
34
26
  "#{prefix_class_name}#{class_name}#{suffix_class_name}"
35
27
  end
36
28
 
@@ -80,11 +80,12 @@ module InheritanceHelper
80
80
  # @param method [Symbol, String] name of a class method that returns a Hash, Array or Set
81
81
  # @param value [Object] the value or values to add
82
82
  # @return [Module] this class
83
+ # @raise [TypeError] if the class method returns `nil`
83
84
  # @example
84
85
  # add_value_to_class_method(:attributes, name: :string) # {} => {name: :string}
85
86
  # add_value_to_class_method(:fields, [:a, :b]) # [] => [:a, :b]
86
87
  def add_value_to_class_method(method, value)
87
- old_value = send(method)
88
+ old_value = current_class_method_value(method)
88
89
 
89
90
  new_value =
90
91
  case old_value
@@ -106,12 +107,27 @@ module InheritanceHelper
106
107
  # to `dup` and `<<`)
107
108
  # @param value [Object] the element to append
108
109
  # @return [Module] this class
110
+ # @raise [TypeError] if the class method returns `nil`
109
111
  # @example
110
112
  # append_value_to_class_method(:callbacks, [:save, :log]) # [] => [[:save, :log]]
111
113
  def append_value_to_class_method(method, value)
112
- old_value = send(method)
114
+ old_value = current_class_method_value(method)
113
115
  new_value = old_value.dup << value
114
116
  redefine_class_method(method, old_value.frozen? ? new_value.freeze : new_value)
115
117
  end
118
+
119
+ private
120
+
121
+ # @api private
122
+ # @param method [Symbol, String] name of the class method
123
+ # @return [Object] the method's current value
124
+ # @raise [TypeError] if the method returns `nil`
125
+ def current_class_method_value(method)
126
+ value = send(method)
127
+ return value unless value.nil?
128
+
129
+ raise TypeError, "#{self}.#{method} returned nil; define it to return a starting value such as {}.freeze " \
130
+ 'or [].freeze'
131
+ end
116
132
  end
117
133
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module InheritanceHelper
4
4
  # Gem version, bumped by release-please
5
- VERSION = '0.2.6'
5
+ VERSION = '0.3.0'
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inheritance-helper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Doug Youch
@@ -42,7 +42,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
42
42
  requirements:
43
43
  - - ">="
44
44
  - !ruby/object:Gem::Version
45
- version: '3.2'
45
+ version: '3.3'
46
46
  required_rubygems_version: !ruby/object:Gem::Requirement
47
47
  requirements:
48
48
  - - ">="