rdf-n3 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.
data/.yardopts CHANGED
@@ -8,3 +8,4 @@
8
8
  -
9
9
  AUTHORS
10
10
  VERSION
11
+ History.txt
data/History.txt CHANGED
@@ -1,2 +1,11 @@
1
+ === 0.0.2
2
+ * N3 parsing and Turtle serialization substantially complete.
3
+ * A little more work needed on some tests and some lingering issues in RDF.rb to be resolved.
4
+ * Added script/console and script/parse
5
+ * Updates to reader to bring it in line with other readers. Implement uri() and ns() as helper functions for constructing URIs.
6
+ * Literal_normalization to override RDF::Literal.initialize and create Literal#valid?
7
+ * rdf_escape Literals when serializing via to_s
8
+ * Remove trailing "#" from URIs when normalizing.
9
+
1
10
  === 0.0.1
2
11
  * First port from RdfContext version 0.5.4
data/README.rdoc CHANGED
@@ -10,6 +10,8 @@ RDF::N3 is an Notation-3 (n3-rdf) parser for Ruby using the RDF.rb library suite
10
10
  RDF::N3 parses Notation-3, Turtle and N-Triples into statements or triples. It also serializes to Turtle.
11
11
 
12
12
  * Fully compliant N3-rdf parser (N3-rdf level)
13
+ * Also parses Turtle and N-Triples
14
+ * Turtle serializer
13
15
 
14
16
  Install with 'gem install rdf-n3'
15
17
 
@@ -25,8 +27,8 @@ Instantiate a parser and parse source, specifying type and base-URL
25
27
  end
26
28
 
27
29
  == Dependencies
