yaml-ld 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/psych/amazing_print.rb +61 -0
  4. data/lib/yaml_ld/api.rb +69 -11
  5. data/lib/yaml_ld/format.rb +25 -0
  6. data/lib/yaml_ld/reader.rb +3 -1
  7. data/lib/yaml_ld/representation.rb +256 -0
  8. data/lib/yaml_ld/version.rb +1 -1
  9. data/lib/yaml_ld.rb +1 -0
  10. data/spec/api_spec.rb +21 -27
  11. data/spec/compact_spec.rb +1 -1
  12. data/spec/expand_spec.rb +1 -1
  13. data/spec/format_spec.rb +56 -0
  14. data/spec/frame_spec.rb +55 -110
  15. data/spec/from_rdf_spec.rb +3 -3
  16. data/spec/matchers.rb +2 -2
  17. data/spec/reader_spec.rb +1 -1
  18. data/spec/representation_spec.rb +170 -0
  19. data/spec/spec_helper.rb +1 -0
  20. data/spec/test-files/test-1-compacted.yamlld +9 -0
  21. data/spec/test-files/test-1-context.yamlld +6 -0
  22. data/spec/test-files/test-1-expanded.yamlld +8 -0
  23. data/spec/test-files/test-1-input.yamlld +1 -0
  24. data/spec/test-files/test-2-compacted.yamlld +17 -0
  25. data/spec/test-files/test-2-context.yamlld +5 -0
  26. data/spec/test-files/test-2-expanded.yamlld +21 -0
  27. data/spec/test-files/test-3-compacted.yamlld +14 -0
  28. data/spec/test-files/test-3-context.yamlld +11 -0
  29. data/spec/test-files/test-3-expanded.yamlld +9 -0
  30. data/spec/test-files/test-4-compacted.yamlld +10 -0
  31. data/spec/test-files/test-4-context.yamlld +5 -0
  32. data/spec/test-files/test-4-expanded.yamlld +9 -0
  33. data/spec/test-files/test-5-compacted.yamlld +11 -0
  34. data/spec/test-files/test-5-context.yamlld +5 -0
  35. data/spec/test-files/test-5-expanded.yamlld +12 -0
  36. data/spec/test-files/test-6-compacted.yamlld +13 -0
  37. data/spec/test-files/test-6-context.yamlld +5 -0
  38. data/spec/test-files/test-6-expanded.yamlld +12 -0
  39. data/spec/test-files/test-7-compacted.yamlld +17 -0
  40. data/spec/test-files/test-7-context.yamlld +4 -0
  41. data/spec/test-files/test-7-expanded.yamlld +23 -0
  42. data/spec/test-files/test-8-compacted.yamlld +25 -0
  43. data/spec/test-files/test-8-context.yamlld +12 -0
  44. data/spec/test-files/test-8-expanded.yamlld +23 -0
  45. data/spec/test-files/test-8-frame.yamlld +15 -0
  46. data/spec/test-files/test-8-framed.yamlld +22 -0
  47. data/spec/test-files/test-9-compacted.yamlld +31 -0
  48. data/spec/test-files/test-9-context.yamlld +20 -0
  49. data/spec/test-files/test-9-expanded.yamlld +17 -0
  50. data/spec/to_rdf_spec.rb +10 -8
  51. metadata +89 -61
  52. data/spec/test-files/test-1-expanded.jsonld +0 -5
  53. data/spec/test-files/test-2-compacted.jsonld +0 -20
  54. data/spec/test-files/test-2-context.jsonld +0 -7
  55. data/spec/test-files/test-2-expanded.jsonld +0 -16
  56. data/spec/test-files/test-3-compacted.jsonld +0 -11
  57. data/spec/test-files/test-3-context.jsonld +0 -8
  58. data/spec/test-files/test-3-expanded.jsonld +0 -10
  59. data/spec/test-files/test-4-compacted.jsonld +0 -10
  60. data/spec/test-files/test-4-context.jsonld +0 -7
  61. data/spec/test-files/test-4-expanded.jsonld +0 -6
  62. data/spec/test-files/test-5-compacted.jsonld +0 -13
  63. data/spec/test-files/test-5-context.jsonld +0 -7
  64. data/spec/test-files/test-5-expanded.jsonld +0 -9
  65. data/spec/test-files/test-6-compacted.jsonld +0 -10
  66. data/spec/test-files/test-6-context.jsonld +0 -7
  67. data/spec/test-files/test-6-expanded.jsonld +0 -10
  68. data/spec/test-files/test-7-compacted.jsonld +0 -23
  69. data/spec/test-files/test-7-context.jsonld +0 -4
  70. data/spec/test-files/test-7-expanded.jsonld +0 -20
  71. data/spec/test-files/test-8-compacted.jsonld +0 -34
  72. data/spec/test-files/test-8-context.jsonld +0 -11
  73. data/spec/test-files/test-8-expanded.jsonld +0 -24
  74. data/spec/test-files/test-8-frame.jsonld +0 -18
  75. data/spec/test-files/test-8-framed.jsonld +0 -25
  76. data/spec/test-files/test-9-compacted.jsonld +0 -20
  77. data/spec/test-files/test-9-context.jsonld +0 -13
  78. data/spec/test-files/test-9-expanded.jsonld +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c57dc93098e9dbf2ab68a8ffd6342fe1b838015e1335496ed918293d1fee4381
