og 0.28.0 → 0.29.0
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/ProjectInfo +2 -2
- data/README +1 -1
- data/doc/RELEASES +34 -0
- data/lib/glue/cacheable.rb +3 -1
- data/lib/glue/hierarchical.rb +3 -3
- data/lib/glue/orderable.rb +1 -1
- data/lib/glue/taggable.rb +71 -21
- data/lib/glue/timestamped.rb +2 -2
- data/lib/og.rb +16 -3
- data/lib/og/entity.rb +58 -10
- data/lib/og/ez/clause.rb +2 -1
- data/lib/og/manager.rb +38 -7
- data/lib/og/relation/joins_many.rb +1 -1
- data/lib/og/store/mysql.rb +2 -18
- data/lib/og/store/psql.rb +2 -1
- data/lib/og/store/sql.rb +79 -26
- data/lib/og/test/assertions.rb +1 -1
- data/lib/og/test/testcase.rb +3 -3
- data/test/glue/tc_revisable.rb +1 -1
- data/test/og/mixin/tc_hierarchical.rb +2 -0
- data/test/og/mixin/tc_orderable.rb +2 -3
- data/test/og/mixin/tc_taggable.rb +8 -11
- data/test/og/mixin/tc_timestamped.rb +1 -1
- data/test/og/store/tc_sti.rb +53 -0
- data/test/og/tc_aggregations_calculations.rb +44 -0
- data/test/og/tc_cacheable.rb +164 -29
- data/test/og/tc_inheritance.rb +4 -1
- data/test/og/tc_multi_validations.rb +1 -0
- data/test/og/tc_override.rb +6 -0
- metadata +11 -10
- data/lib/og/store/alpha/kirby.rb +0 -284
data/lib/og/store/mysql.rb
CHANGED
@@ -67,24 +67,6 @@ module MysqlUtils
|
|
67
67
|
return nil unless str
|
68
68
|
return Mysql.quote(str)
|
69
69
|
end
|
70
|
-
|
71
|
-
def quote(val)
|
72
|
-
case val
|
73
|
-
when Fixnum, Integer, Float
|
74
|
-
val ? val.to_s : 'NULL'
|
75
|
-
when String
|
76
|
-
val ? "'#{escape(val)}'" : 'NULL'
|
77
|
-
when Time
|
78
|
-
val ? "'#{timestamp(val)}'" : 'NULL'
|
79
|
-
when Date
|
80
|
-
val ? "'#{date(val)}'" : 'NULL'
|
81
|
-
when TrueClass
|
82
|
-
val ? "'1'" : 'NULL'
|
83
|
-
else
|
84
|
-
# gmosx: keep the '' for nil symbols.
|
85
|
-
val ? escape(val.to_yaml) : ''
|
86
|
-
end
|
87
|
-
end
|
88
70
|
end
|
89
71
|
|
90
72
|
# A Store that persists objects into a MySQL database.
|
@@ -110,6 +92,7 @@ class MysqlStore < SqlStore
|
|
110
92
|
# gmosx: system is used to avoid shell expansion.
|
111
93
|
system 'mysqladmin', '-f', "--user=#{options[:user]}",
|
112
94
|
"--password=#{options[:password]}",
|
95
|
+
"--host=#{options[:address]}",
|
113
96
|
'create', options[:name]
|
114
97
|
super
|
115
98
|
end
|
@@ -117,6 +100,7 @@ class MysqlStore < SqlStore
|
|
117
100
|
def self.destroy(options)
|
118
101
|
system 'mysqladmin', '-f', "--user=#{options[:user]}",
|
119
102
|
"--password=#{options[:password]}", 'drop',
|
103
|
+
"--host=#{options[:address]}",
|
120
104
|
options[:name]
|
121
105
|
super
|
122
106
|
end
|
data/lib/og/store/psql.rb
CHANGED
@@ -516,6 +516,7 @@ class PsqlStore < SqlStore
|
|
516
516
|
deleted += 1
|
517
517
|
end
|
518
518
|
|
519
|
+
nullified_relations = 0
|
519
520
|
sql_hash[:create].each do |sql|
|
520
521
|
con_retry = true
|
521
522
|
begin
|
@@ -572,7 +573,7 @@ class PsqlStore < SqlStore
|
|
572
573
|
text << "#{deleted} constraints were deleted, " if deleted != 0
|
573
574
|
if broken_relations != 0
|
574
575
|
text.gsub!(/,([^,]+)$/,' and \1')
|
575
|
-
text << "#{broken_relations} relations were broken
|
576
|
+
text << "#{broken_relations} relations were broken causing "
|
576
577
|
if nullified_relations != 0
|
577
578
|
text << "#{nullified_relations} relations to have non-existant foreign keys set to null"
|
578
579
|
text << (deleted_relations == 0 ? ", " : " and ")
|
data/lib/og/store/sql.rb
CHANGED
@@ -97,23 +97,40 @@ module SqlUtils
|
|
97
97
|
|
98
98
|
# Escape the various Ruby types.
|
99
99
|
|
100
|
-
def quote(
|
100
|
+
def quote(vals)
|
101
|
+
vals = [vals] unless vals.is_a?(Array)
|
102
|
+
quoted = vals.inject("") do |s,val|
|
103
|
+
s += case val
|
104
|
+
when Fixnum, Integer, Float
|
105
|
+
val ? val.to_s : 'NULL'
|
106
|
+
when String
|
107
|
+
val ? "'#{escape(val)}'" : 'NULL'
|
108
|
+
when Time
|
109
|
+
val ? "'#{timestamp(val)}'" : 'NULL'
|
110
|
+
when Date
|
111
|
+
val ? "'#{date(val)}'" : 'NULL'
|
112
|
+
when TrueClass
|
113
|
+
val ? "'t'" : 'NULL'
|
114
|
+
else
|
115
|
+
# gmosx: keep the '' for nil symbols.
|
116
|
+
val ? escape(val.to_yaml) : ''
|
117
|
+
end + ','
|
118
|
+
end
|
119
|
+
quoted.chop!
|
120
|
+
vals.size > 1 ? "(#{quoted})" : quoted
|
121
|
+
end
|
122
|
+
|
123
|
+
# Escape the Array Ruby type.
|
124
|
+
|
125
|
+
def quote_array(val)
|
101
126
|
case val
|
102
|
-
when
|
103
|
-
val
|
104
|
-
when String
|
105
|
-
val ? "'#{escape(val)}'" : 'NULL'
|
106
|
-
when Time
|
107
|
-
val ? "'#{timestamp(val)}'" : 'NULL'
|
108
|
-
when Date
|
109
|
-
val ? "'#{date(val)}'" : 'NULL'
|
110
|
-
when TrueClass
|
111
|
-
val ? "'t'" : 'NULL'
|
127
|
+
when Array
|
128
|
+
val.collect{ |v| quotea(v) }.join(',')
|
112
129
|
else
|
113
|
-
|
114
|
-
val ? escape(val.to_yaml) : ''
|
130
|
+
quote(val)
|
115
131
|
end
|
116
132
|
end
|
133
|
+
alias_method :quotea, :quote_array
|
117
134
|
|
118
135
|
# Apply table name conventions to a class name.
|
119
136
|
|
@@ -354,7 +371,7 @@ class SqlStore < Store
|
|
354
371
|
|
355
372
|
def load(pk, klass)
|
356
373
|
pk_field = klass.primary_key.field || klass.primary_key.symbol
|
357
|
-
sql = "SELECT * FROM #{klass::OGTABLE} WHERE #{pk_field}=#{pk}"
|
374
|
+
sql = "SELECT * FROM #{klass::OGTABLE} WHERE #{pk_field}=#{pk.to_i}"
|
358
375
|
sql << " AND ogtype='#{klass}'" if klass.schema_inheritance_child?
|
359
376
|
res = query sql
|
360
377
|
read_one(res, klass)
|
@@ -365,7 +382,7 @@ class SqlStore < Store
|
|
365
382
|
|
366
383
|
def reload(obj, pk)
|
367
384
|
raise 'Cannot reload unmanaged object' unless obj.saved?
|
368
|
-
sql = "SELECT * FROM #{obj.class.table} WHERE #{obj.class.pk_symbol}=#{pk}"
|
385
|
+
sql = "SELECT * FROM #{obj.class.table} WHERE #{obj.class.pk_symbol}=#{pk.to_i}"
|
369
386
|
sql << " AND ogtype='#{obj.class}'" if obj.class.schema_inheritance_child?
|
370
387
|
res = query sql
|
371
388
|
obj.og_read(res.next, 0)
|
@@ -441,8 +458,8 @@ class SqlStore < Store
|
|
441
458
|
|
442
459
|
def find_one(options)
|
443
460
|
klass = options[:class]
|
444
|
-
# gmosx, THINK: should not set this by default.
|
445
|
-
#
|
461
|
+
# gmosx, THINK: should not set this by default.
|
462
|
+
# options[:limit] ||= 1
|
446
463
|
sql = resolve_options(klass, options)
|
447
464
|
read_one(query(sql), klass, options)
|
448
465
|
end
|
@@ -464,14 +481,21 @@ class SqlStore < Store
|
|
464
481
|
end
|
465
482
|
alias_method :find_by_sql_one, :select_one
|
466
483
|
|
467
|
-
# Perform an aggregation over query results.
|
484
|
+
# Perform an aggregation or calculation over query results.
|
485
|
+
# This low level method is used by the Entity
|
486
|
+
# calculation / aggregation methods.
|
487
|
+
#
|
488
|
+
# === Example
|
489
|
+
#
|
490
|
+
# calculate 'COUNT(*)'
|
491
|
+
# calculate 'MIN(age)'
|
492
|
+
# calculate 'SUM(age)', :group => :name
|
468
493
|
|
469
|
-
def aggregate(options)
|
494
|
+
def aggregate(term = 'COUNT(*)', options = {})
|
470
495
|
if options.is_a?(String)
|
471
496
|
sql = options
|
472
497
|
else
|
473
|
-
|
474
|
-
sql = "SELECT #{aggregate} FROM #{options[:class].table}"
|
498
|
+
sql = "SELECT #{term} FROM #{options[:class].table}"
|
475
499
|
if condition = options[:condition]
|
476
500
|
sql << " WHERE #{condition}"
|
477
501
|
sql << " AND " if options[:class].schema_inheritance_child?
|
@@ -479,11 +503,38 @@ class SqlStore < Store
|
|
479
503
|
sql << " WHERE " if options[:class].schema_inheritance_child?
|
480
504
|
end
|
481
505
|
sql << "ogtype='#{options[:class]}'" if options[:class].schema_inheritance_child?
|
506
|
+
if group = options[:group]
|
507
|
+
sql << " GROUP BY #{group}"
|
508
|
+
end
|
509
|
+
if extra = options[:extra_sql]
|
510
|
+
sql << " {extra}"
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
if group
|
515
|
+
# This is an aggregation, so return the calculated values
|
516
|
+
# as an array.
|
517
|
+
values = []
|
518
|
+
res = query(sql)
|
519
|
+
res.each_row do |row, idx|
|
520
|
+
values << row[0].to_f
|
521
|
+
end
|
522
|
+
return values
|
523
|
+
else
|
524
|
+
#--
|
525
|
+
# gmosx, TODO: don't convert to float by default, perhaps
|
526
|
+
# should consult an option.
|
527
|
+
#++
|
528
|
+
return query(sql).first_value.to_f
|
482
529
|
end
|
530
|
+
end
|
531
|
+
alias_method :calculate, :aggregate
|
483
532
|
|
484
|
-
|
533
|
+
# Perform a count query.
|
534
|
+
|
535
|
+
def count(options = {})
|
536
|
+
calculate('COUNT(*)', options).to_i
|
485
537
|
end
|
486
|
-
alias_method :count, :aggregate
|
487
538
|
|
488
539
|
# Relate two objects through an intermediate join table.
|
489
540
|
# Typically used in joins_many and many_to_many relations.
|
@@ -605,7 +656,7 @@ private
|
|
605
656
|
# holds the class name.
|
606
657
|
fields << "ogtype VARCHAR(30)"
|
607
658
|
|
608
|
-
for desc in klass.descendents
|
659
|
+
for desc in klass.schema_inheritance_root_class.descendents
|
609
660
|
properties.update(desc.properties)
|
610
661
|
end
|
611
662
|
end
|
@@ -925,7 +976,9 @@ private
|
|
925
976
|
if condition.is_a?(Array)
|
926
977
|
args = condition.dup
|
927
978
|
str = args.shift
|
928
|
-
|
979
|
+
# ? handles a single type.
|
980
|
+
# ?* handles an array.
|
981
|
+
args.each { |arg| str.sub!(/\?\*/, quotea(arg)); str.sub!(/\?/, quote(arg)) }
|
929
982
|
condition = str
|
930
983
|
end
|
931
984
|
|
@@ -968,7 +1021,7 @@ private
|
|
968
1021
|
|
969
1022
|
# Dynamicaly deserializes a result set row into an object.
|
970
1023
|
# Used for specialized queries or join queries. Please
|
971
|
-
#
|
1024
|
+
# note that this deserialization method is slower than the
|
972
1025
|
# precompiled og_read method.
|
973
1026
|
|
974
1027
|
def read_row(obj, res, res_row, row)
|
data/lib/og/test/assertions.rb
CHANGED
data/lib/og/test/testcase.rb
CHANGED
@@ -20,9 +20,9 @@ class TestCase
|
|
20
20
|
def fixture(*classes)
|
21
21
|
|
22
22
|
for klass in classes
|
23
|
-
f = Fixture.new(klass)
|
23
|
+
f = Glue::Fixture.new(klass)
|
24
24
|
instance_variable_set "@#{klass.to_s.demodulize.underscore.pluralize}", f
|
25
|
-
Fixtures[klass] = f
|
25
|
+
Glue::Fixtures[klass] = f
|
26
26
|
|
27
27
|
# create variables for the fixture objects.
|
28
28
|
|
@@ -40,7 +40,7 @@ class TestCase
|
|
40
40
|
fixture(*classes)
|
41
41
|
|
42
42
|
for klass in classes
|
43
|
-
f = Fixtures[klass]
|
43
|
+
f = Glue::Fixtures[klass]
|
44
44
|
|
45
45
|
for obj in f.objects
|
46
46
|
obj.save
|
data/test/glue/tc_revisable.rb
CHANGED
@@ -61,6 +61,7 @@ class TC_OgHierarchical < Test::Unit::TestCase # :nodoc: all
|
|
61
61
|
|
62
62
|
c1.reload
|
63
63
|
|
64
|
+
=begin
|
64
65
|
Comment.all(:order => "lft, rgt").each { |c|
|
65
66
|
puts sprintf("%3d %3d %s", c.lft, c.rgt, c.body)
|
66
67
|
# p c
|
@@ -71,6 +72,7 @@ class TC_OgHierarchical < Test::Unit::TestCase # :nodoc: all
|
|
71
72
|
c1.full_comments(:order => "lft, rgt").each { |c| puts c.body }
|
72
73
|
puts '--3'
|
73
74
|
c1.direct_comments(:order => "lft, rgt").each { |c| puts c.body }
|
75
|
+
=end
|
74
76
|
|
75
77
|
assert_equal 6, c1.full_comments.size
|
76
78
|
assert_equal 5, c1.comments.size
|
@@ -34,7 +34,7 @@ class TestCaseOgOrderable < Test::Unit::TestCase # :nodoc: all
|
|
34
34
|
property :body, String
|
35
35
|
belongs_to :article, Article
|
36
36
|
|
37
|
-
is Orderable, :scope => :article
|
37
|
+
is Glue::Orderable, :scope => :article
|
38
38
|
|
39
39
|
def initialize(body = nil)
|
40
40
|
@body = body
|
@@ -48,7 +48,7 @@ class TestCaseOgOrderable < Test::Unit::TestCase # :nodoc: all
|
|
48
48
|
|
49
49
|
class Track
|
50
50
|
prop :name, String
|
51
|
-
is Orderable, :scope => :playlist
|
51
|
+
is Glue::Orderable, :scope => :playlist
|
52
52
|
|
53
53
|
belongs_to Playlist
|
54
54
|
|
@@ -62,7 +62,6 @@ class TestCaseOgOrderable < Test::Unit::TestCase # :nodoc: all
|
|
62
62
|
|
63
63
|
def test_all
|
64
64
|
a = Article.create('article')
|
65
|
-
a.save
|
66
65
|
|
67
66
|
c1 = Comment.create('1')
|
68
67
|
a.comments << c1
|
@@ -1,3 +1,5 @@
|
|
1
|
+
$DBG = true
|
2
|
+
|
1
3
|
require File.join(File.dirname(__FILE__), '..', 'CONFIG.rb')
|
2
4
|
|
3
5
|
require 'rubygems'
|
@@ -8,16 +10,6 @@ require 'ostruct'
|
|
8
10
|
require 'og'
|
9
11
|
require 'glue/taggable'
|
10
12
|
|
11
|
-
=begin
|
12
|
-
$og = Og.setup(
|
13
|
-
:store => 'mysql',
|
14
|
-
:name => 'test',
|
15
|
-
:user => 'root',
|
16
|
-
:password => 'navelrulez',
|
17
|
-
:destroy => true
|
18
|
-
)
|
19
|
-
=end
|
20
|
-
|
21
13
|
class TestOgTaggable < Test::Unit::TestCase # :nodoc: all
|
22
14
|
|
23
15
|
class Article
|
@@ -43,7 +35,9 @@ class TestOgTaggable < Test::Unit::TestCase # :nodoc: all
|
|
43
35
|
a3 = Article.create('George')
|
44
36
|
a3.tag('phd name')
|
45
37
|
a3.save
|
46
|
-
|
38
|
+
|
39
|
+
|
40
|
+
assert_equal 6, Tag.count
|
47
41
|
assert_equal 3, a1.tags.size
|
48
42
|
assert a1.tag_names.include?('navel')
|
49
43
|
|
@@ -63,6 +57,9 @@ class TestOgTaggable < Test::Unit::TestCase # :nodoc: all
|
|
63
57
|
|
64
58
|
res = Article.find_with_any_tag('navel', 'gmosx', 'phd')
|
65
59
|
assert_equal 3, res.size
|
60
|
+
|
61
|
+
a1.delete_all_tags
|
62
|
+
assert_equal 0, a1.tags.size
|
66
63
|
|
67
64
|
=begin
|
68
65
|
TODO:
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'CONFIG.rb')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
require 'og'
|
6
|
+
require 'og/store/sql'
|
7
|
+
|
8
|
+
class TC_Sti < Test::Unit::TestCase
|
9
|
+
class StiParent
|
10
|
+
property :one
|
11
|
+
property :two
|
12
|
+
|
13
|
+
schema_inheritance
|
14
|
+
end
|
15
|
+
|
16
|
+
class StiChild < StiParent
|
17
|
+
property :three
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup
|
21
|
+
@store = Og::SqlStore.new(nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
def teardown
|
25
|
+
@store = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_fields_for_class_child
|
29
|
+
fields = @store.__send__(:fields_for_class, StiChild)
|
30
|
+
field_names = fields.map {|f| name, * = f.split(/\s/); name}
|
31
|
+
|
32
|
+
msg = "Sti Child not inheiriting parent fields"
|
33
|
+
|
34
|
+
assert_equal(5, field_names.size, msg)
|
35
|
+
assert(field_names.member?(:one.to_s), msg)
|
36
|
+
assert(field_names.member?(:two.to_s), msg)
|
37
|
+
assert(field_names.member?(:three.to_s), msg)
|
38
|
+
assert(field_names.member?(:ogtype.to_s), msg)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_fields_for_class_parent
|
42
|
+
fields = @store.__send__(:fields_for_class, StiParent)
|
43
|
+
field_names = fields.map {|f| name, * = f.split(/\s/); name}
|
44
|
+
|
45
|
+
msg = "Sti Parent not collecting child fields"
|
46
|
+
|
47
|
+
assert_equal(5, field_names.size, msg)
|
48
|
+
assert(field_names.member?(:one.to_s), msg)
|
49
|
+
assert(field_names.member?(:two.to_s), msg)
|
50
|
+
assert(field_names.member?(:three.to_s), msg)
|
51
|
+
assert(field_names.member?(:ogtype.to_s), msg)
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'CONFIG.rb')
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'facets'
|
5
|
+
|
6
|
+
require 'test/unit'
|
7
|
+
require 'og'
|
8
|
+
|
9
|
+
class TC_OgAggrCalc < Test::Unit::TestCase # :nodoc: all
|
10
|
+
include Og
|
11
|
+
|
12
|
+
class User
|
13
|
+
property :section, String
|
14
|
+
property :age, Fixnum
|
15
|
+
|
16
|
+
def initialize(section, age)
|
17
|
+
@section = section
|
18
|
+
@age = age
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
$og1.manage_classes(User)
|
23
|
+
|
24
|
+
def test_all
|
25
|
+
User.create('a', 12)
|
26
|
+
User.create('a', 16)
|
27
|
+
User.create('d', 34)
|
28
|
+
User.create('d', 33)
|
29
|
+
User.create('c', 27)
|
30
|
+
User.create('c', 31)
|
31
|
+
|
32
|
+
assert_equal 6, User.count
|
33
|
+
assert_equal 12, User.minimum(:age)
|
34
|
+
assert_equal 34, User.maximum(:age)
|
35
|
+
assert_equal 34, User.max(:age)
|
36
|
+
assert_equal 25.5, User.avg(:age)
|
37
|
+
assert_equal 153, User.sum(:age)
|
38
|
+
|
39
|
+
sums = User.sum(:age, :group => :section)
|
40
|
+
assert_equal 3, sums.size
|
41
|
+
assert_equal 28, sums[0]
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|