redlander 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc CHANGED
@@ -8,7 +8,7 @@ Installing Redlander is simple:
8
8
 
9
9
  $ gem install redlander
10
10
 
11
- Note, that in order to install Redlander you need the development libraries of Redland project (tested with redland-1.0.12 in Ubuntu 11.04).
11
+ Note, that you will have to install Redlander (librdf) for Redlander to work.
12
12
 
13
13
  = Usage
14
14
 
@@ -34,17 +34,21 @@ Most of Redlander functionality is accessable via these statements. The API is a
34
34
  $ st = Redlander::Statement.new(:subject => s, :predicate => p, :object => "another label")
35
35
  $ m.statements.add(st)
36
36
 
37
- m.statements.size # => 2
37
+ $ m.statements.size # => 2
38
+
39
+ $ m.statements.each { |st| puts st }
38
40
 
39
41
  Finding statements:
40
42
 
41
43
  m.statements.find(:first, :object => "subject!")
42
44
  m.statements.all(:object => "another label")
43
- m.statements.find(:all, :object => "subject!") { |statement|
45
+ m.statements.find(:all, :object => "subject!").each { |statement|
44
46
  puts statement.subject
45
47
  }
46
48
 
47
- For more detail refer to the documentation of Model, Statement, Storage and other classes of Redlander.
49
+ Note that "m.statements.each" is "lazy", while "m.statements.all" (and other finders) are not.
50
+
51
+ For more details refer to the documentation of Model, Statement, Storage and other classes of Redlander.
48
52
 
49
53
  = Exceptions
50
54
 
data/lib/redlander.rb CHANGED
@@ -10,13 +10,27 @@ module Redlander
10
10
 
11
11
  class RedlandError < RuntimeError; end
12
12
 
13
- class << self
13
+ autoload :ErrorContainer, 'redlander/error_container'
14
+ autoload :Uri, 'redlander/uri'
15
+ autoload :Parser, 'redlander/parser'
16
+ autoload :ParserProxy, 'redlander/parser_proxy'
17
+ autoload :Serializer, 'redlander/serializer'
18
+ autoload :Model, 'redlander/model'
19
+ autoload :ModelProxy, 'redlander/model_proxy'
20
+ autoload :Node, 'redlander/node'
21
+ autoload :Stream, 'redlander/stream'
22
+ autoload :Storage, 'redlander/storage'
23
+ autoload :ParsingInstanceMethods, 'redlander/parser'
24
+ autoload :SerializingInstanceMethods, 'redlander/serializer'
25
+ autoload :StreamEnumerator, 'redlander/stream_enumerator'
26
+ autoload :Statement, 'redlander/statement'
14
27
 
28
+ class << self
15
29
  def rdf_world
16
30
  unless @rdf_world
17
31
  @rdf_world = Redland.librdf_new_world
18
- raise RedlandError.new("Could not create a new RDF world") unless @rdf_world
19
- ObjectSpace.define_finalizer(@rdf_world, proc { Redland.librdf_free_world(@rdf_world) })
32
+ raise RedlandError.new("Could not create a new RDF world") if @rdf_world.null?
33
+ ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_world(@rdf_world) })
20
34
  Redland.librdf_world_open(@rdf_world)
21
35
  end
22
36
  @rdf_world
@@ -38,24 +52,5 @@ module Redlander
38
52
  opts << "#{key}='#{value}'"
39
53
  }.join(',')
40
54
  end
41
-
42
- # Helper method to create an instance of rdfuri.
43
- # For internal use only!
44
- def to_rdf_uri(uri)
45
- return nil if uri.nil?
46
- uri = uri.is_a?(URI) ? uri.to_s : uri
47
- rdf_uri = Redland.librdf_new_uri(rdf_world, uri)
48
- raise RedlandError.new("Failed to create URI from '#{uri}'") unless rdf_uri
49
- ObjectSpace.define_finalizer(rdf_uri, proc { Redland.librdf_free_uri(rdf_uri) })
50
- rdf_uri
51
- end
52
-
53
55
  end
