tripod 0.3.5 → 0.3.6

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
@@ -8,17 +8,13 @@ ActiveModel-style Ruby ORM for RDF Linked Data. Works with SPARQL 1.1 HTTP endpo
8
8
 
9
9
  ## Quick start, for using in a rails app.
10
10
 
11
- 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.
12
-
13
- 1. Install the gem:
14
-
15
- gem install tripod
16
-
17
- 2. Add it to your Gemfile
11
+ 1. Add it to your Gemfile and bundle
18
12
 
19
13
  gem tripod
20
14
 
21
- 3. Configure it (in application.rb, or development.rb/production.rb/test.rb)
15
+ $ bundle
16
+
17
+ 2. Configure it (in application.rb, or development.rb/production.rb/test.rb)
22
18
 
23
19
  # (values shown are the defaults)
24
20
  Tripod.configure do |config|
@@ -27,7 +23,7 @@ Note: Tripod doesn't supply a database. You need to install one. I recommend [Fu
27
23
  config.timeout_seconds = 30
28
24
  end
29
25
 
30
- 4. Include it in your model classes.
26
+ 3. Include it in your model classes.
31
27
 
32
28
  class Person
33
29
  include Tripod::Resource
@@ -45,7 +41,7 @@ Note: Tripod doesn't supply a database. You need to install one. I recommend [Fu
45
41
 
46
42
  # Note: Active Model validations are supported
47
43
 
48
- 5. Use it
44
+ 4. Use it
49
45
 
50
46
  uri = 'http://ric'
51
47
  p = Person.new(uri)
@@ -59,6 +55,10 @@ Note: Tripod doesn't supply a database. You need to install one. I recommend [Fu
59
55
 
60
56
  ric = Person.find('http://ric') #=> returns a single Person object.
61
57
 
58
+ ## Note:
59
+
60
+ 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.
61
+
62
62
 
63
63
  ## Some Other interesting features
64
64
 
@@ -18,22 +18,20 @@ module Tripod::Attributes
18
18
  # @param [ String ] name The name of the field for which to get the value.
19
19
  # @param [ Field ] field An optional Field object
20
20
  #
21
- # @return Either a string or an Array of strings, depending on whether the field is multivalued or not
21
+ # @return RDF:Term or array of them, depending on whether the field is multivalued or not
22
22
  def read_attribute(name, field=nil)
23
23
  field ||= self.fields[name]
24
24
  raise Tripod::Errors::FieldNotPresent.new unless field
25
25
 
26
26
  attr_values = read_predicate(field.predicate)
27
- # We always return strings on way out.
27
+
28
28
  # If the field is multivalued, return an array of the results
29
29
  # If it's not multivalued, return the first (should be only) result.
30
+
30
31
  if field.multivalued
31
- attr_values.map do |v|
32
- v.nil? ? nil : v.to_s
33
- end
32
+ attr_values
34
33
  else
35
- first_val = attr_values.first
36
- first_val.nil? ? nil : first_val.to_s
34
+ attr_values.first
37
35
  end
38
36
  end
39
37
  alias :[] :read_attribute
@@ -0,0 +1,35 @@
1
+ require 'dalli'
2
+
3
+ module Tripod
4
+ module CacheStores
5
+
6
+ # A Tripod::CacheStore that uses Memcached.
7
+ # Note: Make sure you set the memcached -I (slab size) to big enough to store each result,
8
+ # and set the -m (total size) to something quite big (or the cache will recycle too often).
9
+ class MemcachedCacheStore
10
+
11
+ # initialize a memcached cache store at the specified port (default 'localhost:11211')
12
+ def initialize(location)
13
+ @dalli = Dalli::Client.new(location)
14
+ end
15
+
16
+ # takes a block
17
+ def fetch(key)
18
+ raise ArgumentError.new("expected a block") unless block_given?
19
+
20
+ @dalli.fetch(key) do
21
+ yield
22
+ end
23
+ end
24
+
25
+ def write(key, data)
26
+ @dalli.set(key, data)
27
+ end
28
+
29
+ def read(key)
30
+ @dalli.get(key)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -9,10 +9,13 @@ module Tripod
9
9
  # Execute the query and return a +ResourceCollection+ of all hydrated resources
10
10
  # +ResourceCollection+ is an +Enumerable+, Array-like object.
11
11
  def resources
