├── .github └── workflows │ ├── ci.yml │ └── generate-docs.yml ├── .gitignore ├── .yardopts ├── AUTHORS ├── CONTRIBUTING.md ├── CREDITS ├── Gemfile ├── README.md ├── Rakefile ├── UNLICENSE ├── VERSION ├── dependencyci.yml ├── doc └── .gitignore ├── etc ├── doap.html ├── doap.nt ├── doap.ttl └── doap.xml ├── lib └── rdf │ ├── raptor.rb │ └── raptor │ ├── cli.rb │ ├── ffi.rb │ ├── ffi │ ├── v1.rb │ ├── v1 │ │ ├── iostream.rb │ │ ├── iostream_handler.rb │ │ ├── parser.rb │ │ ├── serializer.rb │ │ ├── statement.rb │ │ └── uri.rb │ ├── v2.rb │ └── v2 │ │ ├── iostream.rb │ │ ├── iostream_handler.rb │ │ ├── namespace.rb │ │ ├── parser.rb │ │ ├── serializer.rb │ │ ├── statement.rb │ │ ├── term.rb │ │ ├── uri.rb │ │ └── world.rb │ ├── format.rb │ ├── graphviz.rb │ ├── ntriples.rb │ ├── rdfa.rb │ ├── rdfxml.rb │ ├── turtle.rb │ └── version.rb ├── memtest.rb ├── rdf-raptor.gemspec └── spec ├── format ├── graphviz_spec.rb ├── ntriples_spec.rb ├── rdfa_spec.rb ├── rdfxml_spec.rb └── turtle_spec.rb ├── raptor_cli_spec.rb ├── raptor_ffi_spec.rb ├── raptor_spec.rb ├── spec_helper.rb └── version_spec.rb /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This workflow runs continuous CI across different versions of ruby on all branches and pull requests to develop. 2 | 3 | name: CI 4 | on: 5 | push: 6 | branches: [ '**' ] 7 | pull_request: 8 | branches: [ develop ] 9 | workflow_dispatch: 10 | 11 | jobs: 12 | tests: 13 | name: Ruby ${{ matrix.ruby }} 14 | if: "contains(github.event.commits[0].message, '[ci skip]') == false" 15 | runs-on: ubuntu-latest 16 | env: 17 | CI: true 18 | ALLOW_FAILURES: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'jruby' }} 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | ruby: ['3.0', 3.1, 3.2, ruby-head, jruby] 23 | steps: 24 | - name: Clone repository 25 | uses: actions/checkout@v3 26 | - name: Set up Ruby 27 | uses: ruby/setup-ruby@v1 28 | with: 29 | ruby-version: ${{ matrix.ruby }} 30 | - name: Install dependencies 31 | run: bundle install --jobs 4 --retry 3 32 | - name: Install Raptor2 33 | run: sudo apt-get install raptor2-utils 34 | - name: Run tests 35 | run: ruby --version; bundle exec rspec spec || $ALLOW_FAILURES 36 | - name: Coveralls GitHub Action 37 | uses: coverallsapp/github-action@v2 38 | if: "matrix.ruby == '3.2'" 39 | with: 40 | github-token: ${{ secrets.GITHUB_TOKEN }} 41 | -------------------------------------------------------------------------------- /.github/workflows/generate-docs.yml: -------------------------------------------------------------------------------- 1 | name: Build & deploy documentation 2 | on: 3 | push: 4 | branches: 5 | - master 6 | workflow_dispatch: 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | name: Update gh-pages with docs 11 | steps: 12 | - name: Clone repository 13 | uses: actions/checkout@v3 14 | - name: Set up Ruby 15 | uses: ruby/setup-ruby@v1 16 | with: 17 | ruby-version: "3.1" 18 | - name: Install required gem dependencies 19 | run: gem install yard --no-document 20 | - name: Build YARD Ruby Documentation 21 | run: yardoc 22 | - name: Deploy 23 | uses: peaceiris/actions-gh-pages@v3 24 | with: 25 | github_token: ${{ secrets.GITHUB_TOKEN }} 26 | publish_dir: ./doc/yard 27 | publish_branch: gh-pages 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ruby-version 2 | .DS_Store 3 | .tmp 4 | .yardoc 5 | pkg 6 | tmp 7 | Gemfile.lock 8 | *.log 9 | coverage 10 | .rbx/ 11 | /.byebug_history 12 | Vagrantfile 13 | .vagrant 14 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --title "RDF::Raptor - RDF/XML, Turtle and RDFa Support for RDF.rb" 2 | --output-dir doc/yard 3 | --protected 4 | --no-private 5 | --hide-void-return 6 | --markup markdown 7 | --readme README.md 8 | - 9 | AUTHORS 10 | CREDITS 11 | UNLICENSE 12 | VERSION 13 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | * Arto Bendiken 2 | * John Fieber 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Community contributions are essential for keeping Ruby RDF great. We want to keep it as easy as possible to contribute changes that get things working in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. 4 | 5 | ## Development 6 | 7 | This repository uses [Git Flow](https://github.com/nvie/gitflow) to manage development and release activity. All submissions _must_ be on a feature branch based on the _develop_ branch to ease staging and integration. 8 | 9 | * create or respond to an issue on the [Github Repository](https://github.com/ruby-rdf/rdf-raptor/issues) 10 | * Fork and clone the repo: 11 | `git clone git@github.com:your-username/rdf-raptor.git` 12 | * Install bundle: 13 | `bundle install` 14 | * Create tests in RSpec and make sure you achieve at least 90% code coverage for the feature your adding or behavior being modified. 15 | * Push to your fork and [submit a pull request][pr]. 16 | 17 | ## Do's and Dont's 18 | * Do your best to adhere to the existing coding conventions and idioms. 19 | * Don't use hard tabs, and don't leave trailing whitespace on any line. 20 | Before committing, run `git diff --check` to make sure of this. 21 | * Do document every method you add using [YARD][] annotations. Read the 22 | [tutorial][YARD-GS] or just look at the existing code for examples. 23 | * Don't touch the `.gemspec` or `VERSION` files. If you need to change them, 24 | do so on your private branch only. 25 | * Do feel free to add yourself to the `CREDITS` file and the 26 | corresponding list in the the `README`. Alphabetical order applies. 27 | * Don't touch the `AUTHORS` file. If your contributions are significant 28 | enough, be assured we will eventually add you in there. 29 | * Do note that in order for us to merge any non-trivial changes (as a rule 30 | of thumb, additions larger than about 15 lines of code), we need an 31 | explicit [public domain dedication][PDD] on record from you, 32 | which you will be asked to agree to on the first commit to a repo within the organization. 33 | Note that the agreement applies to all repos in the [Ruby RDF](https://github.com/ruby-rdf/) organization. 34 | 35 | [YARD]: https://yardoc.org/ 36 | [YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md 37 | [PDD]: https://unlicense.org/#unlicensing-contributions 38 | [pr]: https://github.com/ruby-rdf/rdf/compare/ 39 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | * Ben Lavender 2 | * David Butler 3 | * Gregg Kellogg 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | 5 | gem 'rdf', github: 'ruby-rdf/rdf', branch: "develop" 6 | gem 'rdf-spec', github: 'ruby-rdf/rdf-spec', branch: "develop" 7 | gem "rdf-isomorphic", github: "ruby-rdf/rdf-isomorphic", branch: "develop" 8 | 9 | group :test do 10 | gem 'simplecov', '~> 0.22', platforms: :mri 11 | gem 'simplecov-lcov', '~> 0.8', platforms: :mri 12 | end 13 | 14 | group :debug do 15 | gem 'pry' 16 | gem 'pry-byebug', platforms: :mri 17 | end 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Raptor RDF Extension for RDF.rb 2 | =================================== 3 | 4 | This is an [RDF.rb][] extension that adds support for parsing/serializing [NTriples][], 5 | [RDF/XML][], [Turtle][], [RDFa][], and [Graphviz][] data using the [Raptor RDF Parser][Raptor] 6 | library. 7 | 8 | [![Gem Version](https://badge.fury.io/rb/rdf-raptor.svg)](https://badge.fury.io/rb/rdf-raptor) 9 | [![Build Status](https://github.com/ruby-rdf/rdf-raptor/workflows/CI/badge.svg?branch=develop)](https://github.com/ruby-rdf/rdf-raptor/actions?query=workflow%3ACI) 10 | [![Gitter chat](https://badges.gitter.im/ruby-rdf/rdf.png)](https://gitter.im/ruby-rdf/rdf) 11 | 12 | Features 13 | -------- 14 | 15 | * Uses the fast [Raptor][] C library. 16 | * Parses and serializes RDF data from/into the NTriples, RDF/XML, and Turtle formats. 17 | * Extracts RDF statements from XHTML+RDFa documents. 18 | * Serializes RDF statements into Graphviz format. 19 | * Provides serialization format auto-detection for RDF/XML, Turtle and RDFa. 20 | * Compatible with any operating system supported by Raptor and Ruby. 21 | * Compatible with MRI >= 2.6, JRuby and Rubinius. 22 | 23 | Examples 24 | -------- 25 | 26 | require 'rdf/raptor' 27 | 28 | ### Ensuring Raptor is installed and obtaining the version number 29 | 30 | RDF::Raptor.available? #=> true 31 | RDF::Raptor.version #=> "3.2.0" 32 | 33 | ### Parsing RDF statements from an NTriples file 34 | 35 | RDF::Reader.open("http://datagraph.org/jhacker/foaf.nt") do |reader| 36 | reader.each_statement do |statement| 37 | puts statement.inspect 38 | end 39 | end 40 | 41 | ### Parsing RDF statements from an RDF/XML file 42 | 43 | RDF::Reader.open("http://datagraph.org/jhacker/foaf.rdf") do |reader| 44 | reader.each_statement do |statement| 45 | puts statement.inspect 46 | end 47 | end 48 | 49 | ### Parsing RDF statements from a Turtle file 50 | 51 | RDF::Reader.open("http://datagraph.org/jhacker/foaf.ttl") do |reader| 52 | reader.each_statement do |statement| 53 | puts statement.inspect 54 | end 55 | end 56 | 57 | ### Extracting RDF statements from an HTML+RDFa document 58 | 59 | RDF::Reader.open(url = "http://bblfish.net/", format: :rdfa, base_uri: url) do |reader| 60 | reader.each_statement do |statement| 61 | puts statement.inspect 62 | end 63 | end 64 | 65 | ### Serializing RDF statements into an RDF/XML file 66 | 67 | data = RDF::Repository.load("http://datagraph.org/jhacker/foaf.nt") 68 | 69 | RDF::Writer.open("output.rdf") do |writer| 70 | data.each_statement do |statement| 71 | writer << statement 72 | end 73 | end 74 | 75 | ### Serializing RDF statements into a Turtle file 76 | 77 | data = RDF::Repository.load("http://datagraph.org/jhacker/foaf.nt") 78 | 79 | RDF::Writer.open("output.ttl") do |writer| 80 | data.each_statement do |statement| 81 | writer << statement 82 | end 83 | end 84 | 85 | ### Obtaining the NTriples format specification class 86 | 87 | RDF::Format.for(:ntriples) #=> RDF::Raptor::NTriples::Format 88 | RDF::Format.for("input.nt") 89 | RDF::Format.for(file_name: "input.nt") 90 | RDF::Format.for(file_extension: "nt") 91 | RDF::Format.for(content_type: "application/n-triples") 92 | 93 | ### Obtaining the RDF/XML format specification class 94 | 95 | RDF::Format.for(:rdfxml) #=> RDF::Raptor::RDFXML::Format 96 | RDF::Format.for("input.rdf") 97 | RDF::Format.for(file_name: "input.rdf") 98 | RDF::Format.for(file_extension: "rdf") 99 | RDF::Format.for(content_type: "application/rdf+xml") 100 | 101 | ### Obtaining the Turtle format specification class 102 | 103 | RDF::Format.for(:turtle) #=> RDF::Raptor::Turtle::Format 104 | RDF::Format.for("input.ttl") 105 | RDF::Format.for(file_name: "input.ttl") 106 | RDF::Format.for(file_extension: "ttl") 107 | RDF::Format.for(content_type: "text/turtle") 108 | 109 | ### Obtaining the RDFa format specification class 110 | 111 | RDF::Format.for(:rdfa) #=> RDF::Raptor::RDFa::Format 112 | RDF::Format.for("input.html") 113 | RDF::Format.for(file_name: "input.html") 114 | RDF::Format.for(file_extension: "html") 115 | RDF::Format.for(content_type: "application/xhtml+xml") 116 | 117 | ### Obtaining the Graphviz format specification class 118 | 119 | RDF::Format.for(:graphviz) #=> RDF::Raptor::Graphviz::Format 120 | RDF::Format.for("output.dot") 121 | RDF::Format.for(file_name: "output.dot") 122 | RDF::Format.for(file_extension: "") 123 | RDF::Format.for(content_type: "text/vnd.graphviz") 124 | 125 | Documentation 126 | ------------- 127 | 128 | 129 | 130 | * {RDF::Raptor} 131 | * {RDF::Raptor::NTriples} 132 | * {RDF::Raptor::Turtle} 133 | * {RDF::Raptor::RDFXML} 134 | * {RDF::Raptor::RDFa} 135 | * {RDF::Raptor::Graphviz} 136 | 137 | Dependencies 138 | ------------ 139 | 140 | * [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.2) 141 | * [FFI](https://rubygems.org/gems/ffi) (~> 1.15) 142 | * [Raptor][] (>= 2.0), the `libraptor` library or the `rapper` binary 143 | 144 | Installation 145 | ------------ 146 | 147 | The recommended installation method is via [RubyGems](https://rubygems.org/). 148 | To install the latest official release of the `RDF::Raptor` gem, do: 149 | 150 | % [sudo] gem install rdf-raptor 151 | 152 | To install the required [Raptor][] command-line tools themselves, look for a 153 | `raptor` or `raptor-utils` package in your platform's package management 154 | system. For your convenience, here follow installation instructions for the 155 | Mac and the most common Linux and BSD distributions: 156 | 157 | % [sudo] port install raptor # Mac OS X with MacPorts 158 | % [sudo] fink install raptor-bin # Mac OS X with Fink 159 | % brew install raptor # Mac OS X with Homebrew 160 | % [sudo] aptitude install raptor-utils # Ubuntu / Debian with aptitude 161 | % [sudo] apt-get install libraptor2-0 # Ubuntu / Debian with apt-get 162 | % [sudo] yum install raptor2 # Fedora / CentOS / RHEL 163 | % [sudo] zypper install raptor # openSUSE 164 | % [sudo] emerge raptor # Gentoo Linux 165 | % [sudo] pacman -S raptor # Arch Linux 166 | % [sudo] pkg_add -r raptor # FreeBSD 167 | % [sudo] pkg_add raptor # OpenBSD / NetBSD 168 | 169 | If the `libraptor2` library is in the standard library search path, and 170 | the `rapper` command is in the standard command search path, all should 171 | be well and work fine out of the box. However, if either is in a 172 | non-standard location, be sure to set the `RDF_RAPTOR_LIBPATH` and/or 173 | `RDF_RAPTOR_BINPATH` environment variables appropriately before 174 | requiring `rdf/raptor`. 175 | 176 | Download 177 | -------- 178 | 179 | To get a local working copy of the development repository, do: 180 | 181 | % git clone git@github.com:ruby-rdf/rdf-raptor.git 182 | 183 | Alternatively, download the latest development version as a tarball as 184 | follows: 185 | 186 | % wget https://github.com/ruby-rdf/rdf-raptor/tarball/master 187 | 188 | Mailing List 189 | ------------ 190 | 191 | * 192 | 193 | Authors 194 | ------- 195 | 196 | * [Arto Bendiken](https://github.com/artob) - 197 | * [John Fieber](https://github.com/jfieber) - 198 | 199 | Contributors 200 | ------------ 201 | 202 | * [Ben Lavender](https://github.com/bhuga) - 203 | * [David Butler](https://github.com/dwbutler) - 204 | * [Gregg Kellogg](https://github.com/gkellogg) - 205 | 206 | License 207 | ------- 208 | 209 | This is free and unencumbered public domain software. For more information, 210 | see or the accompanying [UNLICENSE][] file. 211 | 212 | [RDF.rb]: https://ruby-rdf.github.io/rdf 213 | [NTriples]: https://en.wikipedia.org/wiki/N-Triples 214 | [RDF/XML]: https://www.w3.org/TR/REC-rdf-syntax/ 215 | [Turtle]: https://en.wikipedia.org/wiki/Turtle_(syntax) 216 | [RDFa]: https://rdfa.info/ 217 | [Graphviz]: https://www.graphviz.org/ 218 | [Raptor]: https://librdf.org/raptor/ 219 | [rapper]: https://librdf.org/raptor/rapper.html 220 | [UNLICENSE]:https://github.com/ruby-rdf/rdf-raptor/blob/master/UNLICENSE 221 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'lib'))) 4 | require 'rubygems' 5 | 6 | namespace :gem do 7 | desc "Build the rdf-raptor-#{File.read('VERSION').chomp}.gem file" 8 | task :build do 9 | sh "gem build rdf-raptor.gemspec && mv rdf-raptor-#{File.read('VERSION').chomp}.gem pkg/" 10 | end 11 | 12 | desc "Release the rdf-raptor-#{File.read('VERSION').chomp}.gem file" 13 | task :release do 14 | sh "gem push pkg/rdf-raptor-#{File.read('VERSION').chomp}.gem" 15 | end 16 | end 17 | 18 | require 'rdf/raptor' 19 | require 'bundler/gem_helper' 20 | Bundler::GemHelper.install_tasks name: 'rdf-raptor' 21 | 22 | desc 'Run memory leak test with valgrind' 23 | task :memtest do 24 | puts system 'valgrind --log-file=valgrind.log --trace-children=yes --leak-check=full ruby memtest.rb; grep -C 20 raptor_ valgrind.log' 25 | end 26 | 27 | require 'rspec/core/rake_task' 28 | desc 'Default: run specs.' 29 | task default: :spec 30 | 31 | desc 'Run specs' 32 | RSpec::Core::RakeTask.new do |t| 33 | t.pattern = './spec/**/*_spec.rb' # don't need this, it's default. 34 | # Put spec opts in a file named .rspec in root 35 | end 36 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 3.3.0 2 | -------------------------------------------------------------------------------- /dependencyci.yml: -------------------------------------------------------------------------------- 1 | platform: 2 | Rubygems: 3 | rdf-isomorphic: 4 | tests: 5 | unmaintained: skip -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | rdoc 2 | yard 3 | -------------------------------------------------------------------------------- /etc/doap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | Raptor RDF Parser plugin for RDF.rb. 12 | 13 | 14 | 15 |

Project description for RDF::RDFa.

16 |

17 | RDF.rb plugin for parsing/serializing RDF/XML, Turtle and RDFa data using the Raptor RDF Parser library. 18 |