54
-
55
- require 'redlander/error_container'
56
- require 'redlander/statement_iterator'
57
- require 'redlander/parser'
58
- require 'redlander/serializer'
59
- require 'redlander/model'
60
- require 'redlander/node'
61
56
  end
@@ -1,41 +1,42 @@
1
- module ErrorContainer
1
+ module Redlander
2
+ module ErrorContainer
3
+ class Errors
4
+ include Enumerable
2
5
 
3
- class Errors
4
- include Enumerable
6
+ def initialize
7
+ @container = []
8
+ end
5
9
 
6
- def initialize
7
- @container = []
8
- end
10
+ def add(error_message)
11
+ if @container.include?(error_message)
12
+ @container
13
+ else
14
+ @container << error_message
15
+ end
16
+ end
9
17
 
10
- def add(error_message)
11
- if @container.include?(error_message)
12
- @container
13
- else
14
- @container << error_message
18
+ def each
19
+ @container.each do |err|
20
+ yield err
21
+ end
15
22
  end
16
- end
17
23
 
18
- def each
19
- @container.each do |err|
20
- yield err
24
+ def empty?
25
+ @container.empty?
21
26
  end
22
- end
23
27
 
24
- def empty?
25
- @container.empty?
26
- end
28
+ def clear
29
+ @container.clear
30
+ end
27
31
 
28
- def clear
29
- @container.clear
32
+ def size
33
+ @container.size
34
+ end
30
35
  end
31
36
 
32
- def size
33
- @container.size
37
+ def errors
38
+ @errors ||= Errors.new
34
39
  end
35
- end
36
40
 
37
- def errors
38
- @errors ||= Errors.new
39
41
  end
40
-
41
42
  end
@@ -1,23 +1,18 @@
1
- require 'redlander/storage'
2
- require 'redlander/model_proxy'
3
-
4
1
  module Redlander
5
-
6
2
  class Model
7
-
8
3
  include Redlander::ParsingInstanceMethods
9
4
  include Redlander::SerializingInstanceMethods
10
5
 
11
6
  attr_reader :rdf_model
12
7
 
13
8
  # Create a new RDF model.
14
- # For explanation of options, read Storage.initialize_storage
9
+ # For explanation of options, read Storage.initialize
15
10
  def initialize(options = {})
16
- @rdf_storage = Storage.initialize_storage(options)
11
+ @storage = Storage.new(options)
17
12
 
18
- @rdf_model = Redland.librdf_new_model(Redlander.rdf_world, @rdf_storage, "")
19
- raise RedlandError.new("Failed to create a new model") unless @rdf_model
20
- ObjectSpace.define_finalizer(@rdf_model, proc { Redland.librdf_free_model(@rdf_model) })
13
+ @rdf_model = Redland.librdf_new_model(Redlander.rdf_world, @storage.rdf_storage, "")
14
+ raise RedlandError.new("Failed to create a new model") if @rdf_model.null?
15
+ ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_model(@rdf_model) })
21
16
  end
22
17
 
23
18
  # Statements contained in the model.
@@ -28,6 +23,23 @@ module Redlander
28
23
  ModelProxy.new(self)
29
24
  end
30
25
 
31
- end
26
+ # Wrap changes to the given model in a transaction.
27
+ # If an exception is raised in the block, the transaction is rolled back.
28
+ # (Does not work for all storages, in which case the changes are instanteous).
29
+ def transaction
30
+ if block_given?
31
+ Redland.librdf_model_transaction_start(@rdf_model).zero? || RedlandError.new("Failed to initialize a transaction")
32
+ yield
33
+ Redland.librdf_model_transaction_commit(@rdf_model).zero? || RedlandError.new("Failed to commit the transaction")
34
+ end
35
+ rescue
36
+ rollback
37
+ raise
38
+ end
32
39
 
40
+ # Rollback the transaction
41
+ def rollback
42
+ Redland.librdf_model_transaction_rollback(@rdf_model).zero? || RedlandError.new("Failed to rollback the latest transaction")
43
+ end
44
+ end
33
45
  end
@@ -1,20 +1,9 @@
1
- require 'redlander/statement'
2
-
3
1
  module Redlander