12
- Tripod::ResourceCollection.new(resources_from_sparql(build_select_query))
12
+ Tripod::ResourceCollection.new(
13
+ resources_from_sparql(build_select_query)
14
+ )
13
15
  end
14
16
 
15
17
  # Execute the query and return the first result as a hydrated resource
18
+
16
19
  def first
17
20
  sq = Tripod::SparqlQuery.new(build_select_query)
18
21
  first_sparql = sq.as_first_query_str
@@ -26,8 +26,6 @@ module Tripod
26
26
 
27
27
  if resource_class._RDF_TYPE
28
28
  self.where("?uri a <#{resource_class._RDF_TYPE.to_s}>")
29
- else
30
- self.where("?uri ?p ?o")
31
29
  end
32
30
 
33
31
  self.graph_uri = resource_class._GRAPH_URI.to_s if resource_class._GRAPH_URI
@@ -86,6 +84,7 @@ module Tripod
86
84
  # @return [ Tripod::Criteria ] A criteria object
87
85
  def graph(graph_uri)
88
86
  self.graph_uri = graph_uri.to_s
87
+ self
89
88
  end
90
89
  end
91
90
  end
@@ -76,8 +76,7 @@ module Tripod::Finders
76
76
  # execute a query to return all objects (restricted by this class's rdf_type if specified)
77
77
  # returns a criteria object
78
78
  def all
79
- criteria = Tripod::Criteria.new(self)
80
- criteria
79
+ where('?uri ?p ?o')
81
80
  end
82
81
 
83
82
  def count
@@ -38,7 +38,7 @@ module Tripod::Resource
38
38
  @graph_uri = RDF::URI(graph_uri) if graph_uri
39
39
  @repository = RDF::Repository.new
40
40
  @new_record = true
41
- self.rdf_type = self.class._RDF_TYPE if respond_to?(:rdf_type=)
41
+ self.rdf_type = self.class._RDF_TYPE if respond_to?(:rdf_type=) && self.class._RDF_TYPE
42
42
  end
43
43
 
44
44
  # default comparison is via the uri
@@ -95,8 +95,8 @@ module Tripod::Resource
95
95
  # makes a "field" on this model called rdf_type
96
96
  # and sets a class level _RDF_TYPE variable with the rdf_type passed in.
97
97
  def rdf_type(new_rdf_type)
98
- field :rdf_type, RDF.type
99
98
  self._RDF_TYPE = RDF::URI.new(new_rdf_type.to_s)
99
+ field :rdf_type, RDF.type, :multivalued => true # things can have more than 1 type and often do
100
100
  end
101
101
 
102
102
  def graph_uri(new_graph_uri)
@@ -31,6 +31,10 @@ module Tripod
31
31
  resources[*args]
32
32
  end
33
33
 
34
+ def ==(other)
35
+ self.to_nt == other.to_nt
36
+ end
37
+
34
38
  # for n-triples we can just concatenate them
35
39
  def to_nt
36
40
  nt = ""
@@ -16,12 +16,24 @@ module Tripod::SparqlClient
16
16
  begin
17
17
  params = { :params => {:query => sparql, :output => format } }
18
18
  hdrs = headers.merge(params)
19
- RestClient::Request.execute(
20
- :method => :get,
21
- :url => Tripod.query_endpoint,
22
- :headers => hdrs,
23
- :timeout => Tripod.timeout_seconds,
24
- )
19
+
20
+ make_the_call =
21
+ -> { RestClient::Request.execute(
22
+ :method => :get,
23
+ :url => Tripod.query_endpoint,
24
+ :headers => hdrs,
25
+ :timeout => Tripod.timeout_seconds
26
+ ) }
27
+
28
+ if Tripod.cache_store # if a cache store is configured
29
+ # SHA-2 the key to keep the it within the small limit for many cache stores (e.g. Memcached is 250bytes)
30
+ # Note: SHA2's are pretty certain to be unique http://en.wikipedia.org/wiki/SHA-2.
31
+ key = 'SPARQL-QUERY-' + Digest::SHA2.hexdigest([format, headers, sparql].join(" "))
32
+ Tripod.cache_store.fetch(key, &make_the_call)
33
+ else
34
+ make_the_call.call()
35
+ end
36
+
25
37
  rescue RestClient::BadRequest => e
26
38
  # just re-raise as a BadSparqlRequest Exception
27
39
  raise Tripod::Errors::BadSparqlRequest.new(e.http_body, e)