4
- data.tar.gz: d1f7c955b184e680448354bab9f3b4ced1c0f11150f7c9cec67ec6feed4148fd
3
+ metadata.gz: c1da1570067d55e5cf71f757cfb5a2e2d8313dc9f63fe045bafde65dcb220a02
4
+ data.tar.gz: '0096c9115d43889554b3e7f6d7ac92d7c8b94b4adce85d8b811f0321186d9dd0'
5
5
  SHA512:
6
- metadata.gz: 4917c179d4ef8c0d1f472e9e2be96fbfb82a76d3b554f2a90fdc610864a4ff0355e3a80806d2bf02de75831f84d17bebc056673d41f4ccf3e7cd2d80726864cd
7
- data.tar.gz: f8d302927836ecfaf4c8e7d6afb209071e772fbd6e75a5622683fd35fbf2f54a123d8e73752934e9d441c791cb6e712e05c0b0b39f0cb5105e9383008f2712be
6
+ metadata.gz: ea19a5310f145f5abb060fd54903f42312a60d197a268ec3a66ba4421ed0497e3f93b1b8b930cda9797bc00aa92d18d9ca1fb7a633cdf9bf7f37bcac1cc50426
7
+ data.tar.gz: a861b85988b30f42bb3afdf7d2ecae69663ca069081b4921303c07c72bc17bc19d4fea27c21ce8a2d027b256a5079581deeb5c9dc0a864fe95edd5420b9a8b8b
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Amazing Print extensions for Psych
4
+ #------------------------------------------------------------------------------
5
+ module AmazingPrint
6
+ module Psych
7
+ def self.included(base)
8
+ base.send :alias_method, :cast_without_psych, :cast
9
+ base.send :alias_method, :cast, :cast_with_psych
10
+ end
11
+
12
+ # Add Psych Node names to the dispatcher pipeline.
13
+ #------------------------------------------------------------------------------
14
+ def cast_with_psych(object, type)
15
+ cast = cast_without_psych(object, type)
16
+ if (defined?(::Psych::Nodes::Node) && object.is_a?(::Psych::Nodes::Node))
17
+ cast = :psych_node
18
+ end
19
+ cast
20
+ end
21
+
22
+ STYLES = %w(ANY BLOCK FLOW) #------------------------------------------------------------------------------
23
+ def awesome_psych_node(object)
24
+ contents = []
25
+ contents << colorize(object.class.name.split('::').last, :class)
26
+ contents << colorize("!<#{object.tag}>", :args) if object.tag
27
+ contents << colorize("&#{object.anchor}", :args) if object.respond_to?(:anchor) && object.anchor
28
+ contents << colorize("(implicit)", :args) if object.respond_to?(:implicit) && object.implicit
29
+ contents << colorize("(implicit end)", :args) if object.respond_to?(:implicit_end) && object.implicit_end
30
+ case object
31
+ when ::Psych::Nodes::Stream
32
+ contents << awesome_array(object.children)
33
+ when ::Psych::Nodes::Document
34
+ contents << colorize('%TAG(' + object.tag_directives.flatten.join(' ') + ')', :args) unless Array(object.tag_directives).empty?
35
+ contents << colorize("version #{object.version.join('.')}", :args) if object.version
36
+ contents << awesome_array(object.children)
37
+ when ::Psych::Nodes::Sequence
38
+ style = %w(ANY BLOCK FLOW)[object.style.to_i]
39
+ contents << awesome_array(object.children)
40
+ when ::Psych::Nodes::Mapping
41
+ style = %w(ANY BLOCK FLOW)[object.style.to_i]
42
+ contents << colorize(style, :args) if object.style
43
+ contents << awesome_hash(object.children.each_slice(2).to_h)
44
+ when ::Psych::Nodes::Scalar
45
+ style = %w(ANY PLAIN SINGLE_QUOTED DOUBLE_QUOTED LITERAL FOLDED)[object.style.to_i]
46
+ contents << colorize(style, :args) if object.style
47
+ contents << colorize("(plain)", :args) if object.plain
48
+ contents << colorize("(quoted)", :args) if object.quoted
49
+ contents << awesome_simple(object.value.inspect, :variable)
50
+ when ::Psych::Nodes::Alias
51
+ # No children
52
+ else
53
+ "Unknown node type: #{object.inspect}"
54
+ end
55
+
56
+ contents.join(' ')
57
+ end
58
+ end
59
+ end
60
+
61
+ AmazingPrint::Formatter.include AmazingPrint::Psych
data/lib/yaml_ld/api.rb CHANGED
@@ -9,7 +9,6 @@ module YAML_LD
9
9
  # @see https://www.w3.org/TR/json-ld11-api/#the-application-programming-interface
