u-attributes 2.4.0 → 2.5.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: 62c6ad9683f0824be6a32648028f25a97ba88a10c7fbdd0b9a2ccfbfe275961b
4
- data.tar.gz: d110a1b4220af76a7f14753cb1ca5067428f06db026466e095d54a22ee46f86f
3
+ metadata.gz: 01dbce30afd7d116e15ea5985e5012d86f2bd110b017394151086df322e9b7ed
4
+ data.tar.gz: 4004e27e835cc216807b2fb2781a15911c16f2ba20e509b7c36188a8b6021f95
5
5
  SHA512:
6
- metadata.gz: 24703dcd81aa785fe00b77a741434ad84600243f4bb132d2bbbce2b5a310d4f2b931c2b6ea41e311a2bcf07876296bd03d8e6c39a92905bc9a4f745fdb30a094
7
- data.tar.gz: 8172811ddaf44a3099e66f63cea5aed99d5c1a63a77944dc1e98c16fa0378603498fbdfe0c77f7220244c8d7b977801a75bd01facc875fb05f0bd1aed0171cc5
6
+ metadata.gz: ca177ca900f63a07901366612f5ef545512db4e8f9b48bea6482403af6346324784910fa9dca963e681449a1d3b583b173317890b76e8a2e03fd46b58af32006
7
+ data.tar.gz: 44372485364d637906d1dad476fc58a98cf228c33e252012af320e7e1da36b82db3943eb635ed34dff211908088f6fc8fe3c2de8a1b9a418f62f8e8b3e1596ed
data/README.md CHANGED
@@ -85,7 +85,7 @@ gem 'u-attributes'
85
85
 
86
86
  | u-attributes | branch | ruby | activemodel |
87
87
  | -------------- | ------- | -------- | ------------- |
88
- | 2.4.0 | main | >= 2.2.0 | >= 3.2, < 6.1 |
88
+ | 2.5.0 | main | >= 2.2.0 | >= 3.2, < 6.1 |
89
89
  | 1.2.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
90
90
 
