tripod 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,110 @@
1
+ # encoding: utf-8
2
+
3
+ # module for all domain objects that need to be persisted to the database
4
+ # as resources
5
+ module Tripod::Resource
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ include Tripod::Components
10
+
11
+ included do
12
+ # every resource needs a graph and a uri set.
13
+ validates_presence_of :uri, :graph_uri
14
+ end
15
+
16
+ attr_reader :new_record
17
+ attr_reader :graph_uri
18
+ attr_reader :uri
19
+
20
+ # Instantiate a +Resource+.
21
+ # Optionsally pass a uri
22
+ #
23
+ # @example Instantiate a new Resource
24
+ # Person.new('http://swirrl.com/ric.rdf#me')
25
+ #
26
+ # @param [ String, RDF::URI ] uri The uri of the resource.
27
+ #
28
+ # @return [ Resource ] A new +Resource+
29
+ def initialize(uri=nil, graph_uri=nil)
30
+ @new_record = true
31
+ @uri = RDF::URI(uri.to_s) if uri
32
+ @graph_uri = RDF::URI(graph_uri.to_s) if graph_uri
33
+ @repository = RDF::Repository.new
34
+ end
35
+
36
+ # Set the uri for this resource
37
+ def uri=(new_uri)
38
+ if new_uri
39
+ @uri = RDF::URI(new_uri.to_s)
40
+ else
41
+ @uri = nil
42
+ end
43
+ end
44
+
45
+ # Set the uri for this resource
46
+ def graph_uri=(new_graph_uri)
47
+ if new_graph_uri
48
+ @graph_uri = RDF::URI(new_graph_uri.to_s)
49
+ else
50
+ @graph_uri = nil
51
+ end
52
+ end
53
+
54
+ # default comparison is via the uri
55
+ def <=>(other)
56
+ uri.to_s <=> uri.to_s
57
+ end
58
+
59
+ # performs equality checking on the uris
60
+ def ==(other)
61
+ self.class == other.class &&
62
+ uri.to_s == uri.to_s
63
+ end
64
+
65
+ # performs equality checking on the class
66
+ def ===(other)
67
+ other.class == Class ? self.class === other : self == other
68
+ end
69
+
70
+ # delegates to ==
71
+ def eql?()
72
+ self == (other)
73
+ end
74
+
75
+ def hash
76
+ identity.hash
77
+ end
78
+
79
+ # a resource is absolutely identified by it's class and id.
80
+ def identity
81
+ [ self.class, self.uri.to_s ]
82
+ end
83
+
84
+ # Return the key value for the resource.
85
+ #
86
+ # @example Return the key.
87
+ # resource.to_key
88
+ #
89
+ # @return [ Object ] The uri of the resource or nil if new.
90
+ def to_key
91
+ (persisted? || destroyed?) ? [ uri.to_s ] : nil
92
+ end
93
+
94
+ def to_a
95
+ [ self ]
96
+ end
97
+
98
+ module ClassMethods
99
+
100
+ # Performs class equality checking.
101
+ def ===(other)
102
+ other.class == Class ? self <= other : other.is_a?(self)
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+
109
+ # causes any hooks to be fired, if they've been setup on_load of :tripod.
110
+ ActiveSupport.run_load_hooks(:triploid, Tripod::Resource)
@@ -0,0 +1,101 @@
1
+ # encoding: utf-8
2
+
3
+ # this module is responsible for connecting to an http sparql endpoint
4
+ module Tripod::SparqlClient
5
+
6
+ module Query
7
+
8
+
9
+ # Runs a +sparql+ query against the endpoint. Returns a RestClient response object.
10
+ #
11
+ # @example Run a query
12
+ # Tripload::Sparql.query('SELECT * WHERE {?s ?p ?o}')
13
+ #
14
+ # @return [ RestClient::Response ]
15
+
16
+ def self.query(sparql, format='json', headers = {})
17
+
18
+ begin
19
+ params = { :params => {:query => sparql, :output => format } }
20
+ hdrs = headers.merge(params)
21
+ RestClient::Request.execute(
22
+ :method => :get,
23
+ :url => Tripod.query_endpoint,
24
+ :headers => hdrs,
25
+ :timeout => Tripod.timeout_seconds,
26
+ )
27
+ rescue RestClient::BadRequest => e
28
+ body = e.http_body
29
+ if body.start_with?('Error 400: Parse error:')
30
+ # TODO: this is a SPARQL parsing exception. Do something different.
31
+ puts body.inspect
32
+ raise e
33
+ else
34
+ puts body.inspect
35
+ raise e
36
+ end
37
+ end
38
+ end
39
+
40
+ # Runs a SELECT +query+ against the endpoint. Returns a hash of the results.
41
+ # Specify +raw_format+ if you want the results raw, as returned from the SPARQL endpoint.
42
+ #
43
+ # @param [ String ] query The query to run
44
+ # @param [ String ] raw_format valid formats are: 'json', 'text', 'csv', 'xml'
45
+ #
46
+ # @example Run a SELECT query
47
+ #  Triploid::Sparql.select('SELECT * WHERE {?s ?p ?o}')
48
+ #
49
+ # @return [ Hash, String ]
50
+ def self.select(query, raw_format=nil)
51
+ query_response = self.query(query, (raw_format || 'json'))
52
+ if raw_format
53
+ query_response.body
54
+ else
55
+ JSON.parse(query_response.body)["results"]["bindings"]
56
+ end
57
+ end
58
+
59
+ # Executes a DESCRIBE +query+ against the SPARQL endpoint.
60
+ # Executes the +query+ and returns ntriples by default
61
+ #
62
+ # @example Run a DESCRIBE query
63
+ # Triploid::Sparql.select('DESCRIBE <http://foo>')
64
+ #
65
+ # @param [ String ] query The query to run
66
+ # @param [ String ] accept_header The header to pass to the database.
67
+
68
+ # @return [ String ] the raw response from the endpoint
69
+ def self.describe(query, accept_header='application/n-triples')
70
+ response = self.query(query, nil, {:accept=>accept_header})
71
+ return response.body
72
+ end
73
+ end
74
+
75
+ module Update
76
+
77
+ def self.update(sparql)
78
+
79
+ begin
80
+ RestClient::Request.execute(
81
+ :method => :post,
82
+ :url => Tripod.update_endpoint,
83
+ :timeout => Tripod.timeout_seconds,
84
+ :payload => {:update => sparql}
85
+ )
86
+ return true
87
+ rescue RestClient::BadRequest => e
88
+ body = e.http_body
89
+ if body.start_with?('Error 400: Parse error:')
90
+ # TODO: this is a SPARQL parsing exception. Do something different.
91
+ puts body.inspect
92
+ raise e
93
+ else
94
+ puts body.inspect
95
+ raise e
96
+ end
97
+ end
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ # This module contains the behaviour for getting the various states through which a
4
+ # resource can transition.
5
+ module Tripod::State
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ attr_writer :destroyed, :new_record
10
+
11
+ # Returns true if the +Resource+ has not been persisted to the database,
12
+ # false if it has. This is determined by the variable @new_record
13
+ # and NOT if the object has an id.
14
+ #
15
+ # @example Is the resource new?
16
+ # person.new_record?
17
+ #
18
+ # @return [ true, false ] True if new, false if not.
19
+ def new_record?
20
+ @new_record ||= false
21
+ end
22
+
23
+ # Checks if the resource has been saved to the database. Returns false
24
+ # if the document has been destroyed.
25
+ #
26
+ # @example Is the resource persisted?
27
+ # person.persisted?
28
+ #
29
+ # @return [ true, false ] True if persisted, false if not.
30
+ def persisted?
31
+ !new_record? && !destroyed?
32
+ end
33
+
34
+ # Returns true if the +Resource+ has been succesfully destroyed, and false
35
+ # if it hasn't. This is determined by the variable @destroyed and NOT
36
+ # by checking the database.
37
+ #
38
+ # @example Is the resource destroyed?
39
+ # person.destroyed?
40
+ #
41
+ # @return [ true, false ] True if destroyed, false if not.
42
+ def destroyed?
43
+ @destroyed ||= false
44
+ end
45
+ alias :deleted? :destroyed?
46
+
47
+ end
@@ -1,3 +1,3 @@
1
1
  module Tripod
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,5 @@
1
+ class Person
2
+
3
+ include Tripod::Resource
4
+
5
+ end
@@ -0,0 +1,35 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
+
4
+ MODELS = File.join(File.dirname(__FILE__), "app/models")
5
+ $LOAD_PATH.unshift(MODELS)
6
+
7
+ require 'tripod'
8
+ require 'rspec'
9
+
10
+ RSpec.configure do |config|
11
+ config.mock_with :rspec
12
+
13
+ config.before(:each) do
14
+ # delete from all named graphs.
15
+ Tripod::SparqlClient::Update.update('
16
+ # delete from default graph:
17
+ DELETE {?s ?p ?o} WHERE {?s ?p ?o};
18
+ # delete from named graphs:
19
+ DELETE {graph ?g {?s ?p ?o}} WHERE {graph ?g {?s ?p ?o}};
20
+ ')
21
+ end
22
+
23
+ end
24
+
25
+ # configure any settings for testing...
26
+ Tripod.configure do |config|
27
+ config.update_endpoint = 'http://127.0.0.1:3030/tripod-test/update'
28
+ config.query_endpoint = 'http://127.0.0.1:3030/tripod-test/sparql'
29
+ end
30
+
31
+ # Autoload every model for the test suite that sits in spec/app/models.
32
+ Dir[ File.join(MODELS, "*.rb") ].sort.each do |file|
33
+ name = File.basename(file, ".rb")
34
+ autoload name.camelize.to_sym, name
35
+ end
@@ -0,0 +1,103 @@
1
+ require "spec_helper"
2
+
3
+ describe Tripod::Attributes do
4
+
5
+ before do
6
+ @uri = 'http://ric'
7
+ @graph = RDF::Graph.new
8
+
9
+ stmt = RDF::Statement.new
10
+ stmt.subject = RDF::URI.new(@uri)
11
+ stmt.predicate = RDF::URI.new('http://blog')
12
+ stmt.object = RDF::URI.new('http://blog1')
13
+ @graph << stmt
14
+
15
+ stmt2 = RDF::Statement.new
16
+ stmt2.subject = RDF::URI.new(@uri)
17
+ stmt2.predicate = RDF::URI.new('http://blog')
18
+ stmt2.object = RDF::URI.new('http://blog2')
19
+ @graph << stmt2
20
+
21
+ stmt3 = RDF::Statement.new
22
+ stmt3.subject = RDF::URI.new(@uri)
23
+ stmt3.predicate = RDF::URI.new('http://name')
24
+ stmt3.object = "ric"
25
+ @graph << stmt3
26
+ end
27
+
28
+ let(:person) do
29
+ p = Person.new(@uri)
30
+ p.hydrate!(@graph)
31
+ p
32
+ end
33
+
34
+ describe "#[]" do
35
+ it 'returns the values where the predicate matches' do
36
+ values = person['http://blog']
37
+ values.length.should == 2
38
+ values.first.should == RDF::URI('http://blog1')
39
+ values[1].should == RDF::URI('http://blog2')
40
+ end
41
+ end
42
+
43
+ describe "#read_attribute" do
44
+ it 'returns the values where the predicate matches' do
45
+ values = person.read_attribute('http://blog')
46
+ values.length.should == 2
47
+ values.first.should == RDF::URI('http://blog1')
48
+ values[1].should == RDF::URI('http://blog2')
49
+ end
50
+ end
51
+
52
+ describe '#[]=' do
53
+
54
+ context 'single term passed' do
55
+ it 'replaces the values where the predicate matches' do
56
+ person['http://name'] = 'richard'
57
+ person['http://name'].should == [RDF::Literal.new('richard')]
58
+ end
59
+ end
60
+
61
+ context 'multiple terms passed' do
62
+ it 'replaces the values where the predicate matches' do
63
+ person['http://name'] = ['richard', 'ric', 'ricardo']
64
+ person['http://name'].should == [RDF::Literal.new('richard'), RDF::Literal.new('ric'), RDF::Literal.new('ricardo')]
65
+ end
66
+ end
67
+ end
68
+
69
+ describe '#write_attribute' do
70
+
71
+ context 'single term passed' do
72
+ it 'replaces the values where the predicate matches' do
73
+ person.write_attribute('http://name', 'richard')
74
+ person['http://name'].should == [RDF::Literal.new('richard')]
75
+ end
76
+ end
77
+
78
+ context 'multiple terms passed' do
79
+ it 'replaces the values where the predicate matches' do
80
+ person.write_attribute('http://name', ['richard', 'ric', 'ricardo'])
81
+ person['http://name'].should == [RDF::Literal.new('richard'), RDF::Literal.new('ric'), RDF::Literal.new('ricardo')]
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ describe '#remove_attribute' do
88
+ it 'remnoves the values where the predicate matches' do
89
+ person.remove_attribute('http://blog')
90
+ person['http://blog'].should be_empty
91
+ end
92
+ end
93
+
94
+ describe "append_to_attribute" do
95
+
96
+ it 'appends values to the existing values for the predicate' do
97
+ person.append_to_attribute('http://name', 'rico')
98
+ person['http://name'].should == [RDF::Literal.new('ric'), RDF::Literal.new('rico')]
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,71 @@
1
+ require "spec_helper"
2
+
3
+ describe Tripod::Finders do
4
+
5
+ let(:ric) do
6
+ @ric_uri = 'http://ric'
7
+ stmts = RDF::Graph.new
8
+
9
+ stmt = RDF::Statement.new
10
+ stmt.subject = RDF::URI.new(@ric_uri)
11
+ stmt.predicate = RDF::URI.new('http://name')
12
+ stmt.object = "ric"
13
+ stmts << stmt
14
+
15
+ stmt = RDF::Statement.new
16
+ stmt.subject = RDF::URI.new(@ric_uri)
17
+ stmt.predicate = RDF::URI.new('http://knows')
18
+ stmt.object = RDF::URI.new('http://bill')
19
+ stmts << stmt
20
+
21
+ r = Person.new(@ric_uri, 'http://people')
22
+ r.hydrate!(stmts)
23
+ r.save
24
+ r
25
+ end
26
+
27
+ let(:bill) do
28
+ @bill_uri = 'http://bill'
29
+ stmts = RDF::Graph.new
30
+ stmt = RDF::Statement.new
31
+ stmt.subject = RDF::URI.new(@bill_uri)
32
+ stmt.predicate = RDF::URI.new('http://name')
33
+ stmt.object = "bill"
34
+ stmts << stmt
35
+ b = Person.new(@bill_uri, 'http://people')
36
+ b.hydrate!(stmts)
37
+ b.save
38
+ b
39
+ end
40
+
41
+ describe '.find' do
42
+
43
+ context 'when record exists' do
44
+
45
+ it 'does not error' do
46
+ r = Person.find(ric.uri)
47
+ end
48
+
49
+ it 'hydrates and return an object' do
50
+ r = Person.find(ric.uri)
51
+ r['http://name'].should == [RDF::Literal.new("ric")]
52
+ r['http://knows'].should == [RDF::URI.new('http://bill')]
53
+ end
54
+
55
+ it 'sets the graph on the instantiated object' do
56
+ r = Person.find(ric.uri)
57
+ r.graph_uri.should_not be_nil
58
+ r.graph_uri.should == RDF::URI("http://people")
59
+ end
60
+
61
+ end
62
+
63
+ context 'when record does not exist' do
64
+ it 'raises not found' do
65
+ lambda { Person.find('http://nonexistant') }.should raise_error(Tripod::Errors::ResourceNotFound)
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ end