tripod 0.0.10 → 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -6,10 +6,12 @@ ActiveModel-style Ruby ORM for RDF Linked Data. Works with SPARQL 1.1 HTTP endpo
6
6
  * Inspired by [Durran Jordan's](https://github.com/durran) [Mongoid](http://mongoid.org/en/mongoid/) ORM for [MongoDB](http://www.mongodb.org/), and [Ben Lavender's](https://github.com/bhuga) RDF ORM, [Spira](https://github.com/ruby-rdf/spira).
7
7
  * Uses [Ruby-RDF](https://github.com/ruby-rdf/rdf) to manage the data internally.
8
8
 
9
- __Warning: Work still in progress / experimental. Not production ready!__
9
+ __Warning: Some features are still experimental.__
10
10
 
11
11
  ## Quick start, for using in a rails app.
12
12
 
13
+ Note: Tripod doesn't supply a database. You need to install one. I recommend [Fuseki](http://jena.apache.org/documentation/serving_data/index.html), which runs on port 3030 by default.
14
+
13
15
  1. Install the gem:
14
16
 
15
17
  gem install tripod
@@ -32,7 +34,11 @@ __Warning: Work still in progress / experimental. Not production ready!__
32
34
  class Person
33
35
  include Tripod::Resource
34
36
 
37
+ rdf_type 'http://person'
38
+ graph_uri 'http://people'
39
+
35
40
  field :name, 'http://name'
41
+ field :knows, 'http://knows', :multivalued => true
36
42
  field :aliases, 'http://alias', :multivalued => true
37
43
  field :age, 'http://age', :datatype => RDF::XSD.integer
38
44
  field :important_dates, 'http://importantdates', :datatype => RDF::XSD.date, :multivalued => true
@@ -43,15 +49,15 @@ __Warning: Work still in progress / experimental. Not production ready!__
43
49
  5. Use it
44
50
 
45
51
  uri = 'http://ric'
46
- graph = 'http://people'
47
- p = Person.new(uri, graph)
52
+ p = Person.new(uri)
48
53
  p.name = 'Ric'
49
54
  p.age = 31
50
55
  p.aliases = ['Rich', 'Richard']
51
56
  p.important_dates = [Date.new(2011,1,1)]
52
- p[RDF::type] = RDF::URI('http://person')
53
57
  p.save!
54
58
 
59
+ # Note: queries supplied to the where method should return the uris of the resource,
60
+ # and what graph they're in.
55
61
  people = Person.where("
56
62
  SELECT ?person ?graph
57
63
  WHERE {
@@ -60,7 +66,7 @@ __Warning: Work still in progress / experimental. Not production ready!__
60
66
  ?person a <http://person> .
61
67
  }
62
68
  }",
63
- :uri_variable => 'person' )
69
+ :uri_variable => 'person' ) # optionally, set a different name for the uri parameter (default: uri)
64
70
  # => returns an array of Person objects, containing all data we know about them.
65
71
 
66
72
  ric = Person.find('http://ric')
@@ -33,6 +33,7 @@ require 'rdf'
33
33
  require 'rdf/rdfxml'
34
34
  require 'rdf/n3'
35
35
  require 'rdf/json'
36
+ require 'json/ld'
36
37
 
37
38
  require 'rest_client'
38
39
 
@@ -64,12 +65,14 @@ end
64
65
  require "tripod/extensions"
65
66
  require "tripod/sparql_client"
66
67
 
68
+ require "tripod/predicates"
67
69
  require "tripod/attributes"
68
70
  require "tripod/errors"
69
71
  require "tripod/fields"
70
72
  require "tripod/finders"
71
73
  require "tripod/persistence"
72
74
  require "tripod/repository"
75
+ require "tripod/serialization"
73
76
  require "tripod/state"
74
77
  require "tripod/version"
75
78
 
@@ -5,77 +5,69 @@ module Tripod::Attributes
5
5
 
6
6
  extend ActiveSupport::Concern
7
7
 
8
- # Reads values from this respource's in-memory statement repository, where the predicate matches that of the uri passed in.
9
- # Returns an Array of RDF::Terms object.
8
+ # Reads an attribute from this resource, based on a defined field
9
+ # Returns the value(s) for the named (or given) field
10
10
  #
11
- # @example Read an attribute.
12
- # person.read_attribute('http://foo')
13
- # person.read_attribute(RDF::URI.new('http://foo'))
11
+ # @example Read the value associated with a predicate.
12
+ # class Person
13
+ # field :name, 'http://name'
14
+ # end
14
15
  #
15
- # @example Read an attribute (alternate syntax.)
16
- # person['http://foo']
17
- # person[RDF::URI.new('http://foo')]
16
+ # person.read_attribute(:name)
18
17
  #
