redlander 0.3.6 → 0.4.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.
@@ -1,86 +1,69 @@
1
1
  module Redlander
2
+ # RDF node (usually, a part of an RDF statement)
2
3
  class Node
4
+ # @api private
3
5
  attr_reader :rdf_node
4
6
 
7
+ # Datatype URI for the literal node, or nil
8
+ attr_reader :datatype
9
+
5
10
  # Create a RDF node.
6
- # Argument can be:
11
+ #
12
+ # @param [Any] arg
7
13
  # - an instance of URI - to create a RDF "resource",
8
- # - an instance of Node - to create a copy of the node,
9
- # - nil (or absent) - to create a "blank" node,
10
- # - an instance of Statement ("role" must be supplied then) -
11
- # to create a node from subject, predicate or object
12
- # (determined by "role" parameter) of the statement.
14
+ # Note that you cannot create a resource node from an URI string,
15
+ # it must be an instance of URI. Otherwise it is treated as a string literal.
16
+ # - nil (or absent) - to create a blank node,
13
17
  # - any other Ruby object, which can be coerced into a literal.
14
- # If nothing else, a RedlandError is thrown.
15
- #
16
- # Note that you cannot create a resource node from an URI string,
17
- # it must be an instance of URI. Otherwise it is treated as a string literal.
18
- def initialize(arg = nil, role = :subject)
19
- @bound = false
18
+ # @param [Hash] options
19
+ # @option options [String] :blank_id optional ID to use for a blank node.
20
+ # @raise [RedlandError] if it fails to create a node from the given args.
21
+ def initialize(arg = nil, options = {})
20
22
  @rdf_node = case arg
23
+ when FFI::Pointer
24
+ unless Redland.librdf_node_is_literal(arg).zero?
25
+ rdf_uri = Redland.librdf_node_get_literal_value_datatype_uri(arg)
26
+ @datatype = rdf_uri.null? ? XmlSchema.datatype_of("") : URI(Redland.librdf_uri_to_string(rdf_uri))
27
+ end
28
+ wrap(arg)
21
29
  when NilClass
22
- Redland.librdf_new_node_from_blank_identifier(Redlander.rdf_world, nil)
30
+ Redland.librdf_new_node_from_blank_identifier(Redlander.rdf_world, options[:blank_id])
23
31
  when URI
24
32
  Redland.librdf_new_node_from_uri_string(Redlander.rdf_world, arg.to_s)
25
- when Node
26
- Redland.librdf_new_node_from_node(arg.rdf_node)
27
- when Statement
28
- # TODO: Bound nodes should better be produced
29
- # using an explicit "factory" like Node.from_statement
30
- # to keep the clutter in the constructor at minimum.
31
- # (Esp. handling the "bound" stuff)
32
- @bound = true
33
- case role
34
- when :subject
35
- copy_rdf_node_on_initialize(Redland.librdf_statement_get_subject(arg.rdf_statement))
36
- when :object
37
- copy_rdf_node_on_initialize(Redland.librdf_statement_get_object(arg.rdf_statement))
38
- when :predicate
39
- copy_rdf_node_on_initialize(Redland.librdf_statement_get_predicate(arg.rdf_statement))
40
- else
41
- raise RedlandError.new("Invalid role specified")
42
- end
43
33
  else
44
34
  value = arg.respond_to?(:xmlschema) ? arg.xmlschema : arg.to_s
45
- datatype = Uri.new(XmlSchema.datatype_of(arg))
46
- Redland.librdf_new_node_from_typed_literal(Redlander.rdf_world, value, nil, datatype.rdf_uri)
35
+ @datatype = XmlSchema.datatype_of(arg)
36
+ Redland.librdf_new_node_from_typed_literal(Redlander.rdf_world, value, nil, Uri.new(@datatype).rdf_uri)
47
37
  end
48
- if @rdf_node.null?
49
- raise RedlandError.new("Failed to create a new node") unless @bound
50
- else
51
- ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_node(@rdf_node) })
52
- # bound nodes cannot be added to (other) statements
53
- freeze if @bound
54
- end
55
- end
56
-
57
- # Bound nodes are those belonging to a statement
58
- # (bound nodes cannot be modified or added to other statements).
59
- def bound?
60
- @bound
38
+ raise RedlandError, "Failed to create a new node" if @rdf_node.null?
39
+ ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_node(@rdf_node) })
61
40
  end
62
41
 
42
+ # Check whether the node is a resource (identified by a URI)
43
+ #
44
+ # @return [Boolean]
63
45
  def resource?
