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.
- 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
|