19
- # @param [ String, RDF::URI ] uri The uri of the attribute to get.
18
+ # @param [ String ] name The name of the field for which to get the value.
19
+ # @param [ Field ] field An optional Field object
20
20
  #
21
- # @return [ Array ] An array of RDF::Terms.
22
- def read_attribute(predicate_uri)
23
- values = []
24
- @repository.query( [:subject, RDF::URI.new(predicate_uri.to_s), :object] ) do |statement|
25
- values << statement.object
21
+ # @return Either a string or an Array of strings, depending on whether the field is multivalued or not
22
+ def read_attribute(name, field=nil)
23
+ field ||= self.fields[name]
24
+ raise Tripod::Errors::FieldNotPresent.new unless field
25
+
26
+ attr_values = read_predicate(field.predicate)
27
+ # We always return strings on way out.
28
+ # If the field is multivalued, return an array of the results
29
+ # If it's not multivalued, return the first (should be only) result.
30
+ if field.multivalued
31
+ attr_values.map do |v|
32
+ v.nil? ? nil : v.to_s
33
+ end
34
+ else
35
+ first_val = attr_values.first
36
+ first_val.nil? ? nil : first_val.to_s
26
37
  end
27
- values
28
38
  end
29
39
  alias :[] :read_attribute
30
40
 
31
- # Replace the statement-values for a single predicate in this resource's in-memory repository.
41
+ # Writes an attribute to the resource, based on a defined field
32
42
  #
33
- # @example Write the attribute.
34
- # person.write_attribute('http://title', "Mr.")
35
- # person.write_attribute('http://title', ["Mrs.", "Ms."])
43
+ # @example Write the value associated with a predicate.
44
+ # class Person
45
+ # field :name, 'http://name'
46
+ # end
36
47
  #
37
- # @example Write the attribute (alternate syntax.)
38
- # person['http://title'] = "Mr."
39
- # person['http://title'] = ["Mrs.", "Ms."]
48
+ # person.write_attribute(:name, 'Bob')
40
49
  #
41
- # @param [ String, RDF::URI ] predicate_uri The name of the attribute to update.
42
- # @param [ Object, Array ] value The values to set for the attribute. Can be an array, or single item. They should compatible with RDF::Terms
43
- def write_attribute(predicate_uri, objects)
44
- raise Tripod::Errors::UriNotSet.new() unless @uri
45
-
46
- # remove existing
47
- remove_attribute(predicate_uri)
50
+ # @param [ String ] name The name of the field for which to set the value.
51
+ # @param [ String ] value The value to set it to
52
+ # @param [ Field ] field An optional Field object
53
+ def write_attribute(name, value, field=nil)
54
+ field ||= self.fields[name]
55
+ raise Tripod::Errors::FieldNotPresent.new unless field
48
56
 
49
- # ... and replace
50
- objects = [objects] unless objects.kind_of?(Array)
51
- objects.each do |object|
52
- @repository << RDF::Statement.new( @uri, RDF::URI.new(predicate_uri.to_s), object )
57
+ if value.kind_of?(Array)
58
+ if field.multivalued
59
+ new_val = []
60
+ value.each do |v|
61
+ new_val << self.class.new_value_for_field(v, field)
62
+ end
63
+ else
64
+ new_val = self.class.new_value_for_field(value.first, field)
65
+ end
66
+ else
67
+ new_val = self.class.new_value_for_field(value, field)
53
68
  end
54
69
 
55
- # returns the new values
56
- read_attribute(predicate_uri)
70
+ write_predicate(field.predicate, new_val)
57
71
  end
58
72
  alias :[]= :write_attribute
59
-
60
- # Append the statement-values for a single predicate in this resource's in-memory repository. Basically just adds a new statement for this ((resource's uri)+predicate)
61
- #
62
- # @example Write the attribute.
63
- # person.append_to_attribute('http://title', "Mrs.")
64
- # person.append_to_attribute('http://title', "Ms.")
65
- #
66
- # @param [ String, RDF::URI ] predicate_uri The uri of the attribute to update.
67
- # @param [ Object ] value The values to append for the attribute. Should compatible with RDF::Terms
68
- def append_to_attribute(predicate_uri, object )
69
- raise Tripod::Errors::UriNotSet.new() unless @uri
70
-
71
- @repository << RDF::Statement.new(@uri, RDF::URI.new(predicate_uri.to_s), object)
72
- end
73
-
74
- def remove_attribute(predicate_uri)
75
- @repository.query( [:subject, RDF::URI.new(predicate_uri.to_s), :object] ) do |statement|
76
- @repository.delete( statement )
77
- end
78
- end
79
- alias :delete :remove_attribute
80
-
81
73
  end
@@ -13,11 +13,13 @@ module Tripod::Components
13
13
  include ActiveModel::Naming
