lab42_basic_constraints 0.1.1 → 0.2.0

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: c88af59f506668cbc7c56fffc54c3430f31a0ebf5ad7d697dd3aadebf6ef5192
4
- data.tar.gz: 84bedc52d927607fbdbc5d108d56d23ab02d8931fb939567a06ece934c9903a5
3
+ metadata.gz: 672bcf05278d0a451a503939dd0ebf0f69b95914be02d466c0024717747fab76
4
+ data.tar.gz: d398d43d6dcbb335df544545e4b6c1ea4795c7a4b086725e9fcb4595f87b3c48
5
5
  SHA512:
6
- metadata.gz: 27926e413ebcf7801f572816d6fe5d23ad340d972526ca4d25246e927d118e7073d789b6f167c61c569067097c0bf761b1a707d151320d30400bbb19558e3113
7
- data.tar.gz: f49c25cdf9c96bc9e0da08d7e7783774dd0c511a814a0431360bdb88a208c836cf15d6b35beb3469558ed93a5674377d56993b1b509e4cb9fe13260f65a20632
6
+ metadata.gz: 98c2067be96925b8724360ed748b91fcb606ecce255867f1da5e11958148cf55006e61a6d1f9397d47c66e552698d9b9131475389dbad69c2c128ab57dabca52
7
+ data.tar.gz: 7839dd0e966966d27cd560bb9b28d43b1181ca1547f116058a6923176aa04ae0a5bc81fa6a812fa75372fb2fc57951f7a4966e0c45461dbd6af0aee7e93d379d
data/README.md CHANGED
@@ -17,27 +17,46 @@ Given That we alias `Lab42::BasicConstraints` to `BC`
17
17
  require "lab42/basic_constraints/alias"
18
18
  ```
19
19
 
20
+ Given
21
+ ```ruby
22
+ let :all_basic_constraints do
23
+ %i[
24
+ all_digits_string alphanumeric_string
25
+ bool
26
+ date date_time day
27
+ hour
28
+ int
29
+ lowercase_string
30
+ minute month
31
+ non_negative_int non_negative_number number
32
+ positive_int positive_number
33
+ second string
34
+ uppercase_string
35
+ year
36
+ ]
37
+ end
38
+ let :all_parametrized_constraints do
39
+ %i[ int_range limited_string ]
40
+ end
41
+
42
+ ```
20
43
  Then All Basic Constraints can be listed by means of `BC.all_constraints`
21
44
 
22
45
  ```ruby
23
- all_basic_constraints = %i[
24
- all_digits_string alphanumeric_string
25
- bool
26
- date date_time day
27
- hour
28
- lowercase_string
29
- minute month
30
- non_empty_string non_negative_float non_negative_int non_negative_number
31
- positive_float positive_int positive_number
32
- second
33
- time
34
- uppercase_string
35
- year
36
- ]
37
-
38
- expect( BC.all_constraints ).to eq(all_basic_constraints)
46
+
47
+ expect( Set.new(BC.all_constraints) ).to eq(Set.new(all_basic_constraints + all_parametrized_constraints))
48
+ ```
49
+
50
+ And they are implemented of course
51
+ ```ruby
52
+ all_basic_constraints.each do | constraint_name |
53
+ expect( BC.from_symbol(constraint_name) ).to be_a(BC::Constraint)
54
+ end
55
+ expect( BC.int_range(min: 0) ).to be_a(BC::Constraint)
56
+ expect( BC.limited_string(max: 10) ).to be_a(BC::Constraint)
39
57
  ```
40
58
 
59
+
41
60
  ## So we got the ball, now use it!
42
61
 
43
62
  The ball, BTW, is a `Lab42::Result` see [lab42_result gem](https://rubygems.org/gems/lab42_result/) for more info
@@ -74,17 +93,12 @@ Example: Constraints with and without defaults
74
93
 
75
94
  ```
76
95
 
77
- A detailed description of all constraints can be found
78
- [here](speculations/constraints.md)
79
-
80
- ## Context We are still in Beta and therefor
81
-
82
- Example: Some constraints are not yet implemented
83
-
96
+ Example: Avoiding the Exception, à la `Hash#fetch`
84
97
  ```ruby
85
- expect{ BC.all_digits_string }.to raise_error(NotImplementedError)
98
+ expect( BC.from_symbol(:positive_number).default{42} ).to eq(42)
86
99
  ```
