axiom-types 0.0.1 → 0.0.2
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 +6 -14
- data/.gitignore +1 -1
- data/.ruby-gemset +1 -0
- data/.travis.yml +2 -6
- data/CONTRIBUTING.md +11 -0
- data/Gemfile +1 -3
- data/Gemfile.devtools +17 -19
- data/README.md +1 -8
- data/Rakefile +0 -1
- data/TODO +0 -6
- data/axiom-types.gemspec +12 -13
- data/config/devtools.yml +2 -0
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/config/reek.yml +67 -60
- data/lib/axiom/types.rb +26 -1
- data/lib/axiom/types/array.rb +12 -1
- data/lib/axiom/types/boolean.rb +17 -0
- data/lib/axiom/types/collection.rb +133 -0
- data/lib/axiom/types/hash.rb +80 -2
- data/lib/axiom/types/object.rb +34 -2
- data/lib/axiom/types/set.rb +12 -1
- data/lib/axiom/types/support/options.rb +13 -1
- data/lib/axiom/types/type.rb +17 -2
- data/lib/axiom/types/version.rb +1 -1
- data/spec/spec_helper.rb +14 -5
- data/spec/unit/axiom/types/array/class_methods/infer_spec.rb +99 -0
- data/spec/unit/axiom/types/boolean/class_methods/infer_spec.rb +33 -0
- data/spec/unit/axiom/types/class_methods/finalize_spec.rb +2 -7
- data/spec/unit/axiom/types/class_methods/infer_spec.rb +54 -0
- data/spec/unit/axiom/types/collection/class_methods/finalize_spec.rb +49 -0
- data/spec/unit/axiom/types/collection/class_methods/infer_spec.rb +111 -0
- data/spec/unit/axiom/types/hash/class_methods/infer_spec.rb +175 -0
- data/spec/unit/axiom/types/object/class_methods/infer_spec.rb +52 -0
- data/spec/unit/axiom/types/options/inherited_spec.rb +36 -10
- data/spec/unit/axiom/types/set/class_methods/infer_spec.rb +99 -0
- data/spec/unit/axiom/types/type/class_methods/constraint_spec.rb +66 -4
- data/spec/unit/axiom/types/type/class_methods/infer_spec.rb +25 -0
- data/spec/unit/axiom/types/type/class_methods/new_spec.rb +4 -0
- metadata +44 -44
- data/.rvmrc +0 -1
data/lib/axiom/types/array.rb
CHANGED
@@ -4,10 +4,21 @@ module Axiom
|
|
4
4
|
module Types
|
5
5
|
|
6
6
|
# Represents an array type
|
7
|
-
class Array <
|
7
|
+
class Array < Collection
|
8
8
|
primitive ::Array
|
9
9
|
coercion_method :to_array
|
10
10
|
|
11
|
+
# Test if the type is a base type
|
12
|
+
#
|
13
|
+
# @return [Boolean]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
def self.base?
|
17
|
+
equal?(Array)
|
18
|
+
end
|
19
|
+
|
20
|
+
private_class_method :base?
|
21
|
+
|
11
22
|
end # class Array
|
12
23
|
end # module Types
|
13
24
|
end # module Axiom
|
data/lib/axiom/types/boolean.rb
CHANGED
@@ -9,6 +9,23 @@ module Axiom
|
|
9
9
|
|
10
10
|
includes true, false
|
11
11
|
|
12
|
+
# Infer the type if the primitive class matches
|
13
|
+
#
|
14
|
+
# @param [Object] object
|
15
|
+
#
|
16
|
+
# @return [Class<Axiom::Types::Boolean>]
|
17
|
+
# returned if the primitive class matches
|
18
|
+
# @return [nil]
|
19
|
+
# returned if the primitive class does not match
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
def self.infer_from_primitive_class(object)
|
23
|
+
self if TrueClass.singleton_class === object ||
|
24
|
+
FalseClass.singleton_class === object
|
25
|
+
end
|
26
|
+
|
27
|
+
private_class_method :infer_from_primitive_class
|
28
|
+
|
12
29
|
end # class Boolean
|
13
30
|
end # module Types
|
14
31
|
end # module Axiom
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Axiom
|
4
|
+
module Types
|
5
|
+
|
6
|
+
# Represents a collection type
|
7
|
+
class Collection < Object
|
8
|
+
primitive ::Enumerable
|
9
|
+
accept_options :member_type
|
10
|
+
|
11
|
+
member_type Object
|
12
|
+
|
13
|
+
# Infer the type of the object
|
14
|
+
#
|
15
|
+
# @example with a type
|
16
|
+
# Axiom::Types::Array.infer(Axiom::Types::Array)
|
17
|
+
# # => Axiom::Types::Array
|
18
|
+
#
|
19
|
+
# @example with a primitive class
|
20
|
+
# Axiom::Types::Collection.infer(::Array)
|
21
|
+
# # => Axiom::Types::Array
|
22
|
+
#
|
23
|
+
# @example with a primitive instance
|
24
|
+
# Axiom::Types::Array.infer(Array[])
|
25
|
+
# # => Axiom::Types::Array
|
26
|
+
#
|
27
|
+
# @example with a primitive instance and a member type
|
28
|
+
# Axiom::Types::Collection.infer(Array[Axiom::Types::String])
|
29
|
+
# # => Axiom::Types::Array subclass w/String member type
|
30
|
+
#
|
31
|
+
# @example with a primitive instance and a member primitive
|
32
|
+
# Axiom::Types::Collection.infer(Array[String])
|
33
|
+
# # => Axiom::Types::Array subclass w/String member type
|
34
|
+
#
|
35
|
+
# @param [Object] object
|
36
|
+
#
|
37
|
+
# @return [Class<Axiom::Types::Collection>]
|
38
|
+
# returned if the type matches
|
39
|
+
# @return [nil]
|
40
|
+
# returned if the type does not match
|
41
|
+
#
|
42
|
+
# @api public
|
43
|
+
def self.infer(object)
|
44
|
+
case object
|
45
|
+
when primitive
|
46
|
+
infer_from_primitive_instance(object)
|
47
|
+
else
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Finalize by setting up constraints for the member
|
53
|
+
#
|
54
|
+
# @return [Class<Axiom::Types::Collection>]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
def self.finalize
|
58
|
+
return self if frozen?
|
59
|
+
member_type.finalize
|
60
|
+
matches_member_type
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
# Infer the type from a primitive instance
|
65
|
+
#
|
66
|
+
# @param [Object] object
|
67
|
+
#
|
68
|
+
# @return [Class<Axiom::Types::Collection>]
|
69
|
+
# returned if the primitive instance matches
|
70
|
+
# @return [nil]
|
71
|
+
# returned if the primitive instance does not match
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
def self.infer_from_primitive_instance(object)
|
75
|
+
member = object.first
|
76
|
+
member_type = Types.infer(member) || Object
|
77
|
+
infer_from(member_type) || new_from(member_type)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Infer the type from the member_type
|
81
|
+
#
|
82
|
+
# @param [Class<Axiom::Types::Object>] member_type
|
83
|
+
#
|
84
|
+
# @return [Class<Axiom::Types::Collection>]
|
85
|
+
# returned if the member_type matches
|
86
|
+
# @return [nil]
|
87
|
+
# returned if the member_type does not match
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
def self.infer_from(member_type)
|
91
|
+
self if self.member_type.equal?(member_type)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Instantiate a new type from a base type
|
95
|
+
#
|
96
|
+
# @param [Class<Axiom::Types::Object>] member_type
|
97
|
+
#
|
98
|
+
# @return [Class<Axiom::Types::Collection>]
|
99
|
+
# returned if a base type
|
100
|
+
# @return [nil]
|
101
|
+
# returned if not a base type
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
def self.new_from(member_type)
|
105
|
+
new { member_type(member_type) } if base?
|
106
|
+
end
|
107
|
+
|
108
|
+
# Test if the type is a base type
|
109
|
+
#
|
110
|
+
# @return [Boolean]
|
111
|
+
#
|
112
|
+
# @api private
|
113
|
+
def self.base?
|
114
|
+
# noop
|
115
|
+
end
|
116
|
+
|
117
|
+
# Add a constraints for the member
|
118
|
+
#
|
119
|
+
# @return [undefined]
|
120
|
+
#
|
121
|
+
# @api private
|
122
|
+
def self.matches_member_type
|
123
|
+
constraint do |object|
|
124
|
+
object.all? { |member| member_type.include?(member) }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
private_class_method :infer_from_primitive_instance, :infer_from,
|
129
|
+
:new_from, :base?, :matches_member_type
|
130
|
+
|
131
|
+
end # class Collection
|
132
|
+
end # module Types
|
133
|
+
end # module Axiom
|
data/lib/axiom/types/hash.rb
CHANGED
@@ -12,9 +12,28 @@ module Axiom
|
|
12
12
|
key_type Object
|
13
13
|
value_type Object
|
14
14
|
|
15
|
+
# Infer the type of the object
|
16
|
+
#
|
17
|
+
# @param [Object] object
|
18
|
+
#
|
19
|
+
# @return [Class<Axiom::Types::Hash>]
|
20
|
+
# returned if the type matches
|
21
|
+
# @return [nil]
|
22
|
+
# returned if the type does not match
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
def self.infer(object)
|
26
|
+
case object
|
27
|
+
when primitive
|
28
|
+
infer_from_primitive_instance(object)
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
15
34
|
# Finalize by setting up constraints for the key and value
|
16
35
|
#
|
17
|
-
# @return [Axiom::Types::
|
36
|
+
# @return [Class<Axiom::Types::Hash>]
|
18
37
|
#
|
19
38
|
# @api private
|
20
39
|
def self.finalize
|
@@ -25,6 +44,64 @@ module Axiom
|
|
25
44
|
super
|
26
45
|
end
|
27
46
|
|
47
|
+
# Infer the type from a primitive instance
|
48
|
+
#
|
49
|
+
# @param [Object] object
|
50
|
+
#
|
51
|
+
# @return [Class<Axiom::Types::Hash>]
|
52
|
+
# returned if the primitive instance matches
|
53
|
+
# @return [nil]
|
54
|
+
# returned if the primitive instance does not match
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
def self.infer_from_primitive_instance(object)
|
58
|
+
key, value = object.first
|
59
|
+
key_type = Types.infer(key) || Object
|
60
|
+
value_type = Types.infer(value) || Object
|
61
|
+
infer_from(key_type, value_type) || new_from(key_type, value_type)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Infer the type from the key_type and value_type
|
65
|
+
#
|
66
|
+
# @param [Class<Axiom::Types::Object>] key_type
|
67
|
+
# @param [Class<Axiom::Types::Object>] value_type
|
68
|
+
#
|
69
|
+
# @return [Class<Axiom::Types::Hash>]
|
70
|
+
# returned if the key_type and value_type match
|
71
|
+
# @return [nil]
|
72
|
+
# returned if the key_type and value_type do not match
|
73
|
+
#
|
74
|
+
# @api private
|
75
|
+
def self.infer_from(key_type, value_type)
|
76
|
+
if self.key_type.equal?(key_type) && self.value_type.equal?(value_type)
|
77
|
+
self
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Instantiate a new type from a base type
|
82
|
+
#
|
83
|
+
# @param [Class<Axiom::Types::Object>] key_type
|
84
|
+
# @param [Class<Axiom::Types::Object>] value_type
|
85
|
+
#
|
86
|
+
# @return [Class<Axiom::Types::Hash>]
|
87
|
+
# returned if a base type
|
88
|
+
# @return [nil]
|
89
|
+
# returned if not a base type
|
90
|
+
#
|
91
|
+
# @api private
|
92
|
+
def self.new_from(key_type, value_type)
|
93
|
+
new { key_type(key_type).value_type(value_type) } if base?
|
94
|
+
end
|
95
|
+
|
96
|
+
# Test if the type is a base type
|
97
|
+
#
|
98
|
+
# @return [Boolean]
|
99
|
+
#
|
100
|
+
# @api private
|
101
|
+
def self.base?
|
102
|
+
equal?(Hash)
|
103
|
+
end
|
104
|
+
|
28
105
|
# Add a constraints for the key and value
|
29
106
|
#
|
30
107
|
# @return [undefined]
|
@@ -38,7 +115,8 @@ module Axiom
|
|
38
115
|
end
|
39
116
|
end
|
40
117
|
|
41
|
-
private_class_method :
|
118
|
+
private_class_method :infer_from_primitive_instance, :infer_from,
|
119
|
+
:new_from, :base?, :matches_key_and_value_types
|
42
120
|
|
43
121
|
end # class Hash
|
44
122
|
end # module Types
|
data/lib/axiom/types/object.rb
CHANGED
@@ -9,9 +9,26 @@ module Axiom
|
|
9
9
|
primitive ::Object.superclass || ::Object
|
10
10
|
coercion_method :to_object
|
11
11
|
|
12
|
+
# Infer the type of the object
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# Axiom::Types::Object.infer(::Object) # => Axiom::Types::Object
|
16
|
+
#
|
17
|
+
# @param [Object] object
|
18
|
+
#
|
19
|
+
# @return [Class<Axiom::Types::Object>]
|
20
|
+
# returned if the type matches
|
21
|
+
# @return [nil]
|
22
|
+
# returned if the type does not match
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
def self.infer(object)
|
26
|
+
super || infer_from_primitive_class(object)
|
27
|
+
end
|
28
|
+
|
12
29
|
# Finalize by setting up a primitive constraint
|
13
30
|
#
|
14
|
-
# @return [Axiom::Types::Object]
|
31
|
+
# @return [Class<Axiom::Types::Object>]
|
15
32
|
#
|
16
33
|
# @api private
|
17
34
|
def self.finalize
|
@@ -20,6 +37,21 @@ module Axiom
|
|
20
37
|
super
|
21
38
|
end
|
22
39
|
|
40
|
+
# Infer the type if the primitive class matches
|
41
|
+
#
|
42
|
+
# @param [Object] object
|
43
|
+
#
|
44
|
+
# @return [Class<Axiom::Types::Object>]
|
45
|
+
# returned if the primitive class matches
|
46
|
+
# @return [nil]
|
47
|
+
# returned if the primitive class does not match
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
def self.infer_from_primitive_class(object)
|
51
|
+
self if object.respond_to?(:ancestors) &&
|
52
|
+
object.ancestors.include?(primitive)
|
53
|
+
end
|
54
|
+
|
23
55
|
# Add a constraint for the primitive
|
24
56
|
#
|
25
57
|
# @return [undefined]
|
@@ -29,7 +61,7 @@ module Axiom
|
|
29
61
|
constraint(&primitive.method(:===))
|
30
62
|
end
|
31
63
|
|
32
|
-
private_class_method :inherits_from_primitive
|
64
|
+
private_class_method :infer_from_primitive_class, :inherits_from_primitive
|
33
65
|
|
34
66
|
end # class Object
|
35
67
|
end # module Types
|
data/lib/axiom/types/set.rb
CHANGED
@@ -4,10 +4,21 @@ module Axiom
|
|
4
4
|
module Types
|
5
5
|
|
6
6
|
# Represents a set type
|
7
|
-
class Set <
|
7
|
+
class Set < Collection
|
8
8
|
primitive ::Set
|
9
9
|
coercion_method :to_set
|
10
10
|
|
11
|
+
# Test if the type is a base type
|
12
|
+
#
|
13
|
+
# @return [Boolean]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
def self.base?
|
17
|
+
equal?(Set)
|
18
|
+
end
|
19
|
+
|
20
|
+
private_class_method :base?
|
21
|
+
|
11
22
|
end # class Set
|
12
23
|
end # module Types
|
13
24
|
end # module Axiom
|
@@ -46,6 +46,18 @@ module Axiom
|
|
46
46
|
self
|
47
47
|
end
|
48
48
|
|
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
|
+
|
49
61
|
private
|
50
62
|
|
51
63
|
# Adds descendant to descendants array and inherits default options
|
@@ -58,7 +70,7 @@ module Axiom
|
|
58
70
|
def inherited(descendant)
|
59
71
|
super
|
60
72
|
options.each do |option, value|
|
61
|
-
descendant.add_accepted_option(option).
|
73
|
+
descendant.add_accepted_option(option).set_option(option, value)
|
62
74
|
end
|
63
75
|
end
|
64
76
|
|
data/lib/axiom/types/type.rb
CHANGED
@@ -10,6 +10,21 @@ module Axiom
|
|
10
10
|
accept_options :constraint
|
11
11
|
constraint Tautology
|
12
12
|
|
13
|
+
# Infer the type of the object
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# type = Axiom::Types::Type.infer(Axiom::Types::Integer)
|
17
|
+
# # => Axiom::Types::Integer
|
18
|
+
#
|
19
|
+
# @param [Object] object
|
20
|
+
#
|
21
|
+
# @return [Class<Axiom::Types::Type>]
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def self.infer(object)
|
25
|
+
self if equal?(object)
|
26
|
+
end
|
27
|
+
|
13
28
|
# Instantiate a new Axiom::Types::Type subclass
|
14
29
|
#
|
15
30
|
# @example
|
@@ -37,7 +52,7 @@ module Axiom
|
|
37
52
|
|
38
53
|
# Finalize by deep freezing
|
39
54
|
#
|
40
|
-
# @return [Axiom::Types::Type]
|
55
|
+
# @return [Class<Axiom::Types::Type>]
|
41
56
|
#
|
42
57
|
# @api private
|
43
58
|
def self.finalize
|
@@ -89,7 +104,7 @@ module Axiom
|
|
89
104
|
# @yieldreturn [Boolean]
|
90
105
|
# true if the object matches the type constraint
|
91
106
|
#
|
92
|
-
# @return [
|
107
|
+
# @return [Class<Axiom::Types::Type>]
|
93
108
|
#
|
94
109
|
# @api public
|
95
110
|
def self.constraint(constraint = Undefined, &block)
|