opentox-ruby-api-wrapper 1.4.0 → 1.5.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/Rakefile CHANGED
@@ -10,10 +10,10 @@ begin
10
10
  gem.email = "helma@in-silico.ch"
11
11
  gem.homepage = "http://github.com/helma/opentox-ruby-api-wrapper"
12
12
  gem.authors = ["Christoph Helma"]
13
- ["sinatra", "rest-client", "rack", "rack-contrib", "rack-flash", "emk-sinatra-url-for", "cehoffman-sinatra-respond_to", "dm-more", "dm-core", "sinatra-static-assets"].each do |dep|
13
+ ["sinatra", "rest-client", "rack", "rack-contrib", "rack-flash", "emk-sinatra-url-for", "sinatra-respond_to", "dm-more", "dm-core", "sinatra-static-assets","tmail"].each do |dep|
14
14
  gem.add_dependency dep
15
15
  end
16
- ['cucumber','jeweler', "thin"].each do |dep|
16
+ ['cucumber','jeweler'].each do |dep|
17
17
  gem.add_development_dependency dep
18
18
  end
19
19
  gem.files = FileList["[A-Z]*", "{bin,generators,lib,test}/**/*", 'lib/jeweler/templates/.gitignore']
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.4.0
1
+ 1.5.0
@@ -90,9 +90,9 @@ sudo apt-get install postgresql-server-dev-8.4 | tee -a $INSTALLLOG
90
90
  #echo "Installing gems jeweler sinatra emk-sinatra-url-for dm-core cehoffman-sinatra-respond_to rest-client rack-contrib thin cucumber datamapper data_objects do_sqlite3 rinruby"
91
91
  sudo gem install jeweler | tee -a $INSTALLLOG
92
92
  sudo gem install sinatra | tee -a $INSTALLLOG
93
- sudo gem install emk-sinatra-url-for -s http://gems.github.com | tee -a $INSTALLLOG
93
+ sudo gem install sinatra-url-for | tee -a $INSTALLLOG
94
94
  sudo gem install dm-core | tee -a $INSTALLLOG
95
- sudo gem install cehoffman-sinatra-respond_to -s http://gems.github.com | tee -a $INSTALLLOG
95
+ sudo gem install sinatra-respond_to | tee -a $INSTALLLOG
96
96
  sudo gem install rest-client | tee -a $INSTALLLOG
97
97
  sudo gem install rack-contrib | tee -a $INSTALLLOG
98
98
  sudo gem install thin | tee -a $INSTALLLOG
data/bin/yaml2owl.rb CHANGED
@@ -5,7 +5,7 @@ require 'opentox-ruby-api-wrapper'
5
5
  input = YAML.load_file(ARGV[0])
6
6
  dataset = OpenTox::Dataset.new
7
7
  dataset.title = input[:title]
8
- dataset.source = input[:source]
8
+ dataset.creator = input[:source]
9
9
  input[:data].each do |c,f|
10
10
  f.each do |k,v|
11
11
  v.each do |value|
data/lib/algorithm.rb CHANGED
@@ -21,8 +21,11 @@ module OpenTox
21
21
  def self.create_model(params)
22
22
  LOGGER.debug params
23
23
  LOGGER.debug File.basename(__FILE__) + ": creating model"
24
+ LOGGER.debug File.join(@@config[:services]["opentox-algorithm"], "lazar")
25
+ #resource = RestClient::Resource.new(File.join(@@config[:services]["opentox-algorithm"], "lazar"), :user => @@users[:users].keys[0], :password => @@users[:users].values[0], :content_type => "application/x-yaml")
24
26
  resource = RestClient::Resource.new(File.join(@@config[:services]["opentox-algorithm"], "lazar"), :user => @@users[:users].keys[0], :password => @@users[:users].values[0], :content_type => "application/x-yaml")
25
- @uri = resource.post(:dataset_uri => params[:dataset_uri], :feature_uri => params[:feature_uri], :feature_generation_uri => File.join(@@config[:services]["opentox-algorithm"], "fminer")).chomp
27
+ #@uri = resource.post(:dataset_uri => params[:dataset_uri], :feature_uri => params[:feature_uri], :feature_generation_uri => File.join(@@config[:services]["opentox-algorithm"], "fminer")).chomp
28
+ @uri = resource.post(:dataset_uri => params[:dataset_uri], :prediction_feature => params[:prediction_feature], :feature_generation_uri => File.join(@@config[:services]["opentox-algorithm"], "fminer")).body.chomp
26
29
  end
