permittable 0.1.0 → 0.1.2
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 +4 -4
- data/CHANGELOG.md +26 -0
- data/lib/permittable/filter_parameter_registry.rb +0 -2
- data/lib/permittable/version.rb +1 -1
- data/lib/permittable.rb +19 -25
- metadata +10 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 84f1f4ca36c37a54ce5117ee6ec10bbd2e5dd5e9626e031c4dc35e8448ba1c35
|
|
4
|
+
data.tar.gz: c9ad5b56d78d44fd15de269547f431fd3441ec7233bc191cfa4c3ce391e8f19b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0a1b6d1805eca305ba87b6ab7b78b238db6230255dcf14fc69096ff37f352c2cb4cc14199f1a68dfa19863dee745583b79f73510de8597a99dbc55c2cd1c01dc
|
|
7
|
+
data.tar.gz: 3630d2cba76050554c72cb763b2dd95f9442f2f12032b7b35932139f2ea0c887ab4f460178deff932d80d74d41b797ceac0f30cd9551984530c05d96a06c8b6a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
<!-- CHANGELOG.md -->
|
|
2
2
|
|
|
3
|
+
## 0.1.2 (2026-08-16)
|
|
4
|
+
<!-- title: gem metadata -->
|
|
5
|
+
|
|
6
|
+
Metadata-only release. `lib/` is byte-for-byte identical to 0.1.1, so upgrading changes nothing at runtime — it exists to publish the rewritten gem description, which RubyGems only refreshes on a new version.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- Rewrote the gem summary and description. Both now open with the comparison the README already draws — strong parameters say which keys may pass, a contract says what each field should be — rather than with implementation vocabulary. The schema-drift guard is presented as a consequence of contracts being class-level data instead of as one more bullet.
|
|
10
|
+
|
|
11
|
+
### Internal
|
|
12
|
+
- The publish job creates the GitHub release itself, taking the title from this file's `<!-- title: ... -->` marker and the body from the matching section.
|
|
13
|
+
- Development dependencies: simplecov 0.22 → 1.1, `actions/checkout` 6 → 7.
|
|
14
|
+
|
|
15
|
+
## 0.1.1 (2026-08-16)
|
|
16
|
+
<!-- title: maintenance release -->
|
|
17
|
+
|
|
18
|
+
Maintenance release. The public API and every documented behaviour are identical to 0.1.0; upgrading is a no-op.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Internal style pass to satisfy the RuboCop config added in this release (hash alignment, guard clauses, anonymous block forwarding).
|
|
22
|
+
- `cast_datetime` folds `DateTime` into the `Time` branch whose body it already shared. Dispatch order is unchanged, so `DateTime` still matches ahead of `Date`, which it subclasses.
|
|
23
|
+
- Dropped `require "set"` from the filter parameter registry: `Set` has been autoloaded since Ruby 3.1 and the gemspec already floors at 3.2.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- CI on Ruby 3.2 (RuboCop + RSpec) and tag-driven publishing to RubyGems via trusted publishing. Repository tooling only — not part of the packaged gem.
|
|
27
|
+
|
|
3
28
|
## 0.1.0 (2026-08-16)
|
|
29
|
+
<!-- title: initial release -->
|
|
4
30
|
|
|
5
31
|
Initial extraction from [concerns_on_rails](https://github.com/VSN2015/concerns_on_rails) (developed there on `feature/permittable` as `ConcernsOnRails::Controllers::Permittable`; concerns_on_rails now depends on this gem and aliases that constant to `::Permittable`).
|
|
6
32
|
|
data/lib/permittable/version.rb
CHANGED
data/lib/permittable.rb
CHANGED
|
@@ -115,11 +115,11 @@ module Permittable
|
|
|
115
115
|
ROUTING_KEYS = %w[controller action format].freeze
|
|
116
116
|
|
|
117
117
|
NORMALIZERS = {
|
|
118
|
-
squish:
|
|
119
|
-
strip:
|
|
118
|
+
squish: ->(v) { v.squish },
|
|
119
|
+
strip: ->(v) { v.strip },
|
|
120
120
|
downcase: ->(v) { v.downcase },
|
|
121
|
-
upcase:
|
|
122
|
-
email:
|
|
121
|
+
upcase: ->(v) { v.upcase },
|
|
122
|
+
email: ->(v) { v.strip.downcase }
|
|
123
123
|
}.freeze
|
|
124
124
|
|
|
125
125
|
@registry_mutex = Mutex.new
|
|
@@ -271,8 +271,8 @@ module Permittable
|
|
|
271
271
|
# (deterministic); explicit offsets are honoured and normalised to UTC.
|
|
272
272
|
def cast_datetime(value)
|
|
273
273
|
case value
|
|
274
|
-
|
|
275
|
-
when DateTime then [:ok, value.to_time.utc]
|
|
274
|
+
# DateTime is listed here, ahead of Date, because it subclasses Date.
|
|
275
|
+
when ActiveSupport::TimeWithZone, Time, DateTime then [:ok, value.to_time.utc]
|
|
276
276
|
when Date then [:ok, Time.utc(value.year, value.month, value.day)]
|
|
277
277
|
when String then [:ok, DateTime.parse(value).to_time.utc]
|
|
278
278
|
else [:error, "invalid_type"]
|
|
@@ -315,8 +315,8 @@ module Permittable
|
|
|
315
315
|
@finalizer = nil
|
|
316
316
|
end
|
|
317
317
|
|
|
318
|
-
def build(&
|
|
319
|
-
instance_eval(&
|
|
318
|
+
def build(&)
|
|
319
|
+
instance_eval(&)
|
|
320
320
|
@fields.map(&:freeze).freeze
|
|
321
321
|
end
|
|
322
322
|
|
|
@@ -331,12 +331,12 @@ module Permittable
|
|
|
331
331
|
|
|
332
332
|
# `required :name` defaults the type to :string. A block instead of a
|
|
333
333
|
# type declares a nested hash of sub-fields.
|
|
334
|
-
def required(name, type = nil, **opts, &
|
|
335
|
-
add_field(name, type, required: true, opts: opts, &
|
|
334
|
+
def required(name, type = nil, **opts, &)
|
|
335
|
+
add_field(name, type, required: true, opts: opts, &)
|
|
336
336
|
end
|
|
337
337
|
|
|
338
|
-
def optional(name, type = nil, **opts, &
|
|
339
|
-
add_field(name, type, required: false, opts: opts, &
|
|
338
|
+
def optional(name, type = nil, **opts, &)
|
|
339
|
+
add_field(name, type, required: false, opts: opts, &)
|
|
340
340
|
end
|
|
341
341
|
|
|
342
342
|
# Array of scalars (`of:`, default :string) or, with a block, an array
|
|
@@ -384,9 +384,7 @@ module Permittable
|
|
|
384
384
|
|
|
385
385
|
def field_name!(name)
|
|
386
386
|
name = name.to_sym
|
|
387
|
-
if @fields.any? { |f| f[:name] == name }
|
|
388
|
-
raise ArgumentError, "#{LABEL}: field :#{name} is declared twice in the same contract"
|
|
389
|
-
end
|
|
387
|
+
raise ArgumentError, "#{LABEL}: field :#{name} is declared twice in the same contract" if @fields.any? { |f| f[:name] == name }
|
|
390
388
|
|
|
391
389
|
name
|
|
392
390
|
end
|
|
@@ -408,9 +406,9 @@ module Permittable
|
|
|
408
406
|
"(supported: #{SCALAR_TYPES.join(', ')})"
|
|
409
407
|
end
|
|
410
408
|
|
|
411
|
-
def nested_fields!(name, &
|
|
409
|
+
def nested_fields!(name, &)
|
|
412
410
|
builder = ContractBuilder.new
|
|
413
|
-
fields = builder.build(&
|
|
411
|
+
fields = builder.build(&)
|
|
414
412
|
raise ArgumentError, "#{LABEL}: nested field :#{name} declares no sub-fields" if fields.empty?
|
|
415
413
|
if builder.finalizer
|
|
416
414
|
raise ArgumentError, "#{LABEL}: finalize is only available at the top level of a contract (found inside :#{name})"
|
|
@@ -638,7 +636,7 @@ module Permittable
|
|
|
638
636
|
def render_invalid_parameters(error)
|
|
639
637
|
ErrorEnvelope.render(
|
|
640
638
|
self, message: error.message, status: error.status,
|
|
641
|
-
|
|
639
|
+
code: "invalid_parameters", details: error.details
|
|
642
640
|
)
|
|
643
641
|
end
|
|
644
642
|
|
|
@@ -650,12 +648,10 @@ module Permittable
|
|
|
650
648
|
result = ActiveSupport::HashWithIndifferentAccess.new
|
|
651
649
|
if source
|
|
652
650
|
result = permittable_check_hash(rule[:fields], source, path: rule[:root] ? rule[:root].to_s : nil,
|
|
653
|
-
|
|
651
|
+
unknown: rule[:unknown], top_level: !rule[:root], violations: violations)
|
|
654
652
|
end
|
|
655
653
|
# finalize only sees a hash every field vouched for — never garbage.
|
|
656
|
-
if violations.empty? && rule[:finalize]
|
|
657
|
-
result = permittable_run_finalize(rule[:finalize], result, violations)
|
|
658
|
-
end
|
|
654
|
+
result = permittable_run_finalize(rule[:finalize], result, violations) if violations.empty? && rule[:finalize]
|
|
659
655
|
return result if violations.empty?
|
|
660
656
|
|
|
661
657
|
raise_invalid_parameters!(violations, status: source ? :unprocessable_entity : :bad_request)
|
|
@@ -757,9 +753,7 @@ module Permittable
|
|
|
757
753
|
|
|
758
754
|
def permittable_check_array(field, value, path:, unknown:, violations:)
|
|
759
755
|
before = violations.length
|
|
760
|
-
if field[:length] && !Coercion.length_ok?(field[:length], value.length)
|
|
761
|
-
violations << { param: path, code: "length" }
|
|
762
|
-
end
|
|
756
|
+
violations << { param: path, code: "length" } if field[:length] && !Coercion.length_ok?(field[:length], value.length)
|
|
763
757
|
out = value.each_with_index.map do |element, index|
|
|
764
758
|
permittable_check_element(field, element, "#{path}[#{index}]", unknown: unknown, violations: violations)
|
|
765
759
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: permittable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ethan Nguyen
|
|
@@ -30,10 +30,13 @@ dependencies:
|
|
|
30
30
|
- - "<"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: '9'
|
|
33
|
-
description: '
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
description: 'Strong parameters answer only which keys may pass. A Permittable contract
|
|
34
|
+
also says what each field should be: it casts the value to a declared type, validates
|
|
35
|
+
bounds and formats, applies defaults, and renders every failure as a 422 that names
|
|
36
|
+
the offending parameter. Because a contract is class-level data rather than code
|
|
37
|
+
inside the action, it can also be checked against the database when the controller
|
|
38
|
+
loads, so a column dropped by a migration fails the deploy instead of the request.
|
|
39
|
+
activesupport is the only runtime dependency.'
|
|
37
40
|
email:
|
|
38
41
|
- doctorit@gmail.com
|
|
39
42
|
executables: []
|
|
@@ -72,8 +75,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
72
75
|
- !ruby/object:Gem::Version
|
|
73
76
|
version: '0'
|
|
74
77
|
requirements: []
|
|
75
|
-
rubygems_version: 3.4.
|
|
78
|
+
rubygems_version: 3.4.19
|
|
76
79
|
signing_key:
|
|
77
80
|
specification_version: 4
|
|
78
|
-
summary:
|
|
81
|
+
summary: Strong parameters for Rails that also know types, bounds, and defaults
|
|
79
82
|
test_files: []
|