og 0.28.0 → 0.29.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -97,7 +97,7 @@ class JoinsMany < Relation
97
97
  end
98
98
 
99
99
  #--
100
- # TODO: optimize this!!
100
+ # FIXME/TODO: optimize this!! use count.
101
101
  #++
102
102
 
103
103
  def count_#{target_plural_name}
@@ -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
@@ -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, causing "
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 ")
@@ -97,23 +97,40 @@ module SqlUtils
97
97
 
98
98
  # Escape the various Ruby types.
99
99
 
100
- def quote(val)
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 Fixnum, Integer, Float
103
- val ? val.to_s : 'NULL'
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
- # gmosx: keep the '' for nil symbols.
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
- # options[:limit] ||= 1
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
- aggregate = options[:aggregate] || 'COUNT(*)'
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
- query(sql).first_value.to_i
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
- args.each { |arg| str.sub!(/\?/, quote(arg)) }
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
- # not that this deserialization method is slower than the
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)
@@ -141,7 +141,7 @@ module Test::Unit::Assertions
141
141
  assert_block(msg) { cookie.value == value }
142
142
  end
143
143
 
144
- # :section: Template related assertions.
144
+ # :section: Glue::Template related assertions.
145
145
 
146
146
  # :section: Redirection assertions.
147
147
 
@@ -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
@@ -8,7 +8,7 @@ require 'glue/revisable'
8
8
  class TestOgRevisable < Test::Unit::TestCase # :nodoc: all
9
9
 
10
10
  class Article
11
- is Revisable
11
+ is Glue::Revisable
12
12
  property :body, String, :revisable => true
13
13
  property :title, String
14
14
 
@@ -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:
@@ -21,7 +21,7 @@ $og = Og.setup(
21
21
  class TestCaseOgTimestamped < Test::Unit::TestCase # :nodoc: all
22
22
 
23
23
  class Article
24
- is Timestamped
24
+ is Glue::Timestamped
25
25
  property :body, String
26
26
 
27
27
  def initialize(body = nil)
@@ -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