14
14
  include ActiveModel::Validations
15
15
 
16
+ include Tripod::Predicates
16
17
  include Tripod::Attributes
17
18
  include Tripod::Persistence
18
19
  include Tripod::Fields
19
20
  include Tripod::Finders
20
21
  include Tripod::Repository
22
+ include Tripod::Serialization
21
23
  include Tripod::State
22
24
 
23
25
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ require 'tripod/errors/field_not_present'
2
3
  require 'tripod/errors/resource_not_found'
3
4
  require 'tripod/errors/uri_not_set'
4
5
  require 'tripod/errors/validations'
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+ module Tripod::Errors
3
+
4
+ # field not present error.
5
+ class FieldNotPresent < StandardError
6
+ end
7
+
8
+ end
@@ -28,9 +28,8 @@ module Tripod::Fields
28
28
  #
29
29
  # @return [ Field ] The generated field
30
30
  def field(name, predicate, options = {})
31
- named = name.to_s
32
31
  # TODO: validate the field params/options here..
33
- add_field(named, predicate, options)
32
+ add_field(name, predicate, options)
34
33
  end
35
34
 
36
35
 
@@ -92,20 +91,7 @@ module Tripod::Fields
92
91
  def create_field_getter(name, meth, field)
93
92
  generated_methods.module_eval do
94
93
  re_define_method(meth) do
95
-
96
- attr_values = read_attribute(field.predicate)
97
-
98
- # We always return strings on way out.
99
- # If the field is multivalued, return an array of the results
100
- # If it's not multivalued, return the first (should be only) result.
101
- if field.multivalued
102
- attr_values.map do |v|
103
- v.nil? ? nil : v.to_s
104
- end
105
- else
106
- first_val = attr_values.first
107
- first_val.nil? ? nil : first_val.to_s
108
- end
94
+ read_attribute(name, field)
109
95
  end
110
96
  end
111
97
  end
@@ -121,20 +107,7 @@ module Tripod::Fields
121
107
  def create_field_setter(name, meth, field)
122
108
  generated_methods.module_eval do
123
109
  re_define_method("#{meth}=") do |value|
124
- if value.kind_of?(Array)
125
- if field.multivalued
126
- new_val = []
127
- value.each do |v|
128
- new_val << self.class.new_value_for_field(v, field)
129
- end
130
- else
131
- new_val = self.class.new_value_for_field(value.first, field)
132
- end
133
- else
134
- new_val = self.class.new_value_for_field(value, field)
135
- end
136
-
137
- write_attribute(field.predicate, new_val)
110
+ write_attribute(name, value, field)
138
111
  end
139
112
  end
140
113
  end
@@ -149,7 +122,7 @@ module Tripod::Fields
149
122
  def create_field_check(name, meth, field)
150
123
  generated_methods.module_eval do
151
124
  re_define_method("#{meth}?") do
152
- attr = read_attribute(field.predicate)
125
+ attr = read_attribute(name, field)
153
126
  attr == true || attr.present?
154
127
  end
155
128
  end
@@ -6,30 +6,37 @@ module Tripod::Finders
6
6
 
7
7
  module ClassMethods
8
8
 
9
- # Find a +Resource+ by its uri.
9
+ # Find a +Resource+ by its uri (and, optionally, by its graph if there are more than one).
10
10
  #
11
11
  # @example Find a single resource by a uri.
12
12
  # Person.find('http://ric')
13
13
  # Person.find(RDF::URI('http://ric'))
14
+ # @example Find a single resource by uri and graph
15
+ # Person.find('http://ric', 'http://example.com/people')
16
+ # Person.find(RDF::URI('http://ric'), Person.find(RDF::URI('http://example.com/people')))
14
17
  #
15
18
  # @param [ String, RDF::URI ] uri The uri of the resource to find
19
+ # @param [ String, RDF::URI ] graph_uri The uri of the graph from which to get the resource
16
20
  #
17
21
  # @raise [ Tripod::Errors::ResourceNotFound ] If no resource found.
18
22
  #
19
23
  # @return [ Resource ] A single resource
20
- def find(uri)
21
-
22
- # do a quick select to see what graph to use.
23
- select_query = "SELECT ?g WHERE { GRAPH ?g {<#{uri.to_s}> ?p ?o } } LIMIT 1"
24
- result = Tripod::SparqlClient::Query.select(select_query)
25
- if result.length > 0
26
- graph_uri_str = result[0]["g"]["value"]
27
- else
28
- raise Tripod::Errors::ResourceNotFound.new
24
+ def find(uri, graph_uri=nil)
25
+
26
+ unless graph_uri
27
+ # do a quick select to see what graph to use.
28
+ select_query = "SELECT ?g WHERE { GRAPH ?g {<#{uri.to_s}> ?p ?o } } LIMIT 1"
29
+ result = Tripod::SparqlClient::Query.select(select_query)
30
+ if result.length > 0
31
+ graph_uri = result[0]["g"]["value"]
32
+ else
33
+ raise Tripod::Errors::ResourceNotFound.new
34
+ end
29
35
  end