4
-
5
2
  class ModelProxy
3
+ include StreamEnumerator
6
4
 
7
- include StatementIterator
8
-
9
- def initialize(model, rdf_stream = nil)
5
+ def initialize(model)
10
6
  @model = model
11
- @rdf_stream = if rdf_stream
12
- rdf_stream
13
- else
14
- Redland.librdf_model_as_stream(@model.rdf_model)
15
- end
16
- raise RedlandError.new("Failed to create a new stream") unless @rdf_stream
17
- ObjectSpace.define_finalizer(@rdf_stream, proc { Redland.librdf_free_stream(@rdf_stream) })
18
7
  end
19
8
 
20
9
  # Add a statement to the model.
@@ -24,7 +13,31 @@ module Redlander
24
13
  #
25
14
  # Returns true on success or false on failure.
26
15
  def add(statement)
27
- statement.model = @model
16
+ if statement.valid?
17
+ Redland.librdf_model_add_statement(@model.rdf_model, statement.rdf_statement).zero?
18
+ end
19
+ end
20
+
21
+ # Delete a statement from the model,
22
+ # or delete all statements matching the given criteria.
23
+ # Source can be either
24
+ # Statement
25
+ # or
26
+ # Hash (all keys are optional)
27
+ # :subject
28
+ # :predicate
29
+ # :object
30
+ def delete(source)
31
+ statement = case source
32
+ when Statement
33
+ source
34
+ when Hash
35
+ Statement.new(source)
36
+ else
37
+ # TODO
38
+ raise NotImplementedError.new
39
+ end
40
+ Redland.librdf_model_remove_statement(@model.rdf_model, statement.rdf_statement).zero?
28
41
  end
29
42
 
30
43
  # Create a statement and add it to the model.
@@ -58,32 +71,31 @@ module Redlander
58
71
  # :all
59
72
  # :first
60
73
  def find(scope, options = {}, &block)
61
- statement = Statement.new(options)
62
- rdf_stream = Redland.librdf_model_find_statements(@model.rdf_model, statement.rdf_statement)
63
- proxy = self.class.new(@model, rdf_stream)
74
+ stream = Stream.new(@model, Statement.new(options))
64
75
 
65
76
  case scope
66
77
  when :first
67
- proxy.first
78
+ stream.current
68
79
  when :all
69
- if block_given?
70
- proxy.each(&block)
71
- else
72
- proxy
73
- end
80
+ stream.tail
74
81
  else
75
82
  raise RedlandError.new("Invalid search scope '#{scope}' specified.")
76
83
  end
77
84
  end
78
85
 
86
+ def first(options = {})
87
+ find(:first, options)
88
+ end
89
+
79
90
  def all(options = {})
80
- [].tap do |st|
81
- find(:all, options) do |fs|
82
- st << fs
83
- end
84
- end
91
+ find(:all, options)
85
92
  end
86
93
 
87
- end
88
94
 
95
+ private
96
+
97
+ def reset_stream
98
+ @stream = Stream.new(@model)
99
+ end
100
+ end
89
101
  end
@@ -1,7 +1,5 @@
1
1
  module Redlander
2
-
3
2
  class Node
4
-
5
3
  attr_reader :rdf_node
6
4
 
7
5
  # Create a RDF node.
@@ -9,58 +7,72 @@ module Redlander
9
7
  # - an instance of URI - to create a RDF "resource",
10
8
  # - an instance of Node - to create a copy of the node,
11
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.
12
13
  # - any other Ruby object, which can be coerced into a literal.
13
14
  # If nothing else, a RedlandError is thrown.
14
15
  #
15
16
  # Note, that you cannot create a resource node from an URI string,
16
17
  # it must be an instance of URI. Otherwise it is treated as a string literal.
17
- def initialize(arg = nil)
18
- @rdf_node = if arg.nil?
18
+ def initialize(arg = nil, role = :subject)
19
+ bound = false
20
+ @rdf_node = case arg
21
+ when NilClass
19
22
  Redland.librdf_new_node_from_blank_identifier(Redlander.rdf_world, nil)
