deprecate_soft 1.0.0 → 1.1.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: f38db5179c81ba2e97d0a09bebeea87648eed96271b87a0089c3e444d122c9f5
4
- data.tar.gz: fba061e831a26a0cf0be6bd99970cf84dd4130d3d93b6e53fbe01f5fa0b2846d
3
+ metadata.gz: 8b9799b61944284b4206b894196574252ccbcf7e550b127d7f45dfe524a943a5
4
+ data.tar.gz: 1474ec114cd60edcd85a7c975efb69121654f19ea0659ca4b13f577ca9370520
5
5
  SHA512:
6
- metadata.gz: 5d5d5ed40760cb78b41399924db249e93c585eef04f2f5d370c2cbfcfcfd5ae1621cea064c1b681e3ab90a16f63b814afe48c50ed5c4f4d32144602e7288ee49
7
- data.tar.gz: 0124ae4b6a825c3a5920b2bee96417e73a44b9c19f527797cc35e7c852e43b5867369b04c0167a2efa2728eed8b7e99c894e9d74464ca09b36a8aef3c98bce64
6
+ metadata.gz: fd8cd841c2c67e77b3b7aed65e4fe0405f19d296c727c051346ba139662c395c3542e2848ee5d9c09146653bd6bdc5fdfa3756fcb57f95412d29a355d729841f
7
+ data.tar.gz: 8662fc4dec07bfc90b334d3aa5127b5c9042af70168d3013e03045f35d19beec572ffb5f16dfc386e169e48e590c83c600c9c7d823b724a5ee48615a7045f5b7
data/.rubocop.yml CHANGED
@@ -36,3 +36,6 @@ Style/Documentation:
36
36
 
37
37
  Style/RedundantSelf:
38
38
  Enabled: false
39
+
40
+ Style/SingleLineMethods:
41
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,7 +1,17 @@
1
1
  ## DepracateSoft Change Log
2
2
 
3
+ ## [1.1.0] - 2025-03-29
4
+ - simplify initialization
5
+ - added tests for corner cases:
6
+ - deprecate_soft called before method definition
7
+ - deprecate_soft called twice
8
+ - deprecate_soft for private methods
9
+ - before_hook or after_hook raise
10
+ - prevent double-wrapping methods
11
+ - ensure that methods run undisturbed, even if the before_hook or after_hook raise an exception
12
+
3
13
  ## [1.0.0] - 2025-03-24
4
14
  - Initial release
15
+
5
16
  ## [0.0.1] - 2025-03-22
6
17
  - pre-release
7
-
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  # DeprecateSoft
3
3
 
4
- DeprecateSoft is a lightweight and flexible Ruby gem designed to help you gracefully and safely deprecate methods.
4
+ DeprecateSoft is a lightweight and flexible Ruby gem designed to help you gracefully and safely delete methods.
5
5
 
6
6
  It was inspired by the need to track deprecated method usage in large codebases before safely removing old code — with zero disruption and flexible metrics support.
7
7
 
@@ -11,6 +11,8 @@ It’s ideal for monitoring deprecated method usage across your application usin
11
11
 
12
12
  Once tracking confirms that a deprecated method is no longer in use, you can confidently delete it from your codebase.
13
13
 
14
+ This mechanism has been **proven in large-scale production systems** to safely clean up legacy code — this gem reimagines that functionality to help you clean up your code with confidence.
15
+
14
16
  ---
15
17
 
16
18
  ## ✨ Features
@@ -19,9 +21,12 @@ Once tracking confirms that a deprecated method is no longer in use, you can con
19
21
  - Works with instance methods in any class or module
20
22
  - Works with class or module methods in any class or module
21
23
  - System-wide hook configuration (before and after)
22
- - No monkey-patching or global pollution
24
+ - No monkey-patching or global pollution — unless you explicitly opt in via `GlobalMonkeyPatch`
23
25
  - Fully compatible with Rails or plain Ruby apps
24
26
 