64
46
  Redland.librdf_node_is_resource(@rdf_node) != 0
65
47
  end
66
48
 
67
49
  # Return true if node is a literal.
50
+ #
51
+ # @return [Boolean]
68
52
  def literal?
69
53
  Redland.librdf_node_is_literal(@rdf_node) != 0
70
54
  end
71
55
 
72
56
  # Return true if node is a blank node.
57
+ #
58
+ # @return [Boolean]
73
59
  def blank?
74
60
  Redland.librdf_node_is_blank(@rdf_node) != 0
75
61
  end
76
62
 
77
- # Return the datatype URI of the node.
78
- # Returns nil if the node is not a literal, or has no datatype URI.
79
- def datatype
80
- Uri.new(self).to_s if literal?
81
- end
82
-
83
63
  # Equivalency. Only works for comparing two Nodes.
64
+ #
65
+ # @param [Node] other_node Node to be compared with.
66
+ # @return [Boolean]
84
67
  def eql?(other_node)
85
68
  Redland.librdf_node_equals(@rdf_node, other_node.rdf_node) != 0
86
69
  end
@@ -91,14 +74,37 @@ module Redlander
91
74
  end
92
75
 
93
76
  # Convert this node to a string (with a datatype suffix).
77
+ #
78
+ # @return [String]
94
79
  def to_s
95
80
  Redland.librdf_node_to_string(@rdf_node)
96
81
  end
97
82
 
83
+ # Internal URI of the Node.
84
+ #
85
+ # Returns the datatype URI for literal nodes,
86
+ # nil for blank nodes.
87
+ #
88
+ # @return [URI, nil]
89
+ def uri
90
+ if resource?
91
+ URI(to_s[1..-2])
92
+ elsif literal?
93
+ datatype
94
+ else
95
+ nil
96
+ end
97
+ end
98
+
98
99
  # Value of the literal node as a Ruby object instance.
100
+ #
101
+ # Returns an instance of URI for resource nodes,
102
+ # "blank identifier" for blank nodes.
103
+ #
104
+ # @return [URI, Any]
99
105
  def value
100
106
  if resource?
101
- URI.parse(Uri.new(self).to_s)
107
+ uri
102
108
  else
103
109
  XmlSchema.instantiate(to_s)
104
110
  end
@@ -107,13 +113,10 @@ module Redlander
107
113
 
108
114
  private
109
115
 
110
- def copy_rdf_node_on_initialize(n)
116
+ # @api private
117
+ def wrap(n)
111
118
  if n.null?
112
- if bound?
113
- n
114
- else
115
- raise RedlandError.new("Failed to create a new node")
116
- end
119
+ raise RedlandError, "Failed to create a new node"
117
120
  else
118
121
  Redland.librdf_new_node_from_node(n)
119
122
  end