27
30
 
28
31
  def self.uri
data/lib/compound.rb CHANGED
@@ -18,18 +18,18 @@ module OpenTox
18
18
  @uri = File.join(@@config[:services]["opentox-compound"],URI.escape(@inchi))
19
19
  elsif params[:name]
20
20
  # paranoid URI encoding to keep SMILES charges and brackets
21
- @inchi = RestClient.get("#{@@cactus_uri}#{URI.encode(params[:name], Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}/stdinchi").chomp
21
+ @inchi = RestClientWrapper.get("#{@@cactus_uri}#{URI.encode(params[:name], Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}/stdinchi").chomp
22
22
  @uri = File.join(@@config[:services]["opentox-compound"],URI.escape(@inchi))
23
23
  elsif params[:uri]
24
24
  @uri = params[:uri]
25
25
  case params[:uri]
26
26
  when /ambit/ # Ambit does not deliver InChIs reliably
27
- smiles = RestClient.get @uri, :accept => 'chemical/x-daylight-smiles'
27
+ smiles = RestClientWrapper.get @uri, :accept => 'chemical/x-daylight-smiles'
28
28
  @inchi = obconversion(smiles,'smi','inchi')
29
29
  when /InChI/ # shortcut for IST services
30
30
  @inchi = params[:uri].sub(/^.*InChI/, 'InChI')
31
31
  else
32
- @inchi = RestClient.get @uri, :accept => 'chemical/x-inchi'
32
+ @inchi = RestClientWrapper.get @uri, :accept => 'chemical/x-inchi'
33
33
  end
34
34
  end
35
35
  end
@@ -44,7 +44,7 @@ module OpenTox
44
44
  end
45
45
 
46
46
  def image
47
- RestClient.get("#{@@cactus_uri}#{@inchi}/image")
47
+ RestClientWrapper.get("#{@@cactus_uri}#{@inchi}/image")
48
48
  end
49
49
 
50
50
  def image_uri
@@ -18,3 +18,11 @@ set :raise_errors, true
18
18
  end
19
19
 
20
20
  use Rack::ShowExceptions
21
+ #if MAIL
22
+ # use Rack::MailExceptions do |mail|
23
+ # mail.to 'helma@in-silico.ch'
24
+ # mail.subject '[ERROR] %s'
25
+ # mail.from "toxcreate@in-silico.ch"
26
+ # mail.smtp MAIL
27
+ # end
28
+ #end
data/lib/dataset.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  LOGGER.progname = File.expand_path(__FILE__)
2
2
 
3
3
  module OpenTox
4
-
4
+
5
5
  class Dataset
6
6
 
7
- attr_accessor :uri, :title, :source, :identifier, :data, :features, :compounds
7
+ attr_accessor :uri, :title, :creator, :data, :features, :compounds
8
8
 
9
9
  def initialize
10
10
  @data = {}
@@ -12,171 +12,175 @@ module OpenTox
12
12
  @compounds = []
13
13
  end
14
14
 
15
- def self.find(uri)
16
- YAML.load RestClient.get(uri, :accept => 'application/x-yaml').to_s
15
+ def self.find(uri, accept_header=nil)
16
+
17
+ unless accept_header
18
+ #if uri.match(@@config[:services]["opentox-dataset"]) || uri=~ /188.40.32.88/ || uri =~ /informatik/
19
+ if !@@config[:accept_headers]["opentox-dataset"].grep(/yaml/).empty?
20
+ accept_header = 'application/x-yaml'
21
+ else
22
+ accept_header = "application/rdf+xml"
23
+ end
24
+ end
25
+
26
+ case accept_header
27
+ when "application/x-yaml"
28
+ d = YAML.load RestClientWrapper.get(uri.to_s.strip, :accept => 'application/x-yaml').to_s
29
+ d.uri = uri unless d.uri
30
+ when "application/rdf+xml"
31
+ owl = OpenTox::Owl.from_uri(uri.to_s.strip, "Dataset")
32
+
33
+ d = Dataset.new
34
+ d.title = owl.get("title")
35
+ d.creator = owl.get("creator")
36
+ d.uri = owl.uri
37
+
38
+ # when loading a dataset from owl, only compound- and feature-uris are loaded
39
+ owl.load_dataset(d.compounds, d.features)
40
+ # all features are marked as dirty, loaded dynamically later
41
+ d.init_dirty_features(owl)
42
+
43
+ d.compounds.uniq!
44
+ d.features.uniq!
45
+ else
46
+ raise "cannot get datset with accept header: "+accept_header.to_s
47
+ end
48
+ return d
17
49
  end