10
10
  # @author [Gregg Kellogg](http://greggkellogg.net/)
11
11
  class API < ::JSON::LD::API
12
-
13
12
  # The following constants are used to reduce object allocations
14
13
  LINK_REL_CONTEXT = %w(rel http://www.w3.org/ns/yaml-ld#context).freeze
15
14
  LINK_REL_ALTERNATE = %w(rel alternate).freeze
@@ -30,6 +29,35 @@ module YAML_LD
30
29
  # A Serializer method used for generating the YAML serialization of the result. If absent, the internal Ruby objects are returned, which can be transformed to YAML externally via `#to_yaml`.
31
30
  # See {YAML_LD::API.serializer}.
32
31
  # @param [Hash{Symbol => Object}] options
32
+ # @option options [RDF::URI, String, #to_s] :base
33
+ # The Base IRI to use when expanding the document. This overrides the value of `input` if it is a _IRI_. If not specified and `input` is not an _IRI_, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context. If not specified, and a base IRI is found from `input`, options[:base] will be modified with this value.
34
+ # @option options [Boolean] :compactArrays (true)
35
+ # If set to `true`, the JSON-LD processor replaces arrays with just one element with that element during compaction. If set to `false`, all arrays will remain arrays even if they have just one element.
36
+ # @option options [Boolean] :compactToRelative (true)
37
+ # Creates document relative IRIs when compacting, if `true`, otherwise leaves expanded.
38
+ # @option options [Proc] :documentLoader
39
+ # The callback of the loader to be used to retrieve remote documents and contexts. If specified, it must be used to retrieve remote documents and contexts; otherwise, if not specified, the processor's built-in loader must be used. See {documentLoader} for the method signature.
40
+ # @option options [String, #read, Hash, Array, JSON::LD::Context] :expandContext
41
+ # A context that is used to initialize the active context when expanding a document.
42
+ # @option options [Boolean] :extendedYAML (false)
43
+ # Use the expanded internal representation.
44
+ # @option options [Boolean] :extractAllScripts
45
+ # If set, when given an HTML input without a fragment identifier, extracts all `script` elements with type `application/ld+json` into an array during expansion.
46
+ # @option options [Boolean, String, RDF::URI] :flatten
47
+ # If set to a value that is not `false`, the JSON-LD processor must modify the output of the Compaction Algorithm or the Expansion Algorithm by coalescing all properties associated with each subject via the Flattening Algorithm. The value of `flatten must` be either an _IRI_ value representing the name of the graph to flatten, or `true`. If the value is `true`, then the first graph encountered in the input document is selected and flattened.
48
+ # @option options [String] :language
49
+ # When set, this has the effect of inserting a context definition with `@language` set to the associated value, creating a default language for interpreting string values.
50
+ # @option options [Boolean] :lowercaseLanguage
51
+ # By default, language tags are left as is. To normalize to lowercase, set this option to `true`.
52
+ # @option options [Boolean] :ordered (true)
53
+ # Order traversal of dictionary members by key when performing algorithms.
54
+ # @option options [Boolean] :rdfstar (false)
55
+ # support parsing JSON-LD-star statement resources.
56
+ # @option options [Boolean] :rename_bnodes (true)
57
+ # Rename bnodes as part of expansion, or keep them the same.
58
+ # @option options [Boolean] :unique_bnodes (false)
59
+ # Use unique bnode identifiers, defaults to using the identifier which the node was originally initialized with (if any).
60
+ # @option options [Boolean] :validate Validate input, if a string or readable object.
33
61
  # @raise [JsonLdError]
34
62
  # @yield YAML_LD, base_iri
35
63
  # @yieldparam [Array<Hash>] yamlld
@@ -46,7 +74,10 @@ module YAML_LD
46
74
  **options,
47
75
  &block)
48
76
  JSON::LD::API.expand(input,
77
+ allowed_content_types: %r(application/(.+\+)?yaml),
49
78
  documentLoader: documentLoader,
79
+ extendedRepresentation: options[:extendedYAML],
80
+ processingMode: 'json-ld-1.1',
50
81
  serializer: serializer,
51
82
  **options,
52
83
  &block)
@@ -68,7 +99,7 @@ module YAML_LD
68
99
  # See {YAML_LD::API.serializer}.
69
100
  # @param [Boolean] expanded (false) Input is already expanded
70
101
  # @param [Hash{Symbol => Object}] options
71
- # @option options (see #initialize)
102
+ # @option options (see expand)
72
103
  # @yield YAML_LD
73
104
  # @yieldparam [Array<Hash>] yamlld
74
105
  # The expanded YAML-LD document
@@ -83,7 +114,10 @@ module YAML_LD
83
114
  **options,
84
115
  &block)
85
116
  JSON::LD::API.compact(input, context, expanded: expanded,
117
+ allowed_content_types: %r(application/(.+\+)?yaml),
86
118
  documentLoader: documentLoader,
119
+ extendedRepresentation: options[:extendedYAML],
120
+ processingMode: 'json-ld-1.1',
87
121
  serializer: serializer,
88
122
  **options,
89
123
  &block)
@@ -103,9 +137,11 @@ module YAML_LD
103
137
  # A Serializer method used for generating the YAML serialization of the result. If absent, the internal Ruby objects are returned, which can be transformed to YAML externally via `#to_yaml`.
104
138
  # See {YAML_LD::API.serializer}.
105
139
  # @param [Hash{Symbol => Object}] options
106
- # @option options (see #initialize)
140
+ # @option options (see expand)
107
141
  # @option options [Boolean] :createAnnotations
108
142
  # Unfold embedded nodes which can be represented using `@annotation`.
143
+ # @option options [Boolean] :extendedYAML (false)
144
+ # Use the expanded internal representation.
109
145
  # @yield YAML_LD
110
146
  # @yieldparam [Array<Hash>] yamlld
111
147
  # The expanded YAML-LD document
@@ -119,7 +155,10 @@ module YAML_LD
119
155
  **options,
120
156
  &block)
121
157
  JSON::LD::API.flatten(input, context, expanded: expanded,
158
+ allowed_content_types: %r(application/(.+\+)?yaml),
122
159
  documentLoader: documentLoader,
160
+ extendedRepresentation: options[:extendedYAML],
161
+ processingMode: 'json-ld-1.1',
123
162
  serializer: serializer,
124
163
  **options,
125
164
  &block)
@@ -135,11 +174,13 @@ module YAML_LD
135
174
  # @param [String, #read, Hash, Array] frame
136
175
  # The frame to use when re-arranging the data.
137
176
  # @param [Boolean] expanded (false) Input is already expanded
138
- # @option options (see #initialize)
177
+ # @option options (see expand)
139
178
  # @option options ['@always', '@link', '@once', '@never'] :embed ('@once')
140
179
  # a flag specifying that objects should be directly embedded in the output, instead of being referred to by their IRI.
141
180
  # @option options [Boolean] :explicit (false)
142
181
  # a flag specifying that for properties to be included in the output, they must be explicitly declared in the framing context.
182
+ # @option options [Boolean] :extendedYAML (false)
183
+ # Use the expanded internal representation.
143
184
  # @option options [Boolean] :requireAll (false)
144
185
  # A flag specifying that all properties present in the input frame must either have a default value or be present in the JSON-LD input for the frame to match.
145
186
  # @option options [Boolean] :omitDefault (false)
@@ -160,7 +201,10 @@ module YAML_LD
160
201
  **options,
161
202
  &block)
162
203
  JSON::LD::API.frame(input, frame, expanded: expanded,
204
+ allowed_content_types: %r(application/(.+\+)?yaml),
163
205
  documentLoader: documentLoader,
206
+ extendedRepresentation: options[:extendedYAML],
207
+ processingMode: 'json-ld-1.1',
164
208
  serializer: serializer,
165
209
  **options,
166
210
  &block)
