opentox-ruby 0.0.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/helper.rb CHANGED
@@ -1,26 +1,99 @@
1
1
  helpers do
2
2
 
3
- # Authentification
4
- def protected!
5
- response['WWW-Authenticate'] = %(Basic realm="Testing HTTP Auth") and \
6
- throw(:halt, [401, "Not authorized\n"]) and \
7
- return unless authorized?
3
+ # Authentification
4
+ def protected!(subjectid)
5
+ if env["session"]
6
+ unless authorized?(subjectid)
7
+ flash[:notice] = "You don't have access to this section: "
8
+ redirect back
9
+ end
10
+ elsif !env["session"] && subjectid
11
+ unless authorized?(subjectid)
12
+ LOGGER.debug "URI not authorized: clean: " + clean_uri("#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}#{request.env['REQUEST_URI']}").to_s + " full: #{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}#{request.env['REQUEST_URI']} with request: #{request.env['REQUEST_METHOD']}"
13
+ raise OpenTox::NotAuthorizedError.new "Not authorized"
14
+ end
15
+ else
16
+ raise OpenTox::NotAuthorizedError.new "Not authorized" unless authorized?(subjectid)
17
+ end
8
18
  end
9
19
 
10
- def authorized?
11
- @auth ||= Rack::Auth::Basic::Request.new(request.env)
12
- @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == ['api', API_KEY]
20
+ #Check Authorization for URI with method and subjectid.
21
+ def authorized?(subjectid)
22
+ request_method = request.env['REQUEST_METHOD']
23
+ uri = clean_uri("#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}#{request.env['REQUEST_URI']}")
24
+ request_method = "GET" if request_method == "POST" && uri =~ /\/model\/\d+\/?$/
25
+ return OpenTox::Authorization.authorized?(uri, request_method, subjectid)
13
26
  end
14
27
 
15
-
16
- =begin
17
- def xml(object)
18
- builder do |xml|
19
- xml.instruct!
20
- object.to_xml
21
- end
22
- end
23
- =end
28
+ #cleans URI from querystring and file-extension. Sets port 80 to emptystring
29
+ # @param [String] uri
30
+ def clean_uri(uri)
31
+ uri = uri.sub(" ", "%20") #dirty hacks => to fix
32
+ uri = uri[0,uri.index("InChI=")] if uri.index("InChI=")
33
+
34
+ out = URI.parse(uri)
35
+ out.path = out.path[0, out.path.length - (out.path.reverse.rindex(/\/{1}\d+\/{1}/))] if out.path.index(/\/{1}\d+\/{1}/) #cuts after /id/ for a&a
36
+ port = (out.scheme=="http" && out.port==80)||(out.scheme=="https" && out.port==443) ? "" : ":#{out.port.to_s}"
37
+ "#{out.scheme}://#{out.host}#{port}#{out.path.chomp("/")}" #"
38
+ end
39
+
40
+ #unprotected uri for login
41
+ def login_requests
42
+ return env['REQUEST_URI'] =~ /\/login$/
43
+ end
44
+
45
+ def uri_available?(urlStr)
46
+ url = URI.parse(urlStr)
47
+ unless @subjectid
48
+ Net::HTTP.start(url.host, url.port) do |http|
49
+ return http.head(url.request_uri).code == "200"
50
+ end
51
+ else
52
+ Net::HTTP.start(url.host, url.port) do |http|
53
+ return http.post(url.request_uri, "subjectid=#{@subjectid}").code == "202"
54
+ end
55
+ end
56
+ end
24
57
 
25
58
  end
26
59
 
