og 0.31.0 → 0.40.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 (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>