sanitization 1.2.0 → 2.0.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: e2fb43dabdf9271b466909b68e6e6df5b6fe81a20e68820549f1d0d8d4525269
4
- data.tar.gz: 41ce90afc106f2526c7df345de3910d1907ab27fe0250edf5ab00c31f36ee7e4
3
+ metadata.gz: be985c00478f7dc5d47a3019bc1aa0270574b0ed628ac82b0246d965a327f0d4
4
+ data.tar.gz: c0494a6ab39bdfb7b42ab3a10bb09ac1903cd99972e95bacee7849e519a63acf
5
5
  SHA512:
6
- metadata.gz: e3166de8170a6ddaf8cf9bd62b68d8d43ffff7c852f196e7f5af0b3fd9147979bc4d1d7164778425f197e514cff0f9051b41ae22346c48423f53b5d0a1f6541f
7
- data.tar.gz: 7c3b42cb30c2edb909c04ed6f1d3ff2704e9e1fb1a411a05c4535c11956535c08adf4f3714bd1d9b5292aaed7892c7a5e6661a182a32ff6f3f27dc7da6599655
6
+ metadata.gz: 8ca83b8ba349f0f5ec6043f4ce8fa8fdd8afd9b5b39251e108c50b0469f23c513d542519afdd0ea456f61c0bb74324006cce468cba675053e47d4065ff0f53b5
7
+ data.tar.gz: 83312695aba56c68450f7d0b060120b811c221095d862589f7efb84ecddb8af4b0655ae334d90c5790f62fb16603c45d8f16a606ccc8cd86b26c52dee496649e
data/Appraisals CHANGED
@@ -1,25 +1,15 @@
1
- appraise "rails-6" do
2
- gem "activerecord", "~> 6.1.4"
3
- gem "activesupport", "~> 6.1.4"
4
- gem "mutex_m", "~> 0.3.0"
5
- gem "base64"
1
+ appraise "rails-7.1" do
2
+ gem "activerecord", "~> 7.1.0"
3
+ gem "activesupport", "~> 7.1.0"
6
4
  gem "bigdecimal"
7
- gem "sqlite3", "~> 1.4"
8
-
9
- # Fixes uninitialized constant ActiveSupport::LoggerThreadSafeLevel::Logger (NameError)
10
- gem "concurrent-ruby", "< 1.3.5"
5
+ gem "sqlite3", "~> 2.6.0"
11
6
  end
12
7
 
13
- appraise "rails-7" do
14
- gem "activerecord", "~> 7.0.1"
15
- gem "activesupport", "~> 7.0.1"
16
- gem "mutex_m", "~> 0.3.0"
17
- gem "base64"
8
+ appraise "rails-7.2" do
9
+ gem "activerecord", "~> 7.2.0"
10
+ gem "activesupport", "~> 7.2.0"
18
11
  gem "bigdecimal"
19
- gem "sqlite3", "~> 1.4"
20
-
21
- # Fixes uninitialized constant ActiveSupport::LoggerThreadSafeLevel::Logger (NameError)
22
- gem "concurrent-ruby", "< 1.3.5"
12
+ gem "sqlite3", "~> 2.6.0"
23
13
  end
24
14
 
25
15
  appraise "rails-8" do
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # 2.0
2
+ * **BREAKING CHANGE:** Minimum Rails version is now 7.1 (dropped Rails 6.x and 7.0)
3
+ * **BREAKING CHANGE:** Minimum Ruby version is now 3.2
4
+ * Added support for Rails 7.1 and 7.2 in test matrix
5
+ * Reimplemented internals on top of Rails 7.1's `ActiveRecord::Base.normalizes`. The `sanitizes` DSL is unchanged, but normalization now runs at **attribute assignment time** instead of `before_save`. This produces a few subtle behavior changes:
6
+ * **Read-after-write returns the normalized value.** `person.name = " john "; person.name` now returns `"john"` immediately, not `" john "`. Validations and any pre-save reads see normalized values.
7
+ * **Legacy un-normalized rows are no longer rewritten on every save.** Previously the `before_save` callback rewrote every declared column on each save, opportunistically cleaning up old data. To migrate legacy rows now, reassign the attribute or call `record.normalize_attribute(:col)` before saving.
8
+ * **`where` and `find_by` hash conditions now normalize their input.** `User.where(email: " X@Y.com ")` will match a row stored as `"x@y.com"`. Raw-SQL conditions (`where("email = ?", ...)`) still bypass normalization.
9
+ * Apps upgrading from Rails < 7.1 should set `config.active_record.marshalling_format_version = 7.1` to avoid `TypeError` when marshalling records with normalized attributes.
10
+
1
11
  # 1.2
2
12
  * Official support for Rails 8
3
13
  * Official support for Ruby 3.4
