active-orient 0.79 → 0.80
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.graphs.txt.swp +0 -0
- data/Gemfile +2 -6
- data/README.md +29 -27
- data/VERSION +1 -1
- data/active-orient.gemspec +4 -3
- data/bin/active-orient-console +18 -6
- data/changelog.md +60 -0
- data/config/connect.yml +8 -8
- data/examples/books.rb +134 -97
- data/graphs.txt +70 -0
- data/lib/active-orient.rb +2 -0
- data/lib/base.rb +38 -17
- data/lib/base_properties.rb +15 -14
- data/lib/class_utils.rb +11 -50
- data/lib/database_utils.rb +23 -22
- data/lib/init.rb +4 -3
- data/lib/model/custom.rb +7 -4
- data/lib/model/e.rb +6 -0
- data/lib/model/edge.rb +74 -30
- data/lib/model/the_class.rb +181 -131
- data/lib/model/the_record.rb +115 -68
- data/lib/model/vertex.rb +261 -126
- data/lib/other.rb +93 -41
- data/lib/rest/change.rb +23 -20
- data/lib/rest/create.rb +71 -63
- data/lib/rest/delete.rb +80 -64
- data/lib/rest/operations.rb +79 -68
- data/lib/rest/read.rb +42 -24
- data/lib/rest/rest.rb +38 -30
- data/lib/support/conversions.rb +42 -0
- data/lib/support/default_formatter.rb +7 -0
- data/lib/support/errors.rb +41 -0
- data/lib/support/orient.rb +167 -58
- data/lib/support/orientquery.rb +526 -348
- data/lib/support/query.rb +92 -0
- metadata +34 -18
- data/examples/test_commands.rb +0 -97
- data/examples/test_commands_2.rb +0 -59
- data/examples/test_commands_3.rb +0 -55
- data/examples/test_commands_4.rb +0 -33
- data/examples/time_graph.md +0 -162
data/lib/rest/rest.rb
CHANGED
@@ -4,8 +4,9 @@ require_relative "change.rb" # manage update
|
|
4
4
|
require_relative "operations.rb" # manage count, functions and execute
|
5
5
|
require_relative "delete.rb" # manage delete
|
6
6
|
require_relative "../support/logging"
|
7
|
-
require 'cgi'
|
7
|
+
#require 'cgi'
|
8
8
|
require 'rest-client'
|
9
|
+
require 'pond'
|
9
10
|
|
10
11
|
module ActiveOrient
|
11
12
|
|
@@ -63,8 +64,10 @@ Subsequent initialisations are made to initialise namespaced database classes, i
|
|
63
64
|
:port => defaults[:port] ||= 2480,
|
64
65
|
:user => defaults[:user].to_s ,
|
65
66
|
:password => defaults[:password].to_s }
|
66
|
-
|
67
|
-
|
67
|
+
# setup connection pool
|
68
|
+
# database-settings: client.channel.maxPool = 100
|
69
|
+
ActiveOrient.db_pool ||= Pond.new( :maximum_size => 25, :timeout => 50) { get_resource }
|
70
|
+
# ActiveOrient.db_pool.collection = :stack
|
68
71
|
connect()
|
69
72
|
database_classes # initialize @classes-array and ActiveOrient.database_classes
|
70
73
|
ActiveOrient::Base.logger = logger
|
@@ -72,41 +75,46 @@ Subsequent initialisations are made to initialise namespaced database classes, i
|
|
72
75
|
ActiveOrient::Model.db = self
|
73
76
|
ActiveOrient::Model.keep_models_without_file ||= nil
|
74
77
|
preallocate_classes( model_dir ) if preallocate
|
75
|
-
|
78
|
+
Thread.abort_on_exception = true
|
76
79
|
end
|
77
80
|
|
81
|
+
# thread safe method to allocate a resource
|
78
82
|
def get_resource
|
83
|
+
logger.debug {"ALLOCATING NEW RESOURCE --> #{ ActiveOrient.db_pool.size }" }
|
79
84
|
login = [ActiveOrient.default_server[:user] , ActiveOrient.default_server[:password]]
|
80
85
|
server_adress = "http://#{ActiveOrient.default_server[:server]}:#{ActiveOrient.default_server[:port]}"
|
81
|
-
|
86
|
+
RestClient::Resource.new(server_adress, *login)
|
82
87
|
end
|
83
88
|
|
89
|
+
|
90
|
+
|
84
91
|
# Used to connect to the database
|
85
92
|
|
86
|
-
|
87
|
-
|
88
|
-
|
93
|
+
def connect
|
94
|
+
first_tentative = true
|
95
|
+
begin
|
89
96
|
database = ActiveOrient.database
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
97
|
+
logger.progname = 'OrientDB#Connect'
|
98
|
+
r = ActiveOrient.db_pool.checkout do | conn |
|
99
|
+
r = conn["/connect/#{database}"].get
|
100
|
+
end
|
101
|
+
if r.code == 204
|
102
|
+
logger.info{"Connected to database #{database}"}
|
103
|
+
true
|
104
|
+
else
|
105
|
+
logger.error{"Connection to database #{database} could NOT be established"}
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
rescue RestClient::Unauthorized => e
|
109
|
+
if first_tentative
|
110
|
+
logger.info{"Database #{database} NOT present --> creating"}
|
111
|
+
first_tentative = false
|
112
|
+
create_database database: database
|
113
|
+
retry
|
114
|
+
else
|
115
|
+
Kernel.exit
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
112
120
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
=begin
|
2
|
+
Rails-specific stuff
|
3
|
+
|
4
|
+
Mimics ActiveModell::conversions
|
5
|
+
=end
|
6
|
+
module Conversions
|
7
|
+
|
8
|
+
|
9
|
+
=begin
|
10
|
+
Returns an Array of all key attributes if any is set, regardless if the object is persisted or not. Returns nil if there are no key attributes.
|
11
|
+
=end
|
12
|
+
def to_key
|
13
|
+
key = respond_to?(:rid) && rid
|
14
|
+
key ? [key] : nil
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns a +string+ representing the object's key suitable for use in URLs,
|
18
|
+
# # or +nil+ if <tt>persisted?</tt> is +false+.
|
19
|
+
def to_param
|
20
|
+
(persisted? && key = to_key) ? key.join('-') : nil
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# Returns a +string+ identifying the path associated with the object.
|
25
|
+
# # ActionPack uses this to find a suitable partial to represent the object.
|
26
|
+
def to_partial_path
|
27
|
+
self.class._to_partial_path
|
28
|
+
end
|
29
|
+
|
30
|
+
# module ClassMethods #:nodoc:
|
31
|
+
# Provide a class level cache for #to_partial_path. This is an
|
32
|
+
# internal method and should not be accessed directly.
|
33
|
+
|
34
|
+
# def self._to_partial_path #:nodoc:
|
35
|
+
# @_to_partial_path ||= begin
|
36
|
+
# element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
|
37
|
+
# collection = ActiveSupport::Inflector.tableize(name)
|
38
|
+
# "#{collection}/#{element}".freeze
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#end
|
42
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ActiveOrient
|
2
|
+
module Error
|
3
|
+
# Error handling
|
4
|
+
class Error < RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
class ArgumentError < ArgumentError
|
8
|
+
end
|
9
|
+
|
10
|
+
class SymbolError < ArgumentError
|
11
|
+
end
|
12
|
+
|
13
|
+
class LoadError < LoadError
|
14
|
+
end
|
15
|
+
|
16
|
+
class ServerError < RuntimeError
|
17
|
+
end
|
18
|
+
end # module IB
|
19
|
+
end
|
20
|
+
# Patching Object with universally accessible top level error method.
|
21
|
+
# The method is used throughout the lib instead of plainly raising exceptions.
|
22
|
+
# This allows lib user to easily inject user-specific error handling into the lib
|
23
|
+
# by just replacing Object#error method.
|
24
|
+
def error message, type=:standard, backtrace=nil
|
25
|
+
e = case type
|
26
|
+
when :standard
|
27
|
+
ActiveOrientOrient::Error.new message
|
28
|
+
when :args
|
29
|
+
ActiveOrient::ArgumentError.new message
|
30
|
+
when :symbol
|
31
|
+
ActiveOrient::SymbolError.new message
|
32
|
+
when :load
|
33
|
+
AcitveOrient::LoadError.new message
|
34
|
+
when :server
|
35
|
+
ActiveOrient::Error::ServerError.new message
|
36
|
+
end
|
37
|
+
e.set_backtrace(backtrace) if backtrace
|
38
|
+
raise e
|
39
|
+
end
|
40
|
+
|
41
|
+
# resued from https://github.com/ib-ruby/ib-ruby
|
data/lib/support/orient.rb
CHANGED
@@ -6,8 +6,23 @@ module OrientSupport
|
|
6
6
|
# of ActiveOrient
|
7
7
|
#
|
8
8
|
# The Database-Class is available through Array#record
|
9
|
-
|
10
|
-
|
9
|
+
#
|
10
|
+
# *caution:*
|
11
|
+
# Don't mix ActiveOrient::Array's with conventional ones
|
12
|
+
# > t= G21.first
|
13
|
+
# > t.ll
|
14
|
+
# => ["test", "test_2", 5, 8, 7988, "uzg"]
|
15
|
+
# > t.ll = [9,6,7] # This is an assignment of an Array to the variable »ll»
|
16
|
+
# # It does NOT call ActiveOrient::Array#=[].
|
17
|
+
# => [9, 6, 7] # Instead an Array is assigned to the variable »ll»
|
18
|
+
#
|
19
|
+
# it is only updated localy, as shown if we reload the document
|
20
|
+
# > t= G21.first.attributes
|
21
|
+
# => {:ll=>["test", "test_2", 5, 8, 7988, "uzg"]}
|
22
|
+
#
|
23
|
+
# Thus its imperativ to safe the changes made.
|
24
|
+
|
25
|
+
|
11
26
|
class Array < Array
|
12
27
|
include OrientSupport::Support
|
13
28
|
|
@@ -33,7 +48,7 @@ module OrientSupport
|
|
33
48
|
@orient = work_on.class == Class ? work_on.new : work_on
|
34
49
|
super work_with
|
35
50
|
begin
|
36
|
-
@name = @orient.attributes.key(self)
|
51
|
+
@name = block_given? ? yield : @orient.attributes.key(self)
|
37
52
|
rescue TypeError => e # not defined
|
38
53
|
ActiveOrient::Base.logger.debug{ "--------------------Type Error ----------------------------------" }
|
39
54
|
ActiveOrient::Base.logger.debug("OrientSupport::Array"){ "Attributes #{@orient.attributes.inspect}" }
|
@@ -47,7 +62,6 @@ module OrientSupport
|
|
47
62
|
ActiveOrient::Base.logger.debug{ "due to a bug in ActiveSupport DateTime Calculations" }
|
48
63
|
# we just ignore the error
|
49
64
|
end
|
50
|
-
@name = yield if @name.nil? && block_given?
|
51
65
|
end
|
52
66
|
def as_json o=nil
|
53
67
|
map{|x| x.rid? ? x.rid : x }
|
@@ -60,40 +74,115 @@ module OrientSupport
|
|
60
74
|
def to_human
|
61
75
|
map &:to_human
|
62
76
|
end
|
63
|
-
|
77
|
+
=begin
|
78
|
+
|
79
|
+
Appends the arguments to the Array.
|
80
|
+
|
81
|
+
Returns the modified database-document (not the array !!)
|
82
|
+
=end
|
83
|
+
def append *arg
|
84
|
+
|
85
|
+
@orient.update { "set #{@name.to_s} = #{@name} || #{arg.to_or} "}[@name] if check_if_complete
|
86
|
+
@orient.reload!
|
87
|
+
end
|
88
|
+
=begin
|
89
|
+
Append the argument to the Array, changes the Array itself.
|
90
|
+
|
91
|
+
Returns the modified Array ( and is chainable )
|
64
92
|
#
|
65
93
|
# i= V.get( '89:0')
|
66
94
|
# ii=i.zwoebelkuchen << 'z78' << 6 << [454, 787]
|
67
95
|
# => [7, 5, 6, "z78", 78, 45, "z78", 6, 454, 787]
|
68
|
-
=begin
|
69
|
-
Append the argument to the Array, changes the Array itself.
|
70
96
|
|
71
97
|
The change is immediately transmitted to the database.
|
72
98
|
|
99
|
+
The difference to `append`: that method accepts a komma separated list of arguments
|
100
|
+
and returns the modified database-document. `<<` accepts only one argument. An
|
101
|
+
Array is translated into multi-arguments of `append`
|
73
102
|
|
74
|
-
=
|
75
|
-
|
103
|
+
> t = G21.create ll: ['test','test_2', 5, 8 , 7988, "uzg"]
|
104
|
+
INFO->CREATE VERTEX ml_g21 CONTENT {"ll":["test","test_2",5,8,7988,"uzg"]}
|
105
|
+
=> #<ML::G21:0x0000000002622cb0 @metadata={:type=>"d", :class=>"ml_g21", :version=>1,
|
106
|
+
:fieldTypes=>nil, :cluster=>271, :record=>0},
|
107
|
+
@attributes={:ll=>["test", "test_2", 5, 8, 7988, "uzg"]}>
|
108
|
+
> t.ll << [9,10]
|
109
|
+
INFO->update #271:0 set ll = ll || [9, 10] return after @this
|
110
|
+
=> ["test", "test_2", 5, 8, 7988, "uzg"]
|
111
|
+
> t.ll << [9,10] << 'u'
|
112
|
+
INFO->update #271:0 set ll = ll || [9, 10] return after @this
|
113
|
+
INFO->update #271:0 set ll = ll || ['u'] return after @this
|
114
|
+
=> ["test", "test_2", 5, 8, 7988, "uzg", 9, 10]
|
115
|
+
|
116
|
+
|
117
|
+
The Array can be treated separately
|
118
|
+
|
119
|
+
> z = t.ll
|
120
|
+
=> ["test", "test_2", 5, 8, 7988, "uzg"]
|
121
|
+
> z << 78
|
122
|
+
INFO->update #272:0 set ll = ll || [78] return after @this
|
123
|
+
=> ["test", "test_2", 5, 8, 7988, "uzg", 78]
|
76
124
|
|
77
|
-
|
125
|
+
=end
|
126
|
+
def << arg
|
127
|
+
append( *arg).send @name
|
78
128
|
end
|
79
129
|
|
80
|
-
|
130
|
+
=begin
|
131
|
+
|
132
|
+
Removes the specified list entries from the Array
|
133
|
+
|
134
|
+
Returns the modified Array (and is chainable).
|
81
135
|
|
136
|
+
> t= G21.first
|
137
|
+
> t.ll
|
138
|
+
=> ["test", "test_2", 7988, "uzg", 6789, "xvy"]
|
139
|
+
> u= t.ll << 'xvz'
|
140
|
+
# INFO->update #272:0 set ll = ll || ['xvz'] return after @this
|
141
|
+
=> ["test", "test_2", 7988, "uzg", 6789, "xvy", "xvz"]
|
142
|
+
> z= u.remove 'xvy'
|
143
|
+
# INFO->update #272:0 remove ll = 'xvy' return after @this
|
144
|
+
=> ["test", "test_2", 7988, "uzg", 6789, "xvz"]
|
145
|
+
|
146
|
+
The ModelInstance is updated, too, as shown by calling
|
147
|
+
|
148
|
+
> t.ll
|
149
|
+
=> ["test", "test_2", 7988, "uzg", 6789, "xvz"]
|
150
|
+
|
151
|
+
|
152
|
+
Thus
|
153
|
+
|
154
|
+
> t.ll.remove 7988
|
155
|
+
# INFO->update #272:0 remove ll = 7988 return after @this
|
156
|
+
=> ["test", "test_2", "uzg", 6789, "xvz"]
|
157
|
+
|
158
|
+
returns thea modified Array
|
159
|
+
=end
|
82
160
|
def remove *k
|
83
161
|
# todo combine queries in a transaction
|
84
|
-
|
85
|
-
|
162
|
+
ActiveOrient::Base.logger.debug { "delete: #{@name} --< #{k.map(&:to_or).join( ' :: ' )}"}
|
163
|
+
k.map{|l| @orient.update( {remove: { @name => l} } ) }
|
164
|
+
# @orient.reload!
|
165
|
+
# @orient.send @name
|
86
166
|
end
|
87
167
|
|
168
|
+
def remove_by_index index
|
169
|
+
@orient.update( { remove: { @name => "#{@name[index]}" } } )
|
170
|
+
end
|
88
171
|
|
89
|
-
|
172
|
+
def check_if_complete
|
173
|
+
if @name.blank?
|
174
|
+
@orient.logger.warn{ "Database is uneffected. Operation is incomplete/ not allowed" }
|
175
|
+
false
|
176
|
+
else
|
177
|
+
true
|
178
|
+
end
|
179
|
+
end
|
90
180
|
=begin
|
91
181
|
Updating of single items
|
92
182
|
=end
|
93
|
-
|
94
183
|
def []= key, value
|
95
184
|
super
|
96
|
-
@orient.update set: {@name => self} if @name.present?
|
185
|
+
@orient.update set: {@name => self} if @name.present? if check_if_complete
|
97
186
|
end
|
98
187
|
|
99
188
|
|
@@ -103,17 +192,23 @@ The change is immediately transmitted to the database.
|
|
103
192
|
where_string = item.map{|m| where_string = compose_where( m ) }.join(' and ')
|
104
193
|
subquery= OrientSupport::OrientQuery.new from: @orient, projection: "expand( #{@name})"
|
105
194
|
q= OrientSupport::OrientQuery.new from: subquery, where: item
|
106
|
-
@orient.
|
195
|
+
@orient.db.execute{ q.to_s } if check_if_complete
|
107
196
|
|
108
197
|
end
|
109
198
|
|
110
|
-
def method_missing *args
|
111
|
-
|
112
|
-
|
199
|
+
def method_missing method, *args
|
200
|
+
return if empty?
|
201
|
+
if @orient.is_a? ActiveOrient::Model # IB::Model
|
202
|
+
# delegate to public methods
|
203
|
+
self.map{|x| x.public_send(method, *args)}
|
204
|
+
else
|
205
|
+
self.map{|x| x.send method, *args }
|
206
|
+
end
|
113
207
|
rescue NoMethodError => e
|
114
|
-
ActiveOrient::Base.logger.error("OrientSupport::Array"){ "MethodMissing -> Undefined method: #{args.first} -- Args: #{args[1..-1].inspect}"}
|
208
|
+
ActiveOrient::Base.logger.error("OrientSupport::Array"){ "#{self.inspect} MethodMissing -> Undefined method: #{args.first} -- Args: #{args[1..-1].inspect}"}
|
115
209
|
ActiveOrient::Base.logger.error {" The Message #{e.message}"}
|
116
210
|
ActiveOrient::Base.logger.error{ e.backtrace.map {|l| " #{l}\n"}.join }
|
211
|
+
raise
|
117
212
|
end
|
118
213
|
|
119
214
|
end #Class
|
@@ -125,49 +220,51 @@ The change is immediately transmitted to the database.
|
|
125
220
|
include OrientSupport::Support
|
126
221
|
def initialize modelinstance, args
|
127
222
|
super()
|
128
|
-
# puts "Hash.new args #{args}"
|
129
223
|
@orient = modelinstance
|
130
224
|
self.merge! args
|
131
|
-
@name = modelinstance.attributes.key(self)
|
132
|
-
@name = yield if @name.nil? && block_given?
|
133
|
-
# puts "@name #{@name}"
|
225
|
+
@name = block_given? ? yield : modelinstance.attributes.key(self)
|
134
226
|
self
|
135
227
|
end
|
136
228
|
|
137
|
-
|
138
|
-
|
139
|
-
|
229
|
+
def store k, v
|
230
|
+
@orient.update { "set #{@name}[#{k.to_s.to_or}] = #{v.to_or} "}[@name] #if check_if_complete
|
231
|
+
@orient.reload!
|
140
232
|
end
|
141
233
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
234
|
+
alias []= store
|
235
|
+
|
236
|
+
|
237
|
+
# Inserts the provided Hash to the (possibly empty) list-property and updates the dataset
|
238
|
+
#
|
239
|
+
# Keys are translated to symbols
|
240
|
+
#
|
241
|
+
def merge **arg
|
242
|
+
@orient.update @name => super(**arg)
|
243
|
+
@orient.reload!
|
148
244
|
end
|
149
245
|
|
150
|
-
alias <<
|
246
|
+
alias << merge
|
151
247
|
|
152
248
|
# removes a key-value entry from the hash.
|
153
249
|
#
|
154
|
-
# parameter: list of key's
|
250
|
+
# parameter: list of key's
|
251
|
+
#
|
252
|
+
# returns the modified OrientSupport::Hash
|
253
|
+
#
|
254
|
+
# ie, given
|
255
|
+
# b => <Base[51:0]: < Base: 51:0 >, a_set : {:warrant_value=>["8789", "HKD"], :what_if_pm_enabled=>["true", ""], :t_bill_value=>["0", "HKD"]}>
|
256
|
+
# c= b.a_set.remove :warrant_value
|
257
|
+
# INFO->update #51:0 remove a_set = 'warrant_value' return after $current
|
258
|
+
# c => {:what_if_pm_enabled=>["true", ""], :t_bill_value=>["0", "HKD"]}
|
259
|
+
|
260
|
+
|
155
261
|
def remove *k
|
156
262
|
# todo combine queries in a transaction
|
157
|
-
|
263
|
+
|
264
|
+
r= k.map{|key| @orient.update{ "remove #{@name} = #{key.to_s.to_or} " } }
|
265
|
+
@orient.reload!.send @name
|
266
|
+
|
158
267
|
end
|
159
|
-
# def delete *key
|
160
|
-
#
|
161
|
-
# key.each do | k |
|
162
|
-
# o = OrientSupport::OrientQuery.new from: @orient,
|
163
|
-
# kind: 'update',
|
164
|
-
# set: "#{@name}.#{k.to_s}",
|
165
|
-
# return: "$current.#{@name}"
|
166
|
-
# @orient.db.execute{ o.to_s.gsub( 'set ', 'remove ' ) }.first.send( @name ) # extracts the modified array (from DB) from the result
|
167
|
-
# end
|
168
|
-
# @orient.reload!
|
169
|
-
# @orient.send @name # return value
|
170
|
-
# end
|
171
268
|
|
172
269
|
def delete_if &b
|
173
270
|
super &b
|
@@ -175,7 +272,19 @@ The change is immediately transmitted to the database.
|
|
175
272
|
|
176
273
|
end
|
177
274
|
|
178
|
-
|
275
|
+
# slice returns a subset of the hash
|
276
|
+
#
|
277
|
+
# excepts a regular expression as well
|
278
|
+
def slice arg
|
279
|
+
if arg.is_a? Regexp
|
280
|
+
find_all{ |key| key.to_s.match(arg) }.to_h
|
281
|
+
else
|
282
|
+
super arg.to_sym
|
283
|
+
end
|
284
|
+
end
|
285
|
+
def [] arg
|
286
|
+
super
|
287
|
+
end
|
179
288
|
end
|
180
289
|
end #Module
|
181
290
|
|
@@ -185,12 +294,12 @@ class Hash
|
|
185
294
|
"{ " + self.map{ |k,v| [k.to_s,": ", v.to_orient].join }.join(', ') + " }"
|
186
295
|
end
|
187
296
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
end
|
195
|
-
end
|
297
|
+
# def coerce arg
|
298
|
+
# if arg.is_a? DateTime
|
299
|
+
# nil
|
300
|
+
# else
|
301
|
+
# super
|
302
|
+
#
|
303
|
+
# end
|
304
|
+
# end
|
196
305
|
end
|