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.
Files changed (47) hide show
  1. data/.rspec +1 -0
  2. data/.travis.yml +11 -16
  3. data/Gemfile +5 -2
  4. data/Gemfile.devtools +19 -24
  5. data/Guardfile +18 -10
  6. data/README.md +5 -28
  7. data/axiom-types.gemspec +2 -3
  8. data/config/flay.yml +2 -2
  9. data/config/flog.yml +1 -1
  10. data/config/reek.yml +15 -13
  11. data/config/rubocop.yml +62 -0
  12. data/lib/axiom/types.rb +5 -3
  13. data/lib/axiom/types/boolean.rb +4 -2
  14. data/lib/axiom/types/collection.rb +5 -2
  15. data/lib/axiom/types/encodable.rb +1 -1
  16. data/lib/axiom/types/float.rb +3 -0
  17. data/lib/axiom/types/hash.rb +8 -3
  18. data/lib/axiom/types/length_comparable.rb +7 -4
  19. data/lib/axiom/types/numeric.rb +5 -0
  20. data/lib/axiom/types/object.rb +2 -2
  21. data/lib/axiom/types/support/infinity.rb +116 -0
  22. data/lib/axiom/types/support/options.rb +9 -18
  23. data/lib/axiom/types/time.rb +2 -2
  24. data/lib/axiom/types/type.rb +13 -9
  25. data/lib/axiom/types/value_comparable.rb +5 -2
  26. data/lib/axiom/types/version.rb +1 -1
  27. data/spec/unit/axiom/types/boolean/class_methods/infer_spec.rb +6 -0
  28. data/spec/unit/axiom/types/collection/class_methods/finalize_spec.rb +3 -3
  29. data/spec/unit/axiom/types/encodable/class_methods/extended_spec.rb +8 -7
  30. data/spec/unit/axiom/types/encodable/finalize_spec.rb +30 -8
  31. data/spec/unit/axiom/types/hash/class_methods/infer_spec.rb +17 -17
  32. data/spec/unit/axiom/types/infinity/class_methods/coerce_spec.rb +64 -0
  33. data/spec/unit/axiom/types/infinity/class_methods/spaceship_operator_spec.rb +67 -0
  34. data/spec/unit/axiom/types/infinity/class_methods/succ_spec.rb +11 -0
  35. data/spec/unit/axiom/types/length_comparable/class_methods/extended_spec.rb +17 -8
  36. data/spec/unit/axiom/types/negative_infinity/class_methods/spaceship_operator_spec.rb +67 -0
  37. data/spec/unit/axiom/types/object/class_methods/coercion_method_spec.rb +2 -2
  38. data/spec/unit/axiom/types/object/class_methods/infer_spec.rb +13 -1
  39. data/spec/unit/axiom/types/object/class_methods/primitive_spec.rb +4 -4
  40. data/spec/unit/axiom/types/options/accept_options_spec.rb +12 -4
  41. data/spec/unit/axiom/types/options/inherited_spec.rb +10 -36
  42. data/spec/unit/axiom/types/type/class_methods/constraint_spec.rb +6 -11
  43. data/spec/unit/axiom/types/type/class_methods/include_predicate_spec.rb +7 -7
  44. data/spec/unit/axiom/types/type/class_methods/includes_spec.rb +2 -2
  45. data/spec/unit/axiom/types/value_comparable/class_methods/extended_spec.rb +17 -8
  46. metadata +29 -31
  47. checksums.yaml +0 -7
@@ -69,7 +69,7 @@ module Axiom
69
69
  constraint { |object| object.encoding.equal?(encoding) }
70
70
  end
71
71
 
72
- end if RUBY_VERSION >= '1.9'
72
+ end
73
73
 
74
74
  end # module Types
75
75
  end # module Axiom
@@ -8,6 +8,9 @@ module Axiom
8
8
  primitive ::Float
9
9
  coercion_method :to_float
10
10
 
11
+ minimum primitive::MIN
12
+ maximum primitive::MAX
13
+
11
14
  end # class Float
12
15
  end # module Types
13
16
  end # module Axiom
@@ -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.accept_options :minimum_length, :maximum_length
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
- constraint do |object|
45
- object.length.between?(minimum_length, maximum_length)
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
@@ -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
@@ -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
- # Adds new option that an attribute class can accept
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 add_accepted_option(new_option)
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.add_accepted_option(option).set_option(option, value)
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 ReservedMethodError,
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)
@@ -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 = -0x7FFFFFFF
12
- MAXIMUM_SECONDS = 0x7FFFFFFF
11
+ MINIMUM_SECONDS = -2**31
12
+ MAXIMUM_SECONDS = 2**31 - 1
13
13
 
14
14
  primitive ::Time
15
15
  coercion_method :to_time
@@ -30,7 +30,7 @@ module Axiom
30
30
  # @example
31
31
  # type = Axiom::Types::Type.new # => Axiom::Types::Type
32
32
  #
33
- # @param [#call] constraint
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(constraint = Undefined, &block)
47
+ def self.new(*args, &block)
48
48
  type = ::Class.new(self, &block)
49
- type.constraint(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 = self.constraint
140
- @constraint = if current
141
- lambda { |object| constraint.call(object) && current.call(object) }
142
- else
143
- constraint
144
- end
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.accept_options :minimum, :maximum
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
- constraint { |object| object.between?(minimum, maximum) }
46
+ range = minimum..maximum
47
+ constraint(range.method(:cover?))
45
48
  end
46
49
 
47
50
  end # module ValueComparable
@@ -4,7 +4,7 @@ module Axiom
4
4
  module Types
5
5
 
6
6
  # Gem version
7
- VERSION = '0.0.2'.freeze
7
+ VERSION = '0.0.3'.freeze
8
8
 
9
9
  end # module Types
10
10
  end # module Axiom
@@ -30,4 +30,10 @@ describe Axiom::Types::Boolean, '.infer' do
30
30
 
31
31
  it { should be_nil }
32
32
  end
33
+
34
+ context 'when the argument is not nil' do
35
+ let(:arg) { 1 }
36
+
37
+ it { should be_nil }
38
+ end
33
39
  end
@@ -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([ Object.new ])
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([ member ])
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([ member.to_s ])
46
+ should_not include([member.to_s])
47
47
  end
48
48
  end
49
49
  end