og 0.25.0 → 0.26.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.
@@ -76,6 +76,15 @@ module SqlUtils
76
76
  return Date.strptime(str)
77
77
  end
78
78
 
79
+ # Parse a boolean
80
+ # true, 1, t => true
81
+ # other => false
82
+
83
+ def parse_boolean(str)
84
+ return true if (str=='true' || str=='t' || str=='1')
85
+ return false
86
+ end
87
+
79
88
  #--
80
89
  # TODO: implement me!!
81
90
  #++
@@ -167,7 +176,10 @@ module SqlUtils
167
176
  def join_table_info(owner_class, target_class, postfix = nil)
168
177
 
169
178
  # some fixes for schema inheritance.
170
-
179
+
180
+ raise "Undefined owner_class in #{target_class}" unless owner_class
181
+ raise "Undefined target_class in #{owner_class}" unless target_class
182
+
171
183
  owner_class = owner_class.schema_inheritance_root_class if owner_class.schema_inheritance_child?
172
184
  target_class = target_class.schema_inheritance_root_class if target_class.schema_inheritance_child?
173
185
 
@@ -333,7 +345,8 @@ class SqlStore < Store
333
345
  # Loads an object from the store using the primary key.
334
346
 
335
347
  def load(pk, klass)
336
- sql = "SELECT * FROM #{klass::OGTABLE} WHERE #{klass.pk_symbol}=#{pk}"
348
+ pk_field = klass.primary_key.field || klass.primary_key.symbol
349
+ sql = "SELECT * FROM #{klass::OGTABLE} WHERE #{pk_field}=#{pk}"
337
350
  sql << " AND ogtype='#{klass}'" if klass.schema_inheritance_child?
338
351
  res = query sql
339
352
  read_one(res, klass)
@@ -430,7 +443,7 @@ class SqlStore < Store
430
443
  # results.
431
444
 
432
445
  def select(sql, klass)
433
- sql = "SELECT * FROM #{klass.table} " + sql unless sql =~ /SELECT/
446
+ sql = "SELECT * FROM #{klass.table} " + sql unless sql =~ /SELECT/i
434
447
  read_all(query(sql), klass)
435
448
  end
436
449
  alias_method :find_by_sql, :select
@@ -438,7 +451,7 @@ class SqlStore < Store
438
451
  # Specialized one result version of select.
439
452
 
440
453
  def select_one(sql, klass)
441
- sql = "SELECT * FROM #{klass.table} " + sql unless sql =~ /SELECT/
454
+ sql = "SELECT * FROM #{klass.table} " + sql unless sql =~ /SELECT/i
442
455
  read_one(query(sql), klass)
443
456
  end
444
457
  alias_method :find_by_sql_one, :select_one
@@ -545,6 +558,7 @@ private
545
558
  exec "DROP TABLE #{klass.table}"
546
559
  end
547
560
  alias_method :destroy, :drop_table
561
+ alias_method :drop_schema, :drop_schema
548
562
 
549
563
  # Evolve (recreate) the sql table where objects of this class
550
564
  # are persisted.
@@ -654,7 +668,7 @@ private
654
668
  elsif p.klass.ancestors.include?(Date)
655
669
  return "#{self.class}.parse_date(res[#{col} + offset])"
656
670
  elsif p.klass.ancestors.include?(TrueClass)
657
- return "('0' != res[#{col} + offset])"
671
+ return "#{self.class}.parse_boolean(res[#{col} + offset])"
658
672
  elsif p.klass.ancestors.include?(Og::Blob)
659
673
  return "#{self.class}.parse_blob(res[#{col} + offset])"
660
674
  else
@@ -691,13 +705,15 @@ private
691
705
 
692
706
  def eval_og_update(klass)
693
707
  pk = klass.pk_symbol
708
+ pk_field = klass.primary_key.field || klass.primary_key.symbol
709
+
694
710
  props = klass.properties.values.reject { |p| pk == p.symbol }
695
-
711
+
696
712
  updates = props.collect { |p|
697
713
  "#{field_for_property(p)}=#{write_prop(p)}"
698
714
  }
699
715
 
700
- sql = "UPDATE #{klass::OGTABLE} SET #{updates.join(', ')} WHERE #{pk}=#\{@#{pk}\}"
716
+ sql = "UPDATE #{klass::OGTABLE} SET #{updates.join(', ')} WHERE #{pk_field}=#\{@#{pk}\}"
701
717
 
702
718
  klass.module_eval %{
703
719
  def og_update(store, options = nil)
@@ -770,9 +786,9 @@ private
770
786
  def og_create_schema(store)
771
787
  if Og.create_schema
772
788
  #{Glue::Aspects.gen_advice_code(:og_create_schema, klass.advices, :pre) if klass.respond_to?(:advices)}
773
- unless self.class.superclass.ancestors.include? SchemaInheritanceBase
789
+ # unless self.class.superclass.ancestors.include? SchemaInheritanceBase
774
790
  store.send(:create_table, #{klass})
775
- end
791
+ # end
776
792
  #{Glue::Aspects.gen_advice_code(:og_create_schema, klass.advices, :post) if klass.respond_to?(:advices)}
777
793
  end
778
794
  end
@@ -825,7 +841,7 @@ private
825
841
  end
826
842
 
827
843
  if sql = options[:sql]
828
- sql = "SELECT * FROM #{klass.table} " + sql unless sql =~ /SELECT/
844
+ sql = "SELECT * FROM #{klass.table} " + sql unless sql =~ /SELECT/i
829
845
  return sql
830
846
  end
831
847
 
@@ -1021,4 +1037,4 @@ end
1021
1037
  # * Ghislain Mary
1022
1038
  # * Ysabel <deb@ysabel.org>
1023
1039
  # * Guillaume Pierronnet <guillaume.pierronnet@laposte.net>
1024
- # * Rob Pitt <rob@motionpath.com>
1040
+ # * Rob Pitt <rob@motionpath.com>
@@ -80,7 +80,7 @@ class SqliteStore < SqlStore
80
80
  def query(sql)
81
81
  Logger.debug sql if $DBG
82
82
  return @conn.query(sql)
83
- rescue => ex
83
+ rescue Exception => ex
84
84
  handle_sql_exception(ex, sql)
85
85
  end
86
86
 
@@ -162,8 +162,8 @@ private
162
162
  for info in join_tables
163
163
  begin
164
164
  create_join_table_sql(info).each do |sql|
165
- @conn.query(sql).close
166
- end
165
+ @conn.query(sql).close
166
+ end
167
167
  Logger.debug "Created jointable '#{info[:table]}'."
168
168
  rescue Object => ex
169
169
  # gmosx: any idea how to better test this?
@@ -20,7 +20,7 @@ module Validation
20
20
  # Ensures that if a record is found with a matching value,
21
21
  # that it is the same record, allowing updates.
22
22
  #
23
- # The Og libraries are required for this methdod to
23
+ # The Og libraries are required for this method to
24
24
  # work. You can override this method if you want to
25
25
  # use another OR mapping library.
26
26
  #
@@ -31,57 +31,75 @@ module Validation
31
31
  # TODO: :unique should implicitly generate
32
32
  # validate_unique.
33
33
  #++
34
-
35
- def validate_unique(*params)
36
- c = {
37
- :on => :save
38
- }
39
- c.update(params.pop) if params.last.is_a?(Hash)
40
34
 
41
- for name in params do
42
- c[:msg] ||= "#{self.name} with this #{name} already exists"
35
+ def validate_unique(*params)
36
+ c = parse_config(params, :on => :save)
43
37
 
44
- code = %{
45
- others = obj.class.send(:find_by_#{name}, obj.send(:#{name}))
38
+ params.each do |field|
39
+ c[:msg] ||= "#{self.name} with this #{field} already exists"
46
40
 
47
- unless others.empty?
48
- if (others.size != 1) or (others[0].oid != obj.oid)
49
- errors.add(:#{name}, '#{c[:msg]}')
50
- end
41
+ define_validation(:unique, field, c[:on]) do |obj|
42
+ # What to do if value is nil? Right now it is an empty string,
43
+ # which can need to be unique.
44
+ value = obj.send(field) || ''
45
+ others = [*obj.class.send("find_by_#{field}".to_sym, value)]
46
+
47
+ unless others[0].nil?
48
+ obj.errors.add(field, c[:msg]) if others.size > 1 or others[0].oid != obj.oid
51
49
  end
52
- }
53
-
54
- validations! << [code, c[:on]]
50
+ end
55
51
  end
56
52
  end
57
53
 
58
- # Validate the related object or objects. Works
59
- # with all relations.
54
+ # Validate that a property relation is not nil.
55
+ # This works for all relations.
56
+ #
57
+ # You should not put a validation in both related
58
+ # classes, because it can cause infinite looping.
59
+ #
60
+ # The Og libraries are required for this method to
61
+ # work. You can override this method if you want to
62
+ # use another OR mapping library.
63
+ #
64
+ # === Example
65
+ #
66
+ # class Uncle
67
+ # has_many :secrets, Secret
68
+ # validate_related :secrets
69
+ # end
70
+ #
71
+ # class Secret
72
+ # belongs_to :uncle, Uncle
73
+ # end
60
74
 
61
75
  def validate_related(*params)
62
- c = {
76
+ c = parse_config(params,
63
77
  :msg => Glue::Validation::Errors.invalid_relation,
64
78
  :on => :save
65
- }
66
- c.update(params.pop) if params.last.is_a?(Hash)
67
-
68
- for name in params
69
- code = %{
70
- unless (obj.#{name}.is_a?(Array) ? obj.#{name} : [obj.#{name}]).inject(true) { |memo, obj| (obj.nil? or obj.valid?) and memo }
71
- errors.add(:#{name}, '#{c[:msg]}')
72
- end;
73
- }
79
+ )
74
80
 
75
- validations! << [code, c[:on]]
81
+ params.each do |field|
82
+ define_validation(:related, field, c[:on]) do |obj|
83
+ value = obj.send(field)
84
+ if value.members.length == 0
85
+ obj.errors.add(field, c[:msg])
86
+ else
87
+ valid = value.members.inject do |memo, rel_obj|
88
+ (rel_obj.nil? or rel_obj.valid?) and memo
89
+ end
90
+ obj.errors.add(field, c[:msg]) unless valid
91
+ end
92
+ end
76
93
  end
77
94
  end
95
+
78
96
  alias_method :validate_associated, :validate_related
79
97
 
80
- end
81
-
82
- end
98
+ end
99
+
100
+ end
83
101
 
84
102
  end
85
103
 
86
104
  # * George Moschovitis <gm@navel.gr>
87
- # * Bryan Soto
105
+ # * Bryan Soto
@@ -0,0 +1,62 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'og', 'CONFIG.rb')
2
+
3
+ require 'test/unit'
4
+
5
+ require 'og'
6
+ require 'glue/revisable'
7
+
8
+ class Article
9
+ is Revisable
10
+ property :body, String, :revisable => true
11
+ property :title, String
12
+
13
+ def initialize(t, b)
14
+ @title, @body = t, b
15
+ end
16
+ end
17
+
18
+ $og = Og.setup($og_config)
19
+
20
+ class TestOgRevisable < Test::Unit::TestCase # :nodoc: all
21
+
22
+ def test_all
23
+ a = Article.create('hello', 'world')
24
+ a.revise do |a|
25
+ a.body = 'wow!'
26
+ end
27
+ a.revise do |a|
28
+ a.body = 'nice'
29
+ end
30
+ a.revise do |a|
31
+ a.body = 'it'
32
+ end
33
+
34
+ assert_equal 3, a.revisions.count
35
+
36
+ rev = a.get_revision(2)
37
+ assert_equal 'wow!', rev.body
38
+
39
+ a.rollback(2)
40
+ assert_equal 'wow!', a.body
41
+
42
+ a.rollback(1)
43
+ assert_equal 'world', a.body
44
+
45
+ a.rollback(3)
46
+ assert_equal 'nice', a.body
47
+
48
+ assert_equal 6, a.revisions.count
49
+
50
+ a.revise do |a|
51
+ a.title = 'kicks'
52
+ a.body = 'ass'
53
+ end
54
+
55
+ assert_equal 7, a.revisions.count
56
+
57
+ # The automatically generated class.
58
+
59
+ assert_equal 7, Article::Revision.count
60
+ end
61
+
62
+ end
@@ -7,7 +7,7 @@
7
7
 
8
8
  # CHANGE THIS TO SETUP MOST TESTS
9
9
 
10
- configA = :sqlite
10
+ configA = :mysql
11
11
 
12
12
  # FOR MULTI-TESTS
13
13
 
@@ -38,7 +38,7 @@ $og_mysql = {
38
38
  :user => 'root',
39
39
  :name => 'test',
40
40
  # :password => 'navelrulez',
41
- :socket => '/var/run/mysqld/mysqld.sock'
41
+ # :socket => '/var/run/mysqld/mysqld.sock'
42
42
  }
43
43
 
44
44
  $og_psql = {
@@ -0,0 +1,31 @@
1
+ require 'test/unit'
2
+ require 'og'
3
+
4
+ $DBG = true
5
+
6
+ class Track
7
+ prop_accessor :name, String
8
+ belongs_to Disc
9
+ end
10
+
11
+ class Disc
12
+ prop_accessor :name, String
13
+ has_many Track
14
+ end
15
+
16
+ Og.setup( :destroy => true, :store => :kirby )
17
+
18
+
19
+ class TC_Kirby < Test::Unit::TestCase # :nodoc: all
20
+
21
+ def test_all
22
+ d = Disc.create
23
+ t = Track.create
24
+ d.tracks << t
25
+
26
+ assert_equal 1, d.tracks.size
27
+ end
28
+
29
+ end
30
+
31
+ # * Guillaume Pierronnet <guillaume.pierronnet@gmail.com>
@@ -11,6 +11,7 @@ class TC_OgFinder < Test::Unit::TestCase # :nodoc: all
11
11
  class User
12
12
  property :name, String
13
13
  property :age, Fixnum
14
+ property :father, String
14
15
  end
15
16
 
16
17
  def setup
@@ -28,6 +29,25 @@ class TC_OgFinder < Test::Unit::TestCase # :nodoc: all
28
29
  User.find_or_create_by_name_and_age('tml', 3)
29
30
 
30
31
  assert_equal 2, User.all.size
32
+
33
+ # Basic check that tml is just right.
34
+
35
+ u = User.find_by_name('tml')
36
+ assert_equal('tml', u.name)
37
+ assert_equal(3, u.age)
38
+ assert_equal(nil, u.father)
39
+
40
+ # Block form initialization works.
41
+
42
+ u2 = User.find_or_create_by_name_and_age('tommy', 9) {|x| x.father = 'jack' }
43
+ assert_equal('tommy', u2.name)
44
+ assert_equal(9, u2.age)
45
+ assert_equal('jack', u2.father)
46
+
47
+ # Doesn't create another object same object.
48
+
49
+ u3 = User.find_or_create_by_name_and_age('tommy', 9) {|x| x.father = 'jack' }
50
+ assert_equal(1, User.find_all_by_name('tommy').size)
31
51
  end
32
52
 
33
53
  end
@@ -0,0 +1,53 @@
1
+ require File.join(File.dirname(__FILE__), 'CONFIG.rb')
2
+
3
+ $DBG = true
4
+
5
+ require 'test/unit'
6
+
7
+ require 'og'
8
+
9
+ class TC_ValidateUnique < Test::Unit::TestCase # :nodoc: all
10
+ include Og
11
+
12
+ class Framework
13
+ property :app_name, String
14
+ property :creator, String
15
+
16
+ validate_unique :app_name
17
+ end
18
+
19
+ Og.setup($og_config)
20
+
21
+ def setup
22
+ @framework = Framework.new
23
+ @app_name = 'Nitro'
24
+ @framework.app_name = @app_name
25
+ @framework.creator = 'gmosx'
26
+ @framework.save
27
+ end
28
+
29
+ def teardown
30
+ Framework.all.each { |entry| entry.delete }
31
+ end
32
+
33
+ def test_modify_already_created
34
+ # if setup completes, then unique entries are already allowed.
35
+ framework = Framework.find_all_by_app_name(@app_name)
36
+ assert_equal(1, framework.size)
37
+ assert_equal(@app_name, framework[0].app_name)
38
+ framework[0].creator = "George Moschovitis"
39
+ assert(framework[0].valid?)
40
+ assert_equal(0, framework[0].errors.size)
41
+ end
42
+
43
+ def test_duplicate
44
+ @dup = Framework.new
45
+ @dup.app_name = @framework.app_name
46
+ assert_equal(false, @dup.valid?)
47
+ assert(@dup.errors.size > 0)
48
+ assert_equal(1, @dup.errors.on(:app_name).size)
49
+ assert_match(/.*?app_name.*?exists.*?/i, @dup.errors.on(:app_name).first)
50
+ assert(@dup.errors.errors.has_key?(:app_name))
51
+ end
52
+ end
53
+