veritas 0.0.6 → 0.0.7

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 (77) hide show
  1. data/.travis.yml +10 -4
  2. data/Gemfile +1 -1
  3. data/TODO +10 -0
  4. data/config/flay.yml +1 -1
  5. data/config/flog.yml +1 -1
  6. data/config/roodi.yml +2 -2
  7. data/config/site.reek +2 -1
  8. data/lib/veritas.rb +2 -2
  9. data/lib/veritas/algebra/join.rb +13 -91
  10. data/lib/veritas/algebra/product.rb +4 -1
  11. data/lib/veritas/attribute.rb +1 -1
  12. data/lib/veritas/function/connective/negation.rb +13 -1
  13. data/lib/veritas/function/numeric/absolute.rb +5 -19
  14. data/lib/veritas/function/numeric/unary.rb +3 -0
  15. data/lib/veritas/function/numeric/unary_minus.rb +5 -19
  16. data/lib/veritas/function/numeric/unary_plus.rb +5 -21
  17. data/lib/veritas/function/proposition.rb +3 -28
  18. data/lib/veritas/function/string/length.rb +0 -1
  19. data/lib/veritas/function/unary.rb +47 -0
  20. data/lib/veritas/relation.rb +16 -2
  21. data/lib/veritas/relation/header.rb +48 -41
  22. data/lib/veritas/relation/operation/order/direction.rb +13 -1
  23. data/lib/veritas/relation/operation/order/direction_set.rb +9 -198
  24. data/lib/veritas/relation/operation/reverse.rb +0 -2
  25. data/lib/veritas/support/comparator.rb +1 -1
  26. data/lib/veritas/support/evaluator.rb +3 -0
  27. data/lib/veritas/support/immutable.rb +33 -8
  28. data/lib/veritas/tuple.rb +8 -6
  29. data/lib/veritas/version.rb +1 -1
  30. data/spec/integration/veritas/algebra/projection_spec.rb +1 -1
  31. data/spec/integration/veritas/relation/efficient_enumerable_spec.rb +40 -15
  32. data/spec/rcov.opts +1 -0
  33. data/spec/shared/hash_method_behavior.rb +10 -5
  34. data/spec/spec_helper.rb +1 -1
  35. data/spec/unit/veritas/algebra/extension/class_methods/new_spec.rb +1 -1
  36. data/spec/unit/veritas/algebra/join/class_methods/new_spec.rb +1 -1
  37. data/spec/unit/veritas/algebra/join/each_spec.rb +36 -9
  38. data/spec/unit/veritas/aliasable/inheritable_alias_spec.rb +1 -1
  39. data/spec/unit/veritas/comparator/compare_spec.rb +4 -1
  40. data/spec/unit/veritas/evaluator/context/method_missing_spec.rb +7 -1
  41. data/spec/unit/veritas/function/connective/negation/class_methods/operation_spec.rb +11 -0
  42. data/spec/unit/veritas/function/numeric/absolute/class_methods/operation_spec.rb +11 -0
  43. data/spec/unit/veritas/function/numeric/unary_minus/class_methods/operation_spec.rb +11 -0
  44. data/spec/unit/veritas/function/numeric/unary_plus/class_methods/operation_spec.rb +11 -0
  45. data/spec/unit/veritas/function/unary/callable/call_spec.rb +23 -0
  46. data/spec/unit/veritas/function/unary/callable/included_spec.rb +23 -0
  47. data/spec/unit/veritas/function/unary/inspect_spec.rb +34 -0
  48. data/spec/unit/veritas/immutable/fixtures/classes.rb +3 -3
  49. data/spec/unit/veritas/immutable/module_methods/memoize_spec.rb +20 -10
  50. data/spec/unit/veritas/relation/class_methods/coerce_spec.rb +23 -0
  51. data/spec/unit/veritas/relation/each_spec.rb +1 -1
  52. data/spec/unit/veritas/relation/equal_value_spec.rb +12 -0
  53. data/spec/unit/veritas/relation/header/call_spec.rb +23 -0
  54. data/spec/unit/veritas/relation/header/class_methods/coerce_spec.rb +1 -1
  55. data/spec/unit/veritas/relation/header/class_methods/new_spec.rb +2 -2
  56. data/spec/unit/veritas/relation/header/each_spec.rb +1 -1
  57. data/spec/unit/veritas/relation/header/empty_spec.rb +5 -4
  58. data/spec/unit/veritas/relation/header/intersect_spec.rb +18 -4
  59. data/spec/unit/veritas/relation/operation/binary/class_methods/new_spec.rb +25 -25
  60. data/spec/unit/veritas/relation/operation/order/direction/name_spec.rb +13 -0
  61. data/spec/unit/veritas/relation/operation/order/direction_set/class_methods/new_spec.rb +1 -1
  62. data/spec/unit/veritas/relation/operation/order/direction_set/equal_value_spec.rb +13 -0
  63. data/spec/unit/veritas/tuple/call_spec.rb +25 -0
  64. data/spec/unit/veritas/tuple/class_methods/coerce_spec.rb +1 -1
  65. data/tasks/metrics/ci.rake +2 -2
  66. data/tasks/metrics/flay.rake +1 -1
  67. data/tasks/metrics/flog.rake +5 -3
  68. data/tasks/metrics/heckle.rake +18 -11
  69. data/tasks/metrics/reek.rake +1 -1
  70. data/tasks/spec.rake +23 -14
  71. data/veritas.gemspec +14 -8
  72. metadata +31 -25
  73. data/spec/unit/veritas/relation/header/element_reference_spec.rb +0 -21
  74. data/spec/unit/veritas/relation/operation/order/direction_set/each_spec.rb +0 -32
  75. data/spec/unit/veritas/relation/operation/order/direction_set/empty_spec.rb +0 -21
  76. data/spec/unit/veritas/relation/operation/order/direction_set/union_spec.rb +0 -18
  77. data/spec/unit/veritas/tuple/element_reference_spec.rb +0 -22
@@ -44,7 +44,6 @@ module Veritas
44
44
  end
45
45
 
46
46
  module Methods
47
- extend Aliasable
48
47
 
49
48
  # Return a length function
50
49
  #
@@ -10,6 +10,41 @@ module Veritas
10
10
 
11
11
  compare :operand
12
12
 
13
+ # Mixin for adding #call to unary function classes
14
+ module Callable
15
+
16
+ # Hook called when module is included
17
+ #
18
+ # @param [Module] descendant
19
+ # the module or class including Callable
20
+ #
21
+ # @return [self]
22
+ #
23
+ # @api private
24
+ def included(descendant)
25
+ super
26
+ descendant.extend(Callable)
27
+ self
28
+ end
29
+
30
+ # Return the response from the unary operation called on the value
31
+ #
32
+ # @example
33
+ # callable.call(value) # => any object
34
+ #
35
+ # @param [Object] value
36
+ #
37
+ # @return [Object]
38
+ #
39
+ # @api public
40
+ def call(value)
41
+ value.send(operation)
42
+ end
43
+
44
+ end # module Callable
45
+
46
+ extend Callable
47
+
13
48
  # Evaluate the unary connective using the tuple
14
49
  #
15
50
  # @example
@@ -70,6 +105,18 @@ module Veritas
70
105
  cmp?(__method__, other)
71
106
  end
72
107
 
108
+ # Return a string representing the unary function
109
+ #
110
+ # @example
111
+ # unary.inspect # => "+1"
112
+ #
113
+ # @return [String]
114
+ #
115
+ # @api public
116
+ def inspect
117
+ "#{self.class.operation.to_s.upcase.chomp('@')}(#{operand.inspect})"
118
+ end
119
+
73
120
  # Mixin for invertable unary functions
74
121
  module Invertible
75
122
 
@@ -112,7 +112,7 @@ module Veritas
112
112
  seen = {}
113
113
  tuples.each do |tuple|