@@ -1,3 +1,3 @@
1
1
  module Tripod
2
- VERSION = "0.3.5"
2
+ VERSION = "0.3.6"
3
3
  end
data/lib/tripod.rb CHANGED
@@ -51,12 +51,17 @@ module Tripod
51
51
  mattr_accessor :timeout_seconds
52
52
  @@timeout_seconds = 30
53
53
 
54
- # Use +configure+ to override configuration in an app, e.g.:
54
+ mattr_accessor :cache_store
55
+
56
+ # Use +configure+ to override configuration in an app, (defaults shown)
55
57
  #
56
58
  # Tripod.configure do |config|
57
59
  # config.update_endpoint = 'http://127.0.0.1:3030/tripod/update'
58
60
  # config.query_endpoint = 'http://127.0.0.1:3030/tripod/sparql'
59
- # config.timeout_seconds = 30
61
+ # config.timeout_seconds = 30#
62
+ # config.cache_store = nil #e.g Tripod::CacheStores::MemcachedCacheStore.new('localhost:11211')
63
+ # # note: if using memcached, make sure you set the -I (slab size) to big enough to store each result
64
+ # # and set the -m (total size) to something quite big (or the cache will recycle too often).
60
65
  # end
61
66
  #
62
67
  def self.configure
@@ -65,6 +70,8 @@ module Tripod
65
70
 
66
71
  end
67
72
 
73
+ require 'tripod/cache_stores/memcached_cache_store'
74
+
68
75
  require "tripod/extensions"
69
76
  require "tripod/sparql_client"
70
77
  require "tripod/sparql_query"
@@ -6,6 +6,7 @@ class Person
6
6
  graph_uri 'http://graph'
7
7
 
8
8
  field :name, 'http://name'
9
+ field :father, 'http://father'
9
10
  field :knows, 'http://knows', :multivalued => true
10
11
  field :aliases, 'http://alias', :multivalued => true
11
12
  field :age, 'http://age', :datatype => RDF::XSD.integer
@@ -2,14 +2,40 @@ require "spec_helper"
2
2
 
3
3
  describe Tripod::Attributes do
4
4
  describe ".read_attribute" do
5
+
6
+ let!(:other_person) do
7
+ p = Person.new('http://garry')
8
+ p.save!
9
+ p
10
+ end
11
+
5
12
  let(:person) do
6
13
  p = Person.new('http://barry')
7
14
  p.name = 'Barry'
15
+ p.father = other_person.uri
8
16
  p
9
17
  end
10
18
 
11
- it "should read the given attribute" do
12
- person[:name].should == 'Barry'
19
+ context "for a literal" do
20
+ it "should return an RDF::Literal" do
21
+ person[:name].class.should == RDF::Literal
22
+ end
23
+ it "should read the given attribute" do
24
+ person[:name].should == 'Barry'
25
+ end
26
+ end
27
+
28
+ context "for a uri" do
29
+
30
+ it "should return an RDF::URI" do
31
+ puts person[:father]
32
+
33
+ person[:father].class.should == RDF::URI
34
+ end
35
+
36
+ it "should read the given attribute" do
37
+ person[:father].should == other_person.uri
38
+ end
13
39
  end
14
40
 
15
41
  context "where the attribute is multi-valued" do
@@ -3,11 +3,11 @@ require "spec_helper"
3
3
  describe Tripod::Criteria do
4
4
 
5
5
  let(:person_criteria) do
6
- c = Tripod::Criteria.new(Person)
6
+ c = Person.all #Tripod::Criteria.new(Person)
7
7
  end
8
8
 
9
9
  let(:resource_criteria) do
10
- c = Tripod::Criteria.new(Resource)
10
+ c = Resource.all #Tripod::Criteria.new(Resource)
11
11
  end
12
12
 
13
13
  let!(:john) do
@@ -28,14 +28,14 @@ describe Tripod::Criteria do
28
28
 
29
29
  context "for a class with an rdf_type and graph" do
30
30
  it "should return a SELECT query based with an rdf type restriction" do
31
- person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> } }"
31
+ person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> . ?uri ?p ?o } }"
32
32
  end
33
33
 
34
34
  context "and extra restrictions" do
35
35
  before { person_criteria.where("[pattern]") }
36
36
 
37
37
  it "should return a SELECT query with the extra restriction" do
38
- person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> . [pattern] } }"
38
+ person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> . ?uri ?p ?o . [pattern] } }"
39
39
  end