87
100
 
101
+
88
102
  ## Context Time's a Sticky Wicket
89
103
 
90
104
  ... but as defaults are defined for runtime that shall really help us with that
@@ -112,9 +126,39 @@ Example: Illegal Dates
112
126
  ```ruby
113
127
  d.("2020-00-01") in [raised, message ]
114
128
  expect( raised ).to eq(error)
115
- expect( message ).to eq("2020-00-01 is not a legal date")
129
+ expect( message ).to eq(%{"2020-00-01" is not a legal date})
130
+ ```
131
+
132
+ ## Context Equality More Of A Tricky Sticker
133
+
134
+ And Most Surprisingly
135
+
136
+ ```ruby
137
+ expect(BC.bool).not_to eq(BC.bool)
116
138
  ```
117
139
 
140
+ However in many cases the name and the default say a lot about a constraint's behavior
141
+
142
+ Given
143
+ ```ruby
144
+ let(:natural) {BC.non_negative_int}
145
+ let(:def42) {BC.non_negative_int.set_default(42)}
146
+ ```
147
+
148
+ Then we can compare as follows
149
+ ```ruby
150
+ expect( natural.name ).to eq(BC.non_negative_int.name)
151
+ expect( natural.default ).to eq(0)
152
+ expect( def42.name ).to eq(BC.non_negative_int.name)
153
+ expect( def42.default ).to eq(42)
154
+ ```
155
+
156
+ ## Detailed Description of all Constraints
157
+
158
+ [date_and_time_constraints](date_and_time_constraints.md )
159
+ [div_constraints](div_constraints.md )
160
+ [string_constraints](string_constraints.md )
161
+
118
162
  # LICENSE
119
163
 
120
164
  Copyright 2020 Robert Dober robert.dober@gmail.com
@@ -2,37 +2,37 @@ require_relative "basic_constraints/implementation.rb"
2
2
  module Lab42
3
3
  module BasicConstraints extend self
4
4
  Constraints = {
5
- all_digits_string: :not_yet_implemented,
6
- alphanumeric_string: :not_yet_implemented,
5
+ all_digits_string: :all_digits_string,
6
+ alphanumeric_string: :alphanumeric_string,
7
7
 
8
8
  bool: ->(x){[false, true].include? x},
9
9
 
10
10
  date: :date,
11
- date_time: :not_yet_implemented,
12
- day: :not_yet_implemented,
11
+ date_time: :date_time,
12
+ day: :day,
13
13
 
14
- hour: :not_yet_implemented,
14
+ hour: :hour,
15
15
 
16
- lowercase_string: :not_yet_implemented,
16
+ int: Integer,
17
+ int_range: :int_range,
17
18
 
18
- minute: :not_yet_implemented,
19
- month: :not_yet_implemented,
19
+ limited_string: :limited_string,
20
+ lowercase_string: :lowercase_string,
21
+
22
+ minute: :minute,
23
+ month: :month,
20
24
 
21
- non_empty_string: :not_yet_implemented,
22
- non_negative_float: :not_yet_implemented,
23
25
  non_negative_int: :non_negative_int,
26
+ non_negative_number: :non_negative_number,
27
+ number: Numeric,
24
28
 
25
- non_negative_number: :not_yet_implemented,
26
-
27
- positive_float: :not_yet_implemented,
28
- positive_int: :not_yet_implemented,
29
+ positive_int: :positive_int,
29
30
  positive_number: :positive_number,
30
31
 
31
- second: :not_yet_implemented,
32
-
33
- time: :not_yet_implemented,
32
+ second: :second,
33
+ string: String,
34
34
 
35
- uppercase_string: :not_yet_implemented,
35
+ uppercase_string: :uppercase_string,
36
36
 
37
37
  year: :year
38
38
  }
@@ -49,6 +49,8 @@ module Lab42
49
49
  Constraint.new(name, &cons)
50
50
  when Symbol
51
51
  Implementation.send(cons, *args, **kwds)
52
+ when Class
53
+ Constraint.new(name){ cons === _1 }
52
54
  end
53
55
  end
54
56
 
@@ -5,18 +5,24 @@ module Lab42
5
5
  ConstraintError = Class.new RuntimeError
6
6
  MissingDefaultError = Class.new RuntimeError
7
7
  class Constraint
8
+ attr_reader :default, :name
8
9
 
9
10
  def call value
10
11
  return Result.ok if @constraint.(value)
11
12
  Result.error("#{_show value} is not a legal #{@name}", error: Lab42::BasicConstraints::ConstraintError)
12
13
  end
13
14
 
14
- def default
15
- case @default
16
- when Proc
17
- @default.()
15
+ def default &blk
16
+ if @has_default
17
+ case @default
18
+ when Proc
19
+ @default.()
20
+ else
21
+ @default
22
+ end
18
23
  else
19
- @default
24
+ return blk.() if blk
25
+ raise Lab42::BasicConstraints::MissingDefaultError, "Constraint #{name} has no predefined default"
20
26
  end
21
27
  end
22
28
 
@@ -27,6 +33,7 @@ module Lab42
27
33
  else
28
34
  blk
29
35
  end
36
+ @has_default = true
30
37
  self
31
38
  end
32
39
 
@@ -35,6 +42,8 @@ module Lab42
35
42
  case value
36
43
  when NilClass
37
44
  "nil"
45
+ when String
46
+ value.inspect
38
47
  else
39
48
  value
40
49
  end
@@ -42,9 +51,11 @@ module Lab42
42
51
 
43
52
  def initialize name, &blk
44
53
  @constraint = blk
45
- @default = -> {raise Lab42::BasicConstraints::MissingDefaultError, "Constraint #{name} has no predefined default"}
54
+ @has_default = false
55
+ @default = -> {}
46
56
  @name = name
47
57
  end
48
58
  end
49
59
  end
50
60
  end
61
+ require_relative "constraints/int"
@@ -0,0 +1,19 @@
1
+ require_relative "../helpers/range_helper.rb"
2
+ module Lab42
3
+ module BasicConstraints
4
+ class Constraint
5
+ class IntConstraint < Constraint
6
+
7
+ private
8
+ def initialize name, range: nil, min: nil, max: nil
9
+ super(name)
10
+ range = Helpers::RangeHelper.make_range!(range: range, min: min, max: max)
11
+ @constraint = -> (value) {
12
+ Integer === value && range === value
13
+ }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,29 @@
1
+ module Lab42
2
+ module BasicConstraints
3
+ module Helpers
4
+ module RangeHelper extend self
5
+
6
+ def make_range(range: nil, min: nil, max: nil)
7
+ return unless range || min || max
8
+ raise ArgumentError, "cannot provide min or max with range" if
9
+ range && (min || max)
10
+ range || _make_min_max_range(min, max)
11
+ end
12
+
13
+ def make_range!(range: nil, min: nil, max: nil)
14
+ make_range(range: range, min: min, max: max)
15
+ .tap do |range|
16
+ raise ArgumentError, "Must provide either range or min or max" unless range
17
+ end
18
+ end
19
+
20
+ private
21
+ def _make_min_max_range(min, max)
22
+ min ||= 0
23
+ max ||= Float::INFINITY
24
+ min..max
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,9 +1,38 @@
1
1
  require_relative "constraint"
2
+ require_relative "helpers/range_helper"
2
3
  module Lab42
3
4
  module BasicConstraints
4
5
 
5
6
  module Implementation extend self
6
7
 
8
+ IntConstraints = {
9
+ day: 1..31,
10
+ hour: 0..23,
11
+ minute: 0..59,
12
+ month: 1..12,
13
+ second: 0..59
14
+ }
15
+ IntConstraints.each do |constraint_name, constraint_range|
16
+ define_method constraint_name do
17
+ Constraint::IntConstraint.new(constraint_name, range: constraint_range)
18
+ end
19
+ end
20
+
21
+ def int_range(range: nil, min: nil, max: nil)
22
+ Constraint::IntConstraint.new(:int_range, range: range, min: min, max: max)
23
+ end
24
+
25
+ StringConstraints = {
26
+ all_digits_string: %r{\A\d*\z},
27
+ alphanumeric_string: %r{\A[[:alnum:]]*\z}
28
+ }
29
+
30
+ StringConstraints.each do |constraint_name, constraint_rgx|
31
+ define_method constraint_name do |size: nil, min: nil, max: nil|
32
+ _regex_constraint(constraint_name, constraint_rgx, size: size, min: min, max: max)
33
+ end
34
+ end
35
+
7
36
  def date default: nil
8
37
  require "date"
9
38
  Constraint.new(:date) do |value|
@@ -14,22 +43,55 @@ module Lab42
14
43
  end
15
44
  end
16
45
 
17
- def non_negative_int
18
- Constraint.new :non_negative_int do |value|
19
- Integer === value && value >= 0
46
+ def date_time default: nil
47
+ require "date"
48
+ Constraint.new(:date_time) do |value|
49
+ DateTime.parse(value) rescue false
20
50
  end
51
+ .set_default default do
52
+ DateTime.now.iso8601
53
+ end
54
+ end
55
+
56
+ def limited_string size: nil, min: nil, max: nil
57
+ size_range = Helpers::RangeHelper.make_range!(range: size, min: min, max: max)
58
+ Constraint.new(:limited_string) do |value|
59
+ String === value && size_range === value.size
60
+ end
61
+ end
62
+
63
+ def lowercase_string
64
+ Constraint.new :lowercase_string do |value|
65
+ String === value && value.downcase == value
66
+ end
67
+ end
68
+
69
+ def non_negative_int
70
+ Constraint::IntConstraint.new(:non_negative_int, min: 0)
21
71
  .set_default(0)
22
72
  end
23
73
 
74
+ def non_negative_number
75
+ Constraint.new :non_negative_number do |value|
76
+ Numeric === value && !(Complex === value) && value >= 0
77
+ end
78
+ end
79
+
80
+ def positive_int
81
+ Constraint::IntConstraint.new(:positive_int, min: 1)
82
+ .set_default(1)
83
+ end
84
+
24
85
  def positive_number
25
86
  Constraint.new :positive_number do |value|
26
- Integer === value && value > 0 ||
27
- Float === value && value > 0.0
87
+ Numeric === value && value > 0
28
88
  end
29
89
  end
30
90
 
31
- def not_yet_implemented
32
- raise NotImplementedError
91
+ def uppercase_string
92
+ Constraint.new :uppercase_string do |value|
93
+ String === value && value.upcase == value
94
+ end
33
95
  end
34
96
 
35
97
  def year
@@ -40,6 +102,22 @@ module Lab42
40
102
  Time.now.utc.year
41
103
  end
42
104
  end
105
+
106
+
107
+ private
108
+
109
+ def _regex_constraint(name, rgx, size: nil, min: nil, max: nil)
110
+ range_size = Helpers::RangeHelper.make_range(range: size, min: min, max: max)
111
+ if range_size
112
+ Constraint.new(name) do |value|
113
+ String === value && range_size === value.size && rgx === value
114
+ end
115
+ else
116
+ Constraint.new(name) do |value|
117
+ String === value && rgx === value
118
+ end
119
+ end
120
+ end
43
121
  end
44
122
  end
45
123
  end
@@ -1,5 +1,5 @@
1
1
  module Lab42
2
2
  module BasicConstraints
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lab42_basic_constraints
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Dober
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-18 00:00:00.000000000 Z
11
+ date: 2020-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lab42_result
@@ -91,6 +91,8 @@ files:
91
91
  - lib/lab42/basic_constraints.rb
92
92
  - lib/lab42/basic_constraints/alias.rb
93
93
  - lib/lab42/basic_constraints/constraint.rb
94
+ - lib/lab42/basic_constraints/constraints/int.rb
95
+ - lib/lab42/basic_constraints/helpers/range_helper.rb
94
96
  - lib/lab42/basic_constraints/implementation.rb
95
97
  - lib/lab42/basic_constraints/version.rb
96
98
  homepage: https://bitbucket.org/robertdober/lab42_basic_constraints