changesets 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ CHANGESET.RB
2
+ ------------
3
+
4
+ A plugin for RDF.rb that provides support for creating Changesets that describe changes to an RDF
5
+ graph[1].
6
+
7
+ Changesets can be serialized as RDF and then applied to a triple store. Changesets are currently supported
8
+ by Talis Platform stores and Kasabi[0] datasets.
9
+
10
+ AUTHOR
11
+ ------
12
+
13
+ Leigh Dodds (ld@kasabi.com)
14
+
15
+ INSTALLATION
16
+ ------------
17
+
18
+ Changesets.rb is packaged as a Ruby Gem and can be installed as follows:
19
+
20
+ sudo gem install changesets
21
+
22
+ The source for the project is maintained in github at:
23
+
24
+ http://github.com/ldodds/changesets.rb
25
+
26
+ USAGE
27
+ -----
28
+
29
+ The test suite provides some simple examples of how to construct changesets.
30
+
31
+ [0]: [http://kasabi.com]
32
+ [1]: [http://vocab.org/changeset/schema.html]
@@ -0,0 +1,60 @@
1
+ require 'rake'
2
+
3
+ require 'rake/gempackagetask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/testtask'
6
+ require 'rake/clean'
7
+
8
+ NAME = "changesets"
9
+ VER = "0.1"
10
+
11
+ RDOC_OPTS = ['--quiet', '--title', 'RDF Changesets API']
12
+
13
+ PKG_FILES = %w( README.md Rakefile ) +
14
+ Dir.glob("{tests,lib}/**/*")
15
+
16
+ CLEAN.include ['*.gem', 'pkg']
17
+ SPEC =
18
+ Gem::Specification.new do |s|
19
+ s.name = NAME
20
+ s.version = VER
21
+ s.platform = Gem::Platform::RUBY
22
+ s.required_ruby_version = ">= 1.8.7"
23
+ s.has_rdoc = true
24
+ s.rdoc_options = RDOC_OPTS
25
+ s.summary = "RDF Changesets API"
26
+ s.description = s.summary
27
+ s.author = "Leigh Dodds"
28
+ s.email = 'ld@kasabi.com'
29
+ s.homepage = 'http://github.com/ldodds/changesets.rb'
30
+ s.files = PKG_FILES
31
+ s.require_path = "lib"
32
+ s.test_file = "tests/ts_changeset.rb"
33
+ s.add_dependency("rdf")
34
+ s.add_dependency("mocha", ">= 0.9.5")
35
+ end
36
+
37
+ Rake::GemPackageTask.new(SPEC) do |pkg|
38
+ pkg.need_tar = true
39
+ end
40
+
41
+ Rake::RDocTask.new do |rdoc|
42
+ rdoc.rdoc_dir = 'doc/rdoc'
43
+ rdoc.options += RDOC_OPTS
44
+ rdoc.rdoc_files.include("CHANGES", "lib/**/*.rb")
45
+ end
46
+
47
+ Rake::TestTask.new do |test|
48
+ test.test_files = FileList['tests/tc_*.rb']
49
+ end
50
+
51
+ desc "Install from a locally built copy of the gem"
52
+ task :install do
53
+ sh %{rake package}
54
+ sh %{sudo gem install pkg/#{NAME}-#{VER}}
55
+ end
56
+
57
+ desc "Uninstall the gem"
58
+ task :uninstall => [:clean] do
59
+ sh %{sudo gem uninstall #{NAME}}
60
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'rdf'
3
+
4
+ require 'changesets/rdf_changeset.rb'
@@ -0,0 +1,139 @@
1
+ module RDF
2
+ module Talis
3
+ class Changeset < RDF::Vocabulary('http://purl.org/vocab/changeset/schema#')
4
+ property :removal
5
+ property :addition
6
+ property :creatorName
7
+ property :createdDate
8
+ property :subjectOfChange
9
+ property :changeReason
10
+ property :ChangeSet
11
+ property :precedingChangeSet
12
+ end
13
+ end
14
+ end
15
+
16
+ module RDF
17
+
18
+ class Changeset
19
+
20
+ #Media type for Changesets
21
+ CONTENT_TYPE_TURTLE = "application/vnd.talis.changeset+turtle"
22
+ CONTENT_TYPE_XML = "application/vnd.talis.changeset+xml"
23
+ #Default reason for applying the update to a graph
24
+ DEFAULT_REASON = "Generated in changeset.rb"
25
+ #Default name/label for the change agent
26
+ DEFAULT_CREATOR = "changeset.rb"
27
+
28
+ attr_reader :statements, :subject_of_change
29
+
30
+ def initialize(subject_of_change, change_reason=DEFAULT_REASON, creator_name=DEFAULT_CREATOR)
31
+ @resource = RDF::Node.new
32
+ @subject_of_change = subject_of_change
33
+ @statements = []
34
+ @statements.concat [RDF::Statement.new(@resource, RDF.type, RDF::Talis::Changeset.ChangeSet),
35
+ RDF::Statement.new(@resource, RDF::Talis::Changeset.changeReason, change_reason),
36
+ RDF::Statement.new(@resource, RDF::Talis::Changeset.creatorName, creator_name),
37
+ RDF::Statement.new(@resource, RDF::Talis::Changeset.createdDate, Time.now),
38
+ RDF::Statement.new(@resource, RDF::Talis::Changeset.subjectOfChange, subject_of_change)]
39
+ if block_given?
40
+ yield self
41
+ end
42
+ end
43
+
44
+ #Remove these statements from the graph
45
+ def remove_statements(stmts)
46
+ stmts = [stmts] if stmts.is_a?(RDF::Statement)
47
+ stmts.each do |stmt|
48
+ raise ArgumentError unless stmt.subject == @subject_of_change
49
+ @statements.concat changeset_statement(stmt, :removal)
50
+ end
51
+ end
52
+
53
+ #Add these statements to the graph
54
+ def add_statements(stmts)
55
+ stmts = [stmts] if stmts.is_a?(RDF::Statement)
56
+ stmts.each do |stmt|
57
+ next unless stmt
58
+ raise ArgumentError unless stmt.subject == @subject_of_change
59
+ @statements.concat changeset_statement(stmt, :addition)
60
+ end
61
+ end
62
+
63
+ def changeset_statement(stmt, action)
64
+ s = RDF::Node.new
65
+ [RDF::Statement.new(@resource, RDF::Talis::Changeset.send(action), s),
66
+ RDF::Statement.new(s, RDF.type, RDF.to_rdf+"Statement"),
67
+ RDF::Statement.new(s, RDF.subject, stmt.subject),
68
+ RDF::Statement.new(s, RDF.predicate, stmt.predicate),
69
+ RDF::Statement.new(s, RDF.object, stmt.object)]
70
+ end
71
+
72
+ #Convert into an RDF::Graph object
73
+ def to_graph
74
+ graph = RDF::Graph.new()
75
+ @statements.each do |s|
76
+ graph << s
77
+ end
78
+ graph
79
+ end
80
+
81
+ #Update a predicate from one value to another
82
+ #Will only remove the specified old value, other values for predicate will remain unchanged
83
+ def Changeset.update_property(subject, predicate, old_value, new_value, change_reason=DEFAULT_REASON, creator_name=DEFAULT_CREATOR)
84
+ cs = Changeset.new(subject, change_reason, creator_name) do |cs|
85
+ cs.remove_statements( RDF::Statement.new( subject, predicate, old_value ) )
86
+ cs.add_statements( RDF::Statement.new( subject, predicate, new_value ) )
87
+ end
88
+ cs
89
+ end
90
+
91
+ #Remove a specific single property for a subject
92
+ def Changeset.remove_property(subject, predicate, value, change_reason=DEFAULT_REASON, creator_name=DEFAULT_CREATOR)
93
+ return Changeset.remove_statement( RDF::Statement.new( subject, predicate, value ) )
94
+ end
95
+
96
+ #Remove a statement
97
+ def Changeset.remove_statement( statement, change_reason=DEFAULT_REASON, creator_name=DEFAULT_CREATOR)
98
+ cs = Changeset.new(statement.subject, change_reason, creator_name) do |cs|
99
+ cs.remove_statements( statement )
100
+ end
101
+ cs
102
+ end
103
+
104
+ #Remove all statements with a specific predicate for a specific resource
105
+ def Changeset.remove_properties(subject, predicate, graph, change_reason=DEFAULT_REASON, creator_name=DEFAULT_CREATOR)
106
+ cs = Changeset.new(subject, change_reason, creator_name) do |cs|
107
+ graph.query( [subject, predicate, nil] ).each do |statement|
108
+ cs.remove_statements( statement )
109
+ end
110
+ end
111
+ cs
112
+ end
113
+
114
+ #Remove all statements where the indicated resource is the subject
115
+ def Changeset.remove_subject(subject, graph, change_reason=DEFAULT_REASON, creator_name=DEFAULT_CREATOR)
116
+ cs = Changeset.new(subject, change_reason, creator_name) do |cs|
117
+ graph.query( [subject, nil, nil] ).each do |statement|
118
+ cs.remove_statements( statement )
119
+ end
120
+ end
121
+ cs
122
+ end
123
+
124
+ #Apply an update by removing all statements for the indicated subject from the old graph, replacing it
125
+ #with statements in the new graph
126
+ def Changeset.update(subject, new, old, change_reason=DEFAULT_REASON, creator_name=DEFAULT_CREATOR)
127
+ cs = Changeset.new(subject, change_reason, creator_name) do |cs|
128
+ old.query( [subject, nil, nil] ).each do |statement|
129
+ cs.remove_statements( statement )
130
+ end
131
+ new.query( [subject, nil, nil] ).each do |statement|
132
+ cs.add_statements( statement )
133
+ end
134
+ end
135
+ cs
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,158 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'changesets'
3
+ require 'test/unit'
4
+ require 'mocha'
5
+
6
+ class ChangesetTest < Test::Unit::TestCase
7
+
8
+ def test_init
9
+ cs = RDF::Changeset.new( RDF::URI.new("http://www.example.org") )
10
+ assert_equal( RDF::URI.new("http://www.example.org"), cs.subject_of_change )
11
+ assert_equal( false, cs.statements.empty? )
12
+
13
+ graph = cs.to_graph()
14
+ assert_not_nil( graph )
15
+ assert_equal( cs.statements.length, cs.to_graph.size )
16
+ end
17
+
18
+ def test_add_single_statement
19
+ uri = RDF::URI.new("http://www.example.org")
20
+ cs = RDF::Changeset.new( uri )
21
+ stmts = RDF::Statement.new( uri , RDF.type, RDF::RDFS.Class )
22
+ cs.add_statements( stmts )
23
+ test_for( RDF::Talis::Changeset.addition, cs, stmts )
24
+ end
25
+
26
+ def test_remove_single_statement
27
+ uri = RDF::URI.new("http://www.example.org")
28
+ cs = RDF::Changeset.new( uri )
29
+ stmts = RDF::Statement.new( uri , RDF.type, RDF::RDFS.Class )
30
+ cs.remove_statements( stmts )
31
+ test_for( RDF::Talis::Changeset.removal, cs, stmts )
32
+ end
33
+
34
+ def test_add_statements
35
+ uri = RDF::URI.new("http://www.example.org")
36
+ cs = RDF::Changeset.new( uri )
37
+ stmts = [ RDF::Statement.new( uri , RDF.type, RDF::RDFS.Class ) ]
38
+ cs.add_statements( stmts )
39
+ test_for( RDF::Talis::Changeset.addition, cs, stmts )
40
+ end
41
+
42
+ def test_remove_statements
43
+ uri = RDF::URI.new("http://www.example.org")
44
+ cs = RDF::Changeset.new( uri )
45
+ stmts = [ RDF::Statement.new( uri , RDF.type, RDF::RDFS.Class ) ]
46
+ cs.remove_statements( stmts )
47
+ test_for( RDF::Talis::Changeset.removal, cs, stmts )
48
+ end
49
+
50
+ def test_precondition
51
+ uri = RDF::URI.new("http://www.example.org")
52
+ other = RDF::URI.new("http://www.example.com")
53
+ cs = RDF::Changeset.new( uri )
54
+
55
+ assert_raise ArgumentError do
56
+ cs.add_statements( RDF::Statement.new( other , RDF.type, RDF::RDFS.Class ) )
57
+ end
58
+ assert_raise ArgumentError do
59
+ cs.remove_statements( RDF::Statement.new( other , RDF.type, RDF::RDFS.Class ) )
60
+ end
61
+
62
+ end
63
+
64
+ def test_update_property()
65
+ uri = RDF::URI.new("http://www.example.org")
66
+ cs = RDF::Changeset.update_property(uri, RDF::RDFS.label, RDF::Literal.new("Old"), RDF::Literal.new("New") )
67
+ removal = RDF::Statement.new( uri, RDF::RDFS.label, RDF::Literal.new("Old"))
68
+ addition = RDF::Statement.new( uri, RDF::RDFS.label, RDF::Literal.new("New"))
69
+ test_for( RDF::Talis::Changeset.removal, cs, removal )
70
+ test_for( RDF::Talis::Changeset.addition, cs, addition )
71
+ end
72
+
73
+ def test_remove_property()
74
+ uri = RDF::URI.new("http://www.example.org")
75
+ cs = RDF::Changeset.remove_property(uri, RDF::RDFS.label, RDF::Literal.new("Gone") )
76
+ removal = RDF::Statement.new( uri, RDF::RDFS.label, RDF::Literal.new("Gone"))
77
+ test_for( RDF::Talis::Changeset.removal, cs, removal )
78
+ end
79
+
80
+ def test_remove_statement()
81
+ uri = RDF::URI.new("http://www.example.org")
82
+ removal = RDF::Statement.new( uri, RDF::RDFS.label, RDF::Literal.new("Gone") )
83
+ cs = RDF::Changeset.remove_statement( removal )
84
+ test_for( RDF::Talis::Changeset.removal, cs, removal )
85
+ end
86
+
87
+ def test_remove_properties()
88
+ uri = RDF::URI.new("http://www.example.org")
89
+ graph = RDF::Graph.new()
90
+ label = RDF::Statement.new( uri, RDF::RDFS.label, RDF::Literal.new("Label") )
91
+ title = RDF::Statement.new( uri, RDF::RDFS.label, RDF::Literal.new("Title") )
92
+ graph << label
93
+ graph << title
94
+ cs = RDF::Changeset.remove_properties(uri, RDF::RDFS.label, graph )
95
+ #puts cs.statements
96
+ test_for( RDF::Talis::Changeset.removal, cs, label )
97
+ test_for( RDF::Talis::Changeset.removal, cs, title )
98
+ end
99
+
100
+ def test_remove_subject()
101
+ uri = RDF::URI.new("http://www.example.org")
102
+ graph = RDF::Graph.new()
103
+ label = RDF::Statement.new( uri, RDF::RDFS.label, "Label")
104
+ title = RDF::Statement.new( uri, RDF::DC.title, "Title")
105
+ graph << label
106
+ graph << title
107
+ cs = RDF::Changeset.remove_subject(uri, graph )
108
+ test_for( RDF::Talis::Changeset.removal, cs, [ label, title ] )
109
+ end
110
+
111
+ def test_update()
112
+ uri = RDF::URI.new("http://www.example.org")
113
+ graph = RDF::Graph.new()
114
+ label = RDF::Statement.new( uri, RDF::RDFS.label, "Label")
115
+ title = RDF::Statement.new( uri, RDF::DC.title, "Title")
116
+ graph << label
117
+ graph << title
118
+ cs = RDF::Changeset.update(uri, RDF::Graph.new(), graph )
119
+ test_for( RDF::Talis::Changeset.removal, cs, [ label, title ] )
120
+
121
+ cs = RDF::Changeset.update(uri, graph, RDF::Graph.new() )
122
+ test_for( RDF::Talis::Changeset.addition, cs, [ label, title ] )
123
+ end
124
+
125
+ # def test_apply_to()
126
+ # uri = RDF::URI.new("http://www.example.org")
127
+ # graph = RDF::Graph.new()
128
+ # label = RDF::Statement.new( uri, RDF::RDFS.label, RDF::Literal.new("Label") )
129
+ # title = RDF::Statement.new( uri, RDF::DC.title, RDF::Literal.new("Title") )
130
+ # graph << label
131
+ # graph << title
132
+ # cs = RDF::Changeset.remove_properties(uri, RDF::RDFS.label, graph )
133
+ #
134
+ # cs.apply_to(graph)
135
+ #
136
+ # label = graph.first_object( [uri, RDF::RDFS.label, nil ] )
137
+ # assert_nil(label)
138
+ # end
139
+
140
+
141
+ def test_for( predicate, cs, statements )
142
+ statements = [statements] if statements.is_a?(RDF::Statement)
143
+
144
+ statements.each do |s|
145
+ query = RDF::Query.new do
146
+ pattern [ :x, predicate, :stmt ]
147
+ pattern [ :stmt, RDF.subject, s.subject]
148
+ pattern [ :stmt, RDF.predicate, s.predicate]
149
+ pattern [ :stmt, RDF.object, s.object]
150
+ end
151
+ query.execute(cs.to_graph)
152
+ if query.failed?
153
+ raise "Unable to find #{predicate} for #{s.subject} #{s.predicate}, #{s.object}"
154
+ end
155
+ end
156
+ end
157
+
158
+ end
@@ -0,0 +1,2 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'test/unit'
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: changesets
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Leigh Dodds
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-10-12 00:00:00 Z
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: rdf
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 3
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: mocha
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ hash: 49
42
+ segments:
43
+ - 0
44
+ - 9
45
+ - 5
46
+ version: 0.9.5
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ description: RDF Changesets API
50
+ email: ld@kasabi.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - README.md
59
+ - Rakefile
60
+ - tests/ts_changeset.rb
61
+ - tests/tc_rdf_changeset.rb
62
+ - lib/changesets.rb
63
+ - lib/changesets/rdf_changeset.rb
64
+ homepage: http://github.com/ldodds/changesets.rb
65
+ licenses: []
66
+
67
+ post_install_message:
68
+ rdoc_options:
69
+ - --quiet
70
+ - --title
71
+ - RDF Changesets API
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 57
80
+ segments:
81
+ - 1
82
+ - 8
83
+ - 7
84
+ version: 1.8.7
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ requirements: []
95
+
96
+ rubyforge_project:
97
+ rubygems_version: 1.8.9
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: RDF Changesets API
101
+ test_files:
102
+ - tests/ts_changeset.rb