og 0.16.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +485 -0
- data/README +35 -12
- data/Rakefile +4 -7
- data/benchmark/bench.rb +1 -1
- data/doc/AUTHORS +3 -3
- data/doc/RELEASES +153 -2
- data/doc/config.txt +0 -7
- data/doc/tutorial.txt +7 -0
- data/examples/README +5 -0
- data/examples/mysql_to_psql.rb +25 -50
- data/examples/run.rb +62 -77
- data/install.rb +1 -1
- data/lib/og.rb +45 -106
- data/lib/og/collection.rb +156 -0
- data/lib/og/entity.rb +131 -0
- data/lib/og/errors.rb +10 -15
- data/lib/og/manager.rb +115 -0
- data/lib/og/{mixins → mixin}/hierarchical.rb +43 -37
- data/lib/og/{mixins → mixin}/orderable.rb +35 -35
- data/lib/og/{mixins → mixin}/timestamped.rb +0 -6
- data/lib/og/{mixins → mixin}/tree.rb +0 -4
- data/lib/og/relation.rb +178 -0
- data/lib/og/relation/belongs_to.rb +14 -0
- data/lib/og/relation/has_many.rb +62 -0
- data/lib/og/relation/has_one.rb +17 -0
- data/lib/og/relation/joins_many.rb +69 -0
- data/lib/og/relation/many_to_many.rb +17 -0
- data/lib/og/relation/refers_to.rb +31 -0
- data/lib/og/store.rb +223 -0
- data/lib/og/store/filesys.rb +113 -0
- data/lib/og/store/madeleine.rb +4 -0
- data/lib/og/store/memory.rb +291 -0
- data/lib/og/store/mysql.rb +283 -0
- data/lib/og/store/psql.rb +238 -0
- data/lib/og/store/sql.rb +599 -0
- data/lib/og/store/sqlite.rb +190 -0
- data/lib/og/store/sqlserver.rb +262 -0
- data/lib/og/types.rb +19 -0
- data/lib/og/validation.rb +0 -4
- data/test/og/{mixins → mixin}/tc_hierarchical.rb +21 -23
- data/test/og/{mixins → mixin}/tc_orderable.rb +15 -14
- data/test/og/mixin/tc_timestamped.rb +38 -0
- data/test/og/store/tc_filesys.rb +71 -0
- data/test/og/tc_relation.rb +36 -0
- data/test/og/tc_store.rb +290 -0
- data/test/og/tc_types.rb +21 -0
- metadata +54 -40
- data/examples/mock_example.rb +0 -50
- data/lib/og/adapters/base.rb +0 -706
- data/lib/og/adapters/filesys.rb +0 -117
- data/lib/og/adapters/mysql.rb +0 -350
- data/lib/og/adapters/oracle.rb +0 -368
- data/lib/og/adapters/psql.rb +0 -272
- data/lib/og/adapters/sqlite.rb +0 -265
- data/lib/og/adapters/sqlserver.rb +0 -356
- data/lib/og/database.rb +0 -290
- data/lib/og/enchant.rb +0 -149
- data/lib/og/meta.rb +0 -407
- data/lib/og/testing/mock.rb +0 -165
- data/lib/og/typemacros.rb +0 -24
- data/test/og/adapters/tc_filesys.rb +0 -83
- data/test/og/adapters/tc_sqlite.rb +0 -86
- data/test/og/adapters/tc_sqlserver.rb +0 -96
- data/test/og/tc_automanage.rb +0 -46
- data/test/og/tc_lifecycle.rb +0 -105
- data/test/og/tc_many_to_many.rb +0 -61
- data/test/og/tc_meta.rb +0 -55
- data/test/og/tc_validation.rb +0 -89
- data/test/tc_og.rb +0 -364
@@ -1,7 +1,3 @@
|
|
1
|
-
# * George Moschovitis <gm@navel.gr>
|
2
|
-
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: orderable.rb 1 2005-04-11 11:04:30Z gmosx $
|
4
|
-
|
5
1
|
require 'glue/dynamic_include'
|
6
2
|
|
7
3
|
module Og
|
@@ -11,34 +7,36 @@ module Og
|
|
11
7
|
module Orderable
|
12
8
|
|
13
9
|
def self.append_dynamic_features(base, options)
|
14
|
-
|
10
|
+
o = {
|
15
11
|
:position => 'position',
|
16
12
|
:type => Fixnum,
|
17
|
-
:scope => '"1 = 1"'
|
18
13
|
}
|
19
|
-
|
14
|
+
o.update(options) if options
|
20
15
|
|
21
|
-
if
|
22
|
-
|
16
|
+
if o[:scope].is_a?(Symbol) && o[:scope].to_s !~ /_oid$/
|
17
|
+
o[:scope] = "#{o[:scope]}_oid".intern
|
23
18
|
end
|
24
19
|
|
25
|
-
position =
|
26
|
-
scope =
|
27
|
-
|
28
|
-
if scope.is_a?(Symbol)
|
29
|
-
scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
|
30
|
-
end
|
31
|
-
|
32
|
-
base.module_eval <<-EOE, __FILE__, __LINE__
|
33
|
-
property :#{position}, #{c[:type]}
|
34
|
-
|
35
|
-
def og_pre_insert(conn)
|
36
|
-
add_to_bottom
|
37
|
-
end
|
20
|
+
position = o[:position]
|
21
|
+
scope = o[:scope]
|
38
22
|
|
39
|
-
|
40
|
-
|
23
|
+
if scope
|
24
|
+
if scope.is_a?(Symbol)
|
25
|
+
scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
|
41
26
|
end
|
27
|
+
|
28
|
+
cond = 'condition => ' + scope
|
29
|
+
cond_and = ':condition => ' + scope + ' + " AND " +'
|
30
|
+
else
|
31
|
+
cond = ':condition => nil'
|
32
|
+
cond_and = ':condition => '
|
33
|
+
end
|
34
|
+
|
35
|
+
code = %{
|
36
|
+
property :#{position}, #{o[:type]}
|
37
|
+
|
38
|
+
pre "add_to_bottom", :on => :og_insert
|
39
|
+
pre "decrement_position_of_lower_items", :on => :og_delete
|
42
40
|
|
43
41
|
def move_higher
|
44
42
|
if higher = higher_item
|
@@ -87,12 +85,12 @@ module Orderable
|
|
87
85
|
end
|
88
86
|
|
89
87
|
def higher_item
|
90
|
-
#{base}.one(#{
|
88
|
+
#{base}.one(#{cond_and}"#{position}=\#\{@#{position} - 1\}")
|
91
89
|
end
|
92
90
|
alias_method :previous_item, :higher_item
|
93
91
|
|
94
92
|
def lower_item
|
95
|
-
#{base}.one(#{
|
93
|
+
#{base}.one(#{cond_and}"#{position}=\#\{@#{position} + 1\}")
|
96
94
|
end
|
97
95
|
alias_method :next_item, :lower_item
|
98
96
|
|
@@ -101,7 +99,7 @@ module Orderable
|
|
101
99
|
alias_method :first_item, :top_item
|
102
100
|
|
103
101
|
def bottom_item
|
104
|
-
#{base}.one(#{
|
102
|
+
#{base}.one(#{cond}, :order => "#{position} DESC", :limit => 1)
|
105
103
|
end
|
106
104
|
alias_method :last_item, :last_item
|
107
105
|
|
@@ -117,12 +115,12 @@ module Orderable
|
|
117
115
|
|
118
116
|
def increment_position
|
119
117
|
@#{position} += 1
|
120
|
-
|
118
|
+
update(:#{position})
|
121
119
|
end
|
122
120
|
|
123
121
|
def decrement_position
|
124
122
|
@#{position} -= 1
|
125
|
-
|
123
|
+
update(:#{position})
|
126
124
|
end
|
127
125
|
|
128
126
|
def bottom_position
|
@@ -132,26 +130,28 @@ module Orderable
|
|
132
130
|
|
133
131
|
def set_top_position
|
134
132
|
@#{position} = 1
|
135
|
-
|
133
|
+
update(:#{position})
|
136
134
|
end
|
137
135
|
|
138
136
|
def set_bottom_position
|
139
137
|
@#{position} = bottom_position + 1
|
140
|
-
|
138
|
+
update(:#{position})
|
141
139
|
end
|
142
140
|
|
143
141
|
def increment_position_of_higher_items
|
144
|
-
#{base}.
|
142
|
+
#{base}.update_property("#{position}=(#{position} + 1)", #{cond_and}"#{position} < \#\{@#{position}\}")
|
145
143
|
end
|
146
144
|
|
147
145
|
def increment_position_of_all_items
|
148
|
-
#{base}.
|
146
|
+
#{base}.update_property("#{position}=(#{position} + 1)", #{cond})
|
149
147
|
end
|
150
148
|
|
151
149
|
def decrement_position_of_lower_items
|
152
|
-
#{base}.
|
150
|
+
#{base}.update_property("#{position}=(#{position} - 1)", #{cond_and}"#{position} > \#\{@#{position}\}")
|
153
151
|
end
|
154
|
-
|
152
|
+
}
|
153
|
+
|
154
|
+
base.module_eval(code)
|
155
155
|
end
|
156
156
|
|
157
157
|
|
@@ -1,13 +1,8 @@
|
|
1
|
-
# * George Moschovitis <gm@navel.gr>
|
2
|
-
# (c) 2005 Navel, all rights reserved.
|
3
|
-
# $Id: timestamped.rb 23 2005-04-16 18:20:00Z gmosx $
|
4
|
-
|
5
1
|
module Og
|
6
2
|
|
7
3
|
# Adds timestamping functionality.
|
8
4
|
|
9
5
|
module Timestamped
|
10
|
-
|
11
6
|
property :create_time, Time
|
12
7
|
property :update_time, Time
|
13
8
|
property :access_time, Time
|
@@ -18,7 +13,6 @@ module Timestamped
|
|
18
13
|
def touch!
|
19
14
|
@access_time = Time.now
|
20
15
|
end
|
21
|
-
|
22
16
|
end
|
23
17
|
|
24
18
|
end
|
data/lib/og/relation.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'facet/object/constant'
|
2
|
+
require 'facet/string/plural'
|
3
|
+
require 'facet/string/demodulize'
|
4
|
+
require 'facet/string/underscore'
|
5
|
+
|
6
|
+
module Og
|
7
|
+
|
8
|
+
# A relation between Entities.
|
9
|
+
|
10
|
+
class Relation
|
11
|
+
|
12
|
+
attr_accessor :options
|
13
|
+
|
14
|
+
def initialize(args, options = {})
|
15
|
+
@options = options
|
16
|
+
@options.update(args.pop) if args.last.is_a?(Hash)
|
17
|
+
|
18
|
+
if args.empty? or (not (args.last.is_a?(Class) or args.last.is_a?(Symbol)))
|
19
|
+
raise 'Class of target not defined'
|
20
|
+
end
|
21
|
+
|
22
|
+
@options[:target_class] = args.pop
|
23
|
+
|
24
|
+
target_name = if collection
|
25
|
+
:target_plural_name
|
26
|
+
else
|
27
|
+
:target_singular_name
|
28
|
+
end
|
29
|
+
|
30
|
+
unless args.empty?
|
31
|
+
@options[target_name] = args.first
|
32
|
+
else
|
33
|
+
@options[target_name] = if collection
|
34
|
+
target_class.to_s.demodulize.underscore.downcase.plural.intern
|
35
|
+
else
|
36
|
+
target_class.to_s.demodulize.underscore.downcase.intern
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
@options[:name] = options[target_name]
|
41
|
+
end
|
42
|
+
|
43
|
+
def [](key)
|
44
|
+
@options[key]
|
45
|
+
end
|
46
|
+
|
47
|
+
def []=(key, val)
|
48
|
+
@options[key] = val
|
49
|
+
end
|
50
|
+
|
51
|
+
def resolve_options
|
52
|
+
@options[:owner_pk], @options[:owner_pkclass] = owner_class.primary_key
|
53
|
+
@options[:target_pk], @options[:target_pkclass] = target_class.primary_key
|
54
|
+
end
|
55
|
+
|
56
|
+
# To avoid forward declarations, references to undefined
|
57
|
+
# (at the time of the creation of the relation) classes are
|
58
|
+
# stored as symbols. These symbols are resolved by this
|
59
|
+
# method.
|
60
|
+
#--
|
61
|
+
# FIXME: do something more elegant here.
|
62
|
+
#++
|
63
|
+
|
64
|
+
def resolve_target
|
65
|
+
if target_class.is_a?(Symbol)
|
66
|
+
c = owner_class.name.dup
|
67
|
+
c = "::" + c unless c =~ /::/
|
68
|
+
c.gsub!(/::.*$/, '::')
|
69
|
+
c << target_class.to_s
|
70
|
+
begin
|
71
|
+
klass = constant(c)
|
72
|
+
rescue
|
73
|
+
c = target_class
|
74
|
+
retry
|
75
|
+
end
|
76
|
+
@options[:target_class] = klass
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def enchant
|
81
|
+
end
|
82
|
+
|
83
|
+
# Access the hash values as methods.
|
84
|
+
|
85
|
+
def method_missing(sym, *args)
|
86
|
+
return @options[sym]
|
87
|
+
end
|
88
|
+
|
89
|
+
class << self
|
90
|
+
|
91
|
+
def enchant(klass)
|
92
|
+
if klass.__meta[:relations]
|
93
|
+
for relation in klass.__meta[:relations]
|
94
|
+
relation.resolve_target
|
95
|
+
relation.resolve_options
|
96
|
+
relation.enchant
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
# Relation macros. These macros are used to define Entity
|
106
|
+
# relations.
|
107
|
+
|
108
|
+
module RelationMacros
|
109
|
+
def self.append_features(base)
|
110
|
+
super
|
111
|
+
base.extend(ClassMethods)
|
112
|
+
end
|
113
|
+
|
114
|
+
module ClassMethods
|
115
|
+
|
116
|
+
# === Examples
|
117
|
+
#
|
118
|
+
# belongs_to Article
|
119
|
+
# belongs_to :article, Article
|
120
|
+
# belongs_to :article, Article, :view => 'lala'
|
121
|
+
|
122
|
+
def belongs_to(*args)
|
123
|
+
require 'og/relation/belongs_to'
|
124
|
+
meta :relations, Og::BelongsTo.new(args, :owner_class => self)
|
125
|
+
end
|
126
|
+
|
127
|
+
# === Examples
|
128
|
+
#
|
129
|
+
# refers_to Topic
|
130
|
+
|
131
|
+
def refers_to(*args)
|
132
|
+
require 'og/relation/refers_to'
|
133
|
+
meta :relations, Og::RefersTo.new(args, :owner_class => self)
|
134
|
+
end
|
135
|
+
|
136
|
+
# === Examples
|
137
|
+
#
|
138
|
+
# has_one User
|
139
|
+
|
140
|
+
def has_one(*args)
|
141
|
+
require 'og/relation/has_one'
|
142
|
+
meta :relations, Og::HasOne.new(args, :owner_class => self)
|
143
|
+
end
|
144
|
+
|
145
|
+
# === Examples
|
146
|
+
#
|
147
|
+
# has_many Comment
|
148
|
+
# has_many :comments, Comment
|
149
|
+
|
150
|
+
def has_many(*args)
|
151
|
+
require 'og/relation/has_many'
|
152
|
+
meta :relations, Og::HasMany.new(args, :owner_class => self, :collection => true)
|
153
|
+
end
|
154
|
+
|
155
|
+
def joins_many(*args)
|
156
|
+
require 'og/relation/joins_many'
|
157
|
+
meta :relations, Og::JoinsMany.new(args, :owner_class => self, :collection => true)
|
158
|
+
end
|
159
|
+
|
160
|
+
def many_to_many(*args)
|
161
|
+
require 'og/relation/many_to_many'
|
162
|
+
meta :relations, Og::ManyToMany.new(args, :owner_class => self, :collection => true)
|
163
|
+
end
|
164
|
+
|
165
|
+
def inspect_relations
|
166
|
+
__meta[:relations]
|
167
|
+
end
|
168
|
+
alias_method :relations, :inspect_relations
|
169
|
+
|
170
|
+
def inspect_relation(name)
|
171
|
+
__meta[:relations].find { |r| r[:name] == name }
|
172
|
+
end
|
173
|
+
alias_method :relation, :inspect_relation
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'facet/string/singular'
|
2
|
+
require 'facet/string/demodulize'
|
3
|
+
require 'facet/string/underscore'
|
4
|
+
|
5
|
+
require 'og/relation'
|
6
|
+
require 'og/collection'
|
7
|
+
|
8
|
+
module Og
|
9
|
+
|
10
|
+
class HasManyCollection < Collection
|
11
|
+
end
|
12
|
+
|
13
|
+
# A 'has_many' relation. There should be a respective
|
14
|
+
# 'belongs_to' relation.
|
15
|
+
#
|
16
|
+
# === Examples
|
17
|
+
#
|
18
|
+
# article.comments << Comment.new
|
19
|
+
# article.comments.size
|
20
|
+
|
21
|
+
class HasMany < Relation
|
22
|
+
|
23
|
+
def enchant
|
24
|
+
self[:owner_singular_name] = owner_class.to_s.demodulize.underscore.downcase
|
25
|
+
self[:target_singular_name] = target_plural_name.to_s.singular
|
26
|
+
self[:foreign_key] = "#{foreign_name || owner_singular_name}_#{owner_pk}"
|
27
|
+
|
28
|
+
owner_class.module_eval %{
|
29
|
+
attr_accessor :#{target_plural_name}
|
30
|
+
|
31
|
+
def #{target_plural_name}(options = nil)
|
32
|
+
unless @#{target_plural_name}
|
33
|
+
@#{target_plural_name} = HasManyCollection.new(
|
34
|
+
self,
|
35
|
+
:add_#{target_singular_name},
|
36
|
+
:find_#{target_plural_name},
|
37
|
+
options
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
@#{target_plural_name}.reload(options) if options and options[:reload]
|
42
|
+
@#{target_plural_name}
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_#{target_singular_name}(obj)
|
46
|
+
obj.#{foreign_key} = @#{owner_pk}
|
47
|
+
obj.save
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_#{target_plural_name}(options = {})
|
51
|
+
find_options = {
|
52
|
+
:condition => "#{foreign_key} = \#\{@#{owner_pk}\}"
|
53
|
+
}
|
54
|
+
find_options.update(options) if options
|
55
|
+
#{target_class}.find(find_options)
|
56
|
+
end
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'og/relation/refers_to'
|
2
|
+
|
3
|
+
module Og
|
4
|
+
|
5
|
+
# A 'has_one' relation.
|
6
|
+
#
|
7
|
+
# === Examples
|
8
|
+
#
|
9
|
+
# user.profile = Profile.new
|
10
|
+
# user.profile
|
11
|
+
# user.profile(true) # reload
|
12
|
+
# user.profile(:reload => true)
|
13
|
+
|
14
|
+
class HasOne < RefersTo
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'facet/string/singular'
|
2
|
+
require 'facet/string/demodulize'
|
3
|
+
require 'facet/string/underscore'
|
4
|
+
|
5
|
+
require 'og/relation'
|
6
|
+
require 'og/collection'
|
7
|
+
|
8
|
+
module Og
|
9
|
+
|
10
|
+
class JoinsManyCollection < Collection
|
11
|
+
end
|
12
|
+
|
13
|
+
# A 'joins_many' relation.
|
14
|
+
# This objects is associated with an other using an intermediate
|
15
|
+
# join table.
|
16
|
+
#
|
17
|
+
# === Examples
|
18
|
+
#
|
19
|
+
# joins_many Category
|
20
|
+
# joins_many :categories, Category
|
21
|
+
|
22
|
+
class JoinsMany < Relation
|
23
|
+
|
24
|
+
def enchant
|
25
|
+
self[:owner_singular_name] = owner_class.to_s.demodulize.underscore.downcase
|
26
|
+
self[:target_singular_name] = target_plural_name.to_s.singular
|
27
|
+
|
28
|
+
store = owner_class.ogmanager.store
|
29
|
+
join_table, oidx, tidx = store.join_table(owner_class, target_class)
|
30
|
+
owner_class.meta :join_tables, join_table
|
31
|
+
|
32
|
+
owner_class.module_eval %{
|
33
|
+
attr_accessor :#{target_plural_name}
|
34
|
+
|
35
|
+
def #{target_plural_name}(options = nil)
|
36
|
+
reload = options and options[:reload]
|
37
|
+
|
38
|
+
unless @#{target_plural_name}
|
39
|
+
@#{target_plural_name} = JoinsManyCollection.new(
|
40
|
+
self,
|
41
|
+
:add_#{target_singular_name},
|
42
|
+
:find_#{target_plural_name}
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
@#{target_plural_name}.reload(options) if options and options[:reload]
|
47
|
+
@#{target_plural_name}
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_#{target_singular_name}(obj)
|
51
|
+
obj.save
|
52
|
+
obj.class.ogmanager.store.join(self, obj, "#{join_table}")
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_#{target_plural_name}(options = {})
|
56
|
+
find_options = {
|
57
|
+
:join_table => "#{join_table}",
|
58
|
+
:join_condition => "#{join_table}.key#{tidx}=#{store.table(target_class)}.oid",
|
59
|
+
:condition => "#{join_table}.key#{oidx}=\#\{@#{owner_pk}\}"
|
60
|
+
}
|
61
|
+
find_options.update(options) if options
|
62
|
+
#{target_class}.find(find_options)
|
63
|
+
end
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|