data/CLAUDE.md ADDED
@@ -0,0 +1,50 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What This Is
6
+
7
+ The `sanitization` gem is a Ruby gem that adds automatic string sanitization to ActiveRecord models via a `before_save` callback. It can strip whitespace, collapse multiple spaces, convert empty strings to nil, and apply case transformations.
8
+
9
+ ## Commands
10
+
11
+ ```bash
12
+ # Run tests (default rake task)
13
+ rake spec
14
+
15
+ # Run tests with RSpec directly
16
+ bundle exec rspec
17
+
18
+ # Run a single test file or example
19
+ bundle exec rspec spec/sanitization_spec.rb
20
+ bundle exec rspec spec/sanitization_spec.rb:42
21
+
22
+ # Run tests across all supported Rails versions (6, 7, 8)
23
+ bundle exec appraisal rspec
24
+
25
+ # Run tests for a specific Rails version
26
+ bundle exec appraisal rails-8 rspec
27
+
28
+ # Install appraisal gemfiles after changing Appraisals file
29
+ bundle exec appraisal install
30
+ ```
31
+
32
+ ## Architecture
33
+
34
+ The gem has three core files:
35
+
36
+ - **`lib/sanitization/configuration.rb`** - `Sanitization::Configuration` class with global defaults (strip, collapse, case, nullify, include_text_type). `simple_defaults!` is a shortcut that enables strip + collapse + nullify.
37
+ - **`lib/sanitization/active_record_extension.rb`** - The core logic. `ClassMethods.sanitizes()` introspects table columns, filters by type (string, optionally text), validates options, and stores per-column config in `@sanitization__store`. Registers a `before_save` callback that applies transformations in order: strip -> collapse -> nullify -> case.
38
+ - **`lib/sanitization.rb`** - Entry point that includes `ActiveRecordExtension` into `ActiveRecord::Base` via `class_eval`.
39
+
40
+ ## Testing
41
+
42
+ Tests use RSpec with an in-memory SQLite database. The `spec_helper.rb` creates a `people` table with string, text, date, and integer columns. A custom `String#leetcase` method is defined for testing custom case transformations.
43
+
44
+ CI runs on Ruby 3.2, 3.3, 3.4 against Rails 6.1, 7.0, and 8.0 via Appraisal.
45
+
46
+ Note: ActiveSupport 7+ changed `titlecase` behavior (it strips leading spaces), which affects test expectations between Rails versions.
47
+
48
+ ## Changelog Workflow
49
+
50
+ Track changes in the `## Unreleased` section of `CHANGELOG.md` as they are made. When a new version is released, rename the `## Unreleased` heading to the version number (e.g., `# 1.3`) and add a fresh `## Unreleased` section above it.
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sanitization (1.2.0)
5
- activerecord
6
- activesupport
4
+ sanitization (2.0.0)
5
+ activerecord (>= 7.1)
6
+ activesupport (>= 7.1)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
@@ -43,7 +43,10 @@ GEM
43
43
  i18n (1.14.7)
44
44
  concurrent-ruby (~> 1.0)
45
45
  logger (1.7.0)
46
- minitest (5.25.5)
46
+ minitest (6.0.5)
47
+ drb (~> 2.0)
48
+ prism (~> 1.5)
49
+ prism (1.9.0)
47
50
  rake (13.2.1)
48
51
  rspec (3.13.0)
49
52
  rspec-core (~> 3.13.0)
@@ -59,16 +62,16 @@ GEM
59
62
  rspec-support (~> 3.13.0)
60
63
  rspec-support (3.13.2)
61
64
  securerandom (0.4.1)
62
- sqlite3 (2.6.0-aarch64-linux-gnu)
63
- sqlite3 (2.6.0-aarch64-linux-musl)
64
- sqlite3 (2.6.0-arm-linux-gnu)
65
- sqlite3 (2.6.0-arm-linux-musl)
66
- sqlite3 (2.6.0-arm64-darwin)
67
- sqlite3 (2.6.0-x86-linux-gnu)
68
- sqlite3 (2.6.0-x86-linux-musl)
69
- sqlite3 (2.6.0-x86_64-darwin)
70
- sqlite3 (2.6.0-x86_64-linux-gnu)
71
- sqlite3 (2.6.0-x86_64-linux-musl)
65
+ sqlite3 (2.9.3-aarch64-linux-gnu)
66
+ sqlite3 (2.9.3-aarch64-linux-musl)
67
+ sqlite3 (2.9.3-arm-linux-gnu)
68
+ sqlite3 (2.9.3-arm-linux-musl)
69
+ sqlite3 (2.9.3-arm64-darwin)
70
+ sqlite3 (2.9.3-x86-linux-gnu)
71
+ sqlite3 (2.9.3-x86-linux-musl)
72
+ sqlite3 (2.9.3-x86_64-darwin)
73
+ sqlite3 (2.9.3-x86_64-linux-gnu)
74
+ sqlite3 (2.9.3-x86_64-linux-musl)
72
75
  thor (1.3.2)
73
76
  timeout (0.4.3)
74
77
  tzinfo (2.0.6)
@@ -91,7 +94,6 @@ PLATFORMS
91
94
  DEPENDENCIES
92
95
  appraisal (~> 2.5)
93
96
  byebug
94
- concurrent-ruby (< 1.3.5)
95
97
  cucumber-ci-environment
96
98
  rake (~> 13.2)
97
99
  rspec (~> 3.0)
