statinize 0.0.1 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51c0c480de402d268d6d85db85624f112019e8ee9456e7f32553dac8f42821d5
4
- data.tar.gz: fd177fd2e864c68370840a2c5cdbc0725b7eaa8eeff9358d2a14061176a16de5
3
+ metadata.gz: b8e1c828e69c088b99e338e49626e98faf5dc89a62a86545aa5e4041ce962647
4
+ data.tar.gz: '094d296e476cd29e3c850542d71119eb2f48ac28fc366ecf3deebabb79f9db24'
5
5
  SHA512:
6
- metadata.gz: a262a3f5f8afee169fab603fbc1530f4f84052d7786d47f3eb2bfbed4b3a2c939e15175e800a0aee1c9ce6f47cc0d4627395fe8c8c2862d40cc1d155754278b0
7
- data.tar.gz: 752cb938cef235da3efdf2da7be11b96a3319cab0ee6eda7f58c8a3326d9c8b33c7622d8da7163f9e71a5e9341eb679904e82ef77184de62a0c64033b00da528
6
+ metadata.gz: 40df9591e11075cd22f8c72b6c48ba31a0904ea679d5039cdb4f4508665fc9e02d443a3cb406352323a7be00048537248314914ff1a1523c736dde3dddfaa776
7
+ data.tar.gz: f594086d4366967f456a21fb03f43b5aa83eccbf01865466b90be8130d9fcaac0925f369be2f0375af684d1fc2f4c1533c92f1f24d8acce3fce9f102271841ed
@@ -0,0 +1,35 @@
1
+ module Statinize
2
+ class Attribute
3
+ module Options
4
+ class Conditions < Array
5
+ def truthy?(instance)
6
+ all? do |condition|
7
+ if condition.is_a?(Symbol)
8
+ !!condition.to_proc.call(instance)
9
+ elsif condition.lambda?
10
+ !!instance.instance_exec(&condition)
11
+ elsif condition.is_a?(Proc)
12
+ !!condition.call(instance)
13
+ else
14
+ raise InvalidConditionError, "condition should be a Symbol, Proc, or a lambda"
15
+ end
16
+ end
17
+ end
18
+
19
+ def falsey?(instance)
20
+ all? do |condition|
21
+ if condition.is_a?(Symbol)
22
+ !condition.to_proc.call(instance)
23
+ elsif condition.lambda?
24
+ !instance.instance_exec(&condition)
25
+ elsif condition.is_a?(Proc)
26
+ !condition.call(instance)
27
+ else
28
+ raise InvalidConditionError, "condition should be a Symbol, Proc, or a lambda"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,65 @@
1
+ module Statinize
2
+ class Attribute
3
+ module Options
4
+ def self.extended(instance)
5
+ raise "Cannot extend #{instance}" unless instance.is_a? Hash
6
+
7
+ instance.transform_values! do |value|
8
+ if %i[if unless].include?(instance.key(value))
9
+ Conditions.new(Array(value))
10
+ else
11
+ value
12
+ end
13
+ end
14
+ end
15
+
16
+ def validators
17
+ @validators ||= defined_validators
18
+ .transform_keys { |validator| Object.const_get validator }
19
+ end
20
+
21
+ def all_validators_defined?
22
+ validators_class_names
23
+ .all? { |validator, _| Object.const_defined? validator }
24
+ end
25
+
26
+ def should_cast?
27
+ key?(:type) && key?(:cast)
28
+ end
29
+
30
+ def should_validate?(instance)
31
+ return false if validators.empty?
32
+ return true unless key?(:unless) || key?(:if)
33
+ return false if key?(:unless) && !unless_passed?(instance)
34
+
35
+ if_passed?(instance)
36
+ end
37
+
38
+ private
39
+
40
+ def unless_passed?(instance)
41
+ self[:unless].falsey?(instance)
42
+ end
43
+
44
+ def if_passed?(instance)
45
+ self[:if].truthy?(instance)
46
+ end
47
+
48
+ def filtered_validators
49
+ reject do |k, _|
50
+ ::Statinize::Validator::NOT_VALIDATORS.include? k
51
+ end
52
+ end
53
+
54
+ def defined_validators
55
+ validators_class_names
56
+ .select { |validator, _| Object.const_defined? validator }
57
+ end
58
+
59
+ def validators_class_names
60
+ filtered_validators
61
+ .transform_keys { |validator| "::Statinize::#{validator.to_s.capitalize}Validator" }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,11 @@
1
+ module Statinize
2
+ class Attribute
3
+ class OptionsCollection < Array
4
+ def all_validators_defined?
5
+ raise "Not an option!" unless all? { _1.respond_to? :validators }
6
+
7
+ all?(&:all_validators_defined?)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -2,25 +2,29 @@ module Statinize
2
2
  class Attribute