50
+
51
+ # creates a new dataset, using only those compounsd specified in new_compounds
52
+ # returns uri of new dataset
53
+ def create_new_dataset( new_compounds, new_features, new_title, new_creator )
54
+
55
+ raise "no new compounds selected" unless new_compounds and new_compounds.size>0
56
+
57
+ # load require features
58
+ if ((defined? @dirty_features) && (@dirty_features - new_features).size > 0)
59
+ (@dirty_features - new_features).each{|f| load_feature_values(f)}
60
+ end
61
+
62
+ dataset = OpenTox::Dataset.new
63
+ dataset.title = new_title
64
+ dataset.creator = new_creator
65
+ dataset.features = new_features
66
+ dataset.compounds = new_compounds
67
+
68
+ # Copy dataset data for compounds and features
69
+ # PENDING: why storing feature values in an array?
70
+ new_compounds.each do |c|
71
+ data_c = []
72
+ @data[c].each do |d|
73
+ m = {}
74
+ new_features.each do |f|
75
+ m[f] = d[f]
76
+ end
77
+ data_c << m
78
+ end
79
+
80
+ dataset.data[c] = data_c
81
+ end
82
+ return dataset.save
83
+ end
84
+
85
+ # returns classification value
86
+ def get_predicted_class(compound, feature)
87
+ v = get_value(compound, feature)
88
+ if v.is_a?(Hash)
89
+ if v.has_key?(:classification)
90
+ return v[:classification]
91
+ else
92
+ return "no classification key"
93
+ end
94
+ else
95
+ raise "predicted class value is not a hash\n"+
96
+ "value "+v.to_s+"\n"+
97
+ "value-class "+v.class.to_s+"\n"+
98
+ "dataset "+@uri.to_s+"\n"+
99
+ "compound "+compound.to_s+"\n"+
100
+ "feature "+feature.to_s+"\n"
101
+ end
102
+
103
+ end
104
+
105
+ # returns prediction confidence if available
106
+ def get_prediction_confidence(compound, feature)
107
+ v = get_value(compound, feature)
108
+ if v.is_a?(Hash)
109
+ if v.has_key?(:confidence)
110
+ return v[:confidence].abs
111
+ else
112
+ # PENDING: return nil isntead of raising an exception
113
+ raise "no confidence key"
114
+ end
115
+ else
116
+ raise "prediction confidence value is not a hash value\n"+
117
+ "value "+v.to_s+"\n"+
118
+ "value-class "+v.class.to_s+"\n"+
119
+ "dataset "+@uri.to_s+"\n"+
120
+ "compound "+compound.to_s+"\n"+
121
+ "feature "+feature.to_s+"\n"
122
+ end
123
+ end
124
+
125
+ # return compound-feature value
126
+ def get_value(compound, feature)
127
+ if (defined? @dirty_features) && @dirty_features.include?(feature)
128
+ load_feature_values(feature)
129
+ end
130
+
131
+ v = @data[compound]
132
+ raise "no values for compound "+compound.to_s if v==nil
133
+ if v.is_a?(Array)
134
+ # PENDING: why using an array here?
135
+ v.each do |e|
136
+ if e.is_a?(Hash)
137
+ if e.has_key?(feature)
138
+ return e[feature]
139
+ end
140
+ else
141
+ raise "invalid internal value type"
142
+ end
143
+ end
144
+ raise "feature value no found: "+feature.to_s
145
+ else
146
+ raise "value is not an array\n"+
147
+ "value "+v.to_s+"\n"+
148
+ "value-class "+v.class.to_s+"\n"+
149
+ "dataset "+@uri.to_s+"\n"+
150
+ "compound "+compound.to_s+"\n"+
151
+ "feature "+feature.to_s+"\n"
152
+ end
153
+ end
18
154
 
