u-attributes 1.2.0 → 2.0.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.
data/Rakefile CHANGED
@@ -1,10 +1,10 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
3
 
4
4
  Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
8
  end
9
9
 
10
10
  task :default => :test
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "micro/attributes"
3
+ require 'bundler/setup'
4
+ require 'micro/attributes'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
8
8
 
9
9
  # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
10
+ # require 'pry'
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require 'irb'
14
14
  IRB.start(__FILE__)
@@ -1,35 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "micro/attributes/version"
4
- require "micro/attributes/attributes_utils"
5
- require "micro/attributes/macros"
6
- require "micro/attributes/features"
3
+ require 'kind'
7
4
 
8
5
  module Micro
9
6
  module Attributes
7
+ require 'micro/attributes/version'
8
+ require 'micro/attributes/utils'
9
+ require 'micro/attributes/diff'
10
+ require 'micro/attributes/macros'
11
+ require 'micro/attributes/features'
12
+
10
13
  def self.included(base)
11
14
  base.extend(::Micro::Attributes.const_get(:Macros))
12
15
 
13
16
  base.class_eval do
14
- private_class_method :__attributes_data, :__attributes
15
- private_class_method :__attributes_def, :__attributes_set
16
- private_class_method :__attribute_reader, :__attribute_set
17
+ private_class_method :__attributes, :__attribute_set, :__attribute_reader
17
18
  end
18
19
 
19
20
  def base.inherited(subclass)
20
- subclass.attributes(self.attributes_data({}))
21
+ subclass.__attributes_set_after_inherit__(self.__attributes_data__)
22
+
21
23
  subclass.extend ::Micro::Attributes.const_get('Macros::ForSubclasses'.freeze)
22
24
  end
23
25
  end
24
26
 
25
- def self.to_initialize(diff: false, activemodel_validations: false)
26
- features(*Features.options(:initialize, diff, activemodel_validations))
27
- end
28
-
29
- def self.to_initialize!(diff: false, activemodel_validations: false)
30
- features(*Features.options(:strict_initialize, diff, activemodel_validations))
31
- end
32
-
33
27
  def self.without(*names)
34
28
  Features.without(names)
35
29
  end
@@ -38,28 +32,26 @@ module Micro
38
32
  Features.with(names)
39
33
  end
40
34
 
41
- def self.feature(name)
42
- self.with(name)
35
+ def self.with_all_features
36
+ Features.all
43
37
  end
44
38
 
45
- def self.features(*names)
46
- names.empty? ? Features.all : Features.with(names)
39
+ def attribute?(name)
40
+ self.class.attribute?(name)
47
41
  end
48
42
 
49
- protected def attributes=(arg)
50
- self.class
51
- .attributes_data(AttributesUtils.hash_argument!(arg))
52
- .each { |name, value| __attribute_set(name, value) }
43
+ def attribute(name)
44
+ return unless attribute?(name)
53
45
 
54
- __attributes.freeze
55
- end
46
+ value = public_send(name)
56
47
 
57
- private def __attributes
58
- @__attributes ||= {}
48
+ block_given? ? yield(value) : value
59
49
  end
60
50
 
61
- private def __attribute_set(name, value)
62
- __attributes[name] = instance_variable_set("@#{name}", value) if attribute?(name)
51
+ def attribute!(name, &block)
52
+ attribute(name) { |name| return block ? block[name] : name }
53
+
54
+ raise NameError, "undefined attribute `#{name}"
63
55
  end
64
56
 
65
57
  def attributes(*names)
@@ -70,22 +62,40 @@ module Micro
70
62
  end
71
63
  end
72
64
 
73
- def attribute?(name)
74
- self.class.attribute?(name)
75
- end
65
+ protected
76
66
 
77
- def attribute(name)
78
- return unless attribute?(name)
67
+ def attributes=(arg)
68
+ hash = Utils.stringify_hash_keys(arg)
79
69
 
80
- value = public_send(name)
70
+ __attributes_set(hash, self.class.__attributes_data__)
71
+ end
81
72
 
82
- block_given? ? yield(value) : value
83
- end
73
+ private
84
74
 
85
- def attribute!(name, &block)
86
- attribute(name) { |name| return block ? block[name] : name }
75
+ def __attributes
76
+ @__attributes ||= {}
77
+ end
87
78
 
88
- raise NameError, "undefined attribute `#{name}"
89
- end
79
+ def __attribute_set(name, value)
80
+ __attributes[name] = instance_variable_set("@#{name}", value) if attribute?(name)
81
+ end
82
+
83
+ def __attributes_set(hash, att_data)
84
+ att_data.each do |key, default|
85
+ value = hash[key]
86
+
87
+ final_value =
88
+ if default.respond_to?(:call)
89
+ callable = default.is_a?(Proc) ? default : default.method(:call)
90
+ callable.arity > 0 ? callable.call(value) : callable.call
91
+ else
92
+ value || default
93
+ end
94
+
95
+ __attribute_set(key, final_value)
96
+ end
97
+
98
+ __attributes.freeze
99
+ end
90
100
  end