3
3
  include Comparable
4
4
 
5
- attr_reader :klass, :name, :options, :validators
5
+ attr_reader :klass, :name, :options
6
6
 
7
- def initialize(klass, name, options)
7
+ def initialize(klass, name, opts)
8
8
  @klass = klass
9
9
  @name = name
10
- @options = options
11
- @validators = options.keys
10
+ @options = OptionsCollection.new
11
+ @options << opts.clone.extend(Options)
12
12
  end
13
13
 
14
- def self.create(klass, name, options)
15
- new(klass, name, options).create
14
+ def self.create(klass, name, opts)
15
+ new(klass, name, opts).create
16
16
  end
17
17
 
18
18
  def create
19
- statinizer.add_attribute(self) unless attribute_exists?
19
+ statinizer.add_attribute(self) unless attribute?
20
20
  klass.send(:attr_accessor, name)
21
21
  self
22
22
  end
23
23
 
24
+ def add_options(opts)
25
+ options << opts.extend(Options)
26
+ end
27
+
24
28
  def <=>(other)
25
29
  name == other.name &&
26
30
  options == other.options &&
@@ -33,8 +37,8 @@ module Statinize
33
37
  klass.statinizer
34
38
  end
35
39
 
36
- def attribute_exists?
37
- statinizer.attribute_exists?(self)
40
+ def attribute?
41
+ statinizer.attribute?(self)
38
42
  end
39
43
  end
40
44
  end
@@ -8,14 +8,14 @@ module Statinize
8
8
  Rational => :to_r,
9
9
  Symbol => :to_sym,
10
10
  Enumerator => :to_enum,
11
- Proc => :to_proc
11
+ Proc => :to_proc,
12
12
  }.freeze
13
13
 
14
- def initialize(instance, attr_name, attr_value, validator_value)
14
+ def initialize(instance, attr, option)
15
15
  @instance = instance
16
- @attr_name = attr_name
17
- @attr_value = attr_value
18
- @validator_value = validator_value
16
+ @attr_name = attr.name
17
+ @attr_value = instance.public_send(attr_name)
18
+ @validator_value = option[:type]
19
19
  end
20
20
 
21
21
  def cast
@@ -25,10 +25,6 @@ module Statinize
25
25
  true
26
26
  end
27
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
28
  private
33
29
 
34
30
  attr_reader :instance, :attr_name, :attr_value, :validator_value
@@ -0,0 +1,11 @@
1
+ module Statinize
2
+ class Configuration
3
+ include Singleton
4
+
5
+ attr_accessor :force
6
+
7
+ def self.configure
8
+ yield instance if block_given?
9
+ end
10
+ end
11
+ end
@@ -1,17 +1,15 @@
1
1
  module Statinize
2
- class NotStatinizableError < StandardError; end
3
-
4
2
  class ValidationError < StandardError; end
5
3
 
6
4
  class NoSuchValidatorError < StandardError; end
7
5
 
8
- class UncastableAttributeError < StandardError; end
6
+ class InvalidConditionError < StandardError; end
9
7
 
10
8
  class Errors < Array
11
9
  def to_s
12
10
  nice_errors = map do |i|
13
- "#{i.keys.first.to_s.capitalize} #{i.values.first}"
14
- end.join(', ')
11
+ "#{i.keys.first.to_s.split("_").tap { |attr| attr.first.capitalize! }.join(" ")} #{i.values.first}"
12
+ end.join("; ")
15
13
 
16
14
  "ValidationError: #{nice_errors}"
17
15
  end
@@ -7,47 +7,38 @@ module Statinize
7
7
 
8
8
  module PrependedMethods
9
9
  def initialize(*args, **kwargs)
10
- self.class.statinizer.attrs.map(&:name).each do |attr|
10
+ self.class.statinizer.attributes.map(&:name).each do |attr|
11
11
  instance_variable_set("@#{attr}", kwargs.delete(attr)) if kwargs.key?(attr)
12
12
  end
13
13
 
14
- validator.validate
14
+ define_validation
15
+ validate!
15
16
 
16
17
  super(*args, **kwargs)
17
18
  end
18
19
 
19
- def valid?
20
- validator.valid?
20
+ def validation
21
+ @validation ||= Validation.new(statinizer, self)
21
22
  end
22
23
 
23
- def errors
24
- validator.errors
25
- end
26
-
27
- private
24
+ alias_method :define_validation, :validation
28
25
 
29
- def validator
30
- Validator.new(self)
26
+ def statinizer
27
+ self.class.statinizer
31
28
  end
