og 0.5.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/AUTHORS +19 -0
- data/LICENSE +32 -0
- data/README.og +104 -0
- data/RELEASES.og +23 -0
- data/examples/og/README +4 -0
- data/examples/og/run.rb +251 -0
- data/lib/glue.rb +52 -0
- data/lib/glue/array.rb +84 -0
- data/lib/glue/cache.rb +140 -0
- data/lib/glue/hash.rb +143 -0
- data/lib/glue/inflector.rb +91 -0
- data/lib/glue/logger.rb +51 -0
- data/lib/glue/macro.rb +56 -0
- data/lib/glue/mixins.rb +45 -0
- data/lib/glue/number.rb +30 -0
- data/lib/glue/pool.rb +63 -0
- data/lib/glue/property.rb +301 -0
- data/lib/glue/string.rb +224 -0
- data/lib/glue/time.rb +93 -0
- data/lib/og.rb +411 -0
- data/lib/og/backend.rb +258 -0
- data/lib/og/backends/mysql.rb +360 -0
- data/lib/og/backends/psql.rb +359 -0
- data/lib/og/connection.rb +265 -0
- data/lib/og/meta.rb +139 -0
- data/lib/og/version.rb +8 -0
- data/test/tc_og.rb +179 -0
- metadata +71 -0
data/lib/glue/string.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
# code:
|
2
|
+
# * George Moschovitis <gm@navel.gr>
|
3
|
+
# * Anastasios Koutoumanos <ak@navel.gr>
|
4
|
+
# * Elias Karakoulakis <ekarak@ktismata.com>
|
5
|
+
#
|
6
|
+
# (c) 2004 Navel, all rights reserved.
|
7
|
+
# $Id: string.rb 165 2004-11-18 12:04:04Z gmosx $
|
8
|
+
|
9
|
+
require "uri"
|
10
|
+
|
11
|
+
module G;
|
12
|
+
|
13
|
+
# = StringUtils
|
14
|
+
#
|
15
|
+
# General string utilities collection.
|
16
|
+
#
|
17
|
+
# === Design:
|
18
|
+
#
|
19
|
+
# Implement as a module to avoid class polution. You can
|
20
|
+
# still Ruby's advanced features to include the module in your
|
21
|
+
# class. Passing the object to act upon allows to check for nil,
|
22
|
+
# which isn't possible if you use self.
|
23
|
+
#
|
24
|
+
# === TODO:
|
25
|
+
#
|
26
|
+
# - implement a method that returns easy to remember
|
27
|
+
# pseudo-random strings
|
28
|
+
# - add aliases for those methods in Kernel.
|
29
|
+
#
|
30
|
+
module StringUtils
|
31
|
+
|
32
|
+
@@map_to_greeklish = {
|
33
|
+
"�" => "a", "�" => "A", "�" => "a", "�" => "A",
|
34
|
+
"�" => "b", "�" => "B",
|
35
|
+
"�" => "g", "�" => "G",
|
36
|
+
"�" => "d", "�" => "D",
|
37
|
+
"�" => "e", "�" => "E", "�" => "e", "�" => "E",
|
38
|
+
"�" => "z", "�" => "Z",
|
39
|
+
"�" => "h", "�" => "H", "�" => "h", "�" => "H",
|
40
|
+
"�" => "8", "�" => "8",
|
41
|
+
"�" => "i", "�" => "I", "�" => "i", "�" => "I",
|
42
|
+
"�" => "k", "�" => "K",
|
43
|
+
"�" => "l", "�" => "L",
|
44
|
+
"�" => "m", "�" => "M",
|
45
|
+
"�" => "n", "�" => "N",
|
46
|
+
"�" => "3", "�" => "3",
|
47
|
+
"�" => "o", "�" => "O", "�" => "o", "�" => "O",
|
48
|
+
"�" => "p", "�" => "P",
|
49
|
+
"�" => "r", "�" => "R",
|
50
|
+
"�" => "s", "�" => "s", "�" => "S",
|
51
|
+
"�" => "t", "�" => "T",
|
52
|
+
"�" => "y", "�" => "Y", "�" => "y", "�" => "Y",
|
53
|
+
"�" => "f", "�" => "F",
|
54
|
+
"�" => "x", "�" => "X",
|
55
|
+
"�" => "ps","�" => "PS",
|
56
|
+
"�" => "w", "�" => "W", "�" => "w", "�"=>"W"
|
57
|
+
}
|
58
|
+
|
59
|
+
# Convert the input string to greeklish.
|
60
|
+
#--
|
61
|
+
# gmosx, TODO: remove from public distribution
|
62
|
+
#++
|
63
|
+
#
|
64
|
+
def self.to_greeklish(input)
|
65
|
+
return nil unless input
|
66
|
+
output = ""
|
67
|
+
# gmosx: also parse new lines
|
68
|
+
input.scan(/./m) { |w|
|
69
|
+
c = @@map_to_greeklish[w]
|
70
|
+
output << (c.nil?? w: c)
|
71
|
+
}
|
72
|
+
return output
|
73
|
+
end
|
74
|
+
|
75
|
+
# Move this in String class?
|
76
|
+
#
|
77
|
+
# Tests a string for a valid value (non nil, not empty)
|
78
|
+
#
|
79
|
+
def self.valid?(string)
|
80
|
+
return (not ((nil == string) or (string.empty?)))
|
81
|
+
end
|
82
|
+
|
83
|
+
# returns short abstract of long strings (first 'count'
|
84
|
+
# characters, chopped at the nearest word, appended by '...')
|
85
|
+
# force_cutoff: break forcibly at 'count' chars. Does not accept
|
86
|
+
# count < 2.
|
87
|
+
#
|
88
|
+
def self.head(string, count = 128, force_cutoff = false, ellipsis="...")
|
89
|
+
return nil unless string
|
90
|
+
return nil if count < 2
|
91
|
+
|
92
|
+
if string.size > count
|
93
|
+
cut_at = force_cutoff ? count : (string.index(' ', count-1) || count)
|
94
|
+
xstring = string.slice(0, cut_at)
|
95
|
+
return xstring.chomp(" ") + ellipsis
|
96
|
+
else
|
97
|
+
return string
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Apply a set of rules (regular expression matches) to the
|
102
|
+
# string
|
103
|
+
#
|
104
|
+
# === Requirements:
|
105
|
+
# - the rules must be applied in order! So we cannot use a
|
106
|
+
# hash because the ordering is not guaranteed! we use an
|
107
|
+
# array instead.
|
108
|
+
#
|
109
|
+
# === Input:
|
110
|
+
# the string to rewrite
|
111
|
+
# the array containing rule-pairs (match, rewrite)
|
112
|
+
#
|
113
|
+
# === Output:
|
114
|
+
# the rewritten string
|
115
|
+
|
116
|
+
MATCH = 0
|
117
|
+
REWRITE = 1
|
118
|
+
|
119
|
+
def self.rewrite(string, rules)
|
120
|
+
return nil unless string
|
121
|
+
|
122
|
+
# gmosx: helps to find bugs
|
123
|
+
raise ArgumentError.new("the rules parameter is nil") unless rules
|
124
|
+
|
125
|
+
rewritten_string = string.dup
|
126
|
+
|
127
|
+
for rule in rules
|
128
|
+
rewritten_string.gsub!(rule[MATCH], rule[REWRITE])
|
129
|
+
end
|
130
|
+
|
131
|
+
return (rewritten_string or string)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Enforces a maximum width of a string inside an
|
135
|
+
# html container. If the string exceeds this maximum width
|
136
|
+
# the string gets wraped.
|
137
|
+
#
|
138
|
+
# Not really useful, better use the CSS overflow: hidden
|
139
|
+
# functionality.
|
140
|
+
#
|
141
|
+
# === Input:
|
142
|
+
# the string to be wrapped
|
143
|
+
# the enforced width
|
144
|
+
# the separator used for wrapping
|
145
|
+
#
|
146
|
+
# === Output:
|
147
|
+
# the wrapped string
|
148
|
+
#
|
149
|
+
# === Example:
|
150
|
+
# text = "1111111111111111111111111111111111111111111"
|
151
|
+
# text = wrap(text, 10, " ")
|
152
|
+
# p text # => "1111111111 1111111111 1111111111"
|
153
|
+
#
|
154
|
+
# See the test cases to better understand the behaviour!
|
155
|
+
#
|
156
|
+
def self.wrap(string, width = 20, separator = " ")
|
157
|
+
return nil unless string
|
158
|
+
|
159
|
+
re = /([^#{separator}]{1,#{width}})/
|
160
|
+
wrapped_string = string.scan(re).join(separator)
|
161
|
+
|
162
|
+
return wrapped_string
|
163
|
+
end
|
164
|
+
|
165
|
+
# Replace dangerours chars in filenames
|
166
|
+
#
|
167
|
+
def self.rationalize_filename(filename)
|
168
|
+
return nil unless filename
|
169
|
+
# gmosx: rationalize a copy!!! (add unit test)
|
170
|
+
xfilename = filename.dup()
|
171
|
+
# gmosx: replace some dangerous chars!
|
172
|
+
xfilename.gsub!(/ /, "-")
|
173
|
+
xfilename.gsub!(/!/, "")
|
174
|
+
xfilename.gsub!(/'/, "")
|
175
|
+
xfilename.gsub!(/\(/, "")
|
176
|
+
xfilename.gsub!(/\)/, "")
|
177
|
+
xfilename = self.to_greeklish(xfilename)
|
178
|
+
return xfilename
|
179
|
+
end
|
180
|
+
|
181
|
+
# Returns a random string. one possible use is
|
182
|
+
# password initialization.
|
183
|
+
#
|
184
|
+
# === Input:
|
185
|
+
# the maximum length of the string
|
186
|
+
#
|
187
|
+
# === Output:
|
188
|
+
# the random string
|
189
|
+
#
|
190
|
+
def self.random(max_length = 8, char_re = /[\w\d]/)
|
191
|
+
# gmosx: this is a nice example of input parameter checking.
|
192
|
+
# this is NOT a real time called method so we can add this
|
193
|
+
# check. Congrats to the author.
|
194
|
+
raise ArgumentError.new("char_re must be a regular expression!") unless char_re.is_a?(Regexp)
|
195
|
+
|
196
|
+
string = ""
|
197
|
+
|
198
|
+
while string.length < max_length
|
199
|
+
ch = rand(255).chr
|
200
|
+
string << ch if ch =~ char_re
|
201
|
+
end
|
202
|
+
|
203
|
+
return string
|
204
|
+
end
|
205
|
+
|
206
|
+
# Screen an IP address
|
207
|
+
#--
|
208
|
+
# gmosx: copied this method from n1, check how it works!
|
209
|
+
# probably deprecate?
|
210
|
+
#++
|
211
|
+
def self.screen_ip_address(address)
|
212
|
+
if address
|
213
|
+
return address.split(',').collect { |hostip|
|
214
|
+
hostip.gsub(/\.[^\.]*$/, ".*")
|
215
|
+
}.join(', ')
|
216
|
+
else
|
217
|
+
return "*.*.*.*"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end # module
|
224
|
+
|
data/lib/glue/time.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# code:
|
2
|
+
# * George Moschovitis <gm@navel.gr>
|
3
|
+
#
|
4
|
+
# (c) 2004 Navel, all rights reserved.
|
5
|
+
# $Id: time.rb 167 2004-11-23 14:03:10Z gmosx $
|
6
|
+
|
7
|
+
require "time.rb"
|
8
|
+
|
9
|
+
module G;
|
10
|
+
|
11
|
+
# = Time
|
12
|
+
#
|
13
|
+
# General time utilities collection
|
14
|
+
#
|
15
|
+
# === Design:
|
16
|
+
#
|
17
|
+
# Implement as a module to avoid class polution. You can
|
18
|
+
# still Ruby's advanced features to include the module in your
|
19
|
+
# class. Passing the object to act upon allows to check for nil,
|
20
|
+
# which isn't possible if you use self.
|
21
|
+
#
|
22
|
+
# === TODO:
|
23
|
+
#
|
24
|
+
# - SOS: add test units.
|
25
|
+
# - add aliases for those methods in Kernel ?
|
26
|
+
#
|
27
|
+
module TimeUtils
|
28
|
+
|
29
|
+
NOW = Time.now
|
30
|
+
NEVER = Time.mktime(2038)
|
31
|
+
ZERO = Time.mktime(1972)
|
32
|
+
|
33
|
+
# Convert the time to a nice String representation.
|
34
|
+
#
|
35
|
+
def self.date_time(time)
|
36
|
+
return nil unless time
|
37
|
+
return time.strftime("%d-%m-%Y %H:%M")
|
38
|
+
end
|
39
|
+
|
40
|
+
# this method calculates the days extrema given two time objects.
|
41
|
+
# start time is the given time1 at 00:00:00
|
42
|
+
# end time is the given time2 at 23:59:59:999
|
43
|
+
#
|
44
|
+
# Input:
|
45
|
+
# - the two times (if only time1 is provided then you get an extrema
|
46
|
+
# of exactly one day extrema.
|
47
|
+
#
|
48
|
+
# Output
|
49
|
+
# - the time range. you can get the start/end times using
|
50
|
+
# range methods.
|
51
|
+
#
|
52
|
+
def self.days_extrema(time1, time2=nil)
|
53
|
+
time2 = time1 if (not time2.valid? Time)
|
54
|
+
time2 = NEVER if (time2 <= time1)
|
55
|
+
start_time = Time.self.start_of_day(time1)
|
56
|
+
end_time = self.end_of_day(time2)
|
57
|
+
return (start_time..end_time)
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# set time to start of day
|
62
|
+
#
|
63
|
+
def self.start_of_day(time)
|
64
|
+
return Time.mktime(time.year, time.month, time.day, 0, 0, 0, 0)
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# set time to end of day
|
69
|
+
#
|
70
|
+
def self.end_of_day(time)
|
71
|
+
return Time.mktime(time.year, time.month, time.day, 23, 59, 59, 999)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# returns true only if day of time is included in the
|
76
|
+
# range (stime..etime). Only year days are checked.
|
77
|
+
#
|
78
|
+
def self.time_in_day_range(time, stime=ZERO, etime=NEVER)
|
79
|
+
if (etime <= stime)
|
80
|
+
$log.debug "Invalid end time (#{etime} < #{stime})" if $DBG
|
81
|
+
etime = NEVER
|
82
|
+
end
|
83
|
+
|
84
|
+
stime = start_of_day(stime)
|
85
|
+
etime = end_of_day(etime)
|
86
|
+
|
87
|
+
return (stime..etime).include?(time)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end # module
|
93
|
+
|
data/lib/og.rb
ADDED
@@ -0,0 +1,411 @@
|
|
1
|
+
# code:
|
2
|
+
# * George Moschovitis <gm@navel.gr>
|
3
|
+
#
|
4
|
+
# (c) 2004 Navel, all rights reserved.
|
5
|
+
# $Id: og.rb 167 2004-11-23 14:03:10Z gmosx $
|
6
|
+
|
7
|
+
require "glue/property"
|
8
|
+
require "glue/array"
|
9
|
+
require "glue/hash"
|
10
|
+
require "glue/time"
|
11
|
+
require "glue/pool"
|
12
|
+
|
13
|
+
require "og/meta"
|
14
|
+
|
15
|
+
# = Og
|
16
|
+
#
|
17
|
+
# Og (ObjectGraph) is an efficient, yet simple Object-Relational
|
18
|
+
# mapping library.
|
19
|
+
#
|
20
|
+
# == Features
|
21
|
+
#
|
22
|
+
# The library provides the following features:
|
23
|
+
#
|
24
|
+
# + Object-Relational mapping.
|
25
|
+
# + Absolutely no configuration files.
|
26
|
+
# + Multiple backends (PostgreSQL, MySQL).
|
27
|
+
# + ActiveRecord-style meta language and db aware methods.
|
28
|
+
# + Deserialize to Ruby Objects or ResultSets.
|
29
|
+
# + Deserialize sql join queries to Ruby Objects.
|
30
|
+
# + Serialize arbitrary ruby object graphs through YAML.
|
31
|
+
# + Connection pooling.
|
32
|
+
# + Thread safety.
|
33
|
+
# + SQL transactions.
|
34
|
+
# + Lifecycle callbacks.
|
35
|
+
# + Transparent support for cascading deletes for all backends.
|
36
|
+
# + Hierarchical structures (preorder traversal, materialized paths)
|
37
|
+
# + Works safely as part of distributed application.
|
38
|
+
# + Simple implementation < 2k lines of code.
|
39
|
+
#
|
40
|
+
# === Meta language
|
41
|
+
#
|
42
|
+
# primary_key :pid
|
43
|
+
# prop_accessor Fixnum, :pid, :sql => "smallint DEFAULT 1"
|
44
|
+
# has_many Child, :children
|
45
|
+
# many_to_many Role, :roles
|
46
|
+
# sql_index :pid
|
47
|
+
#
|
48
|
+
# === Design
|
49
|
+
#
|
50
|
+
# Keep the main classes backend agnostic.
|
51
|
+
#
|
52
|
+
# Try to make the methods work with oids. Do NOT implement descendants
|
53
|
+
# use a root id (rid).
|
54
|
+
#
|
55
|
+
# For class ids we use the name instead of a hash. Class ids are
|
56
|
+
# typically not used in querys, they are stored for completeness.
|
57
|
+
# If we store a hash we cannot reclaim the class thus invalidating
|
58
|
+
# the point. Instead of .name(), to_s() is used so the methods
|
59
|
+
# are more flexible (they accept class names too!!)
|
60
|
+
#
|
61
|
+
# Og allows the serialization of arbitrary Ruby objects. Just
|
62
|
+
# mark them as Object (or Array or Hash) in the prop_accessor
|
63
|
+
# and the engine will serialize a YAML dump of the object.
|
64
|
+
# Arbitrary object graphs are supported too.
|
65
|
+
#
|
66
|
+
# This is NOT a singleton, an application may access multiple
|
67
|
+
# databases.
|
68
|
+
#
|
69
|
+
# The $og.xxx methods are more flexible and allow you to use
|
70
|
+
# multiple databases for example.
|
71
|
+
#
|
72
|
+
# === Managed Objects Lifecycle Callbacks
|
73
|
+
#
|
74
|
+
# * og_pre_insert
|
75
|
+
# * og_post_insert
|
76
|
+
# * og_pre_update
|
77
|
+
# * og_post_update
|
78
|
+
# * og_pre_insert_update
|
79
|
+
# * og_post_insert_update
|
80
|
+
# * self.og_pre_delete
|
81
|
+
# * validate
|
82
|
+
#
|
83
|
+
# A class level callback is used for delete because typically you call
|
84
|
+
# delete with an oid and not an object to avoid a deserialization.
|
85
|
+
#
|
86
|
+
# === Future
|
87
|
+
#
|
88
|
+
# * Support prepared statements (pgsql)
|
89
|
+
# * Support stored procedures (pgsql)
|
90
|
+
# * Support caching.
|
91
|
+
# * Deserialize to OpenStruct.
|
92
|
+
# * Better documentation.
|
93
|
+
#
|
94
|
+
module Og
|
95
|
+
|
96
|
+
# If true, only allow reading from the database. Usefull
|
97
|
+
# for maintainance.
|
98
|
+
#
|
99
|
+
$og_read_only_mode = false
|
100
|
+
|
101
|
+
# If true, the library automatically 'enchants' managed classes.
|
102
|
+
# In enchant mode, special db aware methods are added to
|
103
|
+
# managed classes and instances.
|
104
|
+
#
|
105
|
+
$og_enchant_managed_classes = true
|
106
|
+
|
107
|
+
# If true, use Ruby's advanced introspection capabilities to
|
108
|
+
# automatically manage classes tha define properties.
|
109
|
+
$og_auto_manage_classes = true
|
110
|
+
|
111
|
+
# = Unmanageable
|
112
|
+
#
|
113
|
+
# Marker module. If included this in a class, the Og automanager
|
114
|
+
# ignores this class.
|
115
|
+
#
|
116
|
+
module Unmanageable; end
|
117
|
+
|
118
|
+
# = Database
|
119
|
+
#
|
120
|
+
# Encapsulates an Og Database.
|
121
|
+
#
|
122
|
+
class Database
|
123
|
+
|
124
|
+
# Managed class metadata
|
125
|
+
#
|
126
|
+
class ManagedClassMeta
|
127
|
+
# The managed class.
|
128
|
+
attr_accessor :klass
|
129
|
+
|
130
|
+
# A mapping of the database fields to the object properties.
|
131
|
+
attr_accessor :field_index
|
132
|
+
|
133
|
+
def initialize(klass = nil)
|
134
|
+
@klass = klass
|
135
|
+
@field_index = {}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# hash of configuration options.
|
140
|
+
attr_accessor :config
|
141
|
+
|
142
|
+
# Pool of connections to the backend.
|
143
|
+
attr_accessor :connection_pool
|
144
|
+
|
145
|
+
# Managed classes.
|
146
|
+
attr_accessor :managed_classes
|
147
|
+
|
148
|
+
# Initialize the database interface.
|
149
|
+
#
|
150
|
+
def initialize(config)
|
151
|
+
@config = config
|
152
|
+
|
153
|
+
# populate with default options if needed.
|
154
|
+
@config[:connection_count] ||= 1
|
155
|
+
|
156
|
+
# require the backend.
|
157
|
+
backend = @config[:backend] || "psql"
|
158
|
+
require "og/backends/#{backend}"
|
159
|
+
eval %{ @config[:backend] = #{backend.capitalize}Backend }
|
160
|
+
|
161
|
+
@connection_pool = G::Pool.new
|
162
|
+
@managed_classes = G::SafeHash.new
|
163
|
+
|
164
|
+
$log.info "Connecting to database '#{@config[:database]}' using backend '#{backend}'."
|
165
|
+
|
166
|
+
@config[:connection_count].times do
|
167
|
+
@connection_pool << Og::Connection.new(self)
|
168
|
+
end
|
169
|
+
|
170
|
+
if $og_auto_manage_classes
|
171
|
+
# automatically manage classes with properties and metadata.
|
172
|
+
# gmosx: Any idea how to optimize this?
|
173
|
+
classes_to_manage = []
|
174
|
+
ObjectSpace.each_object(Class) do |c|
|
175
|
+
if c.respond_to?(:__props) and c.__props
|
176
|
+
classes_to_manage << c
|
177
|
+
end
|
178
|
+
end
|
179
|
+
$log.info "Og auto manages the following classes:"
|
180
|
+
$log.info "#{classes_to_manage.inspect}"
|
181
|
+
manage_classes(*classes_to_manage)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Shutdown the database interface.
|
186
|
+
#
|
187
|
+
def shutdown
|
188
|
+
for con in @connection_pool
|
189
|
+
con.close()
|
190
|
+
end
|
191
|
+
end
|
192
|
+
alias_method :close, :shutdown
|
193
|
+
|
194
|
+
# Get a connection from the pool to access the database.
|
195
|
+
# Stores the connection in a thread-local variable.
|
196
|
+
#
|
197
|
+
def get_connection
|
198
|
+
$log.debug "Get Og connection" if $DBG
|
199
|
+
|
200
|
+
thread = Thread.current
|
201
|
+
|
202
|
+
unless conn = thread[:og_conn]
|
203
|
+
conn = @connection_pool.pop()
|
204
|
+
thread[:og_conn] = conn
|
205
|
+
end
|
206
|
+
|
207
|
+
return conn
|
208
|
+
end
|
209
|
+
|
210
|
+
# Restore an unused connection to the pool.
|
211
|
+
#
|
212
|
+
def put_connection
|
213
|
+
$log.debug "Put Og connection" if $DBG
|
214
|
+
|
215
|
+
thread = Thread.current
|
216
|
+
|
217
|
+
if conn = thread[:og_conn]
|
218
|
+
thread[:og_conn] = nil
|
219
|
+
return @connection_pool.push(conn)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Utility method, automatically restores a connection to the pool.
|
224
|
+
#
|
225
|
+
def connect(deserialize = nil, &block)
|
226
|
+
result = nil
|
227
|
+
|
228
|
+
begin
|
229
|
+
conn = get_connection()
|
230
|
+
conn.deserialize = deserialize unless deserialize.nil?
|
231
|
+
|
232
|
+
result = yield(conn)
|
233
|
+
|
234
|
+
conn.deserialize = true
|
235
|
+
ensure
|
236
|
+
put_connection()
|
237
|
+
end
|
238
|
+
|
239
|
+
return result
|
240
|
+
end
|
241
|
+
alias_method :open, :connect
|
242
|
+
|
243
|
+
|
244
|
+
# Register a standard Ruby class as managed.
|
245
|
+
#
|
246
|
+
def manage(klass)
|
247
|
+
return if managed?(klass) or klass.ancestors.include?(Og::Unmanageable)
|
248
|
+
|
249
|
+
@managed_classes[klass] = ManagedClassMeta.new(klass)
|
250
|
+
|
251
|
+
# Add standard og methods to the class.
|
252
|
+
convert(klass)
|
253
|
+
|
254
|
+
# Add helper methods to the class.
|
255
|
+
enchant(klass) if $og_enchant_managed_classes
|
256
|
+
end
|
257
|
+
|
258
|
+
# Helper method to set multiple managed classes.
|
259
|
+
#
|
260
|
+
def manage_classes(*klasses)
|
261
|
+
for klass in klasses
|
262
|
+
manage(klass)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# Stop managing a Ruby class
|
267
|
+
#
|
268
|
+
def unmanage(klass)
|
269
|
+
@managed_classes.delete(klass)
|
270
|
+
end
|
271
|
+
|
272
|
+
# Is this class managed?
|
273
|
+
#
|
274
|
+
def managed?(klass)
|
275
|
+
return @managed_classes.include?(klass)
|
276
|
+
end
|
277
|
+
|
278
|
+
# Add standard og functionality to the class
|
279
|
+
#
|
280
|
+
def convert(klass)
|
281
|
+
# gmosx: this check is needed to allow the developer to customize
|
282
|
+
# the sql generated for oid
|
283
|
+
Og::Utils.eval_og_oid(klass) unless klass.instance_methods.include?(:oid)
|
284
|
+
|
285
|
+
klass.class_eval %{
|
286
|
+
inherit_meta(superclass) if superclass
|
287
|
+
|
288
|
+
DBTABLE = "#{Og::Utils.table(klass)}"
|
289
|
+
DBSEQ = "#{Og::Utils.table(klass)}_oids_seq"
|
290
|
+
|
291
|
+
def to_i()
|
292
|
+
@oid
|
293
|
+
end
|
294
|
+
}
|
295
|
+
|
296
|
+
# Create the schema for this class if not available.
|
297
|
+
create_table(klass)
|
298
|
+
|
299
|
+
# Precompile some code that gets executed all the time.
|
300
|
+
# Deletion code is not precompiled, because it is not used
|
301
|
+
# as frequently.
|
302
|
+
Og::Utils.eval_og_insert(klass)
|
303
|
+
Og::Utils.eval_og_update(klass)
|
304
|
+
Og::Utils.eval_og_deserialize(klass, self)
|
305
|
+
end
|
306
|
+
|
307
|
+
# Enchant a managed class. Add useful DB related methods to the
|
308
|
+
# class and its instances.
|
309
|
+
#
|
310
|
+
def enchant(klass)
|
311
|
+
klass.class_eval %{
|
312
|
+
def self.save(obj)
|
313
|
+
$og << obj
|
314
|
+
end
|
315
|
+
|
316
|
+
def self.load(oid_or_name)
|
317
|
+
$og.load(oid_or_name, #{klass})
|
318
|
+
end
|
319
|
+
|
320
|
+
def self.[](oid_or_name)
|
321
|
+
$og.load(oid_or_name, #{klass})
|
322
|
+
end
|
323
|
+
|
324
|
+
def self.load_all(extra_sql = nil)
|
325
|
+
$og.load_all(#{klass}, extra_sql)
|
326
|
+
end
|
327
|
+
|
328
|
+
def self.all(extra_sql = nil)
|
329
|
+
$og.load_all(#{klass}, extra_sql)
|
330
|
+
end
|
331
|
+
|
332
|
+
def self.select(sql)
|
333
|
+
$og.select(sql, #{klass})
|
334
|
+
end
|
335
|
+
|
336
|
+
def self.select_one(sql)
|
337
|
+
$og.select_one(sql, #{klass})
|
338
|
+
end
|
339
|
+
|
340
|
+
def self.delete(obj_or_oid)
|
341
|
+
$og.delete(obj_or_oid, #{klass})
|
342
|
+
end
|
343
|
+
|
344
|
+
def save
|
345
|
+
$og << self
|
346
|
+
end
|
347
|
+
alias_method :save!, :save
|
348
|
+
|
349
|
+
def update_properties(updatesql)
|
350
|
+
$og.pupdate(updatesql, self.oid, #{klass})
|
351
|
+
end
|
352
|
+
alias_method :pupdate!, :update_properties
|
353
|
+
|
354
|
+
def delete!
|
355
|
+
$og.delete(@oid, #{klass})
|
356
|
+
end
|
357
|
+
}
|
358
|
+
end
|
359
|
+
|
360
|
+
# Automatically wrap connection methods.
|
361
|
+
#
|
362
|
+
def self.wrap_method(method, args)
|
363
|
+
args = args.split(/,/)
|
364
|
+
class_eval %{
|
365
|
+
def #{method}(#{args.join(", ")})
|
366
|
+
thread = Thread.current
|
367
|
+
|
368
|
+
unless conn = thread[:og_conn]
|
369
|
+
conn = @connection_pool.pop()
|
370
|
+
thread[:og_conn] = conn
|
371
|
+
end
|
372
|
+
|
373
|
+
return conn.#{method}(#{args.collect {|a| a.split(/=/)[0]}.join(", ")})
|
374
|
+
end
|
375
|
+
}
|
376
|
+
end
|
377
|
+
|
378
|
+
wrap_method :create_table, "klass"
|
379
|
+
wrap_method :drop_table, "klass"
|
380
|
+
wrap_method :save, "obj"; alias_method :<<, :save; alias_method :put, :save
|
381
|
+
wrap_method :insert, "obj"
|
382
|
+
wrap_method :update, "obj"
|
383
|
+
wrap_method :update_properties, "update_sql, obj_or_oid, klass = nil"
|
384
|
+
wrap_method :pupdate, "update_sql, obj_or_oid, klass = nil"
|
385
|
+
wrap_method :load, "oid, klass"; alias_method :get, :load
|
386
|
+
wrap_method :load_by_oid, "oid, klass"
|
387
|
+
wrap_method :load_by_name, "name, klass"
|
388
|
+
wrap_method :load_all, "klass, extrasql = nil"
|
389
|
+
wrap_method :select, "sql, klass"
|
390
|
+
wrap_method :select_one, "sql, klass"
|
391
|
+
wrap_method :count, "sql, klass = nil"
|
392
|
+
wrap_method :delete, "obj_or_oid, klass = nil"
|
393
|
+
wrap_method :query, "sql"
|
394
|
+
wrap_method :exec, "sql"
|
395
|
+
|
396
|
+
def self.create_db!(config)
|
397
|
+
get_connection().db.create_db(config[:database], config[:user],
|
398
|
+
config[:password])
|
399
|
+
end
|
400
|
+
|
401
|
+
def self.drop_db!(config)
|
402
|
+
backend = config[:backend] || "psql"
|
403
|
+
require "og/backends/#{backend}"
|
404
|
+
eval %{
|
405
|
+
#{backend.capitalize}Backend.drop_db(config[:database], config[:user],
|
406
|
+
config[:password])
|
407
|
+
}
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
end # module
|