nitro 0.11.0 → 0.12.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/ChangeLog +150 -0
- data/README +1 -1
- data/RELEASES +89 -0
- data/Rakefile +3 -3
- data/{AUTHORS → doc/AUTHORS} +0 -0
- data/{LICENSE → doc/LICENSE} +0 -0
- data/doc/bugs.txt +2 -1
- data/examples/README.windows +2 -2
- data/examples/blog/lib/blog/controller.rb +9 -8
- data/examples/blog/log/apache.error_log +71 -0
- data/{lib/xsl → examples/blog/root}/base.xsl +0 -0
- data/examples/blog/root/error.xhtml +56 -0
- data/examples/blog/root/index.xhtml +2 -2
- data/examples/blog/root/recent_posts.xhtml +1 -1
- data/examples/blog/root/style.xsl +4 -4
- data/examples/blog/run.rb +1 -2
- data/examples/no_xsl_blog/root/index.xhtml +2 -2
- data/examples/no_xsl_blog/root/recent_posts.xhtml +1 -1
- data/examples/why_wiki/run.rb +19 -19
- data/lib/nitro.rb +2 -21
- data/lib/nitro/adapters/webrick.rb +19 -3
- data/lib/nitro/context.rb +15 -1
- data/lib/nitro/controller.rb +84 -49
- data/lib/nitro/dispatcher.rb +30 -6
- data/lib/nitro/markup.rb +4 -2
- data/lib/nitro/render.rb +15 -11
- data/lib/nitro/routing.rb +33 -0
- data/lib/nitro/runner.rb +38 -3
- data/lib/nitro/scaffold.rb +7 -4
- data/lib/nitro/shaders.rb +11 -4
- data/lib/nitro/template.rb +140 -0
- data/lib/og.rb +25 -11
- data/lib/og/adapter.rb +141 -7
- data/lib/og/adapters/mysql.rb +41 -3
- data/lib/og/adapters/oracle.rb +4 -3
- data/lib/og/adapters/psql.rb +3 -3
- data/lib/og/adapters/sqlite.rb +3 -3
- data/lib/og/connection.rb +5 -1
- data/lib/og/database.rb +26 -12
- data/lib/og/enchant.rb +50 -16
- data/lib/og/meta.rb +15 -15
- data/lib/og/observer.rb +53 -0
- data/test/glue/tc_property_type_checking.rb +3 -0
- data/test/nitro/tc_controller.rb +1 -1
- data/test/nitro/tc_dispatcher.rb +1 -1
- data/test/nitro/tc_template.rb +32 -0
- data/test/og/tc_many_to_many.rb +62 -0
- data/test/og/tc_observer.rb +85 -0
- data/test/tc_og.rb +16 -2
- metadata +12 -14
- data/bin/cluster +0 -218
- data/examples/why_wiki/wiki.yml +0 -6
- data/examples/wiki.yml +0 -1
- data/lib/nitro/ui/select.rb +0 -40
- data/lib/nitro/ui/sitemap.rb +0 -183
- data/test/nitro/ui/tc_sitemap.rb +0 -37
data/lib/og.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# * George Moschovitis <gm@navel.gr>
|
2
2
|
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: og.rb
|
3
|
+
# $Id: og.rb 270 2005-03-07 17:52:16Z gmosx $
|
4
4
|
|
5
5
|
require 'glue'
|
6
6
|
require 'glue/logger'
|
@@ -15,7 +15,7 @@ require 'glue/validation'
|
|
15
15
|
# Og (ObjectGraph) is an efficient, yet simple Object-Relational
|
16
16
|
# mapping library.
|
17
17
|
#
|
18
|
-
#
|
18
|
+
# === Features
|
19
19
|
#
|
20
20
|
# The library provides the following features:
|
21
21
|
#
|
@@ -30,12 +30,13 @@ require 'glue/validation'
|
|
30
30
|
# + Thread safety.
|
31
31
|
# + SQL transactions.
|
32
32
|
# + Lifecycle callbacks.
|
33
|
+
# + Lifecycle observers.
|
33
34
|
# + Transparent support for cascading deletes for all backends.
|
34
35
|
# + Hierarchical structures (preorder traversal, materialized paths)
|
35
36
|
# + Works safely as part of distributed application.
|
36
37
|
# + Simple implementation.
|
37
38
|
#
|
38
|
-
#
|
39
|
+
# === Meta language
|
39
40
|
#
|
40
41
|
# primary_key :pid (NOT IMPLEMENTED)
|
41
42
|
# name_key :name (NOT IMPLEMENTED)
|
@@ -44,13 +45,24 @@ require 'glue/validation'
|
|
44
45
|
# many_to_many Role, :roles
|
45
46
|
# sql_index :pid
|
46
47
|
#
|
47
|
-
#
|
48
|
+
# === Property Metadata
|
49
|
+
#
|
50
|
+
# Og defines, reserves and uses the following property
|
51
|
+
# metadata types:
|
52
|
+
#
|
53
|
+
# [+:sql_index+]
|
54
|
+
# Create an sql index for this property.
|
55
|
+
#
|
56
|
+
# [+:unique+]
|
57
|
+
# This value of the property must be unique.
|
58
|
+
#
|
59
|
+
# [+:name_key+]
|
60
|
+
# This property is used as name-key.
|
61
|
+
#
|
62
|
+
# === Design
|
48
63
|
#
|
49
64
|
# Keep the main classes backend agnostic.
|
50
|
-
|
51
|
-
# Try to make the methods work with oids. Do NOT implement descendants
|
52
|
-
# use a root id (rid).
|
53
|
-
#++
|
65
|
+
#
|
54
66
|
# For class ids we use the name instead of a hash. Class ids are
|
55
67
|
# typically not used in querys, they are stored for completeness.
|
56
68
|
# If we store a hash we cannot reclaim the class thus invalidating
|
@@ -68,8 +80,10 @@ require 'glue/validation'
|
|
68
80
|
# The og.xxx methods are more flexible and allow you to use
|
69
81
|
# multiple databases for example.
|
70
82
|
#
|
71
|
-
#
|
83
|
+
# === Managed Objects Lifecycle Callbacks
|
72
84
|
#
|
85
|
+
# * og_pre_read
|
86
|
+
# * og_post_read
|
73
87
|
# * og_pre_insert
|
74
88
|
# * og_post_insert
|
75
89
|
# * og_pre_update
|
@@ -81,7 +95,7 @@ require 'glue/validation'
|
|
81
95
|
# A class level callback is used for delete because typically you call
|
82
96
|
# delete with an oid and not an object to avoid a deserialization.
|
83
97
|
#
|
84
|
-
#
|
98
|
+
# === Future
|
85
99
|
#
|
86
100
|
# * Support prepared statements (pgsql)
|
87
101
|
# * Support stored procedures (pgsql)
|
@@ -97,7 +111,7 @@ module Og
|
|
97
111
|
|
98
112
|
# The version.
|
99
113
|
|
100
|
-
Version = '0.
|
114
|
+
Version = '0.12.0'
|
101
115
|
|
102
116
|
# Library path.
|
103
117
|
|
data/lib/og/adapter.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# * George Moschovitis <gm@navel.gr>
|
2
2
|
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: adapter.rb
|
3
|
+
# $Id: adapter.rb 270 2005-03-07 17:52:16Z gmosx $
|
4
4
|
|
5
5
|
require 'yaml'
|
6
6
|
require 'singleton'
|
@@ -20,6 +20,11 @@ class Adapter
|
|
20
20
|
# A mapping between Ruby and backend Datastore types.
|
21
21
|
|
22
22
|
attr_accessor :typemap
|
23
|
+
|
24
|
+
# A map for casting Ruby types to SQL safe textual
|
25
|
+
# representations.
|
26
|
+
|
27
|
+
attr_accessor :typecast
|
23
28
|
|
24
29
|
# Lookup the adapter instance from the adapter name.
|
25
30
|
|
@@ -29,6 +34,9 @@ class Adapter
|
|
29
34
|
end
|
30
35
|
|
31
36
|
def initialize
|
37
|
+
# The default mappings, should be valid for most
|
38
|
+
# RDBMS.
|
39
|
+
|
32
40
|
@typemap = {
|
33
41
|
Integer => 'integer',
|
34
42
|
Fixnum => 'integer',
|
@@ -41,6 +49,19 @@ class Adapter
|
|
41
49
|
Array => 'text',
|
42
50
|
Hash => 'text'
|
43
51
|
}
|
52
|
+
|
53
|
+
# The :s: is a marker that will be replaced with the
|
54
|
+
# actual value to be casted. The default parameter of
|
55
|
+
# the Hash handles all other types (Object, Array, etc)
|
56
|
+
|
57
|
+
@typecast = Hash.new("'#\{#{self.class}.escape(:s:.to_yaml)\}'").update(
|
58
|
+
Integer => "\#\{:s:\}",
|
59
|
+
Float => "\#\{:s:\}",
|
60
|
+
String => "'#\{#{self.class}.escape(:s:)\}'",
|
61
|
+
Time => "'#\{#{self.class}.timestamp(:s:)\}'",
|
62
|
+
Date => "'#\{#{self.class}.date(:s:)\}'",
|
63
|
+
TrueClass => "#\{:s: ? \"'t'\" : 'NULL' \}"
|
64
|
+
)
|
44
65
|
end
|
45
66
|
|
46
67
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
@@ -105,7 +126,7 @@ class Adapter
|
|
105
126
|
# Create a new connection to the backend.
|
106
127
|
|
107
128
|
def new_connection(db)
|
108
|
-
return
|
129
|
+
return Connection.new(db)
|
109
130
|
end
|
110
131
|
|
111
132
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
@@ -145,8 +166,8 @@ class Adapter
|
|
145
166
|
# You may want to override this method to map an existing
|
146
167
|
# database schema using Og.
|
147
168
|
|
148
|
-
def self.join_table(klass1, klass2)
|
149
|
-
"og_#{Og.table_prefix}j_#{encode(klass1)}_#{encode(klass2)}"
|
169
|
+
def self.join_table(klass1, klass2, field)
|
170
|
+
"og_#{Og.table_prefix}j_#{encode(klass1)}_#{encode(klass2)}_#{field}"
|
150
171
|
end
|
151
172
|
|
152
173
|
# Return an sql string evaluator for the property.
|
@@ -222,7 +243,10 @@ class Adapter
|
|
222
243
|
if default = p.meta[:default]
|
223
244
|
field << " DEFAULT #{default.inspect} NOT NULL"
|
224
245
|
end
|
225
|
-
|
246
|
+
|
247
|
+
# set unique
|
248
|
+
field << " UNIQUE" if p.meta[:unique]
|
249
|
+
|
226
250
|
# attach extra sql
|
227
251
|
if extra_sql = p.meta[:extra_sql]
|
228
252
|
field << " #{extra_sql}"
|
@@ -266,9 +290,19 @@ class Adapter
|
|
266
290
|
end
|
267
291
|
|
268
292
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
269
|
-
# :section:
|
293
|
+
# :section: Precompile lifecycle methods.
|
270
294
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
271
295
|
|
296
|
+
# Precompile some code that gets executed all the time.
|
297
|
+
# Deletion code is not precompiled, because it is not used
|
298
|
+
# as frequently.
|
299
|
+
|
300
|
+
def eval_lifecycle_methods(klass, db)
|
301
|
+
eval_og_insert(klass, db)
|
302
|
+
eval_og_update(klass, db)
|
303
|
+
eval_og_read(klass, db)
|
304
|
+
end
|
305
|
+
|
272
306
|
# Generate the property for oid.
|
273
307
|
|
274
308
|
def eval_og_oid(klass)
|
@@ -281,6 +315,9 @@ class Adapter
|
|
281
315
|
# The generated code sets the oid when inserting!
|
282
316
|
|
283
317
|
def eval_og_insert(klass, db)
|
318
|
+
|
319
|
+
# Attach object callbacks.
|
320
|
+
|
284
321
|
if klass.instance_methods.include?('og_pre_insert')
|
285
322
|
pre_cb = 'og_pre_insert(conn);'
|
286
323
|
else
|
@@ -300,7 +337,36 @@ class Adapter
|
|
300
337
|
if klass.instance_methods.include?('og_post_insert_update')
|
301
338
|
post_cb << 'og_post_insert_update(conn);'
|
302
339
|
end
|
303
|
-
|
340
|
+
|
341
|
+
# Attach observers.
|
342
|
+
|
343
|
+
if observers = klass.__meta[:og_observers]
|
344
|
+
observers.each_with_index do |o, idx|
|
345
|
+
if o.is_a?(Class)
|
346
|
+
obs = "#{o}.instance"
|
347
|
+
o = o.instance
|
348
|
+
else
|
349
|
+
obs = "self.class.__meta[:og_observers][#{idx}]"
|
350
|
+
end
|
351
|
+
|
352
|
+
if o.respond_to?(:og_pre_insert)
|
353
|
+
pre_cb << "#{obs}.og_pre_insert(conn, self);"
|
354
|
+
end
|
355
|
+
|
356
|
+
if o.respond_to?(:og_post_insert)
|
357
|
+
post_cb << "#{obs}.og_post_insert(conn, self);"
|
358
|
+
end
|
359
|
+
|
360
|
+
if o.respond_to?(:og_pre_insert_update)
|
361
|
+
pre_cb << "#{obs}.og_pre_insert_update(conn, self);"
|
362
|
+
end
|
363
|
+
|
364
|
+
if o.respond_to?(:og_post_insert_update)
|
365
|
+
post_cb << "#{obs}.og_post_insert_update(conn, self);"
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
304
370
|
klass.class_eval %{
|
305
371
|
def og_insert(conn)
|
306
372
|
#{insert_code(klass, db, pre_cb, post_cb)}
|
@@ -320,6 +386,8 @@ class Adapter
|
|
320
386
|
|
321
387
|
sql = "UPDATE #{klass::DBTABLE} SET #{updates.join(', ')} WHERE oid=#\{@oid\}"
|
322
388
|
|
389
|
+
# Attach object callbacks.
|
390
|
+
|
323
391
|
if klass.instance_methods.include?('og_pre_update')
|
324
392
|
pre_cb = 'og_pre_update(conn);'
|
325
393
|
else
|
@@ -340,6 +408,35 @@ class Adapter
|
|
340
408
|
post_cb << 'og_post_insert_update(conn);'
|
341
409
|
end
|
342
410
|
|
411
|
+
# Attach observers.
|
412
|
+
|
413
|
+
if observers = klass.__meta[:og_observers]
|
414
|
+
observers.each_with_index do |o, idx|
|
415
|
+
if o.is_a?(Class)
|
416
|
+
obs = "#{o}.instance"
|
417
|
+
o = o.instance
|
418
|
+
else
|
419
|
+
obs = "self.class.__meta[:og_observers][#{idx}]"
|
420
|
+
end
|
421
|
+
|
422
|
+
if o.respond_to?(:og_pre_update)
|
423
|
+
pre_cb << "#{obs}.og_pre_update(conn, self);"
|
424
|
+
end
|
425
|
+
|
426
|
+
if o.respond_to?(:og_post_update)
|
427
|
+
post_cb << "#{obs}.og_post_update(conn, self);"
|
428
|
+
end
|
429
|
+
|
430
|
+
if o.respond_to?(:og_pre_insert_update)
|
431
|
+
pre_cb << "#{obs}.og_pre_insert_update(conn, self);"
|
432
|
+
end
|
433
|
+
|
434
|
+
if o.respond_to?(:og_post_insert_update)
|
435
|
+
post_cb << "#{obs}.og_post_insert_update(conn, self);"
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
343
440
|
klass.class_eval %{
|
344
441
|
def og_update(conn)
|
345
442
|
#{pre_cb}
|
@@ -367,9 +464,46 @@ class Adapter
|
|
367
464
|
end
|
368
465
|
end
|
369
466
|
|
467
|
+
# Attach object callbacks.
|
468
|
+
|
469
|
+
if klass.instance_methods.include?('og_pre_read')
|
470
|
+
pre_cb = 'og_pre_read(conn);'
|
471
|
+
else
|
472
|
+
pre_cb = ''
|
473
|
+
end
|
474
|
+
|
475
|
+
if klass.instance_methods.include?('og_post_read')
|
476
|
+
post_cb = 'og_post_read(conn);'
|
477
|
+
else
|
478
|
+
post_cb = ''
|
479
|
+
end
|
480
|
+
|
481
|
+
# Attach observers.
|
482
|
+
|
483
|
+
if observers = klass.__meta[:og_observers]
|
484
|
+
observers.each_with_index do |o, idx|
|
485
|
+
if o.is_a?(Class)
|
486
|
+
obs = "#{o}.instance"
|
487
|
+
o = o.instance
|
488
|
+
else
|
489
|
+
obs = "self.class.__meta[:og_observers][#{idx}]"
|
490
|
+
end
|
491
|
+
|
492
|
+
if o.respond_to?(:og_pre_read)
|
493
|
+
pre_cb << "#{obs}.og_pre_read(conn, self);"
|
494
|
+
end
|
495
|
+
|
496
|
+
if o.respond_to?(:og_post_read)
|
497
|
+
post_cb << "#{obs}.og_post_read(conn, self);"
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
370
502
|
klass.class_eval %{
|
371
503
|
def og_read(res, tuple = nil)
|
504
|
+
#{pre_cb}
|
372
505
|
#{code.join('; ')}
|
506
|
+
#{post_cb}
|
373
507
|
end
|
374
508
|
}
|
375
509
|
end
|
data/lib/og/adapters/mysql.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# * George Moschovitis <gm@navel.gr>
|
2
2
|
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: mysql.rb
|
3
|
+
# $Id: mysql.rb 270 2005-03-07 17:52:16Z gmosx $
|
4
4
|
|
5
5
|
require 'mysql'
|
6
6
|
|
@@ -19,6 +19,7 @@ class MysqlAdapter < Adapter
|
|
19
19
|
def initialize
|
20
20
|
super
|
21
21
|
@typemap.update(TrueClass => 'tinyint')
|
22
|
+
@typecast.update(TrueClass => "#\{:s: ? \"1\" : 'NULL' \}")
|
22
23
|
end
|
23
24
|
|
24
25
|
def self.escape(str)
|
@@ -118,6 +119,43 @@ class MysqlAdapter < Adapter
|
|
118
119
|
res.free if res
|
119
120
|
end
|
120
121
|
|
122
|
+
def create_fields(klass)
|
123
|
+
fields = []
|
124
|
+
|
125
|
+
klass.__props.each do |p|
|
126
|
+
klass.sql_index(p.symbol) if p.meta[:sql_index]
|
127
|
+
|
128
|
+
field = "#{p.symbol}"
|
129
|
+
|
130
|
+
if p.meta and p.meta[:sql]
|
131
|
+
field << " #{p.meta[:sql]}"
|
132
|
+
else
|
133
|
+
field << " #{@typemap[p.klass]}"
|
134
|
+
|
135
|
+
if p.meta
|
136
|
+
# set default value (gmosx: not that useful in the
|
137
|
+
# current implementation).
|
138
|
+
if default = p.meta[:default]
|
139
|
+
field << " DEFAULT #{default.inspect} NOT NULL"
|
140
|
+
end
|
141
|
+
|
142
|
+
# set unique
|
143
|
+
# FIXME: correctly handle UNIQUE constrain.
|
144
|
+
# field << " UNIQUE" if p.meta[:unique]
|
145
|
+
|
146
|
+
# attach extra sql
|
147
|
+
if extra_sql = p.meta[:extra_sql]
|
148
|
+
field << " #{extra_sql}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
fields << field
|
154
|
+
end
|
155
|
+
|
156
|
+
return fields
|
157
|
+
end
|
158
|
+
|
121
159
|
def create_table(klass, db)
|
122
160
|
conn = db.get_connection
|
123
161
|
|
@@ -165,11 +203,11 @@ class MysqlAdapter < Adapter
|
|
165
203
|
if klass.__meta and joins = klass.__meta[:sql_join]
|
166
204
|
for data in joins
|
167
205
|
# the class to join to and some options.
|
168
|
-
join_class, options = *data
|
206
|
+
join_name, join_class, options = *data
|
169
207
|
|
170
208
|
# gmosx: dont use DBTABLE here, perhaps the join class
|
171
209
|
# is not managed yet.
|
172
|
-
join_table = "#{self.class.join_table(klass, join_class)}"
|
210
|
+
join_table = "#{self.class.join_table(klass, join_class, join_name)}"
|
173
211
|
join_src = "#{self.class.encode(klass)}_oid"
|
174
212
|
join_dst = "#{self.class.encode(join_class)}_oid"
|
175
213
|
begin
|
data/lib/og/adapters/oracle.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# * Matt Bowen <matt.bowen@farweststeel.com>
|
2
2
|
# * George Moschovitis <gm@navel.gr>
|
3
3
|
# (c) 2004-2005 Navel, all rights reserved.
|
4
|
-
# $Id: oracle.rb
|
4
|
+
# $Id: oracle.rb 270 2005-03-07 17:52:16Z gmosx $
|
5
5
|
|
6
6
|
require 'oracle'
|
7
7
|
|
@@ -28,6 +28,7 @@ class OracleAdapter < Adapter
|
|
28
28
|
Array => 'varchar2(1024)',
|
29
29
|
Hash => 'varchar2(1024)'
|
30
30
|
)
|
31
|
+
@typecast.update(TrueClass => "#\{:s: ? \"1\" : 'NULL' \}")
|
31
32
|
end
|
32
33
|
|
33
34
|
def self.timestamp(time = Time.now)
|
@@ -190,11 +191,11 @@ class OracleAdapter < Adapter
|
|
190
191
|
if klass.__meta and joins = klass.__meta[:sql_join]
|
191
192
|
for data in joins
|
192
193
|
# the class to join to and some options.
|
193
|
-
join_class, options = *data
|
194
|
+
join_name, join_class, options = *data
|
194
195
|
|
195
196
|
# gmosx: dont use DBTABLE here, perhaps the join class
|
196
197
|
# is not managed yet.
|
197
|
-
join_table = "#{self.class.join_table(klass, join_class)}"
|
198
|
+
join_table = "#{self.class.join_table(klass, join_class, join_name)}"
|
198
199
|
join_src = "#{self.class.encode(klass)}_oid"
|
199
200
|
join_dst = "#{self.class.encode(join_class)}_oid"
|
200
201
|
begin
|
data/lib/og/adapters/psql.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# * George Moschovitis <gm@navel.gr>
|
2
2
|
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: psql.rb
|
3
|
+
# $Id: psql.rb 270 2005-03-07 17:52:16Z gmosx $
|
4
4
|
|
5
5
|
require 'postgres'
|
6
6
|
|
@@ -137,11 +137,11 @@ class PsqlAdapter < Adapter
|
|
137
137
|
if klass.__meta and joins = klass.__meta[:sql_join]
|
138
138
|
for data in joins
|
139
139
|
# the class to join to and some options.
|
140
|
-
join_class, options = *data
|
140
|
+
join_name, join_class, options = *data
|
141
141
|
|
142
142
|
# gmosx: dont use DBTABLE here, perhaps the join class
|
143
143
|
# is not managed yet.
|
144
|
-
join_table = "#{self.class.join_table(klass, join_class)}"
|
144
|
+
join_table = "#{self.class.join_table(klass, join_class, join_name)}"
|
145
145
|
join_src = "#{self.class.encode(klass)}_oid"
|
146
146
|
join_dst = "#{self.class.encode(join_class)}_oid"
|
147
147
|
begin
|