active-triples 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.travis.yml +12 -0
- data/AUTHORS +2 -0
- data/Gemfile +3 -0
- data/LICENSE +24 -0
- data/README.md +38 -0
- data/active-triples.gemspec +35 -0
- data/lib/active_triples.rb +29 -0
- data/lib/active_triples/configurable.rb +61 -0
- data/lib/active_triples/indexing.rb +76 -0
- data/lib/active_triples/list.rb +155 -0
- data/lib/active_triples/nested_attributes.rb +132 -0
- data/lib/active_triples/node_config.rb +56 -0
- data/lib/active_triples/properties.rb +96 -0
- data/lib/active_triples/repositories.rb +36 -0
- data/lib/active_triples/resource.rb +369 -0
- data/lib/active_triples/term.rb +188 -0
- data/lib/active_triples/version.rb +3 -0
- data/spec/rdf_list_spec.rb +183 -0
- data/spec/rdf_nested_attributes_spec.rb +183 -0
- data/spec/rdf_properties_spec.rb +81 -0
- data/spec/rdf_repositories_spec.rb +28 -0
- data/spec/rdf_resource_spec.rb +447 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/active_model_lint.rb +81 -0
- metadata +197 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e1dcbe5917c46ab351078d1c6e7b2be54d66c68f
|
4
|
+
data.tar.gz: 5cc8dfc6dfbba4a833566f568d395a15ba79c535
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 17d3134eb50d437947dada9a3eadd4b298968c2fef571e25796de38f4b6de48b3eef9935fcfd9c0a5cd9089570ddd4f4fdcd40a9e1a499d5521026e1d8de020b
|
7
|
+
data.tar.gz: 6beee86d363f0791be42dbd5507a6dad99fa3e7ceb2beedacd39d704ee63ade58dba85fec512d0f5d25fdbf60088844b7aaeb9b3917a70ef697d020193782c9f
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/AUTHORS
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
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 <http://unlicense.org/>
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
ActiveTriples
|
2
|
+
==============
|
3
|
+
|
4
|
+
[![Build Status](https://travis-ci.org/no-reply/ActiveTriples.png?branch=master)](https://travis-ci.org/no-reply/ActiveTriples)
|
5
|
+
|
6
|
+
Modeling RDF graphs in Ruby.
|
7
|
+
|
8
|
+
How to Use
|
9
|
+
-----------
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
class Thing < ActiveTriples::Resource
|
13
|
+
configure :type => RDF::OWL.Thing, :base_uri => 'http://example.org/things#'
|
14
|
+
property :title, :predicate => RDF::DC.title
|
15
|
+
property :description, :predicate => RDF::DC.description
|
16
|
+
property :creator, :predicate => RDF::DC.creator, :class_name => 'Person'
|
17
|
+
end
|
18
|
+
|
19
|
+
obj = Thing.new('123')
|
20
|
+
obj.title = 'Resource'
|
21
|
+
obj.description = 'A resource in an RDF graph.'
|
22
|
+
|
23
|
+
obj.dump :ntriples
|
24
|
+
# => "<http://example.org/things#123> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Thing> .\n<http://example.org/things#123> <http://purl.org/dc/terms/title> \"Resource\" .\n<http://example.org/things#123> <http://purl.org/dc/terms/description> \"A resource in an RDF graph.\" .\n"
|
25
|
+
|
26
|
+
class Person < ActiveTriples::Resource
|
27
|
+
configure :type => RDF::FOAF.Person, :base_uri => 'http://example.org/people#'
|
28
|
+
property :name, :predicate => RDF::FOAF.name
|
29
|
+
end
|
30
|
+
|
31
|
+
obj.creator = Person.new
|
32
|
+
obj.creator
|
33
|
+
# => [#<Person:0x3fbe84ac9234(default)>]
|
34
|
+
|
35
|
+
obj.creator.first.name = 'Herman Melville'
|
36
|
+
obj.dump :ntriples
|
37
|
+
# => "<http://example.org/things#123> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Thing> .\n<http://example.org/things#123> <http://purl.org/dc/terms/title> \"Resource\" .\n<http://example.org/things#123> <http://purl.org/dc/terms/description> \"A resource in an RDF graph.\" .\n<http://example.org/things#123> <http://purl.org/dc/terms/creator> _:g70087502237940 .\n_:g70087502237940 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> .\n"
|
38
|
+
```
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "active_triples/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "active-triples"
|
7
|
+
s.version = ActiveTriples::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Tom Johnson", "Trey Terrell"]
|
10
|
+
s.homepage = 'https://github.com/no-reply/ActiveTriples'
|
11
|
+
s.email = 'thomas.johnson@oregonstate.edu'
|
12
|
+
s.summary = %q{RDF graphs in ActiveModel wrappers.}
|
13
|
+
s.description = %q{ActiveTriples provides tools for modeling RDF as discrete resources.}
|
14
|
+
s.license = "Public Domain"
|
15
|
+
s.required_ruby_version = '>= 1.9.3'
|
16
|
+
|
17
|
+
s.add_dependency('rdf', '~> 1.1')
|
18
|
+
s.add_dependency('linkeddata', '~> 1.1')
|
19
|
+
s.add_dependency('activemodel')
|
20
|
+
s.add_dependency('deprecation', '~> 0.1')
|
21
|
+
s.add_dependency('activesupport')
|
22
|
+
|
23
|
+
s.add_development_dependency('rdoc')
|
24
|
+
s.add_development_dependency('rspec')
|
25
|
+
s.add_development_dependency('webmock')
|
26
|
+
s.add_development_dependency('nokogiri')
|
27
|
+
|
28
|
+
s.files = `git ls-files`.split("\n")
|
29
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
30
|
+
|
31
|
+
s.extra_rdoc_files = [
|
32
|
+
"LICENSE",
|
33
|
+
"README.md"
|
34
|
+
]
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rdf'
|
2
|
+
require 'active_triples/version'
|
3
|
+
|
4
|
+
module ActiveTriples
|
5
|
+
autoload :Resource, 'active_triples/resource'
|
6
|
+
autoload :List, 'active_triples/list'
|
7
|
+
autoload :Term, 'active_triples/term'
|
8
|
+
autoload :Indexing, 'active_triples/indexing'
|
9
|
+
autoload :Configurable, 'active_triples/configurable'
|
10
|
+
autoload :Properties, 'active_triples/properties'
|
11
|
+
autoload :Repositories, 'active_triples/repositories'
|
12
|
+
autoload :NodeConfig, 'active_triples/node_config'
|
13
|
+
autoload :NestedAttributes, 'active_triples/nested_attributes'
|
14
|
+
|
15
|
+
def self.class_from_string(class_name, container_class=Kernel)
|
16
|
+
container_class = container_class.name if container_class.is_a? Module
|
17
|
+
container_parts = container_class.split('::')
|
18
|
+
(container_parts + class_name.split('::')).flatten.inject(Kernel) do |mod, class_name|
|
19
|
+
if mod == Kernel
|
20
|
+
Object.const_get(class_name)
|
21
|
+
elsif mod.const_defined? class_name.to_sym
|
22
|
+
mod.const_get(class_name)
|
23
|
+
else
|
24
|
+
container_parts.pop
|
25
|
+
class_from_string(class_name, container_parts.join('::'))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'deprecation'
|
2
|
+
|
3
|
+
module ActiveTriples
|
4
|
+
##
|
5
|
+
# Module to include configurable class-wide properties common to
|
6
|
+
# Resource and RDFDatastream. It does its work at the class level,
|
7
|
+
# and is meant to be extended.
|
8
|
+
#
|
9
|
+
# Define properties at the class level with:
|
10
|
+
#
|
11
|
+
# configure base_uri: "http://oregondigital.org/resource/", repository: :parent
|
12
|
+
# Available properties are base_uri, rdf_label, type, and repository
|
13
|
+
module Configurable
|
14
|
+
extend Deprecation
|
15
|
+
|
16
|
+
def base_uri
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def rdf_label
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def type
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def rdf_type(value)
|
29
|
+
Deprecation.warn Configurable, "rdf_type is deprecated and will be removed in active-fedora 8.0.0. Use configure type: instead.", caller
|
30
|
+
configure type: value
|
31
|
+
end
|
32
|
+
|
33
|
+
def repository
|
34
|
+
:parent
|
35
|
+
end
|
36
|
+
|
37
|
+
# API method for configuring class properties an RDF Resource may need.
|
38
|
+
# This is an alternative to overriding the methods extended with this module.
|
39
|
+
def configure(options = {})
|
40
|
+
{
|
41
|
+
base_uri: options[:base_uri],
|
42
|
+
rdf_label: options[:rdf_label],
|
43
|
+
type: options[:type],
|
44
|
+
repository: options[:repository]
|
45
|
+
}.each do |name, value|
|
46
|
+
if value
|
47
|
+
value = self.send("transform_#{name}", value) if self.respond_to?("transform_#{name}")
|
48
|
+
define_singleton_method(name) do
|
49
|
+
value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def transform_type(value)
|
56
|
+
RDF::URI.new(value).tap do |value|
|
57
|
+
Resource.type_registry[value] = self
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module ActiveFedora
|
2
|
+
module Rdf
|
3
|
+
module Indexing
|
4
|
+
extend Deprecation
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# In active_fedora 8, we can get the prefix part from Datastream.prefix
|
8
|
+
def apply_prefix(name)
|
9
|
+
"#{dsid.underscore}__#{name}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def prefix(name)
|
13
|
+
Deprecation.warn Indexing, "prefix is deprecated. Use apply_prefix instead. In active-fedora 8, the prefix method will just return the prefix to be applied, and will not do the applying. This will enable conformity between OmDatastream and RdfDatastream"
|
14
|
+
apply_prefix(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_solr(solr_doc = Hash.new) # :nodoc:
|
18
|
+
fields.each do |field_key, field_info|
|
19
|
+
values = resource.get_values(field_key)
|
20
|
+
Array(values).each do |val|
|
21
|
+
if val.kind_of? RDF::URI
|
22
|
+
val = val.to_s
|
23
|
+
elsif val.kind_of? Rdf::Resource
|
24
|
+
val = val.solrize
|
25
|
+
end
|
26
|
+
self.class.create_and_insert_terms(apply_prefix(field_key), val, field_info[:behaviors], solr_doc)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
solr_doc
|
30
|
+
end
|
31
|
+
|
32
|
+
# Gives the primary solr name for a column. If there is more than one indexer on the field definition, it gives the first
|
33
|
+
def primary_solr_name(field)
|
34
|
+
config = self.class.config_for_term_or_uri(field)
|
35
|
+
return nil unless config # punt on index names for deep nodes!
|
36
|
+
if behaviors = config.behaviors
|
37
|
+
behaviors.each do |behavior|
|
38
|
+
result = ActiveFedora::SolrService.solr_name(apply_prefix(field), behavior, type: config.type)
|
39
|
+
return result if Solrizer::DefaultDescriptors.send(behavior).evaluate_suffix(:text).stored?
|
40
|
+
end
|
41
|
+
raise RuntimeError "no stored fields were found"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module ClassMethods
|
46
|
+
def prefix(dsid, name)
|
47
|
+
Deprecation.warn Indexing, "prefix is deprecated and will be removed in active-fedora 8.0.0.", caller
|
48
|
+
"#{dsid.underscore}__#{name}".to_sym
|
49
|
+
end
|
50
|
+
|
51
|
+
# Gives the datatype for a column.
|
52
|
+
def type(field)
|
53
|
+
config_for_term_or_uri(field).type
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
# returns a Hash, e.g.: {field => {:values => [], :type => :something, :behaviors => []}, ...}
|
59
|
+
def fields
|
60
|
+
field_map = {}.with_indifferent_access
|
61
|
+
|
62
|
+
self.class.properties.each do |name, config|
|
63
|
+
type = config[:type]
|
64
|
+
behaviors = config[:behaviors]
|
65
|
+
next unless type and behaviors
|
66
|
+
next if config[:class_name] && config[:class_name] < ActiveFedora::Base
|
67
|
+
resource.query(:subject => rdf_subject, :predicate => config[:predicate]).each_statement do |statement|
|
68
|
+
field_map[name] ||= {:values => [], :type => type, :behaviors => behaviors}
|
69
|
+
field_map[name][:values] << statement.object.to_s
|
70
|
+
end
|
71
|
+
end
|
72
|
+
field_map
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module ActiveTriples
|
2
|
+
##
|
3
|
+
# An implementation of RDF::List intregrated with ActiveTriples.
|
4
|
+
#
|
5
|
+
# A thoughtful reflection period is encouraged before using the
|
6
|
+
# rdf:List concept in your data. The community may pursue other
|
7
|
+
# options for ordered sets.
|
8
|
+
class List < RDF::List
|
9
|
+
include ActiveTriples::NestedAttributes
|
10
|
+
extend Configurable
|
11
|
+
extend Properties
|
12
|
+
|
13
|
+
delegate :rdf_subject, :mark_for_destruction, :marked_for_destruction?, :set_value, :get_values, :parent, :dump, :attributes=, to: :resource
|
14
|
+
alias_method :to_ary, :to_a
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def from_uri(uri, vals)
|
18
|
+
list = ListResource.from_uri(uri, vals)
|
19
|
+
self.new(list.rdf_subject, list)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def resource
|
24
|
+
graph
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(*args)
|
28
|
+
super
|
29
|
+
parent = graph.parent if graph.respond_to? :parent
|
30
|
+
@graph = ListResource.new(subject) << graph unless graph.kind_of? Resource
|
31
|
+
graph << parent if parent
|
32
|
+
graph.list = self
|
33
|
+
graph.singleton_class.properties = self.class.properties
|
34
|
+
graph.singleton_class.properties.keys.each do |property|
|
35
|
+
graph.singleton_class.send(:register_property, property)
|
36
|
+
end
|
37
|
+
graph.insert RDF::Statement.new(subject, RDF.type, RDF.List)
|
38
|
+
graph.reload
|
39
|
+
end
|
40
|
+
|
41
|
+
def []=(idx, value)
|
42
|
+
raise IndexError "index #{idx} too small for array: minimum 0" if idx < 0
|
43
|
+
|
44
|
+
if idx >= length
|
45
|
+
(idx - length).times do
|
46
|
+
self << RDF::OWL.Nothing
|
47
|
+
end
|
48
|
+
return self << value
|
49
|
+
end
|
50
|
+
each_subject.with_index do |v, i|
|
51
|
+
next unless i == idx
|
52
|
+
resource.set_value(v, RDF.first, value)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Override to return AF::Rdf::Resources as values, where
|
58
|
+
# appropriate.
|
59
|
+
def each(&block)
|
60
|
+
return super unless block_given?
|
61
|
+
|
62
|
+
super do |value|
|
63
|
+
block.call(node_from_value(value))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Do these like #each.
|
69
|
+
def first
|
70
|
+
node_from_value(super)
|
71
|
+
end
|
72
|
+
|
73
|
+
def shift
|
74
|
+
node_from_value(super)
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Find an AF::Rdf::Resource from the value returned by RDF::List
|
79
|
+
def node_from_value(value)
|
80
|
+
if value.kind_of? RDF::Resource
|
81
|
+
type_uri = resource.query([value, RDF.type, nil]).to_a.first.try(:object)
|
82
|
+
klass = ActiveTriples::Resource.type_registry[type_uri]
|
83
|
+
klass ||= Resource
|
84
|
+
return klass.from_uri(value,resource)
|
85
|
+
end
|
86
|
+
value
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# This class is the graph/Resource that backs the List and
|
91
|
+
# supplies integration with the rest of ActiveTriples
|
92
|
+
class ListResource < Resource
|
93
|
+
attr_reader :list
|
94
|
+
|
95
|
+
def list=(list)
|
96
|
+
@list ||= list
|
97
|
+
end
|
98
|
+
|
99
|
+
def attributes=(values)
|
100
|
+
raise ArgumentError, "values must be a Hash, you provided #{values.class}" unless values.kind_of? Hash
|
101
|
+
values.with_indifferent_access.each do |key, value|
|
102
|
+
if self.singleton_class.properties.keys.map{ |k| "#{k}_attributes"}.include?(key)
|
103
|
+
klass = properties[key[0..-12]]['class_name']
|
104
|
+
klass = ActiveTriples.class_from_string(klass, final_parent.class) if klass.is_a? String
|
105
|
+
value.is_a?(Hash) ? attributes_hash_to_list(values[key], klass) : attributes_to_list(value, klass)
|
106
|
+
values.delete key
|
107
|
+
end
|
108
|
+
end
|
109
|
+
persist!
|
110
|
+
super
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
def attributes_to_list(value, klass)
|
115
|
+
value.each do |entry|
|
116
|
+
item = klass.new()
|
117
|
+
item.attributes = entry
|
118
|
+
list << item
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def attributes_hash_to_list(value, klass)
|
123
|
+
value.each do |counter, attr|
|
124
|
+
item = klass.new()
|
125
|
+
item.attributes = attr if attr
|
126
|
+
list[counter.to_i] = item
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Monkey patch to allow lists to have subject URIs.
|
133
|
+
# Overrides RDF::List to prevent URI subjects
|
134
|
+
# from being replaced with nodes.
|
135
|
+
#
|
136
|
+
# @NOTE Lists built this way will return false for #valid?
|
137
|
+
def <<(value)
|
138
|
+
value = case value
|
139
|
+
when nil then RDF.nil
|
140
|
+
when RDF::Value then value
|
141
|
+
when Array then RDF::List.new(nil, graph, value)
|
142
|
+
else value
|
143
|
+
end
|
144
|
+
|
145
|
+
if empty?
|
146
|
+
resource.set_value(RDF.first, value)
|
147
|
+
resource.insert([subject, RDF.rest, RDF.nil])
|
148
|
+
resource << value if value.kind_of? Resource
|
149
|
+
return self
|
150
|
+
end
|
151
|
+
super
|
152
|
+
resource << value if value.kind_of? Resource
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|