60
+ before do
61
+ unless !AA_SERVER or login_requests or CONFIG[:authorization][:free_request].include?(env['REQUEST_METHOD'])
62
+ begin
63
+ subjectid = nil
64
+ subjectid = session[:subjectid] if session[:subjectid]
65
+ subjectid = params[:subjectid] if params[:subjectid] and !subjectid
66
+ subjectid = request.env['HTTP_SUBJECTID'] if request.env['HTTP_SUBJECTID'] and !subjectid
67
+ subjectid = request.cookies["subjectid"] unless subjectid
68
+ # see http://rack.rubyforge.org/doc/SPEC.html
69
+ subjectid = CGI.unescape(subjectid) if subjectid.include?("%23")
70
+ @subjectid = subjectid
71
+ rescue
72
+ #LOGGER.debug "OpenTox ruby api wrapper: helper before filter: NO subjectid for URI: #{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}#{request.env['REQUEST_URI']}"
73
+ subjectid = ""
74
+ end
75
+ @subjectid = subjectid
76
+ protected!(subjectid)
77
+
78
+ extension = File.extname(request.path_info) # params[:id] is not yet available
79
+ unless extension.empty?
80
+ #request.path_info.sub!(/\.#{extension}$/,'')
81
+ case extension
82
+ when "html"
83
+ @accept = 'text/html'
84
+ when "yaml"
85
+ @accept = 'application/x-yaml'
86
+ when "csv"
87
+ @accept = 'text/csv'
88
+ when "rdfxml"
89
+ @accept = 'application/rdf+xml'
90
+ when "xls"
91
+ @accept = 'application/ms-excel'
92
+ else
93
+ halt 404, "File format #{extension} not supported."
94
+ end
95
+ end
96
+
97
+ end
98
+ end
99
+
data/lib/model.rb CHANGED
@@ -6,33 +6,71 @@ module OpenTox
6
6
 
7
7
  # Run a model with parameters
8
8
  # @param [Hash] params Parameters for OpenTox model
9
+ # @param [optional,OpenTox::Task] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
9
10
  # @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'
15
- 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}"
11
+ def run( params, accept_header=nil, waiting_task=nil )
12
+ unless accept_header
13
+ if CONFIG[:yaml_hosts].include?(URI.parse(@uri).host)
14
+ accept_header = 'application/x-yaml'
15
+ else
16
+ accept_header = 'application/rdf+xml'
17
+ end
21
18
  end
19
+ LOGGER.info "running model "+@uri.to_s+", params: "+params.inspect+", accept: "+accept_header.to_s
20
+ RestClientWrapper.post(@uri,params,{:accept => accept_header},waiting_task).to_s
22
21
  end
23
22
 
24
23
  # Generic OpenTox model class for all API compliant services
25
24
  class Generic
26
25
  include Model
26
+
27
+ # Find Generic Opentox Model via URI, and loads metadata, could raise NotFound/NotAuthorized error
28
+ # @param [String] uri Model URI
29
+ # @return [OpenTox::Model::Generic] Model instance
30
+ def self.find(uri,subjectid=nil)
31
+ return nil unless uri
32
+ model = Generic.new(uri)
33
+ model.load_metadata(subjectid)
34
+ raise "could not load model metadata '"+uri.to_s+"'" if model.metadata==nil or model.metadata.size==0
35
+ model
36
+ end
37
+
38
+ # provides feature type, possible types are "regression" or "classification"
39
+ # @return [String] feature type, "unknown" if type could not be estimated
40
+ def feature_type(subjectid=nil)
41
+ return @feature_type if @feature_type
42
+
43
+ # dynamically perform restcalls if necessary
44
+ load_metadata(subjectid) if @metadata==nil or @metadata.size==0 or (@metadata.size==1 && @metadata.values[0]==@uri)
45
+ algorithm = OpenTox::Algorithm::Generic.find(@metadata[OT.algorithm], subjectid)
46
+ algorithm_title = algorithm ? algorithm.metadata[DC.title] : nil
47
+ algorithm_type = algorithm ? algorithm.metadata[OT.isA] : nil
48
+ dependent_variable = OpenTox::Feature.find( @metadata[OT.dependentVariables],subjectid )
49
+ dependent_variable_type = dependent_variable ? dependent_variable.feature_type : nil
50
+ type_indicators = [dependent_variable_type, @metadata[OT.isA], @metadata[DC.title],
51
+ @uri, algorithm_type, algorithm_title]
52
+ type_indicators.each do |type|
53
+ case type
54
+ when /(?i)classification/
55
+ @feature_type = "classification"
56
+ break
57
+ when /(?i)regression/
58
+ @feature_type = "regression"
59
+ end
60
+ end
61
+ raise "unknown model "+type_indicators.inspect unless @feature_type
62
+ @feature_type
63
+ end
64
+
27
65
  end
28
-
66
+
29
67
  # Lazy Structure Activity Relationship class
30
68
  class Lazar
31
69
 
32
70
  include Model
