rhubarb 0.2.6 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +5 -0
- data/Rakefile +2 -0
- data/VERSION +1 -1
- data/lib/rhubarb/classmixins.rb +15 -5
- data/lib/rhubarb/mixins/freshness.rb +6 -8
- data/lib/rhubarb/util.rb +11 -19
- data/test/test_rhubarb.rb +102 -15
- metadata +2 -2
data/CHANGES
CHANGED
data/Rakefile
CHANGED
@@ -109,6 +109,7 @@ end
|
|
109
109
|
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
110
110
|
spec.libs << 'lib' << 'spec'
|
111
111
|
spec.pattern = 'spec/**/*_spec.rb'
|
112
|
+
spec.rcov_opts << "-x" << "/usr/lib" << "-x" << ".gem"
|
112
113
|
spec.rcov = true
|
113
114
|
end
|
114
115
|
|
@@ -127,6 +128,7 @@ begin
|
|
127
128
|
Rcov::RcovTask.new do |test|
|
128
129
|
test.libs << 'test'
|
129
130
|
test.pattern = 'test/**/test_*.rb'
|
131
|
+
test.rcov_opts << "-x" << "/usr/lib" << "-x" << ".gem"
|
130
132
|
test.verbose = true
|
131
133
|
end
|
132
134
|
rescue LoadError
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.7
|
data/lib/rhubarb/classmixins.rb
CHANGED
@@ -55,7 +55,7 @@ module Rhubarb
|
|
55
55
|
arg_hash = arg_hash.dup
|
56
56
|
valid_cols = self.colnames.intersection arg_hash.keys
|
57
57
|
select_criteria = valid_cols.map {|col| "#{col.to_s} = #{col.inspect}"}.join(" AND ")
|
58
|
-
arg_hash.each {|
|
58
|
+
arg_hash.each {|key,val| arg_hash[key] = val.row_id if val.respond_to? :row_id}
|
59
59
|
db.do_query("select * from #{table_name} where #{select_criteria} order by row_id", arg_hash) {|tup| results << self.new(tup) }
|
60
60
|
results
|
61
61
|
end
|
@@ -75,7 +75,7 @@ module Rhubarb
|
|
75
75
|
arg_hash = arg_hash.dup
|
76
76
|
valid_cols = self.colnames.intersection arg_hash.keys
|
77
77
|
select_criteria = valid_cols.map {|col| "#{col.to_s} = #{col.inspect}"}.join(" AND ")
|
78
|
-
arg_hash.each {|
|
78
|
+
arg_hash.each {|key,val| arg_hash[key] = val.row_id if val.respond_to? :row_id}
|
79
79
|
db.do_query("DELETE FROM #{table_name} WHERE #{select_criteria}", arg_hash)
|
80
80
|
end
|
81
81
|
|
@@ -92,7 +92,16 @@ module Rhubarb
|
|
92
92
|
klass.class_eval do
|
93
93
|
define_method name.to_s do |*args|
|
94
94
|
# handle reference parameters
|
95
|
-
args
|
95
|
+
if args.size == 1 && args[0].is_a?(Hash)
|
96
|
+
args[0].each do |k,v|
|
97
|
+
args[0][k] = Util::rhubarb_fk_identity(v)
|
98
|
+
end
|
99
|
+
else
|
100
|
+
args = args.map do |arg|
|
101
|
+
raise RuntimeError.new("Hash-valued positional parameters may only appear as named positional parameters.") if arg.is_a?(Hash)
|
102
|
+
Util::rhubarb_fk_identity(arg)
|
103
|
+
end
|
104
|
+
end
|
96
105
|
|
97
106
|
results = []
|
98
107
|
db.do_query(processed_query, args) {|tup| results << self.new(tup)}
|
@@ -124,7 +133,7 @@ module Rhubarb
|
|
124
133
|
set_method_name = "#{cname}=".to_sym
|
125
134
|
|
126
135
|
# does this column reference another table?
|
127
|
-
rf = quals.find {|
|
136
|
+
rf = quals.find {|qual| qual.class == Reference}
|
128
137
|
if rf
|
129
138
|
self.refs[cname] = rf
|
130
139
|
end
|
@@ -154,7 +163,8 @@ module Rhubarb
|
|
154
163
|
define_method get_method_name do
|
155
164
|
freshen
|
156
165
|
return nil unless @tuple
|
157
|
-
|
166
|
+
result = @tuple[cname.to_s]
|
167
|
+
out_xform ? out_xform.call(result) : result
|
158
168
|
end
|
159
169
|
|
160
170
|
if not rf
|
@@ -39,24 +39,22 @@ module Rhubarb
|
|
39
39
|
group_by_clause = "GROUP BY " + args[:group_by].join(", ")
|
40
40
|
where_clause = "WHERE " + select_clauses.join(" AND ")
|
41
41
|
projection = self.colnames - [:created]
|
42
|
-
|
42
|
+
join_criteria = projection.map do |column|
|
43
43
|
"__fresh.#{column} = __freshest.#{column}"
|
44
44
|
end
|
45
45
|
|
46
46
|
projection << "MAX(created) AS __current_version"
|
47
|
-
|
47
|
+
join_criteria << "__fresh.__current_version = __freshest.created"
|
48
48
|
|
49
|
+
join_clause = join_criteria.join(' AND ')
|
50
|
+
|
49
51
|
query = "
|
50
52
|
SELECT __freshest.* FROM (
|
51
53
|
SELECT #{projection.to_a.join(', ')} FROM (
|
52
54
|
SELECT * from #{table_name} #{where_clause}
|
53
55
|
) #{group_by_clause}
|
54
|
-
) as __fresh INNER JOIN #{table_name} as __freshest ON
|
55
|
-
#{join_clause.join(' AND ')}
|
56
|
-
ORDER BY row_id
|
57
|
-
"
|
58
|
-
|
56
|
+
) as __fresh INNER JOIN #{table_name} as __freshest ON #{join_clause} ORDER BY row_id"
|
59
57
|
self.db.execute(query, query_params).map {|tup| self.new(tup) }
|
60
58
|
end
|
61
59
|
end
|
62
|
-
end
|
60
|
+
end
|
data/lib/rhubarb/util.rb
CHANGED
@@ -27,43 +27,35 @@ module Rhubarb
|
|
27
27
|
(object.row_id if object.class.ancestors.include? Persisting) || object
|
28
28
|
end
|
29
29
|
|
30
|
-
def self.blobify(obj)
|
31
|
-
blobify_proc.call(obj)
|
32
|
-
end
|
33
|
-
|
34
30
|
def self.blobify_proc
|
35
|
-
@blobify_proc ||= Proc.new {|
|
31
|
+
@blobify_proc ||= Proc.new {|obj| obj.is_a?(SQLite3::Blob) ? obj : SQLite3::Blob.new(obj)}
|
36
32
|
end
|
37
33
|
|
38
34
|
def self.zblobify_proc
|
39
|
-
@zblobify_proc ||= Proc.new {|
|
35
|
+
@zblobify_proc ||= Proc.new {|obj| obj.is_a?(SQLite3::Blob) ? obj : SQLite3::Blob.new(Zlib::Deflate.deflate(obj))}
|
40
36
|
end
|
41
37
|
|
42
38
|
def self.dezblobify_proc
|
43
|
-
@dezblobify_proc ||= Proc.new do |
|
44
|
-
return nil if
|
45
|
-
Zlib::Inflate.inflate(
|
39
|
+
@dezblobify_proc ||= Proc.new do |obj|
|
40
|
+
return nil if obj.nil? || obj == ""
|
41
|
+
Zlib::Inflate.inflate(obj)
|
46
42
|
end
|
47
43
|
end
|
48
44
|
|
49
45
|
def self.swizzle_object_proc
|
50
|
-
@swizzle_object_proc ||= Proc.new do |
|
51
|
-
yamlrepr =
|
46
|
+
@swizzle_object_proc ||= Proc.new do |obj|
|
47
|
+
yamlrepr = obj.to_yaml
|
52
48
|
SQLite3::Blob.new(Zlib::Deflate.deflate(yamlrepr, Zlib::BEST_COMPRESSION))
|
53
49
|
end
|
54
50
|
end
|
55
51
|
|
56
52
|
def self.deswizzle_object_proc
|
57
|
-
@deswizzle_object_proc ||= Proc.new do |
|
58
|
-
return nil if
|
53
|
+
@deswizzle_object_proc ||= Proc.new do |zy_obj|
|
54
|
+
return nil if zy_obj.nil? || zy_obj == ""
|
59
55
|
|
60
|
-
|
61
|
-
|
56
|
+
obj = YAML.load(Zlib::Inflate.inflate(zy_obj))
|
57
|
+
obj.freeze
|
62
58
|
end
|
63
59
|
end
|
64
|
-
|
65
|
-
def self.identity_proc
|
66
|
-
@identity ||= Proc.new {|x| x}
|
67
|
-
end
|
68
60
|
end
|
69
61
|
end
|
data/test/test_rhubarb.rb
CHANGED
@@ -78,9 +78,11 @@ class ToRef
|
|
78
78
|
declare_column :foo, :string
|
79
79
|
end
|
80
80
|
|
81
|
-
class FromRef
|
81
|
+
class FromRef
|
82
82
|
include Rhubarb::Persisting
|
83
83
|
declare_column :t, :integer, references(ToRef, :on_delete=>:cascade)
|
84
|
+
declare_query :to_is, "t = ?"
|
85
|
+
declare_query :to_is_hash, "t = :t"
|
84
86
|
end
|
85
87
|
|
86
88
|
class FreshTestTable
|
@@ -89,6 +91,16 @@ class FreshTestTable
|
|
89
91
|
declare_column :fie, :integer
|
90
92
|
declare_column :foe, :integer
|
91
93
|
declare_column :fum, :integer
|
94
|
+
declare_column :foo, :integer
|
95
|
+
declare_column :bar, :integer
|
96
|
+
|
97
|
+
def <=>(other)
|
98
|
+
[:fie,:foe,:fum,:foo,:bar].each do |msg|
|
99
|
+
tmpresult = self.send(msg) <=> other.send(msg)
|
100
|
+
return tmpresult unless tmpresult == 0
|
101
|
+
end
|
102
|
+
return 0
|
103
|
+
end
|
92
104
|
end
|
93
105
|
|
94
106
|
class BlobTestTable
|
@@ -641,6 +653,10 @@ class PreparedStmtBackendTests < Test::Unit::TestCase
|
|
641
653
|
assert_equal(tc1, tc1) # equality is reflexive
|
642
654
|
assert_equal(tc1p, tc1) # even after find operations
|
643
655
|
assert_equal(tc1, tc1p) # ... and it should be symmetric
|
656
|
+
|
657
|
+
assert_equal(tc1.hash, tc1p.hash)
|
658
|
+
assert_not_equal(tc2.hash, tc1p.hash)
|
659
|
+
|
644
660
|
assert_not_equal(tc1, tc2) # these are not identical
|
645
661
|
assert_not_equal(tc1p, tc2) # even after find operations
|
646
662
|
assert_not_equal(tc2, tc1p) # ... and it should be symmetric
|
@@ -653,10 +669,29 @@ class PreparedStmtBackendTests < Test::Unit::TestCase
|
|
653
669
|
|
654
670
|
def freshness_query_fixture
|
655
671
|
@flist = []
|
656
|
-
|
657
|
-
|
658
|
-
|
672
|
+
@fresh_fields = [:fee,:fie,:foe,:fum,:foo,:bar,:created,:updated,:row_id]
|
673
|
+
@ffcounts = {:fie=>2, :foe=>3, :fum=>4, :foo=>5, :bar=>6}
|
674
|
+
|
675
|
+
x = 0
|
676
|
+
|
677
|
+
2.times do
|
678
|
+
@ffcounts[:fie].times do |fie|
|
679
|
+
@ffcounts[:foe].times do |foe|
|
680
|
+
@ffcounts[:fum].times do |fum|
|
681
|
+
@ffcounts[:foo].times do |foo|
|
682
|
+
@ffcounts[:bar].times do |bar|
|
683
|
+
row = FreshTestTable.create(:fee=>@flist.size, :fie=>fie, :foe=>foe, :fum=>fum, :foo=>foo, :bar=>bar)
|
684
|
+
assert(row != nil)
|
685
|
+
@flist << row
|
686
|
+
end
|
687
|
+
end
|
688
|
+
end
|
689
|
+
end
|
690
|
+
end
|
659
691
|
end
|
692
|
+
|
693
|
+
@fcount = @flist.size
|
694
|
+
@ffcounts[:fee] = @fcount
|
660
695
|
end
|
661
696
|
|
662
697
|
def test_freshness_query_basic
|
@@ -665,8 +700,8 @@ class PreparedStmtBackendTests < Test::Unit::TestCase
|
|
665
700
|
basic = FreshTestTable.find_freshest(:group_by=>[:fee])
|
666
701
|
|
667
702
|
assert_equal(@flist.size, basic.size)
|
668
|
-
0.upto(
|
669
|
-
|
703
|
+
0.upto(@fcount - 1) do |x|
|
704
|
+
@fresh_fields.each do |msg|
|
670
705
|
assert_equal(@flist[x].send(msg), basic[x].send(msg))
|
671
706
|
end
|
672
707
|
end
|
@@ -680,7 +715,7 @@ class PreparedStmtBackendTests < Test::Unit::TestCase
|
|
680
715
|
|
681
716
|
assert_equal(31, basic.size)
|
682
717
|
0.upto(30) do |x|
|
683
|
-
|
718
|
+
@fresh_fields.each do |msg|
|
684
719
|
assert_equal(@flist[x].send(msg), basic[x].send(msg))
|
685
720
|
end
|
686
721
|
end
|
@@ -690,15 +725,18 @@ class PreparedStmtBackendTests < Test::Unit::TestCase
|
|
690
725
|
freshness_query_fixture
|
691
726
|
# basic test
|
692
727
|
|
693
|
-
|
728
|
+
ffc = @ffcounts[:fie]
|
694
729
|
|
695
|
-
|
730
|
+
basic = FreshTestTable.find_freshest(:group_by=>[:fee], :select_by=>{:fie=>0}, :debug=>true).sort_by {|x| x.fee}
|
731
|
+
|
732
|
+
expected_ct = @fcount/ffc
|
733
|
+
expected_rows = @flist.select{|tup| tup.fie == 0}.sort_by{|tup| tup.fee}
|
696
734
|
|
697
735
|
assert_equal(expected_ct, basic.size)
|
698
736
|
|
699
737
|
0.upto(expected_ct - 1) do |x|
|
700
|
-
|
701
|
-
assert_equal(
|
738
|
+
@fresh_fields.each do |msg|
|
739
|
+
assert_equal(expected_rows[x].send(msg), basic[x].send(msg))
|
702
740
|
end
|
703
741
|
end
|
704
742
|
end
|
@@ -706,26 +744,56 @@ class PreparedStmtBackendTests < Test::Unit::TestCase
|
|
706
744
|
def test_freshness_query_group_single
|
707
745
|
freshness_query_fixture
|
708
746
|
# more basic tests
|
709
|
-
pairs =
|
747
|
+
pairs = @ffcounts.dup
|
748
|
+
pairs.delete(:fee)
|
749
|
+
|
710
750
|
pairs.each do |col,ct|
|
711
751
|
basic = FreshTestTable.find_freshest(:group_by=>[col])
|
712
752
|
assert_equal(ct,basic.size)
|
713
753
|
|
714
754
|
expected_objs = {}
|
715
755
|
|
716
|
-
|
717
|
-
|
756
|
+
needed_vals = Set[*(0..ct).to_a]
|
757
|
+
|
758
|
+
@flist.reverse_each do |row|
|
759
|
+
break if needed_vals.empty?
|
760
|
+
|
761
|
+
colval = row.send(col)
|
762
|
+
if needed_vals.include? colval
|
763
|
+
expected_objs[colval] = row
|
764
|
+
needed_vals.delete(colval)
|
765
|
+
end
|
718
766
|
end
|
719
767
|
|
720
768
|
basic.each do |row|
|
721
769
|
res = expected_objs[row.send(col)]
|
722
|
-
|
770
|
+
puts expected_objs.keys.inspect if res.nil?
|
771
|
+
@fresh_fields.each do |msg|
|
723
772
|
assert_equal(res.send(msg), row.send(msg))
|
724
773
|
end
|
725
774
|
end
|
726
775
|
end
|
727
776
|
end
|
728
777
|
|
778
|
+
def test_freshness_query_group_powerset
|
779
|
+
freshness_query_fixture
|
780
|
+
# more basic tests
|
781
|
+
|
782
|
+
pairs = @ffcounts.dup
|
783
|
+
pairs.delete(:fee)
|
784
|
+
pairs_powerset = pairs.to_a.inject([[]]){|c,y|r=[];c.each{|i|r<<i;r<<i+[y]};r}.map {|h| h.transpose}
|
785
|
+
pairs_powerset.shift
|
786
|
+
|
787
|
+
pairs_powerset.each do |cols,cts|
|
788
|
+
expected_ct = cts.inject(1){|x,acc| acc * x}
|
789
|
+
basic = FreshTestTable.find_freshest(:group_by=>cols)
|
790
|
+
|
791
|
+
assert_equal(expected_ct,basic.size)
|
792
|
+
|
793
|
+
# XXX: test identities, too
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
729
797
|
def test_blob_data
|
730
798
|
words = %w{the quick brown fox jumps over a lazy dog now is time for all good men to come aid party jackdaws love my big sphinx of quartz}
|
731
799
|
text = ""
|
@@ -799,6 +867,25 @@ class PreparedStmtBackendTests < Test::Unit::TestCase
|
|
799
867
|
assert_equal thing, ott.obj
|
800
868
|
end
|
801
869
|
end
|
870
|
+
|
871
|
+
[true, false].each do |create_uses_rowid|
|
872
|
+
[true, false].each do |lookup_uses_rowid|
|
873
|
+
[true, false].each do |to_is_hash|
|
874
|
+
|
875
|
+
define_method("test_reference_params_in_#{to_is_hash ? "hashed_" : ""}custom_queries_#{create_uses_rowid ? "createUsesRowID" : "createUsesObject"}_#{lookup_uses_rowid ? "lookupUsesRowID" : "lookupUsesObject"}".to_sym) do
|
876
|
+
to_refs = %w{foo bar blah}.map {|s| ToRef.create(:foo=>s)}
|
877
|
+
to_refs.each {}
|
878
|
+
to_refs.each {|tr| FromRef.create(:t=>create_uses_rowid ? tr.row_id : tr)}
|
879
|
+
|
880
|
+
to_refs.each do |tr|
|
881
|
+
frs = to_is_hash ? FromRef.to_is_hash(:t=>lookup_uses_rowid ? tr.row_id : tr) : FromRef.to_is(lookup_uses_rowid ? tr.row_id : tr)
|
882
|
+
assert_equal frs.size, 1
|
883
|
+
assert_equal frs[0].t, tr
|
884
|
+
end
|
885
|
+
end
|
886
|
+
end
|
887
|
+
end
|
888
|
+
end
|
802
889
|
end
|
803
890
|
|
804
891
|
class NoPreparedStmtBackendTests < PreparedStmtBackendTests
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rhubarb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William Benton
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-
|
12
|
+
date: 2010-08-31 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|