easyop 0.1.4 → 0.1.5

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: 2b880af1b623158d3acfac130198bd96ce8c268195fd073554283738a880a45a
4
- data.tar.gz: 0c4f5554b456ac252b13734d0964155e2a80909e81946e28f89c529cc60f9081
3
+ metadata.gz: c649ea27731299fcf70421b41e0a19cca45612aa621310bf1bb87a70f5100da6
4
+ data.tar.gz: 7589af6ce926df84ccafb1df8b705430aaabc82b0929a0b9d8ed9bb4b7db818d
5
5
  SHA512:
6
- metadata.gz: c408d6d42a357ba1a6df4da21e13a7b0498d36fa4252e86c68aff289e8843d94d056c430f654f023e6d801ee650fc1bc4c400cb6d058a6b209129e1fbda644fd
7
- data.tar.gz: e89087c84c8fbc77abe96b6713a144dd34b78c2cfd35066f28a14e37516f0e7ae8b0f0cbbcfe0ca9eb9de646c7890552b64d7d2848c9976ff6f51637531a4a8a
6
+ metadata.gz: fc1a7e1c0427da2abb6588c61ece675431708b3b2a2bb623e62356ebfb6edf69025f6c13004ef6967d62f9381cb6c003793958f1c98326c34ba6266a9a56f774
7
+ data.tar.gz: d40f2a49035f449f95bfcefaae66dacc6c4d2e015d03d66c88f5f74e1680f8d7713d3f079ece4011d77ea238fc6181cf7072fbfe651a4be9be0876e5c8839eb2
data/CHANGELOG.md CHANGED
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.5] — 2026-04-14
11
+
12
+ ### Added
13
+
14
+ - **`scrub_params` DSL for `Easyop::Plugins::Recording`** — declare additional params keys/patterns to scrub from `params_data` on a per-class basis. Accepts `Symbol`, `String`, or `Regexp`. Additive with `SCRUBBED_KEYS` and never replaces the built-in list. Inherited by subclasses; any level of the hierarchy can override.
15
+
16
+ ```ruby
17
+ class ApplicationOperation < ...
18
+ scrub_params :api_token, /access.?key/i
19
+ end
20
+
21
+ class Orders::CreateOrder < ApplicationOperation
22
+ scrub_params :card_number # stacks on top of parent's scrub list
23
+ end
24
+ ```
25
+
26
+ - **`scrub_keys:` option on `plugin Easyop::Plugins::Recording`** — supply a list of extra keys/patterns to scrub at plugin install time. Inherited by all classes that share the same `plugin` declaration.
27
+
28
+ ```ruby
29
+ plugin Easyop::Plugins::Recording,
30
+ model: OperationLog,
31
+ scrub_keys: [:stripe_token, /secret/i]
32
+ ```
33
+
34
+ - **`Easyop::Configuration#recording_scrub_keys`** — new global config key. Set once at boot and every recorded operation will scrub these keys in addition to `SCRUBBED_KEYS` and any class-level declarations. Accepts `Symbol`, `String`, or `Regexp`.
35
+
36
+ ```ruby
37
+ Easyop.configure do |c|
38
+ c.recording_scrub_keys = [:api_token, /token/i]
39
+ end
40
+ ```
41
+
42
+ **Scrub precedence (all layers are additive):**
43
+ 1. `SCRUBBED_KEYS` — always applied (built-in list)
44
+ 2. `Easyop.config.recording_scrub_keys` — global config
45
+ 3. `scrub_keys:` plugin option + `scrub_params` DSL — class hierarchy
46
+
10
47
  ## [0.1.4] — 2026-04-14
11
48
 
12
49
  ### Added
@@ -258,6 +295,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
258
295
  - `examples/easyop_test_app/` — full Rails 8 blog application demonstrating all features in real-world code
259
296
  - `examples/usage.rb` — 13 runnable plain-Ruby examples
260
297
 