33
71
  include Algorithm
34
72
 
35
- attr_accessor :compound, :prediction_dataset, :features, :effects, :activities, :p_values, :fingerprints, :feature_calculation_algorithm, :similarity_algorithm, :prediction_algorithm, :min_sim
73
+ attr_accessor :compound, :prediction_dataset, :features, :effects, :activities, :p_values, :fingerprints, :feature_calculation_algorithm, :similarity_algorithm, :prediction_algorithm, :min_sim, :subjectid
36
74
 
37
75
  def initialize(uri=nil)
38
76
 
@@ -60,15 +98,15 @@ module OpenTox
60
98
 
61
99
  # Get URIs of all lazar models
62
100
  # @return [Array] List of lazar model URIs
63
- def self.all
64
- RestClientWrapper.get(CONFIG[:services]["opentox-model"]).to_s.split("\n")
101
+ def self.all(subjectid=nil)
102
+ RestClientWrapper.get(CONFIG[:services]["opentox-model"], :subjectid => subjectid).to_s.split("\n")
65
103
  end
66
104
 
67
105
  # Find a lazar model
68
106
  # @param [String] uri Model URI
69
107
  # @return [OpenTox::Model::Lazar] lazar model
70
- def self.find(uri)
71
- YAML.load RestClientWrapper.get(uri,:accept => 'application/x-yaml')
108
+ def self.find(uri, subjectid=nil)
109
+ YAML.load RestClientWrapper.get(uri,{:accept => 'application/x-yaml', :subjectid => subjectid})
72
110
  end
73
111
 
74
112
  # Create a new lazar model
@@ -77,7 +115,7 @@ module OpenTox
77
115
  def self.create(params)
78
116
  lazar_algorithm = OpenTox::Algorithm::Generic.new File.join( CONFIG[:services]["opentox-algorithm"],"lazar")
79
117
  model_uri = lazar_algorithm.run(params)
80
- OpenTox::Model::Lazar.find(model_uri)
118
+ OpenTox::Model::Lazar.find(model_uri, params[:subjectid])
81
119
  end
82
120
 
83
121
  # Get a parameter value
@@ -89,21 +127,30 @@ module OpenTox
89
127
 
90
128
  # Predict a dataset
91
129
  # @param [String] dataset_uri Dataset URI
130
+ # @param [optional,subjectid]
131
+ # @param [optional,OpenTox::Task] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
92
132
  # @return [OpenTox::Dataset] Dataset with predictions
93
- def predict_dataset(dataset_uri)
94
- @prediction_dataset = Dataset.create
133
+ def predict_dataset(dataset_uri, subjectid=nil, waiting_task=nil)
134
+ @prediction_dataset = Dataset.create(CONFIG[:services]["opentox-dataset"], subjectid)
95
135
  @prediction_dataset.add_metadata({
96
136
  OT.hasSource => @uri,
97
137
  DC.creator => @uri,
98
138
  DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
99
139
  OT.parameters => [{DC.title => "dataset_uri", OT.paramValue => dataset_uri}]
100
140
  })
101
- d = Dataset.new(dataset_uri)
102
- d.load_compounds
141
+ d = Dataset.new(dataset_uri,subjectid)
142
+ d.load_compounds(subjectid)
143
+ count = 0
103
144
  d.compounds.each do |compound_uri|
104
- predict(compound_uri,false)
145
+ begin
146
+ predict(compound_uri,false,subjectid)
147
+ count += 1
148
+ waiting_task.progress( count/d.compounds.size.to_f*100.0 ) if waiting_task
149
+ rescue => ex
150
+ LOGGER.warn "prediction for compound "+compound_uri.to_s+" failed: "+ex.message
151
+ end
105
152
  end
106
- @prediction_dataset.save
153
+ @prediction_dataset.save(subjectid)
107
154
  @prediction_dataset
108
155
  end
109
156
 
@@ -111,7 +158,7 @@ module OpenTox
111
158
  # @param [String] compound_uri Compound URI
112
159
  # @param [optinal,Boolean] verbose Verbose prediction (output includes neighbors and features)
113
160
  # @return [OpenTox::Dataset] Dataset with prediction
114
- def predict(compound_uri,verbose=false)
161
+ def predict(compound_uri,verbose=false,subjectid=nil)
115
162
 