155
+ # loads specified feature and removes dirty-flag, loads all features if feature is nil
156
+ def load_feature_values(feature=nil)
157
+ if feature
158
+ raise "feature already loaded" unless @dirty_features.include?(feature)
159
+ @owl.load_dataset_feature_values(@compounds, @data, feature)
160
+ @dirty_features.delete(feature)
161
+ else
162
+ @data = {}
163
+ @owl.load_dataset_feature_values(@compounds, @data)
164
+ @dirty_features.clear
165
+ end
166
+ end
19
167
 
20
168
  def save
169
+ # loads all features before loading
170
+ if ((defined? @dirty_features) && @dirty_features.size > 0)
171
+ load_feature_values()
172
+ end
173
+
21
174
  @features.uniq!
22
175
  @compounds.uniq!
23
- RestClient::Resource.new(@@config[:services]["opentox-dataset"], :user => @@users[:users].keys[0], :password => @@users[:users].values[0]).post(self.to_yaml, :content_type => "application/x-yaml").chomp.to_s
24
- end
25
-
26
- =begin
27
- # create/add to entry from uris or Redland::Resources
28
- def add(compound,feature,value)
29
- compound = self.find_or_create_compound compound unless compound.class == Redland::Resource
30
- feature = self.find_or_create_feature feature unless feature.class == Redland::Resource
31
- data_entry = @model.subject OT['compound'], compound
32
- if data_entry.nil?
33
- data_entry = @model.create_resource
34
- dataset = @model.subject(RDF['type'],OT[self.owl_class])
35
- @model.add dataset, OT['dataEntry'], data_entry
36
- @model.add data_entry, RDF['type'], OT["DataEntry"]
37
- @model.add data_entry, OT['compound'], compound
38
- end
39
- values = @model.create_resource
40
- @model.add data_entry, OT['values'], values
41
- @model.add values, RDF['type'], OT['FeatureValue']
42
- @model.add values, OT['feature'], feature
43
- @model.add values, OT['value'], value.to_s
44
- end
45
-
46
- def add_tuple(compound,tuple)
47
- compound = self.find_or_create_compound compound unless compound.class == Redland::Resource
48
- data_entry = @model.subject OT['compound'], compound
49
- if data_entry.nil?
50
- data_entry = @model.create_resource
51
- dataset = @model.subject(RDF['type'],OT[self.owl_class])
52
- @model.add dataset, OT['dataEntry'], data_entry
53
- @model.add data_entry, RDF['type'], OT["DataEntry"]
54
- @model.add data_entry, OT['compound'], compound
55
- end
56
- @model.add data_entry, OT['values'], tuple
176
+ OpenTox::RestClientWrapper.post(@@config[:services]["opentox-dataset"],{:content_type => "application/x-yaml"},self.to_yaml).strip
57
177
  end
58
178
 
