statinize 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 51c0c480de402d268d6d85db85624f112019e8ee9456e7f32553dac8f42821d5
4
+ data.tar.gz: fd177fd2e864c68370840a2c5cdbc0725b7eaa8eeff9358d2a14061176a16de5
5
+ SHA512:
6
+ metadata.gz: a262a3f5f8afee169fab603fbc1530f4f84052d7786d47f3eb2bfbed4b3a2c939e15175e800a0aee1c9ce6f47cc0d4627395fe8c8c2862d40cc1d155754278b0
7
+ data.tar.gz: 752cb938cef235da3efdf2da7be11b96a3319cab0ee6eda7f58c8a3326d9c8b33c7622d8da7163f9e71a5e9341eb679904e82ef77184de62a0c64033b00da528
@@ -0,0 +1,40 @@
1
+ module Statinize
2
+ class Attribute
3
+ include Comparable
4
+
5
+ attr_reader :klass, :name, :options, :validators
6
+
7
+ def initialize(klass, name, options)
8
+ @klass = klass
9
+ @name = name
10
+ @options = options
11
+ @validators = options.keys
12
+ end
13
+
14
+ def self.create(klass, name, options)
15
+ new(klass, name, options).create
16
+ end
17
+
18
+ def create
19
+ statinizer.add_attribute(self) unless attribute_exists?
20
+ klass.send(:attr_accessor, name)
21
+ self
22
+ end
23
+
24
+ def <=>(other)
25
+ name == other.name &&
26
+ options == other.options &&
27
+ klass == other.klass
28
+ end
29
+
30
+ private
31
+
32
+ def statinizer
33
+ klass.statinizer
34
+ end
35
+
36
+ def attribute_exists?
37
+ statinizer.attribute_exists?(self)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,48 @@
1
+ module Statinize
2
+ class Caster
3
+ TYPE_CASTINGS = {
4
+ Integer => :to_i,
5
+ String => :to_s,
6
+ Float => :to_f,
7
+ Complex => :to_c,
8
+ Rational => :to_r,
9
+ Symbol => :to_sym,
10
+ Enumerator => :to_enum,
11
+ Proc => :to_proc
12
+ }.freeze
13
+
14
+ def initialize(instance, attr_name, attr_value, validator_value)
15
+ @instance = instance
16
+ @attr_name = attr_name
17
+ @attr_value = attr_value
18
+ @validator_value = validator_value
19
+ end
20
+
21
+ def cast
22
+ return false unless castable?
23
+
24
+ instance.public_send("#{attr_name}=", casted)
25
+ true
26
+ end
27
+
28
+ def self.cast(instance, attr_name, attr_value, validator_value)
29
+ new(instance, attr_name, attr_value, validator_value).cast
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :instance, :attr_name, :attr_value, :validator_value
35
+
36
+ def castable?
37
+ attr_value.respond_to? casting
38
+ end
39
+
40
+ def casting
41
+ @casting ||= TYPE_CASTINGS[validator_value]
42
+ end
43
+
44
+ def casted
45
+ @casted ||= attr_value.public_send(casting)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,19 @@
1
+ module Statinize
2
+ class NotStatinizableError < StandardError; end
3
+
4
+ class ValidationError < StandardError; end
5
+
6
+ class NoSuchValidatorError < StandardError; end
7
+
8
+ class UncastableAttributeError < StandardError; end
9
+
10
+ class Errors < Array
11
+ def to_s
12
+ nice_errors = map do |i|
13
+ "#{i.keys.first.to_s.capitalize} #{i.values.first}"
14
+ end.join(', ')
15
+
16
+ "ValidationError: #{nice_errors}"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,54 @@
1
+ module Statinize
2
+ module Statinizable
3
+ def self.included(klass)
4
+ klass.extend(ClassMethods)
5
+ klass.prepend(PrependedMethods)
6
+ end
7
+
8
+ module PrependedMethods
9
+ def initialize(*args, **kwargs)
10
+ self.class.statinizer.attrs.map(&:name).each do |attr|
11
+ instance_variable_set("@#{attr}", kwargs.delete(attr)) if kwargs.key?(attr)
12
+ end
13
+
14
+ validator.validate
15
+
16
+ super(*args, **kwargs)
17
+ end
18
+
19
+ def valid?
20
+ validator.valid?
21
+ end
22
+
23
+ def errors
24
+ validator.errors
25
+ end
26
+
27
+ private
28
+
29
+ def validator
30
+ Validator.new(self)
31
+ end
32
+ end
33
+
34
+ module ClassMethods
35
+ def statinize(force: false, &block)
36
+ instance_variable_set('@statinizer', Statinizer.new(self, force))
37
+
38
+ class_eval do
39
+ def self.statinizer
40
+ @statinizer
41
+ end
42
+ end
43
+
44
+ block.call
45
+ end
46
+
47
+ def attributes(*attrs, **options)
48
+ attrs.each do |attr|
49
+ Attribute.create(self, attr, options)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,27 @@
1
+ module Statinize
2
+ class Statinizer
3
+ attr_reader :attrs, :force
4
+
5
+ def initialize(klass, force)
6
+ @klass = klass
7
+ @force = force
8
+ @attrs = Set.new
9
+ end
10
+
11
+ def add_attribute(attribute)
12
+ @attrs.add(attribute)
13
+ end
14
+
15
+ def forced_attributes
16
+ attrs.select { |attr| attr.validators.include? :force }
17
+ end
18
+
19
+ def casted_attributes
20
+ attrs.select { |attr| attr.validators.include? :cast }
21
+ end
22
+
23
+ def attribute_exists?(attribute)
24
+ attrs.include? attribute
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,112 @@
1
+ module Statinize
2
+ class Validator
3
+ NOT_VALIDATORS = %i[force cast].freeze
4
+
5
+ def initialize(instance)
6
+ @instance = instance
7
+
8
+ raise NotStatinizableError unless instance&.class&.statinizer.is_a?(Statinizer)
9
+ raise NoSuchValidatorError unless validators_exist?
10
+
11
+ @errors = Errors.new
12
+ @erroneous_attrs = Set.new
13
+ end
14
+
15
+ def valid?
16
+ validate
17
+ return false unless errors.empty?
18
+
19
+ true
20
+ end
21
+
22
+ def invalid?
23
+ !valid?
24
+ end
25
+
26
+ def validate
27
+ attrs.each do |attr|
28
+ attr.options.each do |validator, validator_value|
29
+ next if NOT_VALIDATORS.include? validator
30
+
31
+ attr_name = attr.name
32
+
33
+ validator_class = Object.const_get("Statinize::#{validator.to_s.capitalize}Validator")
34
+
35
+ validator_instance = validator_class.new(instance)
36
+
37
+ validator_class.public_send(
38
+ :attr_accessor,
39
+ *%i[attr_name attr_value validator_value]
40
+ )
41
+
42
+ attr_value = instance.public_send attr_name
43
+
44
+ validator_instance.instance_variable_set('@attr_name', attr_name)
45
+ validator_instance.instance_variable_set('@attr_value', attr_value)
46
+ validator_instance.instance_variable_set('@validator_value', validator_value)
47
+
48
+ next if validator_instance.valid?
49
+ next if should_cast?(validator, attr_name) && casted?(attr_name, attr_value, validator_value)
50
+
51
+ @erroneous_attrs.add attr_name
52
+ add_error validator_instance.error
53
+ end
54
+ end
55
+
56
+ raise ValidationError, errors.to_s if should_raise?
57
+ end
58
+
59
+ protected
60
+
61
+ attr_reader :instance, :errors
62
+
63
+ def statinizer
64
+ @statinizer ||= instance.class.statinizer
65
+ end
66
+
67
+ def attrs
68
+ statinizer.attrs
69
+ end
70
+
71
+ def force?
72
+ statinizer.force
73
+ end
74
+
75
+ def should_raise?
76
+ force? || @erroneous_attrs.intersect?(forced_attributes.map(&:name))
77
+ end
78
+
79
+ def forced_attributes
80
+ statinizer.forced_attributes
81
+ end
82
+
83
+ def casted_attributes
84
+ statinizer.casted_attributes
85
+ end
86
+
87
+ def add_error(error)
88
+ return if error.nil?
89
+
90
+ @errors << error
91
+ end
92
+
93
+ private
94
+
95
+ def validators_exist?
96
+ attrs
97
+ .flat_map(&:validators).uniq
98
+ .reject { |validator| NOT_VALIDATORS.include? validator }
99
+ .map(&:to_s).map(&:capitalize)
100
+ .all? { |validator| Object.const_defined?("Statinize::#{validator}Validator") }
101
+ end
102
+
103
+ def should_cast?(validator, attr_name)
104
+ validator == :type &&
105
+ statinizer.casted_attributes.map(&:name).include?(attr_name)
106
+ end
107
+
108
+ def casted?(attr_name, attr_value, validator_value)
109
+ ::Statinize::Caster.cast(instance, attr_name, attr_value, validator_value)
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,29 @@
1
+ module Statinize
2
+ class PresenceValidator < Validator
3
+ def valid?
4
+ validator_value &&
5
+ !empty_array? &&
6
+ !empty_hash? &&
7
+ !empty_string? &&
8
+ !attr_value.nil?
9
+ end
10
+
11
+ def error
12
+ { attr_name => 'is blank' }
13
+ end
14
+
15
+ private
16
+
17
+ def empty_array?
18
+ attr_value == []
19
+ end
20
+
21
+ def empty_hash?
22
+ attr_value == {}
23
+ end
24
+
25
+ def empty_string?
26
+ attr_value == ''
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ module Statinize
2
+ class TypeValidator < Validator
3
+ def valid?
4
+ attr_value.is_a?(validator_value) || attr_value.nil?
5
+ end
6
+
7
+ def error
8
+ { attr_name => "should be #{validator_value}, found #{attr_value.class} instead" }
9
+ end
10
+ end
11
+ end
data/lib/statinize.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'set'
2
+ require 'pry'
3
+ require_relative 'statinize/validator'
4
+ require_relative 'statinize/caster'
5
+ require_relative 'statinize/errors'
6
+ require_relative 'statinize/validators/type_validator'
7
+ require_relative 'statinize/validators/presence_validator'
8
+ require_relative 'statinize/attribute'
9
+ require_relative 'statinize/statinizer'
10
+ require_relative 'statinize/statinizable'
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: statinize
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Barseek
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-07-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Statinization gem. Allows for creation of attributes for a class with
14
+ a given type.
15
+ email: sergey.b@hey.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/statinize.rb
21
+ - lib/statinize/attribute.rb
22
+ - lib/statinize/caster.rb
23
+ - lib/statinize/errors.rb
24
+ - lib/statinize/statinizable.rb
25
+ - lib/statinize/statinizer.rb
26
+ - lib/statinize/validator.rb
27
+ - lib/statinize/validators/presence_validator.rb
28
+ - lib/statinize/validators/type_validator.rb
29
+ homepage:
30
+ licenses:
31
+ - MIT
32
+ metadata: {}
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubygems_version: 3.3.3
49
+ signing_key:
50
+ specification_version: 4
51
+ summary: Statinize!
52
+ test_files: []