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.
Files changed (41) hide show
  1. checksums.yaml +6 -14
  2. data/.gitignore +1 -1
  3. data/.ruby-gemset +1 -0
  4. data/.travis.yml +2 -6
  5. data/CONTRIBUTING.md +11 -0
  6. data/Gemfile +1 -3
  7. data/Gemfile.devtools +17 -19
  8. data/README.md +1 -8
  9. data/Rakefile +0 -1
  10. data/TODO +0 -6
  11. data/axiom-types.gemspec +12 -13
  12. data/config/devtools.yml +2 -0
  13. data/config/flay.yml +1 -1
  14. data/config/flog.yml +1 -1
  15. data/config/reek.yml +67 -60
  16. data/lib/axiom/types.rb +26 -1
  17. data/lib/axiom/types/array.rb +12 -1
  18. data/lib/axiom/types/boolean.rb +17 -0
  19. data/lib/axiom/types/collection.rb +133 -0
  20. data/lib/axiom/types/hash.rb +80 -2
  21. data/lib/axiom/types/object.rb +34 -2
  22. data/lib/axiom/types/set.rb +12 -1
  23. data/lib/axiom/types/support/options.rb +13 -1
  24. data/lib/axiom/types/type.rb +17 -2
  25. data/lib/axiom/types/version.rb +1 -1
  26. data/spec/spec_helper.rb +14 -5
  27. data/spec/unit/axiom/types/array/class_methods/infer_spec.rb +99 -0
  28. data/spec/unit/axiom/types/boolean/class_methods/infer_spec.rb +33 -0
  29. data/spec/unit/axiom/types/class_methods/finalize_spec.rb +2 -7
  30. data/spec/unit/axiom/types/class_methods/infer_spec.rb +54 -0
  31. data/spec/unit/axiom/types/collection/class_methods/finalize_spec.rb +49 -0
  32. data/spec/unit/axiom/types/collection/class_methods/infer_spec.rb +111 -0
  33. data/spec/unit/axiom/types/hash/class_methods/infer_spec.rb +175 -0
  34. data/spec/unit/axiom/types/object/class_methods/infer_spec.rb +52 -0
  35. data/spec/unit/axiom/types/options/inherited_spec.rb +36 -10
  36. data/spec/unit/axiom/types/set/class_methods/infer_spec.rb +99 -0
  37. data/spec/unit/axiom/types/type/class_methods/constraint_spec.rb +66 -4
  38. data/spec/unit/axiom/types/type/class_methods/infer_spec.rb +25 -0
  39. data/spec/unit/axiom/types/type/class_methods/new_spec.rb +4 -0
  40. metadata +44 -44
  41. data/.rvmrc +0 -1
@@ -4,10 +4,21 @@ module Axiom
4
4
  module Types
5
5
 
6
6
  # Represents an array type
7
- class Array < Object
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
@@ -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
@@ -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::Object]
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 :matches_key_and_value_types
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
@@ -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
@@ -4,10 +4,21 @@ module Axiom
4
4
  module Types
5
5
 
6
6
  # Represents a set type
7
- class Set < Object
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).public_send(option, value)
73
+ descendant.add_accepted_option(option).set_option(option, value)
62
74
  end
63
75
  end
64
76
 
@@ -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 [self]
107
+ # @return [Class<Axiom::Types::Type>]
93
108
  #
94
109
  # @api public
95
110
  def self.constraint(constraint = Undefined, &block)
@@ -4,7 +4,7 @@ module Axiom
4
4
  module Types
5
5
 
6
6
  # Gem version
7
- VERSION = '0.0.1'.freeze
7
+ VERSION = '0.0.2'.freeze
8
8
 
9
9
  end # module Types
10
10
  end # module Axiom