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
@@ -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