@@ -0,0 +1,123 @@
1
+ module Redlander
2
+ # Syntax parsing methods.
3
+ # "self" is assumed to be an instance of Redlander::Model
4
+ module Parsing
5
+ # Core parsing method for non-streams
6
+ #
7
+ # @note
8
+ # If a block is given, the extracted statements will be yielded into
9
+ # the block and inserted into the model depending on the output
10
+ # of the block (if true, the statement will be added,
11
+ # if false, the statement will not be added).
12
+ #
13
+ # @param [String, URI] content
14
+ # - Can be a String,
15
+ # causing the statements to be extracted
16
+ # directly from it, or
17
+ # - URI
18
+ # causing the content to be first pulled
19
+ # from the specified URI (or a local file,
20
+ # if URI schema == "file:")
21
+ # @param [Hash] options
22
+ # @option options [String] :format name of the parser to use,
23
+ # @option options [String] :mime_type MIME type of the syntax, if applicable,
24
+ # @option options [String, URI] :type_uri URI of syntax, if applicable,
25
+ # @option options [String, URI] :base_uri base URI,
26
+ # to be applied to the nodes with relative URIs.
27
+ # @yieldparam [Statement]
28
+ # @raise [RedlandError] if it fails to create a parser or stream
29
+ # @return [Model]
30
+ def from(content, options = {})
31
+ format = options[:format].to_s
32
+ mime_type = options[:mime_type] && options[:mime_type].to_s
33
+ type_uri = options[:type_uri] && options[:type_uri].to_s
34
+ base_uri = options[:base_uri] && options[:base_uri].to_s
35
+ content = Uri.new(content) if content.is_a?(URI)
36
+
37
+ # FIXME: to be fixed in librdf:
38
+ # ntriples parser absolutely needs "\n" at the end of the input
39
+ if format == "ntriples" && !content.is_a?(Uri) && !content.end_with?("\n")
40
+ content << "\n"
41
+ end
42
+
43
+ rdf_parser = Redland.librdf_new_parser(Redlander.rdf_world, format, mime_type, type_uri)
44
+ raise RedlandError, "Failed to create a new '#{format}' parser" if rdf_parser.null?
45
+
46
+ begin
47
+ if block_given?
48
+ rdf_stream =
49
+ if content.is_a?(Uri)
50
+ Redland.librdf_parser_parse_as_stream(rdf_parser, content.rdf_uri, base_uri)
51
+ else
52
+ Redland.librdf_parser_parse_string_as_stream(rdf_parser, content, base_uri)
53
+ end
54
+ raise RedlandError, "Failed to create a new stream" if rdf_stream.null?
55
+
56
+ begin
57
+ while Redland.librdf_stream_end(rdf_stream).zero?
58
+ statement = Statement.new(Redland.librdf_stream_get_object(rdf_stream))
59
+ statements.add(statement) if yield statement
60
+ Redland.librdf_stream_next(rdf_stream)
61
+ end
62
+ ensure
63
+ Redland.librdf_free_stream(rdf_stream)
64
+ end
65
+ else
66
+ if content.is_a?(Uri)
67
+ Redland.librdf_parser_parse_into_model(rdf_parser, content.rdf_uri, base_uri, @rdf_model).zero?
68
+ else
69
+ Redland.librdf_parser_parse_string_into_model(rdf_parser, content, base_uri, @rdf_model).zero?
70
+ end
71
+ end
72
+ ensure
73
+ Redland.librdf_free_parser(rdf_parser)
74
+ end
75
+ self
76
+ end
77
+
78
+ # Parse input in RDF/XML format.
79
+ # Shortcut for {#from}(content, :format => "rdfxml").
80
+ #
81
+ # @param (see #from)
82
+ # @yieldparam [Statement]
83
+ # @return [void]
84
+ def from_rdfxml(content, options = {}, &block)
85
+ from(content, options.merge(:format => "rdfxml"), &block)
86
+ end
87
+
88
+ # Parse input in NTriples format.
89
+ # Shortcut for {#from}(content, :format => "ntriples").
90
+ #
91
+ # @param (see #from)
92
+ # @yieldparam [Statement]
93
+ # @return [void]
94
+ def from_ntriples(content, options = {}, &block)
95
+ from(content, options.merge(:format => "ntriples"), &block)
96
+ end
97
+
98
+ # Parse input in Turtls format.
99
+ # Shortcut for {#from}(content, :format => "turtle").
100
+ #
101
+ # @param (see #from)
102
+ # @yieldparam [Statement]
103
+ # @return [void]
104
+ def from_turtle(content, options = {}, &block)
105
+ from(content, options.merge(:format => "turtle"), &block)
106
+ end
107
+
108
+ # Parse input as stream from URI (or File)
109
+ #
110
+ # @param [URI, String] uri URI of the endpoint or file path
111
+ # @param [Hash] options (see {#from})
112
+ # @yieldparam [Statement]
113
+ # @return [void]
114
+ def from_uri(uri, options = {}, &block)
115
+ if uri.is_a?(String)
116
+ uri = URI.parse(uri)
117
+ uri = URI.parse("file://#{File.expand_path(uri.to_s)}") if uri.scheme.nil?
118
+ end
119
+ from(uri, options, &block)
120
+ end
121
+ alias_method :from_file, :from_uri
122
+ end
123
+ end
@@ -0,0 +1,89 @@
1
+ module Redlander
2
+ # Syntax parsing methods.
3
+ # "self" is assumed to be an instance of Redlander::Model
4
+ module Serializing
5
+ # Serialize model into a string
6
+ #
7
+ # @param [Hash] options
8
+ # @option options [String] :format name of the serializer to use,
9
+ # @option options [String] :mime_type MIME type of the syntax, if applicable,
10
+ # @option options [String, URI] :type_uri URI of syntax, if applicable,
11
+ # @option options [String, URI] :base_uri base URI,
12
+ # to be applied to the nodes with relative URIs.
13
+ # @raise [RedlandError] if it fails to create a serializer
14
+ def to(options = {})
15
+ format = options[:format].to_s
16
+ mime_type = options[:mime_type] && options[:mime_type].to_s
17
+ type_uri = options[:type_uri] && options[:type_uri].to_s
18
+ base_uri = options[:base_uri] && options[:base_uri].to_s
19
+
20
+ rdf_serializer = Redland.librdf_new_serializer(Redlander.rdf_world, format, mime_type, type_uri)
21
+ raise RedlandError, "Failed to create a new serializer" if rdf_serializer.null?
22
+
23
+ begin
24
+ if options[:file]
25
+ Redland.librdf_serializer_serialize_model_to_file(rdf_serializer, options[:file], base_uri, @rdf_model).zero?
26
+ else
27
+ Redland.librdf_serializer_serialize_model_to_string(rdf_serializer, base_uri, @rdf_model)
28
+ end
29
+ ensure
30
+ Redland.librdf_free_serializer(rdf_serializer)
31
+ end
32
+ end
33
+
34
+ # Serialize the model in RDF/XML format.
35
+ # Shortcut for {#to}(:format => "rdfxml").
36
+ #
37
+ # @param (see #to)
38
+ # @return [String]
39
+ def to_rdfxml(options = {})
40
+ to(options.merge(:format => "rdfxml"))
41
+ end
42
+
43
+ # Serialize the model in NTriples format.
44
+ # Shortcut for {#to}(:format => "ntriples").
45
+ #
46
+ # @param (see #to)
47
+ # @return [String]
48
+ def to_ntriples(options = {})
49
+ to(options.merge(:format => "ntriples"))
50
+ end
51
+
52
+ # Serialize the model in Turtle format.
53
+ # Shortcut for {#to}(:format => "turtle").
54
+ #
55
+ # @param (see #to)
56
+ # @return [String]
57
+ def to_turtle(options = {})
58
+ to(options.merge(:format => "turtle"))
59
+ end
60
+
61
+ # Serialize the model in JSON format.
62
+ # Shortcut for {#to}(:format => "json").
63
+ #
64
+ # @param (see #to)
65
+ # @return [String]
66
+ def to_json(options = {})
67
+ to(options.merge(:format => "json"))
68
+ end
69
+
70
+ # Serialize the model in Dot format.
71
+ # Shortcut for {#to}(:format => "dot").
72
+ #
73
+ # @param (see #to)
74
+ # @return [String]
75
+ def to_dot(options = {})
76
+ to(options.merge(:format => "dot"))
77
+ end
78
+
79
+ # Serialize the model to a file.
80
+ # Shortcut for {#to}(:format => "rdfxml").
81
+ #
82
+ # @param [String] filename path to the output file
83
+ # @param [Hash] options (see {#to} options)
84
+ # @return [void]
85
+ def to_file(filename, options = {})
86
+ to(options.merge(:file => filename))
87
+ end
88
+ end
89
+ end
@@ -1,72 +1,80 @@
1
- require 'redlander/stream'
2
- require "redlander/error_container"
3
-
4
1
  module Redlander