114
114
  tuple = Tuple.coerce(header, tuple)
115
- yield(seen[tuple] = tuple) unless seen.key?(tuple)
115
+ yield seen[tuple] = tuple unless seen.key?(tuple)
116
116
  end
117
117
  self
118
118
  end
@@ -181,7 +181,21 @@ module Veritas
181
181
  #
182
182
  # @api private
183
183
  def coerce(object)
184
- project_relation(Relation.new(header, object))
184
+ self.class.coerce(header, object)
185
+ end
186
+
187
+ # Coerce an Enumerable into a Relation
188
+ #
189
+ # @param [Header] header
190
+ # the header to use when initializing a Relation
191
+ # @param [Enumerable] object
192
+ # the object to coerce
193
+ #
194
+ # @return [Relation]
195
+ #
196
+ # @api private
197
+ def self.coerce(header, object)
198
+ object.kind_of?(Relation) ? object : Relation.new(header, object)
185
199
  end
186
200
 
187
201
  end # class Relation
@@ -11,9 +11,10 @@ module Veritas
11
11
  compare :to_set
12
12
 
13
13
  inheritable_alias(
14
- :& => :intersect,
15
- :| => :union,
16
- :- => :difference
14
+ :[] => :call,
15
+ :& => :intersect,
16
+ :| => :union,
17
+ :- => :difference
17
18
  )
18
19
 
19
20
  # Instantiate a Header
@@ -24,12 +25,12 @@ module Veritas
24
25
  # @param [#to_ary] attributes
25
26
  # optional attributes
26
27
  #
27
- # @return [undefined]
28
+ # @return [Header]
28
29
  #
29
30
  # @api public
30
- def self.new(attributes = [])
31
- attributes = coerce_attributes(attributes.to_ary)
32
- assert_unique_attributes(attributes)
31
+ def self.new(attributes = nil)
32
+ attributes = coerce_attributes(attributes)
33
+ assert_unique_names(attributes.map { |attribute| attribute.name })
33
34
  super
34
35
  end
35
36
 
@@ -41,44 +42,53 @@ module Veritas
41
42
  #
42
43
  # @api private
43
44
  def self.coerce_attributes(attributes)
44
- attributes.map { |attribute| Attribute.coerce(attribute) }
45
+ Array(attributes).map { |attribute| coerce_attribute(attribute) }
45
46
  end
46
47
 
47
- # Assert the attributes are unique
48
+ # Coerce the attribute into an Attribute
48
49
  #
49
- # @param [Array<Attribute>] attributes
50
+ # @param [Object] attribute
51
+ #
52
+ # @return [Attribute]
53
+ #
54
+ # @api private
55
+ def self.coerce_attribute(attribute)
56
+ Attribute.coerce(attribute)
57
+ end
58
+
59
+ # Assert the names are unique
60
+ #
61
+ # @param [Array<Symbol>] names
50
62
  #
51
63
  # @return [undefined]
52
64
  #
53
- # @raise [DuplicateAttributeError]
54
- # raised if the attributes are not unique
65
+ # @raise [DuplicateNameError]
66
+ # raised if the names are not unique
55
67
  #
56
68
  # @api private
57
- def self.assert_unique_attributes(attributes)
58
- duplicates = duplicate_attributes(attributes)
69
+ def self.assert_unique_names(names)
70
+ duplicates = duplicate_names(names)
59
71
  if duplicates
60
- raise DuplicateAttributeError, "duplicate attributes: #{duplicates.join(', ')}"
72
+ raise DuplicateNameError, "duplicate names: #{duplicates.join(', ')}"
61
73
  end
62
74
  end
63
75
 
64
- # Returns the duplicate attribute names, if any
76
+ # Returns the duplicate names, if any
65
77
  #
66
- # @param [Array<Attribute>] attributes
78
+ # @param [Array<Symbol>] names
67
79
  #
68
80
  # @return [Array<Symbol>]
69
- # returns an array of duplicate attributes
81
+ # returns an array of duplicate names
70
82
  #
