statinize 0.0.1 → 0.1.1

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