axiom-types 0.0.2 → 0.0.3

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