20
- elsif arg.is_a?(URI)
23
+ when URI
21
24
  Redland.librdf_new_node_from_uri_string(Redlander.rdf_world, arg.to_s)
22
- elsif arg.is_a?(SWIG::TYPE_p_librdf_node_s)
23
- # A special case, where you can pass an instance of SWIG::TYPE_p_librdf_node_s
24
- # in order to create a Node from an internal RDF node representation.
25
- arg
26
- elsif arg.is_a?(Node)
25
+ when Node
27
26
  Redland.librdf_new_node_from_node(arg.rdf_node)
27
+ when Statement
28
+ bound = true
29
+ case role
30
+ when :subject
31
+ Redland.librdf_statement_get_subject(arg.rdf_statement)
32
+ when :object
33
+ Redland.librdf_statement_get_object(arg.rdf_statement)
34
+ when :predicate
35
+ Redland.librdf_statement_get_predicate(arg.rdf_statement)
36
+ else
37
+ raise RedlandError.new("Invalid role specified")
38
+ end
28
39
  else
29
40
  value = arg.respond_to?(:xmlschema) ? arg.xmlschema : arg.to_s
30
- datatype = Redlander.to_rdf_uri(XmlSchema.datatype_of(arg))
31
- Redland.librdf_new_node_from_typed_literal(Redlander.rdf_world, value, nil, datatype)
41
+ datatype = Uri.new(XmlSchema.datatype_of(arg))
42
+ Redland.librdf_new_node_from_typed_literal(Redlander.rdf_world, value, nil, datatype.rdf_uri)
32
43
  end
33
-
34
- raise RedlandError.new("Failed to create a new node") unless @rdf_node
35
- ObjectSpace.define_finalizer(@rdf_node, proc { Redland.librdf_free_node(@rdf_node) })
44
+ if @rdf_node.null?
45
+ raise RedlandError.new("Failed to create a new node") unless bound
46
+ else
47
+ ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_node(@rdf_node) })
48
+ # bound nodes cannot be added to (other) statements
49
+ freeze if bound
50
+ end
36
51
  end
37
52
 
38
53
  def resource?
39
- !Redland.librdf_node_is_resource(@rdf_node).zero?
54
+ Redland.librdf_node_is_resource(@rdf_node) != 0
40
55
  end
41
56
 
42
57
  # Return true if node is a literal.
43
58
  def literal?
44
- !Redland.librdf_node_is_literal(@rdf_node).zero?
59
+ Redland.librdf_node_is_literal(@rdf_node) != 0
45
60
  end
46
61
 
47
62
  # Return true if node is a blank node.
48
63
  def blank?
49
- !Redland.librdf_node_is_blank(@rdf_node).zero?
64
+ Redland.librdf_node_is_blank(@rdf_node) != 0
50
65
  end
51
66
 
52
67
  # Return the datatype URI of the node.
53
68
  # Returns nil if the node is not a literal, or has no datatype URI.
54
69
  def datatype
55
- if rdf_uri = Redland.librdf_node_get_literal_value_datatype_uri(@rdf_node)
56
- ObjectSpace.define_finalizer(rdf_uri, proc { Redland.librdf_free_uri(rdf_uri) })
57
- Redland.librdf_uri_to_string(rdf_uri)
58
- end
70
+ Uri.new(self).to_s if literal?
59
71
  end
60
72
 
61
73
  # Equivalency. Only works for comparing two Nodes.
62
74
  def eql?(other_node)
63
- !Redland.librdf_node_equals(@rdf_node, other_node.rdf_node).zero?
75
+ Redland.librdf_node_equals(@rdf_node, other_node.rdf_node) != 0
64
76
  end
65
77
  alias_method :==, :eql?
66
78
 
@@ -76,15 +88,10 @@ module Redlander
76
88
  # Value of the literal node as a Ruby object instance.
77
89
  def value
78
90
  if resource?
79
- if rdf_uri = Redland.librdf_node_get_uri(@rdf_node)
80
- ObjectSpace.define_finalizer(rdf_uri, proc { Redland.librdf_free_uri(rdf_uri) })
81
- URI.parse(Redland.librdf_uri_to_string(rdf_uri))
82
- end
91
+ URI.parse(Uri.new(self).to_s)
83
92
  else