28
- * [RDF.rb](http://rubygems.org/gems/rdf) (>= 0.1.6)
29
- * [Treetop](http://rubygems.org/gems/treetop) (>= 1.4.0) -- For generating parser only, not necessary for runtime
30
+ * [RDF.rb](http://rubygems.org/gems/rdf) (>= 0.2.0)
31
+ * [Treetop](http://rubygems.org/gems/treetop) (>= 1.4.0)
30
32
 
31
33
  == Resources:
32
34
  * Distiller[http://kellogg-assoc/distiller]
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ require 'yard'
2
3
 
3
4
  begin
4
5
  gem 'jeweler'
@@ -12,10 +13,13 @@ begin
12
13
  gemspec.email = "gregg@kellogg-assoc.com"
13
14
  gemspec.homepage = "http://github.com/gkellogg/rdf-rdfa"
14
15
  gemspec.authors = ["Gregg Kellogg"]
15
- gemspec.add_dependency('rdf', '>= 0.1.6')
16
+ gemspec.add_dependency('rdf', '>= 0.2.0')
17
+ gemspec.add_dependency('treetop', '>= 1.4.0')
16
18
  gemspec.add_development_dependency('rspec')
17
19
  gemspec.add_development_dependency('rdf-spec')
18
- gemspec.add_development_dependency('treetop', '>= 1.4.0')
20
+ gemspec.add_development_dependency('rdf-rdfxml', '>= 0.2.0')
21
+ gemspec.add_development_dependency('rdf-isomorphic')
22
+ gemspec.add_development_dependency('yard')
19
23
  gemspec.extra_rdoc_files = %w(README.rdoc History.txt AUTHORS)
20
24
  end
21
25
  Jeweler::GemcutterTasks.new
@@ -43,4 +47,35 @@ Spec::Rake::SpecTask.new("doc:spec") do |spec|
43
47
  spec.spec_opts = ["--format", "html:doc/spec.html"]
44
48
  end
45
49
 
50
+ YARD::Rake::YardocTask.new do |t|
51
+ t.files = %w(lib/**/*.rb README.rdoc History.txt AUTHORS) # optional
52
+ end
53
+
54
+ desc "Generate test manifest yaml"
55
+ namespace :spec do
56
+ task :prepare do
57
+ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
58
+ require 'rdf/rdfxml'
59
+ require 'rdf/n3'
60
+ require 'spec/rdf_helper'
61
+ require 'fileutils'
62
+
63
+ yaml = File.join(SWAP_DIR, "n3parser.yml")
64
+ FileUtils.rm_f(yaml)
65
+ RdfHelper::TestCase.to_yaml(SWAP_TEST, SWAP_DIR, yaml)
66
+
67
+ yaml = File.join(SWAP_DIR, "regression.yml")
68
+ FileUtils.rm_f(yaml)
69
+ RdfHelper::TestCase.to_yaml(CWM_TEST, SWAP_DIR, yaml)
70
+
71
+ yaml = File.join(TURTLE_DIR, "manifest.yml")
72
+ FileUtils.rm_f(yaml)
73
+ RdfHelper::TestCase.to_yaml(TURTLE_TEST, TURTLE_DIR, yaml)
74
+
75
+ yaml = File.join(TURTLE_DIR, "manifest-bad.yml")
76
+ FileUtils.rm_f(yaml)
77
+ RdfHelper::TestCase.to_yaml(TURTLE_TEST, TURTLE_DIR, yaml)
78
+ end
79
+ end
80
+
46
81
  task :default => :spec
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
data/example.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
4
4
 
5
5
  require 'rubygems'
6
- require 'rdf/rdfa'
6
+ require 'rdf/n3'
7
7
 
8
8
  data = <<-EOF;
9
9
  @prefix dc: <http://purl.org/dc/elements/1.1/>.
data/lib/rdf/n3.rb CHANGED
@@ -1,4 +1,4 @@
1
- $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
1
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..')))
2
2
  require 'rdf'
3
3
 
4
4
  module RDF
@@ -20,11 +20,18 @@ module RDF
20
20
  #
21
21
  # @author [Gregg Kellogg](http://kellogg-assoc.com/)
22
22
  module N3
23
- require 'n3/format'
24
- require 'n3/vocab'
25
- require 'n3/patches/array_hacks'
26
- require 'n3/patches/rdf_escape'
23
+ require 'rdf/n3/format'
24
+ require 'rdf/n3/vocab'
25
+ require 'rdf/n3/patches/array_hacks'
26
+ require 'rdf/n3/patches/literal_hacks'
27
+ require 'rdf/n3/patches/literal_normalization'
28
+ require 'rdf/n3/patches/graph_properties'
29
+ require 'rdf/n3/patches/qname_hacks'
30
+ require 'rdf/n3/patches/rdf_escape'
31
+ require 'rdf/n3/patches/seq'
32
+ require 'rdf/n3/patches/uri_hacks'
27
33
  autoload :Reader, 'rdf/n3/reader'
28
34
  autoload :VERSION, 'rdf/n3/version'
35
+ autoload :Writer, 'rdf/n3/writer'
29
36
  end
30
37
  end
data/lib/rdf/n3/format.rb CHANGED
@@ -4,7 +4,6 @@ module RDF::N3
4
4
  #
5
5
  # @example Obtaining an RDFa format class
6
6
  # RDF::Format.for(:n3) #=> RDF::N3::Format
7
- # RDF::Format.for(:ttl) #=> RDF::N3::Format
8
7
  # RDF::Format.for("etc/foaf.ttl")
9
8
  # RDF::Format.for("etc/foaf.n3")
10
9
  # RDF::Format.for(:file_name => "etc/foaf.ttl")
@@ -1,131 +1,53 @@
1
- # coding: utf-8
2
- require 'iconv'
3
-
4
- class String
5
- #private
6
- # "Borrowed" from JSON utf8_to_json
7
- RDF_MAP = {
8
- "\x0" => '\u0000',
9
- "\x1" => '\u0001',
10
- "\x2" => '\u0002',
11
- "\x3" => '\u0003',
12
- "\x4" => '\u0004',
13
- "\x5" => '\u0005',
14
- "\x6" => '\u0006',
15
- "\x7" => '\u0007',
16
- "\b" => '\b',
17
- "\t" => '\t',
18
- "\n" => '\n',
19
- "\xb" => '\u000B',
20
- "\f" => '\f',
21
- "\r" => '\r',
22
- "\xe" => '\u000E',
23
- "\xf" => '\u000F',
24
- "\x10" => '\u0010',
25
- "\x11" => '\u0011',
26
- "\x12" => '\u0012',
27
- "\x13" => '\u0013',
28
- "\x14" => '\u0014',
29
- "\x15" => '\u0015',
30
- "\x16" => '\u0016',
31
- "\x17" => '\u0017',
32
- "\x18" => '\u0018',
33
- "\x19" => '\u0019',
34
- "\x1a" => '\u001A',
35
- "\x1b" => '\u001B',
36
- "\x1c" => '\u001C',
37
- "\x1d" => '\u001D',
38
- "\x1e" => '\u001E',
39
- "\x1f" => '\u001F',
40
- '"' => '\"',
41
- '\\' => '\\\\',
42
- '/' => '/',
43
- } # :nodoc:
44
-
45
- if defined?(::Encoding)
46
- # Funky way to define constant, but if parsed in 1.8 it generates an 'invalid regular expression' error otherwise
47
- eval %(ESCAPE_RE = %r([\u{80}-\u{10ffff}]))
48
- else
49
- ESCAPE_RE = %r(
50
- [\xc2-\xdf][\x80-\xbf] |
51
- [\xe0-\xef][\x80-\xbf]{2} |
52
- [\xf0-\xf4][\x80-\xbf]{3}
53
- )nx
54
- end
55
-
56
- # Convert a UTF8 encoded Ruby string _string_ to an escaped string, encoded with
57
- # UTF16 big endian characters as \U????, and return it.
58
- #
59
- # \\:: Backslash
60
- # \':: Single quote
61
- # \":: Double quot
62
- # \n:: ASCII Linefeed
63
- # \r:: ASCII Carriage Return
64
- # \t:: ASCCII Horizontal Tab
65
- # \uhhhh:: character in BMP with Unicode value U+hhhh
66
- # \U00hhhhhh:: character in plane 1-16 with Unicode value U+hhhhhh
67
- def rdf_escape
68
- string = self + '' # XXX workaround: avoid buffer sharing
69
- string.gsub!(/["\\\/\x0-\x1f]/) { RDF_MAP[$&] }
70
- if defined?(::Encoding)
71
- string.force_encoding(Encoding::UTF_8)
72
- string.gsub!(ESCAPE_RE) { |c|
73
- s = c.dump.sub(/\"\\u\{(.+)\}\"/, '\1').upcase
74
- (s.length <= 4 ? "\\u0000"[0,6-s.length] : "\\U00000000"[0,10-s.length]) + s
75
- }
76
- string.force_encoding(Encoding::ASCII_8BIT)
1
+ class Array
2
+ # http://wiki.rubygarden.org/Ruby/page/show/ArrayPermute
3
+ # Permute an array, and call a block for each permutation
4
+ # Author: Paul Battley
5
+ def permute(prefixed=[])
6
+ if (length < 2)
7
+ # there are no elements left to permute
8
+ yield(prefixed + self)
77
9
  else
78
- string.gsub!(ESCAPE_RE) { |c|
79
- s = Iconv.new('utf-16be', 'utf-8').iconv(c).unpack('H*').first.upcase
80
- "\\u" + s
81
- }
10
+ # recursively permute the remaining elements
11
+ each_with_index do |e, i|
12
+ (self[0,i]+self[(i+1)..-1]).permute(prefixed+[e]) { |a| yield a }
13
+ end
82
14
  end
83
- string
84
- end
15
+ end unless Array.method_defined?(:permute)
85
16
 
86
- # Unescape characters in strings.
87
- RDF_UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
88
- RDF_UNESCAPE_MAP.update({
89
- ?" => '"',
90
- ?\\ => '\\',
91
- ?/ => '/',
92
- ?b => "\b",
93
- ?f => "\f",
94
- ?n => "\n",
95
- ?r => "\r",
96
- ?t => "\t",
97
- ?u => nil,
98
- })
17
+ # Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options:
18
+ # * <tt>:words_connector</tt> - The sign or word used to join the elements in arrays with two or more elements (default: ", ")
19
+ # * <tt>:two_words_connector</tt> - The sign or word used to join the elements in arrays with two elements (default: " and ")
20
+ # * <tt>:last_word_connector</tt> - The sign or word used to join the last element in arrays with three or more elements (default: ", and ")
21
+ def to_sentence(options = {})
22
+ default_words_connector = ", "
23
+ default_two_words_connector = " and "
24
+ default_last_word_connector = ", and "
99
25
 
100
- if defined?(::Encoding)
101
- UNESCAPE_RE = %r(
102
- (?:\\[\\bfnrt"/]) # Escaped control characters, " and /
103
- |(?:\\U00\h{6}) # 6 byte escaped Unicode
104
- |(?:\\u\h{4}) # 4 byte escaped Unicode
105
- )x
106
- else
107
- UNESCAPE_RE = %r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n
108
- end
109
-
110
- # Reverse operation of escape
111
- # From JSON parser
112
- def rdf_unescape
113
- return '' if self.empty?
114
- string = self.gsub(UNESCAPE_RE) do |c|
115
- case c[1,1]
116
- when 'U'
117
- raise RdfException, "Long Unicode escapes no supported in Ruby 1.8" unless defined?(::Encoding)
118
- eval(c.sub(/\\U00(\h+)/, '"\u{\1}"'))
119
- when 'u'
120
- bytes = [c[2, 2].to_i(16), c[4, 2].to_i(16)]
121
- Iconv.new('utf-8', 'utf-16').iconv(bytes.pack("C*"))
26
+ # Try to emulate to_senteces previous to 2.3
27
+ if options.has_key?(:connector) || options.has_key?(:skip_last_comma)
28
+ ::ActiveSupport::Deprecation.warn(":connector has been deprecated. Use :words_connector instead", caller) if options.has_key? :connector
29
+ ::ActiveSupport::Deprecation.warn(":skip_last_comma has been deprecated. Use :last_word_connector instead", caller) if options.has_key? :skip_last_comma
30
+
31
+ skip_last_comma = options.delete :skip_last_comma
32
+ if connector = options.delete(:connector)
33
+ options[:last_word_connector] ||= skip_last_comma ? connector : ", #{connector}"
122
34
  else
123
- RDF_UNESCAPE_MAP[c[1]]
35
+ options[:last_word_connector] ||= skip_last_comma ? default_two_words_connector : default_last_word_connector
124
36
  end
125
37
  end
126
- string.force_encoding(Encoding::UTF_8) if defined?(::Encoding)
127
- string
128
- rescue Iconv::Failure => e
129
- raise RdfException, "Caught #{e.class}: #{e}"
130
- end
131
- end unless String.method_defined?(:rdf_escape)
38
+
39
+ # options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
40
+ options = {:words_connector => default_words_connector, :two_words_connector => default_two_words_connector, :last_word_connector => default_last_word_connector}.merge(options)
41
+
42
+ case length
43
+ when 0
44
+ ""
45
+ when 1
46
+ self[0].to_s
47
+ when 2
48
+ "#{self[0]}#{options[:two_words_connector]}#{self[1]}"
49
+ else
50
+ "#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
51
+ end
52
+ end unless Array.method_defined?(:to_sentence)
53
+ end
@@ -0,0 +1,34 @@
1
+ module RDF
2
+ class Graph
3
+ # Resource properties
4
+ #
5
+ # Properties arranged as a hash with the predicate Term as index to an array of resources or literals
6
+ #
7
+ # Example:
8
+ # graph.load(':foo a :bar; rdfs:label "An example" .', "http://example.com/")
9
+ # graph.resources(URI.new("http://example.com/subject")) =>
10
+ # {
11
+ # "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" => [<http://example.com/#bar>],
12
+ # "http://example.com/#label" => ["An example"]
13
+ # }
14
+ def properties(subject, recalc = false)
15
+ @properties ||= {}
16
+ @properties.delete(subject.to_s) if recalc
17
+ @properties[subject.to_s] ||= begin
18
+ hash = Hash.new
19
+ self.query(:subject => subject) do |statement|
20
+ pred = statement.predicate.to_s
21
+
22
+ hash[pred] ||= []
23
+ hash[pred] << statement.object
24
+ end
25
+ hash
26
+ end
27
+ end
28
+
29
+ # Get type(s) of subject, returns a list of symbols
30
+ def type_of(subject)
31
+ query(:subject => subject, :predicate => RDF.type).map {|st| st.object}
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ module RDF
2
+ class Literal
3
+ # Support for XML Literals
4
+ # Is this an XMLLiteral?
5
+ def xmlliteral?
6
+ datatype == RDF['XMLLiteral']
7
+ end
8
+
9
+ def anonymous?; false; end unless respond_to?(:anonymous?)
10
+
11
+ ##
12
+ # Returns a string representation of this literal.
13
+ #
14
+ # @return [String]
15
+ def to_s
16
+ quoted = value # FIXME
17
+ output = "\"#{quoted.to_s.rdf_escape}\""
18
+ output << "@#{language}" if has_language? && !has_datatype?
19
+ output << "^^<#{datatype}>" if has_datatype?
20
+ output
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,120 @@
1
+ autoload :Date, 'date'
2
+ autoload :DateTime, 'date'
3
+ autoload :Time, 'time'
4
+
5
+ module RDF
6
+ class Literal
7
+ ##
8
+ # Re-define initialize/new to call _normalize_ on value.
9
+ # @param [Object]
10
+ # @option options [Symbol] :language (nil)
11
+ # @option options [URI] :datatype (nil)
12
+ # @option options[Hash] :namespaces ({})
13
+ def initialize_with_normalization(value, options = {})
14
+ initialize_without_normalization(value, options)
15
+ normalize(options)
16
+ end
17
+
18
+ alias_method :initialize_without_normalization, :initialize
19
+ alias_method :initialize, :initialize_with_normalization
20
+
21
+ def valid?
22
+ case datatype
23
+ when XSD.boolean then %w(1 true 0 false).include?(value.to_s.downcase)
24
+ when XSD.decimal then !!value.to_s.match(/^[\+\-]?\d+(\.\d*)?$/)
25
+ when XSD.double then !!value.to_s.match(/^[\+\-]?\d+(\.\d*([eE][\+\-]?\d+)?)?$/)
26
+ when XSD.integer then !!value.to_s.match(/^[\+\-]?\d+$/)
27
+ else true
28
+ end
29
+ end
30
+
31
+ protected
32
+
33
+ # Normalize literal value
34
+ #
35
+ # Options is a hash passed to initialize
36
+ def normalize(options = {})
37
+ return unless valid? # Only normalize valid value
38
+
39
+ @value = case datatype
40
+ when XSD.boolean then %(1 true).include?(@value.to_s.downcase) ? "true" : "false"
41
+ when XSD.integer then @value.to_i.to_s
42
+ when XSD.decimal then normalize_decimal(@value, options)
43
+ when XSD.double then normalize_double(@value, options)
44
+ when XSD.time then @value.is_a?(Time) ? @value.strftime("%H:%M:%S%Z").sub(/\+00:00|UTC/, "Z") : @value.to_s
45
+ when XSD.dateTime then @value.is_a?(DateTime) ? @value.strftime("%Y-%m-%dT%H:%M:%S%Z").sub(/\+00:00|UTC/, "Z") : @value.to_s
46
+ when XSD.date then @value.is_a?(Date) ? @value.strftime("%Y-%m-%d%Z").sub(/\+00:00|UTC/, "Z") : @value.to_s
47
+ when RDF.XMLLiteral then normalize_xmlliteral(@value, options)
48
+ else @value.to_s
49
+ end
50
+ end
51
+
52
+ def normalize_decimal(contents, options)
53
+ # Can't use simple %f transformation do to special requirements from N3 tests in representation
54
+ i, f = contents.to_s.split(".")
55
+ f = f.to_s[0,16] # Truncate after 15 decimal places
56
+ i.sub!(/^\+?0+(\d)$/, '\1')
57
+ f.sub!(/0*$/, '')
58
+ f = "0" if f.empty?
59
+ "#{i}.#{f}"
60
+ end
61
+
62
+ def normalize_double(contents, options)
63
+ i, f, e = ("%.16E" % contents.to_f).split(/[\.E]/)
64
+ f.sub!(/0*$/, '')
65
+ f = "0" if f.empty?
66
+ e.sub!(/^\+?0+(\d)$/, '\1')
67
+ "#{i}.#{f}E#{e}"
68
+ end
69
+
70
+ # Normalize an XML Literal, by adding necessary namespaces.
71
+ # This should be done as part of initialize
72
+ #
73
+ # namespaces is a hash of prefix => URIs
74
+ def normalize_xmlliteral(contents, options = {})
75
+ options[:namespaces] ||= {}
76
+
77
+ begin
78
+ # Only normalize if Nokogiri is included
79
+ require 'nokogiri' unless defined?(Nokogiri)
80
+ rescue LoadError => e
81
+ contents.to_s # No normalization
82
+ end
83
+
84
+ if contents.is_a?(String)
85
+ ns_hash = {}
86
+ options[:namespaces].each_pair do |prefix, uri|
87
+ attr = prefix.to_s.empty? ? "xmlns" : "xmlns:#{prefix}"
88
+ ns_hash[attr] = uri.to_s
89
+ end
90
+ ns_strs = []
91
+ ns_hash.each_pair {|a, u| ns_strs << "#{a}=\"#{u}\""}
92
+
93
+ # Add inherited namespaces to created root element so that they're inherited to sub-elements
94
+ contents = Nokogiri::XML::Document.parse("<foo #{ns_strs.join(" ")}>#{contents}</foo>").root.children
95
+ end
96
+
97
+ # Add already mapped namespaces and language
98
+ contents.map do |c|
99
+ if c.is_a?(Nokogiri::XML::Element)
100
+ c = Nokogiri::XML.parse(c.dup.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::NO_EMPTY_TAGS)).root
101
+ # Gather namespaces from self and decendant nodes
102
+ c.traverse do |n|
103
+ ns = n.namespace
104
+ next unless ns
105
+ prefix = ns.prefix ? "xmlns:#{ns.prefix}" : "xmlns"
106
+ c[prefix] = ns.href.to_s unless c.namespaces[prefix]
107
+ end
108
+
109
+ # Add lanuage
110
+ if options[:language] && c["lang"].to_s.empty?
111
+ c["xml:lang"] = options[:language]
112
+ end
113
+ end
114
+ c.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::NO_EMPTY_TAGS)
115
+ end.join("")
116
+ end
117
+ end
118
+
119
+ class NormalizationError < IOError; end
120
+ end