27
+ ---
28
+ ## 📖 Blog Posts
29
+ - [Safely Delete Old Ruby Code with deprecate_soft](https://medium.com/@tilo-sloboda/safely-delete-old-code-with-deprecate-soft-89e819b41d52)
25
30
  ---
26
31
 
27
32
  ## 🚀 Installation
@@ -38,6 +43,62 @@ Then run:
38
43
  bundle install
39
44
  ```
40
45
 
46
+ And add your initializer file.
47
+
48
+ ---
49
+
50
+ ## 🧩 Usage
51
+
52
+ Declare `deprecate_soft` **after** the method definition.
53
+
54
+ ### For Instance Methods:
55
+
56
+ ```ruby
57
+ class MyService
58
+ include DeprecateSoft
59
+
60
+ def deprecated_method(a, b)
61
+ puts "doing something with #{a} and #{b}"
62
+ end
63
+
64
+ deprecate_soft :deprecated_method, "Use #new_method instead"
65
+ end
66
+
67
+ MyService.new.deprecated_method(1, 2) # will exercise the tracking hooks
68
+ ```
69
+
70
+ ### For Class Methods:
71
+
72
+ ```ruby
73
+ class MyService
74
+ extend DeprecateSoft
75
+
76
+ def self.deprecated_method(a, b)
77
+ puts "doing something with #{a} and #{b}"
78
+ end
79
+
80
+ deprecate_soft :deprecated_method, "will be removed"
81
+ end
82
+
83
+ MyService.deprecated_method(1, 2) # will exercise the tracking hooks
84
+
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 🔐 What It Does Under the Hood
90
+
91
+ When you call `deprecate_soft :method_name, "reason"`:
92
+
93
+ 1. It renames the original method to `__method_name_deprecated`.
94
+ 2. It defines a new method with the original name that:
95
+ - Calls the configured `before_hook` (if set)
96
+ - Delegates to the original method
97
+ - Calls the configured `after_hook` (if set)
98
+ 3. The optional `message` with the reason can help identifying alternatives.
99
+
100
+ This ensures consistent tracking, clean method resolution, and avoids accidental bypassing.
101
+
41
102
  ---
42
103
 
43
104
  ## ⚙️ Configuration
@@ -96,7 +157,7 @@ end
96
157
 
97
158
  This setup ensures you can plug in **any tracking backend** you like — without polluting the global namespace.
98
159
 
99
- ### 🔧 Customizing Method Name Wrapping
160
+ ### 🔧 Optional: Customizing Method Name Wrapping
100
161
 
101
162
  When `deprecate_soft` wraps a method, it renames the original method internally to preserve its behavior. You can customize how that internal method is named by configuring a `prefix` and `suffix`.
102
163
 
@@ -137,61 +198,6 @@ This gives you full control over how deprecated methods are renamed internally.
137
198
 
138
199
  These names are never called directly — they're used internally to wrap and preserve the original method logic.
139
200
 
140
-
141
- ---
142
-
143
- ## 🧩 Usage
144
-
145
- 🚨 Always declare `deprecate_soft` **after** the method definition!
146
-
147
- ### For Instance Methods:
148
-
149
- ```ruby
150
- class MyService
151
- include DeprecateSoft
152
-
153
- def deprecated_method(a, b)
154
- puts "doing something with #{a} and #{b}"
155
- end
156
-
157
- deprecate_soft :deprecated_method, "Use #new_method instead"
158
- end
159
-
160
- MyService.new.deprecated_method(1, 2) # will exercise the tracking hooks
161
- ```
162
-
163
- ### For Class Methods:
164
-
165
- ```ruby
166
- class MyService
167
- extend DeprecateSoft
168
-
169
- def self.deprecated_method(a, b)
170
- puts "doing something with #{a} and #{b}"
171
- end
172
-
173
- deprecate_soft :deprecated_method, "will be removed"
174
- end
175
-
176
- MyService.deprecated_method(1, 2) # will exercise the tracking hooks
177
-
178
- ```
179
-
180
- ---
181
-
182
- ## 🔐 What It Does Under the Hood
183
-
184
- When you call `deprecate_soft :method_name, "reason"`:
185
-
186
- 1. It renames the original method to `__method_name_deprecated`.
187
- 2. It defines a new method with the original name that:
188
- - Calls the configured `before_hook` (if set)
189
- - Delegates to the original method
190
- - Calls the configured `after_hook` (if set)
191
- 3. The optional `message` with the reason can help identifying alternatives.
192
-
193
- This ensures consistent tracking, clean method resolution, and avoids accidental bypassing.
194
-
195
201
  ---
196
202
 
197
203
  ## 🧪 Example Hook Logic
@@ -273,6 +279,36 @@ Klass#legacy_method:caller:app/jobs/cleanup_job.rb:88 → 4
273
279
 
274
280
  💡 Now you not only know that the method is still used -- you know where from, and how often -- so you can fix your code.
275
281
 
282
+ ---
283
+
284
+ ## 💪 Optional: Global Monkey Patching
285
+
286
+ For large projects, it can be beneficial to enable deprecate_soft across the entire codebase without having to explicitly `include DeprecateSoft` or e`xtend DeprecateSoft` in each class or module.
287
+
288
+ To do this, you can globally monkey-patch `Module` by including `DeprecateSoft::GlobalMonkeyPatch`. This is **entirely optional and not enabled by default**.
289
+
290
+ Add the following to your `config/initializers/deprecate_soft.rb` initializer:
291
+
292
+ ```ruby
293
+ # config/initializers/deprecate_soft.rb
294
+
295
+ require "deprecate_soft"
296
+ require "deprecate_soft/global_monkey_patch"
297
+
298
+ # ...
299
+
300
+ class Module
301
+ include DeprecateSoft::GlobalMonkeyPatch
302
+ end
303
+
304
+ DeprecateSoft.configure do |config|
305
+ #
306
+ # ...
307
+ #
308
+ end
309
+
310
+ ```
311
+
276
312
  ---
277
313
 
278
314
  ## 🛡 Best Practices
@@ -280,7 +316,7 @@ Klass#legacy_method:caller:app/jobs/cleanup_job.rb:88 → 4
280
316
  - Use `deprecate_soft` for methods you plan to remove but want to confirm they are no longer used.
281
317
  - Integrate with your observability platform for tracking.
282
318
  - Review usage stats before deleting deprecated methods from your code.
283
- - 🚨 Always declare `deprecate_soft` **after** the method definition! 🚨
319
+ - Always declare `deprecate_soft` **after** the method definition.
284
320
 
285
321
  ---
286
322
 
@@ -289,7 +325,6 @@ Klass#legacy_method:caller:app/jobs/cleanup_job.rb:88 → 4
289
325
  - Make sure hooks do not raise or interfere with production behavior.
290
326
  - Only use non-blocking, low-latency methods for tracking!
291
327
  - Currently assumes Ruby 2.5+ (for `&.` and keyword args support).
292
- - Currently keeps the visibility of the renamed original method the same (does not make it private).
293
328
 
294
329
  ---
295
330
 
@@ -297,9 +332,9 @@ Klass#legacy_method:caller:app/jobs/cleanup_job.rb:88 → 4
297
332
 
298
333
  Feel free to open issues or pull requests if you'd like to:
299
334
 
300
- - Add support for class methods
335
+ - Request new features
301
336
  - Add Railtie for automatic setup
302
- - Add built-in backends (e.g. Redis, StatsD)
337
+ - Add support for other backends
303
338
 
304
339
  ---
305
340
 
@@ -6,27 +6,55 @@ module DeprecateSoft
6
6
  hidden_method_name = "#{DeprecateSoft.prefix}#{method_name}_#{DeprecateSoft.suffix}"
7
7
 
8
8
  if is_class_method
9
- original_method = context.method(method_name)
9
+ target = context.singleton_class
10
+
11
+ return if target.method_defined?(hidden_method_name) || target.private_method_defined?(hidden_method_name)
10
12
 
11
- context.singleton_class.define_method(hidden_method_name, original_method)
13
+ original_method = context.method(method_name)
14
+ target.define_method(hidden_method_name, original_method)
12
15
 
13
- context.singleton_class.define_method(method_name) do |*args, &block|
16
+ target.define_method(method_name) do |*args, &block|
14
17
  full_name = "#{self.name}.#{method_name}"
15
- DeprecateSoft.before_hook&.call(full_name, message, args: args) if defined?(DeprecateSoft.before_hook)
18
+
19
+ begin
20
+ DeprecateSoft.before_hook&.call(full_name, message, args: args)
21
+ rescue StandardError => e
22
+ warn "DeprecateSoft.before_hook error: #{e.class} - #{e.message}"
23
+ end
24
+
16
25
  result = send(hidden_method_name, *args, &block)
17
- DeprecateSoft.after_hook&.call(full_name, message, result: result) if defined?(DeprecateSoft.after_hook)
26
+
27
+ begin
28
+ DeprecateSoft.after_hook&.call(full_name, message, result: result)
29
+ rescue StandardError => e
30
+ warn "DeprecateSoft.after_hook error: #{e.class} - #{e.message}"
31
+ end
32
+
18
33
  result
19
34
  end
20
35
  else
21
- original_method = context.instance_method(method_name)
36
+ return if context.method_defined?(hidden_method_name) || context.private_method_defined?(hidden_method_name)
22
37
 
38
+ original_method = context.instance_method(method_name)
23
39
  context.define_method(hidden_method_name, original_method)
24
40
 
25
41
  context.define_method(method_name) do |*args, &block|
26
42
  full_name = "#{self.class}##{method_name}"
27
- DeprecateSoft.before_hook&.call(full_name, message, args: args) if defined?(DeprecateSoft.before_hook)
43
+
44
+ begin
45
+ DeprecateSoft.before_hook&.call(full_name, message, args: args)
46
+ rescue StandardError => e
47
+ warn "DeprecateSoft.before_hook error: #{e.class} - #{e.message}"
48
+ end
49
+
28
50
  result = send(hidden_method_name, *args, &block)
29
- DeprecateSoft.after_hook&.call(full_name, message, result: result) if defined?(DeprecateSoft.after_hook)
51
+
52
+ begin
53
+ DeprecateSoft.after_hook&.call(full_name, message, result: result)
54
+ rescue StandardError => e
55
+ warn "DeprecateSoft.after_hook error: #{e.class} - #{e.message}"
56
+ end
57
+
30
58
  result
31
59
  end
32
60
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeprecateSoft
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'
5
5
  end
@@ -5,6 +5,20 @@ require_relative 'deprecate_soft/version'
5
5
  require_relative 'deprecate_soft/method_wrapper'
6
6
 
7
7
  module DeprecateSoft
8
+ def configure_base(base)
9
+ base.extend(ClassMethods)
10
+ base.extend(InstanceMethods)
11
+ end
12
+ module_function :configure_base
13
+
14
+ def included(base)
15
+ configure_base(base)
16
+ end
17
+
18
+ def extended(base)
19
+ configure_base(base)
20
+ end
21
+
8
22
  class << self
9
23
  attr_accessor :before_hook, :after_hook
10
24
  attr_writer :prefix, :suffix
@@ -20,14 +34,6 @@ module DeprecateSoft
20
34
  def configure
21
35
  yield self
22
36
  end
23
-
24
- def included(base)
25
- base.extend InstanceMethods
26
- end
27
-
28
- def extended(base)
29
- base.extend ClassMethods
30
- end
31
37
  end
32
38
 
33
39
  module InstanceMethods
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deprecate_soft
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tilo Sloboda
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-03-25 00:00:00.000000000 Z
11
+ date: 2025-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dogstatsd-ruby
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement