rhubarb 0.2.6 → 0.2.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/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
|