116
163
  @compound = Compound.new compound_uri
117
164
  features = {}
@@ -119,7 +166,7 @@ module OpenTox
119
166
  unless @prediction_dataset
120
167
  #@prediction_dataset = cached_prediction
121
168
  #return @prediction_dataset if cached_prediction
122
- @prediction_dataset = Dataset.create
169
+ @prediction_dataset = Dataset.create(CONFIG[:services]["opentox-dataset"], subjectid)
123
170
  @prediction_dataset.add_metadata( {
124
171
  OT.hasSource => @uri,
125
172
  DC.creator => @uri,
@@ -129,7 +176,7 @@ module OpenTox
129
176
  } )
130
177
  end
131
178
 
132
- return @prediction_dataset if database_activity
179
+ return @prediction_dataset if database_activity(subjectid)
133
180
 
134
181
  neighbors
135
182
  prediction = eval("#{@prediction_algorithm}(@neighbors,{:similarity_algorithm => @similarity_algorithm, :p_values => @p_values})")
@@ -217,7 +264,7 @@ module OpenTox
217
264
  end
218
265
  end
219
266
 
220
- @prediction_dataset.save
267
+ @prediction_dataset.save(subjectid)
221
268
  @prediction_dataset
222
269
  end
223
270
 
@@ -245,11 +292,11 @@ module OpenTox
245
292
 
246
293
  # Find database activities and store them in @prediction_dataset
247
294
  # @return [Boolean] true if compound has databasse activities, false if not
248
- def database_activity
295
+ def database_activity(subjectid)
249
296
  if @activities[@compound.uri]
250
297
  @activities[@compound.uri].each { |act| @prediction_dataset.add @compound.uri, @metadata[OT.dependentVariables], act }
251
298
  @prediction_dataset.add_metadata(OT.hasSource => @metadata[OT.trainingDataset])
252
- @prediction_dataset.save
299
+ @prediction_dataset.save(subjectid)
253
300
  true
254
301
  else
255
302
  false
@@ -257,13 +304,13 @@ module OpenTox
257
304
  end
258
305
 
259
306
  # Save model at model service
260
- def save
261
- self.uri = RestClientWrapper.post(@uri,{:content_type => "application/x-yaml"},self.to_yaml)
307
+ def save(subjectid)
308
+ self.uri = RestClientWrapper.post(@uri,self.to_yaml,{:content_type => "application/x-yaml", :subjectid => subjectid})
262
309
  end
263
310
 
264
311
  # Delete model at model service
265
- def delete
266
- RestClientWrapper.delete @uri unless @uri == CONFIG[:services]["opentox-model"]
312
+ def delete(subjectid)
313
+ RestClientWrapper.delete(@uri, :subjectid => subjectid) unless @uri == CONFIG[:services]["opentox-model"]
267
314
  end
268
315
 
269
316
  end
@@ -0,0 +1,43 @@
1
+ module OpenTox
2
+ module OntologyService
3
+ module Endpoints
4
+ require 'sparql/client'
5
+ @sparql = SPARQL::Client.new("http://apps.ideaconsult.net:8080/ontology")
6
+ def self.qs(classname="Endpoints")
7
+ return "PREFIX ot:<http://www.opentox.org/api/1.1#>
8
+ PREFIX ota:<http://www.opentox.org/algorithms.owl#>
9
+ PREFIX owl:<http://www.w3.org/2002/07/owl#>
10
+ PREFIX dc:<http://purl.org/dc/elements/1.1/>
11
+ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
12
+ PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>
13
+ PREFIX otee:<http://www.opentox.org/echaEndpoints.owl#>
14
+ PREFIX toxcast:<http://www.opentox.org/toxcast.owl#>
15
+ select ?Endpoints ?title ?id
16
+ where {?Endpoints rdfs:subClassOf otee:#{classname}.
17
+ OPTIONAL {?Endpoints dc:title ?title}.
18
+ OPTIONAL {?Endpoints dc:identifier ?id}.}
19
+ ORDER BY ?title"
20
+ end
21
+
22
+ def self.make_option_list(endpoint="Endpoints", level=1)
23
+ out = ""
24
+ results = @sparql.query(qs(endpoint)) rescue results = []
25
+ results.each do |result|
26
+ endpointname = result.Endpoints.to_s.split('#').last
27
+ title = result.bound?(:title) ? result.title : endpointname
28
+ out += "<option value='#{title}' id='#{endpointname}' class='level_#{level}'>#{title}</option>\n"
29
+ out += make_option_list(endpointname, level + 1)
30
+ end
31
+ return out
32
+ end
33
+
34
+ def self.get_endpoint_selectlist(include_blank=true)
35
+ out = "<select id='endpoint' name='endpoint'>\n"
36
+ out += "<option value='' id='please_select'>Please select</option>\n" if include_blank
37
+ out += make_option_list
38
+ out += "</select>\n"
39
+ return out
40
+ end
41
+ end
42
+ end
43
+ end
data/lib/opentox-ruby.rb CHANGED
@@ -1,4 +1,4 @@
1
- ['rubygems', 'sinatra', 'sinatra/url_for', 'rest_client', 'yaml', 'cgi', 'spork', 'overwrite', 'environment'].each do |lib|
1
+ ['rubygems', 'sinatra', 'sinatra/url_for', 'ohm', 'rest_client', 'yaml', 'cgi', 'spork', 'error', 'overwrite', 'environment'].each do |lib|
2
2
  require lib