2
+ # RDF statement
5
3
  class Statement
6
- include ErrorContainer
7
-
4
+ # @api private
8
5
  attr_reader :rdf_statement
9
6
 
10
7
  # Create an RDF statement.
11
- # Source can be:
12
- # Hash, where
13
- # :subject
14
- # :predicate
15
- # :object
16
- # Stream, so that a statement is extracted from its current position
8
+ #
9
+ # @param [Hash] source
10
+ # @option source [Node, String, URI, nil] :subject
11
+ # @option source [Node, String, URI, nil] :predicate
12
+ # @option source [Node, String, URI, nil] :object
13
+ # @raise [NotImplementedError] if cannot create a Statement from the given source.
14
+ # @raise [RedlandError] if it fails to create a Statement.
17
15
  def initialize(source = {})
18
16
  @rdf_statement = case source
19
- when Stream
20
- # Pull a (current) statement from the stream
21
- copy_rdf_statement_on_initialize(Redland.librdf_stream_get_object(source.rdf_stream))
17
+ when FFI::Pointer
18
+ wrap(source)
22
19
  when Hash
23
20
  # Create a new statement from nodes
24
- s = source[:subject] && Node.new(source[:subject]).rdf_node
25
- p = source[:predicate] && Node.new(source[:predicate]).rdf_node
26
- o = source[:object] && Node.new(source[:object]).rdf_node
21
+ s = rdf_node_from(source[:subject])
22
+ p = rdf_node_from(source[:predicate])
23
+ o = rdf_node_from(source[:object])
27
24
  Redland.librdf_new_statement_from_nodes(Redlander.rdf_world, s, p, o)
28
25
  else
29
- # TODO
30
- raise NotImplementedError.new
26
+ raise NotImplementedError, "Cannot create Statement from '#{source.inspect}'"
31
27
  end