@@ -172,7 +216,9 @@ module YAML_LD
172
216
  # @param [String, #read, Hash, Array] input
173
217
  # The YAML-LD object to process when outputting statements.
174
218
  # @param [Boolean] expanded (false) Input is already expanded
175
- # @option options (see #initialize)
219
+ # @option options (see expand)
220
+ # @option options [Boolean] :extendedYAML (false)
221
+ # Use the expanded internal representation.
176
222
  # @option options [Boolean] :produceGeneralizedRdf (false)
177
223
  # If true, output will include statements having blank node predicates, otherwise they are dropped.
178
224
  # @raise [JsonLdError]
@@ -184,7 +230,10 @@ module YAML_LD
184
230
  **options,
185
231
  &block)
186
232
  JSON::LD::API.toRdf(input, expanded: expanded,
233
+ allowed_content_types: %r(application/(.+\+)?yaml),
187
234
  documentLoader: documentLoader,
235
+ extendedRepresentation: options[:extendedYAML],
236
+ processingMode: 'json-ld-1.1',
188
237
  **options,
189
238
  &block)
190
239
  end
@@ -202,7 +251,9 @@ module YAML_LD
202
251
  # A Serializer method used for generating the YAML serialization of the result. If absent, the internal Ruby objects are returned, which can be transformed to YAML externally via `#to_yaml`.
203
252
  # See {YAML_LD::API.serializer}.
204
253
  # @param [Hash{Symbol => Object}] options
205
- # @option options (see #initialize)
254
+ # @option options (see expand)
255
+ # @option options [Boolean] :extendedYAML (false)
256
+ # Use the expanded internal representation.
206
257
  # @yield jsonld
207
258
  # @yieldparam [Hash] jsonld
208
259
  # The JSON-LD document in expanded form
@@ -215,10 +266,13 @@ module YAML_LD
215
266
  **options,
216
267
  &block)
217
268
  JSON::LD::API.fromRdf(input,
218
- useRdfType: useRdfType,
219
- useNativeTypes: useNativeTypes,
269
+ allowed_content_types: %r(application/(.+\+)?yaml),
220
270
  documentLoader: documentLoader,
271
+ extendedRepresentation: options[:extendedYAML],
272
+ processingMode: 'json-ld-1.1',
221
273
  serializer: serializer,
274
+ useRdfType: useRdfType,
275
+ useNativeTypes: useNativeTypes,
222
276
  **options,
223
277
  &block)
224
278
  end
@@ -257,7 +311,7 @@ module YAML_LD
257
311
  content = case content_type
258
312
  when nil, %r(application/(\w+\+)*yaml)
259
313
  # Parse YAML
260
- Psych.safe_load(url.read, aliases: true)
314
+ Representation.load(url.read, filename: url.to_s, **options)
261
315
  else
262
316
  url.read
263
317
  end
@@ -267,7 +321,11 @@ module YAML_LD
267
321
  contextUrl: context_url))
268
322
  elsif url.to_s.match?(/\.yaml\w*$/) || content_type.to_s.match?(%r(application/(\w+\+)*yaml))
269
323
  # Parse YAML