71
83
  # @return [nil]
72
- # returns nil if there are no duplicate attributes
84
+ # returns nil if there are no duplicate names
73
85
  #
74
86
  # @api private
75
- def self.duplicate_attributes(attributes)
76
- names = attributes.map { |attribute| attribute.name }
77
- names.select! { |name| names.count(name) > 1 }
78
- names.uniq!
87
+ def self.duplicate_names(names)
88
+ names.select { |name| names.count(name) > 1 }.uniq!
79
89
  end
80
90
 
81
- private_class_method :coerce_attributes, :assert_unique_attributes, :duplicate_attributes
91
+ private_class_method :coerce_attributes, :coerce_attribute, :assert_unique_names, :duplicate_names
82
92
 
83
93
  # Initialize a Header
84
94
  #
@@ -91,8 +101,8 @@ module Veritas
91
101
  #
92
102
  # @api public
93
103
  def initialize(attributes)
94
- @names = attributes.map { |attribute| attribute.name }
95
- @attributes = Hash[@names.zip(attributes)]
104
+ @attributes = Immutable.freeze_object(attributes)
105
+ @attribute_for = Hash[@attributes.map { |attribute| attribute.name }.zip(@attributes)]
96
106
  end
97
107
 
98
108
  # Iterate over each attribute in the header
@@ -111,25 +121,25 @@ module Veritas
111
121
  # @api public
112
122
  def each
113
123
  return to_enum unless block_given?
114
- @names.each { |name| yield @attributes.fetch(name) }
124
+ to_ary.each { |attribute| yield attribute }
115
125
  self
116
126
  end
117
127
 
118
128
  # Lookup an attribute in the header given a name
119
129
  #
120
130
  # @example
121
- # attribute = header[:id]
131
+ # attribute = header.call(:id)
122
132
  #
123
133
  # @param [Attribute, #to_ary, #to_sym] name
124
134
  #
125
135
  # @return [Attribute]
126
136
  # the attribute when the name is known
127
137
  # @return [nil]
128
- # nil when the attribute is unknown
138
+ # nil when the name is unknown
129
139
  #
130
140
  # @api public
131
- def [](name)
132
- @attributes[Attribute.name_from(name)]
141
+ def call(name)
142
+ @attribute_for[Attribute.name_from(name)]
133
143
  end
134
144
 
135
145
  # Return a header with only the attributes specified
@@ -142,7 +152,7 @@ module Veritas
142
152
  #
143
153
  # @return [Header]
144
154
  #
145
- # @api private
155
+ # @api public
146
156
  def project(attributes)
147
157
  new(attributes.map { |attribute| self[attribute] })
148
158
  end
@@ -204,7 +214,7 @@ module Veritas
204
214
  #
205
215
  # @api private
206
216
  def to_ary
207
- @attributes.values_at(*@names).freeze
217
+ @attributes
208
218
  end
209
219
 
210
220
  # Compare the header with other header for equivalency
@@ -231,7 +241,7 @@ module Veritas
231
241
  #
232
242
  # @api public
233
243
  def empty?
234
- @names.empty?
244
+ to_ary.empty?
235
245
  end
236
246
 
237
247
  # Return a string representing the header
@@ -250,14 +260,13 @@ module Veritas
250
260
 
251
261
  # Utility method to instantiate a Header
252
262
  #
253
- # @param [#to_ary] attributes
254
- # the header attributes
263
+ # @param [Array] *args
255
264
  #
256
265
  # @return [Header]
257
266
  #
258
267
  # @api private
259
- def new(attributes)
260
- self.class.new(attributes)
268
+ def new(*args)
269
+ self.class.new(*args)
261
270
  end
262
271
 
263
272
  # Coerce the object into a Header
@@ -281,11 +290,9 @@ module Veritas
281
290
  #
282
291
  # @api private
283
292
  def self.coerce(object)
284
- object.kind_of?(Header) ? object : new(object)
293
+ object.kind_of?(self) ? object : new(object)
285
294
  end
