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.
Files changed (69) hide show
  1. data/CHANGELOG +485 -0
  2. data/README +35 -12
  3. data/Rakefile +4 -7
  4. data/benchmark/bench.rb +1 -1
  5. data/doc/AUTHORS +3 -3
  6. data/doc/RELEASES +153 -2
  7. data/doc/config.txt +0 -7
  8. data/doc/tutorial.txt +7 -0
  9. data/examples/README +5 -0
  10. data/examples/mysql_to_psql.rb +25 -50
  11. data/examples/run.rb +62 -77
  12. data/install.rb +1 -1
  13. data/lib/og.rb +45 -106
  14. data/lib/og/collection.rb +156 -0
  15. data/lib/og/entity.rb +131 -0
  16. data/lib/og/errors.rb +10 -15
  17. data/lib/og/manager.rb +115 -0
  18. data/lib/og/{mixins → mixin}/hierarchical.rb +43 -37
  19. data/lib/og/{mixins → mixin}/orderable.rb +35 -35
  20. data/lib/og/{mixins → mixin}/timestamped.rb +0 -6
  21. data/lib/og/{mixins → mixin}/tree.rb +0 -4
  22. data/lib/og/relation.rb +178 -0
  23. data/lib/og/relation/belongs_to.rb +14 -0
  24. data/lib/og/relation/has_many.rb +62 -0
  25. data/lib/og/relation/has_one.rb +17 -0
  26. data/lib/og/relation/joins_many.rb +69 -0
  27. data/lib/og/relation/many_to_many.rb +17 -0
  28. data/lib/og/relation/refers_to.rb +31 -0
  29. data/lib/og/store.rb +223 -0
  30. data/lib/og/store/filesys.rb +113 -0
  31. data/lib/og/store/madeleine.rb +4 -0
  32. data/lib/og/store/memory.rb +291 -0
  33. data/lib/og/store/mysql.rb +283 -0
  34. data/lib/og/store/psql.rb +238 -0
  35. data/lib/og/store/sql.rb +599 -0
  36. data/lib/og/store/sqlite.rb +190 -0
  37. data/lib/og/store/sqlserver.rb +262 -0
  38. data/lib/og/types.rb +19 -0
  39. data/lib/og/validation.rb +0 -4
  40. data/test/og/{mixins → mixin}/tc_hierarchical.rb +21 -23
  41. data/test/og/{mixins → mixin}/tc_orderable.rb +15 -14
  42. data/test/og/mixin/tc_timestamped.rb +38 -0
  43. data/test/og/store/tc_filesys.rb +71 -0
  44. data/test/og/tc_relation.rb +36 -0
  45. data/test/og/tc_store.rb +290 -0
  46. data/test/og/tc_types.rb +21 -0
  47. metadata +54 -40
  48. data/examples/mock_example.rb +0 -50
  49. data/lib/og/adapters/base.rb +0 -706
  50. data/lib/og/adapters/filesys.rb +0 -117
  51. data/lib/og/adapters/mysql.rb +0 -350
  52. data/lib/og/adapters/oracle.rb +0 -368
  53. data/lib/og/adapters/psql.rb +0 -272
  54. data/lib/og/adapters/sqlite.rb +0 -265
  55. data/lib/og/adapters/sqlserver.rb +0 -356
  56. data/lib/og/database.rb +0 -290
  57. data/lib/og/enchant.rb +0 -149
  58. data/lib/og/meta.rb +0 -407
  59. data/lib/og/testing/mock.rb +0 -165
  60. data/lib/og/typemacros.rb +0 -24
  61. data/test/og/adapters/tc_filesys.rb +0 -83
  62. data/test/og/adapters/tc_sqlite.rb +0 -86
  63. data/test/og/adapters/tc_sqlserver.rb +0 -96
  64. data/test/og/tc_automanage.rb +0 -46
  65. data/test/og/tc_lifecycle.rb +0 -105
  66. data/test/og/tc_many_to_many.rb +0 -61
  67. data/test/og/tc_meta.rb +0 -55
  68. data/test/og/tc_validation.rb +0 -89
  69. 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
- c = {
10
+ o = {
15
11
  :position => 'position',
16
12
  :type => Fixnum,
17
- :scope => '"1 = 1"'
18
13
  }
19
- c.update(options) if options
14
+ o.update(options) if options
20
15
 
21
- if c[:scope].is_a?(Symbol) && c[:scope].to_s !~ /_oid$/
22
- c[:scope] = "#{c[:scope]}_oid".intern
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 = c[:position]
26
- scope = c[: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
- def self.og_pre_delete(conn, obj)
40
- obj.decrement_position_of_lower_items
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(#{scope} + " AND #{position}=\#\{@#{position} - 1\}")
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(#{scope} + " AND #{position}=\#\{@#{position} + 1\}")
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(#{scope} + " ORDER BY #{position} DESC")
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
- set_property('#{position}', @#{position})
118
+ update(:#{position})
121
119
  end
122
120
 
123
121
  def decrement_position
124
122
  @#{position} -= 1
125
- set_property('#{position}', @#{position})
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
- set_property('#{position}', 1)
133
+ update(:#{position})
136
134
  end
137
135
 
138
136
  def set_bottom_position
139
137
  @#{position} = bottom_position + 1
140
- set_property('#{position}', @#{position})
138
+ update(:#{position})
141
139
  end
142
140
 
143
141
  def increment_position_of_higher_items
144
- #{base}.update("#{position}=(#{position} + 1)", "WHERE " + #{scope} + " AND #{position} < \#\{@#{position}\}")
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}.update("#{position}=(#{position} + 1)", "WHERE " + #{scope})
146
+ #{base}.update_property("#{position}=(#{position} + 1)", #{cond})
149
147
  end
150
148
 
151
149
  def decrement_position_of_lower_items
152
- #{base}.update("#{position}=(#{position} - 1)", "WHERE " + #{scope} + " AND #{position} > \#\{@#{position}\}")
150
+ #{base}.update_property("#{position}=(#{position} - 1)", #{cond_and}"#{position} > \#\{@#{position}\}")
153
151
  end
154
- EOE
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
@@ -1,7 +1,3 @@
1
- # * George Moschovitis <gm@navel.gr>
2
- # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: tree.rb 1 2005-04-11 11:04:30Z gmosx $
4
-
5
1
  # NOT WORKING YET !!!
6
2
 
7
3
  raise 'This is not working yet, do not require this file.'
@@ -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,14 @@
1
+ require 'og/relation/refers_to'
2
+
3
+ module Og
4
+
5
+ class BelongsTo < RefersTo
6
+
7
+ def enchant
8
+ super
9
+ target_class.meta :descendants, [owner_class, foreign_key]
10
+ end
11
+
12
+ end
13
+
14
+ 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