u-attributes 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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