286
295
 
287
- memoize :to_ary
288
-
289
296
  end # class Header
290
297
  end # class Relation
291
298
  end # module Veritas
@@ -31,6 +31,18 @@ module Veritas
31
31
  @attribute = attribute
32
32
  end
33
33
 
34
+ # Return the attribute name
35
+ #
36
+ # @example
37
+ # direction.name # => :id
38
+ #
39
+ # @return [Symbol]
40
+ #
41
+ # @api public
42
+ def name
43
+ attribute.name
44
+ end
45
+
34
46
  # Compare the left and right Tuple attribute values
35
47
  #
36
48
  # @example
@@ -51,7 +63,7 @@ module Veritas
51
63
  # @api public
52
64
  def call(left, right)
53
65
  attribute = self.attribute
54
- self.class.call(left[attribute], right[attribute])
66
+ self.class.call(attribute.call(left), attribute.call(right))
55
67
  end
56
68
 
57
69
  # Rename the contained attribute with the provided aliases
@@ -6,108 +6,23 @@ module Veritas
6
6
  class Order
7
7
 
8
8
  # A class that represents a tuple sort order for a set of attributes
9
- class DirectionSet
10
- extend Aliasable, Comparator
11
- include Enumerable, Immutable
9
+ class DirectionSet < Header
10
+ EMPTY = new
12
11
 
13
12
  compare :to_ary
14
13
 
15
- inheritable_alias(:| => :union)
16
-
17
- # Instantiate a DirectionSet
18
- #
19
- # @example
20
- # directions = DirectionSet.new(directions)
21
- #
22
- # @param [#to_ary] directions
23
- # optional attributes
24
- #
25
- # @return [undefined]
26
- #
27
- # @api public
28
- def self.new(directions)
29
- directions = coerce_directions(directions)
30
- assert_unique_attributes(directions.map { |direction| direction.attribute })
31
- super
32
- end
33
-
34
- # Coerce the attributes into an Array of Direction objects
35
- #
36
- # @param [Array<Direction>] attributes
37
- #
38
- # @return [Array<Direction>]
39
- #
40
- # @api private
41
- def self.coerce_directions(directions)
42
- Array(directions).map { |direction| Ascending.coerce(direction) }
43
- end
44
-
45
- # Assert the attributes are unique
46
- #
47
- # @param [Array<Attribute>] attributes
48
- #
49
- # @return [undefined]
50
- #
51
- # @raise [DuplicateAttributeError]
52
- # raised if the attributes are not unique
53
- #
54
- # @api private
55
- def self.assert_unique_attributes(attributes)
56
- duplicates = duplicate_attributes(attributes)
57
- if duplicates
58
- raise DuplicateAttributeError, "duplicate attributes: #{duplicates.join(', ')}"
59
- end
60
- end
61
-
62
- # Returns the duplicate attribute names, if any
63
- #
64
- # @param [Array<Attribute>] attributes
65
- #
66
- # @return [Array<Symbol>]
67
- # returns an array of duplicate attributes
14
+ # Coerce the attribute into a Direction
68
15
  #
69
- # @return [nil]
70
- # returns nil if there are no duplicate attributes
16
+ # @param [Object] attribute
71
17
  #
72
- # @api private
73
- def self.duplicate_attributes(attributes)
74
- names = attributes.map { |attribute| attribute.name }
75
- names.select! { |name| names.count(name) > 1 }
76
- names.uniq!
77
- end
78
-
79
- private_class_method :coerce_directions, :assert_unique_attributes, :duplicate_attributes
80
-
81
- # Initialize a DirectionSet
82
- #
83
- # @param [Array<Direction>] directions
84
- # the directions to sort with
85
- #
86
- # @return [undefined]
18
+ # @return [Direction]
87
19
  #
88
20
  # @api private
89
- def initialize(directions)
90
- @directions = directions
21
+ def self.coerce_attribute(attribute)
22
+ Ascending.coerce(attribute)
91
23
  end
92
24
 