data/README.md CHANGED
@@ -13,6 +13,19 @@ Sanitization makes it easy to store slightly cleaner strings to your database.
13
13
  - Change casing (ie. upcase, downcase, titlecase, etc)
14
14
 
15
15
 
16
+ ### Built on top of `ActiveRecord::Base.normalizes`
17
+
18
+ As of 2.0, Sanitization is a thin macro layer on top of Rails 7.1's [`ActiveRecord::Base.normalizes`](https://api.rubyonrails.org/classes/ActiveRecord/Normalization/ClassMethods.html). The `sanitizes` DSL is unchanged — under the hood, each declared column is registered with `normalizes`, and Sanitization composes a single normalizer per column from your `:strip`, `:collapse`, `:nullify`, and `:case` options (plus their global defaults).
19
+
20
+ Because `normalizes` runs at attribute **assignment** time (not `before_save`), this gives you a few useful behaviors automatically:
21
+
22
+ - **Read-after-write returns the normalized value.** `person.name = " john "; person.name` returns `"john"` immediately — validations and any pre-save reads see the cleaned value.
23
+ - **`where`/`find_by` hash conditions normalize the lookup value.** `User.where(email: " RAW@X.COM ")` matches a row stored as `"raw@x.com"`. (Raw-SQL conditions still bypass normalization.)
24
+ - **Legacy un-normalized rows are not silently rewritten on save.** Reload + save no longer "heals" old data. To migrate, reassign the attribute or call `record.normalize_attribute(:col)` (a built-in helper from `normalizes`).
25
+
26
+ If you're upgrading from 1.x, see the CHANGELOG for the full list of behavior changes.
27
+
28
+
16
29
  ### Defaults
17
30
 
18
31
  By default, Sanitization has all options disabled. It is recommended you use a configuration block to set
@@ -45,6 +58,11 @@ Sanitization.simple_defaults!
45
58
  - Change casing: (`case: :none|:up|:down|:custom`)
46
59
 
47
60
 
61
+ ## Compatibility
62
+
63
+ - Ruby >= 3.2
64
+ - Rails >= 7.1
65
+
48
66
  ## Installation
49
67
 
