attribeauty 0.3.1 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +1 -1
- data/README.md +42 -1
- data/lib/attribeauty/params.rb +32 -17
- data/lib/attribeauty/types/boolean.rb +2 -0
- data/lib/attribeauty/types/float.rb +2 -0
- data/lib/attribeauty/types/integer.rb +2 -0
- data/lib/attribeauty/types/string.rb +2 -0
- data/lib/attribeauty/types/time.rb +2 -0
- data/lib/attribeauty/validator.rb +51 -26
- data/lib/attribeauty/version.rb +1 -1
- data/lib/attribeauty.rb +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 812191110b6c7f397737c3a9b1cceab66819c07d549c02c31687b8260342f404
|
4
|
+
data.tar.gz: 2ce7d61531ba7dcb7a896994ef94a2fa970bd821eb62d0901cc2b1df5e70947b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbe0d664e43e995eb4caf66f66fe2a242d2f40b9effda38f511775451c666c82e1596a9116197e5cc84848f0f63ab11f2bb0f537f2214e0d38c6f9b3ddba2e1f
|
7
|
+
data.tar.gz: 66fb433c37a8986c4f5cf3bec0dae55246360fb6f65d82e2e25d1f507b3f4526d5476c99fa7f3e5244d6d255cdb9f429c51e7a7c7c84504e61fe2083e864bf0b
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.3] - 2024-06-24
|
4
|
+
|
5
|
+
- handle nil in types. Big refactor
|
6
|
+
|
7
|
+
## [0.3.2] - 2024-06-24
|
8
|
+
|
9
|
+
- added accept! method to raise error if "required's" are missing
|
10
|
+
|
3
11
|
## [0.3.1] - 2024-06-22
|
4
12
|
|
5
13
|
- added container to exclude the "head" param
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -84,6 +84,8 @@ end
|
|
84
84
|
|
85
85
|
Experimental params sanitizer is now available. This will cast your params, and remove elements you want to exclude if `nil` or `empty`
|
86
86
|
|
87
|
+
Why is this needed? Params arrive into the controller in a messy state. Booleans are not ready for caparison, integers are often strings, empty strings, and nils abound. Rails does the casting of params at the model, which is simple and elegant, but in many cases, these params are used for a multitude of things before hitting the database. I truly believe we need to cast them before they do anything.
|
88
|
+
|
87
89
|
```
|
88
90
|
# app/controllers/my_controller.rb
|
89
91
|
class MyController
|
@@ -149,7 +151,46 @@ end
|
|
149
151
|
|
150
152
|
```
|
151
153
|
|
152
|
-
|
154
|
+
If you want to raise an error, rather than just return the errors in an array, use the `accept!` method. Will raise `Attribeauty::MissingAttributeError` with the required elements:
|
155
|
+
|
156
|
+
|
157
|
+
```
|
158
|
+
class MyController
|
159
|
+
def update
|
160
|
+
MyRecord.update(update_params)
|
161
|
+
|
162
|
+
redirect_to index_path
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
# your params look like this:
|
168
|
+
# { user: { profile: [{ address: { street_name: "Main St" } }] } }
|
169
|
+
#
|
170
|
+
def params_filter
|
171
|
+
Attribeauty::Params.with(request.params)
|
172
|
+
end
|
173
|
+
|
174
|
+
# This following with the accept! method
|
175
|
+
# will raise: Attribeauty::MissingAttributeError, "title required, email required"
|
176
|
+
#
|
177
|
+
def update_params
|
178
|
+
params_filter.accept! do
|
179
|
+
container :user do
|
180
|
+
attribute :title, :string, allow_nil: false, required: true
|
181
|
+
attribute :email do
|
182
|
+
attribute :address, :string, allow_empty: false
|
183
|
+
attribute :valid, :boolean, allow_nil: false
|
184
|
+
attribute :ip_address, :string, allow_blank: true
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
```
|
192
|
+
|
193
|
+
See `test/test_params.rb` for more examples
|
153
194
|
|
154
195
|
|
155
196
|
## Development
|
data/lib/attribeauty/params.rb
CHANGED
@@ -10,7 +10,7 @@ module Attribeauty
|
|
10
10
|
new(request_params)
|
11
11
|
end
|
12
12
|
|
13
|
-
attr_reader :allow_nil, :prefix, :request_params, :acceptables, :to_h, :errors
|
13
|
+
attr_reader :allow_nil, :prefix, :request_params, :acceptables, :to_h, :errors, :strict
|
14
14
|
|
15
15
|
def initialize(request_params)
|
16
16
|
@request_params = request_params.transform_keys(&:to_sym)
|
@@ -21,9 +21,17 @@ module Attribeauty
|
|
21
21
|
def accept(&)
|
22
22
|
instance_eval(&)
|
23
23
|
|
24
|
+
raise MissingAttributeError, errors.join(", ") if errors.any? && strict?
|
25
|
+
|
24
26
|
self
|
25
27
|
end
|
26
28
|
|
29
|
+
def accept!(&)
|
30
|
+
@strict = true
|
31
|
+
|
32
|
+
accept(&)
|
33
|
+
end
|
34
|
+
|
27
35
|
def to_hash = to_h
|
28
36
|
|
29
37
|
def [](key)
|
@@ -36,31 +44,18 @@ module Attribeauty
|
|
36
44
|
yield
|
37
45
|
end
|
38
46
|
|
39
|
-
# rubocop:disable Naming::BlockForwarding
|
40
47
|
def attribute(name, type = nil, **args, &block)
|
41
48
|
value = request_params[name]
|
42
49
|
return if value.nil? && args[:required].nil?
|
43
50
|
|
44
51
|
if block_given?
|
45
|
-
@to_h[name] =
|
46
|
-
if value.is_a?(Array)
|
47
|
-
value.map do |val|
|
48
|
-
params = self.class.with(val).accept(&block)
|
49
|
-
@errors.push(*params.errors)
|
50
|
-
params
|
51
|
-
end.reject(&:empty?)
|
52
|
-
else
|
53
|
-
params = self.class.with(value).accept(&block)
|
54
|
-
@errors.push(*params.errors)
|
55
|
-
params
|
56
|
-
end
|
52
|
+
@to_h[name.to_sym] = vals_from_nested(value, &block)
|
57
53
|
else
|
58
|
-
validator = Validator.run(name,
|
59
|
-
@to_h[name.to_sym] = validator.value if validator.valid?
|
54
|
+
validator = Validator.run(name, value, type, **args)
|
60
55
|
@errors.push(*validator.errors)
|
56
|
+
@to_h[name.to_sym] = validator.value if validator.valid?
|
61
57
|
end
|
62
58
|
end
|
63
|
-
# rubocop:enable Naming::BlockForwarding
|
64
59
|
|
65
60
|
def inspect
|
66
61
|
to_h.inspect
|
@@ -69,5 +64,25 @@ module Attribeauty
|
|
69
64
|
def valid?
|
70
65
|
errors.empty?
|
71
66
|
end
|
67
|
+
|
68
|
+
def strict?
|
69
|
+
strict
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def vals_from_nested(value, &block)
|
75
|
+
if value.is_a?(Array)
|
76
|
+
value.map do |val|
|
77
|
+
params = self.class.with(val).accept(&block)
|
78
|
+
@errors.push(*params.errors)
|
79
|
+
params.to_h
|
80
|
+
end.reject(&:empty?)
|
81
|
+
else
|
82
|
+
params = self.class.with(value).accept(&block)
|
83
|
+
@errors.push(*params.errors)
|
84
|
+
params.to_h
|
85
|
+
end
|
86
|
+
end
|
72
87
|
end
|
73
88
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "forwardable"
|
4
|
-
|
5
3
|
module Attribeauty
|
6
4
|
class Validator
|
7
5
|
ALLOWS_HASH = {
|
@@ -9,55 +7,82 @@ module Attribeauty
|
|
9
7
|
allow_empty: :empty?
|
10
8
|
}.freeze
|
11
9
|
|
12
|
-
def self.run(name, type,
|
13
|
-
new(name, type,
|
10
|
+
def self.run(name, type, original_val, **args)
|
11
|
+
new(name, type, original_val, **args).run
|
14
12
|
end
|
15
13
|
|
16
|
-
attr_reader :
|
14
|
+
attr_reader :original_val, :errors, :name, :type, :required, :default, :allows, :value, :valid
|
17
15
|
|
18
|
-
def initialize(name,
|
16
|
+
def initialize(name, original_val, type = nil, **args)
|
19
17
|
@name = name
|
20
18
|
@type = type
|
21
|
-
@
|
22
|
-
@errors = []
|
19
|
+
@original_val = original_val
|
23
20
|
@default = args[:default]
|
24
|
-
@required = args[:required] if
|
25
|
-
allows = args.slice(*allows_array)
|
26
|
-
return if allows.empty?
|
21
|
+
@required = args[:required] if args[:required] == true
|
22
|
+
@allows = args.slice(*allows_array).delete_if { |_key, value| value == true }
|
27
23
|
|
28
|
-
|
29
|
-
|
30
|
-
@predicate = predicate_array
|
24
|
+
@valid = true
|
25
|
+
@errors = []
|
31
26
|
end
|
32
27
|
|
33
28
|
def run
|
34
|
-
|
35
|
-
|
29
|
+
if type.nil?
|
30
|
+
@value = original_val
|
31
|
+
else
|
32
|
+
set_default
|
33
|
+
cast_value
|
34
|
+
handle_missing_required
|
35
|
+
handle_predicates
|
36
|
+
end
|
36
37
|
|
37
38
|
self
|
38
39
|
end
|
39
40
|
|
40
41
|
def valid?
|
41
|
-
|
42
|
-
|
43
|
-
return false
|
44
|
-
end
|
45
|
-
return true if predicate.nil?
|
42
|
+
valid
|
43
|
+
end
|
46
44
|
|
47
|
-
|
48
|
-
return true if bool
|
45
|
+
private
|
49
46
|
|
50
|
-
|
47
|
+
def set_default
|
48
|
+
return unless original_val.nil? && !default.nil?
|
49
|
+
|
50
|
+
@original_val = default
|
51
51
|
end
|
52
52
|
|
53
|
-
|
53
|
+
def cast_value
|
54
|
+
@value = TypeCaster.run(original_val, type)
|
55
|
+
end
|
56
|
+
|
57
|
+
# only returning errors if required is missing, not if nil?, or :empty?
|
58
|
+
def handle_missing_required
|
59
|
+
return unless required? && original_val.nil?
|
60
|
+
|
61
|
+
errors << "#{name} required"
|
62
|
+
@valid = false
|
63
|
+
end
|
64
|
+
|
65
|
+
def handle_predicates
|
66
|
+
return if predicate.nil? || !valid?
|
54
67
|
|
55
|
-
|
68
|
+
@valid = !value.public_send(predicate)
|
69
|
+
end
|
56
70
|
|
57
71
|
def allows_array
|
58
72
|
ALLOWS_HASH.keys
|
59
73
|
end
|
60
74
|
|
75
|
+
# convert allow_nil -> :nil? or allow_empty -> :empty?
|
76
|
+
# this will be used to public_send
|
77
|
+
# NOTE: only one will be checked, if you pass both:
|
78
|
+
# allow_nil and allow_empty, one will be ignored
|
79
|
+
def predicate
|
80
|
+
return if allows.empty?
|
81
|
+
|
82
|
+
key = allows.keys.first
|
83
|
+
ALLOWS_HASH[key]
|
84
|
+
end
|
85
|
+
|
61
86
|
def required?
|
62
87
|
required
|
63
88
|
end
|
data/lib/attribeauty/version.rb
CHANGED
data/lib/attribeauty.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attribeauty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Toby
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-06-
|
11
|
+
date: 2024-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: There are so many of these, I just needed this one.
|
14
14
|
email:
|