32
29
  end
33
30
 
34
31
  module ClassMethods
35
- def statinize(force: false, &block)
36
- instance_variable_set('@statinizer', Statinizer.new(self, force))
32
+ def statinize(&block)
33
+ @statinizer = Statinizer.new(self)
37
34
 
38
- class_eval do
39
- def self.statinizer
40
- @statinizer
41
- end
42
- end
35
+ statinizer.instance_eval(&block)
43
36
 
44
- block.call
37
+ # statinizer.check_validators_exist!
45
38
  end
46
39
 
47
- def attributes(*attrs, **options)
48
- attrs.each do |attr|
49
- Attribute.create(self, attr, options)
50
- end
40
+ def statinizer
41
+ @statinizer
51
42
  end
52
43
  end
53
44
  end
@@ -1,27 +1,61 @@
1
1
  module Statinize
2
2
  class Statinizer
3
- attr_reader :attrs, :force
3
+ attr_reader :klass
4
4
 
5
- def initialize(klass, force)
5
+ def initialize(klass)
6
6
  @klass = klass
7
- @force = force
8
- @attrs = Set.new
7
+ @force = config.force
8
+ end
9
+
10
+ def attribute(*attrs, **options)
11
+ attrs.each do |attr|
12
+ Attribute.create(klass, attr, options)
13
+ end
14
+ end
15
+
16
+ def validate(*attrs, **options)
17
+ attrs.each do |attr|
18
+ attribute = attributes.find { _1.name == attr }
19
+ attribute&.add_options(options)
20
+ end
21
+ end
22
+
23
+ def force(force = nil)
24
+ force.nil? ? @force : @force = force
25
+ end
26
+
27
+ def self.configure(&block)
28
+ Configuration.configure(&block)
29
+ end
30
+
31
+ def config
32
+ @config ||= Configuration.instance
33
+ end
34
+
35
+ def force?
36
+ @force
9
37
  end
10
38
 
11
39
  def add_attribute(attribute)
12
- @attrs.add(attribute)
40
+ attributes.add(attribute)
13
41
  end
14
42
 
15
- def forced_attributes
16
- attrs.select { |attr| attr.validators.include? :force }
43
+ def attributes
44
+ @attributes ||= Set.new
17
45
  end
18
46
 
19
- def casted_attributes
20
- attrs.select { |attr| attr.validators.include? :cast }
47
+ def attribute?(attribute)
48
+ attributes.include? attribute
21
49
  end
22
50
 
23
- def attribute_exists?(attribute)
24
- attrs.include? attribute
51
+ def check_validators_exist!
52
+ raise NoSuchValidatorError unless all_validators_defined?
53
+ end
54
+
55
+ private
56
+
57
+ def all_validators_defined?
58
+ attributes.map { |attr| attr.options.all_validators_defined? }.all? { !!_1 }
25
59
  end
26
60
  end
27
61
  end
@@ -0,0 +1,88 @@
1
+ module Statinize
2
+ class Validation
3
+ INSTANCE_METHODS = %i[validate validate! valid? invalid? errors]
4
+
5
+ attr_reader :statinizer, :instance, :errors
6
+
7
+ def initialize(statinizer, instance)
8
+ @statinizer = statinizer
9
+ @instance = instance
10
+
11
+ define_instance_methods
12
+ end
13
+
14
+ def validate
15
+ @errors = Errors.new
16
+ @erroneous_attributes = Set.new
17
+ @erroneous_forced_attributes = Set.new
18
+
19
+ fill_errors
20
+ end
21
+
22
+ def validate!
23
+ validate
24
+
25
+ raise ValidationError, errors.to_s if should_raise?
26
+ end
27
+
28
+ def valid?
29
+ errors.empty?
30
+ end
31
+
32
+ def invalid?
33
+ !valid?
34
+ end
35
+
36
+ private
37
+
38
+ def fill_errors
39
+ attributes.each do |attr|
40
+ attr.options.each do |option|
41
+ next unless option.should_validate?(instance)
42
+
43
+ attr_value = instance.public_send(attr.name)
44
+
45
+ option.validators.each do |validator_class, validator_value|
46
+ validator_instance = validator_class.new(attr_value, validator_value)
47
+
48
+ next if validator_instance.valid?
49
+ next if validator_class == TypeValidator && cast(attr, option)
50
+
51
+ erroneous_attributes.add(attr)
52
+ erroneous_forced_attributes.add(attr) if option[:force]
53
+ @errors << { attr.name => validator_instance.error }
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def cast(attr, option)
60
+ caster = Caster.new(instance, attr, option)
61
+
62
+ option.should_cast? && caster.cast
63
+ end
64
+
65
+ def should_raise?
66
+ return false if valid?
67
+
68
+ statinizer.force? ||
69
+ erroneous_attributes.intersect?(erroneous_forced_attributes)
70
+ end
71
+
72
+ def attributes
73
+ statinizer.attributes
74
+ end
75
+
76
+ def define_instance_methods
77
+ INSTANCE_METHODS.each do |meth|
78
+ instance.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
79
+ def #{meth}
80
+ validation.#{meth}
81
+ end
82
+ RUBY_EVAL
83
+ end
84
+ end
85
+
86
+ attr_reader :erroneous_attributes, :erroneous_forced_attributes
87
+ end
88
+ end
@@ -1,112 +1,20 @@
1
1
  module Statinize
