opentox-ruby 0.0.1 → 0.0.2
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/README.rdoc +4 -4
- data/Rakefile +35 -35
- data/VERSION +1 -1
- data/lib/algorithm.rb +220 -50
- data/lib/compound.rb +138 -73
- data/lib/dataset.rb +296 -192
- data/lib/environment.rb +44 -29
- data/lib/feature.rb +15 -0
- data/lib/model.rb +240 -112
- data/lib/opentox-ruby.rb +13 -0
- data/lib/opentox.rb +47 -0
- data/lib/overwrite.rb +72 -0
- data/lib/parser.rb +286 -0
- data/lib/rest_client_wrapper.rb +12 -12
- data/lib/serializer.rb +340 -0
- data/lib/task.rb +184 -101
- data/lib/validation.rb +58 -8
- metadata +41 -22
data/lib/model.rb
CHANGED
@@ -1,143 +1,271 @@
|
|
1
1
|
module OpenTox
|
2
|
-
module Model
|
3
2
|
|
4
|
-
|
3
|
+
module Model
|
5
4
|
|
6
|
-
|
7
|
-
MODEL_ATTRIBS.each{ |a| attr_accessor(a) }
|
5
|
+
include OpenTox
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
# Run a model with parameters
|
8
|
+
# @param [Hash] params Parameters for OpenTox model
|
9
|
+
# @return [text/uri-list] Task or resource URI
|
10
|
+
def run(params)
|
11
|
+
if CONFIG[:yaml_hosts].include?(URI.parse(@uri).host)
|
12
|
+
accept = 'application/x-yaml'
|
13
|
+
else
|
14
|
+
accept = 'application/rdf+xml'
|
12
15
|
end
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
owl.rdf
|
20
|
-
end
|
21
|
-
|
22
|
-
protected
|
23
|
-
def initialize(owl)
|
24
|
-
MODEL_ATTRIBS.each do |a|
|
25
|
-
self.send("#{a.to_s}=".to_sym, owl.get(a.to_s)) unless a==:uri
|
26
|
-
end
|
27
|
-
@uri = owl.uri
|
28
|
-
if ENV['RACK_ENV'] =~ /test|debug/
|
29
|
-
begin
|
30
|
-
raise "uri invalid" unless Utils.is_uri?(@uri)
|
31
|
-
raise "no predicted variables" unless @predictedVariables and @predictedVariables.size>0
|
32
|
-
rescue => ex
|
33
|
-
RestClientWrapper.raise_uri_error "invalid model: '"+ex.message+"'\n"+self.to_yaml+"\n",@uri.to_s
|
34
|
-
end
|
35
|
-
LOGGER.warn "model has no dependent variable" unless @dependentVariables and @dependentVariables.size>0
|
36
|
-
LOGGER.warn "model has no algorithm" unless @algorithm and @algorithm.size>0
|
37
|
-
LOGGER.warn "model has no indenpendent variables" unless @independentVariables
|
38
|
-
end
|
16
|
+
begin
|
17
|
+
RestClientWrapper.post(@uri,{:accept => accept},params).to_s
|
18
|
+
rescue => e
|
19
|
+
LOGGER.error "Failed to run #{@uri} with #{params.inspect} (#{e.inspect})"
|
20
|
+
raise "Failed to run #{@uri} with #{params.inspect}"
|
39
21
|
end
|
40
22
|
end
|
41
|
-
|
42
|
-
class
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
LOGGER.debug "Build model, algorithm_uri:"+algorithm_uri.to_s+", algorithm_parms: "+algorithm_params.inspect.to_s
|
47
|
-
uri = OpenTox::RestClientWrapper.post(algorithm_uri,algorithm_params).to_s
|
48
|
-
LOGGER.debug "Build model done: "+uri.to_s
|
49
|
-
RestClientWrapper.raise_uri_error("Invalid build model result: '"+uri.to_s+"'", algorithm_uri, algorithm_params ) unless Utils.model_uri?(uri)
|
50
|
-
return PredictionModel.find(uri)
|
51
|
-
end
|
52
|
-
|
53
|
-
def predict_dataset( dataset_uri )
|
54
|
-
|
55
|
-
LOGGER.debug "Predict dataset: "+dataset_uri.to_s+" with model "+@uri.to_s
|
56
|
-
uri = RestClientWrapper.post(@uri, {:accept => "text/uri-list", :dataset_uri=>dataset_uri})
|
57
|
-
RestClientWrapper.raise_uri_error("Prediciton result no dataset uri: "+uri.to_s, @uri, {:dataset_uri=>dataset_uri} ) unless Utils.dataset_uri?(uri)
|
58
|
-
uri
|
59
|
-
end
|
60
|
-
|
61
|
-
def classification?
|
62
|
-
#HACK replace with request to ontology server
|
63
|
-
if @title =~ /(?i)classification/
|
64
|
-
return true
|
65
|
-
elsif @title =~ /(?i)regression/
|
66
|
-
return false
|
67
|
-
elsif @uri =~/ntua/ and @title =~ /mlr/
|
68
|
-
return false
|
69
|
-
elsif @uri =~/tu-muenchen/ and @title =~ /regression|M5P|GaussP/
|
70
|
-
return false
|
71
|
-
elsif @uri =~/ambit2/ and @title =~ /pKa/ || @title =~ /Regression|Caco/
|
72
|
-
return false
|
73
|
-
elsif @uri =~/majority/
|
74
|
-
return (@uri =~ /class/) != nil
|
75
|
-
else
|
76
|
-
raise "unknown model, uri:'"+@uri.to_s+"' title:'"+@title.to_s+"'"
|
77
|
-
end
|
78
|
-
end
|
23
|
+
|
24
|
+
# Generic OpenTox model class for all API compliant services
|
25
|
+
class Generic
|
26
|
+
include Model
|
79
27
|
end
|
80
28
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
29
|
+
# Lazy Structure Activity Relationship class
|
30
|
+
class Lazar
|
31
|
+
|
32
|
+
include Model
|
33
|
+
include Algorithm
|
34
|
+
|
35
|
+
attr_accessor :compound, :prediction_dataset, :features, :effects, :activities, :p_values, :fingerprints, :feature_calculation_algorithm, :similarity_algorithm, :prediction_algorithm, :min_sim
|
36
|
+
|
37
|
+
def initialize(uri=nil)
|
38
|
+
|
39
|
+
if uri
|
40
|
+
super uri
|
41
|
+
else
|
42
|
+
super CONFIG[:services]["opentox-model"]
|
43
|
+
end
|
44
|
+
|
45
|
+
@metadata[OT.algorithm] = File.join(CONFIG[:services]["opentox-algorithm"],"lazar")
|
46
|
+
|
89
47
|
@features = []
|
90
48
|
@effects = {}
|
91
49
|
@activities = {}
|
92
50
|
@p_values = {}
|
93
51
|
@fingerprints = {}
|
52
|
+
|
53
|
+
@feature_calculation_algorithm = "Substructure.match"
|
54
|
+
@similarity_algorithm = "Similarity.tanimoto"
|
55
|
+
@prediction_algorithm = "Neighbors.weighted_majority_vote"
|
56
|
+
|
57
|
+
@min_sim = 0.3
|
58
|
+
|
94
59
|
end
|
95
60
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
61
|
+
# Get URIs of all lazar models
|
62
|
+
# @return [Array] List of lazar model URIs
|
63
|
+
def self.all
|
64
|
+
RestClientWrapper.get(CONFIG[:services]["opentox-model"]).to_s.split("\n")
|
100
65
|
end
|
101
66
|
|
102
|
-
|
103
|
-
|
67
|
+
# Find a lazar model
|
68
|
+
# @param [String] uri Model URI
|
69
|
+
# @return [OpenTox::Model::Lazar] lazar model
|
70
|
+
def self.find(uri)
|
71
|
+
YAML.load RestClientWrapper.get(uri,:accept => 'application/x-yaml')
|
104
72
|
end
|
105
73
|
|
106
|
-
|
107
|
-
|
108
|
-
|
74
|
+
# Create a new lazar model
|
75
|
+
# @param [optional,Hash] params Parameters for the lazar algorithm (OpenTox::Algorithm::Lazar)
|
76
|
+
# @return [OpenTox::Model::Lazar] lazar model
|
77
|
+
def self.create(params)
|
78
|
+
lazar_algorithm = OpenTox::Algorithm::Generic.new File.join( CONFIG[:services]["opentox-algorithm"],"lazar")
|
79
|
+
model_uri = lazar_algorithm.run(params)
|
80
|
+
OpenTox::Model::Lazar.find(model_uri)
|
109
81
|
end
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
def initialize
|
117
|
-
@source = "http://github.com/helma/opentox-model"
|
118
|
-
@algorithm = File.join(@@config[:services]["opentox-algorithm"],"property_lazar")
|
119
|
-
#@independent_variables = File.join(@@config[:services]["opentox-algorithm"],"fminer#BBRC_representative")
|
120
|
-
@features = []
|
121
|
-
#@effects = {}
|
122
|
-
@activities = {}
|
123
|
-
#@p_values = {}
|
124
|
-
@properties = {}
|
82
|
+
|
83
|
+
# Get a parameter value
|
84
|
+
# @param [String] param Parameter name
|
85
|
+
# @return [String] Parameter value
|
86
|
+
def parameter(param)
|
87
|
+
@metadata[OT.parameters].collect{|p| p[OT.paramValue] if p[DC.title] == param}.compact.first
|
125
88
|
end
|
126
89
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
90
|
+
# Predict a dataset
|
91
|
+
# @param [String] dataset_uri Dataset URI
|
92
|
+
# @return [OpenTox::Dataset] Dataset with predictions
|
93
|
+
def predict_dataset(dataset_uri)
|
94
|
+
@prediction_dataset = Dataset.create
|
95
|
+
@prediction_dataset.add_metadata({
|
96
|
+
OT.hasSource => @uri,
|
97
|
+
DC.creator => @uri,
|
98
|
+
DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
|
99
|
+
OT.parameters => [{DC.title => "dataset_uri", OT.paramValue => dataset_uri}]
|
100
|
+
})
|
101
|
+
d = Dataset.new(dataset_uri)
|
102
|
+
d.load_compounds
|
103
|
+
d.compounds.each do |compound_uri|
|
104
|
+
predict(compound_uri,false)
|
105
|
+
end
|
106
|
+
@prediction_dataset.save
|
107
|
+
@prediction_dataset
|
131
108
|
end
|
132
109
|
|
133
|
-
|
134
|
-
|
110
|
+
# Predict a compound
|
111
|
+
# @param [String] compound_uri Compound URI
|
112
|
+
# @param [optinal,Boolean] verbose Verbose prediction (output includes neighbors and features)
|
113
|
+
# @return [OpenTox::Dataset] Dataset with prediction
|
114
|
+
def predict(compound_uri,verbose=false)
|
115
|
+
|
116
|
+
@compound = Compound.new compound_uri
|
117
|
+
features = {}
|
118
|
+
|
119
|
+
unless @prediction_dataset
|
120
|
+
#@prediction_dataset = cached_prediction
|
121
|
+
#return @prediction_dataset if cached_prediction
|
122
|
+
@prediction_dataset = Dataset.create
|
123
|
+
@prediction_dataset.add_metadata( {
|
124
|
+
OT.hasSource => @uri,
|
125
|
+
DC.creator => @uri,
|
126
|
+
# TODO: fix dependentVariable
|
127
|
+
DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
|
128
|
+
OT.parameters => [{DC.title => "compound_uri", OT.paramValue => compound_uri}]
|
129
|
+
} )
|
130
|
+
end
|
131
|
+
|
132
|
+
return @prediction_dataset if database_activity
|
133
|
+
|
134
|
+
neighbors
|
135
|
+
prediction = eval("#{@prediction_algorithm}(@neighbors,{:similarity_algorithm => @similarity_algorithm, :p_values => @p_values})")
|
136
|
+
|
137
|
+
prediction_feature_uri = File.join( @prediction_dataset.uri, "feature", "prediction", File.basename(@metadata[OT.dependentVariables]),@prediction_dataset.compounds.size.to_s)
|
138
|
+
# TODO: fix dependentVariable
|
139
|
+
@prediction_dataset.metadata[OT.dependentVariables] = prediction_feature_uri
|
140
|
+
|
141
|
+
if @neighbors.size == 0
|
142
|
+
@prediction_dataset.add_feature(prediction_feature_uri, {
|
143
|
+
OT.isA => OT.MeasuredFeature,
|
144
|
+
OT.hasSource => @uri,
|
145
|
+
DC.creator => @uri,
|
146
|
+
DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
|
147
|
+
OT.error => "No similar compounds in training dataset.",
|
148
|
+
OT.parameters => [{DC.title => "compound_uri", OT.paramValue => compound_uri}]
|
149
|
+
})
|
150
|
+
@prediction_dataset.add @compound.uri, prediction_feature_uri, prediction[:prediction]
|
151
|
+
|
152
|
+
else
|
153
|
+
@prediction_dataset.add_feature(prediction_feature_uri, {
|
154
|
+
OT.isA => OT.ModelPrediction,
|
155
|
+
OT.hasSource => @uri,
|
156
|
+
DC.creator => @uri,
|
157
|
+
DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
|
158
|
+
OT.prediction => prediction[:prediction],
|
159
|
+
OT.confidence => prediction[:confidence],
|
160
|
+
OT.parameters => [{DC.title => "compound_uri", OT.paramValue => compound_uri}]
|
161
|
+
})
|
162
|
+
@prediction_dataset.add @compound.uri, prediction_feature_uri, prediction[:prediction]
|
163
|
+
|
164
|
+
if verbose
|
165
|
+
if @feature_calculation_algorithm == "Substructure.match"
|
166
|
+
f = 0
|
167
|
+
@compound_features.each do |feature|
|
168
|
+
feature_uri = File.join( @prediction_dataset.uri, "feature", "descriptor", f.to_s)
|
169
|
+
features[feature] = feature_uri
|
170
|
+
@prediction_dataset.add_feature(feature_uri, {
|
171
|
+
OT.isA => OT.Substructure,
|
172
|
+
OT.smarts => feature,
|
173
|
+
OT.pValue => @p_values[feature],
|
174
|
+
OT.effect => @effects[feature]
|
175
|
+
})
|
176
|
+
@prediction_dataset.add @compound.uri, feature_uri, true
|
177
|
+
f+=1
|
178
|
+
end
|
179
|
+
else
|
180
|
+
@compound_features.each do |feature|
|
181
|
+
features[feature] = feature
|
182
|
+
@prediction_dataset.add @compound.uri, feature, true
|
183
|
+
end
|
184
|
+
end
|
185
|
+
n = 0
|
186
|
+
@neighbors.each do |neighbor|
|
187
|
+
neighbor_uri = File.join( @prediction_dataset.uri, "feature", "neighbor", n.to_s )
|
188
|
+
@prediction_dataset.add_feature(neighbor_uri, {
|
189
|
+
OT.compound => neighbor[:compound],
|
190
|
+
OT.similarity => neighbor[:similarity],
|
191
|
+
OT.measuredActivity => neighbor[:activity],
|
192
|
+
OT.isA => OT.Neighbor
|
193
|
+
})
|
194
|
+
@prediction_dataset.add @compound.uri, neighbor_uri, true
|
195
|
+
f = 0 unless f
|
196
|
+
neighbor[:features].each do |feature|
|
197
|
+
if @feature_calculation_algorithm == "Substructure.match"
|
198
|
+
feature_uri = File.join( @prediction_dataset.uri, "feature", "descriptor", f.to_s) unless feature_uri = features[feature]
|
199
|
+
else
|
200
|
+
feature_uri = feature
|
201
|
+
end
|
202
|
+
@prediction_dataset.add neighbor[:compound], feature_uri, true
|
203
|
+
unless features.has_key? feature
|
204
|
+
features[feature] = feature_uri
|
205
|
+
@prediction_dataset.add_feature(feature_uri, {
|
206
|
+
OT.isA => OT.Substructure,
|
207
|
+
OT.smarts => feature,
|
208
|
+
OT.pValue => @p_values[feature],
|
209
|
+
OT.effect => @effects[feature]
|
210
|
+
})
|
211
|
+
f+=1
|
212
|
+
end
|
213
|
+
end
|
214
|
+
n+=1
|
215
|
+
end
|
216
|
+
# what happens with dataset predictions?
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
@prediction_dataset.save
|
221
|
+
@prediction_dataset
|
135
222
|
end
|
136
223
|
|
137
|
-
|
138
|
-
|
139
|
-
|
224
|
+
# Find neighbors and store them as object variable
|
225
|
+
def neighbors
|
226
|
+
|
227
|
+
@compound_features = eval("#{@feature_calculation_algorithm}(@compound,@features)") if @feature_calculation_algorithm
|
228
|
+
|
229
|
+
@neighbors = []
|
230
|
+
@fingerprints.each do |training_compound,training_features|
|
231
|
+
sim = eval("#{@similarity_algorithm}(@compound_features,training_features,@p_values)")
|
232
|
+
if sim > @min_sim
|
233
|
+
@activities[training_compound].each do |act|
|
234
|
+
@neighbors << {
|
235
|
+
:compound => training_compound,
|
236
|
+
:similarity => sim,
|
237
|
+
:features => training_features,
|
238
|
+
:activity => act
|
239
|
+
}
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
140
244
|
end
|
245
|
+
|
246
|
+
# Find database activities and store them in @prediction_dataset
|
247
|
+
# @return [Boolean] true if compound has databasse activities, false if not
|
248
|
+
def database_activity
|
249
|
+
if @activities[@compound.uri]
|
250
|
+
@activities[@compound.uri].each { |act| @prediction_dataset.add @compound.uri, @metadata[OT.dependentVariables], act }
|
251
|
+
@prediction_dataset.add_metadata(OT.hasSource => @metadata[OT.trainingDataset])
|
252
|
+
@prediction_dataset.save
|
253
|
+
true
|
254
|
+
else
|
255
|
+
false
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# Save model at model service
|
260
|
+
def save
|
261
|
+
self.uri = RestClientWrapper.post(@uri,{:content_type => "application/x-yaml"},self.to_yaml)
|
262
|
+
end
|
263
|
+
|
264
|
+
# Delete model at model service
|
265
|
+
def delete
|
266
|
+
RestClientWrapper.delete @uri unless @uri == CONFIG[:services]["opentox-model"]
|
267
|
+
end
|
268
|
+
|
141
269
|
end
|
142
270
|
end
|
143
271
|
end
|
data/lib/opentox-ruby.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
['rubygems', 'sinatra', 'sinatra/url_for', 'rest_client', 'yaml', 'cgi', 'spork', 'overwrite', 'environment'].each do |lib|
|
2
|
+
require lib
|
3
|
+
end
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'openbabel'
|
7
|
+
rescue LoadError
|
8
|
+
puts "Please install Openbabel with 'rake openbabel:install' in the compound component"
|
9
|
+
end
|
10
|
+
|
11
|
+
['opentox', 'compound','dataset', 'parser','serializer', 'algorithm','model','task','validation','feature', 'rest_client_wrapper'].each do |lib|
|
12
|
+
require lib
|
13
|
+
end
|
data/lib/opentox.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module OpenTox
|
2
|
+
|
3
|
+
attr_reader :uri
|
4
|
+
attr_accessor :metadata
|
5
|
+
|
6
|
+
# Initialize OpenTox object with optional uri
|
7
|
+
# @param [optional, String] URI
|
8
|
+
def initialize(uri=nil)
|
9
|
+
@metadata = {}
|
10
|
+
self.uri = uri if uri
|
11
|
+
end
|
12
|
+
|
13
|
+
# Set URI
|
14
|
+
# @param [String] URI
|
15
|
+
def uri=(uri)
|
16
|
+
@uri = uri
|
17
|
+
@metadata[XSD.anyURI] = uri
|
18
|
+
end
|
19
|
+
|
20
|
+
# Get all objects from a service
|
21
|
+
# @return [Array] List of available URIs
|
22
|
+
def self.all(uri)
|
23
|
+
RestClientWrapper.get(uri,:accept => "text/uri-list").to_s.split(/\n/)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Load (and return) metadata from object URI
|
27
|
+
# @return [Hash] Metadata
|
28
|
+
def load_metadata
|
29
|
+
@metadata = Parser::Owl::Generic.new(@uri).load_metadata
|
30
|
+
@metadata
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_metadata(metadata)
|
34
|
+
metadata.each { |k,v| @metadata[k] = v }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get OWL-DL representation in RDF/XML format
|
38
|
+
# @return [application/rdf+xml] RDF/XML representation
|
39
|
+
def to_rdfxml
|
40
|
+
s = Serializer::Owl.new
|
41
|
+
s.add_metadata(@uri,@metadata)
|
42
|
+
#s.add_parameters(@uri,@parameters) if @parameters
|
43
|
+
s.to_rdfxml
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
data/lib/overwrite.rb
CHANGED
@@ -12,3 +12,75 @@ class Sinatra::Base
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
class String
|
16
|
+
def task_uri?
|
17
|
+
self.uri? && !self.match(/task/).nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
def dataset_uri?
|
21
|
+
self.uri? && !self.match(/dataset/).nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.model_uri?
|
25
|
+
self.uri? && !self.match(/model/).nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def uri?
|
29
|
+
begin
|
30
|
+
u = URI::parse(self)
|
31
|
+
return (u.scheme!=nil and u.host!=nil)
|
32
|
+
rescue URI::InvalidURIError
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
require 'logger'
|
39
|
+
# logging
|
40
|
+
#class Logger
|
41
|
+
class OTLogger < Logger
|
42
|
+
|
43
|
+
def pwd
|
44
|
+
path = Dir.pwd.to_s
|
45
|
+
index = path.rindex(/\//)
|
46
|
+
return path if index==nil
|
47
|
+
path[(index+1)..-1]
|
48
|
+
end
|
49
|
+
|
50
|
+
def trace()
|
51
|
+
lines = caller(0)
|
52
|
+
n = 2
|
53
|
+
line = lines[n]
|
54
|
+
|
55
|
+
while (line =~ /spork.rb/ or line =~ /create/ or line =~ /ot-logger.rb/)
|
56
|
+
n += 1
|
57
|
+
line = lines[n]
|
58
|
+
end
|
59
|
+
|
60
|
+
index = line.rindex(/\/.*\.rb/)
|
61
|
+
return line if index==nil
|
62
|
+
line[index..-1]
|
63
|
+
end
|
64
|
+
|
65
|
+
def format(msg)
|
66
|
+
pwd.ljust(18)+" :: "+msg.to_s+" :: "+trace+" :: "+($sinatra ? $sinatra.request.env['REMOTE_ADDR'] : nil).to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
def debug(msg)
|
70
|
+
super format(msg)
|
71
|
+
end
|
72
|
+
|
73
|
+
def info(msg)
|
74
|
+
super format(msg)
|
75
|
+
end
|
76
|
+
|
77
|
+
def warn(msg)
|
78
|
+
super format(msg)
|
79
|
+
end
|
80
|
+
|
81
|
+
def error(msg)
|
82
|
+
super format(msg)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|