og 0.31.0 → 0.40.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/{AUTHORS → CONTRIBUTORS} +26 -10
- data/doc/LICENSE +2 -3
- data/doc/RELEASES +56 -7
- data/doc/tutorial.txt +15 -15
- data/lib/glue/cacheable.rb +2 -5
- data/lib/glue/hierarchical.rb +1 -4
- data/lib/glue/optimistic_locking.rb +0 -2
- data/lib/glue/orderable.rb +79 -75
- data/lib/glue/revisable.rb +19 -24
- data/lib/glue/searchable.rb +0 -2
- data/lib/glue/taggable.rb +31 -29
- data/lib/glue/timestamped.rb +4 -2
- data/lib/og.rb +50 -29
- data/lib/og/adapter.rb +19 -0
- data/lib/og/adapter/mysql.rb +212 -0
- data/lib/og/adapter/mysql/override.rb +34 -0
- data/lib/og/adapter/mysql/script.rb +15 -0
- data/lib/og/adapter/mysql/utils.rb +40 -0
- data/lib/og/adapter/postgresql.rb +231 -0
- data/lib/og/adapter/postgresql/override.rb +117 -0
- data/lib/og/adapter/postgresql/script.rb +15 -0
- data/lib/og/adapter/postgresql/utils.rb +35 -0
- data/lib/og/adapter/sqlite.rb +132 -0
- data/lib/og/adapter/sqlite/override.rb +33 -0
- data/lib/og/adapter/sqlite/script.rb +15 -0
- data/lib/og/collection.rb +35 -7
- data/lib/og/{evolution.rb → dump.rb} +4 -5
- data/lib/og/entity.rb +102 -173
- data/lib/og/entity/clone.rb +119 -0
- data/lib/og/errors.rb +0 -2
- data/lib/og/manager.rb +85 -37
- data/lib/og/relation.rb +52 -34
- data/lib/og/relation/belongs_to.rb +0 -2
- data/lib/og/relation/has_many.rb +27 -4
- data/lib/og/relation/joins_many.rb +41 -14
- data/lib/og/relation/many_to_many.rb +10 -0
- data/lib/og/relation/refers_to.rb +22 -5
- data/lib/og/store.rb +80 -86
- data/lib/og/store/sql.rb +710 -713
- data/lib/og/store/sql/evolution.rb +119 -0
- data/lib/og/store/sql/join.rb +155 -0
- data/lib/og/store/sql/utils.rb +149 -0
- data/lib/og/test/assertions.rb +1 -3
- data/lib/og/test/testcase.rb +0 -2
- data/lib/og/types.rb +2 -5
- data/lib/og/validation.rb +6 -9
- data/test/{og/mixin → glue}/tc_hierarchical.rb +3 -13
- data/test/glue/tc_og_paginate.rb +47 -0
- data/test/{og/mixin → glue}/tc_optimistic_locking.rb +2 -12
- data/test/{og/mixin → glue}/tc_orderable.rb +15 -23
- data/test/glue/tc_orderable2.rb +47 -0
- data/test/glue/tc_revisable.rb +3 -3
- data/test/{og/mixin → glue}/tc_taggable.rb +20 -10
- data/test/{og/mixin → glue}/tc_timestamped.rb +2 -12
- data/test/glue/tc_webfile.rb +36 -0
- data/test/og/CONFIG.rb +8 -11
- data/test/og/multi_validations_model.rb +14 -0
- data/test/og/store/tc_filesys.rb +3 -1
- data/test/og/store/tc_kirby.rb +16 -13
- data/test/og/store/tc_sti.rb +11 -11
- data/test/og/store/tc_sti2.rb +79 -0
- data/test/og/tc_build.rb +41 -0
- data/test/og/tc_cacheable.rb +3 -2
- data/test/og/tc_has_many.rb +96 -0
- data/test/og/tc_inheritance.rb +6 -4
- data/test/og/tc_joins_many.rb +93 -0
- data/test/og/tc_multi_validations.rb +5 -7
- data/test/og/tc_multiple.rb +7 -6
- data/test/og/tc_override.rb +13 -7
- data/test/og/tc_primary_key.rb +30 -0
- data/test/og/tc_relation.rb +8 -14
- data/test/og/tc_reldelete.rb +163 -0
- data/test/og/tc_reverse.rb +17 -14
- data/test/og/tc_scoped.rb +3 -11
- data/test/og/tc_setup.rb +13 -11
- data/test/og/tc_store.rb +21 -28
- data/test/og/tc_validation2.rb +2 -2
- data/test/og/tc_validation_loop.rb +17 -15
- metadata +109 -103
- data/INSTALL +0 -91
- data/ProjectInfo +0 -51
- data/README +0 -177
- data/doc/config.txt +0 -28
- data/examples/README +0 -23
- data/examples/mysql_to_psql.rb +0 -71
- data/examples/run.rb +0 -271
- data/lib/glue/tree.rb +0 -218
- data/lib/og/store/alpha/filesys.rb +0 -110
- data/lib/og/store/alpha/memory.rb +0 -295
- data/lib/og/store/alpha/sqlserver.rb +0 -256
- data/lib/og/store/kirby.rb +0 -490
- data/lib/og/store/mysql.rb +0 -415
- data/lib/og/store/psql.rb +0 -875
- data/lib/og/store/sqlite.rb +0 -348
- data/lib/og/store/sqlite2.rb +0 -241
- data/setup.rb +0 -1585
- data/test/og/tc_sti_find.rb +0 -35
data/lib/glue/revisable.rb
CHANGED
@@ -3,9 +3,9 @@ module Glue
|
|
3
3
|
# Revision support for Og-managed classes.
|
4
4
|
#
|
5
5
|
# class Article
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
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
|
-
|
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
|
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
|
46
|
-
for
|
47
|
-
|
48
|
-
|
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
|
55
|
-
for
|
56
|
-
|
57
|
-
|
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
|
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
|
-
|
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
|
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
|
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
|
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>
|
data/lib/glue/searchable.rb
CHANGED
data/lib/glue/taggable.rb
CHANGED
@@ -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('
|
100
|
+
# article.tag('great', 'gmosx', 'nitro')
|
101
101
|
# article.tags
|
102
102
|
# article.tag_names
|
103
|
-
# Article.find_with_tags('
|
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
|
-
|
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
|
-
|
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>
|
data/lib/glue/timestamped.rb
CHANGED
@@ -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
|
8
|
-
# and released under the
|
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
|
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.
|
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
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
-
|
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
|
-
|
147
|
+
options[:called_by_og_setup] = true if options[:called_by_og_setup].nil?
|
137
148
|
|
138
|
-
|
139
|
-
m.manage_classes
|
149
|
+
@thread_safe = true
|
140
150
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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>
|
data/lib/og/adapter.rb
ADDED
@@ -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
|