30
36
 
31
37
  # instantiate and hydrate the resource
32
- resource = self.new(uri, graph_uri_str)
38
+ resource = self.new(uri, graph_uri.to_s)
39
+
33
40
  resource.hydrate!
34
41
  resource.new_record = false
35
42
 
@@ -90,7 +97,7 @@ module Tripod::Finders
90
97
  triples_repository.query( [RDF::URI.new(u), :predicate, :object] ) do |statement|
91
98
  data_graph << statement
92
99
  end
93
- r.hydrate!(data_graph)
100
+ r.hydrate!(:graph => data_graph)
94
101
  r.new_record = false
95
102
  resources << r
96
103
  end
@@ -144,6 +144,18 @@ module Tripod::Persistence
144
144
  true
145
145
  end
146
146
 
147
+ def update_attribute(name, value)
148
+ write_attribute(name, value)
149
+ save
150
+ end
151
+
152
+ def update_attributes(attributes={})
153
+ attributes.each_pair do |name, value|
154
+ send "#{name}=", value
155
+ end
156
+ save
157
+ end
158
+
147
159
  module ClassMethods #:nodoc:
148
160
 
149
161
  # Raise an error if validation failed.
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+
3
+ # This module defines behaviour for predicates.
4
+ module Tripod::Predicates
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ # returns a list of predicates (as RDF::URIs) for this resource
9
+ def predicates
10
+ pred_uris = []
11
+ @repository.statements.each do |s|
12
+ pred_uris << s.predicate unless pred_uris.include?(s.predicate)
13
+ end
14
+ pred_uris
15
+ end
16
+
17
+ # Reads values from this resource's in-memory statement repository, where the predicate matches that of the uri passed in.
18
+ # Returns an Array of RDF::Terms object.
19
+ #
20
+ # @example Read the value associated with a predicate.
21
+ # person.read_predicate('http://foo')
22
+ # person.read_predicate(RDF::URI.new('http://foo'))
23
+ #
24
+ # @param [ String, RDF::URI ] uri The uri of the predicate to get.
25
+ #
26
+ # @return [ Array ] An array of RDF::Terms.
27
+ def read_predicate(predicate_uri)
28
+ values = []
29
+ @repository.query( [:subject, RDF::URI.new(predicate_uri.to_s), :object] ) do |statement|
30
+ values << statement.object
31
+ end
32
+ values
33
+ end
34
+
35
+ # Replace the statement-values for a single predicate in this resource's in-memory repository.
36
+ #
37
+ # @example Write the predicate.
38
+ # person.write_predicate('http://title', "Mr.")
39
+ # person.write_predicate('http://title', ["Mrs.", "Ms."])
40
+ #
41
+ # @param [ String, RDF::URI ] predicate_uri The name of the attribute to update.
42
+ # @param [ Object, Array ] value The values to set for the attribute. Can be an array, or single item. They should be compatible with RDF::Terms
43
+ def write_predicate(predicate_uri, objects)
44
+ # remove existing
45
+ remove_predicate(predicate_uri)
46
+
47
+ # ... and replace
48
+ objects = [objects] unless objects.kind_of?(Array)
49
+ objects.each do |object|
50
+ @repository << RDF::Statement.new( @uri, RDF::URI.new(predicate_uri.to_s), object )
51
+ end
52
+
53
+ # returns the new values
54
+ read_predicate(predicate_uri)
55
+ end
56
+
57
+ # Append the statement-values for a single predicate in this resource's in-memory repository. Basically just adds a new statement for this ((resource's uri)+predicate)
58
+ #
59
+ # @example Write the attribute.
60
+ # person.append_to_predicate('http://title', "Mrs.")
61
+ # person.append_to_predicate('http://title', "Ms.")
62
+ #
63
+ # @param [ String, RDF::URI ] predicate_uri The uri of the attribute to update.
64
+ # @param [ Object ] value The values to append for the attribute. Should compatible with RDF::Terms
65
+ def append_to_predicate(predicate_uri, object )
66
+ raise Tripod::Errors::UriNotSet.new() unless @uri
67
+
68
+ @repository << RDF::Statement.new(@uri, RDF::URI.new(predicate_uri.to_s), object)
69
+ end
70
+
71
+ def remove_predicate(predicate_uri)
72
+ @repository.query( [:subject, RDF::URI.new(predicate_uri.to_s), :object] ) do |statement|
73
+ @repository.delete( statement )
74
+ end
75
+ end
76
+ alias :delete :remove_predicate
77
+
78
+ end