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.
- data/ProjectInfo +2 -2
- data/README +1 -1
- data/doc/RELEASES +78 -0
- data/lib/glue/revisable.rb +123 -1
- data/lib/glue/searchable.rb +28 -0
- data/lib/glue/taggable.rb +18 -3
- data/lib/glue/timestamped.rb +5 -5
- data/lib/og.rb +28 -4
- data/lib/og/collection.rb +15 -0
- data/lib/og/entity.rb +64 -44
- data/lib/og/evolution.rb +1 -1
- data/lib/og/manager.rb +16 -0
- data/lib/og/relation.rb +5 -1
- data/lib/og/relation/refers_to.rb +6 -0
- data/lib/og/store.rb +13 -0
- data/lib/og/store/kirby.rb +6 -5
- data/lib/og/store/mysql.rb +34 -5
- data/lib/og/store/psql.rb +409 -82
- data/lib/og/store/sql.rb +27 -11
- data/lib/og/store/sqlite.rb +3 -3
- data/lib/og/validation.rb +53 -35
- data/test/glue/tc_revisable.rb +62 -0
- data/test/og/CONFIG.rb +2 -2
- data/test/og/store/tc_kirby.rb +31 -0
- data/test/og/tc_finder.rb +20 -0
- data/test/og/tc_validation.rb +53 -0
- metadata +9 -6
- data/lib/glue/taggable_old.rb +0 -228
- data/lib/og/vendor/kbserver.rb +0 -20
- data/lib/og/vendor/kirbybase.rb +0 -2941
data/lib/og/store/sql.rb
CHANGED
@@ -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
|
-
|
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 "(
|
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 #{
|
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>
|
data/lib/og/store/sqlite.rb
CHANGED
@@ -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
|
-
|
166
|
-
|
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?
|
data/lib/og/validation.rb
CHANGED
@@ -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
|
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
|
-
|
42
|
-
|
35
|
+
def validate_unique(*params)
|
36
|
+
c = parse_config(params, :on => :save)
|
43
37
|
|
44
|
-
|
45
|
-
|
38
|
+
params.each do |field|
|
39
|
+
c[:msg] ||= "#{self.name} with this #{field} already exists"
|
46
40
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
59
|
-
#
|
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
|
-
|
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
|
data/test/og/CONFIG.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
|
8
8
|
# CHANGE THIS TO SETUP MOST TESTS
|
9
9
|
|
10
|
-
configA = :
|
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
|
-
|
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>
|
data/test/og/tc_finder.rb
CHANGED
@@ -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
|
+
|