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
@@ -3,9 +3,9 @@ module Glue
3
3
  # Revision support for Og-managed classes.
4
4
  #
5
5
  # class Article
6
- # is Revisable
7
- # property :body, String, :revisable => true
8
- # property :title, String
6
+ # is Revisable
7
+ # property :body, String, :revisable => true
8
+ # property :title, String
9
9
  # end
10
10
  #
11
11
  # Generates the Revision class:
@@ -24,7 +24,7 @@ module Glue
24
24
  module Revisable
25
25
  # The revision of the revisable object.
26
26
 
27
- property :revision, Fixnum
27
+ attr_accessor :revision, Fixnum
28
28
 
29
29
  # This mixin is injected into the dynamically generated
30
30
  # Revision class. You can customize this in your application
@@ -37,38 +37,36 @@ module Revisable
37
37
 
38
38
  # Override to handle your options.
39
39
 
40
- def initialize(obj, options = {})
40
+ def initialize obj, options = {}
41
41
  revision_from(obj)
42
42
  @create_time = Time.now
43
43
  end
44
44
 
45
- def revision_from(obj)
46
- for prop in obj.class.properties.values
47
- # gmosx, FIXME: test against primary key, not oid.
48
- unless prop.symbol == :oid
49
- instance_variable_set "@#{prop}", obj.send(prop.to_s)
45
+ def revision_from obj
46
+ for a in obj.class.serializable_attributes
47
+ unless a == obj.class.primary_key
48
+ instance_variable_set "@#{a}", obj.send(a.to_s)
50
49
  end
51
50
  end
52
51
  end
53
52
 
54
- def revision_to(obj)
55
- for prop in obj.class.properties.values
56
- # gmosx, FIXME: test against primary key, not oid.
57
- unless prop.symbol == :oid
58
- obj.instance_variable_set "@#{prop}", self.send(prop.to_s)
53
+ def revision_to obj
54
+ for a in obj.class.serializable_attributes
55
+ unless a == obj.class.primary_key
56
+ obj.instance_variable_set "@#{a}", self.send(a.to_s)
59
57
  end
60
58
  end
61
59
  end
62
60
  alias_method :apply_to, :revision_to
63
61
  end
64
62
 
65
- def self.included(base)
63
+ def self.included base
66
64
  super
67
65
 
68
66
  base.module_eval %{
69
67
  class Revision < #{base}
70
68
  include Revisable::Mixin
71
- belongs_to #{base}
69
+ refers_to #{base}
72
70
  end
73
71
  }
74
72
 
@@ -78,7 +76,7 @@ module Revisable
78
76
  # Can accept options like owner or a comment.
79
77
  # You can specialize this in your app.
80
78
 
81
- def revise(options = {})
79
+ def revise options = {}
82
80
  if self.revision.nil?
83
81
  self.revision = 1
84
82
  else
@@ -92,13 +90,13 @@ module Revisable
92
90
 
93
91
  # Rollback to an older revision.
94
92
 
95
- def rollback(rev, options = {})
93
+ def rollback rev, options = {}
96
94
  self.revise(options) { |obj| get_revision(rev).apply_to(obj) }
97
95
  end
98
96
 
99
97
  # Return a revision.
100
-
101
- def get_revision(rev)
98
+
99
+ def get_revision rev
102
100
  return self if rev.to_i == self.revision
103
101
  self.revisions.find_one(:condition => "revision=#{rev}")
104
102
  end
@@ -119,6 +117,3 @@ module Revisable
119
117
  end
120
118
 
121
119
  end
122
-
123
- # * George Moschovitis <gm@navel.gr>
124
- # * Dirk Barnikel <dirk.barnikel@gmx.de>
@@ -24,5 +24,3 @@ module Searchable
24
24
  end
25
25
 
26
26
  end
27
-
28
- # * George Moschovitis <gm@navel.gr>
@@ -12,7 +12,7 @@ require 'facet/inflect'
12
12
  #++
13
13
 
14
14
  class Tag
15
- property :name, String, :uniq => true
15
+ property :name, String, :uniq => true, :key => true
16
16
  property :count, Fixnum
17
17
 
18
18
  # An alias for count.
@@ -97,10 +97,10 @@ module Glue
97
97
  # ..
98
98
  # end
99
99
  #
100
- # article.tag('navel', 'gmosx', 'nitro')
100
+ # article.tag('great', 'gmosx', 'nitro')
101
101
  # article.tags
102
102
  # article.tag_names
103
- # Article.find_with_tags('navel', 'gmosx')
103
+ # Article.find_with_tags('great', 'gmosx')
104
104
  # Article.find_with_any_tag('name', 'gmosx')
105
105
  #
106
106
  # Tag.find_by_name('ruby').articles
@@ -142,6 +142,7 @@ module Taggable
142
142
 
143
143
  def delete_all_tags
144
144
  for tag in tags
145
+ tag.reload
145
146
  tag.unlink
146
147
  end
147
148
  tags.clear
@@ -160,6 +161,13 @@ module Taggable
160
161
  tags.collect { |t| t.name }.join(separator)
161
162
  end
162
163
 
164
+ # Return the linked tag string.
165
+ # Typically you will overrie this in your application.
166
+
167
+ def tag_string_linked(separator = Taggable.separator)
168
+ tags.collect { |t| %|<a href="/tags/#{t.name}">#{t.name}</a>| }.join(separator)
169
+ end
170
+
163
171
  # Checks to see if this object has been tagged
164
172
  # with +tag_name+.
165
173
 
@@ -173,7 +181,24 @@ module Taggable
173
181
  # INTERSECTION (AND)
174
182
 
175
183
  def find_with_tags(*names)
176
- return find_request(:all, names)
184
+ relation = relations.reject{|r| r.name != :tags}.first
185
+ info = ogmanager.store.join_table_info(relation)
186
+ count = names.size
187
+ names = names.map { |n| ogmanager.store.quote(n) }.join(',')
188
+ sql = %{
189
+ SELECT *
190
+ FROM #{info[:owner_table]} AS o
191
+ WHERE o.oid IN (
192
+ SELECT j.#{info[:owner_key]}
193
+ FROM #{info[:target_table]} AS t
194
+ JOIN #{info[:table]} AS j
195
+ ON t.oid = j.#{info[:target_key]}
196
+ WHERE (t.name IN (#{names}))
197
+ GROUP BY j.#{info[:owner_key]}
198
+ HAVING COUNT(j.#{info[:owner_key]}) = #{count}
199
+ )
200
+ }
201
+ return self.select(sql)
177
202
  end
178
203
  alias_method :find_with_tag, :find_with_tags
179
204
 
@@ -181,29 +206,9 @@ module Taggable
181
206
  # UNION (OR)
182
207
 
183
208
  def find_with_any_tag(*names)
184
- return find_request(:any, names)
185
- end
186
-
187
- private
188
-
189
- def find_request(request_type, names)
190
- condition = ""
191
- opt = names.last
192
- if opt.is_a?(Hash)
193
- names.pop
194
- escaped_condition = ogmanager.store.prepare_statement(opt[:condition])
195
- #escaped_condition = ogmanager.store.escape_condition(opt[:condition])
196
- condition = "AND #{escaped_condition}"
197
- end
198
-
199
- relation = relations.detect {|r| r.name == :tags}
209
+ relation = relations.reject{|r| r.name != :tags}.first
200
210
  info = ogmanager.store.join_table_info(relation)
201
-
202
- if request_type == :all
203
- count = names.size
204
- additionnal_code = "HAVING COUNT(j.#{info[:owner_key]}) = #{count}"
205
- end
206
-
211
+ count = names.size
207
212
  names = names.map { |n| ogmanager.store.quote(n) }.join(',')
208
213
  sql = %{
209
214
  SELECT *
@@ -215,7 +220,6 @@ module Taggable
215
220
  ON t.oid = j.#{info[:target_key]}
216
221
  WHERE (t.name IN (#{names}))
217
222
  GROUP BY j.#{info[:owner_key]}
218
- #{additionnal_code}
219
223
  )
220
224
  }
221
225
  return self.select(sql)
@@ -250,5 +254,3 @@ module Taggable
250
254
  end
251
255
 
252
256
  end
253
-
254
- # * George Moschovitis <gm@navel.gr>
@@ -4,6 +4,10 @@ require 'facets/more/aspects'
4
4
  module Glue
5
5
 
6
6
  # Adds timestamping functionality.
7
+ #--
8
+ # TODO: add an initialize method that inits times
9
+ # before saving?
10
+ #++
7
11
 
8
12
  module Timestamped
9
13
  include ::Aspects
@@ -32,5 +36,3 @@ module TimestampedOnCreate
32
36
  end
33
37
 
34
38
  end
35
-
36
- # * George Moschovitis <gm@navel.gr>
data/lib/og.rb CHANGED
@@ -1,12 +1,11 @@
1
1
  # = Og
2
2
  #
3
3
  # Copyright (c) 2004-2006, George Moschovitis (http://www.gmosx.com)
4
- # Copyright (c) 2004-2006, Navel Ltd (http://www.navel.gr)
5
4
  #
6
5
  # Og (http://www.nitroproject.org) is copyrighted free software
7
- # created and maintained by George Moschovitis (mailto:gm@navel.gr)
8
- # and released under the standard BSD Licence. For details
9
- # consult the file doc/LICENCE.
6
+ # created and maintained by George Moschovitis
7
+ # (mailto:george.moschovitis@gmail.com) and released under the
8
+ # standard BSD Licence. For details consult the file doc/LICENCE.
10
9
 
11
10
  require 'facet/synchash'
12
11
  require 'facet/syncarray'
@@ -35,7 +34,7 @@ require 'glue/configuration'
35
34
  # === Design
36
35
  #
37
36
  # Og allows the serialization of arbitrary Ruby objects. Just
38
- # mark them as Object (or Array or Hash) in the prop_accessor
37
+ # mark them as Object (or Array or Hash) in the attr_accessor
39
38
  # and the engine will serialize a YAML dump of the object.
40
39
  # Arbitrary object graphs are supported too.
41
40
 
@@ -43,12 +42,16 @@ module Og
43
42
 
44
43
  # The version.
45
44
 
46
- Version = '0.31.0'
45
+ Version = '0.40.0'
47
46
 
48
47
  # Library path.
49
48
 
50
49
  LibPath = File.dirname(__FILE__)
51
50
 
51
+ # The default setup for Og managers.
52
+
53
+ setting :manager_options, :default => { :adapter => :sqlite }, :doc => 'The default setup for Og managers'
54
+
52
55
  # If true, check for implicit changes in the object
53
56
  # graph. For example when you add an object to a parent
54
57
  # the object might be removed from his previous parent.
@@ -85,6 +88,11 @@ module Og
85
88
 
86
89
  setting :create_schema, :default => true, :doc => 'If true, Og tries to create/update the schema in the data store'
87
90
 
91
+ # If true, Og destroys the schema on startup. Useful while
92
+ # developing / debugging.
93
+
94
+ setting :destroy_schema, :default => false, :doc => 'If true, Og destroys the schema on startup'
95
+
88
96
  # If true raises exceptions on store errors, usefull when
89
97
  # debugging. For production environments it should probably be
90
98
  # set to false to make the application more fault tolerant.
@@ -125,32 +133,47 @@ module Og
125
133
  # If no options are passed, sqlite is selected
126
134
  # as the default store.
127
135
 
128
- def setup(options = {})
129
- options={:store => :sqlite}.update(options)
130
- # This is a flag a store or manager can use to determine
131
- # if it was being called by Og.setup to provide
132
- # additional, faster or enhanced functionality.
136
+ def start(options = Og.manager_options)
137
+ # Use sqlite as the default adapter.
138
+
139
+ unless options[:adapter] || options[:store]
140
+ options[:adapter] = :sqlite
141
+ end
133
142
 
134
- options[:called_by_og_setup] = true if options[:called_by_og_setup].nil?
143
+ # This is a flag a store or manager can use to determine
144
+ # if it was being called by Og.setup to provide
145
+ # additional, faster or enhanced functionality.
135
146
 
136
- @thread_safe = options.fetch(:thread_safe, true)
147
+ options[:called_by_og_setup] = true if options[:called_by_og_setup].nil?
137
148
 
138
- m = @manager = Manager.new(options)
139
- m.manage_classes
149
+ @thread_safe = true
140
150
 
141
- # Allows functionality that requires a store is
142
- # finalized to be implemented. A vastly superior
143
- # method of constructing foreign key constraints is an
144
- # example of functionality this provides. Currently
145
- # only used by the PostgreSQL store.
146
-
147
- m.post_setup if options[:called_by_og_setup]
151
+ m = @manager = Manager.new(options)
152
+ m.manage_classes(options[:classes])
153
+
154
+ # Allows functionality that requires an initialized
155
+ # store to be implemented. A vastly superior
156
+ # method of constructing foreign key constraints is an
157
+ # example of functionality this provides. Currently
158
+ # only used by the PostgreSQL store.
159
+
160
+ m.post_setup if options[:called_by_og_setup]
161
+
148
162
  return m
163
+ rescue Exception => ex
164
+ Logger.error "#{ex.class} in Og.setup:"
165
+ Logger.error ex.message
166
+ if $DBG
167
+ Logger.error ex.backtrace.join("\n")
168
+ exit
169
+ end
149
170
  end
150
- alias_method :connect, :setup
151
- alias_method :options=, :setup
152
- alias_method :setup=, :setup
153
- alias_method :start, :setup
171
+
172
+ alias run start
173
+ alias connect start
174
+ # The following is deprecated, used for compatibility.
175
+ alias setup start
176
+ alias setup= start
154
177
 
155
178
  # Helper method.
156
179
 
@@ -169,7 +192,7 @@ module Og
169
192
  def thread_safe=(bool)
170
193
  @thread_safe = bool
171
194
  # @manager and @manager.class.managers.each { |m| m.initialize_store }
172
- @thread_safe
195
+ return @thread_safe
173
196
  end
174
197
  end
175
198
 
@@ -184,5 +207,3 @@ require 'og/errors'
184
207
  require 'og/types'
185
208
  require 'og/validation'
186
209
  require 'og/markers'
187
-
188
- # * George Moschovitis <gm@navel.gr>
@@ -0,0 +1,19 @@
1
+ require 'og/store'
2
+
3
+ module Og
4
+
5
+ # A specialization of a Store.
6
+
7
+ class Adapter < Store
8
+
9
+ # Load the store for the given name.
10
+
11
+ def self.for_name name
12
+ Logger.info "Og uses the #{name.to_s.capitalize} store."
13
+ require('og/adapter/' + name.to_s)
14
+ return Og.const_get("#{name.to_s.capitalize}Adapter")
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,212 @@
1
+ begin
2
+ require 'mysql'
3
+ rescue Object => ex
4
+ Logger.error 'Ruby-Mysql bindings are not installed!'
5
+ Logger.error 'Trying to use the pure-Ruby binding included in Og'
6
+ begin
7
+ # Attempt to use the included pure ruby version.
8
+ require 'og/vendor/mysql'
9
+ rescue Object => ex
10
+ Logger.error ex
11
+ end
12
+ end
13
+
14
+ require 'og/store/sql'
15
+ require 'og/adapter/mysql/override'
16
+ require 'og/adapter/mysql/utils'
17
+
18
+ module Og
19
+
20
+ # A Store that persists objects into a MySQL database.
21
+ # To read documentation about the methods, consult
22
+ # the documentation for SqlStore and Store.
23
+ #
24
+ # Here is some useful code to initialize your MySQL
25
+ # RDBMS for development. You probably want to be
26
+ # more careful with provileges on your production
27
+ # environment.
28
+ #
29
+ # mysql> GRANT ALL PRIVELEGES
30
+ # ON keystone.*
31
+ # TO <$sys_dbuser name>@localhost
32
+ # IDENTIFIED BY '(password)'
33
+ # WITH GRANT OPTION;
34
+
35
+ class MysqlAdapter < SqlStore
36
+ include MysqlUtils; extend MysqlUtils
37
+
38
+ # Initialize the MySQL store.
39
+ #
40
+ # === Options
41
+ #
42
+ # * :name, the name of the database.
43
+ # * :user, the username for using the database.
44
+ # * :password, the password of the database user.
45
+ # * :address, the addres where the server is listening.
46
+ # * :port, the port where the server is listening.
47
+ # * :socket, is useful when the pure ruby driver is used.
48
+ # this is the location of mysql.sock. For Ubuntu/Debian
49
+ # this is '/var/run/mysqld/mysqld.sock'. You can find
50
+ # the location for your system in my.cnf
51
+
52
+ def initialize(options)
53
+ super
54
+
55
+ @typemap.update(TrueClass => 'tinyint', Time => 'datetime')
56
+
57
+ @conn = Mysql.connect(
58
+ options[:address] || options[:host] || 'localhost',
59
+ options[:user],
60
+ options[:password],
61
+ options[:name],
62
+ options[:port],
63
+ options[:socket]
64
+ )
65
+
66
+ # You should set recconect to true to avoid MySQL has
67
+ # gone away errors.
68
+
69
+ if @conn.respond_to? :reconnect
70
+ options[:reconnect] = true unless options.has_key?(:reconnect)
71
+ @conn.reconnect = options[:reconnect]
72
+ end
73
+
74
+ rescue Object => ex
75
+ if database_does_not_exist_exception? ex
76
+ Logger.info "Database '#{options[:name]}' not found!"
77
+ create_db(options)
78
+ retry
79
+ end
80
+
81
+ raise
82
+ end
83
+
84
+ def close
85
+ @conn.close
86
+ super
87
+ end
88
+
89
+ # Create a database.
90
+
91
+ def create_db(options)
92
+ # gmosx: system is used to avoid shell expansion.
93
+ system 'mysqladmin', '-f', "--user=#{options[:user]}",
94
+ "--password=#{options[:password]}",
95
+ "--host=#{options[:address]}",
96
+ "--port=#{options.fetch(:port, 3306)}" ,
97
+ 'create', options[:name]
98
+ super
99
+ end
100
+
101
+ # Drop a database.
102
+
103
+ def destroy_db(options)
104
+ system 'mysqladmin', '-f', "--user=#{options[:user]}",
105
+ "--password=#{options[:password]}",
106
+ "--host=#{options[:address]}",
107
+ "--port=#{options.fetch(:port, 3306)}" ,
108
+ 'drop', options[:name]
109
+ super
110
+ end
111
+
112
+ # The type used for default primary keys.
113
+
114
+ def primary_key_type
115
+ 'integer AUTO_INCREMENT PRIMARY KEY'
116
+ end
117
+
118
+ def query_statement(sql)
119
+ @conn.query_with_result = true
120
+ return @conn.query(sql)
121
+ end
122
+
123
+ def exec_statement(sql)
124
+ @conn.query_with_result = false
125
+ @conn.query(sql)
126
+ end
127
+
128
+ # Perform an sql update, return the number of updated rows.
129
+
130
+ def sql_update(sql)
131
+ exec(sql)
132
+ @conn.affected_rows
133
+ end
134
+
135
+ # Return the last inserted row id.
136
+
137
+ def last_insert_id(klass = nil)
138
+ @conn.insert_id
139
+ end
140
+
141
+ # Start a transaction.
142
+
143
+ def start
144
+ # nop
145
+ # FIXME: InnoDB supports transactions.
146
+ end
147
+
148
+ # Commit a transaction.
149
+
150
+ def commit
151
+ # nop, not supported?
152
+ # FIXME: InnoDB supports transactions.
153
+ end
154
+
155
+ # Rollback a transaction.
156
+
157
+ def rollback
158
+ # nop, not supported?
159
+ # FIXME: InnoDB supports transactions.
160
+ end
161
+
162
+ def write_attr(s, a)
163
+ store = self.class
164
+
165
+ if a.class.ancestor? Integer
166
+ "#\{@#{s} || 'NULL'\}"
167
+
168
+ elsif a.class.ancestor? Float
169
+ "#\{@#{s} || 'NULL'\}"
170
+
171
+ elsif a.class.ancestor? String
172
+ %|#\{@#{s} ? "'#\{#{store}.escape(@#{s})\}'" : 'NULL'\}|
173
+
174
+ elsif a.class.ancestor? Time
175
+ %|#\{@#{s} ? "'#\{#{store}.timestamp(@#{s})\}'" : 'NULL'\}|
176
+
177
+ elsif a.class.ancestor? Date
178
+ %|#\{@#{s} ? "'#\{#{store}.date(@#{s})\}'" : 'NULL'\}|
179
+
180
+ elsif a.class.ancestor? TrueClass
181
+ "#\{@#{s} ? \"'1'\" : 'NULL' \}"
182
+
183
+ elsif a.class.ancestor? Og::Blob
184
+ %|#\{@#{s} ? "'#\{#{store}.escape(#{store}.blob(@#{s}))\}'" : 'NULL'\}|
185
+
186
+ else
187
+ # keep the '' for nil symbols.
188
+ %|#\{@#{s} ? "'#\{#{store}.escape(@#{s}.to_yaml)\}'" : "''"\}|
189
+ end
190
+ end
191
+
192
+ # Returns the MySQL information of a table within the database or
193
+ # nil if it doesn't exist. Mostly for internal usage.
194
+
195
+ def table_info(table)
196
+ r = query_statement("SHOW TABLE STATUS FROM #{@options[:name]} LIKE '#{self.class.escape(table.to_s)}'")
197
+ return r && r.blank? ? nil : r.next
198
+ end
199
+
200
+ private
201
+
202
+ def database_does_not_exist_exception?(ex)
203
+ ex.errno == 1049
204
+ end
205
+
206
+ def table_already_exists_exception?(ex)
207
+ ex.errno == 1050 # table already exists.
208
+ end
209
+
210
+ end
211
+
212
+ end