270
- block.call(RemoteDocument.new(Psych.load_file(url.to_s, aliases: true),
324
+ content = Representation.load(RDF::Util::File.open_file(url.to_s).read,
325
+ filename: url.to_s,
326
+ **options)
327
+
328
+ block.call(RemoteDocument.new(content,
271
329
  documentUrl: base_uri,
272
330
  contentType: content_type,
273
331
  contextUrl: context_url))
@@ -288,7 +346,7 @@ module YAML_LD
288
346
  # options passed from the invoking context.
289
347
  def self.serializer(object, *args, **options)
290
348
  # de-alias any objects to avoid the use of aliases and anchors
291
- "%YAML 1.2\n" + Psych.dump(object, **options)
349
+ "%YAML 1.2\n" + Representation.dump(object, **options)
292
350
  end
293
351
  end
294
352
  end
@@ -1,5 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  # frozen_string_literal: true
3
+ require 'json/ld/format'
4
+
3
5
  module YAML_LD
4
6
  ##
5
7
  # YAML-LD format specification.
@@ -28,6 +30,25 @@ module YAML_LD
28
30
  reader { YAML_LD::Reader }
29
31
  writer { YAML_LD::Writer }
30
32
 
33
+ # Specify how to execute CLI commands for each supported format. These are added to the `LD_FORMATS` defined for `JSON::LD::Format`
34
+ # Derived formats (e.g., YAML-LD) define their own entrypoints.
35
+ LD_FORMATS = {
36
+ yamlld: {
37
+ expand: ->(input, **options) {
38
+ YAML_LD::API.expand(input, validate: false, **options)
39
+ },
40
+ compact: ->(input, **options) {
41
+ YAML_LD::API.compact(input, options[:context], **options)
42
+ },
43
+ flatten: ->(input, **options) {
44
+ YAML_LD::API.flatten(input, options[:context], **options)
45
+ },
46
+ frame: ->(input, **options) {
47
+ YAML_LD::API.frame(input, options[:frame], **options)
48
+ },
49
+ }
50
+ }
51
+
31
52
  ##
32
53
  # Sample detection to see if it matches YAML-LD
33
54
  #
@@ -54,3 +75,7 @@ module YAML_LD
54
75
  end
55
76
  end
56
77
  end
78
+
79
+
80
+ # Load into JSON::LD::Format::LD_FORMATS
81
+ ::JSON::LD::Format.const_get(:LD_FORMATS).merge!(::YAML_LD::Format::LD_FORMATS)
@@ -24,7 +24,9 @@ module YAML_LD
24
24
  def initialize(input = $stdin,
25
25
  documentLoader: YAML_LD::API.method(:documentLoader),
26
26
  **options, &block)
27
- input = StringIO.new(input) if input.is_a?(String)
27
+ input = StringIO.new(input).tap do |d|
28
+ d.define_singleton_method(:content_type) {'application/ld+yaml'}
29
+ end if input.is_a?(String)
28
30
  super(input, documentLoader: documentLoader, **options, &block)
29
31
  end
30
32
 
@@ -0,0 +1,256 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+ require 'psych'
4
+ require 'rdf/xsd'
5
+
6
+ module YAML_LD
7
+ ##
8
+ # Transforms a Psych AST to the JSON-LD (extended) Internal Representation build using `Psych.parse`, `.parse_stream`, or `parse_file`.
9
+ #
10
+ # FIXME: Aliases
11
+ module Representation
12
+ ###
13
+ # Load multiple documents given in +yaml+. Returns the parsed documents
14
+ # as a list. If a block is given, each document will be converted to Ruby
15
+ # and passed to the block during parsing
16
+ #
17
+ # @example
18
+ #
19
+ # load_stream("--- foo\n...\n--- bar\n...") # => ['foo', 'bar']
20
+ #
21
+ # list = []
22
+ # load_stream("--- foo\n...\n--- bar\n...") do |ruby|
23
+ # list << ruby
24
+ # end
25
+ # list # => ['foo', 'bar']
26
+ #
27
+ # @param [String] yaml
28
+ # @param [String] filename
29
+ # @param [Object] fallback
30
+ # @param [Hash{Symbol => Object}] options
31
+ # @return [Array<Object>]
32
+ def load_stream(yaml, filename: nil, fallback: [], **options)
33
+ result = if block_given?
34
+ Psych.parse_stream(yaml, filename: filename) do |node|
35
+ yield as_jsonld_ir(node, **options)
36
+ end
37
+ else
38
+ as_jsonld_ir(Psych.parse_stream(yaml, filename: filename), **options)
39
+ end
40
+
41
+ result.is_a?(Array) && result.empty? ? fallback : result
42
+ end
43
+ module_function :load_stream
44
+
45
+ ##
46
+ # Load a single document from +yaml+.
47
+ # @param (see load_stream)
48
+ # @return [Object]
49
+ def load(yaml, filename: nil, fallback: nil, **options)
50
+ result = if block_given?
51
+ load_stream(yaml, filename: filename, **options) do |node|
52
+ yield node.first
53
+ end
54
+ else
55
+ load_stream(yaml, filename: filename, **options).first
56
+ end
57
+
58
+ result || fallback
59
+ end
60
+ module_function :load
61
+
62
+ ##
63
+ # Dump internal representaiton to YAML
64
+ #
65
+ # @option options[Boolean] :extendedYAML
66
+ # Allows the use of aliases and encodes RDF::Literals
67
+ def dump(ir, **options)
68
+ if options[:extendedYAML]
69
+ options = {
70
+ aliases: true,
71
+ permitted_classes: [RDF::Literal]
72
+ }.merge(options)
73
+ else
74
+ # Deep duplicate representation to avoid alias nodes
75
+ ir = deep_dup(ir)
76
+ end
77
+ visitor = Representation::IRTree.create(options)
78
+ visitor << ir
79
+ visitor.tree.yaml
80
+ end
81
+ module_function :dump
82
+
83
+ def deep_dup(obj)
84
+ case obj
85
+ when Array then obj.map {|e| deep_dup(e)}
86
+ when Hash then obj.inject({}) {|memo, (k,v)| memo.merge(k => deep_dup(v))}
87
+ else obj # No need to de-dup
88
+ end
89
+ end
90
+ module_function :deep_dup
91
+
92
+ ##
93
+ # Transform a Psych::Nodes::Node to the JSON-LD Internal Representation
94
+ #
95
+ # @param [Psych::Nodes::Node] node
96
+ # @param [Hash{Symbol => Object}] options
97
+ # @option options [Boolean] :extendedYAML (false)
98
+ # Use the expanded internal representation.
99
+ # @return [Object]
100
+ def as_jsonld_ir(node, **options)
101
+ # Scans scalars for built-in classes
102
+ @ss ||= Psych::ScalarScanner.new(Psych::ClassLoader::Restricted.new([], %i()))
103
+ case node
104
+ when Psych::Nodes::Stream
105
+ node.children.map {|n| as_jsonld_ir(n, **options)}
106
+ when Psych::Nodes::Document then as_jsonld_ir(node.children.first, **options)
107
+ when Psych::Nodes::Sequence then node.children.map {|n| as_jsonld_ir(n, **options)}
108
+ when Psych::Nodes::Mapping
109
+ node.children.each_slice(2).inject({}) do |memo, (k,v)|
110
+ memo.merge(as_jsonld_ir(k) => as_jsonld_ir(v, **options))
111
+ end
112
+ when ::Psych::Nodes::Scalar then scan_scalar(node, **options)
113
+ when ::Psych::Nodes::Alias
114
+ # FIXME
115
+ end
116
+ end
117
+ module_function :as_jsonld_ir
118
+
119
+ ##
120
+ # Scans a scalar value to a JSON-LD IR scalar value.
121
+ # Quoted scalar values are not interpreted.
122
+ #
123
+ # @param [Psych::Nodes::Scalar] node
124
+ # @param [Hash{Symbol => Object}] options
125
+ # @option options [Boolean] :extendedYAML (false)
126
+ # Use the expanded internal representation.
127
+ # @return [Object]
128
+ def scan_scalar(node, **options)
129
+ return node.value if node.quoted # No interpretation
130
+ case node.tag
131
+ when "", NilClass
132
+ # Tokenize, but prohibit certain types
133
+ case node.value
134
+ # Don't scan some scalar values to types other than string
135
+ when Psych::ScalarScanner::TIME,
136
+ /^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/, # Date
137
+ /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}$/, # Time to seconds
138
+ /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}\.[0-9_]*$/ # Time to seconds
139
+ node.value
140
+ else
141
+ @ss.tokenize(node.value)
142
+ end
143
+ when '!str', 'tag:yaml.org,2002:str'
144
+ node.value
145
+ when '!int', 'tag:yaml.org,2002:int'
146
+ Integer(node.value)
147
+ when "!float", "tag:yaml.org,2002:float"
148
+ Float(@ss.tokenize(node.value))
149
+ when "!null", "tag:yaml.org,2002:null"
150
+ nil
151
+ when "!bool", "tag:yaml.org,2002:bool"
152
+ node.value.downcase == 'true'
153
+ when %r(^https://www.w3.org/ns/i18n)
154
+ l_d = node.tag[26..-1]
155
+ l_d = l_d[1..-1] if l_d.start_with?('#')
156
+ l, d = l_d.split('_')
157
+ if !options[:extendedYAML]
158
+ (node.style== Psych::Nodes::Scalar::PLAIN ? @ss.tokenize(node.value) : node.value)
159
+ elsif d.nil?
160
+ # Just use language component
161
+ RDF::Literal(node.value, language: l)
162
+ else
163
+ # Language and direction
164
+ RDF::Literal(node.value, datatype: RDF::URI("https://www.w3.org/ns/i18n##{l_d}"))
165
+ end
166
+ else
167
+ tag = node.tag
168
+ options[:extendedYAML] ?
169
+ RDF::Literal(node.value, datatype: RDF::URI(tag), validate: true) :
170
+ (node.style== Psych::Nodes::Scalar::PLAIN ? @ss.tokenize(node.value) : node.value)
171
+ end
172
+ end
173
+ module_function :scan_scalar
174
+
175
+ ##
176
+ # Build a YAML AST with an RDF::Literal visitor
177
+ #
178
+ #
179
+ # builder = Psych::Visitors::YAMLTree.new
180
+ # builder << { :foo => 'bar' }
181
+ # builder.tree # => #<Psych::Nodes::Stream .. }
182
+ # builder.tree.yaml # => "..."
183
+ #
184
+ # @note Rails 6.0 not compatible with Psych 4.0, which defines `RestrictedYamlTree`.
185
+ class IRTree < (Psych::Visitors.const_defined?(:RestrictedYamlTree) ? Psych::Visitors::RestrictedYAMLTree : Psych::Visitors::YAMLTree)
186
+ ##
187
+ # Retrive the literals from an object
188
+ def datatypes(object)
189
+ case object
190
+ when Array then object.inject([]) {|memo, o| memo += datatypes(o)}
191
+ when Hash then object.values.inject([]) {|memo, o| memo += datatypes(o)}
192
+ when RDF::Literal then object.datatype? ? [object.datatype] : []
193
+ else []
194
+ end
195
+ end
196
+
197
+ ##
198
+ # Add the object to be emitted. If the `:extended` option is set when the visitor is created, tags are added to the document.
199
+ def push object
200
+ start unless started?
201
+ version = []
202
+ version = [1,1] if @options[:header]
203
+
204
+ case @options[:version]
205
+ when Array
206
+ version = @options[:version]
207
+ when String
208
+ version = @options[:version].split('.').map { |x| x.to_i }
209
+ else
210
+ version = [1,1]
211
+ end if @options.key? :version
212
+
213
+ dts = datatypes(object).uniq
214
+ tags = dts.inject({}) do |memo, dt|
215
+ # Find the most suitable voabulary, if any
216
+ if memo.keys.any? {|u| dt.to_s.start_with?(u)}
217
+ memo
218
+ # Already have a prefix for this
219
+ elsif vocab = RDF::Vocabulary.each.to_a.detect {|v| dt.to_s.index(v.to_uri.to_s) == 0}
220
+ # Index by vocabulary URI
221
+ memo.merge(vocab.to_uri.to_s => "!#{vocab.__prefix__}!")
222
+ else
223
+ memo
224
+ end
225
+ end
226
+
227
+ @emitter.start_document version, tags.invert.to_a, false
228
+ accept object
229
+ @emitter.end_document !@emitter.streaming?
230
+ end
231
+ alias :<< :push
232
+
233
+ ##
234
+ # Emit an RDF Literal. If `extended` is set, use the datatype as an tag,
235
+ # otherwise, emit in expanded form.
236
+ def visit_RDF_Literal o
237
+ tag = case o.datatype
238
+ when nil then nil
239
+ when RDF::XSD.string then nil
240
+ when RDF.langString
241
+ "https://www.w3.org/ns/i18n##{o.language}" if @options[:extendedYAML]
242
+ else
243
+ if o.datatype.to_s.start_with?('https://www.w3.org/ns/i18n#')
244
+ o.datatype.to_s if @options[:extendedYAML]
245
+ elsif @options[:extendedYAML]
246
+ o.datatype.to_s
247
+ else
248
+ nil
249
+ end
250
+ end
251
+ formatted = o.value
252
+ register o, @emitter.scalar(formatted, nil, tag, false, false, Psych::Nodes::Scalar::PLAIN)
253
+ end
254
+ end
255
+ end
256
+ end
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  # frozen_string_literal: true
3
3
  module YAML_LD::VERSION
4
- VERSION_FILE = File.join(File.expand_path(File.dirname(__FILE__)), "..", "..", "..", "VERSION")
4
+ VERSION_FILE = File.expand_path("../../../VERSION", __FILE__)
5
5
  MAJOR, MINOR, TINY, EXTRA = File.read(VERSION_FILE).chomp.split(".")
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
data/lib/yaml_ld.rb CHANGED
@@ -28,6 +28,7 @@ module YAML_LD
28
28
 
29
29
  autoload :API, 'yaml_ld/api'
30
30
  autoload :Reader, 'yaml_ld/reader'
31
+ autoload :Representation, 'yaml_ld/representation'
31
32
  autoload :VERSION, 'yaml_ld/version'
32
33
  autoload :Writer, 'yaml_ld/writer'
33
34