yaml-ld 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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