axiom-types 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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