3
3
  end
4
4
 
@@ -8,6 +8,7 @@ rescue LoadError
8
8
  puts "Please install Openbabel with 'rake openbabel:install' in the compound component"
9
9
  end
10
10
 
11
- ['opentox', 'compound','dataset', 'parser','serializer', 'algorithm','model','task','validation','feature', 'rest_client_wrapper'].each do |lib|
11
+ ['opentox', 'compound','dataset', 'parser','serializer', 'algorithm','model','task','validation','feature',
12
+ 'rest_client_wrapper', 'authorization', 'policy', 'helper', 'to-html' ].each do |lib|
12
13
  require lib
13
14
  end
data/lib/opentox.rb CHANGED
@@ -19,14 +19,14 @@ module OpenTox
19
19
 
20
20
  # Get all objects from a service
21
21
  # @return [Array] List of available URIs
22
- def self.all(uri)
23
- RestClientWrapper.get(uri,:accept => "text/uri-list").to_s.split(/\n/)
22
+ def self.all(uri, subjectid=nil)
23
+ RestClientWrapper.get(uri,:accept => "text/uri-list", :subjectid => subjectid).to_s.split(/\n/)
24
24
  end
25
25
 
26
26
  # Load (and return) metadata from object URI
27
27
  # @return [Hash] Metadata
28
- def load_metadata
29
- @metadata = Parser::Owl::Generic.new(@uri).load_metadata
28
+ def load_metadata(subjectid=nil)
29
+ @metadata = Parser::Owl::Generic.new(@uri).load_metadata(subjectid)
30
30
  @metadata
31
31
  end
32
32
 
@@ -43,5 +43,10 @@ module OpenTox
43
43
  s.to_rdfxml
44
44
  end
45
45
 
46
+ # deletes the resource, deletion should have worked when no RestCallError raised
47
+ def delete(subjectid=nil)
48
+ RestClientWrapper.delete(uri,:subjectid => subjectid)
49
+ end
50
+
46
51
  end
47
52
 
data/lib/overwrite.rb CHANGED
@@ -1,38 +1,91 @@
1
1
  # class overwrites aka monkey patches
