og 0.31.0 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/doc/{AUTHORS → CONTRIBUTORS} +26 -10
  2. data/doc/LICENSE +2 -3
  3. data/doc/RELEASES +56 -7
  4. data/doc/tutorial.txt +15 -15
  5. data/lib/glue/cacheable.rb +2 -5
  6. data/lib/glue/hierarchical.rb +1 -4
  7. data/lib/glue/optimistic_locking.rb +0 -2
  8. data/lib/glue/orderable.rb +79 -75
  9. data/lib/glue/revisable.rb +19 -24
  10. data/lib/glue/searchable.rb +0 -2
  11. data/lib/glue/taggable.rb +31 -29
  12. data/lib/glue/timestamped.rb +4 -2
  13. data/lib/og.rb +50 -29
  14. data/lib/og/adapter.rb +19 -0
  15. data/lib/og/adapter/mysql.rb +212 -0
  16. data/lib/og/adapter/mysql/override.rb +34 -0
  17. data/lib/og/adapter/mysql/script.rb +15 -0
  18. data/lib/og/adapter/mysql/utils.rb +40 -0
  19. data/lib/og/adapter/postgresql.rb +231 -0
  20. data/lib/og/adapter/postgresql/override.rb +117 -0
  21. data/lib/og/adapter/postgresql/script.rb +15 -0
  22. data/lib/og/adapter/postgresql/utils.rb +35 -0
  23. data/lib/og/adapter/sqlite.rb +132 -0
  24. data/lib/og/adapter/sqlite/override.rb +33 -0
  25. data/lib/og/adapter/sqlite/script.rb +15 -0
  26. data/lib/og/collection.rb +35 -7
  27. data/lib/og/{evolution.rb → dump.rb} +4 -5
  28. data/lib/og/entity.rb +102 -173
  29. data/lib/og/entity/clone.rb +119 -0
  30. data/lib/og/errors.rb +0 -2
  31. data/lib/og/manager.rb +85 -37
  32. data/lib/og/relation.rb +52 -34
  33. data/lib/og/relation/belongs_to.rb +0 -2
  34. data/lib/og/relation/has_many.rb +27 -4
  35. data/lib/og/relation/joins_many.rb +41 -14
  36. data/lib/og/relation/many_to_many.rb +10 -0
  37. data/lib/og/relation/refers_to.rb +22 -5
  38. data/lib/og/store.rb +80 -86
  39. data/lib/og/store/sql.rb +710 -713
  40. data/lib/og/store/sql/evolution.rb +119 -0
  41. data/lib/og/store/sql/join.rb +155 -0
  42. data/lib/og/store/sql/utils.rb +149 -0
  43. data/lib/og/test/assertions.rb +1 -3
  44. data/lib/og/test/testcase.rb +0 -2
  45. data/lib/og/types.rb +2 -5
  46. data/lib/og/validation.rb +6 -9
  47. data/test/{og/mixin → glue}/tc_hierarchical.rb +3 -13
  48. data/test/glue/tc_og_paginate.rb +47 -0
  49. data/test/{og/mixin → glue}/tc_optimistic_locking.rb +2 -12
  50. data/test/{og/mixin → glue}/tc_orderable.rb +15 -23
  51. data/test/glue/tc_orderable2.rb +47 -0
  52. data/test/glue/tc_revisable.rb +3 -3
  53. data/test/{og/mixin → glue}/tc_taggable.rb +20 -10
  54. data/test/{og/mixin → glue}/tc_timestamped.rb +2 -12
  55. data/test/glue/tc_webfile.rb +36 -0
  56. data/test/og/CONFIG.rb +8 -11
  57. data/test/og/multi_validations_model.rb +14 -0
  58. data/test/og/store/tc_filesys.rb +3 -1
  59. data/test/og/store/tc_kirby.rb +16 -13
  60. data/test/og/store/tc_sti.rb +11 -11
  61. data/test/og/store/tc_sti2.rb +79 -0
  62. data/test/og/tc_build.rb +41 -0
  63. data/test/og/tc_cacheable.rb +3 -2
  64. data/test/og/tc_has_many.rb +96 -0
  65. data/test/og/tc_inheritance.rb +6 -4
  66. data/test/og/tc_joins_many.rb +93 -0
  67. data/test/og/tc_multi_validations.rb +5 -7
  68. data/test/og/tc_multiple.rb +7 -6
  69. data/test/og/tc_override.rb +13 -7
  70. data/test/og/tc_primary_key.rb +30 -0
  71. data/test/og/tc_relation.rb +8 -14
  72. data/test/og/tc_reldelete.rb +163 -0
  73. data/test/og/tc_reverse.rb +17 -14
  74. data/test/og/tc_scoped.rb +3 -11
  75. data/test/og/tc_setup.rb +13 -11
  76. data/test/og/tc_store.rb +21 -28
  77. data/test/og/tc_validation2.rb +2 -2
  78. data/test/og/tc_validation_loop.rb +17 -15
  79. metadata +109 -103
  80. data/INSTALL +0 -91
  81. data/ProjectInfo +0 -51
  82. data/README +0 -177
  83. data/doc/config.txt +0 -28
  84. data/examples/README +0 -23
  85. data/examples/mysql_to_psql.rb +0 -71
  86. data/examples/run.rb +0 -271
  87. data/lib/glue/tree.rb +0 -218
  88. data/lib/og/store/alpha/filesys.rb +0 -110
  89. data/lib/og/store/alpha/memory.rb +0 -295
  90. data/lib/og/store/alpha/sqlserver.rb +0 -256
  91. data/lib/og/store/kirby.rb +0 -490
  92. data/lib/og/store/mysql.rb +0 -415
  93. data/lib/og/store/psql.rb +0 -875
  94. data/lib/og/store/sqlite.rb +0 -348
  95. data/lib/og/store/sqlite2.rb +0 -241
  96. data/setup.rb +0 -1585
  97. data/test/og/tc_sti_find.rb +0 -35
