bel 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/README.md +4 -2
  4. data/bin/bel +60 -3
  5. data/lib/bel/completion_rule.rb +2 -2
  6. data/lib/bel/evidence_model.rb +5 -0
  7. data/lib/bel/evidence_model/bel_parameter.rb +1 -6
  8. data/lib/bel/evidence_model/buffering_evidence_combiner.rb +182 -0
  9. data/lib/bel/evidence_model/evidence.rb +21 -0
  10. data/lib/bel/evidence_model/hash_map_references.rb +33 -0
  11. data/lib/bel/evidence_model/map_references.rb +30 -0
  12. data/lib/bel/evidence_model/map_references_combiner.rb +30 -0
  13. data/lib/bel/evidence_model/streaming_evidence_combiner.rb +37 -0
  14. data/lib/bel/evidence_model/util.rb +84 -0
  15. data/lib/bel/gen.rb +7 -3
  16. data/lib/bel/gen/annotation.rb +5 -2
  17. data/lib/bel/gen/evidence.rb +10 -4
  18. data/lib/bel/gen/namespace.rb +37 -0
  19. data/lib/bel/gen/parameter.rb +71 -0
  20. data/lib/bel/gen/sample_resources.rb +52 -0
  21. data/lib/bel/gen/statement.rb +33 -0
  22. data/lib/bel/gen/term.rb +33 -0
  23. data/lib/bel/language.rb +4 -4
  24. data/lib/bel/libbel/library_resolver.rb +1 -1
  25. data/lib/bel/namespace.rb +3 -3
  26. data/lib/bel/quoting.rb +218 -14
  27. data/lib/bel/translator.rb +11 -3
  28. data/lib/bel/translator/plugins/bel_script/bel_citation_serialization.rb +2 -2
  29. data/lib/bel/translator/plugins/bel_script/bel_discrete_serialization.rb +1 -1
  30. data/lib/bel/translator/plugins/bel_script/bel_yielder.rb +32 -21
  31. data/lib/bel/translator/plugins/bel_script/evidence_serialization.rb +11 -10
  32. data/lib/bel/translator/plugins/bel_script/evidence_yielder.rb +2 -2
  33. data/lib/bel/translator/plugins/bel_script/translator.rb +1 -1
  34. data/lib/bel/translator/plugins/json_evidence/translator.rb +5 -1
  35. data/lib/bel/translator/plugins/rdf/monkey_patch.rb +4 -0
  36. data/lib/bel/translator/plugins/xbel/evidence_handler.rb +23 -12
  37. data/lib/bel/translator/plugins/xbel/evidence_yielder.rb +1 -1
  38. data/lib/bel/translator/plugins/xbel/translator.rb +4 -4
  39. data/lib/bel/translator/plugins/xbel/xbel_yielder.rb +24 -7
  40. data/lib/bel/util.rb +55 -21
  41. data/lib/bel/version.rb +1 -1
  42. metadata +13 -3
  43. data/lib/bel/gen/bel_expression.rb +0 -128
@@ -0,0 +1,33 @@
1
+ require 'bel'
2
+ require_relative '../gen'
3
+ BEL::Gen.soft_require('rantly')
4
+
5
+ module BEL
6
+ module Gen
7
+
8
+ # The {Statement} module defines methods that generate random BEL
9
+ # {BEL::Model::Statement statements}.
10
+ module Statement
11
+ include BEL::Gen::Term
12
+
13
+ # Array of all BEL 1.0 relationships including both short and long form.
14
+ RELATIONSHIPS = BEL::Language::RELATIONSHIPS.each.to_a.flatten.sort.uniq
15
+
16
+ # Returns a randomly chosen relationship.
17
+ # @return [Symbol] the relationship label (short or long form)
18
+ def relationship
19
+ Rantly {
20
+ choose(*RELATIONSHIPS)
21
+ }
22
+ end
23
+
24
+ # Returns a randomly constructed BEL statement.
25
+ # @return [String] the statement label
26
+ def bel_statement
27
+ sub = bel_term
28
+ obj = bel_term
29
+ "#{sub} #{relationship} #{obj}"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ require 'bel'
2
+ require_relative '../gen'
3
+ BEL::Gen.soft_require('rantly')
4
+
5
+ module BEL
6
+ module Gen
7
+
8
+ # The {Term} module defines methods that generate random BEL
9
+ # {BEL::Model::Term terms}.
10
+ module Term
11
+ include BEL::Gen::Parameter
12
+
13
+ # Array of all BEL 1.0 functions including both short and long form.
14
+ FUNCTIONS = BEL::Language::FUNCTIONS.map { |_, fx|
15
+ [ fx[:short_form], fx[:long_form] ]
16
+ }.flatten.sort.uniq
17
+
18
+ # Returns a randomly chosen function.
19
+ # @return [Symbol] the function label (short or long form)
20
+ def function
21
+ Rantly {
22
+ choose(*FUNCTIONS)
23
+ }
24
+ end
25
+
26
+ # Returns a randomly constructed BEL term.
27
+ # @return [String] the term label
28
+ def bel_term
29
+ "#{function}(#{bel_parameter})"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -32,7 +32,7 @@ module BEL
32
32
  else
