great_guardian 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9755441c71cd68b3ec0775ef0aa266a9e8ab9591d1546bdb6bdc1bf69f813a6b
4
+ data.tar.gz: 51b9331234a489ded36dd642a3711895a603ab55cc1143ebd6e24897207a6b2f
5
+ SHA512:
6
+ metadata.gz: fc93e36dcab7b24a90ba0a0fb50926b78d47f68a84c6fd1643a7ddc8b949736c415f0f9c85e787cdd647ec7813813d92e7b89aac6284879d1ad7822a3b7c2f67
7
+ data.tar.gz: 9fcfe88e86a9f64a57453eca27e4dd5445ea3a8ff95bd30ea17e87baefbf550a28fdf8f262c615d0f99cee90fd8b47d1aa3d0b06529626b0a9c075f7b46bb1f8
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014-2020 Cyril Kato
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,71 @@
1
+ # Great Guardian
2
+
3
+ [![Build Status](https://api.travis-ci.org/cyril/great_guardian.rb.svg?branch=master)][travis]
4
+ [![Gem Version](https://badge.fury.io/rb/great_guardian.svg)][gem]
5
+ [![Inline docs](https://inch-ci.org/github/cyril/great_guardian.rb.svg?branch=master)][inchpages]
6
+ [![Documentation](http://img.shields.io/:yard-docs-38c800.svg)][rubydoc]
7
+
8
+ > Web parameters validation for Ruby 🛡️
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'great_guardian', '>= 0.1.0.beta1'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle install
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install great_guardian --pre
25
+
26
+ ## Usage
27
+
28
+ ```ruby
29
+ class EmailAttribute < GreatGuardian::Attribute::Base
30
+ def self.expected_value_type
31
+ ::GreatGuardian::ExpectedValue::String
32
+ end
33
+
34
+ def self.default_constraints
35
+ {
36
+ pattern: /\A[^@]+@[^@]+$\z/i
37
+ }
38
+ end
39
+ end
40
+
41
+ email_attribute = EmailAttribute.new(required: true)
42
+
43
+ email_attribute.call(nil) # => #<GreatGuardian::Verdict:0x00007ffd3f00ad40 @attribute_name="email_attribute", @value=nil, @error_message=["attribute.email_attribute.errors.required", {:name=>["attribute.email_attribute.name"], :expected=>nil}], @medium=:body>
44
+ email_attribute.call(4) # => #<GreatGuardian::Verdict:0x00007ffd3e360590 @attribute_name="email_attribute", @value=4, @error_message=["attribute.email_attribute.errors.type", {:name=>["attribute.email_attribute.name"], :expected=>["expected_value.string.type"]}], @medium=:body>
45
+ email_attribute.call('boom') # => #<GreatGuardian::Verdict:0x00007ffd3e3d8360 @attribute_name="email_attribute", @value="boom", @error_message=["attribute.email_attribute.errors.pattern", {:name=>["attribute.email_attribute.name"], :expected=>/\A[^@]+@[^@]+$\z/i}], @medium=:body>
46
+ email_attribute.call('bob@gmail.com') # => #<GreatGuardian::Verdict:0x00007ffd3e3c23d0 @attribute_name="email_attribute", @value="bob@gmail.com", @error_message=nil, @medium=:body>
47
+ ```
48
+
49
+ ## Contact
50
+
51
+ * Home page: https://github.com/cyril/great_guardian.rb
52
+ * Bugs/issues: https://github.com/cyril/great_guardian.rb/issues
53
+
54
+ ## Rubies
55
+
56
+ * [MRI](https://www.ruby-lang.org/)
57
+ * [Rubinius](https://rubinius.com/)
58
+ * [JRuby](https://www.jruby.org/)
59
+
60
+ ## Versioning
61
+
62
+ __GreatGuardian__ follows [Semantic Versioning 2.0](https://semver.org/).
63
+
64
+ ## License
65
+
66
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
67
+
68
+ [gem]: https://rubygems.org/gems/great_guardian
69
+ [travis]: https://travis-ci.org/cyril/great_guardian.rb
70
+ [inchpages]: https://inch-ci.org/github/cyril/great_guardian.rb
71
+ [rubydoc]: https://rubydoc.info/gems/great_guardian/frames
@@ -0,0 +1,6 @@
1
+ module GreatGuardian
2
+ end
3
+
4
+ require_relative File.join('great_guardian', 'attribute')
5
+ require_relative File.join('great_guardian', 'expected_value')
6
+ require_relative File.join('great_guardian', 'verdict')
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GreatGuardian
4
+ # Collection of attributes.
5
+ module Attribute
6
+ end
7
+ end
8
+
9
+ Dir[File.join File.dirname(__FILE__), 'attribute', '*.rb'].each do |fname|
10
+ require_relative fname
11
+ end
@@ -0,0 +1,161 @@
1
+ module GreatGuardian
2
+ module Attribute
3
+ class Base
4
+ attr_reader :custom_constraints, :default_value, :medium
5
+
6
+ def initialize(required: true, medium: :body, default_value: nil, **custom_constraints)
7
+ raise ::ArgumentError, required.inspect unless [false, true].include?(required)
8
+ raise ::ArgumentError, medium.inspect unless %i[body header querystring].include?(medium)
9
+
10
+ @custom_constraints = custom_constraints
11
+ @default_value = default_value
12
+ @medium = medium
13
+ @required = required
14
+ end
15
+
16
+ def call(actual_value)
17
+ actual_value = actual_value_or_default_value(actual_value)
18
+ error_message = compute_error_message(actual_value)
19
+
20
+ Verdict.new(to_s, actual_value, error_message, medium)
21
+ end
22
+
23
+ def constraints
24
+ self.class.default_constraints.merge(**custom_constraints)
25
+ end
26
+
27
+ def expected_value
28
+ self.class.expected_value_type.new(**constraints)
29
+ end
30
+
31
+ def required?
32
+ @required
33
+ end
34
+
35
+ def to_s
36
+ self.class
37
+ .name
38
+ .split('::')
39
+ .fetch(-1)
40
+ .gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
41
+ .gsub(/([a-z\d])([A-Z])/,'\1_\2')
42
+ .tr('-', '_')
43
+ .downcase
44
+ end
45
+
46
+ def to_sym
47
+ to_s.to_sym
48
+ end
49
+
50
+ def body?
51
+ medium.equal?(:body)
52
+ end
53
+
54
+ def header_or_querystring?
55
+ !body?
56
+ end
57
+
58
+ def self.possible_values; end
59
+
60
+ def self.default_value; end
61
+
62
+ def self.default_constraints
63
+ {}
64
+ end
65
+
66
+ def self.first_matched_error_on_complex_validation_logic_against(_actual_value); end
67
+
68
+ private
69
+
70
+ def actual_value_or_default_value(actual_value)
71
+ return actual_value unless not_required_and_nil_or_empty?(actual_value)
72
+
73
+ return default_value unless default_value.nil?
74
+ return self.class.default_value unless self.class.default_value.nil?
75
+
76
+ actual_value
77
+ end
78
+
79
+ def compute_error_message(actual_value)
80
+ return if not_required_and_nil_or_empty?(actual_value)
81
+
82
+ if required_but_nil?(actual_value)
83
+ return i18n_attribute_error(:required, name: i18n_attribute_name)
84
+ end
85
+
86
+ first_error = expected_value.first_matched_error_against(actual_value, is_native: body?)
87
+
88
+ if first_error.nil?
89
+ unless self.class.possible_values.nil?
90
+ actual_value_is_included_in_possible_values = if self.class.expected_value_type.equal?(::GreatGuardian::ExpectedValue::Array)
91
+ actual_value.all? { |item| self.class.possible_values.include?(item) }
92
+ else
93
+ self.class.possible_values.include?(actual_value)
94
+ end
95
+
96
+ unless actual_value_is_included_in_possible_values
97
+ return i18n_attribute_error(:possible_values,
98
+ name: i18n_attribute_name,
99
+ expected: self.class.possible_values.map(&:inspect).sort.join(', ')
100
+ )
101
+ end
102
+ end
103
+
104
+ error_on_complex_validation_logic = self.class.first_matched_error_on_complex_validation_logic_against(actual_value)
105
+
106
+ unless error_on_complex_validation_logic.nil?
107
+ return i18n_attribute_error(error_on_complex_validation_logic)
108
+ end
109
+ end
110
+
111
+ return if first_error.nil?
112
+
113
+ expected = expected_value.public_send(first_error)
114
+
115
+ if first_error.equal?(:type)
116
+ expected = i18n_expected_value_type(expected_value.type)
117
+ end
118
+
119
+ i18n_attribute_error(first_error,
120
+ name: i18n_attribute_name,
121
+ expected: expected
122
+ )
123
+ end
124
+
125
+ def not_required_and_nil_or_empty?(actual_value)
126
+ return false if required?
127
+
128
+ return true if actual_value.nil? && body?
129
+ return true if actual_value.to_s.empty? && header_or_querystring?
130
+
131
+ false
132
+ end
133
+
134
+ def required_but_nil?(actual_value)
135
+ actual_value.nil? && required?
136
+ end
137
+
138
+ def i18n_attribute_name
139
+ [
140
+ "attribute.#{self.to_sym}.name"
141
+ ]
142
+ end
143
+
144
+ def i18n_expected_value_type(expected_value)
145
+ [
146
+ "expected_value.#{expected_value}.type"
147
+ ]
148
+ end
149
+
150
+ def i18n_attribute_error(error, name: nil, expected: nil)
151
+ [
152
+ "attribute.#{self.to_sym}.errors.#{error}",
153
+ {
154
+ name: name,
155
+ expected: expected
156
+ }
157
+ ]
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GreatGuardian
4
+ # Collection of expected values.
5
+ module ExpectedValue
6
+ end
7
+ end
8
+
9
+ Dir[File.join File.dirname(__FILE__), 'expected_value', '*.rb'].each do |fname|
10
+ require_relative fname
11
+ end
@@ -0,0 +1,41 @@
1
+ require_relative 'base'
2
+
3
+ module GreatGuardian
4
+ module ExpectedValue
5
+ class Array < Base
6
+ attr_reader :minlen, :maxlen
7
+
8
+ def initialize(minlen: nil, maxlen: nil)
9
+ raise ::ArgumentError, minlen.inspect if !minlen.nil? && !minlen.is_a?(::Integer)
10
+ raise ::ArgumentError, maxlen.inspect if !maxlen.nil? && !maxlen.is_a?(::Integer)
11
+
12
+ if !minlen.nil? && !maxlen.nil? && (minlen > maxlen)
13
+ raise ::ArgumentError, minlen.inspect, maxlen.inspect
14
+ end
15
+
16
+ @minlen = minlen
17
+ @maxlen = maxlen
18
+ end
19
+
20
+ def first_matched_error_against(actual_value, is_native: true)
21
+ actual_value = emulate(actual_value) unless is_native
22
+
23
+ return :type unless valid_type?(actual_value)
24
+ return :minlen if !minlen.nil? && actual_value.length < minlen
25
+ return :maxlen if !maxlen.nil? && actual_value.length > maxlen
26
+ end
27
+
28
+ def self.types
29
+ [::Array]
30
+ end
31
+
32
+ def emulate(value)
33
+ if value.to_s.start_with?('[') && value.to_s.end_with?(']')
34
+ return value.to_s.split(',').map(&:strip)
35
+ end
36
+
37
+ value
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ module GreatGuardian
2
+ module ExpectedValue
3
+ class Base
4
+ def initialize(**)
5
+ end
6
+
7
+ def valid_type?(actual_value)
8
+ self.class.types.any? { |type| actual_value.is_a?(type) }
9
+ end
10
+
11
+ def type
12
+ self.class
13
+ .name
14
+ .split('::')
15
+ .fetch(-1)
16
+ .gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
17
+ .gsub(/([a-z\d])([A-Z])/,'\1_\2')
18
+ .tr('-', '_')
19
+ .downcase
20
+ .to_sym
21
+ end
22
+
23
+ def self.types
24
+ raise ::NotImplementedError
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ # require_relative ::File.join('..', 'expected_value')
@@ -0,0 +1,24 @@
1
+ require_relative 'base'
2
+
3
+ module GreatGuardian
4
+ module ExpectedValue
5
+ class Boolean < Base
6
+ def first_matched_error_against(actual_value, is_native: true)
7
+ actual_value = emulate(actual_value) unless is_native
8
+
9
+ return :type unless valid_type?(actual_value)
10
+ end
11
+
12
+ def self.types
13
+ [::FalseClass, ::TrueClass]
14
+ end
15
+
16
+ def emulate(value)
17
+ return true if value.eql?('true')
18
+ return false if value.eql?('false')
19
+
20
+ value
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ require_relative 'base'
2
+
3
+ module GreatGuardian
4
+ module ExpectedValue
5
+ class Number < Base
6
+ attr_reader :min, :max
7
+
8
+ def initialize(min: nil, max: nil)
9
+ raise ::ArgumentError, min.inspect if !min.nil? && !min.is_a?(::Numeric)
10
+ raise ::ArgumentError, max.inspect if !max.nil? && !max.is_a?(::Numeric)
11
+
12
+ if !min.nil? && !max.nil? && (min > max)
13
+ raise ::ArgumentError, min.inspect, max.inspect
14
+ end
15
+
16
+ @min = min
17
+ @max = max
18
+ end
19
+
20
+ def first_matched_error_against(actual_value, is_native: true)
21
+ actual_value = emulate(actual_value) unless is_native
22
+
23
+ return :type unless valid_type?(actual_value)
24
+ return :min if !min.nil? && actual_value < min
25
+ return :max if !max.nil? && actual_value > max
26
+ end
27
+
28
+ def self.types
29
+ [::Numeric]
30
+ end
31
+
32
+ def emulate(value)
33
+ if value.match?(/\A-?([0-9]+.)?[0-9]+\z/)
34
+ return value.include?('.') ? value.to_f : value.to_i
35
+ end
36
+
37
+ value
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ require_relative 'base'
2
+
3
+ module GreatGuardian
4
+ module ExpectedValue
5
+ class String < Base
6
+ attr_reader :minlen, :maxlen, :pattern
7
+
8
+ def initialize(minlen: nil, maxlen: nil, pattern: nil)
9
+ raise ::ArgumentError, minlen.inspect if !minlen.nil? && !minlen.is_a?(::Integer)
10
+ raise ::ArgumentError, maxlen.inspect if !maxlen.nil? && !maxlen.is_a?(::Integer)
11
+
12
+ if !minlen.nil? && !maxlen.nil? && (minlen > maxlen)
13
+ raise ::ArgumentError, minlen.inspect, maxlen.inspect
14
+ end
15
+
16
+ raise ::ArgumentError, pattern.inspect if !pattern.nil? && !pattern.is_a?(::Regexp)
17
+
18
+ @minlen = minlen
19
+ @maxlen = maxlen
20
+ @pattern = pattern
21
+ end
22
+
23
+ def first_matched_error_against(actual_value, is_native: true)
24
+ actual_value = emulate(actual_value) unless is_native
25
+
26
+ return :type unless valid_type?(actual_value)
27
+ return :minlen if !minlen.nil? && actual_value.length < minlen
28
+ return :maxlen if !maxlen.nil? && actual_value.length > maxlen
29
+ return :pattern if !pattern.nil? && !actual_value.match?(pattern)
30
+ end
31
+
32
+ def self.types
33
+ [::String]
34
+ end
35
+
36
+ def emulate(value)
37
+ value.to_s
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GreatGuardian
4
+ # Decision of Great Guardian.
5
+ class Verdict
6
+ attr_reader :value, :error_message, :medium
7
+
8
+ def initialize(attribute_name, value, error_message, medium)
9
+ raise ::ArgumentError, medium.inspect unless %i[body header querystring].include?(medium)
10
+
11
+ @attribute_name = attribute_name
12
+ @value = value
13
+ @error_message = error_message
14
+ @medium = medium
15
+ end
16
+
17
+ def valid?
18
+ error_message.nil?
19
+ end
20
+
21
+ def invalid?
22
+ !valid?
23
+ end
24
+
25
+ def to_s
26
+ @attribute_name
27
+ end
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: great_guardian
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.beta1
5
+ platform: ruby
6
+ authors:
7
+ - Cyril Kato
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-performance
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.17'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.17'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.9'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.9'
97
+ description: "Web parameters validation for Ruby \U0001F6E1️"
98
+ email: contact@cyril.email
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - LICENSE.md
104
+ - README.md
105
+ - lib/great_guardian.rb
106
+ - lib/great_guardian/attribute.rb
107
+ - lib/great_guardian/attribute/base.rb
108
+ - lib/great_guardian/expected_value.rb
109
+ - lib/great_guardian/expected_value/array.rb
110
+ - lib/great_guardian/expected_value/base.rb
111
+ - lib/great_guardian/expected_value/boolean.rb
112
+ - lib/great_guardian/expected_value/number.rb
113
+ - lib/great_guardian/expected_value/string.rb
114
+ - lib/great_guardian/verdict.rb
115
+ homepage: https://github.com/cyril/great_guardian.rb
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">"
131
+ - !ruby/object:Gem::Version
132
+ version: 1.3.1
133
+ requirements: []
134
+ rubygems_version: 3.1.2
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: "Web parameters validation for Ruby \U0001F6E1️"
138
+ test_files: []