84
93
  XmlSchema.instantiate(to_s)
85
94
  end
86
95
  end
87
-
88
96
  end
89
-
90
97
  end
@@ -1,9 +1,5 @@
1
- require 'redlander/parser_proxy'
2
-
3
1
  module Redlander
4
-
5
2
  class Parser
6
-
7
3
  attr_reader :rdf_parser
8
4
 
9
5
  # Create a new parser.
@@ -15,8 +11,8 @@ module Redlander
15
11
  def initialize(name = :rdfxml)
16
12
  # name, mime-type and syntax uri can all be nil, which defaults to :rdfxml parser
17
13
  @rdf_parser = Redland.librdf_new_parser(Redlander.rdf_world, name.to_s, nil, nil)
18
- raise RedlandError.new("Failed to create a new parser") unless @rdf_parser
19
- ObjectSpace.define_finalizer(@rdf_parser, proc { Redland.librdf_free_parser(@rdf_parser) })
14
+ raise RedlandError.new("Failed to create a new parser") if @rdf_parser.null?
15
+ ObjectSpace.define_finalizer(self, proc { Redland.librdf_free_parser(@rdf_parser) })
20
16
  end
21
17
 
22
18
  # Parse the content (String) into the model.
@@ -28,7 +24,12 @@ module Redlander
28
24
  def from_string(model, content, options = {})
29
25
  # FIXME: A bug (?) in Redland breaks NTriples parser if its input is not terminated with "\n"
30
26
  content.concat("\n") unless content.end_with?("\n")
31
- Redland.librdf_parser_parse_string_into_model(@rdf_parser, content, Redlander.to_rdf_uri(options[:base_uri]), model.rdf_model).zero?
27
+ base_uri = if options.has_key?(:base_uri)
28
+ Uri.new(options[:base_uri]).rdf_uri
29
+ else
30
+ nil
31
+ end
32
+ Redland.librdf_parser_parse_string_into_model(@rdf_parser, content, base_uri, model.rdf_model).zero?
32
33
  end
33
34
 
34
35
  # Parse the content from URI into the model.
@@ -41,25 +42,24 @@ module Redlander
41
42
  def from_uri(model, uri, options = {})
42
43
  uri = URI.parse(uri)
43
44
  uri = URI.parse("file://#{File.expand_path(uri.to_s)}") if uri.scheme.nil?
44
- Redland.librdf_parser_parse_into_model(@rdf_parser, Redlander.to_rdf_uri(uri), Redlander.to_rdf_uri(options[:base_uri]), model.rdf_model).zero?
45
+ base_uri = if options.has_key?(:base_uri)
46
+ Uri.new(options[:base_uri]).rdf_uri
47
+ else
48
+ nil
49
+ end
50
+ Redland.librdf_parser_parse_into_model(@rdf_parser, Uri.new(uri).rdf_uri, base_uri, model.rdf_model).zero?
45
51
  end
46
52
  alias_method :from_file, :from_uri
47
53
 
48
54
  def statements(content, options = {})
49
- # BUG: Parser accumulates data from consequent runs??? WTF, Redland?!
50
- # When parsing a series of files, parser reported a duplicate entry,
51
- # then seemed to have stopped yielding statements at all.
52
-
53
55
  # FIXME: A bug (?) in Redland breaks NTriples parser if its input is not terminated with "\n"
54
56
  content.concat("\n") unless content.end_with?("\n")
55
57
  ParserProxy.new(self, content, options)
56
58
  end
57
-
58
59
  end
59
60
 
60
-
61
+ # Applied to Model
61
62
  module ParsingInstanceMethods
62
-
63
63
  def from_rdfxml(content, options = {})
64
64
  parser = Parser.new(:rdfxml)
65
65
  parser.from_string(self, content, options)
@@ -86,7 +86,5 @@ module Redlander
86
86
  parser.from_uri(self, uri, parser_options)
87
87
  end
88
88
  alias_method :from_file, :from_uri
89
-
90
89
  end
91
-
92
90
  end