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
@@ -0,0 +1,34 @@
|
|
1
|
+
#--
|
2
|
+
# Customize the standard MySQL driver objects to make
|
3
|
+
# more compatible with Og.
|
4
|
+
#++
|
5
|
+
|
6
|
+
class Mysql # :nodoc: all
|
7
|
+
|
8
|
+
class Result # :nodoc: all
|
9
|
+
def blank?
|
10
|
+
0 == num_rows
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method :next, :fetch_row
|
14
|
+
|
15
|
+
def each_row
|
16
|
+
each do |row|
|
17
|
+
yield(row, 0)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def first_value
|
22
|
+
val = fetch_row[0]
|
23
|
+
free
|
24
|
+
return val
|
25
|
+
end
|
26
|
+
|
27
|
+
alias close free
|
28
|
+
|
29
|
+
def fields
|
30
|
+
fetch_fields.collect { |f| f.name }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Helper for mysql scripts.
|
2
|
+
#
|
3
|
+
# === Example
|
4
|
+
#
|
5
|
+
# mysql '-u root -p', %{
|
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 mysql(opts, stream)
|
12
|
+
IO.popen("mysql #{opts}", 'w') do |io|
|
13
|
+
io.puts stream
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'og/store/sql/utils'
|
2
|
+
|
3
|
+
module Og
|
4
|
+
|
5
|
+
module MysqlUtils
|
6
|
+
include SqlUtils
|
7
|
+
|
8
|
+
def escape(str)
|
9
|
+
return nil unless str
|
10
|
+
return Mysql.quote(str)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Escape the various Ruby types.
|
14
|
+
|
15
|
+
def quote(vals)
|
16
|
+
vals = [vals] unless vals.is_a?(Array)
|
17
|
+
quoted = vals.inject('') do |s, val|
|
18
|
+
s += case val
|
19
|
+
when Fixnum, Integer, Float
|
20
|
+
val ? val.to_s : 'NULL'
|
21
|
+
when String
|
22
|
+
val ? "'#{escape(val)}'" : 'NULL'
|
23
|
+
when Time
|
24
|
+
val ? "'#{timestamp(val)}'" : 'NULL'
|
25
|
+
when Date
|
26
|
+
val ? "'#{date(val)}'" : 'NULL'
|
27
|
+
when TrueClass, FalseClass
|
28
|
+
val ? "'1'" : 'NULL'
|
29
|
+
else
|
30
|
+
# gmosx: keep the '' for nil symbols.
|
31
|
+
val ? escape(val.to_yaml) : ''
|
32
|
+
end + ','
|
33
|
+
end
|
34
|
+
quoted.chop!
|
35
|
+
vals.size > 1 ? "(#{quoted})" : quoted
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
begin
|
2
|
+
require 'postgres'
|
3
|
+
rescue Object => ex
|
4
|
+
Logger.error 'Ruby-PostgreSQL bindings are not installed!'
|
5
|
+
Logger.error ex
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'og/store/sql'
|
9
|
+
require 'og/adapter/postgresql/override'
|
10
|
+
require 'og/adapter/postgresql/utils'
|
11
|
+
|
12
|
+
module Og
|
13
|
+
|
14
|
+
# A Store that persists objects into a PostgreSQL database.
|
15
|
+
# To read documentation about the methods, consult the
|
16
|
+
# documentation for SqlStore and Store.
|
17
|
+
#
|
18
|
+
# This is the reference Og store.
|
19
|
+
|
20
|
+
class PostgresqlAdapter < SqlStore
|
21
|
+
extend PostgresqlUtils; include PostgresqlUtils
|
22
|
+
|
23
|
+
def initialize(options)
|
24
|
+
super
|
25
|
+
|
26
|
+
@typemap.update(Og::Blob => 'bytea')
|
27
|
+
|
28
|
+
@conn = PGconn.connect(
|
29
|
+
options[:address] || options[:host],
|
30
|
+
options[:port], nil, nil,
|
31
|
+
options[:name],
|
32
|
+
options[:user].to_s,
|
33
|
+
options[:password].to_s
|
34
|
+
)
|
35
|
+
schema_order = options[:schema_order]
|
36
|
+
encoding = options[:encoding]
|
37
|
+
min_messages = options[:min_messages]
|
38
|
+
|
39
|
+
@conn.exec("SET search_path TO #{schema_order}") if schema_order
|
40
|
+
@conn.exec("SET client_encoding TO '#{encoding}'") if encoding
|
41
|
+
@conn.exec("SET client_min_messages TO '#{min_messages}'") if min_messages
|
42
|
+
rescue PGError => ex
|
43
|
+
if database_does_not_exist_exception? ex
|
44
|
+
Logger.info "Database '#{options[:name]}' not found!"
|
45
|
+
create_db(options)
|
46
|
+
retry
|
47
|
+
end
|
48
|
+
raise
|
49
|
+
end
|
50
|
+
|
51
|
+
def close
|
52
|
+
@conn.close
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_db(options)
|
57
|
+
# gmosx: system is used to avoid shell expansion.
|
58
|
+
system 'createdb', options[:name], '-U', options[:user]
|
59
|
+
super
|
60
|
+
end
|
61
|
+
|
62
|
+
def destroy_db(options)
|
63
|
+
system 'dropdb', options[:name], '-U', options[:user]
|
64
|
+
super
|
65
|
+
end
|
66
|
+
|
67
|
+
# The type used for default primary keys.
|
68
|
+
|
69
|
+
def primary_key_type
|
70
|
+
'serial PRIMARY KEY'
|
71
|
+
end
|
72
|
+
|
73
|
+
def enchant(klass, manager)
|
74
|
+
pk = klass.primary_key
|
75
|
+
|
76
|
+
seq = if klass.schema_inheritance_child?
|
77
|
+
"#{table(klass.schema_inheritance_root_class)}_#{pk}_seq"
|
78
|
+
else
|
79
|
+
"#{table(klass)}_#{pk}_seq"
|
80
|
+
end
|
81
|
+
|
82
|
+
pkann = klass.ann[pk]
|
83
|
+
|
84
|
+
pkann[:sequence] = (pkann[:sql] =~ /SERIAL/i) ? seq : false
|
85
|
+
|
86
|
+
super
|
87
|
+
end
|
88
|
+
|
89
|
+
def query_statement(sql)
|
90
|
+
return @conn.exec(sql)
|
91
|
+
end
|
92
|
+
|
93
|
+
def exec_statement(sql)
|
94
|
+
@conn.exec(sql).clear
|
95
|
+
end
|
96
|
+
|
97
|
+
def sql_update(sql)
|
98
|
+
Logger.debug sql if $DBG
|
99
|
+
res = @conn.exec(sql)
|
100
|
+
changed = res.cmdtuples
|
101
|
+
res.clear
|
102
|
+
return changed
|
103
|
+
end
|
104
|
+
|
105
|
+
# Return the last inserted row id.
|
106
|
+
|
107
|
+
def last_insert_id(klass)
|
108
|
+
seq = klass.ann[klass.primary_key][:sequence]
|
109
|
+
|
110
|
+
res = query("SELECT nextval('#{seq}')")
|
111
|
+
lid = Integer(res.first_value)
|
112
|
+
|
113
|
+
return lid
|
114
|
+
end
|
115
|
+
|
116
|
+
# The insert sql statements.
|
117
|
+
|
118
|
+
def insert_sql(sql, klass)
|
119
|
+
str = ''
|
120
|
+
|
121
|
+
if klass.ann[klass.primary_key][:sequence]
|
122
|
+
str << "@#{klass.primary_key} = store.last_insert_id(#{klass})\n"
|
123
|
+
end
|
124
|
+
|
125
|
+
str << "store.exec \"#{sql}\""
|
126
|
+
|
127
|
+
return str
|
128
|
+
end
|
129
|
+
|
130
|
+
# :section: Transaction methods.
|
131
|
+
|
132
|
+
# Start a new transaction.
|
133
|
+
|
134
|
+
def start
|
135
|
+
# neumann: works with earlier PSQL databases too.
|
136
|
+
exec('BEGIN TRANSACTION') if @transaction_nesting < 1
|
137
|
+
|
138
|
+
if @transaction_nesting >= 1 && @conn.server_version > 80000
|
139
|
+
exec("SAVEPOINT SP#{@transaction_nesting}")
|
140
|
+
end
|
141
|
+
|
142
|
+
@transaction_nesting += 1
|
143
|
+
end
|
144
|
+
|
145
|
+
# Commit a transaction.
|
146
|
+
|
147
|
+
def commit
|
148
|
+
@transaction_nesting -= 1
|
149
|
+
exec('COMMIT') if @transaction_nesting < 1
|
150
|
+
|
151
|
+
if @transaction_nesting >= 1 && @conn.server_version > 80000
|
152
|
+
exec("RELEASE SAVEPOINT SP#{@transaction_nesting}")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Rollback a transaction.
|
157
|
+
|
158
|
+
def rollback
|
159
|
+
@transaction_nesting -= 1
|
160
|
+
exec('ROLLBACK') if @transaction_nesting < 1
|
161
|
+
|
162
|
+
if @transaction_nesting >= 1 && @conn.server_version > 80000
|
163
|
+
exec("ROLLBACK TO SAVEPOINT SP#{@transaction_nesting}")
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def read_attr(s, a, col)
|
168
|
+
store = self.class
|
169
|
+
{
|
170
|
+
String => nil,
|
171
|
+
Integer => :parse_int,
|
172
|
+
Float => :parse_float,
|
173
|
+
Time => :parse_timestamp,
|
174
|
+
Date => :parse_date,
|
175
|
+
TrueClass => :parse_boolean,
|
176
|
+
Og::Blob => :parse_blob
|
177
|
+
}.each do |klass, meth|
|
178
|
+
if a.class.ancestor? klass
|
179
|
+
return meth ?
|
180
|
+
"#{store}.#{meth}(res[#{col} + offset])" : "res[#{col} + offset]"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# else try to load it via YAML
|
185
|
+
"YAML::load(res[#{col} + offset])"
|
186
|
+
end
|
187
|
+
|
188
|
+
def eval_og_allocate(klass)
|
189
|
+
if klass.schema_inheritance?
|
190
|
+
klass.module_eval %{
|
191
|
+
def self.og_allocate(res, row = 0)
|
192
|
+
Object.constant(res[0]).allocate
|
193
|
+
end
|
194
|
+
}
|
195
|
+
else
|
196
|
+
klass.module_eval %{
|
197
|
+
def self.og_allocate(res, row = 0)
|
198
|
+
self.allocate
|
199
|
+
end
|
200
|
+
}
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def read_row(obj, res, res_row, row)
|
205
|
+
res.fields.each_with_index do |field, idx|
|
206
|
+
obj.instance_variable_set "@#{field}", res.getvalue(row, idx)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Returns the PostgreSQL information of a table within the database or
|
211
|
+
# nil if it doesn't exist. Mostly for internal usage.
|
212
|
+
|
213
|
+
def table_info(table)
|
214
|
+
r = query_statement("SELECT c.* FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog', 'pg_toast') AND pg_catalog.pg_table_is_visible(c.oid) AND c.relname = '#{self.class.escape(table.to_s)}'")
|
215
|
+
return r && r.blank? ? nil : r.next
|
216
|
+
end
|
217
|
+
|
218
|
+
private
|
219
|
+
|
220
|
+
def database_does_not_exist_exception?(ex)
|
221
|
+
ex.message =~ /database .* does not exist/i
|
222
|
+
end
|
223
|
+
|
224
|
+
def table_already_exists_exception?(ex)
|
225
|
+
ex.message =~ /already exists/
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#--
|
2
|
+
# Customize the standard Postgresql driver objects to make them
|
3
|
+
# more compatible with Og.
|
4
|
+
#++
|
5
|
+
|
6
|
+
class PGconn # :nodoc: all
|
7
|
+
|
8
|
+
# Redirect notices from backend to the logger instead of stderr
|
9
|
+
|
10
|
+
alias_method :initialize_pre_on_notice, :initialize
|
11
|
+
def initialize(*args)
|
12
|
+
initialize_pre_on_notice(*args)
|
13
|
+
|
14
|
+
on_notice { |message| Logger.info message }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Check for older versions of ruby-postgres and postgres-pr
|
18
|
+
|
19
|
+
unless PGconn.instance_methods.include?('server_version')
|
20
|
+
|
21
|
+
# call-seq:
|
22
|
+
# conn.server_version -> Integer
|
23
|
+
#
|
24
|
+
# The number is formed by converting the major, minor, and revision numbers
|
25
|
+
# into two-decimal-digit numbers and appending them together. For example,
|
26
|
+
# version 7.4.2 will be returned as 70402, and version 8.1 will be returned
|
27
|
+
# as 80100 (leading zeroes are not shown). Zero is returned if the
|
28
|
+
# connection is bad.
|
29
|
+
# See also parse_version.
|
30
|
+
|
31
|
+
def server_version
|
32
|
+
@server_version ||= parse_version(exec('SHOW server_version').first_value)
|
33
|
+
rescue PGError => e
|
34
|
+
return 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_version(version)
|
38
|
+
version.split('.').map{|v| v.rjust(2,'0') }.join.to_i
|
39
|
+
end
|
40
|
+
private :parse_version
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
class PGresult # :nodoc: all
|
48
|
+
def blank?
|
49
|
+
0 == num_tuples
|
50
|
+
end
|
51
|
+
|
52
|
+
def next
|
53
|
+
@row_idx ||= -1
|
54
|
+
result[@row_idx += 1]
|
55
|
+
end
|
56
|
+
|
57
|
+
def each_row
|
58
|
+
result.each_with_index do |row, idx|
|
59
|
+
yield(row, idx)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def first_value
|
64
|
+
val = getvalue(0, 0)
|
65
|
+
clear
|
66
|
+
return val
|
67
|
+
end
|
68
|
+
|
69
|
+
alias_method :close, :clear
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
class PGError # :nodoc: all
|
74
|
+
attr_accessor :og_info
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
# Compatibility layer for postgres-pr
|
79
|
+
|
80
|
+
if defined?(PostgresPR)
|
81
|
+
|
82
|
+
class PGconn # :nodoc: all
|
83
|
+
alias_method :query_pre_resque, :query
|
84
|
+
alias_method :exec_old, :exec
|
85
|
+
|
86
|
+
def query(*args)
|
87
|
+
exec_old(*args)
|
88
|
+
# postgres-pr raises NoMethodError when querying if no conn is available
|
89
|
+
rescue RuntimeError, NoMethodError => e
|
90
|
+
raise PGError, e.message
|
91
|
+
end
|
92
|
+
|
93
|
+
alias_method :exec, :query
|
94
|
+
|
95
|
+
class << self
|
96
|
+
alias_method :new_pre_resque, :new
|
97
|
+
|
98
|
+
def new(*args)
|
99
|
+
new_pre_resque(*args)
|
100
|
+
rescue RuntimeError => e
|
101
|
+
raise PGError, e.message
|
102
|
+
end
|
103
|
+
|
104
|
+
alias_method :connect, :new
|
105
|
+
end
|
106
|
+
|
107
|
+
def on_notice(&block)
|
108
|
+
@notice_processor = block
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
class PGError # :nodoc: all
|
114
|
+
alias error message
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Helper for mysql scripts.
|
2
|
+
#
|
3
|
+
# === Example
|
4
|
+
#
|
5
|
+
# mysql '-u root -p', %{
|
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 psql(opts, stream)
|
12
|
+
IO.popen("psql #{opts}", 'w') do |io|
|
13
|
+
io.puts stream
|
14
|
+
end
|
15
|
+
end
|