tripod 0.0.2 → 0.0.3

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
@@ -1,7 +1,13 @@
1
1
  # Tripod
2
2
 
3
- Active Model style Ruby ORM for RDF data, inspired by http://github.com/mongoid/mongoid
3
+ ActiveModel-style Ruby ORM for RDF data.
4
4
 
5
- Warning: Work still in progress / experimental. Not production ready!
5
+ gem install tripod
6
6
 
7
- Copyright (c) 2012 Swirrl IT Limited. http://swirrl.com. Released under MIT License
7
+ [Documentation](http://rubydoc.info/github/Swirrl/tripod/master/frames)
8
+
9
+ __Warning: Work still in progress / experimental. Not production ready!__
10
+
11
+ Heavily inspired by [Durran Jordan's](https://github.com/durran) excellent [Mongoid](http://mongoid.org/en/mongoid/) ORM for [MongoDB](http://www.mongodb.org/).
12
+
13
+ Copyright (c) 2012 [Swirrl IT Limited](http://swirrl.com). Released under MIT License
data/lib/tripod.rb CHANGED
@@ -60,6 +60,7 @@ module Tripod
60
60
 
61
61
  end
62
62
 
63
+ require "tripod/extensions"
63
64
  require "tripod/sparql_client"
64
65
 
65
66
  require "tripod/attributes"
@@ -58,12 +58,8 @@ module Tripod::Attributes
58
58
  # 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)
59
59
  #
60
60
  # @example Write the attribute.
61
- # person.append_attribute('http://title', "Mrs.")
62
- # person.append_attribute('http://title', "Ms.")
63
- #
64
- # @example Write the attribute (alternate syntax.)
65
- # person['http://title'] << "Mrs."
66
- # person['http://title'] << "Ms."
61
+ # person.append_to_attribute('http://title', "Mrs.")
62
+ # person.append_to_attribute('http://title', "Ms.")
67
63
  #
68
64
  # @param [ String, RDF::URI ] predicate_uri The uri of the attribute to update.
69
65
  # @param [ Object ] value The values to append for the attribute. Should compatible with RDF::Terms
@@ -76,5 +72,6 @@ module Tripod::Attributes
76
72
  @repository.delete( statement )
77
73
  end
78
74
  end
75
+ alias :delete :remove_attribute
79
76
 
80
77
  end
@@ -0,0 +1,2 @@
1
+ # encoding: utf-8
2
+ require "tripod/extensions/module"
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ module Tripod::Extensions
3
+ module Module
4
+
5
+ # Redefine the method. Will undef the method if it exists or simply
6
+ # just define it.
7
+ #
8
+ # @example Redefine the method.
9
+ # Object.re_define_method("exists?") do
10
+ # self
11
+ # end
12
+ #
13
+ # @param [ String, Symbol ] name The name of the method.
14
+ # @param [ Proc ] block The method body.
15
+ #
16
+ # @return [ Method ] The new method.
17
+ def re_define_method(name, &block)
18
+ undef_method(name) if method_defined?(name)
19
+ define_method(name, &block)
20
+ end
21
+ end
22
+ end
23
+
24
+ ::Module.__send__(:include, Tripod::Extensions::Module)
data/lib/tripod/fields.rb CHANGED
@@ -1,7 +1,175 @@
1
1
  # encoding: utf-8
2
+ require "tripod/fields/standard"
2
3
 
3
4
  # This module defines behaviour for fields.
4
5
  module Tripod::Fields
5
6
  extend ActiveSupport::Concern
6
7
 
8
+ included do
9
+ class_attribute :fields
10
+ self.fields = {}
11
+ end
12
+
13
+ module ClassMethods
14
+
15
+ # Defines all the fields that are accessible on the Resource
16
+ # For each field that is defined, a getter and setter will be
17
+ # added as an instance method to the Resource.
18
+ #
19
+ # @example Define a field.
20
+ # field :name, :predicate => 'http://name'
21
+ #
22
+ # @param [ Symbol ] name The name of the field.
23
+ # @param [ String, RDF::URI ] predicate The predicate for the field.
24
+ # @param [ Hash ] options The options to pass to the field.
25
+ #
26
+ # @option options [ String, RDF::URI ] datatype The uri of the datatype for the field (will be used to create an RDF::Literal of the right type on the way in only).
27
+ # @option options [ Boolean ] multivalued Is this a multi-valued field? Default is false.
28
+ #
29
+ # @return [ Field ] The generated field
30
+ def field(name, predicate, options = {})
31
+ named = name.to_s
32
+ # TODO: validate the field params/options here..
33
+ add_field(named, predicate, options)
34
+ end
35
+
36
+
37
+ def new_value_for_field(value, field)
38
+ if field.datatype
39
+ RDF::Literal.new(value, :datatype => field.datatype)
40
+ else
41
+ value
42
+ end
43
+ end
44
+
45
+ protected
46
+
47
+ # Define a field attribute for the +Resource+.
48
+ #
49
+ # @example Set the field.
50
+ # Person.add_field(:name, :predicate => 'http://myfield')
51
+ #
52
+ # @param [ Symbol ] name The name of the field.
53
+ # @param [ String, RDF::URI ] predicate The predicate for the field.
54
+ # @param [ Hash ] options The hash of options.
55
+ def add_field(name, predicate, options = {})
56
+ # create a field object and store it in our hash
57
+ field = field_for(name, predicate, options)
58
+ fields[name] = field
59
+
60
+ # set up the accessors for the fields
61
+ create_accessors(name, name, options)
62
+ field
63
+ end
64
+
65
+ # Create the field accessors.
66
+ #
67
+ # @example Generate the accessors.
68
+ # Person.create_accessors(:name, "name")
69
+ # person.name #=> returns the field
70
+ # person.name = "" #=> sets the field
71
+ # person.name? #=> Is the field present?
72
+ #
73
+ # @param [ Symbol ] name The name of the field.
74
+ # @param [ Symbol ] meth The name of the accessor.
75
+ # @param [ Hash ] options The options.
76
+ def create_accessors(name, meth, options = {})
77
+ field = fields[name]
78
+
79
+ create_field_getter(name, meth, field)
80
+ create_field_setter(name, meth, field)
81
+ create_field_check(name, meth, field)
82
+ end
83
+
84
+ # Create the getter method for the provided field.
85
+ #
86
+ # @example Create the getter.
87
+ # Model.create_field_getter("name", "name", field)
88
+ #
89
+ # @param [ String ] name The name of the attribute.
90
+ # @param [ String ] meth The name of the method.
91
+ # @param [ Field ] field The field.
92
+ def create_field_getter(name, meth, field)
93
+ generated_methods.module_eval do
94
+ 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(&:to_s)
103
+ else
104
+ attr_values.first.to_s
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ # Create the setter method for the provided field.
111
+ #
112
+ # @example Create the setter.
113
+ # Model.create_field_setter("name", "name")
114
+ #
115
+ # @param [ String ] name The name of the attribute.
116
+ # @param [ String ] meth The name of the method.
117
+ # @param [ Field ] field The field.
118
+ def create_field_setter(name, meth, field)
119
+ generated_methods.module_eval do
120
+ re_define_method("#{meth}=") do |value|
121
+ if value.kind_of?(Array)
122
+ if field.multivalued
123
+ new_val = []
124
+ value.each do |v|
125
+ new_val << self.class.new_value_for_field(v, field)
126
+ end
127
+ else
128
+ new_val = self.class.new_value_for_field(value.first, field)
129
+ end
130
+ else
131
+ new_val = self.class.new_value_for_field(value, field)
132
+ end
133
+
134
+ write_attribute(field.predicate, new_val)
135
+ end
136
+ end
137
+ end
138
+
139
+ # Create the check method for the provided field.
140
+ #
141
+ # @example Create the check.
142
+ # Model.create_field_check("name", "name")
143
+ #
144
+ # @param [ String ] name The name of the attribute.
145
+ # @param [ String ] meth The name of the method.
146
+ def create_field_check(name, meth, field)
147
+ generated_methods.module_eval do
148
+ re_define_method("#{meth}?") do
149
+ attr = read_attribute(field.predicate)
150
+ attr == true || attr.present?
151
+ end
152
+ end
153
+ end
154
+
155
+ # Include the field methods as a module, so they can be overridden.
156
+ #
157
+ # @example Include the fields.
158
+ # Person.generated_methods
159
+ #
160
+ # @return [ Module ] The module of generated methods.
161
+ def generated_methods
162
+ @generated_methods ||= begin
163
+ mod = Module.new
164
+ include(mod)
165
+ mod
166
+ end
167
+ end
168
+
169
+
170
+ # instantiates and returns a new standard field
171
+ def field_for(name, predicate, options)
172
+ Tripod::Fields::Standard.new(name, predicate, options)
173
+ end
174
+ end
7
175
  end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ module Tripod::Fields
3
+ # Defines the behaviour for defined fields in the resource.
4
+ class Standard
5
+
6
+ # Set readers for the instance variables.
7
+ attr_accessor :name, :predicate, :options, :datatype, :multivalued
8
+
9
+ # Create the new field with a name and optional additional options.
10
+ #
11
+ # @example Create the new field.
12
+ # Field.new(:name, 'http://foo', opts)
13
+ #
14
+ # @param [ String ] name The field name.
15
+ # @param [ String, RDF::URI ] predicate The field's predicate.
16
+ # @param [ Hash ] options The field options.
17
+ #
18
+ # @option options [ String, RDF::URI ] datatype The uri of the datatype for the field (will be used to create an RDF::Literal of the right type on the way in only).
19
+ # @option options [ Boolean ] multivalued Is this a multi-valued field? Default is false.
20
+ def initialize(name, predicate, options = {})
21
+ @name = name
22
+ @options = options
23
+ @predicate = RDF::URI.new(predicate.to_s)
24
+ @datatype = RDF::URI.new(options[:datatype].to_s) if options[:datatype]
25
+ @multivalued = options[:multivalued] || false
26
+ end
27
+
28
+ end
29
+ end
@@ -14,7 +14,7 @@ module Tripod::Finders
14
14
  #
15
15
  # @param [ String, RDF::URI ] uri The uri of the resource to find
16
16
  #
17
- # @raise [ Errors::DocumentNotFound ] If no document found.
17
+ # @raise [ Tripod::Errors::ResourceNotFound ] If no resource found.
18
18
  #
19
19
  # @return [ Resource ] A single resource
20
20
  def find(uri)
@@ -29,6 +29,16 @@ module Tripod::Persistence
29
29
  end
30
30
  end
31
31
 
32
+ # Save the resource, and raise an exception if it fails.
33
+ # Note: As with save(), regardless of whether it's a new_record or not, we always make the
34
+ # db match the contents of this resource's statements.
35
+ #
36
+ # @example Save the resource.
37
+ # resource.save
38
+ #
39
+ # @raise [Tripod::Errors::Validations] if invalid
40
+ #
41
+ # @return [ true ] True is success.
32
42
  def save!()
33
43
  # try to save
34
44
  unless self.save()
@@ -13,7 +13,7 @@ module Tripod::Repository
13
13
  # person.hydrate!
14
14
  #
15
15
  # @example Hydrate the resource from a passed in graph
16
- #  person.hydrate!(my_graph)
16
+ # person.hydrate!(my_graph)
17
17
  #
18
18
  # @return [ RDF::Repository ] A reference to the repository for this instance.
19
19
  def hydrate!(graph = nil)
@@ -5,14 +5,12 @@ module Tripod::SparqlClient
5
5
 
6
6
  module Query
7
7
 
8
-
9
8
  # Runs a +sparql+ query against the endpoint. Returns a RestClient response object.
10
9
  #
11
10
  # @example Run a query
12
- # Tripload::Sparql.query('SELECT * WHERE {?s ?p ?o}')
11
+ # Tripod::SparqlClient::Query.query('SELECT * WHERE {?s ?p ?o}')
13
12
  #
14
13
  # @return [ RestClient::Response ]
15
-
16
14
  def self.query(sparql, format='json', headers = {})
17
15
 
18
16
  begin
@@ -44,7 +42,7 @@ module Tripod::SparqlClient
44
42
  # @param [ String ] raw_format valid formats are: 'json', 'text', 'csv', 'xml'
45
43
  #
46
44
  # @example Run a SELECT query
47
- Triploid::Sparql.select('SELECT * WHERE {?s ?p ?o}')
45
+ Tripod::SparqlClient::Query.select('SELECT * WHERE {?s ?p ?o}')
48
46
  #
49
47
  # @return [ Hash, String ]
50
48
  def self.select(query, raw_format=nil)
@@ -60,7 +58,7 @@ module Tripod::SparqlClient
60
58
  # Executes the +query+ and returns ntriples by default
61
59
  #
62
60
  # @example Run a DESCRIBE query
63
- # Triploid::Sparql.select('DESCRIBE <http://foo>')
61
+ # Tripod::SparqlClient::Query.select('DESCRIBE <http://foo>')
64
62
  #
65
63
  # @param [ String ] query The query to run
66
64
  # @param [ String ] accept_header The header to pass to the database.
@@ -74,6 +72,12 @@ module Tripod::SparqlClient
74
72
 
75
73
  module Update
76
74
 
75
+ # Runs a +sparql+ update against the endpoint. Returns true if success.
76
+ #
77
+ # @example Run a query
78
+ # Tripod::SparqlClient::Update.update('DELETE {?s ?p ?o} WHERE {?s ?p ?o};')
79
+ #
80
+ # @return [ true ]
77
81
  def self.update(sparql)
78
82
 
79
83
  begin
@@ -83,7 +87,7 @@ module Tripod::SparqlClient
83
87
  :timeout => Tripod.timeout_seconds,
84
88
  :payload => {:update => sparql}
85
89
  )
86
- return true
90
+ true
87
91
  rescue RestClient::BadRequest => e
88
92
  body = e.http_body
89
93
  if body.start_with?('Error 400: Parse error:')
data/lib/tripod/state.rb CHANGED
@@ -21,7 +21,7 @@ module Tripod::State
21
21
  end
22
22
 
23
23
  # Checks if the resource has been saved to the database. Returns false
24
- # if the document has been destroyed.
24
+ # if the resource has been destroyed.
25
25
  #
26
26
  # @example Is the resource persisted?
27
27
  # person.persisted?
@@ -1,3 +1,3 @@
1
1
  module Tripod
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -2,4 +2,9 @@ class Person
2
2
 
3
3
  include Tripod::Resource
4
4
 
5
+ field :name, 'http://name'
6
+ field :aliases, 'http://alias', :multivalued => true
7
+ field :age, 'http://age', :datatype => RDF::XSD.integer
8
+ field :important_dates, 'http://importantdates', :datatype => RDF::XSD.date, :multivalued => true
9
+
5
10
  end
@@ -0,0 +1,87 @@
1
+ require "spec_helper"
2
+
3
+ describe Tripod::Fields do
4
+
5
+ describe ".field" do
6
+
7
+ let(:barry) do
8
+ b = Person.new('http://barry')
9
+ b['http://name'] = 'Barry'
10
+ b['http://alias'] = ['Basildon', 'Baz']
11
+ b['http://age'] = 54
12
+ b['http://importantdates'] = [Date.new(2010,01,01), Date.new(2012,01,01)]
13
+ b
14
+ end
15
+
16
+ context "with no options" do
17
+
18
+ it "creates a getter for the field, which accesses data for the predicate, returning a single String" do
19
+ barry.name.should == "Barry"
20
+ end
21
+
22
+ it "creates a setter for the field, which sets data for the predicate" do
23
+ barry.name = "Basildon"
24
+ barry.name.should == "Basildon"
25
+ barry['http://name'].should == [RDF::Literal.new("Basildon")]
26
+ end
27
+
28
+ end
29
+
30
+ context "with multivalued option" do
31
+
32
+ it "creates a getter for the field, which accesses data for the predicate, returning an array of Strings" do
33
+ barry.aliases.should == ['Basildon', 'Baz']
34
+ end
35
+
36
+ it "creates a setter for the field, which sets data for the predicate" do
37
+ barry.aliases = ['Basildon', 'Baz', 'B-man']
38
+ barry.aliases.should == ['Basildon', 'Baz', 'B-man']
39
+ barry['http://alias'].should == [RDF::Literal.new("Basildon"),RDF::Literal.new("Baz"),RDF::Literal.new("B-man")]
40
+ end
41
+
42
+ context 'with data type' do
43
+
44
+ it "creates a getter for the field, which accesses data for the predicate, returning an array of Strings" do
45
+ barry.important_dates.class.should == Array
46
+ barry.important_dates.should == ["2010-01-01Z", "2012-01-01Z"]
47
+ end
48
+
49
+ it "creates a setter for the field, which sets data for the predicate, using the right data type" do
50
+ barry.important_dates = [Date.new(2010,01,02),Date.new(2010,01,03)]
51
+ barry['http://importantdates'].should == [ RDF::Literal.new(Date.new(2010,01,02)), RDF::Literal.new(Date.new(2010,01,03)) ]
52
+ barry['http://importantdates'].map(&:datatype).should == [RDF::XSD.date,RDF::XSD.date]
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+ context 'with data type' do
60
+
61
+ it "creates a getter for the field, which accesses data for the predicate, returning a single String" do
62
+ barry.age.should == "54"
63
+ end
64
+
65
+ it "creates a setter for the field, which sets data for the predicate, using the right data type" do
66
+ barry.age = 57
67
+ barry.age.should == '57'
68
+ barry['http://age'] = [ RDF::Literal.new(57) ]
69
+ barry['http://age'].first.datatype = RDF::XSD.integer
70
+ end
71
+
72
+ end
73
+
74
+ context 'with no data type specified' do
75
+
76
+ it "creates the right kind of literals when setting values." do
77
+ barry.name == 100 # set an integer
78
+ barry['http://name'] = [ RDF::Literal.new(100) ]
79
+ barry['http://name'].first.datatype = RDF::XSD.integer
80
+ end
81
+
82
+
83
+ end
84
+
85
+ end
86
+
87
+ 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.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-27 00:00:00.000000000 Z
12
+ date: 2012-08-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
16
- requirement: &70292958584520 !ruby/object:Gem::Requirement
16
+ requirement: &70124707968440 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70292958584520
24
+ version_requirements: *70124707968440
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activemodel
27
- requirement: &70292958583980 !ruby/object:Gem::Requirement
27
+ requirement: &70124707967900 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '3.1'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70292958583980
35
+ version_requirements: *70124707967900
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: equivalent-xml
38
- requirement: &70292958583560 !ruby/object:Gem::Requirement
38
+ requirement: &70124707967480 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70292958583560
46
+ version_requirements: *70124707967480
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rdf
49
- requirement: &70292958583020 !ruby/object:Gem::Requirement
49
+ requirement: &70124707966940 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0.3'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70292958583020
57
+ version_requirements: *70124707966940
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rdf-rdfxml
60
- requirement: &70292958582600 !ruby/object:Gem::Requirement
60
+ requirement: &70124707966520 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *70292958582600
68
+ version_requirements: *70124707966520
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rdf-n3
71
- requirement: &70292958582140 !ruby/object:Gem::Requirement
71
+ requirement: &70124707966060 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *70292958582140
79
+ version_requirements: *70124707966060
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rdf-json
82
- requirement: &70292958581720 !ruby/object:Gem::Requirement
82
+ requirement: &70124707965640 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,7 +87,7 @@ dependencies:
87
87
  version: '0'
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *70292958581720
90
+ version_requirements: *70124707965640
91
91
  description: RDF ruby ORM
92
92
  email:
93
93
  - ric@swirrl.com
@@ -107,7 +107,10 @@ files:
107
107
  - lib/tripod/errors/resource_not_found.rb
108
108
  - lib/tripod/errors/uri_not_set.rb
109
109
  - lib/tripod/errors/validations.rb
110
+ - lib/tripod/extensions.rb
111
+ - lib/tripod/extensions/module.rb
110
112
  - lib/tripod/fields.rb
113
+ - lib/tripod/fields/standard.rb
111
114
  - lib/tripod/finders.rb
112
115
  - lib/tripod/persistence.rb
113
116
  - lib/tripod/repository.rb
@@ -118,6 +121,7 @@ files:
118
121
  - spec/app/models/person.rb
119
122
  - spec/spec_helper.rb
120
123
  - spec/tripod/attributes_spec.rb
124
+ - spec/tripod/fields_spec.rb
121
125
  - spec/tripod/finders_spec.rb
122
126
  - spec/tripod/persistence_spec.rb
123
127
  - spec/tripod/repository_spec.rb
@@ -152,6 +156,7 @@ test_files:
152
156
  - spec/app/models/person.rb
153
157
  - spec/spec_helper.rb
154
158
  - spec/tripod/attributes_spec.rb
159
+ - spec/tripod/fields_spec.rb
155
160
  - spec/tripod/finders_spec.rb
156
161
  - spec/tripod/persistence_spec.rb
157
162
  - spec/tripod/repository_spec.rb