veritas 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +10 -4
- data/Gemfile +1 -1
- data/TODO +10 -0
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/config/roodi.yml +2 -2
- data/config/site.reek +2 -1
- data/lib/veritas.rb +2 -2
- data/lib/veritas/algebra/join.rb +13 -91
- data/lib/veritas/algebra/product.rb +4 -1
- data/lib/veritas/attribute.rb +1 -1
- data/lib/veritas/function/connective/negation.rb +13 -1
- data/lib/veritas/function/numeric/absolute.rb +5 -19
- data/lib/veritas/function/numeric/unary.rb +3 -0
- data/lib/veritas/function/numeric/unary_minus.rb +5 -19
- data/lib/veritas/function/numeric/unary_plus.rb +5 -21
- data/lib/veritas/function/proposition.rb +3 -28
- data/lib/veritas/function/string/length.rb +0 -1
- data/lib/veritas/function/unary.rb +47 -0
- data/lib/veritas/relation.rb +16 -2
- data/lib/veritas/relation/header.rb +48 -41
- data/lib/veritas/relation/operation/order/direction.rb +13 -1
- data/lib/veritas/relation/operation/order/direction_set.rb +9 -198
- data/lib/veritas/relation/operation/reverse.rb +0 -2
- data/lib/veritas/support/comparator.rb +1 -1
- data/lib/veritas/support/evaluator.rb +3 -0
- data/lib/veritas/support/immutable.rb +33 -8
- data/lib/veritas/tuple.rb +8 -6
- data/lib/veritas/version.rb +1 -1
- data/spec/integration/veritas/algebra/projection_spec.rb +1 -1
- data/spec/integration/veritas/relation/efficient_enumerable_spec.rb +40 -15
- data/spec/rcov.opts +1 -0
- data/spec/shared/hash_method_behavior.rb +10 -5
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/veritas/algebra/extension/class_methods/new_spec.rb +1 -1
- data/spec/unit/veritas/algebra/join/class_methods/new_spec.rb +1 -1
- data/spec/unit/veritas/algebra/join/each_spec.rb +36 -9
- data/spec/unit/veritas/aliasable/inheritable_alias_spec.rb +1 -1
- data/spec/unit/veritas/comparator/compare_spec.rb +4 -1
- data/spec/unit/veritas/evaluator/context/method_missing_spec.rb +7 -1
- data/spec/unit/veritas/function/connective/negation/class_methods/operation_spec.rb +11 -0
- data/spec/unit/veritas/function/numeric/absolute/class_methods/operation_spec.rb +11 -0
- data/spec/unit/veritas/function/numeric/unary_minus/class_methods/operation_spec.rb +11 -0
- data/spec/unit/veritas/function/numeric/unary_plus/class_methods/operation_spec.rb +11 -0
- data/spec/unit/veritas/function/unary/callable/call_spec.rb +23 -0
- data/spec/unit/veritas/function/unary/callable/included_spec.rb +23 -0
- data/spec/unit/veritas/function/unary/inspect_spec.rb +34 -0
- data/spec/unit/veritas/immutable/fixtures/classes.rb +3 -3
- data/spec/unit/veritas/immutable/module_methods/memoize_spec.rb +20 -10
- data/spec/unit/veritas/relation/class_methods/coerce_spec.rb +23 -0
- data/spec/unit/veritas/relation/each_spec.rb +1 -1
- data/spec/unit/veritas/relation/equal_value_spec.rb +12 -0
- data/spec/unit/veritas/relation/header/call_spec.rb +23 -0
- data/spec/unit/veritas/relation/header/class_methods/coerce_spec.rb +1 -1
- data/spec/unit/veritas/relation/header/class_methods/new_spec.rb +2 -2
- data/spec/unit/veritas/relation/header/each_spec.rb +1 -1
- data/spec/unit/veritas/relation/header/empty_spec.rb +5 -4
- data/spec/unit/veritas/relation/header/intersect_spec.rb +18 -4
- data/spec/unit/veritas/relation/operation/binary/class_methods/new_spec.rb +25 -25
- data/spec/unit/veritas/relation/operation/order/direction/name_spec.rb +13 -0
- data/spec/unit/veritas/relation/operation/order/direction_set/class_methods/new_spec.rb +1 -1
- data/spec/unit/veritas/relation/operation/order/direction_set/equal_value_spec.rb +13 -0
- data/spec/unit/veritas/tuple/call_spec.rb +25 -0
- data/spec/unit/veritas/tuple/class_methods/coerce_spec.rb +1 -1
- data/tasks/metrics/ci.rake +2 -2
- data/tasks/metrics/flay.rake +1 -1
- data/tasks/metrics/flog.rake +5 -3
- data/tasks/metrics/heckle.rake +18 -11
- data/tasks/metrics/reek.rake +1 -1
- data/tasks/spec.rake +23 -14
- data/veritas.gemspec +14 -8
- metadata +31 -25
- data/spec/unit/veritas/relation/header/element_reference_spec.rb +0 -21
- data/spec/unit/veritas/relation/operation/order/direction_set/each_spec.rb +0 -32
- data/spec/unit/veritas/relation/operation/order/direction_set/empty_spec.rb +0 -21
- data/spec/unit/veritas/relation/operation/order/direction_set/union_spec.rb +0 -18
- data/spec/unit/veritas/tuple/element_reference_spec.rb +0 -22
@@ -38,7 +38,7 @@ module Veritas
|
|
38
38
|
# @api private
|
39
39
|
def define_hash_method(methods)
|
40
40
|
define_method(:hash) do
|
41
|
-
self.class.hash ^ methods.map { |method| send(method).hash }.
|
41
|
+
self.class.hash ^ methods.map { |method| send(method).hash }.reduce(0, :^)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -29,7 +29,7 @@ module Veritas
|
|
29
29
|
#
|
30
30
|
# @api public
|
31
31
|
def freeze
|
32
|
-
|
32
|
+
memory # initialize memory
|
33
33
|
super
|
34
34
|
end
|
35
35
|
|
@@ -45,7 +45,7 @@ module Veritas
|
|
45
45
|
#
|
46
46
|
# @api public
|
47
47
|
def memoized(name)
|
48
|
-
|
48
|
+
memory[name]
|
49
49
|
end
|
50
50
|
|
51
51
|
# Sets a memoized value for a method
|
@@ -62,9 +62,7 @@ module Veritas
|
|
62
62
|
#
|
63
63
|
# @api public
|
64
64
|
def memoize(name, value)
|
65
|
-
unless
|
66
|
-
@__memory[name] = Immutable.freeze_object(value)
|
67
|
-
end
|
65
|
+
store_memory(name, value) unless memory.key?(name)
|
68
66
|
self
|
69
67
|
end
|
70
68
|
|
@@ -80,6 +78,33 @@ module Veritas
|
|
80
78
|
self
|
81
79
|
end
|
82
80
|
|
81
|
+
private
|
82
|
+
|
83
|
+
# The memoized method results
|
84
|
+
#
|
85
|
+
# @return [Hash]
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
def memory
|
89
|
+
@__memory ||= {}
|
90
|
+
end
|
91
|
+
|
92
|
+
# Store the value in memory
|
93
|
+
#
|
94
|
+
# @param [Symbol] name
|
95
|
+
# the method name
|
96
|
+
# @param [Object] value
|
97
|
+
# the value to memoize
|
98
|
+
#
|
99
|
+
# @return [self]
|
100
|
+
#
|
101
|
+
# @return [value]
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
def store_memory(name, value)
|
105
|
+
memory[name] = Immutable.freeze_object(value)
|
106
|
+
end
|
107
|
+
|
83
108
|
# Attempt to freeze an object
|
84
109
|
#
|
85
110
|
# @example using a value object
|
@@ -179,10 +204,10 @@ module Veritas
|
|
179
204
|
original = instance_method(method)
|
180
205
|
undef_method(method)
|
181
206
|
define_method(method) do |*args|
|
182
|
-
if
|
183
|
-
|
207
|
+
if memory.key?(method)
|
208
|
+
memoized(method)
|
184
209
|
else
|
185
|
-
|
210
|
+
store_memory(method, original.bind(self).call(*args))
|
186
211
|
end
|
187
212
|
end
|
188
213
|
end
|
data/lib/veritas/tuple.rb
CHANGED
@@ -4,9 +4,11 @@ module Veritas
|
|
4
4
|
|
5
5
|
# A set of objects representing a unique fact in a relation
|
6
6
|
class Tuple
|
7
|
-
extend Comparator
|
7
|
+
extend Aliasable, Comparator
|
8
8
|
include Immutable
|
9
9
|
|
10
|
+
inheritable_alias(:[] => :call)
|
11
|
+
|
10
12
|
compare :data
|
11
13
|
|
12
14
|
# The tuple header
|
@@ -41,15 +43,15 @@ module Veritas
|
|
41
43
|
# Lookup a value in the tuple given an attribute
|
42
44
|
#
|
43
45
|
# @example
|
44
|
-
# value = tuple
|
46
|
+
# value = tuple.call(attribute)
|
45
47
|
#
|
46
48
|
# @param [Attribute] attribute
|
47
49
|
#
|
48
50
|
# @return [Object]
|
49
51
|
#
|
50
52
|
# @api public
|
51
|
-
def
|
52
|
-
data.fetch(header
|
53
|
+
def call(attribute)
|
54
|
+
data.fetch(header.call(attribute))
|
53
55
|
end
|
54
56
|
|
55
57
|
# Return a tuple with only the specified attributes
|
@@ -111,7 +113,7 @@ module Veritas
|
|
111
113
|
# @api private
|
112
114
|
def predicate
|
113
115
|
header.reduce(Function::Proposition::Tautology.instance) do |predicate, attribute|
|
114
|
-
predicate.and(attribute.eq(self
|
116
|
+
predicate.and(attribute.eq(attribute.call(self)))
|
115
117
|
end
|
116
118
|
end
|
117
119
|
|
@@ -184,7 +186,7 @@ module Veritas
|
|
184
186
|
object.kind_of?(Tuple) ? object : new(header, object)
|
185
187
|
end
|
186
188
|
|
187
|
-
memoize :
|
189
|
+
memoize :predicate, :to_ary
|
188
190
|
|
189
191
|
end # class Tuple
|
190
192
|
end # module Veritas
|
data/lib/veritas/version.rb
CHANGED
@@ -6,7 +6,7 @@ describe Algebra::Projection do
|
|
6
6
|
context 'remove attributes in predicate' do
|
7
7
|
subject { restriction.project([ :name ]) }
|
8
8
|
|
9
|
-
let(:left) { Relation.new([ [ :id, Integer ] ], (
|
9
|
+
let(:left) { Relation.new([ [ :id, Integer ] ], (0..11).map { |n| [ n ] }) }
|
10
10
|
let(:right) { Relation.new([ [ :name, String ] ], [ [ 'Dan Kubb' ], [ 'John Doe' ], [ 'Jane Doe' ] ]) }
|
11
11
|
let(:relation) { left * right }
|
12
12
|
let(:restriction) { relation.restrict { |r| r.id.gte(1).and(r.id.lte(10)).and(r.name.eq('Dan Kubb')) } }
|
@@ -1,18 +1,15 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
|
+
require 'timeout'
|
4
5
|
|
5
6
|
# use an infinite list to simulate handling a large Array.
|
6
7
|
# if any operation is inefficient, then the specs will never exit
|
7
8
|
class InfiniteList
|
8
9
|
include Enumerable
|
9
10
|
|
10
|
-
def each
|
11
|
-
index
|
12
|
-
loop do
|
13
|
-
yield [ index ]
|
14
|
-
index += 1
|
15
|
-
end
|
11
|
+
def each(&block)
|
12
|
+
0.upto(Float::INFINITY) { |index| yield [ index ] }
|
16
13
|
self
|
17
14
|
end
|
18
15
|
end
|
@@ -22,7 +19,9 @@ describe Relation do
|
|
22
19
|
let(:relation) { Relation.new([ [ :id, Integer ] ], InfiniteList.new) }
|
23
20
|
|
24
21
|
def sample(relation)
|
25
|
-
|
22
|
+
Timeout.timeout(1) do
|
23
|
+
relation.to_enum.take(5)
|
24
|
+
end
|
26
25
|
end
|
27
26
|
|
28
27
|
it '#project should be efficient' do
|
@@ -35,9 +34,40 @@ describe Relation do
|
|
35
34
|
sample(restricted).should == [ [ 6 ], [ 7 ], [ 8 ], [ 9 ], [ 10 ] ]
|
36
35
|
end
|
37
36
|
|
38
|
-
it '#
|
39
|
-
|
40
|
-
sample(
|
37
|
+
it '#rename should be efficient' do
|
38
|
+
renamed = relation.rename(:id => :other_id)
|
39
|
+
sample(renamed).should == [ [ 0 ], [ 1 ], [ 2 ], [ 3 ], [ 4 ] ]
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#join should be efficient' do
|
43
|
+
let(:other) { Relation.new([ [ :id, Integer ], [ :name, String ] ], [ [ 1, 'Dan Kubb' ] ]) }
|
44
|
+
|
45
|
+
it 'has an infinite left relation' do
|
46
|
+
pending 'Change #join to handle infinite sets'
|
47
|
+
join = other.join(relation)
|
48
|
+
sample(join).should == [ [ 1, 'Dan Kubb' ] ]
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'has an infinite right relation' do
|
52
|
+
pending 'Change #join to handle infinite sets'
|
53
|
+
join = relation.join(other)
|
54
|
+
sample(join).should == [ [ 1, 'Dan Kubb' ] ]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#product should be efficient' do
|
59
|
+
let(:other) { Relation.new([ [ :name, String ] ], [ [ 'Dan Kubb' ] ]) }
|
60
|
+
|
61
|
+
it 'has an infinite left relation' do
|
62
|
+
product = relation.product(other)
|
63
|
+
sample(product).should == [ [ 0, 'Dan Kubb' ], [ 1, 'Dan Kubb' ], [ 2, 'Dan Kubb' ], [ 3, 'Dan Kubb' ], [ 4, 'Dan Kubb' ] ]
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'has an infinite right relation' do
|
67
|
+
pending 'Change #product to handle infinite sets'
|
68
|
+
product = other.product(relation)
|
69
|
+
sample(product).should == [ [ 0, 'Dan Kubb' ], [ 1, 'Dan Kubb' ], [ 2, 'Dan Kubb' ], [ 3, 'Dan Kubb' ], [ 4, 'Dan Kubb' ] ]
|
70
|
+
end
|
41
71
|
end
|
42
72
|
|
43
73
|
it '#difference should be efficient' do
|
@@ -49,10 +79,5 @@ describe Relation do
|
|
49
79
|
union = relation.union(Relation.new(relation.header, [ [ 1 ] ]))
|
50
80
|
sample(union).should == [ [ 0 ], [ 1 ], [ 2 ], [ 3 ], [ 4 ] ]
|
51
81
|
end
|
52
|
-
|
53
|
-
it '#rename should be efficient' do
|
54
|
-
renamed = relation.rename(:id => :other_id)
|
55
|
-
sample(renamed).should == [ [ 0 ], [ 1 ], [ 2 ], [ 3 ], [ 4 ] ]
|
56
|
-
end
|
57
82
|
end
|
58
83
|
end
|
data/spec/rcov.opts
CHANGED
@@ -3,11 +3,16 @@
|
|
3
3
|
shared_examples_for 'a hash method' do
|
4
4
|
it_should_behave_like 'an idempotent method'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
specification = proc do
|
7
|
+
should be_instance_of(Fixnum)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'is a fixnum' do
|
11
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
12
|
+
pending('Under rubinius #hash does not always use a Fixnum', &specification)
|
13
|
+
else
|
14
|
+
instance_eval(&specification)
|
15
|
+
end
|
11
16
|
end
|
12
17
|
|
13
18
|
it 'memoizes the hash code' do
|
data/spec/spec_helper.rb
CHANGED
@@ -17,6 +17,6 @@ describe Algebra::Extension, '.new' do
|
|
17
17
|
context 'with a duplicate attribute name provided' do
|
18
18
|
let(:extensions) { { :id => proc {}, :name => proc {} } }
|
19
19
|
|
20
|
-
specify { expect { subject }.to raise_error(
|
20
|
+
specify { expect { subject }.to raise_error(DuplicateNameError, 'duplicate names: id, name') }
|
21
21
|
end
|
22
22
|
end
|
@@ -24,6 +24,6 @@ describe Algebra::Join, '.new' do
|
|
24
24
|
context 'with relations having different headers' do
|
25
25
|
let(:right) { Relation.new([ [ :name, String ] ], [ [ 'Dan Kubb' ] ]) }
|
26
26
|
|
27
|
-
|
27
|
+
it { should be_instance_of(object) }
|
28
28
|
end
|
29
29
|
end
|
@@ -5,16 +5,43 @@ require 'spec_helper'
|
|
5
5
|
describe Algebra::Join, '#each' do
|
6
6
|
subject { object.each { |tuple| yields << tuple } }
|
7
7
|
|
8
|
-
let(:left) { Relation.new([ [ :id, Integer ] ], [ [ 1 ], [ 2 ] ])
|
9
|
-
let(:
|
10
|
-
let(:
|
11
|
-
let(:yields) { [] }
|
8
|
+
let(:left) { Relation.new([ [ :id, Integer ] ], [ [ 1 ], [ 2 ] ]) }
|
9
|
+
let(:object) { described_class.new(left, right) }
|
10
|
+
let(:yields) { [] }
|
12
11
|
|
13
|
-
|
12
|
+
context 'when the attributes are joined' do
|
13
|
+
let(:right) { Relation.new([ [ :id, Integer ], [ :name, String ] ], [ [ 2, 'Dan Kubb' ] ]) }
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
it_should_behave_like 'an #each method'
|
16
|
+
|
17
|
+
it 'yields each tuple in the join' do
|
18
|
+
expect { subject }.to change { yields.dup }.
|
19
|
+
from([]).
|
20
|
+
to([ [ 2, 'Dan Kubb' ] ])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when the attributes are disjoint' do
|
25
|
+
let(:right) { Relation.new([ [ :name, String ] ], [ [ 'Dan Kubb' ] ]) }
|
26
|
+
|
27
|
+
it_should_behave_like 'an #each method'
|
28
|
+
|
29
|
+
it 'yields each tuple in the product' do
|
30
|
+
expect { subject }.to change { yields.dup }.
|
31
|
+
from([]).
|
32
|
+
to([ [ 1, 'Dan Kubb' ], [ 2, 'Dan Kubb' ] ])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when the attributes are an intersection' do
|
37
|
+
let(:right) { Relation.new([ [ :id, Integer ] ], [ [ 1 ] ]) }
|
38
|
+
|
39
|
+
it_should_behave_like 'an #each method'
|
40
|
+
|
41
|
+
it 'yields each tuple in the intersection' do
|
42
|
+
expect { subject }.to change { yields.dup }.
|
43
|
+
from([]).
|
44
|
+
to([ [ 1 ] ])
|
45
|
+
end
|
19
46
|
end
|
20
47
|
end
|
@@ -41,7 +41,7 @@ describe Aliasable, '#inheritable_alias' do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'sets the file and line number properly' do
|
44
|
-
if RUBY_PLATFORM
|
44
|
+
if RUBY_PLATFORM.include?('java')
|
45
45
|
pending('Kernel#caller returns the incorrect line number in JRuby', &specification)
|
46
46
|
else
|
47
47
|
instance_eval(&specification)
|
@@ -24,8 +24,11 @@ describe Comparator, '#compare' do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'defines a #hash method that uses the class and declared methods' do
|
27
|
+
# use Fixnum#eql? instead of Fixnum#equal? because the hash value may not
|
28
|
+
# always be the same object (i.e. on JRuby), and we mostly only care that
|
29
|
+
# the type and value match the expected Fixnum.
|
27
30
|
subject
|
28
|
-
instance.hash.should
|
31
|
+
instance.hash.should eql(object.hash ^ false.hash ^ instance.to_s.hash)
|
29
32
|
end
|
30
33
|
|
31
34
|
it 'memoizes #hash' do
|
@@ -24,6 +24,12 @@ describe Evaluator::Context, '#method_missing' do
|
|
24
24
|
context 'with an unknown attribute' do
|
25
25
|
subject { object.unknown }
|
26
26
|
|
27
|
-
|
27
|
+
if RUBY_VERSION >= '1.9.2' && RUBY_ENGINE == 'rbx'
|
28
|
+
# Mark this spec as pending until the following rubinius 1.9 mode bug is
|
29
|
+
# resolved: https://github.com/rubinius/rubinius/issues/1194
|
30
|
+
specify { pending 'BasicObject#method_missing does not raise NoMethodError for unknown methods' }
|
31
|
+
else
|
32
|
+
specify { expect { subject }.to raise_error(NoMethodError) }
|
33
|
+
end
|
28
34
|
end
|
29
35
|
end
|