og 0.16.0 → 0.17.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/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
|