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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0635b380b4cb80f46aa693faef308dd93acf646950c8a592efe9ee56f3f019d1
4
- data.tar.gz: f47043d7784f3068f3f82575bc71a5cdf7006cc73e8a8b4f481381a55091cb84
3
+ metadata.gz: 812191110b6c7f397737c3a9b1cceab66819c07d549c02c31687b8260342f404
4
+ data.tar.gz: 2ce7d61531ba7dcb7a896994ef94a2fa970bd821eb62d0901cc2b1df5e70947b
5
5
  SHA512:
6
- metadata.gz: 90040026bba413f4390d1c8141140dc90d7547b47e1af3ec737d23a6ec79c7a9ca9677086318b4d44107acdaf39b8587711e41231074ad80854c619eb3a02883
7
- data.tar.gz: 747703281202de16748413ecee1703ad15a6353a31caa289bf77b68e41d6ca820cf2d0bce5087af54ab7c39a237b398492701fa964a02af707dc00c4131fd487
6
+ metadata.gz: dbe0d664e43e995eb4caf66f66fe2a242d2f40b9effda38f511775451c666c82e1596a9116197e5cc84848f0f63ab11f2bb0f537f2214e0d38c6f9b3ddba2e1f
7
+ data.tar.gz: 66fb433c37a8986c4f5cf3bec0dae55246360fb6f65d82e2e25d1f507b3f4526d5476c99fa7f3e5244d6d255cdb9f429c51e7a7c7c84504e61fe2083e864bf0b
data/.rubocop.yml CHANGED
@@ -29,3 +29,6 @@ Metrics/PerceivedComplexity:
29
29
 
30
30
  Metrics/CyclomaticComplexity:
31
31
  Enabled: false
32
+
33
+ Naming::BlockForwarding:
34
+ Enabled: false
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- attribeauty (0.3.1)
4
+ attribeauty (0.3.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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
- See `test/test_params.rb` for what is expected.
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
@@ -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, type, value, **args)
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
@@ -16,6 +16,8 @@ module Attribeauty
16
16
  ].to_set.freeze
17
17
 
18
18
  def cast(value)
19
+ return false if value.nil?
20
+
19
21
  !FALSE_VALUES.include?(value)
20
22
  end
21
23
  end
@@ -5,6 +5,8 @@ module Attribeauty
5
5
  # custom float type
6
6
  class Float
7
7
  def cast(value)
8
+ return if value.nil?
9
+
8
10
  Float(value)
9
11
  end
10
12
  end
@@ -5,6 +5,8 @@ module Attribeauty
5
5
  # custom integer type
6
6
  class Integer
7
7
  def cast(value)
8
+ return if value.nil?
9
+
8
10
  Integer(value)
9
11
  end
10
12
  end
@@ -5,6 +5,8 @@ module Attribeauty
5
5
  # custom string type
6
6
  class String
7
7
  def cast(value)
8
+ return if value.nil?
9
+
8
10
  String(value)
9
11
  end
10
12
  end
@@ -5,6 +5,8 @@ module Attribeauty
5
5
  # custom Time type
6
6
  class Time
7
7
  def cast(value)
8
+ return if value.nil?
9
+
8
10
  case value
9
11
  when Time
10
12
  value
@@ -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, original_value, **args)
13
- new(name, type, original_value, **args).run
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 :original_value, :errors, :name, :type, :required, :default, :predicate, :value
14
+ attr_reader :original_val, :errors, :name, :type, :required, :default, :allows, :value, :valid
17
15
 
18
- def initialize(name, type, original_value, **args)
16
+ def initialize(name, original_val, type = nil, **args)
19
17
  @name = name
20
18
  @type = type
21
- @original_value = original_value
22
- @errors = []
19
+ @original_val = original_val
23
20
  @default = args[:default]
24
- @required = args[:required] if [true, false].include?(args[:required])
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
- predicate_array = allows.first
29
- predicate_array[0] = :"#{ALLOWS_HASH[predicate_array[0]]}"
30
- @predicate = predicate_array
24
+ @valid = true
25
+ @errors = []
31
26
  end
32
27
 
33
28
  def run
34
- @original_value = default if original_value.nil? && !default.nil?
35
- @value = TypeCaster.run(original_value, type)
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
- if required? && original_value.nil?
42
- errors << "#{name} required"
43
- return false
44
- end
45
- return true if predicate.nil?
42
+ valid
43
+ end
46
44
 
47
- method, bool = predicate
48
- return true if bool
45
+ private
49
46
 
50
- !value.public_send(method)
47
+ def set_default
48
+ return unless original_val.nil? && !default.nil?
49
+
50
+ @original_val = default
51
51
  end
52
52
 
53
- private
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
- def set_args; end
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Attribeauty
4
- VERSION = "0.3.1"
4
+ VERSION = "0.3.3"
5
5
  end
data/lib/attribeauty.rb CHANGED
@@ -7,6 +7,7 @@ require "forwardable"
7
7
  # Module
8
8
  module Attribeauty
9
9
  class Error < StandardError; end
10
+ class MissingAttributeError < StandardError; end
10
11
 
11
12
  class << self
12
13
  def configuration
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.1
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-22 00:00:00.000000000 Z
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: