axiom-types 0.0.2 → 0.0.3
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.
- data/.rspec +1 -0
- data/.travis.yml +11 -16
- data/Gemfile +5 -2
- data/Gemfile.devtools +19 -24
- data/Guardfile +18 -10
- data/README.md +5 -28
- data/axiom-types.gemspec +2 -3
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/reek.yml +15 -13
- data/config/rubocop.yml +62 -0
- data/lib/axiom/types.rb +5 -3
- data/lib/axiom/types/boolean.rb +4 -2
- data/lib/axiom/types/collection.rb +5 -2
- data/lib/axiom/types/encodable.rb +1 -1
- data/lib/axiom/types/float.rb +3 -0
- data/lib/axiom/types/hash.rb +8 -3
- data/lib/axiom/types/length_comparable.rb +7 -4
- data/lib/axiom/types/numeric.rb +5 -0
- data/lib/axiom/types/object.rb +2 -2
- data/lib/axiom/types/support/infinity.rb +116 -0
- data/lib/axiom/types/support/options.rb +9 -18
- data/lib/axiom/types/time.rb +2 -2
- data/lib/axiom/types/type.rb +13 -9
- data/lib/axiom/types/value_comparable.rb +5 -2
- data/lib/axiom/types/version.rb +1 -1
- data/spec/unit/axiom/types/boolean/class_methods/infer_spec.rb +6 -0
- data/spec/unit/axiom/types/collection/class_methods/finalize_spec.rb +3 -3
- data/spec/unit/axiom/types/encodable/class_methods/extended_spec.rb +8 -7
- data/spec/unit/axiom/types/encodable/finalize_spec.rb +30 -8
- data/spec/unit/axiom/types/hash/class_methods/infer_spec.rb +17 -17
- data/spec/unit/axiom/types/infinity/class_methods/coerce_spec.rb +64 -0
- data/spec/unit/axiom/types/infinity/class_methods/spaceship_operator_spec.rb +67 -0
- data/spec/unit/axiom/types/infinity/class_methods/succ_spec.rb +11 -0
- data/spec/unit/axiom/types/length_comparable/class_methods/extended_spec.rb +17 -8
- data/spec/unit/axiom/types/negative_infinity/class_methods/spaceship_operator_spec.rb +67 -0
- data/spec/unit/axiom/types/object/class_methods/coercion_method_spec.rb +2 -2
- data/spec/unit/axiom/types/object/class_methods/infer_spec.rb +13 -1
- data/spec/unit/axiom/types/object/class_methods/primitive_spec.rb +4 -4
- data/spec/unit/axiom/types/options/accept_options_spec.rb +12 -4
- data/spec/unit/axiom/types/options/inherited_spec.rb +10 -36
- data/spec/unit/axiom/types/type/class_methods/constraint_spec.rb +6 -11
- data/spec/unit/axiom/types/type/class_methods/include_predicate_spec.rb +7 -7
- data/spec/unit/axiom/types/type/class_methods/includes_spec.rb +2 -2
- data/spec/unit/axiom/types/value_comparable/class_methods/extended_spec.rb +17 -8
- metadata +29 -31
- checksums.yaml +0 -7
data/lib/axiom/types/float.rb
CHANGED
data/lib/axiom/types/hash.rb
CHANGED
@@ -14,6 +14,9 @@ module Axiom
|
|
14
14
|
|
15
15
|
# Infer the type of the object
|
16
16
|
#
|
17
|
+
# @example
|
18
|
+
# type = Axiom::Types.infer(object)
|
19
|
+
#
|
17
20
|
# @param [Object] object
|
18
21
|
#
|
19
22
|
# @return [Class<Axiom::Types::Hash>]
|
@@ -60,6 +63,7 @@ module Axiom
|
|
60
63
|
value_type = Types.infer(value) || Object
|
61
64
|
infer_from(key_type, value_type) || new_from(key_type, value_type)
|
62
65
|
end
|
66
|
+
private_class_method :infer_from_primitive_instance
|
63
67
|
|
64
68
|
# Infer the type from the key_type and value_type
|
65
69
|
#
|
@@ -77,6 +81,7 @@ module Axiom
|
|
77
81
|
self
|
78
82
|
end
|
79
83
|
end
|
84
|
+
private_class_method :infer_from
|
80
85
|
|
81
86
|
# Instantiate a new type from a base type
|
82
87
|
#
|
@@ -92,6 +97,7 @@ module Axiom
|
|
92
97
|
def self.new_from(key_type, value_type)
|
93
98
|
new { key_type(key_type).value_type(value_type) } if base?
|
94
99
|
end
|
100
|
+
private_class_method :new_from
|
95
101
|
|
96
102
|
# Test if the type is a base type
|
97
103
|
#
|
@@ -101,6 +107,7 @@ module Axiom
|
|
101
107
|
def self.base?
|
102
108
|
equal?(Hash)
|
103
109
|
end
|
110
|
+
private_class_method :base?
|
104
111
|
|
105
112
|
# Add a constraints for the key and value
|
106
113
|
#
|
@@ -114,9 +121,7 @@ module Axiom
|
|
114
121
|
end
|
115
122
|
end
|
116
123
|
end
|
117
|
-
|
118
|
-
private_class_method :infer_from_primitive_instance, :infer_from,
|
119
|
-
:new_from, :base?, :matches_key_and_value_types
|
124
|
+
private_class_method :matches_key_and_value_types
|
120
125
|
|
121
126
|
end # class Hash
|
122
127
|
end # module Types
|
@@ -17,7 +17,11 @@ module Axiom
|
|
17
17
|
# @api private
|
18
18
|
def self.extended(descendant)
|
19
19
|
super
|
20
|
-
descendant.
|
20
|
+
descendant.class_eval do
|
21
|
+
accept_options :minimum_length, :maximum_length
|
22
|
+
minimum_length Infinity
|
23
|
+
maximum_length NegativeInfinity
|
24
|
+
end
|
21
25
|
end
|
22
26
|
|
23
27
|
# Finalize by setting up a length range constraint
|
@@ -41,9 +45,8 @@ module Axiom
|
|
41
45
|
#
|
42
46
|
# @api private
|
43
47
|
def has_length_within_range
|
44
|
-
|
45
|
-
|
46
|
-
end
|
48
|
+
range = minimum_length..maximum_length
|
49
|
+
constraint { |object| range.cover?(object.length) }
|
47
50
|
end
|
48
51
|
|
49
52
|
end # module LengthComparable
|
data/lib/axiom/types/numeric.rb
CHANGED
@@ -5,9 +5,14 @@ module Axiom
|
|
5
5
|
|
6
6
|
# Represents a numeric type
|
7
7
|
class Numeric < Object
|
8
|
+
extend ValueComparable
|
9
|
+
|
8
10
|
primitive ::Numeric
|
9
11
|
coercion_method :to_numeric
|
10
12
|
|
13
|
+
minimum NegativeInfinity
|
14
|
+
maximum Infinity
|
15
|
+
|
11
16
|
end # class Numeric
|
12
17
|
end # module Types
|
13
18
|
end # module Axiom
|
data/lib/axiom/types/object.rb
CHANGED
@@ -51,6 +51,7 @@ module Axiom
|
|
51
51
|
self if object.respond_to?(:ancestors) &&
|
52
52
|
object.ancestors.include?(primitive)
|
53
53
|
end
|
54
|
+
private_class_method :infer_from_primitive_class
|
54
55
|
|
55
56
|
# Add a constraint for the primitive
|
56
57
|
#
|
@@ -60,8 +61,7 @@ module Axiom
|
|
60
61
|
def self.inherits_from_primitive
|
61
62
|
constraint(&primitive.method(:===))
|
62
63
|
end
|
63
|
-
|
64
|
-
private_class_method :infer_from_primitive_class, :inherits_from_primitive
|
64
|
+
private_class_method :inherits_from_primitive
|
65
65
|
|
66
66
|
end # class Object
|
67
67
|
end # module Types
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Axiom
|
4
|
+
module Types
|
5
|
+
|
6
|
+
# Represent an infinite number
|
7
|
+
class Infinity < BasicObject
|
8
|
+
extend ::Comparable
|
9
|
+
|
10
|
+
# Test the number against infinity
|
11
|
+
#
|
12
|
+
# @param [Numeric, Infinity] other
|
13
|
+
#
|
14
|
+
# @return [0]
|
15
|
+
# returned if the other object is infinity
|
16
|
+
# @return [1]
|
17
|
+
# returned if the other object is something other than infinity
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
def self.<=>(other)
|
21
|
+
case other
|
22
|
+
when ::BigDecimal then 1
|
23
|
+
when number then 0
|
24
|
+
when ::Numeric, inverse.singleton_class then 1
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Coerce a number into an Infinity class for comparison
|
29
|
+
#
|
30
|
+
# @param [::Numeric] other
|
31
|
+
#
|
32
|
+
# @return [Array(Infinity, Infinity)]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
def self.coerce(other)
|
36
|
+
case other
|
37
|
+
when ::BigDecimal then [inverse, self]
|
38
|
+
when number then [self, self]
|
39
|
+
when ::Numeric then [inverse, self]
|
40
|
+
else
|
41
|
+
raise ::TypeError, "#{other.class} cannot be coerced"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return the next successive object, which is always self
|
46
|
+
#
|
47
|
+
# @return [Class<Infinity>]
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
def self.succ
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
# The inverse of infinity
|
55
|
+
#
|
56
|
+
# @return [Class<NegativeInfinity>]
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
def self.inverse
|
60
|
+
NegativeInfinity
|
61
|
+
end
|
62
|
+
private_class_method :inverse
|
63
|
+
|
64
|
+
# The numeric representation of infinity
|
65
|
+
#
|
66
|
+
# @return [Float]
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
def self.number
|
70
|
+
::Float::INFINITY
|
71
|
+
end
|
72
|
+
private_class_method :number
|
73
|
+
|
74
|
+
end # class Infinity
|
75
|
+
|
76
|
+
# Represent a negative infinite number
|
77
|
+
class NegativeInfinity < Infinity
|
78
|
+
|
79
|
+
# Test the number against negative infinity
|
80
|
+
#
|
81
|
+
# @param [Numeric, Infinity] _other
|
82
|
+
#
|
83
|
+
# @return [0]
|
84
|
+
# returned if the other object is negative infinity
|
85
|
+
# @return [-1]
|
86
|
+
# returned if the other object is not negative infinity
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
def self.<=>(_other)
|
90
|
+
comparison = super
|
91
|
+
-comparison if comparison
|
92
|
+
end
|
93
|
+
|
94
|
+
# The inverse of negative infinity
|
95
|
+
#
|
96
|
+
# @return [Class<Infinity>]
|
97
|
+
#
|
98
|
+
# @api private
|
99
|
+
def self.inverse
|
100
|
+
Infinity
|
101
|
+
end
|
102
|
+
private_class_method :inverse
|
103
|
+
|
104
|
+
# The numeric representation of negative infinity
|
105
|
+
#
|
106
|
+
# @return [Float]
|
107
|
+
#
|
108
|
+
# @api private
|
109
|
+
def self.number
|
110
|
+
-::Float::INFINITY
|
111
|
+
end
|
112
|
+
private_class_method :number
|
113
|
+
|
114
|
+
end # class NegativeInfinity
|
115
|
+
end # module Types
|
116
|
+
end # module Axiom
|
@@ -22,15 +22,15 @@ module Axiom
|
|
22
22
|
def accept_options(*new_options)
|
23
23
|
(new_options - accepted_options).each do |new_option|
|
24
24
|
assert_method_available(new_option)
|
25
|
-
add_accepted_option(new_option)
|
26
25
|
define_option_method(new_option)
|
26
|
+
setup_option(new_option)
|
27
27
|
end
|
28
28
|
self
|
29
29
|
end
|
30
30
|
|
31
31
|
protected
|
32
32
|
|
33
|
-
#
|
33
|
+
# Set up the option in the current class and descendants
|
34
34
|
#
|
35
35
|
# @param [Symbol] new_option
|
36
36
|
# new option to be added
|
@@ -38,7 +38,8 @@ module Axiom
|
|
38
38
|
# @return [self]
|
39
39
|
#
|
40
40
|
# @api private
|
41
|
-
def
|
41
|
+
def setup_option(new_option)
|
42
|
+
instance_variable_set(:"@#{new_option}", nil)
|
42
43
|
accepted_options << new_option
|
43
44
|
descendants.each do |descendant|
|
44
45
|
descendant.send(__method__, new_option)
|
@@ -46,18 +47,6 @@ module Axiom
|
|
46
47
|
self
|
47
48
|
end
|
48
49
|
|
49
|
-
# Set the option if it is not already set
|
50
|
-
#
|
51
|
-
# @param [Symbol] option
|
52
|
-
# @param [Object] value
|
53
|
-
#
|
54
|
-
# @return [self]
|
55
|
-
#
|
56
|
-
# @api private
|
57
|
-
def set_option(option, value)
|
58
|
-
public_send(option, value) unless public_send(option)
|
59
|
-
end
|
60
|
-
|
61
50
|
private
|
62
51
|
|
63
52
|
# Adds descendant to descendants array and inherits default options
|
@@ -70,7 +59,7 @@ module Axiom
|
|
70
59
|
def inherited(descendant)
|
71
60
|
super
|
72
61
|
options.each do |option, value|
|
73
|
-
descendant.
|
62
|
+
descendant.setup_option(option).public_send(option, value)
|
74
63
|
end
|
75
64
|
end
|
76
65
|
|
@@ -116,8 +105,10 @@ module Axiom
|
|
116
105
|
# @api private
|
117
106
|
def assert_method_available(name)
|
118
107
|
return unless respond_to?(name)
|
119
|
-
raise
|
108
|
+
raise(
|
109
|
+
ReservedMethodError,
|
120
110
|
"method named `#{name.inspect}` is already defined"
|
111
|
+
)
|
121
112
|
end
|
122
113
|
|
123
114
|
# Adds a reader/writer method for the give option name
|
@@ -128,7 +119,7 @@ module Axiom
|
|
128
119
|
#
|
129
120
|
# @api private
|
130
121
|
def define_option_method(name)
|
131
|
-
ivar = "@#{name}"
|
122
|
+
ivar = :"@#{name}"
|
132
123
|
define_singleton_method(name) do |*args|
|
133
124
|
return instance_variable_get(ivar) if args.empty?
|
134
125
|
instance_variable_set(ivar, *args)
|
data/lib/axiom/types/time.rb
CHANGED
@@ -8,8 +8,8 @@ module Axiom
|
|
8
8
|
extend ValueComparable
|
9
9
|
|
10
10
|
# The minimum and maximum seconds for Time on 32-bit systems
|
11
|
-
MINIMUM_SECONDS = -
|
12
|
-
MAXIMUM_SECONDS =
|
11
|
+
MINIMUM_SECONDS = -2**31
|
12
|
+
MAXIMUM_SECONDS = 2**31 - 1
|
13
13
|
|
14
14
|
primitive ::Time
|
15
15
|
coercion_method :to_time
|
data/lib/axiom/types/type.rb
CHANGED
@@ -30,7 +30,7 @@ module Axiom
|
|
30
30
|
# @example
|
31
31
|
# type = Axiom::Types::Type.new # => Axiom::Types::Type
|
32
32
|
#
|
33
|
-
# @param [#call]
|
33
|
+
# @param [Array(#call)] args
|
34
34
|
# optional constraint for the new type
|
35
35
|
#
|
36
36
|
# @yield [object]
|
@@ -44,9 +44,9 @@ module Axiom
|
|
44
44
|
# @return [Class<Axiom::Types::Type>]
|
45
45
|
#
|
46
46
|
# @api public
|
47
|
-
def self.new(
|
47
|
+
def self.new(*args, &block)
|
48
48
|
type = ::Class.new(self, &block)
|
49
|
-
type.constraint(
|
49
|
+
type.constraint(*args)
|
50
50
|
type.finalize
|
51
51
|
end
|
52
52
|
|
@@ -82,6 +82,9 @@ module Axiom
|
|
82
82
|
constraint.call(object)
|
83
83
|
end
|
84
84
|
|
85
|
+
# Silence warnings when redeclaring constraint
|
86
|
+
singleton_class.class_eval { undef_method :constraint }
|
87
|
+
|
85
88
|
# Add a constraint to the type
|
86
89
|
#
|
87
90
|
# @example with an argument
|
@@ -136,12 +139,13 @@ module Axiom
|
|
136
139
|
#
|
137
140
|
# @api private
|
138
141
|
def self.add_constraint(constraint)
|
139
|
-
current
|
140
|
-
@constraint =
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
142
|
+
current = self.constraint
|
143
|
+
@constraint =
|
144
|
+
if current
|
145
|
+
->(object) { constraint.call(object) && current.call(object) }
|
146
|
+
else
|
147
|
+
constraint
|
148
|
+
end
|
145
149
|
end
|
146
150
|
|
147
151
|
private_class_method :add_constraint
|
@@ -17,7 +17,9 @@ module Axiom
|
|
17
17
|
# @api private
|
18
18
|
def self.extended(descendant)
|
19
19
|
super
|
20
|
-
descendant.
|
20
|
+
descendant.class_eval do
|
21
|
+
accept_options :minimum, :maximum
|
22
|
+
end
|
21
23
|
end
|
22
24
|
|
23
25
|
# Finalize by setting up a value range constraint
|
@@ -41,7 +43,8 @@ module Axiom
|
|
41
43
|
#
|
42
44
|
# @api private
|
43
45
|
def has_value_within_range
|
44
|
-
|
46
|
+
range = minimum..maximum
|
47
|
+
constraint(range.method(:cover?))
|
45
48
|
end
|
46
49
|
|
47
50
|
end # module ValueComparable
|
data/lib/axiom/types/version.rb
CHANGED
@@ -16,7 +16,7 @@ describe Axiom::Types::Collection, '.finalize' do
|
|
16
16
|
its(:constraint) { should be_frozen }
|
17
17
|
|
18
18
|
it 'adds a constraint that returns true for a collection' do
|
19
|
-
should include([
|
19
|
+
should include([Object.new])
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'adds a constraint that returns false for a non-collection' do
|
@@ -39,11 +39,11 @@ describe Axiom::Types::Collection, '.finalize' do
|
|
39
39
|
its(:constraint) { should be_frozen }
|
40
40
|
|
41
41
|
it 'adds a constraint that returns true for a valid member' do
|
42
|
-
should include([
|
42
|
+
should include([member])
|
43
43
|
end
|
44
44
|
|
45
45
|
it 'adds a constraint that returns false for an invalid member' do
|
46
|
-
should_not include([
|
46
|
+
should_not include([member.to_s])
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|