91
101
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro::Attributes
4
+ module Diff
5
+ class Changes
6
+ TO = 'to'.freeze
7
+ FROM = 'from'.freeze
8
+ FROM_TO_ERROR = 'pass the attribute name with the :from and :to values'.freeze
9
+
10
+ attr_reader :from, :to, :differences
11
+
12
+ def initialize(from:, to:)
13
+ raise ArgumentError, "expected an instance of #{from.class}" unless to.is_a?(from.class)
14
+ @from, @to = from, to
15
+ @differences = diff(from.attributes, to.attributes).freeze
16
+ end
17
+
18
+ def empty?
19
+ @differences.empty?
20
+ end
21
+ alias_method :blank?, :empty?
22
+
23
+ def present?
24
+ !empty?
25
+ end
26
+
27
+ def changed?(name = nil, from: nil, to: nil)
28
+ if name.nil?
29
+ return present? if from.nil? && to.nil?
30
+ raise ArgumentError, FROM_TO_ERROR
31
+ elsif from.nil? && to.nil?
32
+ differences.has_key?(name.to_s)
33
+ else
34
+ result = @differences[name.to_s]
35
+ result ? result[FROM] == from && result[TO] == to : false
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def diff(from_attributes, to_attributes)
42
+ @from_attributes, @to_attributes = from_attributes, to_attributes
43
+ @from_attributes.each_with_object({}) do |(from_key, from_val), acc|
44
+ to_value = @to_attributes[from_key]
45
+ acc[from_key] = {FROM => from_val, TO => to_value}.freeze if from_val != to_value
46
+ end
47
+ end
48
+
49
+ private_constant :TO, :FROM, :FROM_TO_ERROR
50
+ end
51
+ end
52
+ end
@@ -1,22 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "micro/attributes/with"
3
+ require 'micro/attributes/with'
4
4
 
5
5
  module Micro
6
6
  module Attributes
7
7
  module Features
8
8
  extend self
9
9
 