93
- EMPTY = new([])
94
-
95
- # Return a direction set with only the attributes specified
96
- #
97
- # @example
98
- # projected = directions.project([ attribute ])
99
- #
100
- # @param [Array<Attribute>] attributes
101
- # the attributes to keep in the direction set
102
- #
103
- # @return [DirectionSet]
104
- #
105
- # @api public
106
- def project(attributes)
107
- new select { |direction|
108
- attributes.include?(direction.attribute)
109
- }
110
- end
25
+ private_class_method :coerce_attribute
111
26
 
112
27
  # Rename the contained attributes with the provided aliases
113
28
  #
@@ -136,50 +51,6 @@ module Veritas
136
51
  new(map { |direction| direction.reverse })
137
52
  end
138
53
 
139
- # Iterate over each direction in the set
140
- #
141
- # @example
142
- # directions = DirectionSet.new(directions)
143
- # directions.each { |direction| ... }
144
- #
145
- # @yield [direction]
146
- #
147
- # @yieldparam [Direction] direction
148
- # each direction in the set
149
- #
150
- # @return [self]
151
- #
152
- # @api public
153
- def each
154
- return to_enum unless block_given?
155
- to_ary.each { |tuple| yield tuple }
156
- self
157
- end
158
-
159
- # Return the directions as an Array
160
- #
161
- # @return [Array]
162
- #
163
- # @api private
164
- def to_ary
165
- @directions
166
- end
167
-
168
- # Union the directions with another set of directions
169
- #
170
- # @example
171
- # union = directions.union(other)
172
- #
173
- # @param [DirectionSet] other
174
- # the directions to union with
175
- #
176
- # @return [DirectionSet]
177
- #
178
- # @api public
179
- def union(other)
180
- new(to_ary | other.to_ary)
181
- end
182
-
183
54
  # Return each attribute in an Array
184
55
  #
185
56
  # @return [Array]
@@ -202,46 +73,8 @@ module Veritas
202
73
  tuples.sort { |left, right| cmp_tuples(left, right) }
203
74
  end
204
75
 
205
- # Compare the directions with other directions for equivalency
206
- #
207
- # @example
208
- # directions == other # => true or false
209
- #
210
- # @param [DirectionSet] other
211
- # the other directions to compare with
212
- #
213
- # @return [Boolean]
214
- #
215
- # @api public
216
- def ==(other)
217
- cmp?(__method__, coerce(other))
218
- end
219
-
220
- # Test if there are no directions
221
- #
222
- # @example
223
- # directions.empty? # => true or false
224
- #
225
- # @return [Boolean]
226
- #
227
- # @api public
228
- def empty?
229
- @directions.empty?
230
- end
231
-
232
76
  private
233
77
 
234
- # Utility method for initializing a set of directions
235
- #
236
- # @param [Array<Direction>] directions
237
- #
238
- # @return [DirectionSet]
239
- #
240
- # @api private
241
- def new(directions)
242
- self.class.new(directions)
243
- end
244
-
245
78
  # Compare the attributes for each Tuple
246
79
  #
247
80
  # @param [Tuple] left
@@ -264,29 +97,7 @@ module Veritas
264
97
  end
265
98
  end
266
99
 
267
- # Coerce directions into a DirectionSet
268
- #
269
- # @param [DirectionSet, Array<Direction, Attribute>]
270
- #
271
- # @return [DirectionSet]
272
- #
273
- # @api private
274
- def coerce(object)
275
- self.class.coerce(object)
276
- end
277
-
278
- # Coerce directions into a DirectionSet
279
- #
280
- # @param [DirectionSet, Array<Direction, Attribute>]
281
- #
282
- # @return [DirectionSet]
283
- #
284
- # @api private
285
- def self.coerce(object)
286
- object.kind_of?(DirectionSet) ? object : new(object)
287
- end
288
-
289
- memoize :reverse
100
+ memoize :reverse, :attributes
290
101
 
291
102
  end # class DirectionSet
292
103
  end # class Order