59
- def create_tuple(feature,t)
60
- feature = self.find_or_create_feature feature unless feature.class == Redland::Resource
61
- tuple = @model.create_resource
62
- @model.add tuple, RDF['type'], OT["Tuple"]
63
- @model.add tuple, OT['feature'], feature
64
- t.each do |name,value|
65
- f = self.find_or_create_feature name unless name.class == Redland::Resource
66
- complex_value = @model.create_resource
67
- feature = self.find_or_create_feature(name)
68
- @model.add tuple, OT['complexValue'], complex_value
69
- @model.add complex_value, RDF['type'], OT["FeatureValue"]
70
- @model.add complex_value, OT['feature'], f
71
- @model.add complex_value, OT['value'], value.to_s
72
- end
73
-
74
- tuple
75
- end
76
-
77
- # find or create a new compound and return the resource
78
- def find_or_create_compound(uri)
79
- compound = @model.subject(DC["identifier"], uri)
80
- if compound.nil?
81
- compound = @model.create_resource(uri)
82
- @model.add compound, RDF['type'], OT["Compound"]
83
- @model.add compound, DC["identifier"], uri
84
- end
85
- compound
86
- end
87
-
88
- # find or create a new feature and return the resource
89
- def find_or_create_feature(uri)
90
- feature = @model.subject(DC["identifier"], uri)
91
- if feature.nil?
92
- feature = @model.create_resource(uri)
93
- @model.add feature, RDF['type'], OT["Feature"]
94
- @model.add feature, DC["identifier"], uri
95
- @model.add feature, DC["title"], File.basename(uri).split(/#/)[1]
96
- @model.add feature, DC['source'], uri
97
- end
98
- feature
99
- end
100
-
101
- def self.create(data, content_type = 'application/rdf+xml')
102
- resource = RestClient::Resource.new(@@config[:services]["opentox-dataset"], :user => @@users[:users].keys[0], :password => @@users[:users].values[0])
103
- uri = resource.post data, :content_type => content_type
104
- dataset = Dataset.new
105
- dataset.read uri.chomp.to_s
106
- dataset
107
- end
108
-
109
- def features
110
- features = []
111
- @model.subjects(RDF['type'], OT["Feature"]).each do |feature_node|
112
- features << @model.object(feature_node, DC["identifier"])#
113
- end
114
- features
115
- end
116
-
117
- def data
118
- data = {}
119
- @model.subjects(RDF['type'], OT['DataEntry']).each do |data_entry|
120
- compound_node = @model.object(data_entry, OT['compound'])
121
- compound_uri = @model.object(compound_node, DC['identifier']).to_s
122
- @model.find(data_entry, OT['values'], nil) do |s,p,values|
123
- feature_node = @model.object values, OT['feature']
124
- feature_uri = @model.object(feature_node, DC['identifier']).to_s.sub(/\^\^.*$/,'') # remove XML datatype
125
- type = @model.object(values, RDF['type'])
126
- if type == OT['FeatureValue']
127
- value = @model.object(values, OT['value']).to_s
128
- case value.to_s
129
- when TRUE_REGEXP # defined in environment.rb
130
- value = true
131
- when FALSE_REGEXP # defined in environment.rb
132
- value = false
133
- else
134
- LOGGER.warn compound_uri + " has value '" + value.to_s + "' for feature " + feature_uri
135
- value = nil
136
- end
137
- data[compound_uri] = {} unless data[compound_uri]
138
- data[compound_uri][feature_uri] = [] unless data[compound_uri][feature_uri]
139
- data[compound_uri][feature_uri] << value unless value.nil?
140
- elsif type == OT['Tuple']
141
- entry = {}
142
- data[compound_uri] = {} unless data[compound_uri]
143
- data[compound_uri][feature_uri] = [] unless data[compound_uri][feature_uri]
144
- @model.find(values, OT['complexValue'],nil) do |s,p,complex_value|
145
- name_node = @model.object complex_value, OT['feature']
146
- name = @model.object(name_node, DC['title']).to_s
147
- value = @model.object(complex_value, OT['value']).to_s
148
- v = value.sub(/\^\^.*$/,'') # remove XML datatype
149
- v = v.to_f if v.match(/^[\.|\d]+$/) # guess numeric datatype
150
- entry[name] = v
151
- end
152
- data[compound_uri][feature_uri] << entry
153
- end
154
- end
155
- end
156
- data
157
- end
158
-
159
- def compounds
160
- compounds = []
161
- @model.subjects(RDF['type'], OT["Compound"]).each do |compound_node|
162
- compounds << @model.object(compound_node, DC["identifier"]).to_s
163
- end
164
- compounds
165
- end
166
-
167
- # Delete a dataset
168
- def delete
169
- resource = RestClient::Resource.new(@uri, :user => @@users[:users].keys[0], :password => @@users[:users].values[0])
170
- resource.delete
179
+ def init_dirty_features(owl)
180
+ @dirty_features = @features
181
+ @owl = owl
171
182
  end
183
+ end
172
184
 
173
- def to_owl
174
- end
175
-
176
- def from_owl
177
- end
178
-
179
- =end
180
- end
181
185
 
182
186
  end
data/lib/environment.rb CHANGED
@@ -40,10 +40,77 @@ if @@config[:database]
40
40
  end
41
41
  end
42
42
 
43
+ # mail for error messages
44
+ load File.join config_dir,"mail.rb" if File.exists?(File.join config_dir,"mail.rb")
45
+
46
+ # hack: store sinatra in global var to make url_for and halt methods accessible
47
+ before {$sinatra = self unless $sinatra}
48
+
49
+ class Sinatra::Base
50
+ # overwriting halt to log halts (!= 202)
51
+ def halt(*response)
52
+ LOGGER.error "halt "+response.first.to_s+" "+(response.size>1 ? response[1].to_s : "") if response and response.first and response.first >= 300
53
+ # orig sinatra code:
54
+ response = response.first if response.length == 1
55
+ throw :halt, response
56
+ end
57
+ end
58
+
43
59
  # logging
60
+ class MyLogger < Logger
61
+
62
+ def pwd
63
+ path = Dir.pwd.to_s
64
+ index = path.rindex(/\//)
65
+ return path if index==nil
66
+ path[(index+1)..-1]
67
+ end
68
+
69
+ def trace()
70
+ lines = caller(0)
71
+ n = 2
72
+ line = lines[n]
73
+
74
+ while (line =~ /spork.rb/ or line =~ /as_task/ or line =~ /environment.rb/)
75
+ n += 1
76
+ line = lines[n]
77
+ end
78
+
79
+ index = line.rindex(/\/.*\.rb/)
80
+ return line if index==nil
81
+ line[index..-1]
82
+ end
83
+
84
+ def format(msg)
85
+ pwd.ljust(18)+" :: "+msg.to_s+" :: "+trace+" :: "+ENV['REMOTE_ADDR'].to_s
86
+ end
87
+
88
+ def debug(msg)
89
+ super format(msg)
90
+ end
91
+
92
+ def info(msg)
93
+ super format(msg)
94
+ end
95
+
96
+ def warn(msg)
97
+ super format(msg)
98
+ end
99
+
100
+ def error(msg)
101
+ super format(msg)
102
+ end
103
+
104
+ end
105
+
106
+
44
107
  logfile = "#{LOG_DIR}/#{ENV["RACK_ENV"]}.log"
45
- LOGGER = Logger.new(logfile,'daily') # daily rotation
46
- LOGGER.level = Logger::DEBUG
108
+ LOGGER = MyLogger.new(logfile,'daily') # daily rotation
109
+
110
+ #LOGGER = MyLogger.new(STDOUT)
111
+ #LOGGER.datetime_format = "%Y-%m-%d %H:%M:%S "
112
+
113
+ #LOGGER.level = Logger::DEBUG
47
114
 
48
115
  if File.exist?(user_file)
49
116
  @@users = YAML.load_file(user_file)
@@ -56,8 +123,7 @@ end
56
123
  begin
57
124
  0 < @@users[:users].keys.length
58
125
  rescue
59
- puts "Please edit #{user_file} and restart your application. Create at least one user with password."
60
- exit
126
+ raise "Please edit #{user_file} and restart your application. Create at least one user with password."
61
127
  end
62
128
 
63
129
  # RDF namespaces
@@ -65,7 +131,12 @@ RDF = Redland::Namespace.new 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
65
131
  OWL = Redland::Namespace.new 'http://www.w3.org/2002/07/owl#'
66
132
  DC = Redland::Namespace.new 'http://purl.org/dc/elements/1.1/'
67
133
  OT = Redland::Namespace.new 'http://www.opentox.org/api/1.1#'
134
+ XML = Redland::Namespace.new 'http://www.w3.org/2001/XMLSchema#'
68
135
 
69
136
  # Regular expressions for parsing classification data
70
- TRUE_REGEXP = /^(true|active|1)/
71
- FALSE_REGEXP = /^(false|inactive|0)/
137
+ TRUE_REGEXP = /^(true|active|$1^)/
138
+ FALSE_REGEXP = /^(false|inactive|$0^)/
139
+
140
+ # Task durations
141
+ DEFAULT_TASK_MAX_DURATION = @@config[:default_task_max_duration]
142
+ EXTERNAL_TASK_MAX_DURATION = @@config[:external_task_max_duration]
data/lib/features.rb ADDED
@@ -0,0 +1,15 @@
1
+ module OpenTox
2
+
3
+ module Feature
4
+
5
+ def self.domain( feature_uri )
6
+ #TODO
7
+ if feature_uri =~ /ambit/
8
+ return nil
9
+ else
10
+ return ["true", "false"]
11
+ end
12
+ end
13
+
14
+ end
15
+ end