2
- # hack: store sinatra in global var to make url_for and halt methods accessible
3
- before{ $sinatra = self unless $sinatra }
2
+ # hack: store sinatra instance in global var $url_provider to make url_for and halt methods accessible
3
+ before {
4
+ raise "should not happen, url provider already differently initialized "+
5
+ $url_provider.request.host.to_s+" != "+self.request.host.to_s if
6
+ $url_provider and $url_provider.request.host!=self.request.host and
7
+ $url_provider.request.script_name!=self.request.script_name
8
+ $url_provider = self
9
+ # stupid internet explorer does not ask for text/html, add this manually
10
+ request.env['HTTP_ACCEPT'] += ";text/html" if request.env["HTTP_USER_AGENT"]=~/MSIE/
11
+ }
12
+
13
+ # Error handling
14
+ # Errors are logged as error and formated according to acccept-header
15
+ # Non OpenTox::Errors (defined in error.rb) are handled as internal error (500), stacktrace is logged
16
+ # IMPT: set sinatra settings :show_exceptions + :raise_errors to false in config.ru, otherwise Rack::Showexceptions takes over
17
+ error Exception do
18
+ error = request.env['sinatra.error']
19
+ # log error message and backtrace to logfile
20
+ LOGGER.error error.class.to_s+": "+error.message
21
+ LOGGER.error ":\n"+error.backtrace.join("\n")
22
+
23
+ actor = "#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}#{request.env['REQUEST_URI']}"
24
+ rep = OpenTox::ErrorReport.create(error, actor)
25
+
26
+ case request.env['HTTP_ACCEPT']
27
+ when /rdf/
28
+ content_type 'application/rdf+xml'
29
+ halt error.http_code,rep.to_rdfxml
30
+ when /html/
31
+ content_type 'text/html'
32
+ halt error.http_code,(OpenTox.text_to_html rep.to_yaml, @subjectid)
33
+ else
34
+ content_type 'application/x-yaml'
35
+ halt error.http_code,rep.to_yaml
36
+ end
37
+ end
4
38
 
5
39
  class Sinatra::Base
6
- # overwriting halt to log halts (!= 202)
7
- def halt(*response)
8
- LOGGER.error "halt "+response.first.to_s+" "+(response.size>1 ? response[1].to_s : "") if response and response.first and response.first >= 300
9
- # orig sinatra code:
10
- response = response.first if response.length == 1
11
- throw :halt, response
40
+
41
+ def return_task( task )
42
+ code = task.running? ? 202 : 200
43
+ case request.env['HTTP_ACCEPT']
44
+ when /rdf/
45
+ response['Content-Type'] = "application/rdf+xml"
46
+ halt code,task.to_rdfxml
47
+ when /yaml/
48
+ response['Content-Type'] = "application/x-yaml"
49
+ halt code,task.to_yaml # PENDING differs from task-webservice
50
+ when /html/
51
+ response['Content-Type'] = "text/html"
52
+ halt code,OpenTox.text_to_html(task.to_yaml, @subjectid)
53
+ else # default /uri-list/
54
+ response['Content-Type'] = "text/uri-list"
55
+ halt code,task.uri+"\n"
56
+ end
12
57
  end
13
58
  end
14
59
 
15
60
  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
61
+ def task_uri?
62
+ self.uri? && !self.match(/task/).nil?
63
+ end
64
+
65
+ def dataset_uri?
66
+ self.uri? && !self.match(/dataset/).nil?
67
+ end
68
+
69
+ def self.model_uri?
70
+ self.uri? && !self.match(/model/).nil?
71
+ end
27
72
 
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
73
+ def uri?
74
+ begin
75
+ u = URI::parse(self)
76
+ return (u.scheme!=nil and u.host!=nil)
77
+ rescue URI::InvalidURIError
78
+ return false
35
79
  end
80
+ end
81
+
82
+ def underscore
83
+ self.gsub(/::/, '/').
84
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
85
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
86
+ tr("-", "_").
87
+ downcase
88
+ end
36
89
  end
37
90
 
38
91
  require 'logger'
@@ -52,7 +105,7 @@ class OTLogger < Logger
52
105
  n = 2
53
106
  line = lines[n]
54
107
 
55
- while (line =~ /spork.rb/ or line =~ /create/ or line =~ /ot-logger.rb/)
108
+ while (line =~ /spork.rb/ or line =~ /create/ or line =~ /overwrite.rb/)
56
109
  n += 1
57
110
  line = lines[n]
58
111
  end
@@ -63,7 +116,7 @@ class OTLogger < Logger
63
116
  end
64
117
 
65
118
  def format(msg)
66
- pwd.ljust(18)+" :: "+msg.to_s+" :: "+trace+" :: "+($sinatra ? $sinatra.request.env['REMOTE_ADDR'] : nil).to_s
119
+ pwd.ljust(18)+" :: "+msg.to_s+" :: "+trace
67
120
  end
68
121
 
69
122
  def debug(msg)
@@ -84,3 +137,9 @@ class OTLogger < Logger
84
137
 
85
138
  end
86
139
 
140
+ # make migration from datamapper more straightforward
141
+ class Ohm::Model
142
+ def self.get(id)
143
+ self[id]
144
+ end
145
+ end