2
2
  class Validator
3
- NOT_VALIDATORS = %i[force cast].freeze
3
+ NOT_VALIDATORS = %i[force cast if unless]
4
4
 
5
- def initialize(instance)
6
- @instance = instance
5
+ attr_accessor :attr_value, :validator_value
7
6
 
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
7
+ def initialize(attr_value, validator_value)
8
+ @attr_value = attr_value
9
+ @validator_value = validator_value
13
10
  end
14
11
 
15
12
  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)
13
+ raise NoMethodError, "#valid? method is not implemented on a #{self.class}"
106
14
  end
107
15
 
108
- def casted?(attr_name, attr_value, validator_value)
109
- ::Statinize::Caster.cast(instance, attr_name, attr_value, validator_value)
16
+ def error
17
+ raise NoMethodError, "#error method is not implemented on a #{self.class}"
110
18
  end
111
19
  end
112
20
  end
@@ -0,0 +1,11 @@
1
+ module Statinize
2
+ class InclusionValidator < Validator
3
+ def valid?
4
+ validator_value.is_a?(Array) && validator_value.include?(attr_value)
5
+ end
6
+
7
+ def error
8
+ "should be one of #{validator_value.join(", ")}"
9
+ end
10
+ end
11
+ end
@@ -9,7 +9,7 @@ module Statinize
9
9
  end
10
10
 
11
11
  def error
12
- { attr_name => 'is blank' }
12
+ "is blank"
13
13
  end
14
14
 
15
15
  private
@@ -23,7 +23,7 @@ module Statinize
23
23
  end
24
24
 
25
25
  def empty_string?
26
- attr_value == ''
26
+ attr_value == ""
27
27
  end
28
28
  end
29
29
  end
@@ -5,7 +5,7 @@ module Statinize
5
5
  end
6
6
 
7
7
  def error
8
- { attr_name => "should be #{validator_value}, found #{attr_value.class} instead" }
8
+ "should be #{validator_value}, found #{attr_value.class} instead"
9
9
  end
10
10
  end
11
11
  end
data/lib/statinize.rb CHANGED
@@ -1,10 +1,17 @@
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'
1
+ require "set"
2
+ require "pry"
3
+ require "singleton"
4
+ require_relative "statinize/statinizable"
5
+ require_relative "statinize/statinizer"
6
+ require_relative "statinize/configuration"
7
+ require_relative "statinize/attribute"
8
+ require_relative "statinize/attribute/options"
9
+ require_relative "statinize/attribute/options/conditions"
10
+ require_relative "statinize/attribute/options_collection"
11
+ require_relative "statinize/validator"
12
+ require_relative "statinize/validation"
13
+ require_relative "statinize/validators/type_validator"
14
+ require_relative "statinize/validators/presence_validator"
15
+ require_relative "statinize/validators/inclusion_validator"
16
+ require_relative "statinize/caster"
17
+ require_relative "statinize/errors"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statinize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Barseek
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-01 00:00:00.000000000 Z
11
+ date: 2022-07-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Statinization gem. Allows for creation of attributes for a class with
14
14
  a given type.
@@ -19,11 +19,17 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - lib/statinize.rb
21
21
  - lib/statinize/attribute.rb
22
+ - lib/statinize/attribute/options.rb
23
+ - lib/statinize/attribute/options/conditions.rb
24
+ - lib/statinize/attribute/options_collection.rb
22
25
  - lib/statinize/caster.rb
26
+ - lib/statinize/configuration.rb
23
27
  - lib/statinize/errors.rb
24
28
  - lib/statinize/statinizable.rb
25
29
  - lib/statinize/statinizer.rb
30
+ - lib/statinize/validation.rb
26
31
  - lib/statinize/validator.rb
32
+ - lib/statinize/validators/inclusion_validator.rb
27
33
  - lib/statinize/validators/presence_validator.rb
28
34
  - lib/statinize/validators/type_validator.rb
29
35
  homepage: