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