@@ -0,0 +1,35 @@
1
+ require 'og/store/sql/utils'
2
+
3
+ module Og
4
+
5
+ module PostgresqlUtils
6
+ include SqlUtils
7
+
8
+ def escape(str)
9
+ return nil unless str
10
+ return PGconn.escape(str.to_s)
11
+ end
12
+
13
+ # Blobs are actually a lot faster (and use up less
14
+ # storage) for large data I think, as they need not to be
15
+ # encoded and decoded. I'd like to have both ;-) BYTEA is
16
+ # easier to handle than BLOBs, but if you implement BLOBs in a way
17
+ # that they are transparent to the user (as I did in Ruby/DBI),
18
+ # I'd prefer that way.
19
+
20
+ def blob(val)
21
+ val.gsub(/[\000-\037\047\134\177-\377]/) do |b|
22
+ "\\#{ b[0].to_s(8).rjust(3, '0') }"
23
+ end
24
+ end
25
+
26
+ def parse_blob(val)
27
+ return '' unless val
28
+
29
+ val.gsub(/\\(\\|'|[0-3][0-7][0-7])/) do |s|
30
+ if s.size == 2 then s[1,1] else s[1,3].oct.chr end
31
+ end
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,132 @@
1
+ begin
2
+ require 'sqlite3'
3
+ rescue Object => ex
4
+ Logger.error 'Ruby-Sqlite3 bindings are not installed!'
5
+ Logger.error ex
6
+ end
7
+
8
+ require 'fileutils'
9
+ require 'set'
10
+
11
+ require 'og/store/sql'
12
+ require 'og/adapter/sqlite/override'
13
+
14
+ module Og
15
+
16
+ # A Store that persists objects into an Sqlite3 database.
17
+ #
18
+ # As well as the usual options to the constructor, you can also pass a
19
+ # :busy_timeout option which defines how quickly to retry a query, should the
20
+ # database be locked. The default value is 50ms. The retry will currently
21
+ # continue until successful.
22
+ #
23
+ # To read documentation about the methods, consult the documentation
24
+ # for SqlStore and Store.
25
+
26
+ class SqliteAdapter < SqlStore
27
+ include SqlUtils; extend SqlUtils
28
+
29
+ # Initialize the Sqlite store.
30
+ # This store provides a default name.
31
+
32
+ def initialize(options)
33
+ super
34
+ @busy_timeout = (options[:busy_timeout] || 50)/1000
35
+ @conn = SQLite3::Database.new(db_filename(options))
36
+ end
37
+
38
+ def close
39
+ @conn.close
40
+ super
41
+ end
42
+
43
+ # Override if needed.
44
+
45
+ def db_filename(options)
46
+ options[:name] ||= 'data'
47
+ if options[:name] == ':memory:'
48
+ ':memory:'
49
+ else
50
+ "#{options[:name]}.db"
51
+ end
52
+ end
53
+
54
+ def destroy_db(options)
55
+ begin
56
+ FileUtils.rm(db_filename(options))
57
+ super
58
+ rescue Errno::ENOENT => ex # FIXME: Lookup Win32/Linux/BSD error
59
+ Logger.info "Cannot drop '#{options[:name]}'!"
60
+ end
61
+ end
62
+
63
+ # The type used for default primary keys.
64
+
65
+ def primary_key_type
66
+ 'integer PRIMARY KEY'
67
+ end
68
+
69
+ def query_statement(sql)
70
+ return query(sql)
71
+ end
72
+
73
+ def exec_statement(sql)
74
+ query(sql).close()
75
+ end
76
+
77
+ def start
78
+ @conn.transaction if @transaction_nesting < 1
79
+ @transaction_nesting += 1
80
+ end
81
+
82
+ def commit
83
+ @transaction_nesting -= 1
84
+ @conn.commit if @transaction_nesting < 1
85
+ end
86
+
87
+ def rollback
88
+ @transaction_nesting -= 1
89
+ @conn.rollback if @transaction_nesting < 1
90
+ end
91
+
92
+ def sql_update(sql)
93
+ exec(sql)
94
+ @conn.changes
95
+ end
96
+
97
+ def last_insert_id(klass = nil)
98
+ query("SELECT last_insert_rowid()").first_value.to_i
99
+ end
100
+
101
+ # Returns the Sqlite information of a table within the database or
102
+ # nil if it doesn't exist. Mostly for internal usage.
103
+
104
+ def table_info(table)
105
+ r = query_statement("SELECT name FROM sqlite_master WHERE type='table' AND name='#{self.class.escape(table.to_s)}'");
106
+ return r && r.blank? ? nil : r.next
107
+ end
108
+
109
+ # SQLite send back a BusyException if the database is locked.
110
+ # Currently we keep sending the query until success or the universe implodes.
111
+ # Note that the SQLite3 ruby library provides a busy_timeout, and busy_handler
112
+ # facility, but I couldn't get the thing to work.
113
+ def query(sql)
114
+ begin
115
+ return @conn.query(sql)
116
+ rescue SQLite3::BusyException
117
+ sleep(@busy_timeout)
118
+ retry
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ # gmosx: any idea how to better test this?
125
+
126
+ def table_already_exists_exception?(ex)
127
+ ex.to_s =~ /table .* already exists/i
128
+ end
129
+
130
+ end
131
+
132
+ end
@@ -0,0 +1,33 @@
1
+ #--
2
+ # Customize the standard Sqlite3 resultset to make
3
+ # more compatible with Og.
4
+ #++
5
+
6
+ module SQLite3 # :nodoc: all
7
+
8
+ class ResultSet # :nodoc: all
9
+ alias_method :blank?, :eof?
10
+
11
+ def each_row
12
+ each do |row|
13
+ yield(row, 0)
14
+ end
15
+ end
16
+
17
+ def first_value
18
+ val = self.next[0]
19
+ close
20
+ return val
21
+ end
22
+
23
+ alias_method :fields, :columns
24
+ end
25
+
26
+ class SQLException # :nodoc: all
27
+ def table_already_exists?
28
+ # gmosx: any idea how to better test this?
29
+ self.to_s =~ /table .* already exists/i
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,15 @@
1
+ # Helper for sqlite scripts.
2
+ #
3
+ # === Example
4
+ #
5
+ # sqlite 'filename', %{
6
+ # drop database if exists weblog_development;
7
+ # create database weblog_development;
8
+ # grant all on weblog_development.* to #{`id -un`.strip}@localhost;
9
+ # }
10
+
11
+ def sqlite(opts, stream)
12
+ IO.popen("mysql #{opts}", 'w') do |io|
13
+ io.puts stream
14
+ end
15
+ end
@@ -15,6 +15,14 @@ class Collection
15
15
 
16
16
  attr_accessor :members
17
17
 
18
+ # When the collection is in building mode or the owner
19
+ # object is unsaved, added members are accumulated in the
20
+ # building_memebers array. These relations are serialized
21
+ # when the owner object is saved (or when save_building_members
22
+ # is explicitly called).
23
+
24
+ attr_accessor :building_members
25
+
18
26
  # The class of the members of this collection.
19
27
 
20
28
  attr_accessor :member_class
@@ -114,13 +122,21 @@ class Collection
114
122
 
115
123
  # Add a new member to the collection.
116
124
  # this method will overwrite any objects already
117
- # existing in the collection
125
+ # existing in the collection.
126
+ #
127
+ # If the collection is in build mode or the object
128
+ # is unsaved, the member is accumulated in a buffer. All
129
+ # accumulated members relations are saved when the object
130
+ # is saved.
118
131
 
119
132
  def push(obj, options = nil)
120
133
  remove(obj) if members.include?(obj)
121
- @members.push(obj)
134
+ @members << obj
122
135
  unless @building or owner.unsaved?
123
136
  @owner.send(@insert_proc, obj, options)
137
+ else
138
+ (@building_members ||= []) << obj
139
+ @owner.instance_variable_set '@pending_building_collections', true
124
140
  end
125
141
  end
126
142
  alias_method :<<, :push
@@ -217,17 +233,32 @@ class Collection
217
233
  # Allows to perform a scoped query.
218
234
 
219
235
  def find(options = {})
236
+ tmp = nil
220
237
  @member_class.with_scope(options) do
221
- return @owner.send(@find_proc, @find_options)
238
+ tmp = @owner.send(@find_proc, @find_options)
222
239
  end
240
+ return tmp
223
241
  end
224
242
 
225
243
  # Find one object.
226
244
 
227
- def find_one(options = {})
245
+ def find_one options = {}
228
246
  find(options).first
229
247
  end
230
248
 
249
+ # In building mode, relations for this collection are
250
+ # accumulated in @building_relations. These relations are
251
+ # saved my calling this method.
252
+
253
+ def save_building_members(options = nil)
254
+ return unless @building_members
255
+
256
+ for obj in @building_members
257
+ @owner.send(@insert_proc, obj, options)
258
+ end
259
+ @building_members = nil
260
+ end
261
+
231
262
  # Try to execute an accumulator or else
232
263
  # redirect all other methods to the members array.
233
264
  #
@@ -252,6 +283,3 @@ private
252
283
  end
253
284
 
254
285
  end
255
-
256
- # * George Moschovitis <gm@navel.gr>
257
- # * Julien Perrot <jperrot@exosec.fr>
@@ -3,10 +3,10 @@ require 'fileutils'
3
3
  require 'facet/kernel/constant'
4
4
  require 'facet/kernel/assign_with'
5
5
 
6
- # $DBG = true
7
-
8
6
  module Og
9
7
 
8
+ # Add import/export functionality to the Og Manager.
9
+
10
10
  class Manager
11
11
 
12
12
  # Dump Og managed objects to the filesystem.
@@ -23,7 +23,7 @@ class Manager
23
23
  File.open("#{basedir}/#{c}.yml", 'w') { |f| f << all.to_yaml }
24
24
  end
25
25
  end
26
- alias_method :export, :dump
26
+ alias export dump
27
27
 
28
28
  # Load Og managed objects from the filesystem. This method can apply
29
29
  # optional transformation rules in order to evolve a schema.
@@ -70,8 +70,7 @@ class Manager
70
70
  end
71
71
  end
72
72
  end
73
- alias_method :import, :load
74
- alias_method :evolve, :load
73
+ alias import load
75
74
 
76
75
  end
77
76
 
@@ -1,18 +1,15 @@
1
1
  require 'facets/core/module/class_extension'
2
- require 'facet/kernel/assign_with'
2
+ require 'facets/core/kernel/assign_with'
3
3
 
4
- require 'glue/property'
5
4
  require 'og/relation'
6
5
  require 'og/ez/clause'
7
6
  require 'og/ez/condition'
8
7
 
9
- require 'rexml/document' # for to_xml
10
-
11
8
  module Og
12
9
 
13
10
  # Include this module to classes to make them managable by Og.
14
11
  #--
15
- # gmosx, WARTNING: If you change the methods here, don't
12
+ # gmosx, WARNING: If you change the methods here, don't
16
13
  # forget to update the Cacheable overrides.
17
14
  #++
18
15
 
@@ -33,8 +30,8 @@ module EntityMixin
33
30
  def save(options = nil)
34
31
  self.class.ogmanager.store.save(self, options)
35
32
  end
36
- alias_method :save!, :save
37
- alias_method :validate_and_save, :save
33
+ alias save! save
34
+ alias validate_and_save save
38
35
 
39
36
  # Force saving of the objects, even if the validations
40
37
  # don't pass.
@@ -56,24 +53,60 @@ module EntityMixin
56
53
  self.class.ogmanager.store.update(self, options)
57
54
  end
58
55
 
59
- def update_properties(*properties)
60
- self.class.ogmanager.store.update(self, :only => properties)
56
+ def update_attributes(*attrs)
57
+ self.class.ogmanager.store.update(self, :only => attrs)
61
58
  end
62
- alias_method :update_property, :update_properties
63
- alias_method :pupdate, :update_properties
59
+ alias update_attribute update_attributes
60
+ alias aupdate update_attributes
61
+ # For backwards compatibility, deprecated.
62
+ alias update_properties update_attributes
63
+ alias update_property update_attributes
64
64
 
65
65
  def update_by_sql(set)
66
66
  self.class.ogmanager.store.update_by_sql(self, set)
67
67
  end
68
- alias_method :update_sql, :update_by_sql
69
- alias_method :supdate, :update_by_sql
68
+ alias update_sql update_by_sql
69
+ alias supdate update_by_sql
70
+
71
+ # Set attributes (update + save).
72
+ #
73
+ # === Examples
74
+ #
75
+ # a = Article[oid]
76
+ # a.set_attributes :accepted => true, :update_time => Time.now
77
+ # a.set_attribute :accepted => true
78
+ #
79
+ #--
80
+ # gmosx, THINK: maybe make this the default behaviour of
81
+ # update_attributes?
82
+ #++
83
+
84
+ def set_attributes(attrs = {})
85
+ for a, val in attrs
86
+ instance_variable_set "@#{a}", val
87
+ end
88
+ update_attributes(*attrs.keys)
89
+ end
90
+ alias set_attribute set_attributes
91
+
92
+ # Set attribute (like instance_variable_set)
93
+ #
94
+ # === Example
95
+ #
96
+ # a = Article[oid]
97
+ # a.instance_attribute_set :accepted, true
98
+
99
+ def instance_attribute_set(a, val)
100
+ instance_variable_set "@#{a}", val
101
+ update_attribute(a.to_sym)
102
+ end
70
103
 
71
104
  # Reload this entity instance from the store.
72
105
 
73
106
  def reload
74
107
  self.class.ogmanager.store.reload(self, self.pk)
75
108
  end
76
- alias_method :reload!, :reload
109
+ alias reload! reload
77
110
 
78
111
  # Delete this entity instance from the store.
79
112
 
@@ -93,11 +126,11 @@ module EntityMixin
93
126
  end
94
127
  alias_method :serialized?, :saved?
95
128
 
96
- def assign_properties(values, options = {})
97
- Property.populate_object(self, values, options)
129
+ def assign_attributes(values, options = {})
130
+ AttributeUtils.populate_object(self, values, options)
98
131
  return self
99
132
  end
100
- alias_method :assign, :assign_properties
133
+ alias_method :assign, :assign_attributes
101
134
 
102
135
  # Returns a symbol => value hash of the object's
103
136
  # properties.
@@ -109,36 +142,37 @@ module EntityMixin
109
142
  end
110
143
  end
111
144
 
112
- def og_quote(obj)
113
- self.class.ogmanager.store.quote(obj)
114
- end
145
+ # Save all building collections. Transparently called
146
+ # when saving an object, allows efficient object relationship
147
+ # setup. Example:
148
+ #
149
+ # a = Article.new
150
+ # a.categories << c1
151
+ # a.categories << c2
152
+ # a.tags << t1
153
+ # a.tags << t2
154
+ # a.save
155
+ #
156
+ #--
157
+ # TODO: at the moment, this only handles collection relations.
158
+ # Should handle belongs_to/refers_to as well.
159
+ #++
115
160
 
116
- def og_clone(*args)
117
- Og::Entity.clone(self,*args)
118
- end
119
-
120
- # convert Og object to REXML-Object
121
- # example usage:
122
- # User[1].to_rexml
123
-
124
- def to_rexml
125
- xml = REXML::Element.new(self.class.to_s.downcase)
126
- xml.add_attribute("oid", self.oid.to_s)
127
- self.class.properties.keys.each do |key|
128
- xml << REXML::Element.new(key.to_s).add_text(self.send(key).to_s) unless key == :oid
161
+ def save_building_collections(options = nil)
162
+ if @pending_building_collections
163
+ for rel in self.class.relations
164
+ next unless rel.class.ann.self.collection?
165
+ collection = send(rel.name.to_s)
166
+ collection.save_building_members
167
+ end
168
+ @pending_building_collections = false
129
169
  end
130
- return xml
131
- end
132
- alias_method :to_xml_dom, :to_rexml
133
-
134
- # convert Og object to an XML-String
135
- # example usage:
136
- # User[1].to_xml
137
-
138
- def to_xml
139
- self.to_rexml.to_s
140
170
  end
141
171
 
172
+ def og_quote(obj)
173
+ self.class.ogmanager.store.quote(obj)
174
+ end
175
+
142
176
  include RelationDSL
143
177
 
144
178
  class_extension do
@@ -161,16 +195,25 @@ module EntityMixin
161
195
  return obj
162
196
  end
163
197
 
164
- def assign_properties(values, options = {})
165
- Property.populate_object(self.new, values, options)
198
+ def assign_attributes(values, options = {})
199
+ AttributeUtils.populate_object(self.new, values, options)
166
200
  end
167
- alias_method :assign, :assign_properties
201
+ alias_method :assign, :assign_attributes
168
202
 
169
203
  # Load an instance of this Entity class using the primary
170
- # key.
204
+ # key. If the class defines a text key, this string key
205
+ # may optionally be used!
171
206
 
172
207
  def load(pk)
173
- ogmanager.store.load(pk, self)
208
+ # gmosx: leave the checks in this order (optimized)
209
+ if pk.to_i == 0 and (key = ann.self[:text_key])
210
+ # A string is passed as pk, try to use it as a
211
+ # tesxt key too.
212
+ send("find_by_#{key}", pk)
213
+ else
214
+ # A valid pk is always > 0
215
+ ogmanager.store.load(pk, self)
216
+ end
174
217
  end
175
218
  alias_method :[], :load
176
219
  alias_method :exist?, :load
@@ -375,14 +418,7 @@ module EntityMixin
375
418
  # Returns the primary key for this class.
376
419
 
377
420
  def primary_key
378
- # gmosx: LEAVE as is, seems to be a workaround for a
379
- # nasty bug in the current facets version.
380
- pk = ann.self.primary_key
381
- if pk.nil?
382
- pk = Entity.resolve_primary_key(self)
383
- ann :self, :primary_key => pk
384
- end
385
- return pk
421
+ Entity.resolve_primary_key(self)
386
422
  end
387
423
 
388
424
  # Set the default find options for this entity.
@@ -446,8 +482,8 @@ module EntityMixin
446
482
  # Set the primary key.
447
483
 
448
484
  def set_primary_key(pk, pkclass = Fixnum)
449
- #ann self, :primary_key => Property.new(:symbol => pk, :klass => pkclass)
450
- ann self, :primary_key => properties[pk].dup
485
+ self.ann[pk].primary_key = true
486
+ self.ann[pk].class ||= pkclass
451
487
  end
452
488
 
453
489
  # Is this entity a polymorphic parent?
@@ -615,9 +651,8 @@ private
615
651
  field_name = relation.foreign_key
616
652
  value = value.send(relation.target_class.primary_key.symbol)
617
653
  else
618
- field_name = properties[name.to_sym][:field] ||
619
- properties[name.to_sym][:name] ||
620
- properties[name.to_sym][:symbol]
654
+ anno = ann(name.to_sym)
655
+ field_name = anno[:field] || anno[:name] || name.to_sym
621
656
  end
622
657
  options["#{name}_op".to_sym] = 'IN' if value.is_a?(Array)
623
658
  %|#{field_name} #{options.delete("#{name}_op".to_sym) || '='} #{ogmanager.store.quote(value)}|
@@ -645,23 +680,18 @@ class Entity
645
680
  class << self
646
681
 
647
682
  def resolve_primary_key(klass)
648
- # Is the class annotated with a primary key?
649
-
650
- if pk = klass.ann.self[:primary_key]
651
- return pk
652
- end
653
-
654
683
  # Search the properties, try to find one annotated as primary_key.
655
684
 
656
- for p in klass.properties.values
657
- if p.primary_key
658
- return Property.new(:symbol => p.symbol, :klass => p.klass)
685
+ for a in klass.attributes
686
+ anno = klass.ann(a)
687
+ if anno.primary_key?
688
+ return a
659
689
  end
660
690
  end
661
691
 
662
692
  # The default primary key is oid.
663
693
 
664
- return Property.new(:symbol => :oid, :klass => Fixnum)
694
+ return :oid
665
695
  end
666
696
 
667
697
  # Converts a string into it's corresponding class. Added to support STI.
@@ -673,7 +703,7 @@ class Entity
673
703
 
674
704
  def entity_from_string(str)
675
705
  res = nil
676
- Og::Manager.managed_classes.each do |klass|
706
+ Og.manager.managed_classes.each do |klass|
677
707
  if klass.name == str
678
708
  res = klass
679
709
  break
@@ -681,110 +711,9 @@ class Entity
681
711
  end
682
712
  res
683
713
  end
684
-
685
- # Entity copying support. Eventually this should all
686
- # be eval'd in at enchanting stage for the minor
687
- # speed increase.
688
- # TODO: Convert to enchantments on objects
689
-
690
- # Accepts source object, destination and ignore.
691
- # Source and destination are self explanatory; ignore
692
- # is a list of properties not to copy (i.e.
693
- # :create_time,:update_time).
694
- # By default sets the class variables directly on the
695
- # remote model instance, if you set use_setter_method to
696
- # true, uses create_time= style copying tactics,
697
-
698
- def copy_properties(source, destination, ignore = [], use_setter_method = false)
699
- property_copier(source, destination, ignore, use_setter_method, false)
700
- end
701
-
702
- # Copies relations of one record to another. Only copies
703
- # has_one, refers_to, belongs_to relationships as
704
- # has_many requires modifying of other objects and
705
- # cannot be copied (by design). If you think you need to copy
706
- # these relations, what you need is a joins_many relationship
707
- # which can be copied.
708
-
709
- def copy_inferior_relations(source, destination, ignore = [])
710
- real_ignore = Array.new
711
-
712
- # Map relation symbols to foreign keys.
713
-
714
- ignore.each do |symbol|
715
- source.class.relations.reject{|r| [Og::JoinsMany, Og::ManyToMany, Og::HasMany].include?(r.class)}.each do |relation|
716
- if relation.name == symbol.to_s
717
- real_ignore << relation.foreign_key.to_sym
718
- break
719
- end
720
- end
721
- end
722
-
723
- # Use instance variable property copier method.
724
-
725
- property_copier(source, destination, real_ignore, false, true)
726
- end
727
-
728
- def copy_equal_relations(source, destination, ignore = [])
729
- source.class.relations.reject{|r| not [Og::JoinsMany, Og::ManyToMany].include?(r.class)}.each do |relation|
730
- next if relation.name == nil or ignore.include?(relation.name)
731
- source.send(relation.name).each do |related|
732
- destination.send(relation.name).send(:<<, related)
733
- end
734
- end
735
- end
736
-
737
- # Copies all relations *except* HasMany which is impossible
738
- # to copy. Use a JoinsMany relation instead if you need a
739
- # copyable HasMany (which is irrational).
740
-
741
- def copy_relations(source, destination, ignore = [])
742
- copy_inferior_relations(source, destination, ignore)
743
- copy_equal_relations(source, destination, ignore)
744
- end
745
-
746
- # Clones an object in every possible way (cannot copy
747
- # HasMany but can copy all others - BelongsTo, etc).
748
- # Provide a source object as first arguments, the rest
749
- # (if any) are passed along to the initialize constructor
750
- # when calling new to make the copied object.
751
-
752
- def clone(source,*args)
753
- destination = source.class.new(*args)
754
- copy_properties(source, destination, [], false)
755
- # Must save here to copy join tables.
756
- destination.save!
757
- copy_relations(source, destination, [])
758
- destination.save!
759
- destination
760
- end
761
-
762
- # Does the work of clone_properties and copy_inferior_relations.
763
- # Syntax is the same with one extra field to tell the
764
- # routine what it is copying.
765
-
766
- def property_copier(source,destination,ignore,use_setter_method,relations)
767
- primary_key_symbol = source.class.primary_key.symbol
768
- source.class.properties.to_a.each do |symbol, property|
769
- next if primary_key_symbol == symbol or ignore.include?(symbol) or
770
- (relations and not property.relation) or (not relations and property.relation)
771
-
772
- variable = "@#{symbol}"
773
- if use_setter_method
774
- destination.send("#{symbol}=".to_sym,source.instance_variable_get(variable))
775
- else
776
- destination.instance_variable_set(variable, source.instance_variable_get(variable))
777
- end
778
- end
779
- end
780
714
 
781
715
  end
782
716
 
783
717
  end
784
718
 
785
719
  end
786
-
787
- # * George Moschovitis <gm@navel.gr>
788
- # * Tom Sawyer <transfire@gmail.com>
789
- # * Rob Pitt <rob@motionpath.com>
790
- # * Fabian Buch <fabian@fabian-buch.de>