redlander 0.2.2 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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