40
40
  end
41
41
 
@@ -43,7 +43,7 @@ describe Tripod::Criteria do
43
43
  before { person_criteria.graph("http://anothergraph") }
44
44
 
45
45
  it "should override the graph in the query" do
46
- person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://anothergraph> as ?graph) WHERE { GRAPH <http://anothergraph> { ?uri a <http://person> } }"
46
+ person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://anothergraph> as ?graph) WHERE { GRAPH <http://anothergraph> { ?uri a <http://person> . ?uri ?p ?o } }"
47
47
  end
48
48
  end
49
49
  end
@@ -116,7 +116,6 @@ describe Tripod::Criteria do
116
116
 
117
117
  describe "#count" do
118
118
 
119
-
120
119
  it "should return a set of hydrated objects for the criteria" do
121
120
  person_criteria.count.should == 2
122
121
  person_criteria.where("?uri <http://name> 'John'").count.should ==1
@@ -129,7 +128,7 @@ describe Tripod::Criteria do
129
128
  end
130
129
 
131
130
  it "should execute the right Sparql" do
132
- sparql = "SELECT COUNT(*) { SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> } } LIMIT 10 OFFSET 20 }"
131
+ sparql = "SELECT COUNT(*) { SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> . ?uri ?p ?o } } LIMIT 10 OFFSET 20 }"
133
132
  Tripod::SparqlClient::Query.should_receive(:select).with(sparql).and_call_original
134
133
  Person.all.limit(10).offset(20).count
135
134
  end
@@ -2,9 +2,9 @@ require "spec_helper"
2
2
 
3
3
  describe Tripod::Criteria do
4
4
 
5
- let(:person_criteria) { Tripod::Criteria.new(Person) }
5
+ let(:person_criteria) { Person.all }
6
6
 
7
- let(:resource_criteria) { Tripod::Criteria.new(Resource) }
7
+ let(:resource_criteria) { Resource.all }
8
8
 
9
9
  describe "#initialize" do
10
10
 
@@ -18,7 +18,7 @@ describe Tripod::Criteria do
18
18
 
19
19
  context "with rdf_type set on the class" do
20
20
  it "should initialize the where clauses to include a type restriction" do
21
- person_criteria.where_clauses.should == ["?uri a <http://person>"]
21
+ person_criteria.where_clauses.should == ["?uri a <http://person>", "?uri ?p ?o"]
22
22
  end
23
23
  end
24
24
 
@@ -65,8 +65,8 @@ describe Tripod::Finders do
65
65
  end
66
66
 
67
67
  describe ".all" do
68
- it "should make and return a new criteria for the current class" do
69
- Person.all.should == Tripod::Criteria.new(Person)
68
+ it "should make and return a new criteria for the current class, with a where clause of ?uri ?p ?o already started" do
69
+ Person.all.should == Tripod::Criteria.new(Person).where("?uri ?p ?o")
70
70
  end
71
71
  end
72
72
 
@@ -23,7 +23,7 @@ describe Tripod::Resource do
23
23
 
24
24
  context "with rdf_type specified at class level" do
25
25
  it "sets the rdf type from the class" do
26
- person.rdf_type.to_s.should == 'http://person'
26
+ person.rdf_type.should == ['http://person']
27
27
  end
28
28
  end
29
29
 
data/tripod.gemspec CHANGED
@@ -27,4 +27,5 @@ Gem::Specification.new do |gem|
27
27
  gem.add_dependency "rdf-json"
28
28
  gem.add_dependency "json-ld"
29
29
  gem.add_dependency "guid"
30
+ gem.add_dependency "dalli", "~> 2.6"
30
31
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tripod
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,11 +11,11 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-02-25 00:00:00.000000000 Z
14
+ date: 2013-03-13 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rest-client
18
- requirement: &70333194336060 !ruby/object:Gem::Requirement
18
+ requirement: &70167526722900 !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ! '>='
@@ -23,10 +23,10 @@ dependencies:
23
23
  version: '0'
24
24
  type: :runtime
25
25
  prerelease: false
26
- version_requirements: *70333194336060
26
+ version_requirements: *70167526722900
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
- requirement: &70333194335020 !ruby/object:Gem::Requirement
29
+ requirement: &70167526720820 !ruby/object:Gem::Requirement
30
30
  none: false
31
31
  requirements:
32
32
  - - ~>
