u-attributes 2.4.0 → 2.5.0

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: 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