10
- ALL = [
10
+ STRICT_INITIALIZE = 'strict_initialize'.freeze
11
+
12
+ ALL_VISIBLE = [
11
13
  DIFF = 'diff'.freeze,
12
14
  INITIALIZE = 'initialize'.freeze,
13
- STRICT_INITIALIZE = 'strict_initialize'.freeze,
14
15
  ACTIVEMODEL_VALIDATIONS = 'activemodel_validations'.freeze
15
16
  ].sort.freeze
16
17
 
18
+ ALL = (ALL_VISIBLE + [STRICT_INITIALIZE]).sort.freeze
19
+
17
20
  INVALID_NAME = [
18
21
  'Invalid feature name! Available options: ',
19
- ALL.map { |feature_name| ":#{feature_name}" }.join(', ')
22
+ ALL_VISIBLE.map { |feature_name| ":#{feature_name}" }.join(', ')
20
23
  ].join
21
24
 
22
25
  OPTIONS = {
@@ -56,48 +59,53 @@ module Micro
56
59
  end
57
60
  end
58
61
 
59
- def options(init, diff, activemodel_validations)
60
- [init].tap do |options|
61
- options << :diff if diff
62
- options << :activemodel_validations if activemodel_validations
62
+ private
63
+
64
+ def fetch_feature_name(name)
65
+ return name unless name.is_a?(Hash)
66
+
67
+ STRICT_INITIALIZE if name[:initialize] == :strict
63
68
  end
64
- end
65
69
 
66
- private
70
+ def normalize_names(args)
71
+ names = Array(args).dup
67
72
 
68
- def normalize_names(args)
69
- Array(args).map { |arg| arg.to_s.downcase }.uniq
70
- end
73
+ last_feature = fetch_feature_name(names.pop)
71
74
 
72
- def valid_names?(names)
73
- names.all? { |name| ALL.include?(name) }
74
- end
75
+ features = names.empty? ? [last_feature] : names + [last_feature]
76
+ features.map! { |name| name.to_s.downcase }
77
+ features.uniq
78
+ end
75
79
 
76
- def valid_names!(args)
77
- names = normalize_names(args)
80
+ def valid_names?(names)
81
+ names.all? { |name| ALL.include?(name) }
82
+ end
78
83
 
79
- raise ArgumentError, INVALID_NAME if args.empty? || !valid_names?(names)
84
+ def valid_names!(args)
85
+ names = normalize_names(args)
80
86
 
81
- yield(names)
82
- end
87
+ raise ArgumentError, INVALID_NAME if names.empty? || !valid_names?(names)
83
88
 
84
- def an_initialize?(name)
85
- name == INITIALIZE || name == STRICT_INITIALIZE
86
- end
89
+ yield(names)
90
+ end
91
+
92
+ def an_initialize?(name)
93
+ name == INITIALIZE || name == STRICT_INITIALIZE
94
+ end
87
95
 
88
- def delete_initialize_if_has_strict_initialize(names)
89
- return unless names.include?(STRICT_INITIALIZE)
96
+ def delete_initialize_if_has_strict_initialize(names)
97
+ return unless names.include?(STRICT_INITIALIZE)
90
98
 
91
- names.delete_if { |name| name == INITIALIZE }
92
- end
99
+ names.delete_if { |name| name == INITIALIZE }
100
+ end
93
101
 
94
- def except_options(names_to_exclude)
95
- (ALL - names_to_exclude).tap do |names|
96
- names.delete_if { |name| an_initialize?(name) } if names_to_exclude.include?(INITIALIZE)
102
+ def except_options(names_to_exclude)
103
+ (ALL - names_to_exclude).tap do |names|
104
+ names.delete_if { |name| an_initialize?(name) } if names_to_exclude.include?(INITIALIZE)
97
105
 
98
- delete_initialize_if_has_strict_initialize(names)
106
+ delete_initialize_if_has_strict_initialize(names)
107
+ end
99
108
  end
100
- end
101
109
  end
102
110
  end
103
111
  end
@@ -3,36 +3,30 @@
3
3
  module Micro::Attributes
4
4
  module Features
5
5
  module ActiveModelValidations
6
- @@__active_model_required = false
7
- @@__active_model_load_error = false
8
-
9
- V32 = '3.2'
10
-
11
6
  def self.included(base)
12
- if !@@__active_model_load_error && !@@__active_model_required
13
- begin
14
- require 'active_model'
15
- rescue LoadError => e
16
- @@__active_model_load_error = true
17
- end
18
- @@__active_model_required = true
19
- end
7
+ begin
8
+ require 'active_model'
20
9
 
21
- unless @@__active_model_load_error
22
10
  base.send(:include, ::ActiveModel::Validations)
11
+ base.extend(ClassMethods)
12
+ rescue LoadError
13
+ end
14
+ end
15
+
16
+ module ClassMethods
17
+ def __call_after_attribute_set__(attr_name, options)
18
+ validate, validates = options.values_at(:validate, :validates)
23
19
 
24
- if ::ActiveModel::VERSION::STRING >= V32
25
- base.class_eval(<<-RUBY)
26
- def initialize(arg)
27
- self.attributes=arg
28
- run_validations!
29
- end
30
- RUBY
31
- end
20
+ self.validate(validate) if validate
21
+ self.validates(attr_name, validates) if validates
32
22
  end
33
23
  end
34
24
 
35
- private_constant :V32
25
+ private
26
+
27
+ def __call_after_micro_attribute
28
+ run_validations! if respond_to?(:run_validations!, true)
29
+ end
36
30
  end
37
31
  end
38
32
  end
@@ -3,59 +3,11 @@
3
3
  module Micro::Attributes
4
4
  module Features
5
5
  module Diff
6
- class Changes
7
- TO = 'to'.freeze
8
- FROM = 'from'.freeze
9
- FROM_TO_ERROR = 'pass the attribute name with the :from and :to values'.freeze
10
-
11
- attr_reader :from, :to, :differences
12
-
13
- def initialize(from:, to:)
14
- raise ArgumentError, "expected an instance of #{from.class}" unless to.is_a?(from.class)
15
- @from, @to = from, to
16
- @differences = diff(from.attributes, to.attributes).freeze
17
- end
18
-
19
- def empty?
20
- @differences.empty?
21
- end
22
- alias_method :blank?, :empty?
23
-
24
- def present?
25
- !empty?
26
- end
27
-
28
- def changed?(name = nil, from: nil, to: nil)
29
- if name.nil?
30
- return present? if from.nil? && to.nil?
31
- raise ArgumentError, FROM_TO_ERROR
32
- elsif from.nil? && to.nil?
33
- differences.has_key?(name.to_s)
34
- else
35
- result = @differences[name.to_s]
36
- result ? result[FROM] == from && result[TO] == to : false
37
- end
38
- end
39
-
40
- private
41
-
42
- def diff(from_attributes, to_attributes)
43
- @from_attributes, @to_attributes = from_attributes, to_attributes
44
- @from_attributes.each_with_object({}) do |(from_key, from_val), acc|
45
- to_value = @to_attributes[from_key]
46
- acc[from_key] = {FROM => from_val, TO => to_value}.freeze if from_val != to_value
47
- end
48
- end
49
-
50
- private_constant :TO, :FROM, :FROM_TO_ERROR
51
- end
52
-
53
6
  def diff_attributes(to)
54
- return Changes.new(from: self, to: to) if to.is_a?(::Micro::Attributes)
7
+ return Micro::Attributes::Diff::Changes.new(from: self, to: to) if to.is_a?(::Micro::Attributes)
8
+
55
9
  raise ArgumentError, "#{to.inspect} must implement Micro::Attributes"
56
10
  end
57
-
58
- private_constant :Changes
59
11
  end
60
12
  end
61
13
  end