@@ -34,10 +34,10 @@ dependencies:
34
34
  version: '3.1'
35
35
  type: :runtime
36
36
  prerelease: false
37
- version_requirements: *70333194335020
37
+ version_requirements: *70167526720820
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: equivalent-xml
40
- requirement: &70333194334540 !ruby/object:Gem::Requirement
40
+ requirement: &70167518449700 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
43
  - - ! '>='
@@ -45,10 +45,10 @@ dependencies:
45
45
  version: '0'
46
46
  type: :runtime
47
47
  prerelease: false
48
- version_requirements: *70333194334540
48
+ version_requirements: *70167518449700
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: rdf
51
- requirement: &70333194333460 !ruby/object:Gem::Requirement
51
+ requirement: &70167518447940 !ruby/object:Gem::Requirement
52
52
  none: false
53
53
  requirements:
54
54
  - - ~>
@@ -56,10 +56,10 @@ dependencies:
56
56
  version: '1.0'
57
57
  type: :runtime
58
58
  prerelease: false
59
- version_requirements: *70333194333460
59
+ version_requirements: *70167518447940
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: rdf-rdfxml
62
- requirement: &70333194332500 !ruby/object:Gem::Requirement
62
+ requirement: &70167518446320 !ruby/object:Gem::Requirement
63
63
  none: false
64
64
  requirements:
65
65
  - - ! '>='
@@ -67,10 +67,10 @@ dependencies:
67
67
  version: '0'
68
68
  type: :runtime
69
69
  prerelease: false
70
- version_requirements: *70333194332500
70
+ version_requirements: *70167518446320
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: rdf-n3
73
- requirement: &70333194331640 !ruby/object:Gem::Requirement
73
+ requirement: &70167518445720 !ruby/object:Gem::Requirement
74
74
  none: false
75
75
  requirements:
76
76
  - - ! '>='
@@ -78,10 +78,10 @@ dependencies:
78
78
  version: '0'
79
79
  type: :runtime
80
80
  prerelease: false
81
- version_requirements: *70333194331640
81
+ version_requirements: *70167518445720
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: rdf-json
84
- requirement: &70333194331040 !ruby/object:Gem::Requirement
84
+ requirement: &70167518444840 !ruby/object:Gem::Requirement
85
85
  none: false
86
86
  requirements:
87
87
  - - ! '>='
@@ -89,10 +89,10 @@ dependencies:
89
89
  version: '0'
90
90
  type: :runtime
91
91
  prerelease: false
92
- version_requirements: *70333194331040
92
+ version_requirements: *70167518444840
93
93
  - !ruby/object:Gem::Dependency
94
94
  name: json-ld
95
- requirement: &70333194330380 !ruby/object:Gem::Requirement
95
+ requirement: &70167518444020 !ruby/object:Gem::Requirement
96
96
  none: false
97
97
  requirements:
98
98
  - - ! '>='
@@ -100,10 +100,10 @@ dependencies:
100
100
  version: '0'
101
101
  type: :runtime
102
102
  prerelease: false
103
- version_requirements: *70333194330380
103
+ version_requirements: *70167518444020
104
104
  - !ruby/object:Gem::Dependency
105
105
  name: guid
106
- requirement: &70333194329820 !ruby/object:Gem::Requirement
106
+ requirement: &70167518443180 !ruby/object:Gem::Requirement
107
107
  none: false
108
108
  requirements:
109
109
  - - ! '>='
@@ -111,7 +111,18 @@ dependencies:
111
111
  version: '0'
112
112
  type: :runtime
113
113
  prerelease: false
114
- version_requirements: *70333194329820
114
+ version_requirements: *70167518443180
115
+ - !ruby/object:Gem::Dependency
116
+ name: dalli
117
+ requirement: &70167518442440 !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ~>
121
+ - !ruby/object:Gem::Version
122
+ version: '2.6'
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: *70167518442440
115
126
  description: RDF ruby ORM
116
127
  email:
117
128
  - ric@swirrl.com
@@ -128,6 +139,7 @@ files:
128
139
  - Rakefile
129
140
  - lib/tripod.rb
130
141
  - lib/tripod/attributes.rb
142
+ - lib/tripod/cache_stores/memcached_cache_store.rb
131
143
  - lib/tripod/components.rb
132
144
  - lib/tripod/criteria.rb
133
145
  - lib/tripod/criteria/execution.rb