33
33
  self.value.to_s
34
34
  end
35
- %Q{SET DOCUMENT #{self.name} = #{ensure_quotes(property_value)}}
35
+ %Q{SET DOCUMENT #{self.name} = #{quote_if_needed(property_value)}}
36
36
  end
37
37
  alias_method :to_s, :to_bel
38
38
  end
@@ -94,10 +94,10 @@ module BEL
94
94
 
95
95
  def to_bel
96
96
  if self.value.respond_to? :each
97
- value = self.value.map {|v| always_quote(v)}
97
+ value = self.value.map {|v| quote(v)}
98
98
  value = "{#{value.join(',')}}"
99
99
  else
100
- value = ensure_quotes(self.value)
100
+ value = quote_if_needed(self.value)
101
101
  end
102
102
  "SET #{self.name} = #{value}"
103
103
  end
@@ -125,7 +125,7 @@ module BEL
125
125
  end
126
126
 
127
127
  def to_bel
128
- %Q{SET STATEMENT_GROUP = #{ensure_quotes(self.name)}}
128
+ %Q{SET STATEMENT_GROUP = #{quote_if_needed(self.name)}}
129
129
  end
130
130
 
131
131
  alias_method :to_s, :to_bel
@@ -40,7 +40,7 @@ module BEL
40
40
 
41
41
  def compiled_library(lib_name)
42
42
  exist = File.method(:exist?)
43
- compiled_library_paths(lib_name).select(&exist).first
43
+ compiled_library_paths(lib_name).detect(&exist)
44
44
  end
45
45
 
46
46
  def compiled_library_paths(lib_name)
@@ -239,9 +239,9 @@ module BEL
239
239
  class NamespaceDefinition
240
240
  include Enumerable
241
241
 
242
- attr_reader :prefix
243
- attr_reader :url
244
- attr_reader :rdf_uri
242
+ attr_accessor :prefix
243
+ attr_accessor :url
244
+ attr_accessor :rdf_uri
245
245
 
246
246
  def initialize(prefix, url, rdf_uri = DEFAULT_URI)
247
247
  @prefix = prefix
@@ -1,32 +1,236 @@
1
1
  module BEL
2
+
3
+ # The Quoting module implements quoting rules consistent with BEL
4
+ # and BEL Script. Double quotes are used to group a string together
5
+ # which may contain whitespace or special characters.
6
+ #
7
+ # A value can either be an identifier or a string value. An
8
+ # identifier can only include the characters +[0-9A-Za-z_]+. A string
9
+ # value is necessary when at least one of +[^0-9A-Za-z_]+ exists in
10
+ # the value.
11
+ #
12
+ # Uses:
13
+ #
14
+ # BEL: The BEL parameters must be an identifier or string value.
15
+ #
16
+ # BEL Script: BEL parameters, document property values, and annotation
17
+ # values must be an identifier or string value.
2
18
  module Quoting
3
19
 
4
- NonWordMatcher = Regexp.compile(/[^0-9a-zA-Z_]/)
5
- KeywordMatcher = Regexp.compile(/^(SET|DEFINE|a|g|p|r|m)$/)
20
+ # Declares BEL Script keywords that cause problems with the OpenBEL
21
+ # Framework parser.
22
+ Keywords = %w(SET DEFINE a g p r m)
6
23
 
7
- def ensure_quotes identifier
8
- return "" unless identifier
9
- identifier.to_s.gsub! '"', '\"'
10
- if quotes_required? identifier
11
- %Q{"#{identifier}"}
24
+ # Regular expression that matches one of {Quoting::Keywords}.
25
+ KeywordMatcher = Regexp.compile(/^(#{Keywords.join('|')})$/)
26
+
27
+ # Regular expression that matches on any non-word character.
28
+ NonWordMatcher = Regexp.compile(/[^0-9a-zA-Z_]/)
29
+
30
+ # Regular expression that matches a value surrounded by unescaped
31
+ # double quotes.
32
+ StrictQuotedMatcher = Regexp.compile(/\A".*?(?<!\\)"\Z/m)
33
+
34
+ # Regular expression that matches a value surrounded by double quotes
35
+ # that may be escaped.
36
+ LenientQuotedMatcher = Regexp.compile(/\A".*?"\Z/m)
37
+
38
+ # Regular expression that matches double quotes that are not escaped.
39
+ QuoteNotEscapedMatcher = Regexp.compile(/(?<!\\)"/m)
40
+
41
+ # Returns +value+ surrounded by double quotes. This method is idempotent
42
+ # so +value+ will only be quoted once regardless of how may times the
43
+ # method is called on it.
44
+ #
45
+ # @example Quoting a BEL parameter.
46
+ # quote("apoptotic process")
47
+ # # => "\"apoptotic process\""
48
+ # @example Escaping quotes within a value.
49
+ # quote("vesicle fusion with \"Golgi apparatus\"")
50
+ # # => "\"vesicle fusion with \\\"Golgi apparatus\\\"\""
51
+ #
52
+ # @parameter [#to_s] value a value to be quoted
53
+ # @return [String] value surrounded by double quotes
54
+ def quote(value)
55
+ string = value.to_s
56
+ unquoted = unquote(string)
57
+ escaped = unquoted.gsub(QuoteNotEscapedMatcher, "\\\"")
58
+ %Q{"#{escaped}"}
59
+ end
60
+
61
+ # Returns +value+ with surrounded quotes removed.
62
+ #
63
+ # @example Unquoting a BEL parameter.
64
+ # unquote("\"apoptotic process\"")
65
+ # # => "apoptotic process"
66
+ # @example Escaped quotes are preserved.
67
+ # unquote("\"vesicle fusion with \"Golgi apparatus\"\"")
68
+ #
69
+ # @parameter [#to_s] value a value to be unquoted
70
+ # @return [String] value with surrounding double quotes removed
71
+ def unquote(value)
72
+ string = value.to_s
73
+ if string =~ StrictQuotedMatcher
74
+ string[1...-1]
75
+ else
76
+ string
77
+ end
78
+ end
79
+
80
+ # Returns +value+ with quoting applied only if necessary. A +value+
81
+ # consisting of only word character (e.g. [0-9A-Za-z_]) does not need
82
+ # quoting. A +value+ consisting of at least one non-word character
83
+ # (e.g. [^0-9A-Za-z_]) will requiring quoting.
84
+ #
85
+ # @example Quotes added when value includes spaces.
86
+ # quote_if_needed("apoptotic process")
87
+ # # => "\"apoptotic process\""
88
+ # @example Quotes added when value includes double quote.
89
+ # quote_if_needed("vesicle fusion with \"Golgi apparatus\"")
90
+ # # => "\"vesicle fusion with \\\"Golgi apparatus\\\"\""
91
+ # @example No quotes necessary for identifier.
92
+ # quote_if_needed("AKT1_HUMAN")
93
+ # # => "AKT1_HUMAN"
94
+ #
95
+ # @parameter [#to_s] value that may be quoted
96
+ # @return [String] original value or quoted value
97
+ def quote_if_needed(value)
98
+ if string_value?(value)
99
+ quote(value)
12
100
  else
13
- identifier
101
+ value.to_s
14
102
  end
15
103
  end
16
104
 
105
+ # Returns whether the +value+ is surrounded by double quotes.
106
+ #
107
+ # @example Returns +true+ when value is quoted.
108
+ # quoted?("\"vesicle fusion with \"Golgi apparatus\"")
109
+ # # => true
110
+ # @example Returns +false+ when value is not quoted.
111
+ # quoted?("apoptotic process")
112
+ # # => false
113
+ #
114
+ # @parameter [#to_s] value to test
115
+ # @return [Boolean] +true+ if +value+ is quoted, +false+ if
116
+ # +value+ is not quoted
117
+ def quoted?(value)
118
+ string = value.to_s
119
+ (string =~ LenientQuotedMatcher) != nil
120
+ end
121
+
122
+ # Returns whether the +value+ is not surrounded by double quotes.
123
+ #
124
+ # @example Returns +true+ when value is not quoted.
125
+ # unquoted?("apoptotic process")
126
+ # # => true
127
+ # @example Returns +false+ when value is quoted.
128
+ # unquoted?("\"vesicle fusion with \"Golgi apparatus\"")
129
+ # # => false
130
+ #
131
+ # @parameter [#to_s] value to test
132
+ # @return [Boolean] +true+ if +value+ is not quoted, +false+ if
133
+ # +value+ is quoted
134
+ def unquoted?(value)
135
+ !quoted?(value)
136
+ end
137
+
138
+ # Returns whether the +value+ represents an identifier. An
139
+ # identifier consists of only word characters (e.g. [0-9A-Za-z_]).
140
+ #
141
+ # @example Returns +true+ when representing an identifier.
142
+ # identifier_value?("AKT1_HUMAN")
143
+ # # => true
144
+ # @example Returns +false+ when not representing an identifier.
145
+ # identifier_value?("apoptotic process")
146
+ # # => false
147
+ #
148
+ # @parameter [#to_s] value to test
149
+ # @return [Boolean] +true+ if +value+ is an identifier,
150
+ # +false+ if +value+ is not an identifier
151
+ def identifier_value?(value)
152
+ string = value.to_s
153
+ [NonWordMatcher, KeywordMatcher].none? { |matcher|
154
+ matcher.match string
155
+ }
156
+ end
157
+
158
+ # Returns whether the +value+ represents a string value. A string
159
+ # value consists of at least one non-word character
160
+ # (e.g. [^0-9A-Za-z_]).
161
+ #
162
+ # @example Returns +true+ when representing a string value.
163
+ # string_value?("apoptotic process")
164
+ # # => true
165
+ # @example Returns +false+ when not representing a string value.
166
+ # string_value?("AKT1_HUMAN")
167
+ # # => false
168
+ #
169
+ # @parameter [#to_s] value to test
170
+ # @return [Boolean] +true+ if +value+ is a string value,
171
+ # +false+ if +value+ is not a string value
172
+ def string_value?(value)
173
+ string = value.to_s
174
+ [NonWordMatcher, KeywordMatcher].any? { |matcher|
175
+ matcher.match string
176
+ }
177
+ end
178
+
179
+ ## Deprecated, remove in [0.6.0].
180
+
181
+ # @deprecated Use {#quote_if_needed} instead. Will be removed in a
182
+ # future release.
183
+ def ensure_quotes identifier
184
+ warn <<-DOC.gsub(/^\s+/, '')
185
+ Deprecation Warning
186
+ -------------------
187
+ The BEL::Quoting::ensure_quotes method is deprecated and
188
+ will be removed in a future relase.
189
+ Call module method BEL::Quoting.quote_if_needed instead.
190
+ DOC
191
+ quote_if_needed(identifier)
192
+ end
193
+
194
+ # @deprecated Use {#unquote} instead. Will be removed in a
195
+ # future release.
17
196
  def remove_quotes identifier
18
- identifier.to_s.gsub!(/\A"|"\Z/, '')
19
- identifier
197
+ warn <<-DOC.gsub(/^\s+/, '')
198
+ Deprecation Warning
199
+ -------------------
200
+ The BEL::Quoting::remove_quotes method is deprecated and
201
+ will be removed in a future relase.
202
+ Call module method BEL::Quoting.unquote instead.
203
+ DOC
204
+ unquote(identifier)
20
205
  end
21
206
 
207
+ # @deprecated Use {#quote} instead. Will be removed in a
208
+ # future release.
22
209
  def always_quote identifier
23
- return "" unless identifier
24
- identifier.to_s.gsub! '"', '\"'
25
- %Q("#{identifier}")
210
+ warn <<-DOC.gsub(/^\s+/, '')
211
+ Deprecation Warning
212
+ -------------------
213
+ The BEL::Quoting::always_quote method is deprecated and
214
+ will be removed in a future relase.
215
+ Call module method BEL::Quoting.quote instead.
216
+ DOC
217
+ quote(identifier)
26
218
  end
27
219
 
220
+ # @deprecated Use {#quoted?} or {#unquoted?} instead. Will be removed
221
+ # in a future release.
28
222
  def quotes_required? identifier
29
- [NonWordMatcher, KeywordMatcher].any? { |m| m.match identifier.to_s }
223
+ warn <<-DOC.gsub(/^\s+/, '')
224
+ Deprecation Warning
225
+ -------------------
226
+ The BEL::Quoting::quotes_required? method is deprecated and
227
+ will be removed in a future relase.
228
+ You can use BEL::Quoting.quoted? and BEL::Quoting.unquoted?
229
+ going forward.
230
+ DOC
231
+ [NonWordMatcher, KeywordMatcher].any? { |m|
232
+ m.match identifier.to_s
233
+ }
30
234
  end
31
235
  end
32
236
  end
@@ -39,8 +39,9 @@ module BEL
39
39
  #
40
40
  # @param [#to_s] value an id, media type, or file extension value that
41
41
  # identifies a translator plugin
42
- # @return [#create_translator] if a single a translator plugin was found
43
- # @return [Array<#create_translator>] if multiple translator plugins
42
+ # @return [nil] when no translator plugin was found
43
+ # @return [#create_translator] when single translator plugin was found
44
+ # @return [Array<#create_translator>] when multiple translator plugins
44
45
  # were found
45
46
  def self.for(value)
46
47
  return nil unless value
@@ -57,7 +58,14 @@ module BEL
57
58
  match |= (t.file_extensions.include?(value_symbol))
58
59
  match
59
60
  }
60
- matches.size == 1 ? matches.first : matches
61
+
62
+ if matches.empty?
63
+ nil
64
+ elsif matches.size == 1
65
+ matches.first
66
+ else
67
+ matches
68
+ end
61
69
  end
62
70
  end
63
71
 
@@ -44,12 +44,12 @@ module BEL::Translator::Plugins::BelScript::BelCitationSerialization
44
44
 
45
45
  # Reset cumulative annotations if new citation.
46
46
  if cumulative_citation == nil
47
- bel << %Q{SET STATEMENT_GROUP = #{ensure_quotes(evidence.citation.id)}\n}
47
+ bel << %Q{SET STATEMENT_GROUP = #{quote_if_needed(evidence.citation.id)}\n}
48
48
  cumulative_annotations.clear
49
49
  elsif evidence.citation != cumulative_citation
50
50
  bel << %Q{UNSET STATEMENT_GROUP\n}
51
51
  bel << "\n\n"
52
- bel << %Q{SET STATEMENT_GROUP = #{ensure_quotes(evidence.citation.id)}\n}
52
+ bel << %Q{SET STATEMENT_GROUP = #{quote_if_needed(evidence.citation.id)}\n}
53
53
  cumulative_annotations.clear
54
54
  end
55
55
 
@@ -1,4 +1,4 @@
1
- require_relative 'evidence_serialization.rb'
1
+ require_relative 'evidence_serialization'
2
2
 
3
3
  # BEL Script evidence serialization that writes each evidence with their full
4
4
  # set of annotations (i.e. includes all `SET` and necessary `UNSET` records).
@@ -25,8 +25,11 @@ module BEL::Translator::Plugins
25
25
  # +:citation+ => {BelCitationSerialization}; otherwise the default
26
26
  # of {BelCitationSerialization} is used
27
27
  def initialize(data, options = {})
28
- @data = data
29
- @write_header = options.fetch(:write_header, true)
28
+ @data = data
29
+ @streaming = options.fetch(:streaming, false)
30
+ @write_header = options.fetch(:write_header, true)
31
+ @annotation_reference_map = options.fetch(:annotation_reference_map, nil)
32
+ @namespace_reference_map = options.fetch(:namespace_reference_map, nil)
30
33
 
31
34
  # augment self with BEL serialization stategy.
32
35
  serialization = options[:serialization]
@@ -56,20 +59,31 @@ module BEL::Translator::Plugins
56
59
 
57
60
  def each
58
61
  if block_given?
62
+ combiner =
63
+ if @streaming
64
+ BEL::Model::StreamingEvidenceCombiner.new(@data)
65
+ elsif @annotation_reference_map && @namespace_reference_map
66
+ BEL::Model::MapReferencesCombiner.new(
67
+ @data,
68
+ BEL::Model::HashMapReferences.new(
69
+ @annotation_reference_map,
70
+ @namespace_reference_map
71
+ )
72
+ )
73
+ else
74
+ BEL::Model::BufferingEvidenceCombiner.new(@data)
75
+ end
76
+
59
77
  header_flag = true
60
- @data.each { |evidence|
78
+ combiner.each { |evidence|
61
79
 
62
80
  # serialize evidence
63
81
  bel = to_bel(evidence)
64
82
 
65
83
  if @write_header && header_flag
66
84
  yield document_header(evidence.metadata.document_header)
67
- yield namespaces(
68
- evidence.references.namespaces
69
- )
70
- yield annotations(
71
- evidence.references.annotations
72
- )
85
+ yield namespaces(combiner.namespace_references)
86
+ yield annotations(combiner.annotation_references)
73
87
 
74
88
  yield <<-COMMENT.gsub(/^\s+/, '')
75
89
  ###############################################
@@ -118,18 +132,16 @@ module BEL::Translator::Plugins
118
132
  bel
119
133
  end
120
134
 
121
- def annotations(annotations)
135
+ def annotations(annotation_references)
122
136
  bel = <<-COMMENT.gsub(/^\s+/, '')
123
137
  ###############################################
124
138
  # Annotation Definitions Section
125
139
  COMMENT
126
140
 
127
- return bel unless annotations
141
+ return bel unless annotation_references
128
142
 
129
- annotations.reduce(bel) { |bel, annotation|
130
- keyword = annotation[:keyword]
131
- type = annotation[:type]
132
- domain = annotation[:domain]
143
+ annotation_references.reduce(bel) { |bel, ref|
144
+ keyword, type, domain = ref.values_at(:keyword, :type, :domain)
133
145
  bel << "DEFINE ANNOTATION #{keyword} AS "
134
146
 
135
147
  case type.to_sym
@@ -147,18 +159,17 @@ module BEL::Translator::Plugins
147
159
  bel
148
160
  end
149
161
 
150
- def namespaces(namespaces)
162
+ def namespaces(namespace_references)
151
163
  bel = <<-COMMENT.gsub(/^\s+/, '')
152
164
  ###############################################
153
165
  # Namespace Definitions Section
154
166
  COMMENT
155
167
 
156
- return bel unless namespaces
168
+ return bel unless namespace_references
157
169
 
158
- namespaces.reduce(bel) { |bel, namespace|
159
- keyword = namespace[:keyword]
160
- uri = namespace[:uri]
161
- bel << %Q{DEFINE NAMESPACE #{keyword} AS URL "#{uri}"\n}
170
+ namespace_references.reduce(bel) { |bel, ref|
171
+ keyword, url = ref.values_at(:keyword, :uri)
172
+ bel << %Q{DEFINE NAMESPACE #{keyword} AS URL "#{url}"\n}
162
173
  bel
163
174
  }
164
175
  bel << "\n"