50
68
  ```sh
@@ -6,13 +6,10 @@ gem "rake", "~> 13.2"
6
6
  gem "rspec", "~> 3.0"
7
7
  gem "appraisal", "~> 2.5"
8
8
  gem "cucumber-ci-environment"
9
- gem "sqlite3", "~> 1.4"
9
+ gem "sqlite3", "~> 2.6.0"
10
10
  gem "byebug"
11
- gem "activerecord", "~> 6.1.4"
12
- gem "activesupport", "~> 6.1.4"
13
- gem "mutex_m", "~> 0.3.0"
14
- gem "base64"
11
+ gem "activerecord", "~> 7.1.0"
12
+ gem "activesupport", "~> 7.1.0"
15
13
  gem "bigdecimal"
16
- gem "concurrent-ruby", "< 1.3.5"
17
14
 
18
15
  gemspec path: "../"
@@ -0,0 +1,113 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ sanitization (2.0.0)
5
+ activerecord (>= 7.1)
6
+ activesupport (>= 7.1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activemodel (7.1.6)
12
+ activesupport (= 7.1.6)
13
+ activerecord (7.1.6)
14
+ activemodel (= 7.1.6)
15
+ activesupport (= 7.1.6)
16
+ timeout (>= 0.4.0)
17
+ activesupport (7.1.6)
18
+ base64
19
+ benchmark (>= 0.3)
20
+ bigdecimal
21
+ concurrent-ruby (~> 1.0, >= 1.0.2)
22
+ connection_pool (>= 2.2.5)
23
+ drb
24
+ i18n (>= 1.6, < 2)
25
+ logger (>= 1.4.2)
26
+ minitest (>= 5.1)
27
+ mutex_m
28
+ securerandom (>= 0.3)
29
+ tzinfo (~> 2.0)
30
+ appraisal (2.5.0)
31
+ bundler
32
+ rake
33
+ thor (>= 0.14.0)
34
+ base64 (0.3.0)
35
+ benchmark (0.5.0)
36
+ bigdecimal (4.1.2)
37
+ byebug (13.0.0)
38
+ reline (>= 0.6.0)
39
+ concurrent-ruby (1.3.6)
40
+ connection_pool (3.0.2)
41
+ cucumber-ci-environment (13.0.0)
42
+ diff-lcs (1.6.2)
43
+ drb (2.2.3)
44
+ i18n (1.14.8)
45
+ concurrent-ruby (~> 1.0)
46
+ io-console (0.8.2)
47
+ logger (1.7.0)
48
+ mini_portile2 (2.8.9)
49
+ minitest (6.0.5)
50
+ drb (~> 2.0)
51
+ prism (~> 1.5)
52
+ mutex_m (0.3.0)
53
+ prism (1.9.0)
54
+ rake (13.4.2)
55
+ reline (0.6.3)
56
+ io-console (~> 0.5)
57
+ rspec (3.13.2)
58
+ rspec-core (~> 3.13.0)
59
+ rspec-expectations (~> 3.13.0)
60
+ rspec-mocks (~> 3.13.0)
61
+ rspec-core (3.13.6)
62
+ rspec-support (~> 3.13.0)
63
+ rspec-expectations (3.13.5)
64
+ diff-lcs (>= 1.2.0, < 2.0)
65
+ rspec-support (~> 3.13.0)
66
+ rspec-mocks (3.13.8)
67
+ diff-lcs (>= 1.2.0, < 2.0)
68
+ rspec-support (~> 3.13.0)
69
+ rspec-support (3.13.7)
70
+ securerandom (0.4.1)
71
+ sqlite3 (2.6.0)
72
+ mini_portile2 (~> 2.8.0)
73
+ sqlite3 (2.6.0-aarch64-linux-gnu)
74
+ sqlite3 (2.6.0-aarch64-linux-musl)
75
+ sqlite3 (2.6.0-arm-linux-gnu)
76
+ sqlite3 (2.6.0-arm-linux-musl)
77
+ sqlite3 (2.6.0-arm64-darwin)
78
+ sqlite3 (2.6.0-x86-linux-gnu)
79
+ sqlite3 (2.6.0-x86-linux-musl)
80
+ sqlite3 (2.6.0-x86_64-darwin)
81
+ sqlite3 (2.6.0-x86_64-linux-gnu)
82
+ sqlite3 (2.6.0-x86_64-linux-musl)
83
+ thor (1.5.0)
84
+ timeout (0.6.1)
85
+ tzinfo (2.0.6)
86
+ concurrent-ruby (~> 1.0)
87
+
88
+ PLATFORMS
89
+ aarch64-linux-gnu
90
+ aarch64-linux-musl
91
+ arm-linux-gnu
92
+ arm-linux-musl
93
+ arm64-darwin
94
+ x86-linux-gnu
95
+ x86-linux-musl
96
+ x86_64-darwin
97
+ x86_64-linux-gnu
98
+ x86_64-linux-musl
99
+
100
+ DEPENDENCIES
101
+ activerecord (~> 7.1.0)
102
+ activesupport (~> 7.1.0)
103
+ appraisal (~> 2.5)
104
+ bigdecimal
105
+ byebug
106
+ cucumber-ci-environment
107
+ rake (~> 13.2)
108
+ rspec (~> 3.0)
109
+ sanitization!
110
+ sqlite3 (~> 2.6.0)
111
+
112
+ BUNDLED WITH
113
+ 2.6.3
@@ -6,13 +6,10 @@ gem "rake", "~> 13.2"
6
6
  gem "rspec", "~> 3.0"
7
7
  gem "appraisal", "~> 2.5"
8
8
  gem "cucumber-ci-environment"
9
- gem "sqlite3", "~> 1.4"
9
+ gem "sqlite3", "~> 2.6.0"
10
10
  gem "byebug"
11
- gem "activerecord", "~> 7.0.1"
12
- gem "activesupport", "~> 7.0.1"
13
- gem "mutex_m", "~> 0.3.0"
14
- gem "base64"
11
+ gem "activerecord", "~> 7.2.0"
12
+ gem "activesupport", "~> 7.2.0"
15
13
  gem "bigdecimal"
16
- gem "concurrent-ruby", "< 1.3.5"
17
14
 
18
15
  gemspec path: "../"
@@ -0,0 +1,108 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ sanitization (2.0.0)
5
+ activerecord (>= 7.1)
6
+ activesupport (>= 7.1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activemodel (7.2.3.1)
12
+ activesupport (= 7.2.3.1)
13
+ activerecord (7.2.3.1)
14
+ activemodel (= 7.2.3.1)
15
+ activesupport (= 7.2.3.1)
16
+ timeout (>= 0.4.0)
17
+ activesupport (7.2.3.1)
18
+ base64
19
+ benchmark (>= 0.3)
20
+ bigdecimal
21
+ concurrent-ruby (~> 1.0, >= 1.3.1)
22
+ connection_pool (>= 2.2.5)
23
+ drb
24
+ i18n (>= 1.6, < 2)
25
+ logger (>= 1.4.2)
26
+ minitest (>= 5.1, < 6)
27
+ securerandom (>= 0.3)
28
+ tzinfo (~> 2.0, >= 2.0.5)
29
+ appraisal (2.5.0)
30
+ bundler
31
+ rake
32
+ thor (>= 0.14.0)
33
+ base64 (0.3.0)
34
+ benchmark (0.5.0)
35
+ bigdecimal (4.1.2)
36
+ byebug (13.0.0)
37
+ reline (>= 0.6.0)
38
+ concurrent-ruby (1.3.6)
39
+ connection_pool (3.0.2)
40
+ cucumber-ci-environment (13.0.0)
41
+ diff-lcs (1.6.2)
42
+ drb (2.2.3)
43
+ i18n (1.14.8)
44
+ concurrent-ruby (~> 1.0)
45
+ io-console (0.8.2)
46
+ logger (1.7.0)
47
+ mini_portile2 (2.8.9)
48
+ minitest (5.27.0)
49
+ rake (13.4.2)
50
+ reline (0.6.3)
51
+ io-console (~> 0.5)
52
+ rspec (3.13.2)
53
+ rspec-core (~> 3.13.0)
54
+ rspec-expectations (~> 3.13.0)
55
+ rspec-mocks (~> 3.13.0)
56
+ rspec-core (3.13.6)
57
+ rspec-support (~> 3.13.0)
58
+ rspec-expectations (3.13.5)
59
+ diff-lcs (>= 1.2.0, < 2.0)
60
+ rspec-support (~> 3.13.0)
61
+ rspec-mocks (3.13.8)
62
+ diff-lcs (>= 1.2.0, < 2.0)
63
+ rspec-support (~> 3.13.0)
64
+ rspec-support (3.13.7)
65
+ securerandom (0.4.1)
66
+ sqlite3 (2.6.0)
67
+ mini_portile2 (~> 2.8.0)
68
+ sqlite3 (2.6.0-aarch64-linux-gnu)
69
+ sqlite3 (2.6.0-aarch64-linux-musl)
70
+ sqlite3 (2.6.0-arm-linux-gnu)
71
+ sqlite3 (2.6.0-arm-linux-musl)
72
+ sqlite3 (2.6.0-arm64-darwin)
73
+ sqlite3 (2.6.0-x86-linux-gnu)
74
+ sqlite3 (2.6.0-x86-linux-musl)
75
+ sqlite3 (2.6.0-x86_64-darwin)
76
+ sqlite3 (2.6.0-x86_64-linux-gnu)
77
+ sqlite3 (2.6.0-x86_64-linux-musl)
78
+ thor (1.5.0)
79
+ timeout (0.6.1)
80
+ tzinfo (2.0.6)
81
+ concurrent-ruby (~> 1.0)
82
+
83
+ PLATFORMS
84
+ aarch64-linux-gnu
85
+ aarch64-linux-musl
86
+ arm-linux-gnu
87
+ arm-linux-musl
88
+ arm64-darwin
89
+ x86-linux-gnu
90
+ x86-linux-musl
91
+ x86_64-darwin
92
+ x86_64-linux-gnu
93
+ x86_64-linux-musl
94
+
95
+ DEPENDENCIES
96
+ activerecord (~> 7.2.0)
97
+ activesupport (~> 7.2.0)
98
+ appraisal (~> 2.5)
99
+ bigdecimal
100
+ byebug
101
+ cucumber-ci-environment
102
+ rake (~> 13.2)
103
+ rspec (~> 3.0)
104
+ sanitization!
105
+ sqlite3 (~> 2.6.0)
106
+
107
+ BUNDLED WITH
108
+ 2.6.3
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- sanitization (1.1.4)
5
- activerecord
6
- activesupport
4
+ sanitization (2.0.0)
5
+ activerecord (>= 7.1)
6
+ activesupport (>= 7.1)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
@@ -43,7 +43,11 @@ GEM
43
43
  i18n (1.14.7)
44
44
  concurrent-ruby (~> 1.0)
45
45
  logger (1.7.0)
46
- minitest (5.25.5)
46
+ mini_portile2 (2.8.9)
47
+ minitest (6.0.5)
48
+ drb (~> 2.0)
49
+ prism (~> 1.5)
50
+ prism (1.9.0)
47
51
  rake (13.2.1)
48
52
  rspec (3.13.0)
49
53
  rspec-core (~> 3.13.0)
@@ -59,6 +63,8 @@ GEM
59
63
  rspec-support (~> 3.13.0)
60
64
  rspec-support (3.13.2)
61
65
  securerandom (0.4.1)
66
+ sqlite3 (2.6.0)
67
+ mini_portile2 (~> 2.8.0)
62
68
  sqlite3 (2.6.0-aarch64-linux-gnu)
63
69
  sqlite3 (2.6.0-aarch64-linux-musl)
64
70
  sqlite3 (2.6.0-arm-linux-gnu)
@@ -93,7 +99,6 @@ DEPENDENCIES
93
99
  appraisal (~> 2.5)
94
100
  bigdecimal
95
101
  byebug
96
- concurrent-ruby (< 1.3.5)
97
102
  cucumber-ci-environment
98
103
  rake (~> 13.2)
99
104
  rspec (~> 3.0)
@@ -5,6 +5,52 @@ module Sanitization
5
5
  base.extend(ClassMethods)
6
6
  end
7
7
 
8
+ # Taken from `strip_attributes`: https://github.com/rmm5t/strip_attributes/blob/master/lib/strip_attributes.rb
9
+ # Unicode invisible and whitespace characters. The POSIX character class
10
+ # [:space:] corresponds to the Unicode class Z ("separator"). We also
11
+ # include the following characters from Unicode class C ("control"), which
12
+ # are spaces or invisible characters that make no sense at the start or end
13
+ # of a string:
14
+ # U+180E MONGOLIAN VOWEL SEPARATOR
15
+ # U+200B ZERO WIDTH SPACE
16
+ # U+200C ZERO WIDTH NON-JOINER
17
+ # U+200D ZERO WIDTH JOINER
18
+ # U+2060 WORD JOINER
19
+ # U+FEFF ZERO WIDTH NO-BREAK SPACE
20
+ MULTIBYTE_WHITE = "\u180E\u200B\u200C\u200D\u2060\uFEFF".freeze
21
+ MULTIBYTE_BLANK = /[[:blank:]#{MULTIBYTE_WHITE}]/.freeze
22
+ MULTIBYTE_SUPPORTED = "\u0020" == " "
23
+
24
+ def self.format_value(value, settings)
25
+ return value unless value.is_a?(String)
26
+
27
+ v = value.dup
28
+ v.strip! if value_or_default(settings, :strip)
29
+
30
+ if value_or_default(settings, :collapse)
31
+ if MULTIBYTE_SUPPORTED && Encoding.compatible?(v, MULTIBYTE_BLANK)
32
+ v.gsub!(/#{MULTIBYTE_BLANK}+/, " ")
33
+ else
34
+ v.squeeze!(" ")
35
+ end
36
+ end
37
+
38
+ return nil if value_or_default(settings, :nullify) && v.empty? && settings[:null]
39
+
40
+ case_method = value_or_default(settings, :case)
41
+ v = v.send(case_method) if case_method && case_method != :none
42
+
43
+ v
44
+ end
45
+
46
+ def self.value_or_default(settings, transform)
47
+ if settings[transform].nil?
48
+ Sanitization.configuration[transform]
49
+ else
50
+ settings[transform]
51
+ end
52
+ end
53
+
8
54
  module ClassMethods
9
55
  attr_accessor :sanitization__store
10
56
 
@@ -51,84 +97,21 @@ module Sanitization
51
97
  unless @valid_case_methods.include?(options[:case]) || options[:case] == :none
52
98
  end
53
99
 
100
+ klass = self
54
101
  columns_to_format.each do |col|
55
- self.sanitization__store[col.name] ||= {}
102
+ fresh = !self.sanitization__store.key?(col.name)
103
+ self.sanitization__store[col.name] ||= { null: col.null }
56
104
  self.sanitization__store[col.name].merge!(options.slice(:case, :strip, :collapse, :nullify))
57
- end
58
-
59
- class_eval <<-EOV
60
- include Sanitization::ActiveRecordExtension::InstanceMethods
61
- before_save :sanitization__format_strings
62
- EOV
63
- end
64
- alias sanitization sanitizes
65
- end # module ClassMethods
66
-
67
- module InstanceMethods
68
- # Taken from `strip_attributes`: https://github.com/rmm5t/strip_attributes/blob/master/lib/strip_attributes.rb
69
- # Unicode invisible and whitespace characters. The POSIX character class
70
- # [:space:] corresponds to the Unicode class Z ("separator"). We also
71
- # include the following characters from Unicode class C ("control"), which
72
- # are spaces or invisible characters that make no sense at the start or end
73
- # of a string:
74
- # U+180E MONGOLIAN VOWEL SEPARATOR
75
- # U+200B ZERO WIDTH SPACE
76
- # U+200C ZERO WIDTH NON-JOINER
77
- # U+200D ZERO WIDTH JOINER
78
- # U+2060 WORD JOINER
79
- # U+FEFF ZERO WIDTH NO-BREAK SPACE
80
- MULTIBYTE_WHITE = "\u180E\u200B\u200C\u200D\u2060\uFEFF".freeze
81
- MULTIBYTE_SPACE = /[[:space:]#{MULTIBYTE_WHITE}]/.freeze
82
- MULTIBYTE_BLANK = /[[:blank:]#{MULTIBYTE_WHITE}]/.freeze
83
- MULTIBYTE_SUPPORTED = "\u0020" == " "
84
-
85
- private
86
-
87
- def sanitization__format_strings
88
- return unless self.class.sanitization__store
89
105
 
90
- class_formatting = self.class.sanitization__store
91
- class_formatting.each_pair do |col_name, col_formatting|
92
- sanitization__format_column(col_name, col_formatting)
93
- end
94
- end
95
-
96
- def sanitization__format_column(col_name, col_formatting)
97
- return unless self[col_name].is_a?(String)
98
-
99
- self[col_name].strip! if value_or_default(col_formatting, :strip)
100
-
101
- if value_or_default(col_formatting, :collapse)
102
- if MULTIBYTE_SUPPORTED && Encoding.compatible?(self[col_name], MULTIBYTE_BLANK)
103
- self[col_name].gsub!(/#{MULTIBYTE_BLANK}+/, " ")
104
- else
105
- self[col_name].squeeze!(" ")
106
+ if fresh
107
+ col_name = col.name
108
+ normalizes col_name.to_sym, with: ->(value) {
109
+ Sanitization::ActiveRecordExtension.format_value(value, klass.sanitization__store[col_name])
110
+ }
106
111
  end
107
112
  end
108
-
109
- if value_or_default(col_formatting, :nullify) && !self[col_name].nil? && self[col_name].to_s.empty? && \
110
- self.class.columns.select { |c| c.name == col_name }.first.null
111
- return self[col_name] = nil
112
- end
113
-
114
- case_formatting_method = value_or_default(col_formatting, :case)
115
- if !case_formatting_method.nil? && case_formatting_method != :none
116
- self[col_name] = self[col_name].send(case_formatting_method)
117
- end
118
-
119
- self[col_name]
120
113
  end
121
-
122
- def value_or_default(col_formatting, transform)
123
- if col_formatting[transform].nil?
124
- Sanitization.configuration[transform]
125
- else
126
- col_formatting[transform]
127
- end
128
- end
129
-
130
-
131
- end # module InstanceMethods
114
+ alias sanitization sanitizes
115
+ end # module ClassMethods
132
116
  end # module ActiveRecordExt
133
117
  end # module Sanitization
134
-
@@ -1,3 +1,3 @@
1
1
  module Sanitization
2
- VERSION = "1.2.0"
2
+ VERSION = "2.0.0"
3
3
  end
data/sanitization.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.description = %q{Sanitization makes it easy to store slightly cleaner strings to your database.}
11
11
  spec.homepage = "https://github.com/cmer/sanitization"
12
12
  spec.license = "MIT"
13
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.2.0")
14
14
 
15
15
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
16
16
 
@@ -26,9 +26,8 @@ Gem::Specification.new do |spec|
26
26
  spec.bindir = "exe"
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ["lib"]
29
- spec.add_runtime_dependency "activerecord"
30
- spec.add_runtime_dependency "activesupport"
29
+ spec.add_runtime_dependency "activerecord", ">= 7.1"
30
+ spec.add_runtime_dependency "activesupport", ">= 7.1"
31
31
  spec.add_development_dependency "appraisal", "~> 2.5"
32
- spec.add_development_dependency "concurrent-ruby", "< 1.3.5"
33
32
  spec.add_development_dependency "sqlite3", "~> 2.6.0"
34
33
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sanitization
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carl Mercier
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-27 00:00:00.000000000 Z
10
+ date: 2026-04-28 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activerecord
@@ -15,28 +15,28 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '0'
18
+ version: '7.1'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '0'
25
+ version: '7.1'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: activesupport
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: '0'
32
+ version: '7.1'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: '0'
39
+ version: '7.1'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: appraisal
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -51,20 +51,6 @@ dependencies:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '2.5'
54
- - !ruby/object:Gem::Dependency
55
- name: concurrent-ruby
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "<"
59
- - !ruby/object:Gem::Version
60
- version: 1.3.5
61
- type: :development
62
- prerelease: false
63
- version_requirements: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "<"
66
- - !ruby/object:Gem::Version
67
- version: 1.3.5
68
54
  - !ruby/object:Gem::Dependency
69
55
  name: sqlite3
70
56
  requirement: !ruby/object:Gem::Requirement
@@ -93,6 +79,7 @@ files:
93
79
  - ".tool-versions"
94
80
  - Appraisals
95
81
  - CHANGELOG.md
82
+ - CLAUDE.md
96
83
  - Gemfile
97
84
  - Gemfile.lock
98
85
  - LICENSE.txt
@@ -100,10 +87,10 @@ files:
100
87
  - Rakefile
101
88
  - bin/console
102
89
  - bin/setup
103
- - gemfiles/rails_6.gemfile
104
- - gemfiles/rails_6.gemfile.lock
105
- - gemfiles/rails_7.gemfile
106
- - gemfiles/rails_7.gemfile.lock
90
+ - gemfiles/rails_7.1.gemfile
91
+ - gemfiles/rails_7.1.gemfile.lock
92
+ - gemfiles/rails_7.2.gemfile
93
+ - gemfiles/rails_7.2.gemfile.lock
107
94
  - gemfiles/rails_8.gemfile
108
95
  - gemfiles/rails_8.gemfile.lock
109
96
  - lib/sanitization.rb
@@ -126,7 +113,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
126
113
  requirements:
127
114
  - - ">="
128
115
  - !ruby/object:Gem::Version
129
- version: 2.7.0
116
+ version: 3.2.0
130
117
  required_rubygems_version: !ruby/object:Gem::Requirement
131
118
  requirements:
132
119
  - - ">="
@@ -1,86 +0,0 @@
1
- PATH
2
- remote: ..
3
- specs:
4
- sanitization (1.1.4)
5
- activerecord
6
- activesupport
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- activemodel (6.1.7.10)
12
- activesupport (= 6.1.7.10)
13
- activerecord (6.1.7.10)
14
- activemodel (= 6.1.7.10)
15
- activesupport (= 6.1.7.10)
16
- activesupport (6.1.7.10)
17
- concurrent-ruby (~> 1.0, >= 1.0.2)
18
- i18n (>= 1.6, < 2)
19
- minitest (>= 5.1)
20
- tzinfo (~> 2.0)
21
- zeitwerk (~> 2.3)
22
- appraisal (2.5.0)
23
- bundler
24
- rake
25
- thor (>= 0.14.0)
26
- base64 (0.2.0)
27
- bigdecimal (3.1.9)
28
- byebug (12.0.0)
29
- concurrent-ruby (1.3.4)
30
- cucumber-ci-environment (10.0.1)
31
- diff-lcs (1.6.1)
32
- i18n (1.14.7)
33
- concurrent-ruby (~> 1.0)
34
- mini_portile2 (2.8.8)
35
- minitest (5.25.5)
36
- mutex_m (0.3.0)
37
- rake (13.2.1)
38
- rspec (3.13.0)
39
- rspec-core (~> 3.13.0)
40
- rspec-expectations (~> 3.13.0)
41
- rspec-mocks (~> 3.13.0)
42
- rspec-core (3.13.3)
43
- rspec-support (~> 3.13.0)
44
- rspec-expectations (3.13.3)
45
- diff-lcs (>= 1.2.0, < 2.0)
46
- rspec-support (~> 3.13.0)
47
- rspec-mocks (3.13.2)
48
- diff-lcs (>= 1.2.0, < 2.0)
49
- rspec-support (~> 3.13.0)
50
- rspec-support (3.13.2)
51
- sqlite3 (1.7.3)
52
- mini_portile2 (~> 2.8.0)
53
- thor (1.3.2)
54
- tzinfo (2.0.6)
55
- concurrent-ruby (~> 1.0)
56
- zeitwerk (2.7.2)
57
-
58
- PLATFORMS
59
- aarch64-linux-gnu
60
- aarch64-linux-musl
61
- arm-linux-gnu
62
- arm-linux-musl
63
- arm64-darwin
64
- x86-linux-gnu
65
- x86-linux-musl
66
- x86_64-darwin
67
- x86_64-linux-gnu
68
- x86_64-linux-musl
69
-
70
- DEPENDENCIES
71
- activerecord (~> 6.1.4)
72
- activesupport (~> 6.1.4)
73
- appraisal (~> 2.5)
74
- base64
75
- bigdecimal
76
- byebug
77
- concurrent-ruby (< 1.3.5)
78
- cucumber-ci-environment
79
- mutex_m (~> 0.3.0)
80
- rake (~> 13.2)
81
- rspec (~> 3.0)
82
- sanitization!
83
- sqlite3 (~> 1.4)
84
-
85
- BUNDLED WITH
86
- 2.6.3
@@ -1,84 +0,0 @@
1
- PATH
2
- remote: ..
3
- specs:
4
- sanitization (1.1.4)
5
- activerecord
6
- activesupport
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- activemodel (7.0.8.7)
12
- activesupport (= 7.0.8.7)
13
- activerecord (7.0.8.7)
14
- activemodel (= 7.0.8.7)
15
- activesupport (= 7.0.8.7)
16
- activesupport (7.0.8.7)
17
- concurrent-ruby (~> 1.0, >= 1.0.2)
18
- i18n (>= 1.6, < 2)
19
- minitest (>= 5.1)
20
- tzinfo (~> 2.0)
21
- appraisal (2.5.0)
22
- bundler
23
- rake
24
- thor (>= 0.14.0)
25
- base64 (0.2.0)
26
- bigdecimal (3.1.9)
27
- byebug (12.0.0)
28
- concurrent-ruby (1.3.4)
29
- cucumber-ci-environment (10.0.1)
30
- diff-lcs (1.6.1)
31
- i18n (1.14.7)
32
- concurrent-ruby (~> 1.0)
33
- mini_portile2 (2.8.8)
34
- minitest (5.25.5)
35
- mutex_m (0.3.0)
36
- rake (13.2.1)
37
- rspec (3.13.0)
38
- rspec-core (~> 3.13.0)
39
- rspec-expectations (~> 3.13.0)
40
- rspec-mocks (~> 3.13.0)
41
- rspec-core (3.13.3)
42
- rspec-support (~> 3.13.0)
43
- rspec-expectations (3.13.3)
44
- diff-lcs (>= 1.2.0, < 2.0)
45
- rspec-support (~> 3.13.0)
46
- rspec-mocks (3.13.2)
47
- diff-lcs (>= 1.2.0, < 2.0)
48
- rspec-support (~> 3.13.0)
49
- rspec-support (3.13.2)
50
- sqlite3 (1.7.3)
51
- mini_portile2 (~> 2.8.0)
52
- thor (1.3.2)
53
- tzinfo (2.0.6)
54
- concurrent-ruby (~> 1.0)
55
-
56
- PLATFORMS
57
- aarch64-linux-gnu
58
- aarch64-linux-musl
59
- arm-linux-gnu
60
- arm-linux-musl
61
- arm64-darwin
62
- x86-linux-gnu
63
- x86-linux-musl
64
- x86_64-darwin
65
- x86_64-linux-gnu
66
- x86_64-linux-musl
67
-
68
- DEPENDENCIES
69
- activerecord (~> 7.0.1)
70
- activesupport (~> 7.0.1)
71
- appraisal (~> 2.5)
72
- base64
73
- bigdecimal
74
- byebug
75
- concurrent-ruby (< 1.3.5)
76
- cucumber-ci-environment
77
- mutex_m (~> 0.3.0)
78
- rake (~> 13.2)
79
- rspec (~> 3.0)
80
- sanitization!
81
- sqlite3 (~> 1.4)
82
-
83
- BUNDLED WITH
84
- 2.6.3