19 |
20 |
Implemented Specifications
21 | 29 |
30 |
Creator
31 | 33 | Gregg Kellogg 34 | 35 |
36 |
Contributors
37 | 40 |
41 |
Created
2011-08-29
42 |
Blog
https://greggkellogg.net/
43 |
Bug DB
44 | 45 | https://github.com/ruby-rdf/rdf-rdfa/issues 46 | 47 |
48 |
Programming Language
Ruby
49 |
Category
50 | Resource Description Framework 51 | for 52 | Ruby 53 |
54 |
Download
55 | https://rubygems.org/gems/rdf-raptor 56 |
57 |
Home Page
58 | https://github.com/ruby-rdf/rdf-raptor 59 |
60 |
License
61 | Public Domain 62 |
63 |
Mailing List
64 | https://lists.w3.org/Archives/Public/public-rdf-ruby/ 65 |
66 |
67 | 68 | 69 | -------------------------------------------------------------------------------- /etc/doap.nt: -------------------------------------------------------------------------------- 1 | . 2 | "Arto Bendiken" . 3 | . 4 | "a033f652c84a4d73b8c26d318c2395699dd2bdfb" . 5 | "d0737cceb55eb7d740578d2db1bc0727e3ed49ce" . 6 | . 7 | . 8 | "Raptor RDF Parser plugin for RDF.rb."@en . 9 | . 10 | . 11 | . 12 | _:jfieber . 13 | . 14 | . 15 | "RDF.rb plugin for parsing/serializing RDF/XML, Turtle and RDFa data using the Raptor RDF Parser library."@en . 16 | "RDF::Raptor" . 17 | . 18 | . 19 | . 20 | . 21 | "Ruby" . 22 | . 23 | . 24 | . 25 | "2010-03-23" . 26 | . 27 | _:jfieber "John Fieber" . 28 | _:jfieber . 29 | _:jfieber "f7653fc1ac0e82ebb32f092389bd5fc728eaae12" . 30 | _:jfieber . 31 | . 32 | "Ben Lavender" . 33 | . 34 | "dbf45f4ffbd27b67aa84f02a6a31c144727d10af" . 35 | . 36 | . 37 | . 38 | "Gregg Kellogg" . 39 | . 40 | "35bc44e6d0070e5ad50ccbe0d24403c96af2b9bd" . 41 | . 42 | -------------------------------------------------------------------------------- /etc/doap.ttl: -------------------------------------------------------------------------------- 1 | @base . 2 | @prefix rdf: . 3 | @prefix rdfs: . 4 | @prefix dc: . 5 | @prefix foaf: . 6 | @prefix doap: . 7 | 8 | <> a doap:Project ; 9 | doap:name "RDF::Raptor" ; 10 | doap:homepage ; 11 | doap:license ; 12 | doap:shortdesc "Raptor RDF Parser plugin for RDF.rb."@en ; 13 | doap:description "RDF.rb plugin for parsing/serializing RDF/XML, Turtle and RDFa data using the Raptor RDF Parser library."@en ; 14 | doap:created "2010-03-23" ; 15 | doap:programming-language "Ruby" ; 16 | doap:download-page <> ; 17 | doap:bug-database ; 18 | doap:blog ; 19 | doap:developer , 20 | _:jfieber, 21 | _:dwbutler, 22 | ; 23 | doap:maintainer _:dwbutler, ; 24 | doap:documenter ; 25 | doap:helper , 26 | ; 27 | foaf:maker ; 28 | dc:creator . 29 | 30 | a foaf:Person ; 31 | foaf:name "Arto Bendiken" ; 32 | foaf:mbox ; 33 | foaf:mbox_sha1sum "d0737cceb55eb7d740578d2db1bc0727e3ed49ce", 34 | "a033f652c84a4d73b8c26d318c2395699dd2bdfb" ; 35 | foaf:homepage . 36 | 37 | a foaf:Person ; 38 | foaf:name "Ben Lavender" ; 39 | foaf:mbox ; 40 | foaf:mbox_sha1sum "dbf45f4ffbd27b67aa84f02a6a31c144727d10af" ; 41 | foaf:homepage . 42 | 43 | _:dwbutler a foaf:Person ; 44 | foaf:name "David Butler" ; 45 | #foaf:mbox ; 46 | foaf:mbox_sha1sum "8125fe100c2ed5f6ced2a88bd09a244e1c91682b" ; 47 | foaf:homepage . 48 | 49 | _:jfieber a foaf:Person ; 50 | foaf:name "John Fieber" ; 51 | #foaf:mbox ; 52 | foaf:mbox_sha1sum "f7653fc1ac0e82ebb32f092389bd5fc728eaae12" ; 53 | foaf:homepage . 54 | 55 | a foaf:Person ; 56 | foaf:name "Gregg Kellogg" ; 57 | foaf:mbox ; 58 | foaf:mbox_sha1sum "35bc44e6d0070e5ad50ccbe0d24403c96af2b9bd" ; 59 | foaf:homepage ; 60 | rdfs:isDefinedBy . -------------------------------------------------------------------------------- /etc/doap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | dbf45f4ffbd27b67aa84f02a6a31c144727d10af 9 | 10 | Ben Lavender 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | a033f652c84a4d73b8c26d318c2395699dd2bdfb 22 | d0737cceb55eb7d740578d2db1bc0727e3ed49ce 23 | 24 | Arto Bendiken 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 2010-03-23 35 | 36 | RDF.rb plugin for parsing/serializing RDF/XML, Turtle and RDFa data using the Raptor RDF Parser library. 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 35bc44e6d0070e5ad50ccbe0d24403c96af2b9bd 47 | 48 | Gregg Kellogg 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | f7653fc1ac0e82ebb32f092389bd5fc728eaae12 59 | 60 | John Fieber 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | RDF::Raptor 80 | 81 | Ruby 82 | 83 | Raptor RDF Parser plugin for RDF.rb. 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /lib/rdf/raptor.rb: -------------------------------------------------------------------------------- 1 | require 'rdf' # @see https://rubygems.org/gems/rdf 2 | require 'rdf/raptor/format' 3 | 4 | module RDF 5 | ## 6 | # **`RDF::Raptor`** is a Raptor RDF Parser plugin for RDF.rb. 7 | # 8 | # * {RDF::Raptor::NTriples} provides support for the standard 9 | # machine-readable N-Triples format. 10 | # * {RDF::Raptor::Turtle} provides support for the popular 11 | # human-readable Turtle format. 12 | # * {RDF::Raptor::RDFXML} provides support for the standard 13 | # machine-readable RDF/XML format. 14 | # * {RDF::Raptor::RDFa} provides support for extracting 15 | # RDF statements from XHTML+RDFa documents. 16 | # * {RDF::Raptor::Graphviz} provides support for serializing 17 | # RDF statements to the Graphviz DOT format. 18 | # 19 | # @example Requiring the `RDF::Raptor` module 20 | # require 'rdf/raptor' 21 | # 22 | # @example Checking whether Raptor is installed 23 | # RDF::Raptor.available? #=> true 24 | # 25 | # @example Obtaining the Raptor version number 26 | # RDF::Raptor.version #=> "1.4.21" 27 | # 28 | # @example Obtaining the Raptor engine 29 | # RDF::Raptor::ENGINE #=> :ffi 30 | # 31 | # @example Obtaining an N-Triples format class 32 | # RDF::Format.for(:ntriples) #=> RDF::Raptor::NTriples::Format 33 | # RDF::Format.for("input.nt") 34 | # RDF::Format.for(file_name: "input.nt") 35 | # RDF::Format.for(file_extension: "nt") 36 | # RDF::Format.for(content_type: "text/plain") 37 | # 38 | # @example Obtaining a Turtle format class 39 | # RDF::Format.for(:turtle) #=> RDF::Raptor::Turtle::Format 40 | # RDF::Format.for("input.ttl") 41 | # RDF::Format.for(file_name: "input.ttl") 42 | # RDF::Format.for(file_extension: "ttl") 43 | # RDF::Format.for(content_type: "text/turtle") 44 | # 45 | # @example Obtaining an RDF/XML format class 46 | # RDF::Format.for(:rdfxml) #=> RDF::Raptor::RDFXML::Format 47 | # RDF::Format.for("input.rdf") 48 | # RDF::Format.for(file_name: "input.rdf") 49 | # RDF::Format.for(file_extension: "rdf") 50 | # RDF::Format.for(content_type: "application/rdf+xml") 51 | # 52 | # @example Obtaining an RDFa format class 53 | # RDF::Format.for(:rdfa) #=> RDF::Raptor::RDFa::Format 54 | # RDF::Format.for("input.html") 55 | # RDF::Format.for(file_name: "input.html") 56 | # RDF::Format.for(file_extension: "html") 57 | # RDF::Format.for(content_type: "application/xhtml+xml") 58 | # 59 | # {RDF::Raptor} includes an FFI implementation, which loads the 60 | # `libraptor2` library into the Ruby process, as well as a CLI 61 | # implementation, which drives the `rapper` command-line tool in a 62 | # sub-process. 63 | # 64 | # The FFI implementation is used by default unless the `libraptor2` library 65 | # cannot be found, or if the `RDF_RAPTOR_ENGINE` environment variable is 66 | # explicitly set to `'cli'`. 67 | # 68 | # If the `libraptor2` library is in the standard library search path, and 69 | # the `rapper` command is in the standard command search path, all should 70 | # be well and work fine out of the box. However, if either is in a 71 | # non-standard location, be sure to set the `RDF_RAPTOR_LIBPATH` and/or 72 | # `RDF_RAPTOR_BINPATH` environment variables appropriately before 73 | # requiring `rdf/raptor`. 74 | # 75 | # @see https://ruby-rdf.github.io/rdf/ 76 | # @see https://librdf.org/raptor/ 77 | # @see https://wiki.github.com/ffi/ffi/ 78 | # 79 | # @author [Arto Bendiken](https://github.com/artob) 80 | # @author [John Fieber](https://github.com/jfieber) 81 | module Raptor 82 | LIBRAPTOR = ENV['RDF_RAPTOR_LIBPATH'] || ['libraptor2', 'libraptor2.so.0'] unless const_defined?(:LIBRAPTOR) 83 | RAPPER = ENV['RDF_RAPTOR_BINPATH'] || 'rapper' unless const_defined?(:RAPPER) 84 | 85 | require 'rdf/raptor/version' 86 | begin 87 | # Try FFI implementation 88 | raise LoadError if ENV['RDF_RAPTOR_ENGINE'] == 'cli' # override 89 | require 'rdf/raptor/ffi' 90 | include RDF::Raptor::FFI 91 | extend RDF::Raptor::FFI 92 | rescue LoadError => e 93 | # CLI fallback 94 | require 'rdf/raptor/cli' 95 | include RDF::Raptor::CLI 96 | extend RDF::Raptor::CLI 97 | end 98 | 99 | ## 100 | # Returns `true` if the `rapper` binary is available. 101 | # 102 | # @example 103 | # RDF::Raptor.available? #=> true 104 | # 105 | # @return [Boolean] 106 | def self.available? 107 | !!version 108 | end 109 | 110 | require 'rdf/raptor/ntriples' 111 | require 'rdf/raptor/turtle' 112 | require 'rdf/raptor/rdfxml' 113 | require 'rdf/raptor/rdfa' 114 | require 'rdf/raptor/graphviz' 115 | end # Raptor 116 | end # RDF 117 | -------------------------------------------------------------------------------- /lib/rdf/raptor/cli.rb: -------------------------------------------------------------------------------- 1 | require 'tempfile' 2 | 3 | module RDF::Raptor 4 | ## 5 | # A command-line interface to Raptor's `rapper` utility. 6 | module CLI 7 | ENGINE = :cli 8 | 9 | ## 10 | # Returns the installed `rapper` version number, or `nil` if `rapper` is 11 | # not available. 12 | # 13 | # @example 14 | # RDF::Raptor.version #=> "1.4.21" 15 | # 16 | # @return [String] 17 | def version 18 | if `#{RAPPER} --version 2>/dev/null` =~ /^(\d+)\.(\d+)\.(\d+)/ 19 | [$1, $2, $3].join('.') 20 | end 21 | end 22 | 23 | ## 24 | # CLI reader implementation. 25 | class Reader < RDF::Reader 26 | ## 27 | # Initializes the CLI reader instance. 28 | # 29 | # @param [IO, File, RDF::URI, String] input 30 | # @param [String, #to_s] base_uri ("file:///dev/stdin") 31 | # @param [Hash{Symbol => Object}] options 32 | # any additional options (see `RDF::Reader#initialize`) 33 | # @yield [reader] `self` 34 | # @yieldparam [RDF::Reader] reader 35 | # @yieldreturn [void] ignored 36 | def initialize(input = $stdin, base_uri: nil, **options, &block) 37 | raise RDF::ReaderError, "`rapper` binary not found" unless RDF::Raptor.available? 38 | 39 | format = self.class.format.rapper_format 40 | case input 41 | when RDF::URI, %r(^(file|http|https|ftp)://) 42 | @command = "#{RAPPER} -q -i #{format} -o ntriples '#{input}'" 43 | @command << " '#{base_uri}'" if options.has_key?(:base_uri) 44 | @rapper = IO.popen(@command, 'rb') 45 | 46 | when File, Tempfile 47 | @command = "#{RAPPER} -q -i #{format} -o ntriples '#{File.expand_path(input.path)}'" 48 | @command << " '#{base_uri}'" if options.has_key?(:base_uri) 49 | @rapper = IO.popen(@command, 'rb') 50 | 51 | else # IO, String 52 | @command = "#{RAPPER} -q -i #{format} -o ntriples file:///dev/stdin" 53 | @command << " '#{base_uri}'" if options.has_key?(:base_uri) 54 | @rapper = IO.popen(@command, 'rb+') 55 | pid = fork do 56 | # process to feed `rapper` 57 | begin 58 | @rapper.close_read 59 | if input.respond_to?(:read) 60 | buf = String.new 61 | while input.read(8192, buf) 62 | @rapper.write(buf) 63 | end 64 | else 65 | @rapper.write(input.to_s) 66 | end 67 | @rapper.close_write 68 | ensure 69 | Process.exit 70 | end 71 | end 72 | Process.detach(pid) 73 | @rapper.close_write 74 | end 75 | 76 | @options = options 77 | @reader = RDF::NTriples::Reader.new(@rapper, @options).extend(Extensions) 78 | 79 | if block_given? 80 | case block.arity 81 | when 0 then instance_eval(&block) 82 | else block.call(self) 83 | end 84 | end 85 | end 86 | 87 | protected 88 | 89 | ## 90 | # @return [Array(RDF::Resource, RDF::URI, RDF::Term)] 91 | # @see RDF::Reader#read_triple 92 | def read_triple 93 | raise EOFError if @rapper.closed? 94 | begin 95 | triple = @reader.read_triple 96 | rescue EOFError 97 | @rapper.close 98 | raise 99 | end 100 | triple 101 | end 102 | 103 | ## 104 | # Extensions for `RDF::NTriples::Reader`. 105 | module Extensions 106 | NODEID = RDF::NTriples::Reader::NODEID 107 | GENID = /^genid\d+$/ 108 | 109 | ## 110 | # Generates fresh random identifiers for Raptor's `_:genid[0-9]+` 111 | # blank nodes, while preserving any user-specified blank node 112 | # identifiers verbatim. 113 | # 114 | # @private 115 | # @see RDF::NTriples::Reader#read_node 116 | # @see https://github.com/ruby-rdf/rdf-raptor/issues/#issue/9 117 | def read_node 118 | if node_id = match(NODEID) 119 | @nodes ||= {} 120 | @nodes[node_id] ||= RDF::Node.new(GENID === node_id ? nil : node_id) 121 | end 122 | end 123 | end 124 | end # Reader 125 | 126 | ## 127 | # CLI writer implementation. 128 | class Writer < RDF::Writer 129 | ## 130 | # Initializes the CLI writer instance. 131 | # 132 | # @param [IO, File] output 133 | # @param [Hash{Symbol => Object}] options 134 | # any additional options (see `RDF::Writer#initialize`) 135 | # @yield [writer] `self` 136 | # @yieldparam [RDF::Writer] writer 137 | # @yieldreturn [void] 138 | def initialize(output = $stdout, base_uri: nil, **options, &block) 139 | raise RDF::WriterError, "`rapper` binary not found" unless RDF::Raptor.available? 140 | 141 | format = self.class.format.rapper_format 142 | case output 143 | when File, IO, StringIO, Tempfile 144 | @command = "#{RAPPER} -q -i turtle -o #{format} file:///dev/stdin" 145 | @command << " '#{base_uri}'" if options.has_key?(:base_uri) 146 | @rapper = IO.popen(@command, 'rb+') 147 | else 148 | raise ArgumentError, "unsupported output type: #{output.inspect}" 149 | end 150 | @writer = RDF::NTriples::Writer.new(@rapper, **options) 151 | super(output, base_uri: base_uri, **options, &block) 152 | end 153 | 154 | protected 155 | 156 | ## 157 | # @return [void] 158 | # @see RDF::Writer#write_prologue 159 | def write_prologue 160 | super 161 | end 162 | 163 | ## 164 | # @param [RDF::Resource] subject 165 | # @param [RDF::URI] predicate 166 | # @param [RDF::Term] object 167 | # @return [void] 168 | # @see RDF::Writer#write_triple 169 | def write_triple(subject, predicate, object) 170 | output_transit(false) 171 | @writer.write_triple(subject, predicate, object) 172 | output_transit(false) 173 | end 174 | 175 | ## 176 | # @return [void] 177 | # @see RDF::Writer#write_epilogue 178 | def write_epilogue 179 | @rapper.close_write unless @rapper.closed? 180 | output_transit(true) 181 | end 182 | 183 | ## 184 | # Feeds any available `rapper` output to the destination. 185 | # 186 | # @param [Boolean] may_block 187 | # @return [void] 188 | def output_transit(may_block) 189 | unless @rapper.closed? 190 | chunk_size = @options[:chunk_size] || 4096 # bytes 191 | begin 192 | loop do 193 | @output.write(may_block ? @rapper.sysread(chunk_size) : @rapper.read_nonblock(chunk_size)) 194 | end 195 | rescue EOFError => e 196 | @rapper.close 197 | rescue Errno::EAGAIN, Errno::EINTR 198 | # eat 199 | end 200 | end 201 | end 202 | end # Writer 203 | end # CLI 204 | end # RDF::Raptor 205 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi.rb: -------------------------------------------------------------------------------- 1 | require 'tempfile' 2 | require 'ffi' # @see https://rubygems.org/gems/ffi 3 | 4 | module RDF::Raptor 5 | ## 6 | # A foreign-function interface (FFI) to `libraptor`. 7 | # 8 | # @see http://librdf.org/raptor/api/ 9 | # @see http://librdf.org/raptor/libraptor.html 10 | module FFI 11 | autoload :V1, 'rdf/raptor/ffi/v1' 12 | autoload :V2, 'rdf/raptor/ffi/v2' 13 | 14 | ENGINE = :ffi 15 | 16 | ## 17 | # Returns the installed `libraptor` version number, or `nil` if 18 | # `libraptor` is not available. 19 | # 20 | # @example 21 | # RDF::Raptor.version #=> "1.4.21" 22 | # 23 | # @return [String] an "x.y.z" version string 24 | def version 25 | V2.raptor_version_string.freeze 26 | end 27 | 28 | ## 29 | # FFI reader implementation. 30 | class Reader < RDF::Reader 31 | include RDF::Util::Logger 32 | 33 | ## 34 | # Initializes the FFI reader instance. 35 | # 36 | # @param [IO, File, RDF::URI, String] input 37 | # @param [Hash{Symbol => Object}] options 38 | # any additional options (see `RDF::Reader#initialize`) 39 | # @option options [String, #to_s] :base_uri ("file:///dev/stdin") 40 | # @yield [reader] `self` 41 | # @yieldparam [RDF::Reader] reader 42 | # @yieldreturn [void] ignored 43 | def initialize(input = $stdin, **options, &block) 44 | @format = self.class.format.rapper_format 45 | @parser = V2::Parser.new(@format) 46 | @parser.error_handler = ERROR_HANDLER 47 | @parser.warning_handler = WARNING_HANDLER 48 | 49 | @parser.namespace_handler = Proc.new do |user_data, raptor_namespace| 50 | namespace = V2::Namespace.new(raptor_namespace) 51 | prefix(namespace.prefix, namespace.uri) if namespace.prefix_length > 0 52 | end 53 | 54 | super 55 | end 56 | 57 | ERROR_HANDLER = Proc.new do |user_data, locator, message| 58 | line = V2.raptor_locator_line(locator) 59 | log_error(message, lineno: line) 60 | end 61 | 62 | WARNING_HANDLER = Proc.new do |user_data, locator, message| 63 | line = V2.raptor_locator_line(locator) 64 | log_warn(message, lineno: line) 65 | end 66 | 67 | ## 68 | # The Raptor parser instance. 69 | # 70 | # @return [V2::Parser] 71 | attr_reader :parser 72 | 73 | ## 74 | # @yield [statement] 75 | # @yieldparam [RDF::Statement] statement 76 | # @yieldreturn [void] ignored 77 | # @see RDF::Reader#each_statement 78 | def each_statement(**options, &block) 79 | if block_given? 80 | if options[:raw] 81 | # this is up to an order of magnitude faster... 82 | parse(@input) do |parser, statement| 83 | block.call(V2::Statement.new(statement, self)) 84 | end 85 | else 86 | parse(@input) do |parser, statement| 87 | block.call(V2::Statement.new(statement, self).to_rdf) 88 | end 89 | end 90 | 91 | if validate? && log_statistics[:error] 92 | raise RDF::ReaderError, "Errors found during processing" 93 | end 94 | end 95 | enum_for(:each_statement, **options) 96 | end 97 | alias_method :each, :each_statement 98 | 99 | ## 100 | # @yield [triple] 101 | # @yieldparam [Array(RDF::Resource, RDF::URI, RDF::Term)] triple 102 | # @yieldreturn [void] ignored 103 | # @see RDF::Reader#each_triple 104 | def each_triple(&block) 105 | if block_given? 106 | each_statement do |statement| 107 | block.call(*statement.to_triple) 108 | end 109 | end 110 | enum_for(:each_triple) 111 | end 112 | 113 | ## 114 | # @private 115 | # @param [RDF::URI, File, Tempfile, IO, StringIO] input 116 | # the input stream 117 | # @yield [parser, statement] 118 | # each statement in the input stream 119 | # @yieldparam [FFI::Pointer] parser 120 | # @yieldparam [FFI::Pointer] statement 121 | # @yieldreturn [void] ignored 122 | # @return [void] 123 | def parse(input, &block) 124 | @parser.parse(input, **@options, &block) 125 | end 126 | 127 | GENID = /^genid\d+$/ 128 | 129 | ## 130 | # @param [String] uri_str 131 | # @return [RDF::URI] 132 | def create_uri(uri_str) 133 | RDF::URI.intern(uri_str) 134 | end 135 | 136 | ## 137 | # @param [String] node_id 138 | # @return [RDF::Node] 139 | def create_node(node_id) 140 | @nodes ||= {} 141 | @nodes[node_id] ||= RDF::Node.new(GENID === node_id ? nil : node_id) 142 | end 143 | end # Reader 144 | 145 | ## 146 | # FFI writer implementation. 147 | class Writer < RDF::Writer 148 | include RDF::Util::Logger 149 | 150 | ## 151 | # Initializes the FFI writer instance. 152 | # 153 | # @param [IO, File] output 154 | # @param [Hash{Symbol => Object}] options 155 | # any additional options (see `RDF::Writer#initialize`) 156 | # @yield [writer] `self` 157 | # @yieldparam [RDF::Writer] writer 158 | # @yieldreturn [void] ignored 159 | def initialize(output = $stdout, **options, &block) 160 | @format = self.class.format.rapper_format 161 | @serializer = V2::Serializer.new(@format) 162 | @serializer.error_handler = ERROR_HANDLER 163 | @serializer.warning_handler = WARNING_HANDLER 164 | @serializer.start_to(output, **options) 165 | super 166 | end 167 | 168 | ERROR_HANDLER = Proc.new do |user_data, locator, message| 169 | log_error(message) 170 | end 171 | 172 | WARNING_HANDLER = Proc.new do |user_data, locator, message| 173 | log_warn(message) 174 | end 175 | 176 | ## 177 | # The Raptor serializer instance. 178 | # 179 | # @return [V2::Serializer] 180 | attr_reader :serializer 181 | 182 | def self.serialize(value) 183 | output = StringIO.new 184 | writer = new(output) 185 | case value 186 | when nil then nil 187 | when FalseClass then value.to_s 188 | when RDF::Statement 189 | writer.write_triple(statement.subject, statement.predicate, statement.object) 190 | when RDF::Term 191 | writer.write_term(value) 192 | else 193 | raise ArgumentError, "expected an RDF::Statement or RDF::Term, but got #{value.inspect}" 194 | end 195 | 196 | output.to_s 197 | end 198 | 199 | ## 200 | # @param [RDF::Resource] subject 201 | # @param [RDF::URI] predicate 202 | # @param [RDF::Term] object 203 | # @return [void] 204 | # @see RDF::Writer#write_triple 205 | def write_triple(subject, predicate, object) 206 | @serializer.serialize_triple(subject, predicate, object) 207 | end 208 | 209 | def write_term(value) 210 | raise NotImplementedError 211 | end 212 | 213 | ## 214 | # @return [void] 215 | # @see RDF::Writer#write_prologue 216 | def write_prologue 217 | @serializer.prefix(self.prefixes) 218 | super 219 | end 220 | 221 | ## 222 | # @return [void] 223 | # @see RDF::Writer#write_epilogue 224 | def write_epilogue 225 | @serializer.finish 226 | super 227 | end 228 | end # Writer 229 | 230 | ## 231 | # @private 232 | module LibC 233 | extend ::FFI::Library 234 | ffi_lib ::FFI::Library::LIBC 235 | attach_function :strlen, [:pointer], :size_t 236 | end # LibC 237 | end # FFI 238 | end # RDF::Raptor 239 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v1.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI 2 | ## 3 | # A foreign-function interface (FFI) to `libraptor` 1.4.x. 4 | # 5 | # @see https://librdf.org/raptor/libraptor.html 6 | module V1 7 | autoload :IOStream, 'rdf/raptor/ffi/v1/iostream' 8 | autoload :IOStreamHandler, 'rdf/raptor/ffi/v1/iostream_handler' 9 | autoload :Parser, 'rdf/raptor/ffi/v1/parser' 10 | autoload :Serializer, 'rdf/raptor/ffi/v1/serializer' 11 | autoload :Statement, 'rdf/raptor/ffi/v1/statement' 12 | autoload :URI, 'rdf/raptor/ffi/v1/uri' 13 | 14 | extend ::FFI::Library 15 | ffi_lib RDF::Raptor::LIBRAPTOR 16 | 17 | # TODO: Ideally this would be an enum, but the JRuby FFI (as of 18 | # version 1.4.0) has problems with enums as part of structs: 19 | # `Unknown field type: # (ArgumentError)` 20 | RAPTOR_IDENTIFIER_TYPE_UNKNOWN = 0 21 | RAPTOR_IDENTIFIER_TYPE_RESOURCE = 1 22 | RAPTOR_IDENTIFIER_TYPE_ANONYMOUS = 2 23 | RAPTOR_IDENTIFIER_TYPE_LITERAL = 5 24 | 25 | # @see https://librdf.org/raptor/api-1.4/tutorial-initialising-finishing.html 26 | attach_function :raptor_init, [], :void 27 | attach_function :raptor_finish, [], :void 28 | attach_function :raptor_alloc_memory, [:size_t], :pointer 29 | attach_function :raptor_calloc_memory, [:size_t, :size_t], :pointer 30 | attach_function :raptor_free_memory, [:pointer], :void 31 | 32 | # @see https://librdf.org/raptor/api-1.4/raptor-section-locator.html 33 | typedef :pointer, :raptor_locator 34 | attach_function :raptor_locator_line, [:raptor_locator], :int 35 | attach_function :raptor_locator_column, [:raptor_locator], :int 36 | attach_function :raptor_locator_byte, [:raptor_locator], :int 37 | 38 | # @see https://librdf.org/raptor/api-1.4/raptor-section-general.html 39 | attach_variable :raptor_version_major, :int 40 | attach_variable :raptor_version_minor, :int 41 | attach_variable :raptor_version_release, :int 42 | attach_variable :raptor_version_decimal, :int 43 | callback :raptor_message_handler, [:pointer, :raptor_locator, :string], :void 44 | 45 | # @see https://librdf.org/raptor/api-1.4/raptor-section-uri.html 46 | typedef :pointer, :raptor_uri 47 | attach_function :raptor_new_uri, [:string], :raptor_uri 48 | attach_function :raptor_uri_copy, [:raptor_uri], :raptor_uri 49 | attach_function :raptor_uri_equals, [:raptor_uri, :raptor_uri], :int 50 | attach_function :raptor_uri_as_string, [:raptor_uri], :string 51 | attach_function :raptor_uri_to_string, [:raptor_uri], :string 52 | attach_function :raptor_uri_print, [:raptor_uri, :pointer], :void 53 | attach_function :raptor_free_uri, [:raptor_uri], :void 54 | 55 | # @see https://librdf.org/raptor/api-1.4/raptor-section-triples.html 56 | typedef :int, :raptor_identifier_type 57 | typedef :pointer, :raptor_identifier 58 | typedef :pointer, :raptor_statement 59 | attach_function :raptor_statement_compare, [:raptor_statement, :raptor_statement], :int 60 | attach_function :raptor_print_statement, [:raptor_statement, :pointer], :void 61 | attach_function :raptor_print_statement_as_ntriples, [:pointer, :pointer], :void 62 | attach_function :raptor_statement_part_as_string, [:pointer, :raptor_identifier_type, :raptor_uri, :pointer], :string 63 | 64 | # @see https://librdf.org/raptor/api-1.4/raptor-section-parser.html 65 | callback :raptor_statement_handler, [:pointer, :raptor_statement], :void 66 | typedef :pointer, :raptor_parser 67 | typedef :string, :mime_type 68 | typedef :string, :buffer 69 | attach_function :raptor_new_parser, [:string], :raptor_parser 70 | attach_function :raptor_guess_parser_name, [:raptor_uri, :mime_type, :buffer, :size_t, :string], :string 71 | attach_function :raptor_set_error_handler, [:raptor_parser, :pointer, :raptor_message_handler], :void 72 | attach_function :raptor_set_warning_handler, [:raptor_parser, :pointer, :raptor_message_handler], :void 73 | attach_function :raptor_set_statement_handler, [:raptor_parser, :pointer, :raptor_statement_handler], :void 74 | attach_function :raptor_parse_file, [:raptor_parser, :raptor_uri, :raptor_uri], :int 75 | attach_function :raptor_parse_file_stream, [:raptor_parser, :pointer, :string, :raptor_uri], :int 76 | attach_function :raptor_parse_uri, [:raptor_parser, :raptor_uri, :raptor_uri], :int 77 | attach_function :raptor_start_parse, [:raptor_parser, :string], :int 78 | attach_function :raptor_parse_chunk, [:raptor_parser, :string, :size_t, :int], :int 79 | attach_function :raptor_get_mime_type, [:raptor_parser], :string 80 | attach_function :raptor_set_parser_strict, [:raptor_parser, :int], :void 81 | attach_function :raptor_get_need_base_uri, [:raptor_parser], :int 82 | attach_function :raptor_free_parser, [:raptor_parser], :void 83 | 84 | # @see https://librdf.org/raptor/api-1.4/raptor-section-iostream.html 85 | typedef :pointer, :raptor_iostream 86 | attach_function :raptor_new_iostream_from_handler2, [:pointer, :pointer], :raptor_iostream 87 | attach_function :raptor_free_iostream, [:raptor_iostream], :void 88 | callback :raptor_iostream_init_func, [:pointer], :int 89 | callback :raptor_iostream_finish_func, [:pointer], :void 90 | callback :raptor_iostream_write_byte_func, [:pointer, :int], :int 91 | callback :raptor_iostream_write_bytes_func, [:pointer, :pointer, :size_t, :size_t], :int 92 | callback :raptor_iostream_write_end_func, [:pointer], :void 93 | callback :raptor_iostream_read_bytes_func, [:pointer, :pointer, :size_t, :size_t], :int 94 | callback :raptor_iostream_read_eof_func, [:pointer], :int 95 | 96 | # @see https://librdf.org/raptor/api-1.4/raptor-section-xml-namespace.html 97 | typedef :pointer, :raptor_namespace 98 | 99 | # @see https://librdf.org/raptor/api-1.4/raptor-section-serializer.html 100 | typedef :pointer, :raptor_serializer 101 | attach_function :raptor_new_serializer, [:string], :raptor_serializer 102 | attach_function :raptor_free_serializer, [:raptor_serializer], :void 103 | attach_function :raptor_serialize_start_to_iostream, [:raptor_serializer, :raptor_uri, :raptor_iostream], :int 104 | attach_function :raptor_serialize_start_to_filename, [:raptor_serializer, :string], :int 105 | attach_function :raptor_serialize_statement, [:raptor_serializer, :raptor_statement], :int 106 | attach_function :raptor_serialize_end, [:raptor_serializer], :int 107 | attach_function :raptor_serializer_set_error_handler, [:raptor_serializer, :pointer, :raptor_message_handler], :void 108 | attach_function :raptor_serializer_set_warning_handler, [:raptor_serializer, :pointer, :raptor_message_handler], :void 109 | 110 | # Initialize the world. 111 | # We do this exactly once and never release because we can't delegate 112 | # any memory management to the Ruby GC. 113 | # Internally `raptor_init`/`raptor_finish` work with reference counts. 114 | raptor_init 115 | 116 | ## 117 | # Allocates memory for the string `str` inside `libraptor`, copying the 118 | # string into the newly-allocated buffer. 119 | # 120 | # The buffer should later be deallocated using `raptor_free_string`. 121 | # 122 | # @return [FFI::Pointer] 123 | def raptor_new_string(str) 124 | ptr = V1.raptor_alloc_memory(str.bytesize + 1) 125 | ptr.put_string(0, str) 126 | ptr 127 | end 128 | module_function :raptor_new_string 129 | 130 | alias_method :raptor_free_string, :raptor_free_memory 131 | module_function :raptor_free_string 132 | end # V1 133 | end # RDF::Raptor::FFI 134 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v1/iostream.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V1 2 | ## 3 | # This class provides an I/O stream that can write to filenames, `FILE*`, 4 | # strings and user-defined output via callbacks. 5 | # 6 | # @see https://librdf.org/raptor/api-1.4/raptor-section-iostream.html 7 | class IOStream < ::FFI::ManagedStruct 8 | include RDF::Raptor::FFI 9 | layout :user_data, :pointer # the actual layout is private 10 | 11 | ## 12 | # @overload initialize(ptr) 13 | # @param [FFI::Pointer] ptr 14 | # 15 | # @overload initialize(handler) 16 | # @param [V1::IOStreamHandler] handler 17 | # 18 | # @overload initialize(file) 19 | # @param [File, Tempfile] file 20 | # 21 | def initialize(ptr_or_obj, **options) 22 | ptr = case ptr_or_obj 23 | when FFI::Pointer 24 | ptr_or_obj 25 | when V1::IOStreamHandler 26 | @handler = ptr_or_obj # prevents premature GC 27 | V1.raptor_new_iostream_from_handler2(self, @handler) 28 | when File, Tempfile 29 | V1.raptor_new_iostream_to_filename(File.expand_path(ptr_or_obj.path)) 30 | when false 31 | V1.raptor_new_iostream_to_sink() 32 | else nil 33 | end 34 | 35 | @free_iostream = options[:free_iostream] || true 36 | 37 | raise ArgumentError, "invalid argument: #{ptr_or_obj.inspect}" if ptr.nil? || ptr.null? 38 | super(ptr) 39 | end 40 | 41 | ## 42 | # Releases `libraptor` memory associated with this structure. 43 | # 44 | # @param [FFI::Pointer] ptr 45 | # @return [void] 46 | def self.release(ptr) 47 | if @free_iostrem 48 | V1.raptor_free_iostream(ptr) 49 | end 50 | end 51 | end # IOStream 52 | end # RDF::Raptor::FFI::V1 53 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v1/iostream_handler.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V1 2 | ## 3 | # @see https://librdf.org/raptor/api-1.4/raptor-section-iostream.html 4 | class IOStreamHandler < ::FFI::Struct 5 | include RDF::Raptor::FFI 6 | layout :version, :int, 7 | :init, :raptor_iostream_init_func, 8 | :finish, :raptor_iostream_finish_func, 9 | :write_byte, :raptor_iostream_write_byte_func, 10 | :write_bytes, :raptor_iostream_write_bytes_func, 11 | :write_end, :raptor_iostream_write_end_func, 12 | :read_bytes, :raptor_iostream_read_bytes_func, 13 | :read_eof, :raptor_iostream_read_eof_func 14 | 15 | HANDLERS = [:init, :finish, :write_byte, :write_bytes, :read_bytes, :read_eof] 16 | 17 | ## 18 | # The IO object to operate upon. 19 | # 20 | # @return [IO] 21 | attr_accessor :io 22 | 23 | ## 24 | # @overload initialize(ptr) 25 | # @param [FFI::Pointer] ptr 26 | # 27 | # @overload initialize(io) 28 | # @param [IO, StringIO] io 29 | # 30 | def initialize(ptr_or_io = nil) 31 | if ptr_or_io.respond_to?(:write) 32 | @io = ptr_or_io 33 | super() 34 | else 35 | case ptr_or_io 36 | when FFI::Pointer 37 | super(ptr_or_io) 38 | when nil 39 | super() 40 | else 41 | raise ArgumentError, "invalid argument: #{ptr_or_io.inspect}" 42 | end 43 | end 44 | initialize! 45 | end 46 | 47 | ## 48 | # @return [void] 49 | def initialize! 50 | self[:version] = 2 51 | 52 | #define_handler(:init) do |context| 53 | # $stderr.puts("#{self.class}: init") 54 | #end 55 | #define_handler(:finish) do |context| 56 | # $stderr.puts("#{self.class}: finish") 57 | #end 58 | define_handler(:write_byte) do |context, byte| 59 | begin 60 | @io.putc(byte) 61 | 0 62 | rescue => e 63 | $stderr.puts("#{e} in #{self.class}#write_byte") 64 | 1 65 | end 66 | end 67 | define_handler(:write_bytes) do |context, data, size, nmemb| 68 | begin 69 | @io.write(data.read_string(size * nmemb)) 70 | 0 71 | rescue => e 72 | $stderr.puts("#{e} in #{self.class}#write_bytes") 73 | 1 74 | end 75 | end 76 | #define_handler(:write_end) do |context| 77 | # $stderr.puts("#{self.class}: write_end") 78 | #end 79 | #define_handler(:read_bytes) do |context, data, size, nmemb| 80 | # $stderr.puts("#{self.class}: read_bytes") 81 | #end 82 | #define_handler(:read_eof) do |context| 83 | # $stderr.puts("#{self.class}: read_eof") 84 | #end 85 | end 86 | 87 | ## 88 | # @param [Proc] func 89 | # @return [void] 90 | def init_handler=(func) 91 | define_handler(:init, &func) 92 | end 93 | alias_method :init=, :init_handler= 94 | 95 | ## 96 | # @param [Proc] func 97 | # @return [void] 98 | def finish_handler=(func) 99 | define_handler(:finish, &func) 100 | end 101 | alias_method :finish=, :finish_handler= 102 | 103 | ## 104 | # @param [Proc] func 105 | # @return [void] 106 | def write_byte_handler=(func) 107 | define_handler(:write_byte, &func) 108 | end 109 | alias_method :write_byte=, :write_byte_handler= 110 | 111 | ## 112 | # @param [Proc] func 113 | # @return [void] 114 | def write_bytes_handler=(func) 115 | define_handler(:write_bytes, &func) 116 | end 117 | alias_method :write_bytes=, :write_bytes_handler= 118 | 119 | ## 120 | # @param [Proc] func 121 | # @return [void] 122 | def write_end_handler=(func) 123 | define_handler(:write_end, &func) 124 | end 125 | alias_method :write_end=, :write_end_handler= 126 | 127 | ## 128 | # @param [Proc] func 129 | # @return [void] 130 | def read_bytes_handler=(func) 131 | define_handler(:read_bytes, &func) 132 | end 133 | alias_method :read_bytes=, :read_bytes_handler= 134 | 135 | ## 136 | # @param [Proc] func 137 | # @return [void] 138 | def read_eof_handler=(func) 139 | define_handler(:read_eof, &func) 140 | end 141 | alias_method :read_eof=, :read_eof_handler= 142 | 143 | ## 144 | # @param [Symbol, #to_sym] name 145 | # @return [void] 146 | def define_handler(name, &block) 147 | name = name.to_sym 148 | raise ArgumentError, "invalid IOStreamHandler function name: #{name}" unless HANDLERS.include?(name) 149 | @procs ||= {} # prevents premature GC of the procs 150 | @procs[name] = self[name] = block 151 | end 152 | end # IOStreamHandler 153 | end # RDF::Raptor::FFI::V1 154 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v1/parser.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V1 2 | ## 3 | # This class provides the functionality of turning syntaxes into RDF 4 | # triples - RDF parsing. 5 | # 6 | # @see https://librdf.org/raptor/api-1.4/raptor-section-parser.html 7 | class Parser < ::FFI::ManagedStruct 8 | include RDF::Raptor::FFI 9 | layout :world, :pointer # the actual layout is private 10 | 11 | # The default base URI 12 | BASE_URI = 'file:///dev/stdin' 13 | 14 | # The maximum chunk size for `#parse_stream` 15 | BUFFER_SIZE = 64 * 1024 16 | 17 | ## 18 | # @overload initialize(ptr) 19 | # @param [FFI::Pointer] ptr 20 | # 21 | # @overload initialize(name) 22 | # @param [Symbol, String] name 23 | # 24 | def initialize(ptr_or_name) 25 | ptr = case ptr_or_name 26 | when FFI::Pointer then ptr_or_name 27 | when Symbol then V1.raptor_new_parser(ptr_or_name.to_s) 28 | when String then V1.raptor_new_parser(ptr_or_name) 29 | else nil 30 | end 31 | raise ArgumentError, "invalid argument: #{ptr_or_name.inspect}" if ptr.nil? || ptr.null? 32 | super(ptr) 33 | end 34 | 35 | ## 36 | # Releases `libraptor` memory associated with this structure. 37 | # 38 | # @param [FFI::Pointer] ptr 39 | # @return [void] 40 | def self.release(ptr) 41 | V1.raptor_free_parser(ptr) 42 | end 43 | 44 | ## 45 | # @param [Proc] handler 46 | # @return [void] 47 | def error_handler=(handler) 48 | V1.raptor_set_error_handler(self, self, handler) 49 | end 50 | 51 | ## 52 | # @param [Proc] handler 53 | # @return [void] 54 | def warning_handler=(handler) 55 | V1.raptor_set_warning_handler(self, self, handler) 56 | end 57 | 58 | ## 59 | # @param [Proc] handler 60 | # @return [void] 61 | def statement_handler=(handler) 62 | V1.raptor_parser_set_statement_handler(self, self, handler) 63 | end 64 | 65 | ## 66 | # @param [Object] input 67 | # the input to parse 68 | # @param [Hash{Symbol => Object}] options 69 | # any additional options for parsing 70 | # @option options [String, #to_s] :base_uri (nil) 71 | # the base URI to use when resolving relative URIs 72 | # @yield [parser, statement] 73 | # each statement in the input 74 | # @yieldparam [FFI::Pointer] parser 75 | # @yieldparam [FFI::Pointer] statement 76 | # @yieldreturn [void] ignored 77 | # @return [void] 78 | def parse(input, **options, &block) 79 | case input 80 | when RDF::URI, %r(^(file|https|http|ftp)://) 81 | parse_url(input, **options, &block) 82 | when File, Tempfile 83 | parse_file(input, **options, &block) 84 | when IO, StringIO 85 | parse_stream(input, **options, &block) 86 | when String 87 | parse_buffer(input, **options, &block) 88 | else 89 | raise ArgumentError, "don't know how to parse #{input.inspect}" 90 | end 91 | end 92 | 93 | ## 94 | # @param [RDF::URI, String, #to_s] url 95 | # the input URL to parse 96 | # @param [Hash{Symbol => Object}] options 97 | # any additional options for parsing (see {#parse}) 98 | # @yield [parser, statement] 99 | # each statement in the input 100 | # @yieldparam [FFI::Pointer] parser 101 | # @yieldparam [FFI::Pointer] statement 102 | # @yieldreturn [void] ignored 103 | # @return [void] 104 | def parse_url(url, base_uri: nil, **options, &block) 105 | self.statement_handler = block if block_given? 106 | 107 | data_url = V1::URI.new((url.respond_to?(:to_uri) ? url.to_uri : url).to_s) 108 | base_uri = base_uri.to_s.empty? ? nil : V1::URI.new(base_uri.to_s) 109 | 110 | result = V1.raptor_parse_uri(self, data_url, base_uri) 111 | # TODO: error handling if result.nonzero? 112 | end 113 | alias_method :parse_uri, :parse_url 114 | 115 | ## 116 | # @param [File, Tempfile, #path] file 117 | # the input file to parse 118 | # @param [Hash{Symbol => Object}] options 119 | # any additional options for parsing (see {#parse}) 120 | # @yield [parser, statement] 121 | # each statement in the input 122 | # @yieldparam [FFI::Pointer] parser 123 | # @yieldparam [FFI::Pointer] statement 124 | # @yieldreturn [void] ignored 125 | # @return [void] 126 | def parse_file(file, base_uri: nil, **options, &block) 127 | self.statement_handler = block if block_given? 128 | 129 | data_url = V1::URI.new("file://#{File.expand_path(file.path)}") 130 | base_uri = base_uri.to_s.empty? ? nil : V1::URI.new(base_uri.to_s) 131 | 132 | result = V1.raptor_parse_file(self, data_url, base_uri) 133 | # TODO: error handling if result.nonzero? 134 | end 135 | 136 | ## 137 | # @param [IO, StringIO, #readpartial] stream 138 | # the input stream to parse 139 | # @param [Hash{Symbol => Object}] options 140 | # any additional options for parsing (see {#parse}) 141 | # @yield [parser, statement] 142 | # each statement in the input 143 | # @yieldparam [FFI::Pointer] parser 144 | # @yieldparam [FFI::Pointer] statement 145 | # @yieldreturn [void] ignored 146 | # @return [void] 147 | def parse_stream(stream, base_uri: nil, **options, &block) 148 | self.statement_handler = block if block_given? 149 | 150 | begin 151 | parse_start!((base_uri || BASE_URI).to_s) 152 | loop do 153 | parse_chunk(stream.sysread(BUFFER_SIZE)) 154 | end 155 | rescue EOFError => e 156 | parse_end! 157 | end 158 | end 159 | 160 | ## 161 | # @param [String, #to_str] buffer 162 | # the input buffer to parse 163 | # @param [Hash{Symbol => Object}] options 164 | # any additional options for parsing (see {#parse}) 165 | # @yield [parser, statement] 166 | # each statement in the input 167 | # @yieldparam [FFI::Pointer] parser 168 | # @yieldparam [FFI::Pointer] statement 169 | # @yieldreturn [void] ignored 170 | # @return [void] 171 | def parse_buffer(buffer, base_uri: nil, **options, &block) 172 | self.statement_handler = block if block_given? 173 | 174 | parse_start!((base_uri || BASE_URI).to_s) 175 | parse_chunk(buffer.to_str) 176 | parse_end! 177 | end 178 | 179 | ## 180 | # @private 181 | # @param [String] base_uri 182 | # @return [void] 183 | def parse_start!(base_uri = BASE_URI) 184 | result = V1.raptor_start_parse(self, base_uri) 185 | # TODO: error handling if result.nonzero? 186 | end 187 | 188 | ## 189 | # @private 190 | # @param [String] buffer 191 | # the input chunk to parse 192 | # @return [void] 193 | def parse_chunk(buffer) 194 | result = V1.raptor_parse_chunk(self, buffer, buffer.bytesize, 0) 195 | # TODO: error handling if result.nonzero? 196 | end 197 | 198 | ## 199 | # @private 200 | # @return [void] 201 | def parse_end! 202 | result = V1.raptor_parse_chunk(self, nil, 0, 1) # EOF 203 | end 204 | end # Parser 205 | end # RDF::Raptor::FFI::V1 206 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v1/serializer.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V1 2 | ## 3 | # This class provides the functionality of turning RDF triples into 4 | # syntaxes - RDF serializing. 5 | # 6 | # @see https://librdf.org/raptor/api-1.4/raptor-section-serializer.html 7 | class Serializer < ::FFI::ManagedStruct 8 | include RDF::Raptor::FFI 9 | layout :world, :pointer # the actual layout is private 10 | 11 | ## 12 | # @overload initialize(ptr) 13 | # @param [FFI::Pointer] ptr 14 | # 15 | # @overload initialize(name) 16 | # @param [Symbol, String] name 17 | # 18 | def initialize(ptr_or_name) 19 | ptr = case ptr_or_name 20 | when FFI::Pointer then ptr_or_name 21 | when Symbol then V1.raptor_new_serializer(ptr_or_name.to_s) 22 | when String then V1.raptor_new_serializer(ptr_or_name) 23 | else nil 24 | end 25 | raise ArgumentError, "invalid argument: #{ptr_or_name.inspect}" if ptr.nil? || ptr.null? 26 | super(ptr) 27 | end 28 | 29 | ## 30 | # Releases `libraptor` memory associated with this structure. 31 | # 32 | # @param [FFI::Pointer] ptr 33 | # @return [void] 34 | def self.release(ptr) 35 | V1.raptor_free_serializer(ptr) 36 | end 37 | 38 | ## 39 | # @param [Proc] handler 40 | # @return [void] 41 | def error_handler=(handler) 42 | V1.raptor_serializer_set_error_handler(self, self, handler) 43 | end 44 | 45 | ## 46 | # @param [Proc] handler 47 | # @return [void] 48 | def warning_handler=(handler) 49 | V1.raptor_serializer_set_warning_handler(self, self, handler) 50 | end 51 | 52 | ## 53 | # @param [Object] output 54 | # where output should be written to 55 | # @param [Hash{Symbol => Object}] options 56 | # any additional options for serializing 57 | # @option options [String, #to_s] :base_uri (nil) 58 | # the base URI to use when resolving relative URIs 59 | # @return [void] 60 | def start_to(output, **options) 61 | if output.respond_to?(:write) 62 | start_to_stream(output, **options) 63 | else 64 | raise ArgumentError, "don't know how to serialize to #{output.inspect}" 65 | end 66 | end 67 | 68 | ## 69 | # @param [IO, StringIO] stream 70 | # @param [Hash{Symbol => Object}] options 71 | # any additional options for serializing (see {#start_to}) 72 | # @return [void] 73 | def start_to_stream(stream, **options) 74 | iostream = V1::IOStream.new(V1::IOStreamHandler.new(stream), free_iostream: false) 75 | start_to_iostream(iostream, **options) 76 | end 77 | 78 | ## 79 | # @param [V1::IOStream] iostream 80 | # @param [Hash{Symbol => Object}] options 81 | # any additional options for serializing (see {#start_to}) 82 | # @return [void] 83 | def start_to_iostream(iostream, **options) 84 | @iostream = iostream # prevents premature GC 85 | @base_uri = options[:base_uri].to_s.empty? ? nil : V1::URI.new(options[:base_uri].to_s) 86 | if V1.raptor_serialize_start_to_iostream(self, @base_uri, @iostream).nonzero? 87 | raise RDF::WriterError, "raptor_serialize_start_to_iostream() failed" 88 | end 89 | end 90 | 91 | ## 92 | # @return [void] 93 | def finish 94 | if V1.raptor_serialize_end(self).nonzero? 95 | raise RDF::WriterError, "raptor_serialize_end() failed" 96 | end 97 | @iostream = @base_uri = nil # allows GC 98 | end 99 | 100 | ## 101 | # @param [RDF::Resource] subject 102 | # @param [RDF::URI] predicate 103 | # @param [RDF::Term] object 104 | # @return [void] 105 | def serialize_triple(subject, predicate, object) 106 | raptor_statement = V1::Statement.new 107 | raptor_statement.subject = subject 108 | raptor_statement.predicate = predicate 109 | raptor_statement.object = object 110 | begin 111 | serialize_raw_statement(raptor_statement) 112 | ensure 113 | raptor_statement.release 114 | raptor_statement = nil 115 | end 116 | end 117 | 118 | ## 119 | # @param [V1::Statement] statement 120 | # @return [void] 121 | def serialize_raw_statement(statement) 122 | if V1.raptor_serialize_statement(self, statement).nonzero? 123 | raise RDF::WriterError, "raptor_serialize_statement() failed" 124 | end 125 | end 126 | end # Serializer 127 | end # RDF::Raptor::FFI::V1 128 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v1/statement.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V1 2 | ## 3 | # @see https://librdf.org/raptor/api-1.4/raptor-section-triples.html 4 | class Statement < ::FFI::Struct 5 | include RDF::Raptor::FFI 6 | layout :subject, :pointer, 7 | :subject_type, :int, 8 | :predicate, :pointer, 9 | :predicate_type, :int, 10 | :object, :pointer, 11 | :object_type, :int, 12 | :object_literal_datatype, :pointer, 13 | :object_literal_language, :pointer 14 | 15 | ## 16 | # @param [FFI::Pointer] ptr 17 | # @param [#create_node] factory 18 | def initialize(ptr = nil, factory = nil) 19 | super(ptr) 20 | @factory = factory if factory 21 | end 22 | 23 | ## 24 | # Releases `libraptor` memory associated with this structure. 25 | # 26 | # @param [FFI::Pointer] ptr 27 | # @return [void] 28 | def self.release(ptr) 29 | raptor_free_memory(ptr) unless ptr.null? 30 | end 31 | 32 | # @return [Object] 33 | attr_accessor :id 34 | 35 | # @return [RDF::Resource] 36 | attr_accessor :graph_name 37 | 38 | ## 39 | # @return [RDF::Resource] 40 | def subject 41 | @subject ||= case self[:subject_type] 42 | when RAPTOR_IDENTIFIER_TYPE_ANONYMOUS 43 | @factory.create_node(self[:subject].read_string) 44 | when RAPTOR_IDENTIFIER_TYPE_RESOURCE 45 | @factory.create_uri(V1.raptor_uri_as_string(self[:subject])) 46 | end 47 | end 48 | 49 | ## 50 | # Sets the subject term from an `RDF::Resource`. 51 | # 52 | # @param [RDF::Resource] resource 53 | # @return [void] 54 | def subject=(resource) 55 | @subject = nil 56 | case resource 57 | when RDF::Node 58 | self[:subject_type] = RAPTOR_IDENTIFIER_TYPE_ANONYMOUS 59 | self[:subject] = V1.raptor_new_string(resource.id.to_s) 60 | when RDF::URI 61 | self[:subject_type] = RAPTOR_IDENTIFIER_TYPE_RESOURCE 62 | self[:subject] = V1.raptor_new_uri(resource.to_s) 63 | else 64 | raise ArgumentError, "subject term must be an RDF::Node or RDF::URI" 65 | end 66 | @subject = resource 67 | end 68 | 69 | ## 70 | # @return [String] 71 | def subject_as_string 72 | V1.raptor_statement_part_as_string( 73 | self[:subject], 74 | self[:subject_type], 75 | nil, nil) 76 | end 77 | 78 | ## 79 | # @return [RDF::URI] 80 | def predicate 81 | @predicate ||= case self[:predicate_type] 82 | when RAPTOR_IDENTIFIER_TYPE_RESOURCE 83 | RDF::URI.intern(V1.raptor_uri_as_string(self[:predicate])) 84 | end 85 | end 86 | 87 | ## 88 | # Sets the predicate term from an `RDF::URI`. 89 | # 90 | # @param [RDF::URI] uri 91 | # @return [void] 92 | def predicate=(uri) 93 | @predicate = nil 94 | raise ArgumentError, "predicate term must be an RDF::URI" unless uri.is_a?(RDF::URI) 95 | self[:predicate_type] = RAPTOR_IDENTIFIER_TYPE_RESOURCE 96 | self[:predicate] = V1.raptor_new_uri(uri.to_s) 97 | @predicate = uri 98 | end 99 | 100 | ## 101 | # @return [String] 102 | def predicate_as_string 103 | V1.raptor_statement_part_as_string( 104 | self[:predicate], 105 | self[:predicate_type], 106 | nil, nil) 107 | end 108 | 109 | ## 110 | # @return [RDF::Term] 111 | def object 112 | @object ||= case self[:object_type] 113 | when RAPTOR_IDENTIFIER_TYPE_ANONYMOUS 114 | @factory.create_node(self[:object].read_string) 115 | when RAPTOR_IDENTIFIER_TYPE_RESOURCE 116 | @factory.create_uri(V1.raptor_uri_as_string(self[:object])) 117 | when RAPTOR_IDENTIFIER_TYPE_LITERAL 118 | str = self[:object].read_string.unpack('U*').pack('U*') 119 | case 120 | when !self[:object_literal_language].null? 121 | RDF::Literal.new(str, language: self[:object_literal_language].read_string) 122 | when !self[:object_literal_datatype].null? 123 | RDF::Literal.new(str, datatype: V1.raptor_uri_as_string(self[:object_literal_datatype])) 124 | else 125 | RDF::Literal.new(str) 126 | end 127 | end 128 | end 129 | 130 | ## 131 | # Sets the object term from an `RDF::Term`. 132 | # 133 | # The value must be one of `RDF::Resource` or `RDF::Literal`. 134 | # 135 | # @param [RDF::Term] value 136 | # @return [void] 137 | def object=(value) 138 | @object = nil 139 | case value 140 | when RDF::Node 141 | self[:object_type] = RAPTOR_IDENTIFIER_TYPE_ANONYMOUS 142 | self[:object] = V1.raptor_new_string(value.id.to_s) 143 | when RDF::URI 144 | self[:object_type] = RAPTOR_IDENTIFIER_TYPE_RESOURCE 145 | self[:object] = V1.raptor_new_uri(value.to_s) 146 | when RDF::Literal 147 | self[:object_type] = RAPTOR_IDENTIFIER_TYPE_LITERAL 148 | self[:object] = V1.raptor_new_string(value.value) 149 | self[:object_literal_datatype] = V1.raptor_new_uri(value.datatype.to_s) if value.datatype 150 | self[:object_literal_language] = V1.raptor_new_string(value.language.to_s) if value.language? 151 | else 152 | raise ArgumentError, "object term must be an RDF::Node, RDF::URI, or RDF::Literal" 153 | end 154 | @object = value 155 | end 156 | 157 | ## 158 | # @return [String] 159 | def object_as_string 160 | V1.raptor_statement_part_as_string( 161 | self[:object], 162 | self[:object_type], 163 | self[:object_literal_datatype], 164 | self[:object_literal_language]) 165 | end 166 | 167 | ## 168 | # @return [Array(RDF::Resource, RDF::URI, RDF::Term)] 169 | # @see RDF::Statement#to_triple 170 | def to_triple 171 | [subject, predicate, object] 172 | end 173 | 174 | ## 175 | # @return [Array(RDF::Resource, RDF::URI, RDF::Term, nil)] 176 | # @see RDF::Statement#to_quad 177 | def to_quad 178 | [subject, predicate, object, graph_name] 179 | end 180 | 181 | ## 182 | # @return [RDF::Statement] 183 | def to_rdf 184 | RDF::Statement.new(subject, predicate, object, graph_name: graph_name) 185 | end 186 | 187 | ## 188 | # @return [void] 189 | def reset! 190 | @subject = @predicate = @object = @graph_name = nil 191 | end 192 | 193 | ## 194 | # Releases `libraptor` memory associated with this structure. 195 | # 196 | # @return [void] 197 | def free 198 | if self[:subject_type].nonzero? && !(self[:subject].null?) 199 | self[:subject] = case self[:subject_type] 200 | when RAPTOR_IDENTIFIER_TYPE_ANONYMOUS 201 | V1.raptor_free_string(self[:subject]) 202 | when RAPTOR_IDENTIFIER_TYPE_RESOURCE 203 | V1.raptor_free_uri(self[:subject]) 204 | end 205 | self[:subject_type] = RAPTOR_IDENTIFIER_TYPE_UNKNOWN 206 | end 207 | 208 | if self[:predicate_type].nonzero? && !(self[:predicate].null?) 209 | self[:predicate] = V1.raptor_free_uri(self[:predicate]) 210 | self[:predicate_type] = RAPTOR_IDENTIFIER_TYPE_UNKNOWN 211 | end 212 | 213 | if self[:object_type].nonzero? && !(self[:object].null?) 214 | self[:object] = case self[:object_type] 215 | when RAPTOR_IDENTIFIER_TYPE_ANONYMOUS 216 | V1.raptor_free_string(self[:object]) 217 | when RAPTOR_IDENTIFIER_TYPE_RESOURCE 218 | V1.raptor_free_uri(self[:object]) 219 | when RAPTOR_IDENTIFIER_TYPE_LITERAL 220 | V1.raptor_free_string(self[:object]) 221 | unless self[:object_literal_datatype].null? 222 | self[:object_literal_datatype] = V1.raptor_free_uri(self[:object_literal_datatype]) 223 | end 224 | unless self[:object_literal_language].null? 225 | self[:object_literal_language] = V1.raptor_free_string(self[:object_literal_language]) 226 | end 227 | end 228 | self[:object_type] = RAPTOR_IDENTIFIER_TYPE_UNKNOWN 229 | end 230 | end 231 | alias_method :release, :free 232 | end # Statement 233 | end # RDF::Raptor::FFI::V1 234 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v1/uri.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V1 2 | ## 3 | # Raptor has a `raptor_uri` class which must be used for manipulating and 4 | # passing URI references. The default internal implementation uses `char*` 5 | # strings for URIs, manipulating them and constructing them. 6 | # 7 | # @see https://librdf.org/raptor/api-1.4/raptor-section-uri.html 8 | class URI < ::FFI::ManagedStruct 9 | include RDF::Raptor::FFI 10 | include RDF::Resource 11 | layout :string, [:char, 1] # a safe dummy layout, since it is \0-terminated 12 | 13 | ## 14 | # @overload initialize(ptr) 15 | # @param [FFI::Pointer] ptr 16 | # 17 | # @overload initialize(name) 18 | # @param [RDF::URI, String] name 19 | # 20 | def initialize(ptr_or_name) 21 | ptr = case ptr_or_name 22 | when FFI::Pointer then ptr_or_name 23 | when RDF::URI then V1.raptor_new_uri(ptr_or_name.to_s) 24 | when String then V1.raptor_new_uri(ptr_or_name) 25 | else nil 26 | end 27 | raise ArgumentError, "invalid argument: #{ptr_or_name.inspect}" if ptr.nil? || ptr.null? 28 | super(ptr) 29 | end 30 | 31 | ## 32 | # Releases `libraptor` memory associated with this structure. 33 | # 34 | # @param [FFI::Pointer] ptr 35 | # @return [void] 36 | def self.release(ptr) 37 | V1.raptor_free_uri(ptr) 38 | end 39 | 40 | ## 41 | # @return [Boolean] `true` 42 | def uri? 43 | true 44 | end 45 | 46 | ## 47 | # @return [URI] a copy of `self` 48 | def dup 49 | copy = self.class.new(V1.raptor_uri_copy(self)) 50 | copy.taint if tainted? 51 | copy 52 | end 53 | 54 | ## 55 | # @return [URI] a copy of `self` 56 | def clone 57 | copy = self.class.new(V1.raptor_uri_copy(self)) 58 | copy.taint if tainted? 59 | copy.freeze if frozen? 60 | copy 61 | end 62 | 63 | ## 64 | # @return [Integer] 65 | def length 66 | LibC.strlen(self) 67 | end 68 | alias_method :size, :length 69 | 70 | ## 71 | # @return [Boolean] `true` or `false` 72 | def ==(other) 73 | return true if self.equal?(other) 74 | case other 75 | when self.class 76 | !(V1.raptor_uri_equals(self, other).zero?) 77 | when RDF::URI, String 78 | to_str == other.to_str 79 | else false 80 | end 81 | end 82 | alias_method :===, :== 83 | 84 | ## 85 | # @return [Boolean] `true` or `false` 86 | def eql?(other) 87 | return true if self.equal?(other) 88 | other.is_a?(self.class) && !(V1.raptor_uri_equals(self, other).zero?) 89 | end 90 | 91 | ## 92 | # @return [Fixnum] 93 | def hash 94 | to_str.hash 95 | end 96 | 97 | ## 98 | # @return [String] the URI string 99 | def to_str 100 | V1.raptor_uri_as_string(self) 101 | end 102 | alias_method :to_s, :to_str 103 | 104 | ## 105 | # @return [RDF::URI] 106 | def to_rdf 107 | RDF::URI.intern(to_str) 108 | end 109 | alias_method :to_uri, :to_rdf 110 | end # URI 111 | end # RDF::Raptor::FFI::V1 112 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v2.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI 2 | ## 3 | # A foreign-function interface (FFI) to `libraptor` 2.x. 4 | # 5 | # @see https://librdf.org/raptor/libraptor.html 6 | module V2 7 | autoload :IOStream, 'rdf/raptor/ffi/v2/iostream' 8 | autoload :IOStreamHandler, 'rdf/raptor/ffi/v2/iostream_handler' 9 | autoload :Parser, 'rdf/raptor/ffi/v2/parser' 10 | autoload :Serializer, 'rdf/raptor/ffi/v2/serializer' 11 | autoload :Statement, 'rdf/raptor/ffi/v2/statement' 12 | autoload :URI, 'rdf/raptor/ffi/v2/uri' 13 | autoload :Term, 'rdf/raptor/ffi/v2/term' 14 | autoload :Namespace, 'rdf/raptor/ffi/v2/namespace' 15 | autoload :World, 'rdf/raptor/ffi/v2/world' 16 | 17 | extend ::FFI::Library 18 | ffi_lib RDF::Raptor::LIBRAPTOR 19 | 20 | # TODO: Ideally this would be an enum, but the JRuby FFI (as of 21 | # version 1.4.0) has problems with enums as part of structs: 22 | # `Unknown field type: # (ArgumentError)` 23 | RAPTOR_TERM_TYPE_UNKNOWN = 0 24 | RAPTOR_TERM_TYPE_URI = 1 25 | RAPTOR_TERM_TYPE_LITERAL = 2 26 | RAPTOR_TERM_TYPE_BLANK = 4 27 | 28 | # @see https://librdf.org/raptor/api/tutorial-initialising-finishing.html 29 | typedef :pointer, :raptor_world 30 | typedef :int, :raptor_version 31 | typedef :pointer, :raptor_iostream 32 | attach_function :raptor_new_world_internal, [:raptor_version], :raptor_world 33 | attach_function :raptor_free_world, [:raptor_world], :void 34 | attach_function :raptor_alloc_memory, [:size_t], :pointer 35 | attach_function :raptor_calloc_memory, [:size_t, :size_t], :pointer 36 | attach_function :raptor_free_memory, [:pointer], :void 37 | 38 | # @see https://librdf.org/raptor/api-1.4/raptor-section-locator.html 39 | typedef :pointer, :raptor_locator 40 | attach_function :raptor_locator_line, [:raptor_locator], :int 41 | attach_function :raptor_locator_column, [:raptor_locator], :int 42 | attach_function :raptor_locator_byte, [:raptor_locator], :int 43 | 44 | # @see https://librdf.org/raptor/api/raptor2-section-general.html 45 | attach_variable :raptor_version_string, :string 46 | attach_variable :raptor_version_major, :int 47 | attach_variable :raptor_version_minor, :int 48 | attach_variable :raptor_version_release, :int 49 | attach_variable :raptor_version_decimal, :int 50 | callback :raptor_message_handler, [:pointer, :raptor_locator, :string], :void 51 | 52 | # @see https://librdf.org/raptor/api-1.4/raptor-section-uri.html 53 | typedef :pointer, :raptor_uri 54 | attach_function :raptor_new_uri, [:raptor_world, :string], :raptor_uri 55 | attach_function :raptor_uri_copy, [:raptor_uri], :raptor_uri 56 | attach_function :raptor_uri_equals, [:raptor_uri, :raptor_uri], :int 57 | attach_function :raptor_uri_as_string, [:raptor_uri], :string 58 | attach_function :raptor_uri_to_string, [:raptor_uri], :string 59 | attach_function :raptor_uri_print, [:raptor_uri, :pointer], :void 60 | attach_function :raptor_free_uri, [:raptor_uri], :void 61 | 62 | # @see https://librdf.org/raptor/api/raptor2-section-triples.html 63 | typedef :int, :raptor_identifier_type 64 | typedef :pointer, :raptor_identifier 65 | typedef :pointer, :raptor_statement 66 | attach_function :raptor_statement_compare, [:raptor_statement, :raptor_statement], :int 67 | attach_function :raptor_statement_print, [:raptor_statement, :pointer], :void 68 | attach_function :raptor_statement_print_as_ntriples, [:pointer, :pointer], :void 69 | #attach_function :raptor_statement_part_as_string, [:pointer, :raptor_identifier_type, :raptor_uri, :pointer], :string 70 | attach_function :raptor_free_statement, [:raptor_statement], :void 71 | typedef :pointer, :raptor_term 72 | typedef :string, :literal 73 | typedef :pointer, :datatype 74 | typedef :string, :language 75 | typedef :string, :blank 76 | attach_function :raptor_new_term_from_uri, [:raptor_world, :raptor_uri], :raptor_term 77 | attach_function :raptor_new_term_from_uri_string, [:raptor_world, :string], :raptor_term 78 | attach_function :raptor_new_term_from_literal, [:raptor_world, :literal, :datatype, :language], :raptor_term 79 | attach_function :raptor_new_term_from_blank, [:raptor_world, :blank], :raptor_term 80 | attach_function :raptor_free_term, [:raptor_term], :void 81 | attach_function :raptor_term_ntriples_write, [:raptor_term, :raptor_iostream], :int 82 | 83 | # @see https://librdf.org/raptor/api/raptor2-section-xml-namespace.html 84 | typedef :pointer, :raptor_namespace 85 | attach_function :raptor_free_namespace, [:raptor_namespace], :void 86 | 87 | # @see https://librdf.org/raptor/api/raptor2-section-parser.html 88 | callback :raptor_statement_handler, [:pointer, :raptor_statement], :void 89 | callback :raptor_namespace_handler, [:pointer, :raptor_namespace], :void 90 | typedef :pointer, :raptor_parser 91 | typedef :string, :mime_type 92 | typedef :string, :buffer 93 | attach_function :raptor_new_parser, [:raptor_world, :string], :raptor_parser 94 | attach_function :raptor_world_guess_parser_name, [:raptor_world, :raptor_uri, :mime_type, :buffer, :size_t, :string], :string 95 | attach_function :raptor_parser_set_namespace_handler, [:raptor_parser, :pointer, :raptor_namespace_handler], :void 96 | #attach_function :raptor_set_error_handler, [:raptor_parser, :pointer, :raptor_message_handler], :void 97 | #attach_function :raptor_set_warning_handler, [:raptor_parser, :pointer, :raptor_message_handler], :void 98 | attach_function :raptor_parser_set_statement_handler, [:raptor_parser, :pointer, :raptor_statement_handler], :void 99 | attach_function :raptor_parser_parse_file, [:raptor_parser, :raptor_uri, :raptor_uri], :int 100 | attach_function :raptor_parser_parse_file_stream, [:raptor_parser, :pointer, :string, :raptor_uri], :int 101 | attach_function :raptor_parser_parse_uri, [:raptor_parser, :raptor_uri, :raptor_uri], :int 102 | attach_function :raptor_parser_parse_start, [:raptor_parser, :raptor_uri], :int 103 | attach_function :raptor_parser_parse_chunk, [:raptor_parser, :string, :size_t, :int], :int 104 | #attach_function :raptor_get_mime_type, [:raptor_parser], :string 105 | #attach_function :raptor_set_parser_strict, [:raptor_parser, :int], :void 106 | #attach_function :raptor_get_need_base_uri, [:raptor_parser], :int 107 | attach_function :raptor_parser_parse_abort, [], :void 108 | attach_function :raptor_free_parser, [:raptor_parser], :void 109 | 110 | # @see https://librdf.org/raptor/api/raptor2-section-iostream.html 111 | attach_function :raptor_new_iostream_from_handler, [:raptor_world, :pointer, :pointer], :raptor_iostream 112 | attach_function :raptor_new_iostream_to_filename, [:raptor_world, :string], :raptor_iostream 113 | attach_function :raptor_new_iostream_to_sink, [:raptor_world], :raptor_iostream 114 | attach_function :raptor_free_iostream, [:raptor_iostream], :void 115 | callback :raptor_iostream_init_func, [:pointer], :int 116 | callback :raptor_iostream_finish_func, [:pointer], :void 117 | callback :raptor_iostream_write_byte_func, [:pointer, :int], :int 118 | callback :raptor_iostream_write_bytes_func, [:pointer, :pointer, :size_t, :size_t], :int 119 | callback :raptor_iostream_write_end_func, [:pointer], :void 120 | callback :raptor_iostream_read_bytes_func, [:pointer, :pointer, :size_t, :size_t], :int 121 | callback :raptor_iostream_read_eof_func, [:pointer], :int 122 | 123 | # @see https://librdf.org/raptor/api/raptor2-section-serializer.html 124 | typedef :pointer, :raptor_serializer 125 | typedef :string, :prefix 126 | attach_function :raptor_new_serializer, [:raptor_world, :string], :raptor_serializer 127 | attach_function :raptor_free_serializer, [:raptor_serializer], :void 128 | attach_function :raptor_serializer_start_to_iostream, [:raptor_serializer, :raptor_uri, :raptor_iostream], :int 129 | attach_function :raptor_serializer_start_to_filename, [:raptor_serializer, :string], :int 130 | attach_function :raptor_serializer_serialize_statement, [:raptor_serializer, :raptor_statement], :int 131 | attach_function :raptor_serializer_serialize_end, [:raptor_serializer], :int 132 | attach_function :raptor_serializer_set_namespace, [:raptor_serializer, :raptor_uri, :prefix], :int 133 | #attach_function :raptor_serializer_set_error_handler, [:raptor_serializer, :pointer, :raptor_message_handler], :void 134 | #attach_function :raptor_serializer_set_warning_handler, [:raptor_serializer, :pointer, :raptor_message_handler], :void 135 | 136 | # Initialize the world. 137 | # We do this exactly once and never release because we can't delegate 138 | # any memory management to the Ruby GC. 139 | # Internally `raptor_init`/`raptor_finish` work with reference counts. 140 | @world = nil 141 | def self.world 142 | @world ||= World.new 143 | end 144 | 145 | ## 146 | # Allocates memory for the string `str` inside `libraptor`, copying the 147 | # string into the newly-allocated buffer. 148 | # 149 | # The buffer should later be deallocated using `raptor_free_string`. 150 | # 151 | # @return [FFI::Pointer] 152 | def raptor_new_string(str) 153 | ptr = V2.raptor_alloc_memory(str.bytesize + 1) 154 | ptr.put_string(0, str) 155 | ptr 156 | end 157 | module_function :raptor_new_string 158 | 159 | alias_method :raptor_free_string, :raptor_free_memory 160 | module_function :raptor_free_string 161 | 162 | end # v2 163 | end # RDF::Raptor::FFI 164 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v2/iostream.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V2 2 | ## 3 | # This class provides an I/O stream that can write to filenames, `FILE*`, 4 | # strings and user-defined output via callbacks. 5 | # 6 | # @see https://librdf.org/raptor/api-1.4/raptor-section-iostream.html 7 | class IOStream < ::FFI::ManagedStruct 8 | include RDF::Raptor::FFI 9 | layout :world, :pointer, 10 | :user_data, :pointer, 11 | :handler, :pointer, 12 | :offset, :size_t, 13 | :mode, :int, 14 | :flags, :int 15 | 16 | ## 17 | # @overload initialize(ptr) 18 | # @param [FFI::Pointer] ptr 19 | # 20 | # @overload initialize(handler) 21 | # @param [V2::IOStreamHandler] handler 22 | # 23 | # @overload initialize(file) 24 | # @param [File, Tempfile] file 25 | # 26 | def initialize(ptr_or_obj, **options) 27 | ptr = case ptr_or_obj 28 | when FFI::Pointer 29 | ptr_or_obj 30 | when V2::IOStreamHandler 31 | @handler = ptr_or_obj # prevents premature GC 32 | V2.raptor_new_iostream_from_handler(V2.world, self, @handler) 33 | when File, Tempfile 34 | V2.raptor_new_iostream_to_filename(V2.world, File.expand_path(ptr_or_obj.path)) 35 | when false 36 | V2.raptor_new_iostream_to_sink(V2.world) 37 | else nil 38 | end 39 | raise ArgumentError, "invalid argument: #{ptr_or_obj.inspect}" if ptr.nil? || ptr.null? 40 | 41 | @free_iostream = options[:free_iostream] || true 42 | super(ptr) 43 | end 44 | 45 | ## 46 | # Releases `libraptor` memory associated with this structure. 47 | # 48 | # @param [FFI::Pointer] ptr 49 | # @return [void] 50 | def self.release(ptr) 51 | if @free_iostream 52 | V2.raptor_free_iostream(ptr) 53 | end 54 | end 55 | end # IOStream 56 | end # RDF::Raptor::FFI::V2 57 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v2/iostream_handler.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V2 2 | ## 3 | # @see https://librdf.org/raptor/api-1.4/raptor-section-iostream.html 4 | class IOStreamHandler < ::FFI::Struct 5 | include RDF::Raptor::FFI 6 | layout :version, :int, 7 | :init, :raptor_iostream_init_func, 8 | :finish, :raptor_iostream_finish_func, 9 | :write_byte, :raptor_iostream_write_byte_func, 10 | :write_bytes, :raptor_iostream_write_bytes_func, 11 | :write_end, :raptor_iostream_write_end_func, 12 | :read_bytes, :raptor_iostream_read_bytes_func, 13 | :read_eof, :raptor_iostream_read_eof_func 14 | 15 | HANDLERS = [:init, :finish, :write_byte, :write_bytes, :read_bytes, :read_eof] 16 | 17 | ## 18 | # The IO object to operate upon. 19 | # 20 | # @return [IO] 21 | attr_accessor :io 22 | 23 | ## 24 | # @overload initialize(ptr) 25 | # @param [FFI::Pointer] ptr 26 | # 27 | # @overload initialize(io) 28 | # @param [IO, StringIO] io 29 | # 30 | def initialize(ptr_or_io = nil) 31 | if ptr_or_io.respond_to?(:write) 32 | @io = ptr_or_io 33 | super() 34 | else 35 | case ptr_or_io 36 | when FFI::Pointer 37 | super(ptr_or_io) 38 | when nil 39 | super() 40 | else 41 | raise ArgumentError, "invalid argument: #{ptr_or_io.inspect}" 42 | end 43 | end 44 | initialize! 45 | end 46 | 47 | ## 48 | # @return [void] 49 | def initialize! 50 | self[:version] = 2 51 | 52 | #define_handler(:init) do |context| 53 | # $stderr.puts("#{self.class}: init") 54 | #end 55 | #define_handler(:finish) do |context| 56 | # $stderr.puts("#{self.class}: finish") 57 | #end 58 | define_handler(:write_byte) do |context, byte| 59 | begin 60 | @io.putc(byte) 61 | 0 62 | rescue => e 63 | $stderr.puts("#{e} in #{self.class}#write_byte") 64 | 1 65 | end 66 | end 67 | define_handler(:write_bytes) do |context, data, size, nmemb| 68 | begin 69 | @io.write(data.read_string(size * nmemb)) 70 | 0 71 | rescue => e 72 | $stderr.puts("#{e} in #{self.class}#write_bytes") 73 | 1 74 | end 75 | end 76 | #define_handler(:write_end) do |context| 77 | # $stderr.puts("#{self.class}: write_end") 78 | #end 79 | #define_handler(:read_bytes) do |context, data, size, nmemb| 80 | # $stderr.puts("#{self.class}: read_bytes") 81 | #end 82 | #define_handler(:read_eof) do |context| 83 | # $stderr.puts("#{self.class}: read_eof") 84 | #end 85 | end 86 | 87 | ## 88 | # @param [Proc] func 89 | # @return [void] 90 | def init_handler=(func) 91 | define_handler(:init, &func) 92 | end 93 | alias_method :init=, :init_handler= 94 | 95 | ## 96 | # @param [Proc] func 97 | # @return [void] 98 | def finish_handler=(func) 99 | define_handler(:finish, &func) 100 | end 101 | alias_method :finish=, :finish_handler= 102 | 103 | ## 104 | # @param [Proc] func 105 | # @return [void] 106 | def write_byte_handler=(func) 107 | define_handler(:write_byte, &func) 108 | end 109 | alias_method :write_byte=, :write_byte_handler= 110 | 111 | ## 112 | # @param [Proc] func 113 | # @return [void] 114 | def write_bytes_handler=(func) 115 | define_handler(:write_bytes, &func) 116 | end 117 | alias_method :write_bytes=, :write_bytes_handler= 118 | 119 | ## 120 | # @param [Proc] func 121 | # @return [void] 122 | def write_end_handler=(func) 123 | define_handler(:write_end, &func) 124 | end 125 | alias_method :write_end=, :write_end_handler= 126 | 127 | ## 128 | # @param [Proc] func 129 | # @return [void] 130 | def read_bytes_handler=(func) 131 | define_handler(:read_bytes, &func) 132 | end 133 | alias_method :read_bytes=, :read_bytes_handler= 134 | 135 | ## 136 | # @param [Proc] func 137 | # @return [void] 138 | def read_eof_handler=(func) 139 | define_handler(:read_eof, &func) 140 | end 141 | alias_method :read_eof=, :read_eof_handler= 142 | 143 | ## 144 | # @param [Symbol, #to_sym] name 145 | # @return [void] 146 | def define_handler(name, &block) 147 | name = name.to_sym 148 | raise ArgumentError, "invalid IOStreamHandler function name: #{name}" unless HANDLERS.include?(name) 149 | @procs ||= {} # prevents premature GC of the procs 150 | @procs[name] = self[name] = block 151 | end 152 | end # IOStreamHandler 153 | end # RDF::Raptor::FFI::V1 154 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v2/namespace.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V2 2 | ## 3 | # @see https://librdf.org/raptor/api/raptor2-section-xml-namespace.html 4 | class Namespace < ::FFI::Struct 5 | include RDF::Raptor::FFI 6 | # @see https://github.com/dajobe/raptor/blob/f4b2597d4279dcb283bf5c32e5435696fd28a8ec/src/raptor_internal.h#L428 7 | layout :next, :pointer, 8 | :nstack, :pointer, 9 | :prefix, :string, 10 | :prefix_length, :int, 11 | :uri, :pointer, 12 | :depth, :int, 13 | :is_xml, :int, 14 | :is_rdf_ms, :int, 15 | :is_rdf_schema, :int 16 | 17 | def prefix 18 | self[:prefix].to_s 19 | end 20 | 21 | def prefix_length 22 | self[:prefix_length] 23 | end 24 | 25 | def uri 26 | RDF::URI.new(V2.raptor_uri_as_string(self[:uri])) 27 | end 28 | 29 | ## 30 | # Releases `libraptor` memory associated with this structure. 31 | # 32 | # @return [void] 33 | def free 34 | V2.raptor_free_namespace(self) unless ptr.null? 35 | end 36 | alias_method :release, :free 37 | end 38 | end -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v2/parser.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V2 2 | ## 3 | # This class provides the functionality of turning syntaxes into RDF 4 | # triples - RDF parsing. 5 | # 6 | # @see https://librdf.org/raptor/api/raptor2-section-parser.html 7 | class Parser < ::FFI::ManagedStruct 8 | include RDF::Raptor::FFI 9 | layout :world, :pointer # the actual layout is private 10 | 11 | # The default base URI 12 | BASE_URI = 'file:///dev/stdin' 13 | 14 | # The maximum chunk size for `#parse_stream` 15 | BUFFER_SIZE = 64 * 1024 16 | 17 | ## 18 | # @overload initialize(ptr) 19 | # @param [FFI::Pointer] ptr 20 | # 21 | # @overload initialize(name) 22 | # @param [Symbol, String] name 23 | # 24 | def initialize(ptr_or_name) 25 | ptr = case ptr_or_name 26 | when FFI::Pointer then ptr_or_name 27 | when Symbol then V2.raptor_new_parser(V2.world, ptr_or_name.to_s) 28 | when String then V2.raptor_new_parser(V2.world, ptr_or_name) 29 | else nil 30 | end 31 | raise ArgumentError, "invalid argument: #{ptr_or_name.inspect}" if ptr.nil? || ptr.null? 32 | super(ptr) 33 | end 34 | 35 | ## 36 | # Releases `libraptor` memory associated with this structure. 37 | # 38 | # @param [FFI::Pointer] ptr 39 | # @return [void] 40 | def self.release(ptr) 41 | V2.raptor_free_parser(ptr) 42 | end 43 | 44 | ## 45 | # @param [Proc] handler 46 | # @return [void] 47 | def error_handler=(handler) 48 | #V2.raptor_set_error_handler(self, self, handler) 49 | end 50 | 51 | ## 52 | # @param [Proc] handler 53 | # @return [void] 54 | def warning_handler=(handler) 55 | #V2.raptor_set_warning_handler(self, self, handler) 56 | end 57 | 58 | ## 59 | # @param [Proc] handler 60 | # @return [void] 61 | def statement_handler=(handler) 62 | V2.raptor_parser_set_statement_handler(self, self, handler) 63 | end 64 | 65 | ## 66 | # @param [Proc] handler 67 | # @return [void] 68 | def namespace_handler=(handler) 69 | V2.raptor_parser_set_namespace_handler(self, self, handler) 70 | end 71 | 72 | ## 73 | # @param [Object] input 74 | # the input to parse 75 | # @param [Hash{Symbol => Object}] options 76 | # any additional options for parsing 77 | # @option options [String, #to_s] :base_uri (nil) 78 | # the base URI to use when resolving relative URIs 79 | # @yield [parser, statement] 80 | # each statement in the input 81 | # @yieldparam [FFI::Pointer] parser 82 | # @yieldparam [FFI::Pointer] statement 83 | # @yieldreturn [void] ignored 84 | # @return [void] 85 | def parse(input, **options, &block) 86 | case input 87 | when RDF::URI, URI, %r(^(file|https|http|ftp)://) 88 | parse_url(input, **options, &block) 89 | when File, Tempfile 90 | parse_file(input, **options, &block) 91 | when IO, StringIO 92 | parse_stream(input, **options, &block) 93 | when String 94 | parse_buffer(input, **options, &block) 95 | else 96 | raise ArgumentError, "don't know how to parse #{input.inspect}" 97 | end 98 | end 99 | 100 | ## 101 | # @param [RDF::URI, String, #to_s] url 102 | # the input URL to parse 103 | # @param [Hash{Symbol => Object}] options 104 | # any additional options for parsing (see {#parse}) 105 | # @yield [parser, statement] 106 | # each statement in the input 107 | # @yieldparam [FFI::Pointer] parser 108 | # @yieldparam [FFI::Pointer] statement 109 | # @yieldreturn [void] ignored 110 | # @return [void] 111 | def parse_url(url, **options, &block) 112 | self.statement_handler = block if block_given? 113 | 114 | data_url = V2::URI.new((url.respond_to?(:to_uri) ? url.to_uri : url).to_s) 115 | base_uri = options[:base_uri].to_s.empty? ? nil : V2::URI.new(options[:base_uri].to_s) 116 | 117 | result = V2.raptor_parser_parse_uri(self, data_url, base_uri) 118 | # TODO: error handling if result.nonzero? 119 | end 120 | alias_method :parse_uri, :parse_url 121 | 122 | ## 123 | # @param [File, Tempfile, #path] file 124 | # the input file to parse 125 | # @param [Hash{Symbol => Object}] options 126 | # any additional options for parsing (see {#parse}) 127 | # @yield [parser, statement] 128 | # each statement in the input 129 | # @yieldparam [FFI::Pointer] parser 130 | # @yieldparam [FFI::Pointer] statement 131 | # @yieldreturn [void] ignored 132 | # @return [void] 133 | def parse_file(file, **options, &block) 134 | self.statement_handler = block if block_given? 135 | 136 | data_url = V2::URI.new("file://#{File.expand_path(file.path)}") 137 | base_uri = options[:base_uri].to_s.empty? ? nil : V2::URI.new(options[:base_uri].to_s) 138 | 139 | result = V2.raptor_parser_parse_file(self, data_url, base_uri) 140 | # TODO: error handling if result.nonzero? 141 | end 142 | 143 | ## 144 | # @param [IO, StringIO, #readpartial] stream 145 | # the input stream to parse 146 | # @param [Hash{Symbol => Object}] options 147 | # any additional options for parsing (see {#parse}) 148 | # @yield [parser, statement] 149 | # each statement in the input 150 | # @yieldparam [FFI::Pointer] parser 151 | # @yieldparam [FFI::Pointer] statement 152 | # @yieldreturn [void] ignored 153 | # @return [void] 154 | def parse_stream(stream, **options, &block) 155 | self.statement_handler = block if block_given? 156 | 157 | begin 158 | parse_start!(options[:base_uri] || BASE_URI) 159 | loop do 160 | parse_chunk(stream.sysread(BUFFER_SIZE)) 161 | end 162 | rescue EOFError => e 163 | parse_end! 164 | end 165 | end 166 | 167 | ## 168 | # @param [String, #to_str] buffer 169 | # the input buffer to parse 170 | # @param [Hash{Symbol => Object}] options 171 | # any additional options for parsing (see {#parse}) 172 | # @yield [parser, statement] 173 | # each statement in the input 174 | # @yieldparam [FFI::Pointer] parser 175 | # @yieldparam [FFI::Pointer] statement 176 | # @yieldreturn [void] ignored 177 | # @return [void] 178 | def parse_buffer(buffer, **options, &block) 179 | self.statement_handler = block if block_given? 180 | 181 | parse_start!((options[:base_uri] || BASE_URI).to_s) 182 | parse_chunk(buffer.to_str) 183 | parse_end! 184 | end 185 | 186 | ## 187 | # @private 188 | # @param [String] base_uri 189 | # @return [void] 190 | def parse_start!(base_uri = BASE_URI) 191 | result = V2.raptor_parser_parse_start(self, V2::URI.new(base_uri)) 192 | # TODO: error handling if result.nonzero? 193 | end 194 | 195 | ## 196 | # @private 197 | # @param [String] buffer 198 | # the input chunk to parse 199 | # @return [void] 200 | def parse_chunk(buffer) 201 | result = V2.raptor_parser_parse_chunk(self, buffer, buffer.bytesize, 0) 202 | # TODO: error handling if result.nonzero? 203 | end 204 | 205 | ## 206 | # @private 207 | # @return [void] 208 | def parse_end! 209 | result = V2.raptor_parser_parse_chunk(self, nil, 0, 1) # EOF 210 | end 211 | end # Parser 212 | end # RDF::Raptor::FFI::V2 213 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v2/serializer.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V2 2 | ## 3 | # This class provides the functionality of turning RDF triples into 4 | # syntaxes - RDF serializing. 5 | # 6 | # @see https://librdf.org/raptor/api-1.4/raptor-section-serializer.html 7 | class Serializer < ::FFI::ManagedStruct 8 | include RDF::Raptor::FFI 9 | 10 | attr_reader :iostream 11 | 12 | # Note this layout is private 13 | layout :world, :pointer, 14 | :locator, :pointer, 15 | :failed, :bool, 16 | :base_uri, :pointer, 17 | :context, :pointer, 18 | :iostream, :pointer, 19 | :free_iostream_on_end, :bool, 20 | :factory, :pointer, 21 | :options, :pointer 22 | 23 | ## 24 | # @overload initialize(ptr) 25 | # @param [FFI::Pointer] ptr 26 | # 27 | # @overload initialize(name) 28 | # @param [Symbol, String] name 29 | # 30 | def initialize(ptr_or_name) 31 | ptr = case ptr_or_name 32 | when FFI::Pointer then ptr_or_name 33 | when Symbol then V2.raptor_new_serializer(V2.world, ptr_or_name.to_s) 34 | when String then V2.raptor_new_serializer(V2.world, ptr_or_name) 35 | else nil 36 | end 37 | raise ArgumentError, "invalid argument: #{ptr_or_name.inspect}" if ptr.nil? || ptr.null? 38 | super(ptr) 39 | end 40 | 41 | ## 42 | # Releases `libraptor` memory associated with this structure. 43 | # 44 | # @param [FFI::Pointer] ptr 45 | # @return [void] 46 | def self.release(ptr) 47 | V2.raptor_free_serializer(ptr) 48 | end 49 | 50 | ## 51 | # @param [Proc] handler 52 | # @return [void] 53 | def error_handler=(handler) 54 | V2.raptor_serializer_set_error_handler(self, self, handler) if V2.respond_to?(:raptor_serializer_set_error_handler) 55 | end 56 | 57 | ## 58 | # @param [Proc] handler 59 | # @return [void] 60 | def warning_handler=(handler) 61 | V2.raptor_serializer_set_warning_handler(self, self, handler) if V2.respond_to?(:raptor_serializer_set_warning_handler) 62 | end 63 | 64 | ## 65 | # @param [Object] output 66 | # where output should be written to 67 | # @param [Hash{Symbol => Object}] options 68 | # any additional options for serializing 69 | # @option options [String, #to_s] :base_uri (nil) 70 | # the base URI to use when resolving relative URIs 71 | # @return [void] 72 | def start_to(output, **options) 73 | if output.respond_to?(:write) 74 | start_to_stream(output, **options) 75 | else 76 | raise ArgumentError, "don't know how to serialize to #{output.inspect}" 77 | end 78 | end 79 | 80 | ## 81 | # @param [IO, StringIO] stream 82 | # @param [Hash{Symbol => Object}] options 83 | # any additional options for serializing (see {#start_to}) 84 | # @return [void] 85 | def start_to_stream(stream, **options) 86 | iostream = V2::IOStream.new(V2::IOStreamHandler.new(stream), free_iostream: self[:free_iostream_on_end]) 87 | start_to_iostream(iostream, **options) 88 | end 89 | 90 | ## 91 | # @param [V2::IOStream] iostream 92 | # @param [Hash{Symbol => Object}] options 93 | # any additional options for serializing (see {#start_to}) 94 | # @return [void] 95 | def start_to_iostream(iostream, base_uri: nil, **options) 96 | @iostream = iostream # prevents premature GC 97 | @base_uri = base_uri.to_s.empty? ? nil : V2::URI.new(base_uri.to_s) 98 | if V2.raptor_serializer_start_to_iostream(self, @base_uri, @iostream).nonzero? 99 | raise RDF::WriterError, "raptor_serialize_start_to_iostream() failed" 100 | end 101 | end 102 | 103 | ## 104 | # @param [Hash{namespace => uri}] prefixes 105 | # @return [void] 106 | def prefix(prefixes) 107 | prefixes.each do |name, uri| 108 | if V2.raptor_serializer_set_namespace(self, V2::URI.new(uri.to_s), name.to_s).nonzero? 109 | raise RDF::WriterError, "raptor_serializer_set_namespace() failed" 110 | end 111 | end 112 | end 113 | 114 | ## 115 | # @return [void] 116 | def finish 117 | if V2.raptor_serializer_serialize_end(self).nonzero? 118 | raise RDF::WriterError, "raptor_serialize_end() failed" 119 | end 120 | @iostream = @base_uri = nil # allows GC 121 | end 122 | 123 | ## 124 | # @param [RDF::Resource] subject 125 | # @param [RDF::URI] predicate 126 | # @param [RDF::Term] object 127 | # @return [void] 128 | def serialize_triple(subject, predicate, object) 129 | raptor_statement = V2::Statement.new 130 | raptor_statement.subject = subject 131 | raptor_statement.predicate = predicate 132 | raptor_statement.object = object 133 | begin 134 | serialize_raw_statement(raptor_statement) 135 | ensure 136 | raptor_statement.release 137 | raptor_statement = nil 138 | end 139 | end 140 | 141 | ## 142 | # @param [V2::Statement] statement 143 | # @return [void] 144 | def serialize_raw_statement(statement) 145 | if V2.raptor_serializer_serialize_statement(self, statement).nonzero? 146 | raise RDF::WriterError, "raptor_serialize_statement() failed" 147 | end 148 | end 149 | end # Serializer 150 | end # RDF::Raptor::FFI::V2 151 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v2/statement.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V2 2 | ## 3 | # @see https://librdf.org/raptor/api-1.4/raptor-section-triples.html 4 | class Statement < ::FFI::Struct 5 | include RDF::Raptor::FFI 6 | layout :world, :pointer, 7 | :usage, :int, 8 | :subject, :pointer, 9 | :predicate, :pointer, 10 | :object, :pointer, 11 | :graph, :pointer 12 | 13 | ## 14 | # @param [FFI::Pointer] ptr 15 | # @param [#create_node] factory 16 | def initialize(ptr = nil, factory = nil) 17 | super(ptr) 18 | @factory = factory if factory 19 | end 20 | 21 | ## 22 | # Releases `libraptor` memory associated with this structure. 23 | # 24 | # @return [void] 25 | def release 26 | V2.raptor_free_statement(ptr) unless ptr.null? 27 | end 28 | 29 | # @return [Object] 30 | attr_accessor :id 31 | 32 | # @return [RDF::Resource] 33 | attr_accessor :graph_name 34 | 35 | ## 36 | # @return [RDF::Resource] 37 | def subject 38 | @subject ||= V2::Term.new(self[:subject], @factory).value 39 | end 40 | 41 | ## 42 | # Sets the subject term from an `RDF::Resource`. 43 | # 44 | # @param [RDF::Resource] resource 45 | # @return [void] 46 | def subject=(resource) 47 | @subject = nil 48 | case resource 49 | when RDF::Node 50 | self[:subject] = V2.raptor_new_term_from_blank(V2.world, resource.id.to_s) 51 | when RDF::URI 52 | self[:subject] = V2.raptor_new_term_from_uri_string(V2.world, resource.to_s) 53 | else 54 | raise ArgumentError, "subject term must be an RDF::Node or RDF::URI" 55 | end 56 | @subject = resource 57 | end 58 | 59 | ## 60 | # @return [RDF::URI] 61 | def predicate 62 | @predicate ||= V2::Term.new(self[:predicate], @factory).value 63 | end 64 | 65 | ## 66 | # Sets the predicate term from an `RDF::URI`. 67 | # 68 | # @param [RDF::URI] uri 69 | # @return [void] 70 | def predicate=(uri) 71 | @predicate = nil 72 | raise ArgumentError, "predicate term must be an RDF::URI" unless uri.is_a?(RDF::URI) 73 | self[:predicate] = V2.raptor_new_term_from_uri_string(V2.world, uri.to_s) 74 | @predicate = uri 75 | end 76 | 77 | ## 78 | # @return [RDF::Term] 79 | def object 80 | @object ||= V2::Term.new(self[:object], @factory).value 81 | end 82 | 83 | ## 84 | # Sets the object term from an `RDF::Term`. 85 | # 86 | # The value must be one of `RDF::Resource` or `RDF::Literal`. 87 | # 88 | # @param [RDF::Term] value 89 | # @return [void] 90 | def object=(value) 91 | @object = nil 92 | case value 93 | when RDF::Node 94 | self[:object] = V2.raptor_new_term_from_blank(V2.world, value.id.to_s) 95 | when RDF::URI 96 | self[:object] = V2.raptor_new_term_from_uri_string(V2.world, value.to_s) 97 | when RDF::Literal 98 | self[:object] = V2.raptor_new_term_from_literal(V2.world, value.to_s, 99 | value.datatype? ? V2.raptor_new_uri(V2.world, value.datatype.to_s) : nil, 100 | value.language? ? value.language.to_s : nil) 101 | else 102 | raise ArgumentError, "object term must be an RDF::Node, RDF::URI, or RDF::Literal" 103 | end 104 | @object = value 105 | end 106 | 107 | ## 108 | # @return [Array(RDF::Resource, RDF::URI, RDF::Term)] 109 | # @see RDF::Statement#to_triple 110 | def to_triple 111 | [subject, predicate, object] 112 | end 113 | 114 | ## 115 | # @return [Array(RDF::Resource, RDF::URI, RDF::Term, nil)] 116 | # @see RDF::Statement#to_quad 117 | def to_quad 118 | [subject, predicate, object, graph_name] 119 | end 120 | 121 | ## 122 | # @return [RDF::Statement] 123 | def to_rdf 124 | RDF::Statement.new(subject, predicate, object, graph_name: graph_name) 125 | end 126 | 127 | ## 128 | # @return [void] 129 | def reset! 130 | @subject = @predicate = @object = @graph_name = nil 131 | end 132 | 133 | ## 134 | # Releases `libraptor` memory associated with this structure. 135 | # 136 | # @return [void] 137 | def free 138 | V2.raptor_free_statement(self) 139 | @subject = @predicate = @object = nil # Allow GC to start 140 | end 141 | alias_method :release, :free 142 | end # Statement 143 | end # RDF::Raptor::FFI::V2 144 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v2/term.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V2 2 | ## 3 | # @see https://librdf.org/raptor/api-1.4/raptor-section-triples.html 4 | class Term < ::FFI::Struct 5 | include RDF::Raptor::FFI 6 | 7 | class LiteralValue < ::FFI::Struct 8 | include RDF::Raptor::FFI 9 | 10 | layout :string, :string, 11 | :string_len, :int, 12 | :datatype, :pointer, 13 | :language, :string, 14 | :language_len, :char 15 | 16 | def to_str 17 | self[:string].unpack('U*').pack('U*') 18 | end 19 | 20 | def language 21 | unless self[:language].nil? or self[:language].empty? 22 | self[:language] 23 | end 24 | end 25 | 26 | def datatype 27 | if self[:datatype] && !self[:datatype].null? 28 | RDF::URI.intern(V2.raptor_uri_to_string(self[:datatype])) 29 | end 30 | end 31 | 32 | def to_rdf 33 | str = self.to_str 34 | case 35 | when language = self.language 36 | RDF::Literal.new(str, language: language) 37 | when datatype = self.datatype 38 | RDF::Literal.new(str, datatype: datatype) 39 | else 40 | RDF::Literal.new(str) 41 | end 42 | end 43 | end 44 | 45 | class BlankValue < ::FFI::Struct 46 | layout :string, :string, 47 | :string_len, :int 48 | 49 | def to_str 50 | self[:string] 51 | end 52 | end 53 | 54 | class Value < ::FFI::Union 55 | include RDF::Raptor::FFI 56 | 57 | layout :uri, :pointer, 58 | :literal, LiteralValue, 59 | :blank, BlankValue 60 | end 61 | 62 | layout :world, :pointer, 63 | :usage, :int, 64 | :type, :int, 65 | :value, Value 66 | ## 67 | # @param [FFI::Pointer] ptr 68 | # @param [#create_node] factory 69 | def initialize(ptr = nil, factory = nil) 70 | super(ptr) 71 | @factory = factory if factory 72 | end 73 | 74 | def value 75 | case self[:type] 76 | when RAPTOR_TERM_TYPE_BLANK 77 | @factory.create_node(self[:value][:blank].to_str) 78 | when RAPTOR_TERM_TYPE_URI 79 | @factory.create_uri(V2.raptor_uri_as_string(self[:value][:uri])) 80 | when RAPTOR_TERM_TYPE_LITERAL 81 | self[:value][:literal].to_rdf 82 | end 83 | end 84 | 85 | ## 86 | # Releases `libraptor` memory associated with this structure. 87 | # 88 | # @param [FFI::Pointer] ptr 89 | # @return [void] 90 | def release(ptr = nil) 91 | V2.raptor_free_term(self) unless ptr.null? 92 | end 93 | 94 | end # Term 95 | end # RDF::Raptor::FFI::V2 96 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v2/uri.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V2 2 | ## 3 | # Raptor has a `raptor_uri` class which must be used for manipulating and 4 | # passing URI references. The default internal implementation uses `char*` 5 | # strings for URIs, manipulating them and constructing them. 6 | # 7 | # @see https://librdf.org/raptor/api-1.4/raptor-section-uri.html 8 | class URI < ::FFI::ManagedStruct 9 | include RDF::Raptor::FFI 10 | include RDF::Resource 11 | layout :string, [:char, 1] # a safe dummy layout, since it is \0-terminated 12 | 13 | ## 14 | # @overload initialize(ptr) 15 | # @param [FFI::Pointer] ptr 16 | # 17 | # @overload initialize(name) 18 | # @param [RDF::URI, String] name 19 | # 20 | def initialize(ptr_or_name) 21 | ptr = case ptr_or_name 22 | when FFI::Pointer then ptr_or_name 23 | when RDF::URI then V2.raptor_new_uri(V2.world, ptr_or_name.to_s) 24 | when String then V2.raptor_new_uri(V2.world, ptr_or_name) 25 | else nil 26 | end 27 | raise ArgumentError, "invalid argument: #{ptr_or_name.inspect}" if ptr.nil? || ptr.null? 28 | super(ptr) 29 | end 30 | 31 | ## 32 | # Releases `libraptor` memory associated with this structure. 33 | # 34 | # @param [FFI::Pointer] ptr 35 | # @return [void] 36 | def self.release(ptr) 37 | V2.raptor_free_uri(ptr) 38 | end 39 | 40 | ## 41 | # @return [Boolean] `true` 42 | def uri? 43 | true 44 | end 45 | 46 | ## 47 | # @return [URI] a copy of `self` 48 | def dup 49 | copy = self.class.new(V2.raptor_uri_copy(self)) 50 | copy.taint if tainted? 51 | copy 52 | end 53 | 54 | ## 55 | # @return [URI] a copy of `self` 56 | def clone 57 | copy = self.class.new(V2.raptor_uri_copy(self)) 58 | copy.taint if tainted? 59 | copy.freeze if frozen? 60 | copy 61 | end 62 | 63 | ## 64 | # @return [Integer] 65 | def length 66 | LibC.strlen(self) 67 | end 68 | alias_method :size, :length 69 | 70 | ## 71 | # @return [Boolean] `true` or `false` 72 | def ==(other) 73 | return true if self.equal?(other) 74 | case other 75 | when self.class 76 | !(V2.raptor_uri_equals(self, other).zero?) 77 | when RDF::URI, String 78 | to_str == other.to_str 79 | else false 80 | end 81 | end 82 | alias_method :===, :== 83 | 84 | ## 85 | # @return [Boolean] `true` or `false` 86 | def eql?(other) 87 | return true if self.equal?(other) 88 | other.is_a?(self.class) && !(V2.raptor_uri_equals(self, other).zero?) 89 | end 90 | 91 | ## 92 | # @return [Fixnum] 93 | def hash 94 | to_str.hash 95 | end 96 | 97 | ## 98 | # @return [String] the URI string 99 | def to_str 100 | V2.raptor_uri_as_string(self) 101 | end 102 | alias_method :to_s, :to_str 103 | 104 | ## 105 | # @return [RDF::URI] 106 | def to_rdf 107 | RDF::URI.intern(to_str) 108 | end 109 | alias_method :to_uri, :to_rdf 110 | end # URI 111 | end # RDF::Raptor::FFI::V2 112 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ffi/v2/world.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor::FFI::V2 2 | class World < ::FFI::AutoPointer 3 | include RDF::Raptor::FFI 4 | 5 | def initialize() 6 | ptr = V2.raptor_new_world_internal(V2.raptor_version_decimal) 7 | super(ptr, self.class.method(:release)) 8 | end 9 | 10 | ## 11 | # Releases `libraptor` memory associated with this structure. 12 | # 13 | # @param [FFI::Pointer] ptr 14 | # @return [void] 15 | def self.release(ptr) 16 | V2.raptor_free_world(ptr) 17 | end 18 | 19 | end # World 20 | end # RDF::Raptor::FFI::V2 21 | -------------------------------------------------------------------------------- /lib/rdf/raptor/format.rb: -------------------------------------------------------------------------------- 1 | module RDF 2 | module Raptor 3 | ## 4 | # RDF::Raptor::Format mixin. 5 | module Format 6 | ## 7 | # @overload rapper_format 8 | # 9 | # @overload rapper_format(format) 10 | # @param [Symbol] format 11 | # 12 | # @return [void] 13 | def rapper_format(format = nil) 14 | unless format 15 | @rapper_format 16 | else 17 | @rapper_format = format 18 | end 19 | end 20 | 21 | def detect(sample) 22 | parser_name = RDF::Raptor::FFI::V2.raptor_world_guess_parser_name(RDF::Raptor::FFI::V2.world, nil, nil, sample, sample.length, nil) 23 | parser_name == rapper_format.to_s 24 | end 25 | end # Format 26 | end 27 | end -------------------------------------------------------------------------------- /lib/rdf/raptor/graphviz.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor 2 | ## 3 | # Graphviz support. 4 | # 5 | # @example Requiring the `RDF::Raptor` module 6 | # require 'rdf/raptor' 7 | # 8 | # @example Serializing RDF statements into a Graphviz file 9 | # RDF::Writer.open("output.dot") do |writer| 10 | # graph.each_statement do |statement| 11 | # writer << statement 12 | # end 13 | # end 14 | # 15 | module Graphviz 16 | ## 17 | # Graphviz format specification. 18 | # 19 | # @example Obtaining a Graphviz format class 20 | # RDF::Format.for(:graphviz) #=> RDF::Raptor::Graphviz::Format 21 | # RDF::Format.for("output.dot") 22 | # RDF::Format.for(file_name: "output.dot") 23 | # RDF::Format.for(file_extension: "dot") 24 | # RDF::Format.for(content_type: "text/vnd.graphviz") 25 | # 26 | # @see https://www.iana.org/assignments/media-types/text/vnd.graphviz 27 | class Format < RDF::Format 28 | extend RDF::Raptor::Format 29 | 30 | content_type 'text/vnd.graphviz', aliases: ['application/x-graphviz', 'text/x-graphviz'], extension: :dot # TODO: also .gv 31 | content_encoding 'utf-8' 32 | rapper_format :dot 33 | 34 | writer { RDF::Raptor::Graphviz::Writer } 35 | end # Format 36 | 37 | ## 38 | # Graphviz serializer. 39 | # 40 | # @example Obtaining a Graphviz writer class 41 | # RDF::Writer.for(:graphviz) #=> RDF::Raptor::Graphviz::Writer 42 | # RDF::Writer.for("output.dot") 43 | # RDF::Writer.for(file_name: "output.dot") 44 | # RDF::Writer.for(file_extension: "dot") 45 | # RDF::Writer.for(content_type: "text/vnd.graphviz") 46 | # 47 | # @example Serializing RDF statements into a Graphviz file 48 | # RDF::Writer.open("output.dot") do |writer| 49 | # graph.each_statement do |statement| 50 | # writer << statement 51 | # end 52 | # end 53 | # 54 | class Writer < RDF::Raptor::Writer 55 | format RDF::Raptor::Graphviz::Format 56 | end # Writer 57 | end # Graphviz 58 | end # RDF::Raptor 59 | -------------------------------------------------------------------------------- /lib/rdf/raptor/ntriples.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor 2 | ## 3 | # N-Triples support. 4 | # 5 | # @example Requiring the `RDF::Raptor` module 6 | # require 'rdf/raptor' 7 | # 8 | # @example Parsing RDF statements from an N-Triples file 9 | # RDF::Reader.open("input.nt") do |reader| 10 | # reader.each_statement do |statement| 11 | # puts statement.inspect 12 | # end 13 | # end 14 | # 15 | # @example Serializing RDF statements into an N-Triples file 16 | # RDF::Writer.open("output.nt") do |writer| 17 | # graph.each_statement do |statement| 18 | # writer << statement 19 | # end 20 | # end 21 | # 22 | # @see https://www.w3.org/TR/rdf-testcases/#ntriples 23 | module NTriples 24 | ## 25 | # N-Triples format specification. 26 | # 27 | # @example Obtaining an N-Triples format class 28 | # RDF::Format.for(:ntriples) #=> RDF::Raptor::NTriples::Format 29 | # RDF::Format.for("input.nt") 30 | # RDF::Format.for(file_name: "input.nt") 31 | # RDF::Format.for(file_extension: "nt") 32 | # RDF::Format.for(content_type: "text/plain") 33 | # 34 | class Format < RDF::Format 35 | extend RDF::Raptor::Format 36 | 37 | content_type 'application/n-triples', extension: :nt, alias: ['text/plain'] 38 | content_encoding 'utf-8' 39 | rapper_format :ntriples 40 | 41 | reader { RDF::Raptor::NTriples::Reader } 42 | writer { RDF::Raptor::NTriples::Writer } 43 | 44 | def self.detect(sample) 45 | # Raptor's format guessing isn't fully supported 46 | RDF::NTriples::Format.detect(sample) 47 | end 48 | end # Format 49 | 50 | ## 51 | # N-Triples parser. 52 | # 53 | # @example Obtaining an N-Triples reader class 54 | # RDF::Reader.for(:ntriples) #=> RDF::Raptor::NTriples::Reader 55 | # RDF::Reader.for("input.nt") 56 | # RDF::Reader.for(file_name: "input.nt") 57 | # RDF::Reader.for(file_extension: "nt") 58 | # RDF::Reader.for(content_type: "text/plain") 59 | # 60 | # @example Parsing RDF statements from an N-Triples file 61 | # RDF::Reader.open("input.nt") do |reader| 62 | # reader.each_statement do |statement| 63 | # puts statement.inspect 64 | # end 65 | # end 66 | # 67 | class Reader < RDF::Raptor::Reader 68 | format RDF::Raptor::NTriples::Format 69 | end # Reader 70 | 71 | ## 72 | # N-Triples serializer. 73 | # 74 | # @example Obtaining an N-Triples writer class 75 | # RDF::Writer.for(:ntriples) #=> RDF::Raptor::NTriples::Writer 76 | # RDF::Writer.for("output.nt") 77 | # RDF::Writer.for(file_name: "output.nt") 78 | # RDF::Writer.for(file_extension: "nt") 79 | # RDF::Writer.for(content_type: "text/plain") 80 | # 81 | # @example Serializing RDF statements into an N-Triples file 82 | # RDF::Writer.open("output.nt") do |writer| 83 | # graph.each_statement do |statement| 84 | # writer << statement 85 | # end 86 | # end 87 | # 88 | class Writer < RDF::Raptor::Writer 89 | include RDF::Raptor::FFI 90 | format RDF::Raptor::NTriples::Format 91 | 92 | def write_term(value) 93 | V2.raptor_term_ntriples_write(value, @serializer.iostream) 94 | end 95 | end # Writer 96 | end # NTriples 97 | end # RDF::Raptor 98 | -------------------------------------------------------------------------------- /lib/rdf/raptor/rdfa.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor 2 | ## 3 | # RDFa support. 4 | # 5 | # @example Requiring the `RDF::Raptor` module 6 | # require 'rdf/raptor' 7 | # 8 | # @example Extracting RDF statements from an XHTML+RDFa file 9 | # RDF::Reader.open("input.html") do |reader| 10 | # reader.each_statement do |statement| 11 | # puts statement.inspect 12 | # end 13 | # end 14 | # 15 | # @see https://rdfa.info/ 16 | module RDFa 17 | ## 18 | # RDFa format specification. 19 | # 20 | # @example Obtaining an RDFa format class 21 | # RDF::Format.for(:rdfa) #=> RDF::Raptor::RDFa::Format 22 | # RDF::Format.for("input.html") 23 | # RDF::Format.for(file_name: "input.html") 24 | # RDF::Format.for(file_extension: "html") 25 | # RDF::Format.for(content_type: "application/xhtml+xml") 26 | # 27 | class Format < RDF::Format 28 | extend RDF::Raptor::Format 29 | 30 | content_type 'text/html', 31 | aliases: %w(application/xhtml+xml image/svg+xml), 32 | extensions: [:html, :xhtml, :svg] 33 | content_encoding 'utf-8' 34 | rapper_format :rdfa 35 | 36 | reader { RDF::Raptor::RDFa::Reader } 37 | 38 | ## 39 | # Sample detection to see if it matches RDFa (not RDF/XML or Microdata) 40 | # 41 | # Use a text sample to detect the format of an input file. Sub-classes implement 42 | # a matcher sufficient to detect probably format matches, including disambiguating 43 | # between other similar formats. 44 | # 45 | # @param [String] sample Beginning several bytes (~ 1K) of input. 46 | # @return [Boolean] 47 | def self.detect(sample) 48 | (sample.match(/<[^>]*(about|resource|prefix|typeof|property|vocab)\s*="[^>]*>/m) || 49 | sample.match(/<[^>]*DOCTYPE\s+html[^>]*>.*xmlns:/im) 50 | ) && !sample.match(/<(\w+:)?(RDF)/) 51 | end 52 | 53 | def self.symbols 54 | [:rdfa, :lite, :html, :xhtml, :svg] 55 | end 56 | end # Format 57 | 58 | ## 59 | # RDFa extractor. 60 | # 61 | # @example Obtaining an RDFa reader class 62 | # RDF::Reader.for(:rdfa) #=> RDF::Raptor::RDFa::Reader 63 | # RDF::Reader.for("input.html") 64 | # RDF::Reader.for(file_name: "input.html") 65 | # RDF::Reader.for(file_extension: "html") 66 | # RDF::Reader.for(content_type: "application/xhtml+xml") 67 | # 68 | # @example Extracting RDF statements from an XHTML+RDFa file 69 | # RDF::Reader.open("input.html") do |reader| 70 | # reader.each_statement do |statement| 71 | # puts statement.inspect 72 | # end 73 | # end 74 | # 75 | class Reader < RDF::Raptor::Reader 76 | format RDF::Raptor::RDFa::Format 77 | end # Reader 78 | end # RDFa 79 | end # RDF::Raptor 80 | -------------------------------------------------------------------------------- /lib/rdf/raptor/rdfxml.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor 2 | ## 3 | # RDF/XML support. 4 | # 5 | # @example Requiring the `RDF::Raptor` module 6 | # require 'rdf/raptor' 7 | # 8 | # @example Parsing RDF statements from an RDF/XML file 9 | # RDF::Reader.open("input.rdf") do |reader| 10 | # reader.each_statement do |statement| 11 | # puts statement.inspect 12 | # end 13 | # end 14 | # 15 | # @example Serializing RDF statements into an RDF/XML file 16 | # RDF::Writer.open("output.rdf") do |writer| 17 | # graph.each_statement do |statement| 18 | # writer << statement 19 | # end 20 | # end 21 | # 22 | # @see https://www.w3.org/TR/REC-rdf-syntax/ 23 | module RDFXML 24 | ## 25 | # RDF/XML format specification. 26 | # 27 | # @example Obtaining an RDF/XML format class 28 | # RDF::Format.for(:rdfxml) #=> RDF::Raptor::RDFXML::Format 29 | # RDF::Format.for("input.rdf") 30 | # RDF::Format.for(file_name: "input.rdf") 31 | # RDF::Format.for(file_extension: "rdf") 32 | # RDF::Format.for(content_type: "application/rdf+xml") 33 | # 34 | class Format < RDF::Format 35 | extend RDF::Raptor::Format 36 | 37 | content_type 'application/rdf+xml', extension: :rdf 38 | content_encoding 'utf-8' 39 | rapper_format :rdfxml 40 | 41 | reader { RDF::Raptor::RDFXML::Reader } 42 | writer { RDF::Raptor::RDFXML::Writer } 43 | 44 | def self.detect(sample) 45 | # Raptor guess is not fully supported 46 | sample.match(/<(\w+:)?(RDF)/) 47 | end 48 | 49 | def self.symbols 50 | [:rdfxml, :rdf] 51 | end 52 | end # Format 53 | 54 | ## 55 | # RDF/XML parser. 56 | # 57 | # @example Obtaining an RDF/XML reader class 58 | # RDF::Reader.for(:rdfxml) #=> RDF::Raptor::RDFXML::Reader 59 | # RDF::Reader.for("input.rdf") 60 | # RDF::Reader.for(file_name: "input.rdf") 61 | # RDF::Reader.for(file_extension: "rdf") 62 | # RDF::Reader.for(content_type: "application/rdf+xml") 63 | # 64 | # @example Parsing RDF statements from an RDF/XML file 65 | # RDF::Reader.open("input.rdf") do |reader| 66 | # reader.each_statement do |statement| 67 | # puts statement.inspect 68 | # end 69 | # end 70 | # 71 | class Reader < RDF::Raptor::Reader 72 | format RDF::Raptor::RDFXML::Format 73 | end # Reader 74 | 75 | ## 76 | # RDF/XML serializer. 77 | # 78 | # @example Obtaining an RDF/XML writer class 79 | # RDF::Writer.for(:rdfxml) #=> RDF::Raptor::RDFXML::Writer 80 | # RDF::Writer.for("output.rdf") 81 | # RDF::Writer.for(file_name: "output.rdf") 82 | # RDF::Writer.for(file_extension: "rdf") 83 | # RDF::Writer.for(content_type: "application/rdf+xml") 84 | # 85 | # @example Serializing RDF statements into an RDF/XML file 86 | # RDF::Writer.open("output.rdf") do |writer| 87 | # graph.each_statement do |statement| 88 | # writer << statement 89 | # end 90 | # end 91 | # 92 | class Writer < RDF::Raptor::Writer 93 | format RDF::Raptor::RDFXML::Format 94 | end # Writer 95 | end # RDFXML 96 | end # RDF::Raptor 97 | -------------------------------------------------------------------------------- /lib/rdf/raptor/turtle.rb: -------------------------------------------------------------------------------- 1 | module RDF::Raptor 2 | ## 3 | # Turtle support. 4 | # 5 | # @example Requiring the `RDF::Raptor` module 6 | # require 'rdf/raptor' 7 | # 8 | # @example Parsing RDF statements from a Turtle file 9 | # RDF::Reader.open("input.ttl") do |reader| 10 | # reader.each_statement do |statement| 11 | # puts statement.inspect 12 | # end 13 | # end 14 | # 15 | # @example Serializing RDF statements into a Turtle file 16 | # RDF::Writer.open("output.ttl") do |writer| 17 | # graph.each_statement do |statement| 18 | # writer << statement 19 | # end 20 | # end 21 | # 22 | # @see https://www.w3.org/TeamSubmission/turtle/ 23 | module Turtle 24 | ## 25 | # Turtle format specification. 26 | # 27 | # @example Obtaining a Turtle format class 28 | # RDF::Format.for(:turtle) #=> RDF::Raptor::Turtle::Format 29 | # RDF::Format.for("input.ttl") 30 | # RDF::Format.for(file_name: "input.ttl") 31 | # RDF::Format.for(file_extension: "ttl") 32 | # RDF::Format.for(content_type: "text/turtle") 33 | # 34 | class Format < RDF::Format 35 | extend RDF::Raptor::Format 36 | 37 | content_type 'text/turtle', aliases: ['application/x-turtle', 'application/turtle'], extension: :ttl 38 | content_encoding 'utf-8' 39 | rapper_format :turtle 40 | 41 | reader { RDF::Raptor::Turtle::Reader } 42 | writer { RDF::Raptor::Turtle::Writer } 43 | 44 | ## 45 | # Sample detection to see if it matches Turtle (or N-Triples) 46 | # 47 | # Use a text sample to detect the format of an input file. Sub-classes implement 48 | # a matcher sufficient to detect probably format matches, including disambiguating 49 | # between other similar formats. 50 | # 51 | # @param [String] sample Beginning several bytes (~ 1K) of input. 52 | # @return [Boolean] 53 | def self.detect(sample) 54 | !!sample.match(%r( 55 | (?:@(base|prefix)) | # Turtle keywords 56 | ["']{3} | # STRING_LITERAL_LONG_SINGLE_QUOTE/2 57 | "[^"]*"^^ | "[^"]*"@ | # Typed/Language literals 58 | (?: 59 | (?:\s*(?:(?:<[^>]*>) | (?:\w*:\w+) | (?:"[^"]*"))\s*[,;]) || 60 | (?:\s*(?:(?:<[^>]*>) | (?:\w*:\w+) | (?:"[^"]*"))){3} 61 | ) 62 | )mx) && !( 63 | sample.match(%r([{}])) || # TriG 64 | sample.match(%r(@keywords|=>|\{)) || # N3 65 | sample.match(%r(<(?:\/|html|rdf))i) || # HTML, RDF/XML 66 | sample.match(%r(^(?:\s*<[^>]*>){4}.*\.\s*$)) || # N-Quads 67 | sample.match(%r("@(context|subject|iri)")) # JSON-LD 68 | ) 69 | end 70 | 71 | def self.symbols 72 | [:turtle, :ttl] 73 | end 74 | end # Format 75 | 76 | ## 77 | # Turtle parser. 78 | # 79 | # @example Obtaining a Turtle reader class 80 | # RDF::Reader.for(:turtle) #=> RDF::Raptor::Turtle::Reader 81 | # RDF::Reader.for("input.ttl") 82 | # RDF::Reader.for(file_name: "input.ttl") 83 | # RDF::Reader.for(file_extension: "ttl") 84 | # RDF::Reader.for(content_type: "text/turtle") 85 | # 86 | # @example Parsing RDF statements from a Turtle file 87 | # RDF::Reader.open("input.ttl") do |reader| 88 | # reader.each_statement do |statement| 89 | # puts statement.inspect 90 | # end 91 | # end 92 | # 93 | class Reader < RDF::Raptor::Reader 94 | format RDF::Raptor::Turtle::Format 95 | end # Reader 96 | 97 | ## 98 | # Turtle serializer. 99 | # 100 | # @example Obtaining a Turtle writer class 101 | # RDF::Writer.for(:turtle) #=> RDF::Raptor::Turtle::Writer 102 | # RDF::Writer.for("output.ttl") 103 | # RDF::Writer.for(file_name: "output.ttl") 104 | # RDF::Writer.for(file_extension: "ttl") 105 | # RDF::Writer.for(content_type: "text/turtle") 106 | # 107 | # @example Serializing RDF statements into a Turtle file 108 | # RDF::Writer.open("output.ttl") do |writer| 109 | # graph.each_statement do |statement| 110 | # writer << statement 111 | # end 112 | # end 113 | # 114 | class Writer < RDF::Raptor::Writer 115 | format RDF::Raptor::Turtle::Format 116 | end # Writer 117 | end # Turtle 118 | end # RDF::Raptor 119 | -------------------------------------------------------------------------------- /lib/rdf/raptor/version.rb: -------------------------------------------------------------------------------- 1 | module RDF; module Raptor 2 | module VERSION 3 | VERSION_FILE = File.expand_path("../../../../VERSION", __FILE__) 4 | MAJOR, MINOR, TINY, EXTRA = File.read(VERSION_FILE).chop.split(".") 5 | STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.') 6 | 7 | ## 8 | # @return [String] 9 | def self.to_s() STRING end 10 | 11 | ## 12 | # @return [String] 13 | def self.to_str() STRING end 14 | 15 | ## 16 | # @return [Array(Integer, Integer, Integer)] 17 | def self.to_a() STRING.split(".") end 18 | end 19 | end; end 20 | -------------------------------------------------------------------------------- /memtest.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'rdf/raptor' 3 | 4 | 100.times do 5 | RDF::Reader.open("etc/doap.ttl").each {|s| s.inspect} 6 | end -------------------------------------------------------------------------------- /rdf-raptor.gemspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby -rubygems 2 | # -*- encoding: utf-8 -*- 3 | 4 | Gem::Specification.new do |gem| 5 | gem.version = File.read('VERSION').chomp 6 | gem.date = File.mtime('VERSION').strftime('%Y-%m-%d') 7 | 8 | gem.name = 'rdf-raptor' 9 | gem.homepage = 'https://github.com/ruby-rdf/rdf-raptor' 10 | gem.license = 'Unlicense' 11 | gem.summary = 'Raptor RDF Parser plugin for RDF.rb.' 12 | gem.description = 'RDF.rb plugin for parsing/serializing NTriples, RDF/XML, Turtle and RDFa data using the Raptor RDF Parser library.' 13 | gem.metadata = { 14 | "documentation_uri" => "https://ruby-rdf.github.io/rdf-raptor", 15 | "bug_tracker_uri" => "https://github.com/ruby-rdf/rdf-raptor/issues", 16 | "homepage_uri" => "https://github.com/ruby-rdf/rdf-raptor", 17 | "mailing_list_uri" => "https://lists.w3.org/Archives/Public/public-rdf-ruby/", 18 | "source_code_uri" => "https://github.com/ruby-rdf/rdf-raptor", 19 | } 20 | 21 | gem.authors = ['Arto Bendiken', 'John Fieber'] 22 | gem.email = 'public-rdf-ruby@w3.org' 23 | 24 | gem.platform = Gem::Platform::RUBY 25 | gem.files = %w(AUTHORS CONTRIBUTING.md CREDITS README.md UNLICENSE VERSION etc/doap.ttl) + Dir.glob('lib/**/*.rb') 26 | gem.require_paths = %w(lib) 27 | 28 | gem.required_ruby_version = '>= 3.0' 29 | gem.requirements = ['libraptor2 (>= 2.0)'] 30 | gem.add_runtime_dependency 'ffi', '~> 1.15' 31 | gem.add_runtime_dependency 'rdf', '~> 3.3' 32 | gem.add_development_dependency 'yard' , '~> 0.9' 33 | gem.add_development_dependency 'rspec', '~> 3.12' 34 | gem.add_development_dependency 'rspec-its','~> 1.3' 35 | gem.add_development_dependency 'rdf-spec', '~> 3.2' 36 | gem.add_development_dependency 'rake', '~> 13.0' 37 | 38 | gem.post_install_message = <<-MESSAGE 39 | NOTE: RDF::Raptor requires libraptor2 to be installed on the system. Please see the Installation 40 | section in the README for more information on how to install it. 41 | MESSAGE 42 | end 43 | -------------------------------------------------------------------------------- /spec/format/graphviz_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), '../spec_helper') 2 | require 'rdf/spec/format' 3 | require 'rdf/spec/reader' 4 | require 'rdf/spec/writer' 5 | 6 | describe RDF::Raptor::Graphviz::Format do 7 | it_behaves_like 'an RDF::Format' do 8 | let(:format_class) {RDF::Raptor::Graphviz::Format} 9 | end 10 | 11 | it "should be discoverable" do 12 | formats = [ 13 | RDF::Format.for(:graphviz), 14 | RDF::Format.for("output.dot"), 15 | RDF::Format.for(file_name: "output.dot"), 16 | RDF::Format.for(file_extension: "dot"), 17 | RDF::Format.for(content_type: "text/vnd.graphviz"), 18 | ] 19 | formats.each { |format| expect(format).to eq(RDF::Raptor::Graphviz::Format) } 20 | end 21 | end 22 | 23 | describe RDF::Raptor::Graphviz::Writer do 24 | it_behaves_like 'an RDF::Writer' do 25 | let(:writer) {RDF::Raptor::Graphviz::Writer.new} 26 | end 27 | 28 | it "should return :graphviz for to_sym" do 29 | expect(described_class.to_sym).to eq(:graphviz) 30 | end 31 | 32 | it "should be discoverable" do 33 | writers = [ 34 | RDF::Writer.for(:graphviz), 35 | RDF::Writer.for("output.dot"), 36 | RDF::Writer.for(file_name: "output.dot"), 37 | RDF::Writer.for(file_extension: "dot"), 38 | RDF::Writer.for(content_type: "text/vnd.graphviz"), 39 | ] 40 | writers.each { |writer| expect(writer).to eq(RDF::Raptor::Graphviz::Writer) } 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/format/ntriples_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), '../spec_helper') 2 | require 'rdf/spec/format' 3 | require 'rdf/spec/reader' 4 | require 'rdf/spec/writer' 5 | 6 | describe RDF::Raptor::NTriples::Format do 7 | before(:each) do 8 | # Remove RDF::NTriples if loaded 9 | subclasses = RDF::Format.class_variable_get(:@@subclasses) 10 | if subclasses.map(&:to_s).include?("RDF::NTriples::Format") 11 | RDF::Format.class_variable_set(:@@subclasses, subclasses - [RDF::NTriples::Format]) 12 | RDF::Format.class_variable_get(:@@content_types).values.each {|v| v.delete(RDF::NTriples::Format)} 13 | RDF::Format.class_variable_get(:@@file_extensions).values.each {|v| v.delete(RDF::NTriples::Format)} 14 | end 15 | end 16 | 17 | it_behaves_like 'an RDF::Format' do 18 | let(:format_class) {RDF::Raptor::NTriples::Format} 19 | end 20 | 21 | it "should be discoverable" do 22 | formats = [ 23 | RDF::Format.for(:ntriples), 24 | RDF::Format.for("input.nt"), 25 | RDF::Format.for(file_name: "input.nt"), 26 | RDF::Format.for(file_extension: "nt"), 27 | RDF::Format.for(content_type: "text/plain"), 28 | RDF::Format.for(content_type: "application/n-triples"), 29 | ] 30 | formats.each { |format| expect(format).to eq(described_class) } 31 | end 32 | 33 | describe ".detect" do 34 | { 35 | ntriples: " .", 36 | literal: ' "literal" .', 37 | multi_line: %(\n \n "literal"\n .), 38 | }.each do |sym, str| 39 | it "detects #{sym}" do 40 | expect(described_class.detect(str)).to be true 41 | end 42 | end 43 | 44 | { 45 | nquads: " . ", 46 | nq_literal: ' "literal" .', 47 | nq_multi_line: %(\n \n "literal"\n \n .), 48 | turtle: "@prefix foo: .\n foo:a foo:b .", 49 | trig: "{ .}", 50 | rdfxml: '', 51 | n3: '@prefix foo: .\nfoo:bar = { } .', 52 | }.each do |sym, str| 53 | it "does not detect #{sym}" do 54 | expect(described_class.detect(str)).to be false 55 | end 56 | end 57 | end 58 | end 59 | 60 | describe RDF::Raptor::NTriples::Reader do 61 | before(:each) do 62 | # Remove RDF::NTriples if loaded 63 | subclasses = RDF::Format.class_variable_get(:@@subclasses) 64 | if subclasses.map(&:to_s).include?("RDF::NTriples::Format") 65 | RDF::Format.class_variable_set(:@@subclasses, subclasses - [RDF::NTriples::Format]) 66 | RDF::Format.class_variable_get(:@@content_types).values.each {|v| v.delete(RDF::NTriples::Format)} 67 | RDF::Format.class_variable_get(:@@file_extensions).values.each {|v| v.delete(RDF::NTriples::Format)} 68 | end 69 | end 70 | 71 | let(:reader) {RDF::Raptor::NTriples::Reader.new(reader_input)} 72 | let(:reader_input) {%q( 73 | . 74 | "RDF.rb" . 75 | )} 76 | let(:reader_count) {2} 77 | it_behaves_like 'an RDF::Reader' do 78 | around(:each) do |example| 79 | pending("validation") if example.description.include?('invalidates given invalid input and validate: true') 80 | example.run 81 | end 82 | end 83 | 84 | it "should return :ntriples for to_sym" do 85 | expect(described_class.to_sym).to eq(:ntriples) 86 | end 87 | 88 | it "should be discoverable" do 89 | readers = [ 90 | RDF::Reader.for(:ntriples), 91 | RDF::Reader.for("input.nt"), 92 | RDF::Reader.for(file_name: "input.nt"), 93 | RDF::Reader.for(file_extension: "nt"), 94 | RDF::Reader.for(content_type: "text/plain"), 95 | RDF::Reader.for(content_type: "application/n-triples"), 96 | ] 97 | readers.each { |reader| expect(reader).to eq(RDF::Raptor::NTriples::Reader) } 98 | end 99 | 100 | it 'should yield statements' do 101 | inner = double("inner") 102 | expect(inner).to receive(:called).with(RDF::Statement).twice 103 | reader.each_statement do |statement| 104 | inner.called(statement.class) 105 | end 106 | end 107 | 108 | it 'should yield raw statements' do 109 | reader.each_statement(raw: true) do |statement| 110 | expect(statement).to be_a RDF::Raptor::FFI::V2::Statement 111 | end 112 | end 113 | 114 | it "should yield triples" do 115 | inner = double("inner") 116 | expect(inner).to receive(:called).with(RDF::URI, RDF::URI, RDF::URI).once 117 | expect(inner).to receive(:called).with(RDF::URI, RDF::URI, RDF::Literal).once 118 | reader.each_triple do |subject, predicate, object| 119 | inner.called(subject.class, predicate.class, object.class) 120 | end 121 | end 122 | 123 | it "should open and parse a file" do 124 | RDF::Reader.open(File.expand_path("../../../etc/doap.nt", __FILE__)) do |reader| 125 | expect(reader).to be_a subject.class 126 | expect(reader.count).to be > 0 127 | end 128 | end 129 | 130 | it "should parse a URI" do 131 | reader = RDF::Raptor::NTriples::Reader.new 132 | result = reader.parse("http://dbpedia.org/data/Michael_Jackson.ntriples") 133 | expect(result).to eq(0) 134 | end 135 | 136 | it "should parse a String" do 137 | reader = RDF::Raptor::NTriples::Reader.new 138 | result = reader.parse(reader_input) 139 | expect(result).to eq(0) 140 | end 141 | end 142 | 143 | describe RDF::Raptor::NTriples::Writer do 144 | before(:each) do 145 | # Remove RDF::NTriples if loaded 146 | subclasses = RDF::Format.class_variable_get(:@@subclasses) 147 | if subclasses.map(&:to_s).include?("RDF::NTriples::Format") 148 | RDF::Format.class_variable_set(:@@subclasses, subclasses - [RDF::NTriples::Format]) 149 | RDF::Format.class_variable_get(:@@content_types).values.each {|v| v.delete(RDF::NTriples::Format)} 150 | RDF::Format.class_variable_get(:@@file_extensions).values.each {|v| v.delete(RDF::NTriples::Format)} 151 | RDF.send(:remove_constant, :NTriples) 152 | end 153 | end 154 | 155 | it_behaves_like 'an RDF::Writer' do 156 | let(:writer) {RDF::Raptor::NTriples::Writer.new} 157 | end 158 | 159 | it "should return :ntriples for to_sym" do 160 | expect(described_class.to_sym).to eq(:ntriples) 161 | end 162 | 163 | it "should be discoverable" do 164 | writers = [ 165 | RDF::Writer.for(:ntriples), 166 | RDF::Writer.for("output.nt"), 167 | RDF::Writer.for(file_name: "output.nt"), 168 | RDF::Writer.for(file_extension: "nt"), 169 | RDF::Writer.for(content_type: "text/plain"), 170 | RDF::Writer.for(content_type: "application/n-triples"), 171 | ] 172 | writers.each { |writer| expect(writer).to eq(RDF::Raptor::NTriples::Writer) } 173 | end 174 | end 175 | 176 | describe RDF::Raptor::NTriples do 177 | let(:reader) {RDF::Raptor::NTriples::Reader} 178 | let(:writer) {RDF::Raptor::NTriples::Writer} 179 | let(:statement) { 180 | RDF::Statement.new( 181 | RDF::URI("https://rubygems.org/gems/rdf"), 182 | RDF::URI("http://purl.org/dc/terms/creator"), 183 | RDF::URI("https://ar.to/#self")) 184 | } 185 | let(:stmt_string) {" .\n"} 186 | let(:graph) {RDF::Graph.new {|g| g << statement}} 187 | 188 | context "when writing" do 189 | =begin 190 | it "should correctly format statements" do 191 | writer.new.format_statement(statement).should == stmt_string 192 | end 193 | 194 | context "should correctly format blank nodes" do 195 | specify {writer.new.format_node(RDF::Node.new('foobar')).should == '_:foobar'} 196 | specify {writer.new.format_node(RDF::Node.new('')).should_not == '_:'} 197 | end 198 | 199 | it "should correctly format URI references" do 200 | writer.new.format_uri(RDF::URI('https://rubgems.org/gems/rdf/')).should == '' 201 | end 202 | 203 | it "should correctly format plain literals" do 204 | writer.new.format_literal(RDF::Literal.new('Hello, world!')).should == '"Hello, world!"' 205 | end 206 | 207 | it "should correctly format language-tagged literals" do 208 | writer.new.format_literal(RDF::Literal.new('Hello, world!', language: :en)).should == '"Hello, world!"@en' 209 | end 210 | 211 | it "should correctly format datatyped literals" do 212 | writer.new.format_literal(RDF::Literal.new(3.1415)).should == '"3.1415"^^' 213 | end 214 | =end 215 | 216 | it "should output statements to a string buffer" do 217 | output = writer.buffer { |w| w << statement } 218 | expect(output).to eq(stmt_string) 219 | end 220 | 221 | it "should dump statements to a string buffer" do 222 | output = StringIO.new 223 | writer.dump(graph, output) 224 | expect(output.string).to eq(stmt_string) 225 | end 226 | 227 | it "should dump arrays of statements to a string buffer" do 228 | output = StringIO.new 229 | writer.dump(graph.to_a, output) 230 | expect(output.string).to eq(stmt_string) 231 | end 232 | 233 | it "should dump statements to a file" do 234 | require 'tmpdir' # for Dir.tmpdir 235 | writer.dump(graph, filename = File.join(Dir.tmpdir, "test.nt")) 236 | expect(File.read(filename)).to eq(stmt_string) 237 | File.unlink(filename) 238 | end 239 | end 240 | end 241 | -------------------------------------------------------------------------------- /spec/format/rdfa_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), '../spec_helper') 2 | require 'rdf/spec/format' 3 | require 'rdf/spec/reader' 4 | require 'rdf/spec/writer' 5 | 6 | describe RDF::Raptor::RDFa::Format do 7 | it_behaves_like 'an RDF::Format' do 8 | let(:format_class) {RDF::Raptor::RDFa::Format} 9 | end 10 | 11 | it "should be discoverable" do 12 | formats = [ 13 | RDF::Format.for(:rdfa), 14 | RDF::Format.for("input.html"), 15 | RDF::Format.for(file_name: "input.html"), 16 | RDF::Format.for(file_extension: "html"), 17 | RDF::Format.for(content_type: "application/xhtml+xml"), 18 | ] 19 | formats.each { |format| expect(format).to eq(RDF::Raptor::RDFa::Format) } 20 | end 21 | end 22 | 23 | describe RDF::Raptor::RDFa::Reader do 24 | let!(:doap) {File.expand_path("../../../etc/doap.html", __FILE__)} 25 | let!(:doap_count) {27} 26 | 27 | # @see lib/rdf/spec/reader.rb in rdf-spec 28 | #it_behaves_like 'an RDF::Reader', skip: "Travis reports wrong statement" do 29 | # let(:reader) {RDF::Raptor::RDFa::Reader.new(@reader_input)} 30 | # let(:reader_input) {File.read(doap)} 31 | # let(:reader_count) {doap_count} 32 | #end 33 | 34 | it "should return :rdfa for to_sym" do 35 | expect(described_class.to_sym).to eq(:rdfa) 36 | end 37 | 38 | it "should be discoverable" do 39 | readers = [ 40 | RDF::Reader.for(:rdfa), 41 | RDF::Reader.for("input.html"), 42 | RDF::Reader.for(file_name: "input.html"), 43 | RDF::Reader.for(file_extension: "html"), 44 | RDF::Reader.for(content_type: "application/xhtml+xml"), 45 | ] 46 | readers.each { |reader| expect(reader).to eq(RDF::Raptor::RDFa::Reader) } 47 | end 48 | 49 | context "when opening and parsing a file" do 50 | let(:reader) { RDF::Reader.open("etc/doap.html") } 51 | after { reader.close } 52 | 53 | specify { expect(reader).to be_a subject.class } 54 | specify { expect(reader.statements.to_a).to_not be_empty } 55 | 56 | it "reads xml namespaces" do 57 | reader.statements.to_a 58 | expect(reader.prefixes[:foaf]).to eq(RDF::URI("http://xmlns.com/foaf/0.1/")) 59 | end 60 | 61 | it "reads HTML5 prefixes" do 62 | pending 'libraptor does not support prefixes for HTML 5 + RDFa 1.1' 63 | expect(reader.prefixes[:doap]).to eq(RDF::URI("http://usefulinc.com/ns/doap#")) 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /spec/format/rdfxml_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), '../spec_helper') 2 | require 'rdf/spec/format' 3 | require 'rdf/spec/reader' 4 | require 'rdf/spec/writer' 5 | 6 | describe RDF::Raptor::RDFXML::Format do 7 | it_behaves_like 'an RDF::Format' do 8 | let(:format_class) {RDF::Raptor::RDFXML::Format} 9 | end 10 | 11 | it "should be discoverable" do 12 | formats = [ 13 | RDF::Format.for(:rdfxml), 14 | RDF::Format.for("input.rdf"), 15 | RDF::Format.for(file_name: "input.rdf"), 16 | RDF::Format.for(file_extension: "rdf"), 17 | RDF::Format.for(content_type: "application/rdf+xml"), 18 | ] 19 | formats.each { |format| expect(format).to eq(RDF::Raptor::RDFXML::Format) } 20 | end 21 | end 22 | 23 | describe RDF::Raptor::RDFXML::Reader do 24 | let!(:doap) {File.expand_path("../../../etc/doap.xml", __FILE__)} 25 | let!(:doap_count) {41} 26 | 27 | it_behaves_like 'an RDF::Reader' do 28 | around(:each) do |example| 29 | pending("validation") if example.description.include?('invalidates given invalid input and validate: true') 30 | example.run 31 | end 32 | let(:reader) {RDF::Raptor::RDFXML::Reader.new} 33 | let(:reader_input) {File.read(doap)} 34 | let(:reader_count) {doap_count} 35 | end 36 | 37 | it "should be discoverable" do 38 | readers = [ 39 | RDF::Reader.for(:rdfxml), 40 | RDF::Reader.for("input.rdf"), 41 | RDF::Reader.for(file_name: "input.rdf"), 42 | RDF::Reader.for(file_extension: "rdf"), 43 | RDF::Reader.for(content_type: "application/rdf+xml"), 44 | ] 45 | readers.each { |reader| expect(reader).to eq(RDF::Raptor::RDFXML::Reader) } 46 | end 47 | 48 | context 'interface' do 49 | subject { 50 | RDF::Raptor::RDFXML::Reader.new(%q( 51 | 53 | 54 | 55 | foo 56 | 57 | )) 58 | } 59 | 60 | it "should return reader" do 61 | is_expected.to be_a(RDF::Raptor::RDFXML::Reader) 62 | end 63 | 64 | it "should yield statements" do 65 | inner = double("inner") 66 | expect(inner).to receive(:called).with(RDF::Statement).exactly(4).times 67 | subject.each_statement do |statement| 68 | inner.called(statement.class) 69 | end 70 | end 71 | 72 | it "should yield triples" do 73 | inner = double("inner") 74 | expect(inner).to receive(:called).with(RDF::URI, RDF::URI, RDF::URI).twice 75 | expect(inner).to receive(:called).with(RDF::URI, RDF::URI, RDF::Literal).twice 76 | subject.each_triple do |subject, predicate, object| 77 | inner.called(subject.class, predicate.class, object.class) 78 | end 79 | end 80 | 81 | it "reads prefixes" do 82 | subject.each_triple.map {} 83 | expect(subject.prefixes[:rdf]).to eq(RDF) 84 | expect(subject.prefixes[:ex]).to eq(RDF::URI.new('http://www.example.org/')) 85 | end 86 | 87 | it "opens and parses a file" do 88 | RDF::Reader.open("etc/doap.xml") do |reader| 89 | expect(reader).to be_a subject.class 90 | expect(reader.statements.count).to_not be_zero 91 | expect(reader.prefixes[:doap]).to eq(RDF::URI("http://usefulinc.com/ns/doap#")) 92 | end 93 | end 94 | end 95 | end 96 | 97 | describe RDF::Raptor::RDFXML::Writer do 98 | it_behaves_like 'an RDF::Writer' do 99 | let(:writer) {RDF::Raptor::RDFXML::Writer.new} 100 | end 101 | 102 | it "should be discoverable" do 103 | writers = [ 104 | RDF::Writer.for(:rdfxml), 105 | RDF::Writer.for("output.rdf"), 106 | RDF::Writer.for(file_name: "output.rdf"), 107 | RDF::Writer.for(file_extension: "rdf"), 108 | RDF::Writer.for(content_type: "application/rdf+xml"), 109 | ] 110 | writers.each { |writer| expect(writer).to eq(RDF::Raptor::RDFXML::Writer) } 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /spec/format/turtle_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), '../spec_helper') 2 | require 'rdf/spec/format' 3 | require 'rdf/spec/reader' 4 | require 'rdf/spec/writer' 5 | 6 | describe RDF::Raptor::Turtle::Format do 7 | it_behaves_like 'an RDF::Format' do 8 | let(:format_class) {RDF::Raptor::Turtle::Format} 9 | end 10 | 11 | it "should be discoverable" do 12 | formats = [ 13 | RDF::Format.for(:turtle), 14 | RDF::Format.for("input.ttl"), 15 | RDF::Format.for(file_name: "input.ttl"), 16 | RDF::Format.for(file_extension: "ttl"), 17 | RDF::Format.for(content_type: "text/turtle"), 18 | ] 19 | formats.each { |format| expect(format).to eq(RDF::Raptor::Turtle::Format) } 20 | end 21 | end 22 | 23 | describe RDF::Raptor::Turtle::Reader do 24 | let!(:doap) {File.expand_path("../../../etc/doap.ttl", __FILE__)} 25 | let!(:doap_count) {47} 26 | 27 | it_behaves_like 'an RDF::Reader' do 28 | around(:each) do |example| 29 | pending("validation") if example.description.include?('invalidates given invalid input and validate: true') 30 | example.run 31 | end 32 | let(:reader) {RDF::Raptor::Turtle::Reader.new(reader_input)} 33 | let(:reader_input) {File.read(doap)} 34 | let(:reader_count) {doap_count} 35 | end 36 | 37 | it "should return :turtle for to_sym" do 38 | expect(described_class.to_sym).to eq(:turtle) 39 | end 40 | 41 | it "should be discoverable" do 42 | readers = [ 43 | RDF::Reader.for(:turtle), 44 | RDF::Reader.for("input.ttl"), 45 | RDF::Reader.for(file_name: "input.ttl"), 46 | RDF::Reader.for(file_extension: "ttl"), 47 | RDF::Reader.for(content_type: "text/turtle"), 48 | ] 49 | readers.each { |reader| expect(reader).to eq(RDF::Raptor::Turtle::Reader) } 50 | end 51 | 52 | it "opens and parses a file" do 53 | RDF::Reader.open("etc/doap.ttl") do |reader| 54 | expect(reader).to be_a subject.class 55 | expect(reader.statements.count).to_not be_zero 56 | expect(reader.prefixes[:doap]).to eq(RDF::URI("http://usefulinc.com/ns/doap#")) 57 | end 58 | end 59 | end 60 | 61 | describe RDF::Raptor::Turtle::Writer do 62 | it_behaves_like 'an RDF::Writer' do 63 | let(:writer) {RDF::Raptor::Turtle::Writer.new} 64 | end 65 | 66 | it "should return :ttl for to_sym" do 67 | expect(described_class.to_sym).to eq(:turtle) 68 | end 69 | 70 | it "should be discoverable" do 71 | writers = [ 72 | RDF::Writer.for(:turtle), 73 | RDF::Writer.for("output.ttl"), 74 | RDF::Writer.for(file_name: "output.ttl"), 75 | RDF::Writer.for(file_extension: "ttl"), 76 | RDF::Writer.for(content_type: "text/turtle"), 77 | ] 78 | writers.each { |writer| expect(writer).to eq(RDF::Raptor::Turtle::Writer) } 79 | end 80 | 81 | it "should not use pname URIs without prefix" do 82 | input = %( .) 83 | serialize(input, nil, 84 | [%r(^\s+\s+ \.$)], 85 | prefixes: { } 86 | ) 87 | end 88 | 89 | it "should use pname URIs with prefix" do 90 | input = %( .) 91 | serialize(input, nil, 92 | [%r(^@prefix foaf: \.$), 93 | %r(^foaf:b\s+foaf:c\s+foaf:d \.$)], 94 | prefixes: { foaf: "http://xmlns.com/foaf/0.1/"} 95 | ) 96 | end 97 | 98 | def parse(input, **options) 99 | graph = RDF::Graph.new 100 | RDF::Raptor::Turtle::Reader.new(input, **options).each do |statement| 101 | graph << statement 102 | end 103 | graph 104 | end 105 | 106 | # Serialize ntstr to a string and compare against regexps 107 | def serialize(ntstr, base = nil, regexps = [], **options) 108 | prefixes = options[:prefixes] || {nil => ""} 109 | g = parse(ntstr, base_uri: base, prefixes: prefixes, validate: false) 110 | @debug = [] 111 | result = RDF::Raptor::Turtle::Writer.buffer(base_uri: base, prefixes: prefixes, **options) do |writer| 112 | writer << g 113 | end 114 | 115 | regexps.each do |re| 116 | expect(result).to match(re) 117 | end 118 | 119 | result 120 | end 121 | end -------------------------------------------------------------------------------- /spec/raptor_cli_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), 'spec_helper') 2 | ENV['RDF_RAPTOR_ENGINE'] = 'cli' 3 | require 'rdf/raptor/cli' 4 | require 'rdf/raptor/ffi' 5 | 6 | describe RDF::Raptor, cli: true do 7 | it 'should load the CLI engine' do 8 | #subject.ENGINE.should eql(:cli) 9 | expect(subject.included_modules).to include(RDF::Raptor::CLI) 10 | expect(subject.included_modules).not_to include(RDF::Raptor::FFI) 11 | end 12 | end 13 | 14 | describe RDF::Raptor::CLI, cli: true do 15 | it 'should return the libraptor version' do 16 | expect(RDF::Raptor.version).not_to be_nil 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/raptor_ffi_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), 'spec_helper') 2 | require 'rdf/raptor/ffi' 3 | require 'rdf/raptor/cli' 4 | 5 | describe RDF::Raptor do 6 | it 'should load the FFI engine' do 7 | #subject.ENGINE.should eql(:ffi) 8 | expect(subject.included_modules).to include(RDF::Raptor::FFI) 9 | expect(subject.included_modules).not_to include(RDF::Raptor::CLI) 10 | end 11 | end 12 | 13 | describe RDF::Raptor::FFI do 14 | it 'should return the libraptor version' do 15 | expect(RDF::Raptor.version).not_to be_nil 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/raptor_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), 'spec_helper') 2 | 3 | describe RDF::Raptor do 4 | it 'should return the libraptor version' do 5 | expect(subject.version).not_to be_nil 6 | end 7 | 8 | it 'should be available' do 9 | expect(subject.available?).to be true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | 3 | begin 4 | require 'pry' 5 | require 'pry-byebug' 6 | rescue LoadError 7 | end 8 | 9 | begin 10 | require 'simplecov' 11 | require 'simplecov-lcov' 12 | 13 | SimpleCov::Formatter::LcovFormatter.config do |config| 14 | #Coveralls is coverage by default/lcov. Send info results 15 | config.report_with_single_file = true 16 | config.single_report_path = 'coverage/lcov.info' 17 | end 18 | 19 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ 20 | SimpleCov::Formatter::HTMLFormatter, 21 | SimpleCov::Formatter::LcovFormatter 22 | ]) 23 | SimpleCov.start do 24 | add_filter "/spec/" 25 | end 26 | rescue LoadError => e 27 | STDERR.puts "Coverage Skipped: #{e.message}" 28 | end 29 | 30 | require 'rdf/raptor' 31 | require 'rdf/spec' 32 | require 'rdf/spec/matchers' 33 | 34 | RSpec.configure do |config| 35 | config.include(RDF::Spec::Matchers) 36 | config.exclusion_filter = {ruby: lambda { |version| 37 | RUBY_VERSION.to_s !~ /^#{version}/ 38 | }} 39 | 40 | unless ENV['RDF_RAPTOR_ENGINE'] == 'cli' 41 | config.filter_run_excluding cli: true 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/version_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), 'spec_helper') 2 | 3 | describe 'RDF::Raptor::VERSION' do 4 | it "should match the VERSION file" do 5 | expect(RDF::Raptor::VERSION.to_s).to eq(File.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).chomp) 6 | end 7 | end 8 | --------------------------------------------------------------------------------