32
- raise RedlandError.new("Failed to create a new statement") if @rdf_statement.null?
28
+ raise RedlandError, "Failed to create a new statement" if @rdf_statement.null?
33
29
  ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_statement(@rdf_statement) })
34
30
  end
35
31
 
32
+ # Subject of the statment.
33
+ #
34
+ # @return [Node, nil]
36
35
  def subject
37
- node = Node.new(self, :subject)
38
- node.rdf_node.null? ? nil : node
36
+ rdf_node = Redland.librdf_statement_get_subject(@rdf_statement)
37
+ rdf_node.null? ? nil : Node.new(rdf_node)
39
38
  end
40
39
 
40
+ # Predicate of the statement.
41
+ #
42
+ # @return [Node, nil]
41
43
  def predicate
42
- node = Node.new(self, :predicate)
43
- node.rdf_node.null? ? nil : node
44
+ rdf_node = Redland.librdf_statement_get_predicate(@rdf_statement)
45
+ rdf_node.null? ? nil : Node.new(rdf_node)
44
46
  end
45
47
 
48
+ # Object of the statement.
49
+ #
50
+ # @return [Node, nil]
46
51
  def object
47
- node = Node.new(self, :object)
48
- node.rdf_node.null? ? nil : node
52
+ rdf_node = Redland.librdf_statement_get_object(@rdf_statement)
53
+ rdf_node.null? ? nil : Node.new(rdf_node)
49
54
  end
50
55
 
51
- # set the subject of the statement
56
+ # Set the subject of the statement
57
+ #
58
+ # @param [Node, nil] node
59
+ # @return [void]
52
60
  def subject=(node)
53
- binding_to_statement(node) {
54
- Redland.librdf_statement_set_subject(@rdf_statement, node.rdf_node)
55
- }
61
+ Redland.librdf_statement_set_subject(@rdf_statement, rdf_node_from(node))
56
62
  end
57
63
 
58
- # set the predicate of the statement
64
+ # Set the predicate of the statement
65
+ #
66
+ # @param [Node, nil] node
67
+ # @return [void]
59
68
  def predicate=(node)
60
- binding_to_statement(node) {
61
- Redland.librdf_statement_set_predicate(@rdf_statement, node.rdf_node)
62
- }
69
+ Redland.librdf_statement_set_predicate(@rdf_statement, rdf_node_from(node))
63
70
  end
64
71
 
65
- # set the object of the statement
72
+ # Set the object of the statement
73
+ #
74
+ # @param [Node, nil] node
75
+ # @return [void]
66
76
  def object=(node)
67
- binding_to_statement(node) {
68
- Redland.librdf_statement_set_object(@rdf_statement, node.rdf_node)
69
- }
77
+ Redland.librdf_statement_set_object(@rdf_statement, rdf_node_from(node))
70
78
  end
71
79
 
72
80
  def eql?(other_statement)
@@ -84,36 +92,29 @@ module Redlander
84
92
  Redland.librdf_statement_to_string(@rdf_statement)
85
93
  end
86
94
 
87
- # A valid statement satisfies the following:
88
- # URI or blank subject, URI predicate and URI or blank or literal object (i.e. anything).
89
- def valid?
90
- attributes_satisfy? ? errors.clear : errors.add("is invalid")
91
- errors.empty?
92
- end
93
-
94
95
 
95
96
  private
96
97
 
97
- def attributes_satisfy?
98
- !subject.nil? && (subject.resource? || subject.blank?) &&
99
- !predicate.nil? && predicate.resource? &&
100
- !object.nil?
101
- end
102
-
103
- def binding_to_statement(node)
104
- if node.frozen?
105
- raise RedlandError.new("Cannot assign a bound node")
98
+ # @api private
99
+ def wrap(s)
100
+ if s.null?
101
+ raise RedlandError, "Failed to create a new statement"
106
102
  else
107
- node.freeze
108
- yield
103
+ Redland.librdf_new_statement_from_statement(s)
109
104
  end
110
105
  end
111
106
 
112
- def copy_rdf_statement_on_initialize(s)
113
- if s.null?
114
- raise RedlandError.new("Failed to create a new statement")
107
+ # Create a Node from the source
108
+ # and get its rdf_node, or return nil
109
+ # @api private
110
+ def rdf_node_from(source)
111
+ case source
112
+ when NilClass
113
+ nil
114
+ when Node
115
+ source.rdf_node
115
116
  else
116
- Redland.librdf_new_statement_from_statement(s)
117
+ Node.new(source).rdf_node
117
118
  end
118
119
  end
119
120
  end