91
91
  > **Note**: The activemodel is an optional dependency, this module [can be enabled](#activemodelvalidation-extension) to validate the attributes.
@@ -618,7 +618,7 @@ The method `Micro::Attributes.with()` will raise an exception if no arguments/fe
618
618
 
619
619
  ```ruby
620
620
  class Job
621
- include Micro::Attributes.with() # ArgumentError (Invalid feature name! Available options: :activemodel_validations, :diff, :initialize, :keys_as_symbol)
621
+ include Micro::Attributes.with() # ArgumentError (Invalid feature name! Available options: :accept, :activemodel_validations, :diff, :initialize, :keys_as_symbol)
622
622
  end
623
623
  ```
624
624
 
@@ -16,6 +16,7 @@ module Micro
16
16
  base.class_eval do
17
17
  private_class_method :__attributes, :__attribute_reader
18
18
  private_class_method :__attribute_assign, :__attributes_data_to_assign
19
+ private_class_method :__attributes_required_add, :__attributes_data_to_assign
19
20
  end
20
21
 
21
22
  def base.inherited(subclass)
@@ -86,29 +87,36 @@ module Micro
86
87
  protected
87
88
 
88
89
  def attributes=(arg)
89
- hash = self.class.__attributes_keys__(arg)
90
+ hash = self.class.__attributes_keys_transform__(arg)
90
91
 
91
92
  __attributes_missing!(hash)
92
93
 
94
+ __call_before_attributes_assign
93
95
  __attributes_assign(hash)
96
+ __call_after_attributes_assign
97
+
98
+ __attributes
94
99
  end
95
100
 
96
101
  private
97
102
 
103
+ def __call_before_attributes_assign; end
104
+ def __call_after_attributes_assign; end
105
+
98
106
  def extract_attributes_from(other)
99
107
  Utils::ExtractAttribute.from(other, keys: defined_attributes)
100
108
  end
101
109
 
102
110
  def __attribute_key(value)
103
- self.class.__attribute_key__(value)
111
+ self.class.__attribute_key_transform__(value)
104
112
  end
105
113
 
106
114
  def __attributes
107
115
  @__attributes ||= {}
108
116
  end
109
117
 
110
- FetchValueToAssign = -> (value, default) do
111
- if default.is_a?(Proc)
118
+ FetchValueToAssign = -> (value, default, keep_proc = false) do
119
+ if default.is_a?(Proc) && !keep_proc
112
120
  default.arity > 0 ? default.call(value) : default.call
113
121
  else
114
122
  value.nil? ? default : value
@@ -116,15 +124,17 @@ module Micro
116
124
  end
117
125
 
118
126
  def __attributes_assign(hash)
119
- self.class.__attributes_data__.each do |name, default|
120
- __attribute_assign(name, FetchValueToAssign.(hash[name], default)) if attribute?(name)
127
+ self.class.__attributes_data__.each do |name, attribute_data|
128
+ __attribute_assign(name, hash[name], attribute_data) if attribute?(name)
121
129
  end
122
130
 
123
131
  __attributes.freeze
124
132
  end
125
133
 
126
- def __attribute_assign(name, value)
127
- __attributes[name] = instance_variable_set("@#{name}", value)
134
+ def __attribute_assign(name, initialize_value, attribute_data)
135
+ value_to_assign = FetchValueToAssign.(initialize_value, attribute_data[0])
136
+
137
+ __attributes[name] = instance_variable_set("@#{name}", value_to_assign)
128
138
  end
129
139
 
130
140
  MISSING_KEYWORD = 'missing keyword'.freeze
@@ -35,17 +35,17 @@ module Micro::Attributes
35
35
 
36
36
  raise ArgumentError, FROM_TO_ERROR
37
37
  elsif from.nil? && to.nil?
38
- differences.has_key?(key_access(name))
38
+ differences.has_key?(key_transform(name))
39
39
  else
40
- result = @differences[key_access(name)]
40
+ result = @differences[key_transform(name)]
41
41
  result ? result[@from_key] == from && result[@to_key] == to : false
42
42
  end
43
43
  end
44
44
 
45
45
  private
46
46
 
47
- def key_access(key)
48
- @from_class.__attribute_key__(key)
47
+ def key_transform(key)
48
+ @from_class.__attribute_key_transform__(key)
49
49
  end
50
50
 
51
51
  def diff(from_attributes, to_attributes)
@@ -1,15 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'micro/attributes/with'
4
-
5
3
  module Micro
6
4
  module Attributes
5
+ module With
6
+ end
7
+
7
8
  module Features
9
+ require 'micro/attributes/features/diff'
10
+ require 'micro/attributes/features/accept'
11
+ require 'micro/attributes/features/accept/strict'
12
+ require 'micro/attributes/features/initialize'
13
+ require 'micro/attributes/features/initialize/strict'
14
+ require 'micro/attributes/features/keys_as_symbol'
15
+ require 'micro/attributes/features/activemodel_validations'
16
+
8
17
  extend self
9
18
 
10
19
  module Name
11
20
  ALL = [
12
21
  DIFF = 'diff'.freeze,
22
+ ACCEPT = 'accept'.freeze,
13
23
  INITIALIZE = 'initialize'.freeze,
14
24
  KEYS_AS_SYMBOL = 'keys_as_symbol'.freeze,
15
25
  ACTIVEMODEL_VALIDATIONS = 'activemodel_validations'.freeze
@@ -19,36 +29,74 @@ module Micro
19
29
  module Options
20
30
  KEYS = [
21
31
  DIFF = 'Diff'.freeze,
22
- INIT = 'Init'.freeze,
23
- INIT_STRICT = 'InitStrict'.freeze,
32
+ INIT = 'Initialize'.freeze,
33
+ ACCEPT = 'Accept'.freeze,
34
+ INIT_STRICT = 'InitializeStrict'.freeze,
35
+ ACCEPT_STRICT = 'AcceptStrict'.freeze,
24
36
  KEYS_AS_SYMBOL = 'KeysAsSymbol'.freeze,
25
- AM_VALIDATIONS = 'AMValidations'.freeze
37
+ AM_VALIDATIONS = 'ActiveModelValidations'.freeze
26
38
  ].sort.freeze
27
39
 
40
+ KEYS_TO_FEATURES = {
41
+ DIFF => Features::Diff,
42
+ INIT => Features::Initialize,
43
+ ACCEPT => Features::Accept,
44
+ INIT_STRICT => Features::Initialize::Strict,
45
+ ACCEPT_STRICT => Features::Accept::Strict,
46
+ KEYS_AS_SYMBOL => Features::KeysAsSymbol,
47
+ AM_VALIDATIONS => Features::ActiveModelValidations
48
+ }.freeze
49
+
28
50
  NAMES_TO_KEYS = {
29
51
  Name::DIFF => DIFF,
52
+ Name::ACCEPT => ACCEPT,
30
53
  Name::INITIALIZE => INIT,
31
54
  Name::KEYS_AS_SYMBOL => KEYS_AS_SYMBOL,
32
55
  Name::ACTIVEMODEL_VALIDATIONS => AM_VALIDATIONS
33
56
  }.freeze
34
57
 
35
- KEYS_TO_MODULES = {
36
- DIFF => With::Diff,
37
- INIT => With::Initialize,
38
- INIT_STRICT => With::StrictInitialize,
39
- KEYS_AS_SYMBOL => With::KeysAsSymbol,
40
- AM_VALIDATIONS => With::ActiveModelValidations
41
- }.freeze
58
+ INIT_INIT_STRICT = "#{INIT}_#{INIT_STRICT}".freeze
59
+ ACCEPT_ACCEPT_STRICT = "#{ACCEPT}_#{ACCEPT_STRICT}".freeze
60
+
61
+ BuildKey = -> combination do
62
+ combination.sort.join('_')
63
+ .sub(INIT_INIT_STRICT, INIT_STRICT)
64
+ .sub(ACCEPT_ACCEPT_STRICT, ACCEPT_STRICT)
65
+ end
66
+
67
+ KEYS_TO_MODULES = begin
68
+ combinations = (1..KEYS.size).map { |n| KEYS.combination(n).to_a }.flatten(1).sort_by { |i| "#{i.size}#{i.join}" }
69
+ combinations.delete_if { |combination| combination.include?(INIT_STRICT) && !combination.include?(INIT) }
70
+ combinations.delete_if { |combination| combination.include?(ACCEPT_STRICT) && !combination.include?(ACCEPT) }
71
+ combinations.each_with_object({}) do |combination, features|
72
+ included = [
73
+ 'def self.included(base)',
74
+ ' base.send(:include, ::Micro::Attributes)',
75
+ combination.map { |key| " base.send(:include, ::#{KEYS_TO_FEATURES[key].name})" },
76
+ 'end'
77
+ ].flatten.join("\n")
78
+
79
+ key = BuildKey.call(combination)
80
+
81
+ With.const_set(key, Module.new.tap { |mod| mod.instance_eval(included) })
82
+
83
+ features[key] = With.const_get(key, false)
84
+ end.freeze
85
+ end
86
+
87
+ ACTIVEMODEL_VALIDATION = 'activemodel_validation'.freeze
42
88
 
43
89
  def self.fetch_key(arg)
44
90
  if arg.is_a?(Hash)
91
+ return ACCEPT_STRICT if arg[:accept] == :strict
92
+
45
93
  INIT_STRICT if arg[:initialize] == :strict
46
94
  else
47
- name = String(arg)
95
+ str = String(arg)
48
96
 
49
- return name if KEYS_TO_MODULES.key?(name)
97
+ name = str == ACTIVEMODEL_VALIDATION ? Name::ACTIVEMODEL_VALIDATIONS : str
50
98
 
51
- NAMES_TO_KEYS[name]
99
+ KEYS_TO_MODULES.key?(name) ? name : NAMES_TO_KEYS[name]
52
100
  end
53
101
  end
54
102
 
@@ -65,23 +113,26 @@ module Micro
65
113
  yield(keys)
66
114
  end
67
115
 
68
- def self.remove_init_keys(keys, if_has_init_in:)
69
- keys.delete_if { |key| key == INIT || key == INIT_STRICT } if if_has_init_in.include?(INIT)
116
+ def self.remove_base_if_has_strict(keys)
117
+ keys.delete_if { |key| key == INIT } if keys.include?(INIT_STRICT)
118
+ keys.delete_if { |key| key == ACCEPT } if keys.include?(ACCEPT_STRICT)
70
119
  end
71
120
 
72
121
  def self.without_keys(keys_to_exclude)
73
- (KEYS - keys_to_exclude).tap do |keys|
74
- remove_init_keys(keys, if_has_init_in: keys_to_exclude)
75
- end
122
+ keys = (KEYS - keys_to_exclude)
123
+ keys.delete_if { |key| key == INIT || key == INIT_STRICT } if keys_to_exclude.include?(INIT)
124
+ keys.delete_if { |key| key == ACCEPT || key == ACCEPT_STRICT } if keys_to_exclude.include?(ACCEPT)
125
+ keys
76
126
  end
77
127
 
78
- def self.fetch_module_by_keys(keys)
79
- keys.delete_if { |key| key == INIT } if keys.include?(INIT_STRICT)
80
-
81
- option = keys.sort.join('_')
128
+ def self.fetch_module_by_keys(combination)
129
+ key = BuildKey.call(combination)
82
130
 
83
- KEYS_TO_MODULES.fetch(option) { With.const_get(option, false) }
131
+ KEYS_TO_MODULES.fetch(key)
84
132
  end
133
+
134
+ private_constant :KEYS_TO_FEATURES, :NAMES_TO_KEYS, :INVALID_NAME
135
+ private_constant :INIT_INIT_STRICT, :ACCEPT_ACCEPT_STRICT, :BuildKey
85
136
  end
86
137
 
87
138
  def all
@@ -90,17 +141,24 @@ module Micro
90
141
 
91
142
  def with(names)
92
143
  Options.fetch_keys(names) do |keys|
144
+ Options.remove_base_if_has_strict(keys)
145
+
93
146
  Options.fetch_module_by_keys(keys)
94
147
  end
95
148
  end
96
149
 
97
150
  def without(names)
98
151
  Options.fetch_keys(names) do |keys|
99
- keys = Options.without_keys(keys)
152
+ keys_to_fetch = Options.without_keys(keys)
100
153
 
101
- keys.empty? ? ::Micro::Attributes : Options.fetch_module_by_keys(keys)
154
+ return ::Micro::Attributes if keys_to_fetch.empty?
155
+
156
+ Options.fetch_module_by_keys(keys_to_fetch)
102
157
  end
103
158
  end
159
+
160
+ private_constant :Name
104
161
  end
162
+
105
163
  end
106
164
  end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro::Attributes
4
+ module Features
5
+ module Accept
6
+ def attributes_errors
7
+ @__attributes_errors
8
+ end
9
+
10
+ def attributes_errors?
11
+ !@__attributes_errors.empty?
12
+ end
13
+
14
+ def rejected_attributes
15
+ @__rejected_attributes ||= attributes_errors.keys
16
+ end
17
+
18
+ def accepted_attributes
19
+ @__accepted_attributes ||= defined_attributes - rejected_attributes
20
+ end
21
+
22
+ def rejected_attributes?
23
+ attributes_errors?
24
+ end
25
+
26
+ def accepted_attributes?
27
+ !rejected_attributes?
28
+ end
29
+
30
+ private
31
+
32
+ def __call_before_attributes_assign
33
+ @__attributes_errors = {}
34
+ end
35
+
36
+ KeepProc = -> validation_data { validation_data[0] == :accept && validation_data[1] == Proc }
37
+
38
+ def __attribute_assign(key, initialize_value, attribute_data)
39
+ validation_data = attribute_data[1]
40
+
41
+ value_to_assign = FetchValueToAssign.(initialize_value, attribute_data[0], KeepProc.(validation_data))
42
+
43
+ value = __attributes[key] = instance_variable_set("@#{key}", value_to_assign)
44
+
45
+ __attribute_accept_or_reject(key, value, validation_data) if !validation_data.empty?
46
+ end
47
+
48
+ def __attribute_accept_or_reject(key, value, validation_data)
49
+ context = Context.with(key, value, validation_data)
50
+
51
+ error_msg = context.rejection_message(Validate.call(context))
52
+
53
+ @__attributes_errors[key] = error_msg if error_msg
54
+ end
55
+
56
+ Context = Struct.new(:key, :value, :validation, :expected, :allow_nil, :rejection) do
57
+ def self.with(key, value, data)
58
+ new(key, value, data[0], data[1], data[2], data[3])
59
+ end
60
+
61
+ def allow_nil?
62
+ allow_nil && value.nil?
63
+ end
64
+
65
+ def accept?
66
+ validation == :accept
67
+ end
68
+
69
+ def rejection_message(default_msg)
70
+ return unless default_msg
71
+
72
+ return default_msg unless rejection || expected.respond_to?(:rejection_message)
73
+
74
+ rejection_msg = rejection || expected.rejection_message
75
+
76
+ return rejection_msg unless rejection_msg.is_a?(Proc)
77
+
78
+ rejection_msg.arity == 0 ? rejection_msg.call : rejection_msg.call(key)
79
+ end
80
+ end
81
+
82
+ module Validate
83
+ module Callable
84
+ MESSAGE = 'is invalid'.freeze
85
+
86
+ def self.call?(exp); exp.respond_to?(:call); end
87
+ def self.call(exp, val); exp.call(val); end
88
+ def self.accept_failed(_exp); MESSAGE; end
89
+ def self.reject_failed(_exp); MESSAGE; end
90
+ end
91
+
92
+ module KindOf
93
+ def self.call?(exp); exp.is_a?(Class) || exp.is_a?(Module); end
94
+ def self.call(exp, val); val.kind_of?(exp); end
95
+ def self.accept_failed(exp); "expected to be a kind of #{exp}"; end
96
+ def self.reject_failed(exp); "expected to not be a kind of #{exp}"; end
97
+ end
98
+
99
+ module Predicate
100
+ QUESTION_MARK = '?'.freeze
101
+
102
+ def self.call?(exp); exp.is_a?(Symbol) && exp.to_s.end_with?(QUESTION_MARK); end
103
+ def self.call(exp, val); val.public_send(exp); end
104
+ def self.accept_failed(exp); "expected to be #{exp}"; end
105
+ def self.reject_failed(exp); "expected to not be #{exp}"; end
106
+ end
107
+
108
+ def self.with(expected)
109
+ return Callable if Callable.call?(expected)
110
+ return KindOf if KindOf.call?(expected)
111
+ return Predicate if Predicate.call?(expected)
112
+ end
113
+
114
+ def self.call(context)
115
+ return if context.allow_nil?
116
+
117
+ validate = self.with(expected = context.expected)
118
+
119
+ return unless validate
120
+
121
+ truthy = validate.call(expected, context.value)
122
+
123
+ return truthy ? nil : validate.accept_failed(expected) if context.accept?
124
+
125
+ validate.reject_failed(expected) if truthy
126
+ end
127
+ end
128
+
129
+ private_constant :KeepProc, :Context, :Validate
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro::Attributes
4
+ module Features
5
+ module Accept
6
+
7
+ module Strict
8
+ ATTRIBUTES_REJECTED = "One or more attributes were rejected. Errors:\n".freeze
9
+
10
+ def __call_after_attributes_assign
11
+ return unless attributes_errors?
12
+
13
+ __raise_error_if_found_attributes_errors
14
+ end
15
+
16
+ def __raise_error_if_found_attributes_errors
17
+ raise ArgumentError, [
18
+ ATTRIBUTES_REJECTED,
19
+ attributes_errors.map { |key, msg| "* #{key.inspect} #{msg}" }.join("\n")
20
+ ].join
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -3,13 +3,45 @@
3
3
  module Micro::Attributes
4
4
  module Features
5
5
  module ActiveModelValidations
6
- def self.included(base)
7
- begin
8
- require 'active_model'
6
+ module Standard
7
+ private def __call_after_attributes_assign
8
+ run_validations!
9
+ end
10
+ end
9
11
 
10
- base.send(:include, ::ActiveModel::Validations)
11
- base.extend(ClassMethods)
12
- rescue LoadError
12
+ module CheckActivemodelValidationErrors
13
+ private def __check_activemodel_validation_errors
14
+ return if errors.blank?
15
+
16
+ errors_hash = errors.to_hash
17
+
18
+ defined_attributes.each do |key|
19
+ value = Utils::Hashes.assoc(errors_hash, key)
20
+
21
+ @__attributes_errors[key] = value.join(', ') if value.present?
22
+ end
23
+ end
24
+ end
25
+
26
+ module WithAccept
27
+ include CheckActivemodelValidationErrors
28
+
29
+ private def __call_after_attributes_assign
30
+ run_validations! unless attributes_errors?
31
+
32
+ __check_activemodel_validation_errors
33
+ end
34
+ end
35
+
36
+ module WithAcceptStrict
37
+ include CheckActivemodelValidationErrors
38
+
39
+ private def __call_after_attributes_assign
40
+ __raise_error_if_found_attributes_errors if attributes_errors?
41
+
42
+ run_validations!
43
+
44
+ __check_activemodel_validation_errors
13
45
  end
14
46
  end
15
47
 
@@ -22,11 +54,23 @@ module Micro::Attributes
22
54
  end
23
55
  end
24
56
 
25
- private
57
+ def self.included(base)
58
+ begin
59
+ require 'active_model'
60
+
61
+ base.send(:include, ::ActiveModel::Validations)
62
+ base.extend(ClassMethods)
26
63
 
27
- def __call_after_micro_attribute
28
- run_validations! if respond_to?(:run_validations!, true)
64
+ case
65
+ when base <= Features::Accept::Strict then base.send(:include, WithAcceptStrict)
66
+ when base <= Features::Accept then base.send(:include, WithAccept)
67
+ else base.send(:include, Standard)
68
+ end
69
+ rescue LoadError
29
70
  end
71
+ end
72
+
73
+ private_constant :Standard, :CheckActivemodelValidationErrors, :WithAccept, :WithAcceptStrict
30
74
  end
31
75
  end
32
76
  end
@@ -7,22 +7,17 @@ module Micro::Attributes
7
7
  base.class_eval(<<-RUBY)
8
8
  def initialize(arg)
9
9
  self.attributes = arg
10
- __call_after_micro_attribute
11
10
  end
12
11
  RUBY
13
12
  end
14
13
 
15
- def with_attribute(key, val)
16
- self.class.new(attributes.merge(key => val))
17
- end
18
-
19
14
  def with_attributes(arg)
20
15
  self.class.new(attributes.merge(arg))
21
16
  end
22
17
 
23
- private
24
-
25
- def __call_after_micro_attribute; end
18
+ def with_attribute(key, val)
19
+ with_attributes(key => val)
20
+ end
26
21
  end
27
22
  end
28
23
  end
@@ -9,15 +9,15 @@ module Micro::Attributes
9
9
  :symbol
10
10
  end
11
11
 
12
- def __attribute_access__(value)
12
+ def __attribute_key_check__(value)
13
13
  Kind::Of.(::Symbol, value)
14
14
  end
15
15
 
16
- def __attribute_key__(value)
16
+ def __attribute_key_transform__(value)
17
17
  value
18
18
  end
19
19
 
20
- def __attributes_keys__(hash)
20
+ def __attributes_keys_transform__(hash)
21
21
  Utils::Hashes.kind(hash)
22
22
  end
23
23
  end
@@ -11,15 +11,15 @@ module Micro
11
11
  :indifferent
12
12
  end
13
13
 
14
- def __attribute_access__(value)
14
+ def __attribute_key_check__(value)
15
15
  value
16
16
  end
17
17
 
18
- def __attribute_key__(value)
18
+ def __attribute_key_transform__(value)
19
19
  value.to_s
20
20
  end
21
21
 
22
- def __attributes_keys__(hash)
22
+ def __attributes_keys_transform__(hash)
23
23
  Utils::Hashes.stringify_keys(hash)
24
24
  end
25
25
 
@@ -32,18 +32,54 @@ module Micro
32
32
  @__attributes_required__ ||= Set.new
33
33
  end
34
34
 
35
- def __attributes_required_add(name, is_required, hasnt_default)
36
- if is_required || (attributes_are_all_required? && hasnt_default)
35
+ def __attributes_required_add(name, opt, hasnt_default)
36
+ if opt[:required] || (attributes_are_all_required? && hasnt_default)
37
37
  __attributes_required__.add(name)
38
38
  end
39
39
 
40
40
  nil
41
41
  end
42
42
 
43
- def __attributes_data_to_assign(name, options)
44
- hasnt_default = !options.key?(:default)
43
+ module Options
44
+ PERMITTED = [
45
+ :default, :required,
46
+ :validate, :validates, # activemodel_validations
47
+ :accept, :reject, :allow_nil, :rejection_message # accept
48
+ ].freeze
45
49
 
46
- hasnt_default ? __attributes_required_add(name, options[:required], hasnt_default) : options[:default]
50
+ INVALID_MESSAGE = [
51
+ "Found one or more invalid options: %{invalid_options}\n\nThe valid ones are: ",
52
+ PERMITTED.map { |key| ":#{key}" }.join(', ')
53
+ ].join.freeze
54
+
55
+ def self.check(opt)
56
+ invalid_keys = opt.keys - PERMITTED
57
+
58
+ return if invalid_keys.empty?
59
+
60
+ invalid_options = { invalid_options: invalid_keys.inspect.tr('[', '').tr(']', '') }
61
+
62
+ raise ArgumentError, (INVALID_MESSAGE % invalid_options)
63
+ end
64
+
65
+ def self.for_accept(opt)
66
+ allow_nil = opt[:allow_nil]
67
+ rejection_message = opt[:rejection_message]
68
+
69
+ return [:accept, opt[:accept], allow_nil, rejection_message] if opt.key?(:accept)
70
+ return [:reject, opt[:reject], allow_nil, rejection_message] if opt.key?(:reject)
71
+
72
+ Kind::Empty::ARRAY
73
+ end
74
+ end
75
+
76
+ def __attributes_data_to_assign(name, opt)
77
+ hasnt_default = !opt.key?(:default)
78
+
79
+ [
80
+ hasnt_default ? __attributes_required_add(name, opt, hasnt_default) : opt[:default],
81
+ Options.for_accept(opt)
82
+ ]
47
83
  end
48
84
 
49
85
  def __attributes
@@ -56,15 +92,18 @@ module Micro
56
92
  attr_reader(name)
57
93
  end
58
94
 
59
- def __attribute_assign(key, can_overwrite, options)
60
- name = __attribute_access__(__attribute_key__(key))
95
+ def __attribute_assign(key, can_overwrite, opt)
96
+ name = __attribute_key_check__(__attribute_key_transform__(key))
97
+
98
+ Options.check(opt)
99
+
61
100
  has_attribute = attribute?(name)
62
101
 
63
102
  __attribute_reader(name) unless has_attribute
64
103
 
65
- __attributes_data__[name] = __attributes_data_to_assign(name, options) if can_overwrite || !has_attribute
104
+ __attributes_data__[name] = __attributes_data_to_assign(name, opt) if can_overwrite || !has_attribute
66
105
 
67
- __call_after_attribute_assign__(name, options)
106
+ __call_after_attribute_assign__(name, opt)
68
107
  end
69
108
 
70
109
  def __call_after_attribute_assign__(attr_name, options); end
@@ -72,12 +111,20 @@ module Micro
72
111
  # NOTE: can't be renamed! It is used by u-case v4.
73
112
  def __attributes_set_after_inherit__(arg)
74
113
  arg.each do |key, val|
75
- __attribute_assign(key, true, val ? { default: val } : {})
114
+ opt = if default = val[0]
115
+ requ_key, requ_val = val[1]
116
+
117
+ hash = requ_key ? { requ_key => requ_val } : {}
118
+ hash[:default] = default
119
+ hash
120
+ end
121
+
122
+ __attribute_assign(key, true, opt || Kind::Empty::HASH)
76
123
  end
77
124
  end
78
125
 
79
126
  def attribute?(name)
80
- __attributes.member?(__attribute_key__(name))
127
+ __attributes.member?(__attribute_key_transform__(name))
81
128
  end
82
129
 
83
130
  def attribute(name, options = Kind::Empty::HASH)
@@ -112,7 +159,7 @@ module Micro
112
159
  private_constant :WRONG_NUMBER_OF_ARGS
113
160
  end
114
161
 
115
- private_constant :ForSubclasses
162
+ private_constant :Options, :ForSubclasses
116
163
  end
117
164
 
118
165
  private_constant :Macros
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  module Attributes
5
- VERSION = '2.4.0'.freeze
5
+ VERSION = '2.5.0'.freeze
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: u-attributes
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.0
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-12 00:00:00.000000000 Z
11
+ date: 2020-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kind
@@ -81,6 +81,8 @@ files:
81
81
  - lib/micro/attributes.rb
82
82
  - lib/micro/attributes/diff.rb
83
83
  - lib/micro/attributes/features.rb
84
+ - lib/micro/attributes/features/accept.rb
85
+ - lib/micro/attributes/features/accept/strict.rb
84
86
  - lib/micro/attributes/features/activemodel_validations.rb
85
87
  - lib/micro/attributes/features/diff.rb
86
88
  - lib/micro/attributes/features/initialize.rb
@@ -89,7 +91,6 @@ files:
89
91
  - lib/micro/attributes/macros.rb
90
92
  - lib/micro/attributes/utils.rb
91
93
  - lib/micro/attributes/version.rb
92
- - lib/micro/attributes/with.rb
93
94
  - lib/u-attributes.rb
94
95
  - test.sh
95
96
  - u-attributes.gemspec
@@ -1,182 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'micro/attributes/features/diff'
4
- require 'micro/attributes/features/initialize'
5
- require 'micro/attributes/features/initialize/strict'
6
- require 'micro/attributes/features/keys_as_symbol'
7
- require 'micro/attributes/features/activemodel_validations'
8
-
9
-
10
- module Micro
11
- module Attributes
12
- module With
13
- #
14
- # Features
15
- #
16
- module Diff
17
- def self.included(base)
18
- base.send(:include, ::Micro::Attributes)
19
- base.send(:include, ::Micro::Attributes::Features::Diff)
20
- end
21
- end
22
-
23
- module Initialize
24
- def self.included(base)
25
- base.send(:include, ::Micro::Attributes)
26
- base.send(:include, ::Micro::Attributes::Features::Initialize)
27
- end
28
- end
29
-
30
- module StrictInitialize
31
- def self.included(base)
32
- base.send(:include, Initialize)
33
- base.send(:include, ::Micro::Attributes::Features::Initialize::Strict)
34
- end
35
- end
36
-
37
- module KeysAsSymbol
38
- def self.included(base)
39
- base.send(:include, ::Micro::Attributes)
40
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
41
- end
42
- end
43
-
44
- module ActiveModelValidations
45
- def self.included(base)
46
- base.send(:include, Initialize)
47
- base.send(:include, ::Micro::Attributes::Features::ActiveModelValidations)
48
- end
49
- end
50
-
51
- #
52
- # Combinations
53
- #
54
- module AMValidations_Diff
55
- def self.included(base)
56
- base.send(:include, ActiveModelValidations)
57
- base.send(:include, ::Micro::Attributes::Features::Diff)
58
- end
59
- end
60
-
61
- module AMValidations_Diff_Init
62
- def self.included(base)
63
- base.send(:include, ActiveModelValidations)
64
- base.send(:include, ::Micro::Attributes::Features::Diff)
65
- end
66
- end
67
-
68
- module AMValidations_Diff_Init_KeysAsSymbol
69
- def self.included(base)
70
- base.send(:include, ActiveModelValidations)
71
- base.send(:include, ::Micro::Attributes::Features::Diff)
72
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
73
- end
74
- end
75
-
76
- module AMValidations_Diff_InitStrict
77
- def self.included(base)
78
- base.send(:include, AMValidations_Diff_Init)
79
- base.send(:include, ::Micro::Attributes::Features::Initialize::Strict)
80
- end
81
- end
82
-
83
- module AMValidations_Diff_InitStrict_KeysAsSymbol
84
- def self.included(base)
85
- base.send(:include, AMValidations_Diff_Init)
86
- base.send(:include, ::Micro::Attributes::Features::Initialize::Strict)
87
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
88
- end
89
- end
90
-
91
- module AMValidations_Diff_KeysAsSymbol
92
- def self.included(base)
93
- base.send(:include, AMValidations_Diff)
94
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
95
- end
96
- end
97
-
98
- module AMValidations_Init
99
- def self.included(base)
100
- base.send(:include, ActiveModelValidations)
101
- end
102
- end
103
-
104
- module AMValidations_Init_KeysAsSymbol
105
- def self.included(base)
106
- base.send(:include, AMValidations_Init)
107
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
108
- end
109
- end
110
-
111
- module AMValidations_InitStrict
112
- def self.included(base)
113
- base.send(:include, ActiveModelValidations)
114
- base.send(:include, ::Micro::Attributes::Features::Initialize::Strict)
115
- end
116
- end
117
-
118
- module AMValidations_InitStrict_KeysAsSymbol
119
- def self.included(base)
120
- base.send(:include, AMValidations_InitStrict)
121
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
122
- end
123
- end
124
-
125
- module AMValidations_KeysAsSymbol
126
- def self.included(base)
127
- base.send(:include, ActiveModelValidations)
128
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
129
- end
130
- end
131
-
132
- module Diff_Init
133
- def self.included(base)
134
- base.send(:include, Initialize)
135
- base.send(:include, ::Micro::Attributes::Features::Diff)
136
- end
137
- end
138
-
139
- module Diff_Init_KeysAsSymbol
140
- def self.included(base)
141
- base.send(:include, Diff_Init)
142
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
143
- end
144
- end
145
-
146
- module Diff_InitStrict
147
- def self.included(base)
148
- base.send(:include, Diff_Init)
149
- base.send(:include, ::Micro::Attributes::Features::Initialize::Strict)
150
- end
151
- end
152
-
153
- module Diff_InitStrict_KeysAsSymbol
154
- def self.included(base)
155
- base.send(:include, Diff_InitStrict)
156
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
157
- end
158
- end
159
-
160
- module Diff_KeysAsSymbol
161
- def self.included(base)
162
- base.send(:include, Diff)
163
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
164
- end
165
- end
166
-
167
- module Init_KeysAsSymbol
168
- def self.included(base)
169
- base.send(:include, Initialize)
170
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
171
- end
172
- end
173
-
174
- module InitStrict_KeysAsSymbol
175
- def self.included(base)
176
- base.send(:include, StrictInitialize)
177
- base.send(:include, ::Micro::Attributes::Features::KeysAsSymbol)
178
- end
179
- end
180
- end
181
- end
182
- end