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