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/logger.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# = Logger
|
2
|
+
#
|
3
|
+
# A simple wrapper arround the Ruby logger. Mainly for compatibility
|
4
|
+
# purposes.
|
5
|
+
#
|
6
|
+
# code:
|
7
|
+
# * George Moschovitis <gm@navel.gr>
|
8
|
+
#
|
9
|
+
# (c) 2004 Navel, all rights reserved.
|
10
|
+
# $Id: logger.rb 161 2004-11-18 10:51:51Z gmosx $
|
11
|
+
|
12
|
+
require "logger"
|
13
|
+
|
14
|
+
# = Logger
|
15
|
+
#
|
16
|
+
# A simple wrapper arround the Ruby logger. Mainly for compatibility
|
17
|
+
# purposes.
|
18
|
+
#
|
19
|
+
class Logger
|
20
|
+
alias_method :devel, :debug
|
21
|
+
alias_method :fine, :debug
|
22
|
+
|
23
|
+
def detach
|
24
|
+
end
|
25
|
+
|
26
|
+
# Used for debuging, remove this in release code.
|
27
|
+
#
|
28
|
+
def d(str)
|
29
|
+
self << "#{str}\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Inspect an object. Used for debugging, remove this in release
|
33
|
+
# code.
|
34
|
+
#
|
35
|
+
def i(obj)
|
36
|
+
self << "Inspect #{obj.inspect()}\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# the default Ruby logger has a hardwired silly format.
|
42
|
+
# we use some Ruby magic to fix it!
|
43
|
+
remove_const "Format"
|
44
|
+
|
45
|
+
Format = "%5s: %s\n"
|
46
|
+
|
47
|
+
def format_message(severity, timestamp, msg, progname)
|
48
|
+
Format % [severity, msg]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
data/lib/glue/macro.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# = Macros
|
2
|
+
#
|
3
|
+
# A powerfull macro implementation for Ruby. Allows definition
|
4
|
+
# of new macro.
|
5
|
+
#
|
6
|
+
# EXPERIMENTAL, not fully working yet
|
7
|
+
#
|
8
|
+
# code:
|
9
|
+
# George Moschovitis <gm@navel.gr>
|
10
|
+
#
|
11
|
+
# (c) 2004 Navel, all rights reserved.
|
12
|
+
# $Id: macro.rb 165 2004-11-18 12:04:04Z gmosx $
|
13
|
+
|
14
|
+
$__macros__ = {}
|
15
|
+
$__required__ = {}
|
16
|
+
|
17
|
+
module Kernel
|
18
|
+
|
19
|
+
alias_method :old_require, :require
|
20
|
+
def require(path)
|
21
|
+
return if $__required__[path]
|
22
|
+
|
23
|
+
source = File.read(path)
|
24
|
+
|
25
|
+
# parse macro
|
26
|
+
source.gsub!(/defmacro\s*\/(.*?)\/\s(.*?)endmacro/m) {
|
27
|
+
$__macros__[Regexp.new($1)] = $2 ; ""
|
28
|
+
}
|
29
|
+
|
30
|
+
# expand macros
|
31
|
+
$__macros__.each { |match, replace|
|
32
|
+
source.gsub!(match, replace)
|
33
|
+
}
|
34
|
+
|
35
|
+
$__required__[path] = true
|
36
|
+
|
37
|
+
eval(source)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
require "nitro/test1.rb"
|
43
|
+
require "nitro/test2.rb"
|
44
|
+
|
45
|
+
__END__
|
46
|
+
|
47
|
+
Examples:
|
48
|
+
|
49
|
+
defmacro /my_macro\((.*)\)/
|
50
|
+
begin
|
51
|
+
my_function(\1)
|
52
|
+
rescue => ex
|
53
|
+
puts ex
|
54
|
+
end
|
55
|
+
endmacro
|
56
|
+
|
data/lib/glue/mixins.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# = Mixins
|
2
|
+
#
|
3
|
+
# A collection of useful mixins. Use these to synthesize your
|
4
|
+
# entities.
|
5
|
+
#
|
6
|
+
# code:
|
7
|
+
# * George Moschovitis <gm@navel.gr>
|
8
|
+
#
|
9
|
+
# (c) 2004 Navel, all rights reserved.
|
10
|
+
# $Id$
|
11
|
+
|
12
|
+
module G;
|
13
|
+
|
14
|
+
# = Expirable
|
15
|
+
#
|
16
|
+
# Generic expiring functionality mixin.
|
17
|
+
#
|
18
|
+
module Expirable
|
19
|
+
attr_accessor :expires
|
20
|
+
|
21
|
+
# Set the expires timeout for this entry.
|
22
|
+
|
23
|
+
def expires_after(timeout = (60*60*24))
|
24
|
+
@expires = Time.now + timeout
|
25
|
+
end
|
26
|
+
|
27
|
+
# Set the expire timeout for this entry. The timeout happens
|
28
|
+
# after (base + rand(spread)) seconds.
|
29
|
+
|
30
|
+
def expires_spread(base, spread)
|
31
|
+
@expires = Time.now + base + rand(spread)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Is this entry expired?
|
35
|
+
|
36
|
+
def expired?
|
37
|
+
if @expires.nil? or (Time.now > @expires)
|
38
|
+
return true
|
39
|
+
else
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end # module
|
data/lib/glue/number.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# code:
|
2
|
+
# * George Moschovitis <gm@navel.gr>
|
3
|
+
#
|
4
|
+
# (c) 2004 Navel, all rights reserved.
|
5
|
+
# $Id: number.rb 167 2004-11-23 14:03:10Z gmosx $
|
6
|
+
|
7
|
+
module G;
|
8
|
+
|
9
|
+
# = NumberUtils
|
10
|
+
#
|
11
|
+
# === Design:
|
12
|
+
#
|
13
|
+
# Implement as a module to avoid class polution. You can
|
14
|
+
# still use Ruby's advanced features to include the module in your
|
15
|
+
# class. Passing the object to act upon allows to check for nil,
|
16
|
+
# which isn't possible if you use self.
|
17
|
+
#
|
18
|
+
module NumberUtils
|
19
|
+
|
20
|
+
# Returns the multiple ceil of a number
|
21
|
+
#
|
22
|
+
def self.ceil_multiple(num, multiple)
|
23
|
+
# gmosx: to_f is needed!s
|
24
|
+
# gmosx: IS THERE a more optimized way to do this?
|
25
|
+
return ((num.to_f/multiple).ceil*multiple)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end # module
|
data/lib/glue/pool.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# code:
|
2
|
+
# * George Moschovitis <gm@navel.gr>
|
3
|
+
#
|
4
|
+
# (c) 2004 Navel, all rights reserved.
|
5
|
+
# $Id: pool.rb 167 2004-11-23 14:03:10Z gmosx $
|
6
|
+
|
7
|
+
require "thread"
|
8
|
+
require "monitor"
|
9
|
+
|
10
|
+
module G
|
11
|
+
|
12
|
+
# = Pool
|
13
|
+
#
|
14
|
+
# Generalized object pool implementation. Implemented as a thread
|
15
|
+
# safe stack. Exclusive locking is needed both for push and pop.
|
16
|
+
#
|
17
|
+
# INVESTIGATE: Could use the SizedQueue/Queue.
|
18
|
+
#
|
19
|
+
class Pool < Array
|
20
|
+
include MonitorMixin
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
super
|
24
|
+
@cv = new_cond()
|
25
|
+
end
|
26
|
+
|
27
|
+
# Add, restore an object to the pool.
|
28
|
+
#
|
29
|
+
def push(obj)
|
30
|
+
synchronize do
|
31
|
+
super
|
32
|
+
@cv.signal()
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Obtain an object from the pool.
|
37
|
+
#
|
38
|
+
def pop
|
39
|
+
synchronize do
|
40
|
+
@cv.wait_while { empty? }
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Obtains an object, passes it to a block for processing
|
46
|
+
# and restores it to the pool.
|
47
|
+
def obtain
|
48
|
+
result = nil
|
49
|
+
|
50
|
+
begin
|
51
|
+
obj = pop()
|
52
|
+
|
53
|
+
result = yield(obj)
|
54
|
+
ensure
|
55
|
+
push(obj)
|
56
|
+
end
|
57
|
+
|
58
|
+
return result
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end # module
|
@@ -0,0 +1,301 @@
|
|
1
|
+
# code:
|
2
|
+
# * George Moschovitis <gm@navel.gr>
|
3
|
+
# design:
|
4
|
+
# * Anastastios Koutoumanos <ak@navel.gr>
|
5
|
+
# * Elias Karakoulakis <ekarak@ktismata.com>
|
6
|
+
#
|
7
|
+
# (c) 2004 Navel, all rights reserved.
|
8
|
+
# $Id: property.rb 167 2004-11-23 14:03:10Z gmosx $
|
9
|
+
|
10
|
+
require "glue/array"
|
11
|
+
require "glue/hash"
|
12
|
+
|
13
|
+
module G
|
14
|
+
|
15
|
+
# = Property
|
16
|
+
#
|
17
|
+
# Ruby attributes are typeless and generally this is good. Some times
|
18
|
+
# we need extra metadata though, for example in relational mapping,
|
19
|
+
# or web form population.
|
20
|
+
#
|
21
|
+
# Only Fixnums, Strings, Floats, Times, Booleans are converted.
|
22
|
+
#
|
23
|
+
# The default = methods do not force the types. A special
|
24
|
+
# __force_set method should be used instead.
|
25
|
+
#
|
26
|
+
#--
|
27
|
+
# TODO:
|
28
|
+
# Inject only the really needd methods into Module.
|
29
|
+
# Perhaps a sync is needed in evals (!!!!)
|
30
|
+
#++
|
31
|
+
#
|
32
|
+
class Property
|
33
|
+
# the symbol of the property
|
34
|
+
attr_accessor :symbol
|
35
|
+
# the string representation of the symbol
|
36
|
+
attr_accessor :name
|
37
|
+
# the class of the property
|
38
|
+
attr_accessor :klass
|
39
|
+
# additional metadata (like sql declaratio, sql index, etc)
|
40
|
+
attr_accessor :meta
|
41
|
+
|
42
|
+
def initialize(symbol, klass, meta = {})
|
43
|
+
@symbol, @klass = symbol, klass
|
44
|
+
@meta = meta
|
45
|
+
@name = @symbol.to_s()
|
46
|
+
end
|
47
|
+
|
48
|
+
def ==(other)
|
49
|
+
return @symbol == other.symbol
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end # module
|
54
|
+
|
55
|
+
class Module
|
56
|
+
|
57
|
+
# Define a property (== typed attribute)
|
58
|
+
# This works like Ruby's standard attr method, ie creates
|
59
|
+
# only one property.
|
60
|
+
#
|
61
|
+
# Use the prop_reader, prop_writer, prop_accessor methods
|
62
|
+
# for multiple properties.
|
63
|
+
#
|
64
|
+
# Examples:
|
65
|
+
# prop String, :name, :sql => "char(32), :sql_index => "name(32)"
|
66
|
+
# --> creates only writer.
|
67
|
+
# prop Fixnum, :oid, writer = true, :sql => "integer PRIMARY KEY"
|
68
|
+
# --> creates reader and writer.
|
69
|
+
#
|
70
|
+
def prop(*params)
|
71
|
+
meta = {}
|
72
|
+
klass = Object
|
73
|
+
|
74
|
+
for param in params
|
75
|
+
if param.is_a?(Class)
|
76
|
+
klass = param
|
77
|
+
elsif param.is_a?(Symbol)
|
78
|
+
symbol = param
|
79
|
+
elsif param.is_a?(TrueClass) or param.is_a?(TrueClass)
|
80
|
+
writer = param
|
81
|
+
elsif param.is_a?(Hash)
|
82
|
+
# the meta hash.
|
83
|
+
meta = param
|
84
|
+
else
|
85
|
+
raise "Error when defining property!"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
unless self.methods.include?("__props")
|
90
|
+
eval %{
|
91
|
+
# Properties
|
92
|
+
# An array is used to enforce order.
|
93
|
+
def __props
|
94
|
+
@__props
|
95
|
+
end
|
96
|
+
|
97
|
+
def __props=(props)
|
98
|
+
@__props = props
|
99
|
+
end
|
100
|
+
|
101
|
+
def __meta
|
102
|
+
@__meta
|
103
|
+
end
|
104
|
+
|
105
|
+
def __meta=(meta)
|
106
|
+
@__meta = meta
|
107
|
+
end
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
@__props = G::SafeArray.new() unless @__props
|
112
|
+
|
113
|
+
property = G::Property.new(symbol, klass, meta)
|
114
|
+
|
115
|
+
reader = meta[:reader] || true
|
116
|
+
writer = writer || meta[:writer] || false
|
117
|
+
|
118
|
+
__add_prop(property, reader, writer)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Helper method. Accepts a collection of symbols and generates
|
122
|
+
# properties. Only generates reader.
|
123
|
+
#
|
124
|
+
# Example:
|
125
|
+
# prop_reader String, :name, :title, :body, :sql => "char(32)"
|
126
|
+
#
|
127
|
+
def prop_reader(*params)
|
128
|
+
meta = {}
|
129
|
+
klass = Object
|
130
|
+
symbols = []
|
131
|
+
|
132
|
+
for param in params
|
133
|
+
if param.is_a?(Class)
|
134
|
+
klass = param
|
135
|
+
elsif param.is_a?(Symbol)
|
136
|
+
symbols << param
|
137
|
+
elsif param.is_a?(Hash)
|
138
|
+
# the meta hash.
|
139
|
+
meta = param
|
140
|
+
else
|
141
|
+
raise "Error when defining property!"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
meta[:reader] = true
|
146
|
+
meta[:writer] = false
|
147
|
+
|
148
|
+
for symbol in symbols
|
149
|
+
prop(klass, symbol, meta)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Helper method. Accepts a collection of symbols and generates
|
154
|
+
# properties. Only generates writer.
|
155
|
+
#
|
156
|
+
# Example:
|
157
|
+
# prop_writer String, :name, :title, :body, :sql => "char(32)"
|
158
|
+
#
|
159
|
+
def prop_writer(*params)
|
160
|
+
meta = {}
|
161
|
+
klass = Object
|
162
|
+
symbols = []
|
163
|
+
|
164
|
+
for param in params
|
165
|
+
if param.is_a?(Class)
|
166
|
+
klass = param
|
167
|
+
elsif param.is_a?(Symbol)
|
168
|
+
symbols << param
|
169
|
+
elsif param.is_a?(Hash)
|
170
|
+
# the meta hash.
|
171
|
+
meta = param
|
172
|
+
else
|
173
|
+
raise "Error when defining property!"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
meta[:reader] = false
|
178
|
+
meta[:writer] = true
|
179
|
+
|
180
|
+
for symbol in symbols
|
181
|
+
prop(klass, symbol, meta)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Helper method. Accepts a collection of symbols and generates
|
186
|
+
# properties. Generates reader and writer.
|
187
|
+
#
|
188
|
+
# Example:
|
189
|
+
# prop_accessor String, :name, :title, :body, :sql => "char(32)"
|
190
|
+
#
|
191
|
+
def prop_accessor(*params)
|
192
|
+
meta = {}
|
193
|
+
klass = Object
|
194
|
+
symbols = []
|
195
|
+
|
196
|
+
for param in params
|
197
|
+
if param.is_a?(Class)
|
198
|
+
klass = param
|
199
|
+
elsif param.is_a?(Symbol)
|
200
|
+
symbols << param
|
201
|
+
elsif param.is_a?(Hash)
|
202
|
+
# the meta hash.
|
203
|
+
meta = param
|
204
|
+
else
|
205
|
+
raise "Error when defining property!"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
meta[:reader] = true
|
210
|
+
meta[:writer] = true
|
211
|
+
|
212
|
+
for symbol in symbols
|
213
|
+
prop(klass, symbol, meta)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Add the property
|
218
|
+
#
|
219
|
+
def __add_prop(prop, reader = true, writer = true)
|
220
|
+
if idx = @__props.index(prop)
|
221
|
+
# override in case of duplicates. Keep the order of the props.
|
222
|
+
@__props[idx] = prop
|
223
|
+
else
|
224
|
+
@__props << prop
|
225
|
+
end
|
226
|
+
|
227
|
+
# Precompile the property read/write methods
|
228
|
+
|
229
|
+
s, klass = prop.symbol, prop.klass
|
230
|
+
|
231
|
+
if reader
|
232
|
+
module_eval %{
|
233
|
+
def #{s}
|
234
|
+
return @#{s}
|
235
|
+
end
|
236
|
+
}
|
237
|
+
end
|
238
|
+
|
239
|
+
if writer
|
240
|
+
module_eval %{
|
241
|
+
def #{s}=(val)
|
242
|
+
@#{s} = val
|
243
|
+
end
|
244
|
+
|
245
|
+
def __force_#{s}(val)
|
246
|
+
@#{s} = } + case klass.name
|
247
|
+
when Fixnum.name
|
248
|
+
"val.to_i()"
|
249
|
+
when String.name
|
250
|
+
"val.to_s()"
|
251
|
+
when Float.name
|
252
|
+
"val.to_f()"
|
253
|
+
when Time.name
|
254
|
+
"Time.parse(val.to_s())"
|
255
|
+
when TrueClass.name, FalseClass.name
|
256
|
+
"val.to_i() > 0"
|
257
|
+
else
|
258
|
+
"val"
|
259
|
+
end + %{
|
260
|
+
end
|
261
|
+
}
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Attach metadata
|
266
|
+
#
|
267
|
+
def meta(key, val)
|
268
|
+
@__meta = G::SafeHash.new unless @__meta
|
269
|
+
|
270
|
+
@__meta[key] = [] unless @__meta[key]
|
271
|
+
|
272
|
+
# guard against duplicates, no need to keep order.
|
273
|
+
@__meta[key].delete_if { |v| val == v }
|
274
|
+
@__meta[key] << val
|
275
|
+
end
|
276
|
+
|
277
|
+
# This method is typically called before including other
|
278
|
+
# modules to preserve properties order.
|
279
|
+
#
|
280
|
+
def inherit_meta(mod = superclass)
|
281
|
+
# concat props.
|
282
|
+
if mod.__props
|
283
|
+
@__props = G::SafeArray.new unless @__props
|
284
|
+
|
285
|
+
mod.__props.each { |p|
|
286
|
+
__add_prop(p)
|
287
|
+
}
|
288
|
+
end
|
289
|
+
|
290
|
+
# concat metadata
|
291
|
+
if mod.__meta
|
292
|
+
mod.__meta.each { |k, val|
|
293
|
+
val.each { |v|
|
294
|
+
meta(k, v)
|
295
|
+
} if val
|
296
|
+
}
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
|