og 0.9.5 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +260 -0
- data/LICENSE +1 -0
- data/README.og +6 -5
- data/RELEASES.og +23 -0
- data/Rakefile +102 -92
- data/examples/og/mock_example.rb +0 -2
- data/examples/og/mysql_to_psql.rb +0 -2
- data/examples/og/run.rb +23 -22
- data/install.rb +44 -0
- data/lib/glue/array.rb +6 -10
- data/lib/glue/attribute.rb +0 -3
- data/lib/glue/cache.rb +1 -1
- data/lib/glue/inflector.rb +5 -5
- data/lib/glue/mixins.rb +3 -12
- data/lib/glue/number.rb +1 -1
- data/lib/glue/object.rb +7 -1
- data/lib/glue/property.rb +32 -22
- data/lib/glue/string.rb +13 -75
- data/lib/glue/time.rb +2 -2
- data/lib/glue/validation.rb +7 -11
- data/lib/og.rb +27 -261
- data/lib/og/adapter.rb +352 -0
- data/lib/og/adapters/mysql.rb +304 -0
- data/lib/og/adapters/psql.rb +286 -0
- data/lib/og/adapters/sqlite.rb +262 -0
- data/lib/og/backend.rb +1 -1
- data/lib/og/connection.rb +123 -87
- data/lib/og/database.rb +268 -0
- data/lib/og/meta.rb +23 -22
- data/lib/og/mock.rb +2 -3
- data/test/og/tc_lifecycle.rb +22 -25
- data/test/og/tc_sqlite.rb +87 -0
- data/test/tc_og.rb +61 -42
- metadata +35 -11
- data/lib/glue/macro.rb +0 -56
- data/lib/og/backends/mysql.rb +0 -370
- data/lib/og/backends/psql.rb +0 -386
- data/lib/og/backends/sqlite.rb +0 -383
- data/lib/og/version.rb +0 -9
data/examples/og/mock_example.rb
CHANGED
data/examples/og/run.rb
CHANGED
@@ -1,22 +1,17 @@
|
|
1
|
-
# = Og Example
|
2
|
-
#
|
3
1
|
# A simple example to demonstrate the Og library.
|
4
2
|
#
|
5
|
-
# code:
|
6
3
|
# * George Moschovitis <gm@navel.gr>
|
7
|
-
#
|
8
4
|
# (c) 2004 Navel, all rights reserved.
|
9
|
-
# $Id: run.rb
|
10
|
-
|
11
|
-
$:.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib')
|
5
|
+
# $Id: run.rb 256 2005-02-11 16:22:33Z gmosx $
|
12
6
|
|
13
7
|
require 'og'
|
14
8
|
|
15
9
|
# Full debug information.
|
10
|
+
|
16
11
|
$DBG = true
|
17
12
|
|
18
|
-
#
|
19
|
-
|
13
|
+
# A child class.
|
14
|
+
|
20
15
|
class Comment
|
21
16
|
prop_accessor :body, String
|
22
17
|
|
@@ -55,8 +50,8 @@ class User
|
|
55
50
|
end
|
56
51
|
|
57
52
|
|
58
|
-
#
|
59
|
-
|
53
|
+
# A parent class.
|
54
|
+
|
60
55
|
class Article
|
61
56
|
prop_accessor :title, String
|
62
57
|
prop_accessor :body, String
|
@@ -95,8 +90,8 @@ class Article
|
|
95
90
|
end
|
96
91
|
end
|
97
92
|
|
98
|
-
#
|
99
|
-
|
93
|
+
# A parent class.
|
94
|
+
|
100
95
|
class Category
|
101
96
|
prop_accessor :title, String
|
102
97
|
prop_accessor :body, String
|
@@ -110,20 +105,20 @@ class Category
|
|
110
105
|
end
|
111
106
|
|
112
107
|
|
113
|
-
#
|
114
|
-
|
108
|
+
# Article comment.
|
109
|
+
|
115
110
|
class ArticleComment < Comment
|
116
111
|
belongs_to :article, Article
|
117
112
|
end
|
118
113
|
|
119
|
-
#
|
120
|
-
|
114
|
+
# User comment.
|
115
|
+
|
121
116
|
class UserComment < Comment
|
122
117
|
belongs_to :author, User
|
123
118
|
end
|
124
119
|
|
125
|
-
#
|
126
|
-
|
120
|
+
# Another child class.
|
121
|
+
|
127
122
|
class Part
|
128
123
|
prop_accessor :name, String
|
129
124
|
belongs_to :article, Article
|
@@ -139,14 +134,20 @@ end
|
|
139
134
|
|
140
135
|
# Og configuration.
|
141
136
|
config = {
|
142
|
-
:address => "localhost",
|
143
137
|
:database => "test",
|
144
|
-
:
|
138
|
+
:adapter => "sqlite",
|
139
|
+
:connection_count => 2
|
140
|
+
}
|
141
|
+
=begin
|
142
|
+
config = {
|
143
|
+
# :address => "localhost",
|
144
|
+
:database => "test",
|
145
|
+
:adapter => "psql",
|
145
146
|
:user => "postgres",
|
146
147
|
:password => "navelrulez",
|
147
148
|
:connection_count => 1
|
148
149
|
}
|
149
|
-
|
150
|
+
|
150
151
|
config = {
|
151
152
|
:address => "localhost",
|
152
153
|
:database => "test",
|
data/install.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# * George Moschovitis <gm@navel.gr>
|
4
|
+
# (c) 2004-2005 Navel, all rights reserved.
|
5
|
+
# $Id$
|
6
|
+
|
7
|
+
require 'rbconfig'
|
8
|
+
require 'ftools'
|
9
|
+
|
10
|
+
dst_dir = Config::CONFIG['sitelibdir']
|
11
|
+
|
12
|
+
Dir.chdir('lib') do
|
13
|
+
Dir['**/*.rb'].each do |file|
|
14
|
+
File.mkpath File.join(dst_dir, File.dirname(file)), true
|
15
|
+
File.install file, File.join(dst_dir, file), 0644, true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# gmosx: this is potentially dangerous, rethink.
|
20
|
+
|
21
|
+
Dir.chdir('vendor') do
|
22
|
+
Dir['**/*.rb'].each do |file|
|
23
|
+
File.mkpath File.join(dst_dir, File.dirname(file)), true
|
24
|
+
File.install file, File.join(dst_dir, file), 0644, true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
puts %{
|
29
|
+
|
30
|
+
|
31
|
+
---
|
32
|
+
Congratulations, you have successfully installed Nitro + Og!
|
33
|
+
The libraries where installed in '#{dst_dir}'.
|
34
|
+
|
35
|
+
To verify that everything works correctly, try to run the tiny
|
36
|
+
example by issuing:
|
37
|
+
|
38
|
+
$ cd exampes/tiny
|
39
|
+
$ ruby ctl
|
40
|
+
|
41
|
+
at the command line.
|
42
|
+
|
43
|
+
Enjoy the magic of Nitro!
|
44
|
+
}
|
data/lib/glue/array.rb
CHANGED
@@ -1,26 +1,22 @@
|
|
1
|
-
# code:
|
2
1
|
# * George Moschovitis <gm@navel.gr>
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# $Id: array.rb 202 2005-01-17 10:44:13Z gmosx $
|
2
|
+
# (c) 2002-2005 Navel, all rights reserved.
|
3
|
+
# $Id: array.rb 259 2005-02-15 08:54:54Z gmosx $
|
6
4
|
|
7
|
-
require
|
5
|
+
require 'sync'
|
8
6
|
|
9
7
|
module N
|
10
8
|
|
11
|
-
# == SafeArray
|
12
|
-
#
|
13
9
|
# A thread-safe array. We use a sync object instead of a mutex,
|
14
10
|
# because it is re-entrant.
|
15
11
|
# An exclusive lock is needed when writing, a shared lock IS NEEDED
|
16
12
|
# when reading
|
17
|
-
|
13
|
+
|
18
14
|
class SafeArray < Array
|
19
15
|
|
20
16
|
attr :sync
|
21
17
|
|
22
18
|
# gmosx: delegator is not used.
|
23
|
-
|
19
|
+
|
24
20
|
def initialize(delegator = nil)
|
25
21
|
@sync = ::Sync.new()
|
26
22
|
end
|
@@ -81,4 +77,4 @@ class SafeArray < Array
|
|
81
77
|
|
82
78
|
end
|
83
79
|
|
84
|
-
end
|
80
|
+
end
|
data/lib/glue/attribute.rb
CHANGED
data/lib/glue/cache.rb
CHANGED
data/lib/glue/inflector.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
# Code from
|
1
|
+
# Code from RubyOnRails (http://www.rubyonrails.com)
|
2
2
|
# Copyright (c) 2004 David Heinemeier Hansson.
|
3
|
-
#
|
4
3
|
|
5
4
|
module N
|
6
5
|
|
7
|
-
# The Inflector transforms words from singular to plural,
|
6
|
+
# The Inflector transforms words from singular to plural,
|
7
|
+
# class names to table names, modulized class names to ones without,
|
8
8
|
# and class names to foreign keys.
|
9
|
-
|
9
|
+
|
10
10
|
module Inflector
|
11
11
|
extend self
|
12
12
|
|
@@ -88,4 +88,4 @@ module Inflector
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
-
end
|
91
|
+
end
|
data/lib/glue/mixins.rb
CHANGED
@@ -1,20 +1,11 @@
|
|
1
|
-
# = Mixins
|
2
|
-
#
|
3
|
-
# A collection of useful mixins. Use these to synthesize your
|
4
|
-
# entities.
|
5
|
-
#
|
6
|
-
# code:
|
7
1
|
# * George Moschovitis <gm@navel.gr>
|
8
|
-
#
|
9
|
-
# (c) 2004 Navel, all rights reserved.
|
2
|
+
# (c) 2004-2005 Navel, all rights reserved.
|
10
3
|
# $Id$
|
11
4
|
|
12
5
|
module N
|
13
6
|
|
14
|
-
# = Expirable
|
15
|
-
#
|
16
7
|
# Generic expiring functionality mixin.
|
17
|
-
|
8
|
+
|
18
9
|
module Expirable
|
19
10
|
attr_accessor :expires
|
20
11
|
|
@@ -42,4 +33,4 @@ module Expirable
|
|
42
33
|
end
|
43
34
|
end
|
44
35
|
|
45
|
-
end
|
36
|
+
end
|
data/lib/glue/number.rb
CHANGED
data/lib/glue/object.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
#
|
1
|
+
# * George Moschovitis <gm@navel.gr>
|
2
|
+
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
+
# $Id: object.rb 259 2005-02-15 08:54:54Z gmosx $
|
4
|
+
|
5
|
+
# Code from RubyOnRails (http://www.rubyonrails.com)
|
2
6
|
|
3
7
|
class Object #:nodoc:
|
4
8
|
def remove_subclasses_of(superclass)
|
@@ -15,6 +19,8 @@ class Object #:nodoc:
|
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
22
|
+
# Code from RubyOnRails (http://www.rubyonrails.com)
|
23
|
+
|
18
24
|
class Class #:nodoc:
|
19
25
|
def remove_subclasses
|
20
26
|
Object.remove_subclasses_of(self)
|
data/lib/glue/property.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# * George Moschovitis <gm@navel.gr>
|
2
2
|
# * Michael Neumann <mneumann@ntecs.de>
|
3
3
|
# (c) 2004-2005 Navel, all rights reserved.
|
4
|
-
# $Id: property.rb
|
4
|
+
# $Id: property.rb 254 2005-02-10 12:44:05Z gmosx $
|
5
5
|
|
6
|
+
require 'glue/attribute'
|
6
7
|
require 'glue/array'
|
7
8
|
require 'glue/hash'
|
8
9
|
|
@@ -17,7 +18,6 @@ module N
|
|
17
18
|
#
|
18
19
|
# The default = methods do not force the types. A special
|
19
20
|
# __force_set method should be used instead.
|
20
|
-
#
|
21
21
|
#--
|
22
22
|
# TODO:
|
23
23
|
# Perhaps a sync is needed in evals (!!!!)
|
@@ -25,6 +25,11 @@ module N
|
|
25
25
|
|
26
26
|
class Property
|
27
27
|
|
28
|
+
# If set to true, perform type checking on property set.
|
29
|
+
# Useful when debugging.
|
30
|
+
|
31
|
+
cattr_accessor :type_checking, false
|
32
|
+
|
28
33
|
# the symbol of the property
|
29
34
|
|
30
35
|
attr_accessor :symbol
|
@@ -57,8 +62,6 @@ class Property
|
|
57
62
|
|
58
63
|
end
|
59
64
|
|
60
|
-
# = PropertyUtils
|
61
|
-
#
|
62
65
|
# A collection of Property related utility methods.
|
63
66
|
|
64
67
|
module PropertyUtils
|
@@ -75,7 +78,7 @@ module PropertyUtils
|
|
75
78
|
|
76
79
|
def self.enchant(target, force = false)
|
77
80
|
unless target.singleton_methods.include?('__props')
|
78
|
-
target.module_eval
|
81
|
+
target.module_eval %{
|
79
82
|
@@__meta = N::SafeHash.new
|
80
83
|
@@__props = N::SafeArray.new
|
81
84
|
|
@@ -94,7 +97,7 @@ module PropertyUtils
|
|
94
97
|
def self.__meta=(meta)
|
95
98
|
@@__meta = meta
|
96
99
|
end
|
97
|
-
|
100
|
+
}
|
98
101
|
end
|
99
102
|
end
|
100
103
|
|
@@ -166,11 +169,25 @@ module PropertyUtils
|
|
166
169
|
|
167
170
|
def self.prop_setter(prop)
|
168
171
|
s = prop.symbol
|
169
|
-
|
172
|
+
|
173
|
+
code = %{
|
170
174
|
def #{s}=(val)
|
175
|
+
}
|
176
|
+
|
177
|
+
if N::Property.type_checking
|
178
|
+
code << %{
|
179
|
+
unless #{prop.klass} == val.class
|
180
|
+
raise "Invalid type, expected '#{prop.klass}', is '\#\{val.class\}'."
|
181
|
+
end
|
182
|
+
}
|
183
|
+
end
|
184
|
+
|
185
|
+
code << %{
|
171
186
|
@#{s} = val
|
172
187
|
end
|
173
188
|
}
|
189
|
+
|
190
|
+
return code
|
174
191
|
end
|
175
192
|
|
176
193
|
# Get the property metadata for the given symbol.
|
@@ -243,17 +260,15 @@ class Module
|
|
243
260
|
def prop(*params)
|
244
261
|
meta, klass, symbols = N::PropertyUtils.resolve_prop_params(params)
|
245
262
|
symbol = symbols.first
|
246
|
-
|
247
263
|
|
248
264
|
N::PropertyUtils.enchant(self)
|
249
265
|
|
250
266
|
if self.is_a?(Class)
|
251
|
-
|
267
|
+
|
252
268
|
# Add some extra code to append features to
|
253
269
|
# subclasses.
|
254
270
|
|
255
|
-
self.module_eval
|
256
|
-
|
271
|
+
self.module_eval %{
|
257
272
|
def self.inherited(sub)
|
258
273
|
N::PropertyUtils.enchant(sub)
|
259
274
|
N::PropertyUtils.copy_props(self, sub)
|
@@ -261,16 +276,13 @@ class Module
|
|
261
276
|
# the hash from the module. super must stay at the end.
|
262
277
|
super
|
263
278
|
end
|
264
|
-
|
265
|
-
end_eval
|
266
|
-
|
279
|
+
}
|
267
280
|
else
|
268
|
-
|
281
|
+
|
269
282
|
# Add some extra code for modules to append
|
270
283
|
# their features to classes that include it.
|
271
284
|
|
272
|
-
self.module_eval
|
273
|
-
|
285
|
+
self.module_eval %{
|
274
286
|
def self.append_features(base)
|
275
287
|
N::PropertyUtils.enchant(base)
|
276
288
|
N::PropertyUtils.copy_props(self, base)
|
@@ -282,9 +294,7 @@ class Module
|
|
282
294
|
|
283
295
|
super
|
284
296
|
end
|
285
|
-
|
286
|
-
end_eval
|
287
|
-
|
297
|
+
}
|
288
298
|
end
|
289
299
|
|
290
300
|
property = N::Property.new(symbol, klass, meta)
|
@@ -366,11 +376,11 @@ class Module
|
|
366
376
|
#++
|
367
377
|
|
368
378
|
def meta(key, val)
|
369
|
-
self.module_eval
|
379
|
+
self.module_eval %{
|
370
380
|
@@__meta[key] ||= []
|
371
381
|
@@__meta[key].delete_if { |v| val == v }
|
372
382
|
@@__meta[key] << val
|
373
|
-
|
383
|
+
}
|
374
384
|
end
|
375
385
|
|
376
386
|
end
|
data/lib/glue/string.rb
CHANGED
@@ -1,17 +1,13 @@
|
|
1
|
-
# code:
|
2
1
|
# * George Moschovitis <gm@navel.gr>
|
3
2
|
# * Anastasios Koutoumanos <ak@navel.gr>
|
4
3
|
# * Elias Karakoulakis <ekarak@ktismata.com>
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# $Id: string.rb 202 2005-01-17 10:44:13Z gmosx $
|
4
|
+
# (c) 2004-2005 Navel, all rights reserved.
|
5
|
+
# $Id: string.rb 259 2005-02-15 08:54:54Z gmosx $
|
8
6
|
|
9
7
|
require "uri"
|
10
8
|
|
11
9
|
module N;
|
12
10
|
|
13
|
-
# = StringUtils
|
14
|
-
#
|
15
11
|
# General string utilities collection.
|
16
12
|
#
|
17
13
|
# === Design:
|
@@ -26,51 +22,8 @@ module N;
|
|
26
22
|
# - implement a method that returns easy to remember
|
27
23
|
# pseudo-random strings
|
28
24
|
# - add aliases for those methods in Kernel.
|
29
|
-
#
|
30
|
-
module StringUtils
|
31
25
|
|
32
|
-
|
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
|
26
|
+
module StringUtils
|
74
27
|
|
75
28
|
# Move this in String class?
|
76
29
|
#
|
@@ -84,7 +37,7 @@ module StringUtils
|
|
84
37
|
# characters, chopped at the nearest word, appended by '...')
|
85
38
|
# force_cutoff: break forcibly at 'count' chars. Does not accept
|
86
39
|
# count < 2.
|
87
|
-
|
40
|
+
|
88
41
|
def self.head(string, count = 128, force_cutoff = false, ellipsis="...")
|
89
42
|
return nil unless string
|
90
43
|
return nil if count < 2
|
@@ -120,7 +73,7 @@ module StringUtils
|
|
120
73
|
return nil unless string
|
121
74
|
|
122
75
|
# gmosx: helps to find bugs
|
123
|
-
raise ArgumentError.new(
|
76
|
+
raise ArgumentError.new('The rules parameter is nil') unless rules
|
124
77
|
|
125
78
|
rewritten_string = string.dup
|
126
79
|
|
@@ -152,7 +105,7 @@ module StringUtils
|
|
152
105
|
# p text # => "1111111111 1111111111 1111111111"
|
153
106
|
#
|
154
107
|
# See the test cases to better understand the behaviour!
|
155
|
-
|
108
|
+
|
156
109
|
def self.wrap(string, width = 20, separator = " ")
|
157
110
|
return nil unless string
|
158
111
|
|
@@ -163,7 +116,7 @@ module StringUtils
|
|
163
116
|
end
|
164
117
|
|
165
118
|
# Replace dangerours chars in filenames
|
166
|
-
|
119
|
+
=begin
|
167
120
|
def self.rationalize_filename(filename)
|
168
121
|
return nil unless filename
|
169
122
|
# gmosx: rationalize a copy!!! (add unit test)
|
@@ -174,10 +127,11 @@ module StringUtils
|
|
174
127
|
xfilename.gsub!(/'/, "")
|
175
128
|
xfilename.gsub!(/\(/, "")
|
176
129
|
xfilename.gsub!(/\)/, "")
|
177
|
-
xfilename = self.to_greeklish(xfilename)
|
130
|
+
# xfilename = self.to_greeklish(xfilename)
|
178
131
|
return xfilename
|
179
132
|
end
|
180
|
-
|
133
|
+
=end
|
134
|
+
|
181
135
|
# Returns a random string. one possible use is
|
182
136
|
# password initialization.
|
183
137
|
#
|
@@ -186,12 +140,12 @@ module StringUtils
|
|
186
140
|
#
|
187
141
|
# === Output:
|
188
142
|
# the random string
|
189
|
-
|
143
|
+
|
190
144
|
def self.random(max_length = 8, char_re = /[\w\d]/)
|
191
145
|
# gmosx: this is a nice example of input parameter checking.
|
192
146
|
# this is NOT a real time called method so we can add this
|
193
147
|
# check. Congrats to the author.
|
194
|
-
raise ArgumentError.new(
|
148
|
+
raise ArgumentError.new('char_re must be a regular expression!') unless char_re.is_a?(Regexp)
|
195
149
|
|
196
150
|
string = ""
|
197
151
|
|
@@ -203,22 +157,6 @@ module StringUtils
|
|
203
157
|
return string
|
204
158
|
end
|
205
159
|
|
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
160
|
end
|
222
161
|
|
223
|
-
end
|
224
|
-
|
162
|
+
end
|