261
- [Unreleased]: https://github.com/pniemczyk/easyop/compare/v0.1.4...HEAD
298
+ [Unreleased]: https://github.com/pniemczyk/easyop/compare/v0.1.5...HEAD
299
+ [0.1.5]: https://github.com/pniemczyk/easyop/compare/v0.1.4...v0.1.5
262
300
  [0.1.4]: https://github.com/pniemczyk/easyop/compare/v0.1.3...v0.1.4
263
301
  [0.1.3]: https://github.com/pniemczyk/easyop/compare/v0.1.2...v0.1.3
data/README.md CHANGED
@@ -647,6 +647,7 @@ end
647
647
  | `model:` | required | ActiveRecord class to write logs into |
648
648
  | `record_params:` | `true` | Set `false` to skip serializing ctx params |
649
649
  | `record_result:` | `nil` | Plugin-level default for result capture (Hash/Proc/Symbol — see below) |
650
+ | `scrub_keys:` | `[]` | Extra keys/patterns to scrub from `params_data` (Symbol, String, Regexp) — additive with built-in list |
650
651
 
651
652
  **Required model columns:**
652
653
 
@@ -696,7 +697,28 @@ root_log = OperationLog.find_by(operation_name: "FullCheckout", parent_reference
696
697
  OperationLog.for_tree(root_log.root_reference_id)
697
698
  ```
698
699
 
699
- The plugin automatically scrubs these keys from `params_data` before persisting: `:password`, `:password_confirmation`, `:token`, `:secret`, `:api_key`. ActiveRecord objects are serialized as `{ id:, class: }` rather than their full representation.
700
+ **Scrubbing params** all layers are additive (none replaces the built-in list):
701
+
702
+ 1. **Built-in `SCRUBBED_KEYS`** — always applied: `:password`, `:password_confirmation`, `:token`, `:secret`, `:api_key`
703
+ 2. **Global config** — applied to every recorded operation:
704
+ ```ruby
705
+ Easyop.configure { |c| c.recording_scrub_keys = [:api_token, /token/i] }
706
+ ```
707
+ 3. **Plugin `scrub_keys:` option** — applied to all subclasses that share the plugin install:
708
+ ```ruby
709
+ plugin Easyop::Plugins::Recording, model: OperationLog, scrub_keys: [:stripe_secret]
710
+ ```
711
+ 4. **`scrub_params` DSL** — per-class, inheritable, and stackable at any level of the hierarchy:
712
+ ```ruby
713
+ class ApplicationOperation < ...
714
+ scrub_params :internal_token, /access.?key/i
715
+ end
716
+ class Payments::ChargeCard < ApplicationOperation
717
+ scrub_params :card_number # stacks on top of parent's list
718
+ end
719
+ ```
720
+
721
+ Internal tracing keys (`__recording_*`) are always excluded. ActiveRecord objects are serialized as `{ id:, class: }` rather than their full representation.
700
722
 
701
723
  **`record_result` DSL — capture output data:**
702
724
 
@@ -1459,7 +1481,7 @@ Edit `lib/easyop/version.rb` and increment the version string following [Semanti
1459
1481
  ```ruby
1460
1482
  # lib/easyop/version.rb
1461
1483
  module Easyop
1462
- VERSION = "0.1.4" # was 0.1.3
1484
+ VERSION = "0.1.5" # was 0.1.4
1463
1485
  end
1464
1486
  ```
1465
1487
 
@@ -1470,45 +1492,45 @@ In `CHANGELOG.md`, move everything under `[Unreleased]` into a new versioned sec
1470
1492
  ```markdown
1471
1493
  ## [Unreleased]
1472
1494
 
1473
- ## [0.1.4] — YYYY-MM-DD # ← new section
1495
+ ## [0.1.5] — YYYY-MM-DD # ← new section
1474
1496
 
1475
1497
  ### Added
1476
1498
  - …
1477
1499
 
1478
- ## [0.1.3] — 2026-04-14
1500
+ ## [0.1.4] — 2026-04-14
1479
1501
  ```
1480
1502
 
1481
1503
  Add a comparison link at the bottom of the file:
1482
1504
 
1483
1505
  ```markdown
1484
- [Unreleased]: https://github.com/pniemczyk/easyop/compare/v0.1.4...HEAD
1506
+ [Unreleased]: https://github.com/pniemczyk/easyop/compare/v0.1.5...HEAD
1507
+ [0.1.5]: https://github.com/pniemczyk/easyop/compare/v0.1.4...v0.1.5
1485
1508
  [0.1.4]: https://github.com/pniemczyk/easyop/compare/v0.1.3...v0.1.4
1486
- [0.1.3]: https://github.com/pniemczyk/easyop/compare/v0.1.2...v0.1.3
1487
1509
  ```
1488
1510
 
1489
1511
  ### 3. Commit the release changes
1490
1512
 
1491
1513
  ```bash
1492
1514
  git add lib/easyop/version.rb CHANGELOG.md
1493
- git commit -m "Release v0.1.4"
1515
+ git commit -m "Release v0.1.5"
1494
1516
  ```
1495
1517
 
1496
1518
  ### 4. Tag the commit
1497
1519
 
1498
1520
  ```bash
1499
- git tag -a v0.1.4 -m "Release v0.1.4"
1521
+ git tag -a v0.1.5 -m "Release v0.1.5"
1500
1522
  ```
1501
1523
 
1502
1524
  ### 5. Push the commit and tag
1503
1525
 
1504
1526
  ```bash
1505
1527
  git push origin master
1506
- git push origin v0.1.4
1528
+ git push origin v0.1.5
1507
1529
  ```
1508
1530
 
1509
1531
  ### 6. Build and push the gem (optional)
1510
1532
 
1511
1533
  ```bash
1512
1534
  gem build easyop.gemspec
1513
- gem push easyop-0.1.4.gem
1535
+ gem push easyop-0.1.5.gem
1514
1536
  ```
@@ -15,10 +15,19 @@ module Easyop
15
15
  # Easyop.configure { |c| c.event_bus = :active_support }
16
16
  attr_accessor :event_bus
17
17
 
18
+ # Extra keys to scrub from params_data across all recorded operations.
19
+ # Appended to Recording::SCRUBBED_KEYS — never replaces the built-in list.
20
+ # Accepts Symbol, String, or Regexp (matched against the stringified key name).
21
+ #
22
+ # @example
23
+ # Easyop.configure { |c| c.recording_scrub_keys = [:api_token, /token/i] }
24
+ attr_accessor :recording_scrub_keys
25
+
18
26
  def initialize
19
- @type_adapter = :native
20
- @strict_types = false
21
- @event_bus = nil # nil = Memory bus (see Easyop::Events::Registry)
27
+ @type_adapter = :native
28
+ @strict_types = false
29
+ @event_bus = nil # nil = Memory bus (see Easyop::Events::Registry)
30
+ @recording_scrub_keys = []
22
31
  end
23
32
  end
24
33
 
@@ -38,8 +38,10 @@ module Easyop
38
38
  # model: (required) ActiveRecord class
39
39
  # record_params: true pass false to skip params serialization
40
40
  # record_result: nil configure result capture at plugin level (Hash/Proc/Symbol)
41
+ # scrub_keys: [] additional keys/patterns to scrub from params_data
42
+ # (Symbol, String, or Regexp — additive with SCRUBBED_KEYS)
41
43
  module Recording
42
- # Sensitive keys scrubbed from params_data before persisting.
44
+ # Sensitive keys always scrubbed from params_data before persisting.
43
45
  SCRUBBED_KEYS = %i[password password_confirmation token secret api_key].freeze
44
46
 
45
47
  # Internal ctx keys used for flow tracing — excluded from params_data.
@@ -49,12 +51,13 @@ module Easyop
49
51
  __recording_parent_reference_id
50
52
  ].freeze
51
53
 
52
- def self.install(base, model:, record_params: true, record_result: nil, **_options)
54
+ def self.install(base, model:, record_params: true, record_result: nil, scrub_keys: [], **_options)
53
55
  base.extend(ClassMethods)
54
56
  base.prepend(RunWrapper)
55
57
  base.instance_variable_set(:@_recording_model, model)
56
58
  base.instance_variable_set(:@_recording_record_params, record_params)
57
59
  base.instance_variable_set(:@_recording_record_result, record_result)
60
+ base.instance_variable_set(:@_recording_scrub_keys, Array(scrub_keys))
58
61
  end
59
62
 
60
63
  module ClassMethods
@@ -109,6 +112,32 @@ module Easyop
109
112
  nil
110
113
  end
111
114
  end
115
+
116
+ # DSL to declare additional keys/patterns to scrub from params_data.
117
+ # Accepts Symbol, String, or Regexp. Additive with SCRUBBED_KEYS and
118
+ # any scrub_keys declared on parent classes or at the plugin install level.
119
+ #
120
+ # @example
121
+ # class ApplicationOperation < ...
122
+ # scrub_params :api_token, /access.?key/i
123
+ # end
124
+ def scrub_params(*keys)
125
+ @_recording_scrub_keys = _own_recording_scrub_keys + keys
126
+ end
127
+
128
+ # Returns the merged scrub list: parent class keys + this class's own keys.
129
+ # Does NOT include SCRUBBED_KEYS or the global config list — those are
130
+ # merged at persist time so they stay hot-reloadable.
131
+ def _recording_scrub_keys
132
+ parent = superclass.respond_to?(:_recording_scrub_keys) ? superclass._recording_scrub_keys : []
133
+ parent + _own_recording_scrub_keys
134
+ end
135
+
136
+ private
137
+
138
+ def _own_recording_scrub_keys
139
+ instance_variable_defined?(:@_recording_scrub_keys) ? @_recording_scrub_keys : []
140
+ end
112
141
  end
113
142
 
114
143
  module RunWrapper
@@ -182,14 +211,35 @@ module Easyop
182
211
  end
183
212
 
184
213
  def _recording_safe_params(ctx)
214
+ # Merge all scrub layers (additive, never replaces built-ins):
215
+ # 1. SCRUBBED_KEYS — built-in sensitive keys, always applied
216
+ # 2. Easyop.config — global extra keys set in an initializer
217
+ # 3. self.class — plugin install scrub_keys: + class scrub_params DSL
218
+ extra = Easyop.config.recording_scrub_keys.to_a + self.class._recording_scrub_keys
185
219
  ctx.to_h
186
- .except(*SCRUBBED_KEYS, *INTERNAL_CTX_KEYS)
220
+ .except(*INTERNAL_CTX_KEYS)
221
+ .reject { |k, _| _recording_scrub_key?(k, extra) }
187
222
  .transform_values { |v| v.is_a?(ActiveRecord::Base) ? { id: v.id, class: v.class.name } : v }
188
223
  .to_json
189
224
  rescue
190
225
  nil
191
226
  end
192
227
 
228
+ # Returns true when +key+ matches any entry in +scrub_list+.
229
+ # Regexp entries match against the stringified key name (case-sensitive
230
+ # unless the Regexp itself uses /i). Symbol/String entries match by value.
231
+ def _recording_scrub_key?(key, extra_list)
232
+ return true if SCRUBBED_KEYS.include?(key.to_sym)
233
+
234
+ extra_list.any? do |pattern|
235
+ case pattern
236
+ when Regexp then pattern.match?(key.to_s)
237
+ when Symbol then pattern == key.to_sym
238
+ else pattern.to_s == key.to_s
239
+ end
240
+ end
241
+ end
242
+
193
243
  def _recording_safe_result(ctx)
194
244
  config = self.class._recording_record_result_config
195
245
  return nil unless config
@@ -1,3 +1,3 @@
1
1
  module Easyop
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.5"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easyop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pawel Niemczyk