tripod 0.9.7 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d0d0bb81441871fbdb4115e399bca413f309c7c4
4
- data.tar.gz: 3b72bae40446b70dd252383f82193c4764a2b669
3
+ metadata.gz: 9c4f4a91c3753acb50660407e1862de999314cf6
4
+ data.tar.gz: 097ab255b4abf50836e5e13844dd96ccfd74e036
5
5
  SHA512:
6
- metadata.gz: 614d0632c00fd4b5c99347f035fad525a2e9a9cf9240cd9376bf1c6fa1585ccda99a28ff0c8f74c988f7c8480723845195b6ffc74d05b5a7c1321ba849436062
7
- data.tar.gz: 515dafcdb449015ef2b8728b00c65687f3d777e232022e34e9d96311a77229a7c9b2eea7332ea6dae7c19c34ba34a52e2b6117bc038fa81314cbba64b819b795
6
+ metadata.gz: cd7b93b8990be65d54c964f347420d74c2c1d2327d9356e4d32e6d831cd751fb96916788e27be8b212d25400f2e7fe0f1ee68857ff28e0601473bd89805a20ec
7
+ data.tar.gz: bda4ed335ee7b00600ffaf219805b7f725a4f59d292a7f7a9ce428df017e8bacbf554dfdc8da8a63a2e281e5aad510ecb8eba1fdface9587682887c5526863b7
@@ -19,6 +19,7 @@ module Tripod::Components
19
19
  include Tripod::Validations
20
20
  include Tripod::Persistence
21
21
  include Tripod::Fields
22
+ include Tripod::Links
22
23
  include Tripod::Finders
23
24
  include Tripod::Repository
24
25
  include Tripod::EagerLoading
@@ -6,6 +6,7 @@ module Tripod::Fields
6
6
  # Set readers for the instance variables.
7
7
  attr_accessor :name, :predicate, :options, :datatype, :is_uri, :multivalued
8
8
  alias_method :is_uri?, :is_uri
9
+ alias_method :multivalued?, :multivalued
9
10
 
10
11
  # Create the new field with a name and optional additional options.
11
12
  #
data/lib/tripod/fields.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
  require "tripod/fields/standard"
3
3
 
4
- # This module defines behaviour for fields.
4
+ # This module defines behaviour for fields.
5
5
  module Tripod::Fields
6
6
  extend ActiveSupport::Concern
7
7
 
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ module Tripod::Links
3
+ # Defines the behaviour for defined links in the resource.
4
+ class LinkedFrom
5
+
6
+ # Set readers for the instance variables.
7
+ attr_accessor :name, :incoming_field, :options, :incoming_field_name, :class_name
8
+
9
+ # Create the new link with a name and optional additional options.
10
+ def initialize(name, incoming_field_name, options = {})
11
+ @name = name
12
+ @options = options
13
+ @incoming_field_name = incoming_field_name
14
+ # if class name not supplied, guess from the field name
15
+ @class_name = options[:class_name] || name.to_s.singularize.classify
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ module Tripod::Links
3
+ # Defines the behaviour for defined links in the resource.
4
+ class LinkedTo
5
+
6
+ # Set readers for the instance variables.
7
+ attr_accessor :name, :predicate, :options, :multivalued, :field_name, :class_name
8
+ alias_method :multivalued?, :multivalued
9
+
10
+ # Create the new link with a name and optional additional options.
11
+ def initialize(name, predicate, options = {})
12
+ @name = name
13
+ @options = options
14
+ @predicate = RDF::URI.new(predicate.to_s)
15
+ @multivalued = options[:multivalued] || false
16
+ @class_name = options[:class_name] || @name.to_s.classify
17
+ @field_name = options[:field_name] || (@name.to_s + ( @multivalued ? "_uris" : "_uri" )).to_sym
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,172 @@
1
+ # encoding: utf-8
2
+ require "tripod/links/linked_to"
3
+ require "tripod/links/linked_from"
4
+
5
+ # This module defines behaviour for fields.
6
+ module Tripod::Links
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :linked_tos
11
+ class_attribute :linked_froms
12
+ self.linked_tos = {}
13
+ self.linked_froms = {}
14
+ end
15
+
16
+ module ClassMethods
17
+
18
+ # Define a link to another resource. Creates relevant fields and getter/setter methods. Note that the getter only retrives saved resources from the db.
19
+ #
20
+ # @example Define a link away from resources of this class to resources of class Organisation
21
+ # linked_to :organisation, 'http://example.com/name'
22
+ #
23
+ # @example Define a multivalued link away from resources of this class (can be combined with other options)
24
+ # linked_to :organisations, 'http://example.com/modified_at', multivalued: true
25
+ #
26
+ # @example Define a link away from resources of this class, specifying the class and the field name that will be generated
27
+ # linked_to :org, 'http://example.com/modified_at', class_name: 'Organisation', field: my_field
28
+ #
29
+ # @param [ Symbol ] name The name of the link.
30
+ # @param [ String, RDF::URI ] predicate The predicate for the field.
31
+ # @param [ Hash ] options The options to pass to the field.
32
+ #
33
+ # @option options [ Boolean ] multivalued Is this a multi-valued field? Default is false.
34
+ # @option options [ String ] class_name The name of the class of resource which we're linking to (normally will derive this from the link name)
35
+ # @option options [ Symbol ] field_name the symbol of the field that will be generated (normally will just add _uri or _uris to the link name)
36
+ #
37
+ # @return [ LinkedTo ] The generated link
38
+ def linked_to(name, predicate, options = {})
39
+ add_linked_to(name, predicate, options)
40
+ end
41
+
42
+
43
+ # Define that another resource links to this one. Creates a getter with the name you specify.
44
+ # For this to work, the incoming class needs to define a linked_to relationship.
45
+ # Just creates the relevant getter which always return an array of objects.
46
+ #
47
+ # @example make a method called people which returns Dog objects, via the linked_to :owner field on Dog. We guess the class name based on the linked_from name.
48
+ #  linked_from :dogs, :owner
49
+ #
50
+ # @example make a method called doggies which returns Dog objects, via the linked_to :person field on Dog.
51
+ #  linked_from :doggies, :person, class_name: 'Dog'
52
+ #
53
+ # @param [ Symbol ] name The name of the link.
54
+ # @param [ Symbol ] incoming_field_name The name of the linked_to relationship on the other class
55
+ # @param [ Hash ] options The options to pass to the field.
56
+ #
57
+ # @option options [ String ] class_name The name of the class that links to this resource, if we can't guess it from the link name
58
+ #
59
+ # @return [ LinkedTo ] The generated link
60
+ def linked_from(name, incoming_field_name, options = {})
61
+ add_linked_from(name, incoming_field_name, options)
62
+ end
63
+
64
+ protected
65
+
66
+ def add_linked_to(name, predicate, options={})
67
+ link = linked_to_for(name, predicate, options)
68
+ linked_tos[name] = link
69
+
70
+ # create the field (always is_uri)
71
+ add_field(link.field_name, predicate, options.merge(is_uri: true))
72
+
73
+ create_linked_to_accessors(name, name)
74
+ end
75
+
76
+ def add_linked_from(name, incoming_field_name, options={})
77
+ link = linked_from_for(name, incoming_field_name, options)
78
+ linked_froms[name] = link
79
+
80
+ create_linked_from_getter(name, name, link)
81
+ end
82
+
83
+ def create_linked_to_accessors(name, meth)
84
+ link = linked_tos[name]
85
+
86
+ create_linked_to_getter(name, meth, link)
87
+ create_linked_to_setter(name, meth, link)
88
+ end
89
+
90
+ def create_linked_from_getter(name, meth, link)
91
+
92
+ generated_methods.module_eval do
93
+ re_define_method(meth) do
94
+ klass = Kernel.const_get(link.class_name)
95
+
96
+ incoming_link = klass.linked_tos[link.incoming_field_name.to_sym]
97
+ incoming_predicate = klass.fields[incoming_link.field_name].predicate
98
+
99
+ # note - this will only find saved ones.
100
+ klass
101
+ .where("?uri <#{incoming_predicate.to_s}> <#{self.uri.to_s}>")
102
+ .resources
103
+ end
104
+ end
105
+ end
106
+
107
+ def create_linked_to_getter(name, meth, link)
108
+
109
+ generated_methods.module_eval do
110
+ re_define_method(meth) do
111
+
112
+ klass = Kernel.eval(link.class_name)
113
+
114
+ if link.multivalued?
115
+
116
+ # # TODO: is there a more efficient way of doing this?
117
+
118
+ # note that we can't just do a query like this:
119
+ # `klass.where('<#{self.uri.to_s}> <#{predicate.to_s}> ?uri').resources`
120
+ # ... because this will only find saved ones and we want to find resources
121
+ # whose uris have been set on the resource but not saved to db yet.
122
+
123
+ criteria = klass.where('?uri ?p ?o')
124
+
125
+ uris = read_attribute(link.field_name)
126
+
127
+ filter_str = "FILTER("
128
+ if uris.any?
129
+ filter_str += uris.map {|u| "?uri = <#{u.to_s}>" }.join(" || ")
130
+ else
131
+ filter_str += "1 = 0"
132
+ end
133
+ filter_str += ")"
134
+
135
+ criteria.where(filter_str).resources
136
+ else
137
+ klass.find(read_attribute(link.field_name)) rescue nil #look it up by it's uri
138
+ end
139
+
140
+ end
141
+ end
142
+ end
143
+
144
+ def create_linked_to_setter(name, meth, link)
145
+
146
+ generated_methods.module_eval do
147
+ re_define_method("#{meth}=") do |value|
148
+
149
+ if link.multivalued?
150
+ val = value.to_a.map{ |v| v.uri }
151
+ write_attribute( link.field_name, val)
152
+ else
153
+ # set the uri from the passed in resource
154
+ write_attribute( link.field_name, value.uri )
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ # instantiates and returns a new LinkedFrom
161
+ def linked_from_for(name, incoming_field_name, options)
162
+ Tripod::Links::LinkedFrom.new(name, incoming_field_name, options)
163
+ end
164
+
165
+ # instantiates and returns a new LinkTo
166
+ def linked_to_for(name, predicate, options)
167
+ Tripod::Links::LinkedTo.new(name, predicate, options)
168
+ end
169
+
170
+ end
171
+
172
+ end
@@ -1,3 +1,3 @@
1
1
  module Tripod
2
- VERSION = "0.9.7"
2
+ VERSION = "0.9.8"
3
3
  end
data/lib/tripod.rb CHANGED
@@ -96,6 +96,7 @@ require "tripod/errors"
96
96
  require "tripod/repository"
97
97
  require "tripod/fields"
98
98
  require "tripod/criteria"
99
+ require "tripod/links"
99
100
  require "tripod/finders"
100
101
  require "tripod/persistence"
101
102
  require "tripod/eager_loading"
@@ -0,0 +1,17 @@
1
+ class Dog
2
+
3
+ include Tripod::Resource
4
+
5
+ rdf_type 'http://example.com/dog'
6
+ graph_uri 'http://example.com/graph'
7
+
8
+ field :name, 'http://example.com/name'
9
+
10
+ linked_to :owner, 'http://example.com/owner', class_name: 'Person'
11
+ linked_to :person, 'http://example.com/person'
12
+ linked_to :friends, 'http://example.com/friend', multivalued: true, class_name: 'Dog'
13
+ linked_to :previous_owner, 'http://example.com/prevowner', class_name: 'Person', field_name: :prev_owner_uri
14
+
15
+ linked_to :arch_enemy, 'http://example.com/archenemy', class_name: 'Dog'
16
+ linked_to :enemies, 'http://example.com/enemy', class_name: 'Dog'
17
+ end
@@ -12,6 +12,9 @@ class Person
12
12
  field :age, 'http://example.com/age', :datatype => RDF::XSD.integer
13
13
  field :important_dates, 'http://example.com/importantdates', :datatype => RDF::XSD.date, :multivalued => true
14
14
 
15
+ linked_from :owns_dogs, :owner, class_name: 'Dog'
16
+ linked_from :dogs, :person
17
+
15
18
  before_save :pre_save
16
19
  before_destroy :pre_destroy
17
20
 
@@ -0,0 +1,134 @@
1
+ require "spec_helper"
2
+
3
+ describe Tripod::Links do
4
+
5
+ let(:barry) do
6
+ b = Person.new('http://example.com/id/barry')
7
+ b.name = 'Barry'
8
+ b.save!
9
+ b
10
+ end
11
+
12
+ let(:gary) do
13
+ g = Person.new('http://example.com/id/gary')
14
+ g.name = 'Gary'
15
+ g.save!
16
+ g
17
+ end
18
+
19
+ let(:jonno) do
20
+ j = Person.new('http://example.com/id/jonno')
21
+ j.name = 'Jonno'
22
+ j.save!
23
+ j
24
+ end
25
+
26
+ let(:fido) do
27
+ d = Dog.new('http://example.com/id/fido')
28
+ d.name = "fido"
29
+ d.save!
30
+ d
31
+ end
32
+
33
+ let!(:spot) do
34
+ d = Dog.new('http://example.com/id/spot')
35
+ d.name = "spot"
36
+ d.owner = barry
37
+ d.save!
38
+ d
39
+ end
40
+
41
+ let!(:rover) do
42
+ d = Dog.new('http://example.com/id/rover')
43
+ d.name = 'Rover'
44
+ d.owner = barry
45
+ d.person = gary
46
+ d.previous_owner = jonno
47
+ d.save!
48
+ d
49
+ end
50
+
51
+
52
+ describe ".linked_from" do
53
+
54
+ context "class name is specified" do
55
+ it "creates a getter which returns the resources" do
56
+ barry.owns_dogs.to_a == [rover, spot]
57
+ end
58
+ end
59
+
60
+ context "class name is not specified" do
61
+ it "creates a getter which returns the resources of the right class, based on the link name" do
62
+ gary.dogs.to_a.should == [rover]
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ describe ".linked_to" do
69
+
70
+ it "creates a getter for the field, with a default name, which returns the uri" do
71
+ rover.owner_uri.should == barry.uri
72
+ end
73
+
74
+ it "creates a setter for the link" do
75
+ rover.owner = gary
76
+ rover.owner_uri.should == gary.uri
77
+ end
78
+
79
+ context 'the class name is specified' do
80
+ it "creates a getter for the link, which returns a resource of the right type" do
81
+ rover.owner.class.should == Person
82
+ rover.owner.should == barry
83
+ end
84
+ end
85
+
86
+ context 'the class name is not specified' do
87
+ it "creates a getter for the link, which automatically returns a resource of the right type (from link name)" do
88
+ rover.person.class.should == Person
89
+ rover.person.should == gary
90
+ end
91
+ end
92
+
93
+ context 'when the field name is set to an alternative field name' do
94
+ it "uses that for the field name" do
95
+ rover.prev_owner_uri.should == jonno.uri
96
+ end
97
+ end
98
+
99
+ context 'its a multivalued field' do
100
+ it "creates a getter and setter for multiple values, instantiating the right types of resource" do
101
+ rover.friends = [fido, spot]
102
+
103
+ rover.friends.each do |f|
104
+ f.class.should == Dog
105
+ end
106
+
107
+ rover.friends.length.should == 2
108
+
109
+ rover.friends.to_a.first.uri.should == fido.uri
110
+ rover.friends.to_a.last.uri.should == spot.uri
111
+ end
112
+
113
+ it "creates field getters and setters with the _uris suffix" do
114
+ rover.friends_uris = [fido.uri, spot.uri]
115
+ rover.friends_uris.should == [fido.uri, spot.uri]
116
+ end
117
+
118
+ end
119
+
120
+ context "when the value is not set for a link" do
121
+ context "single valued" do
122
+ it "should be nil" do
123
+ rover.arch_enemy.should be_nil
124
+ end
125
+ end
126
+
127
+ context "multivalued" do
128
+ it "should be nil" do
129
+ rover.enemies.should be_nil
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
data/tripod.gemspec CHANGED
@@ -19,7 +19,8 @@ Gem::Specification.new do |gem|
19
19
  gem.rubyforge_project = "tripod"
20
20
 
21
21
  gem.add_dependency "rest-client"
22
- gem.add_dependency "activemodel", "> 3.2"
22
+ gem.add_dependency "activemodel", "~> 3.2"
23
+ gem.add_dependency "activesupport", "~> 3.2"
23
24
  gem.add_dependency "equivalent-xml"
24
25
  gem.add_dependency "rdf", "~> 1.1"
25
26
  gem.add_dependency "rdf-rdfxml"
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.9.7
4
+ version: 0.9.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ric Roberts
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-06-19 00:00:00.000000000 Z
13
+ date: 2014-06-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rest-client
@@ -30,14 +30,28 @@ dependencies:
30
30
  name: activemodel
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
- - - ">"
33
+ - - "~>"
34
34
  - !ruby/object:Gem::Version
35
35
  version: '3.2'
36
36
  type: :runtime
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
- - - ">"
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '3.2'
43
+ - !ruby/object:Gem::Dependency
44
+ name: activesupport
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.2'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
41
55
  - !ruby/object:Gem::Version
42
56
  version: '3.2'
43
57
  - !ruby/object:Gem::Dependency
@@ -207,6 +221,9 @@ files:
207
221
  - lib/tripod/fields/standard.rb
208
222
  - lib/tripod/finders.rb
209
223
  - lib/tripod/graphs.rb
224
+ - lib/tripod/links.rb
225
+ - lib/tripod/links/linked_from.rb
226
+ - lib/tripod/links/linked_to.rb
210
227
  - lib/tripod/locale/en.yml
211
228
  - lib/tripod/persistence.rb
212
229
  - lib/tripod/predicates.rb
@@ -220,6 +237,7 @@ files:
220
237
  - lib/tripod/streaming.rb
221
238
  - lib/tripod/validations/is_url.rb
222
239
  - lib/tripod/version.rb
240
+ - spec/app/models/dog.rb
223
241
  - spec/app/models/person.rb
224
242
  - spec/app/models/resource.rb
225
243
  - spec/spec_helper.rb
@@ -230,6 +248,7 @@ files:
230
248
  - spec/tripod/fields_spec.rb
231
249
  - spec/tripod/finders_spec.rb
232
250
  - spec/tripod/graphs_spec.rb
251
+ - spec/tripod/links_spec.rb
233
252
  - spec/tripod/memcached_cache_store_spec.rb
234
253
  - spec/tripod/persistence_spec.rb
235
254
  - spec/tripod/predicates_spec.rb
@@ -266,6 +285,7 @@ signing_key:
266
285
  specification_version: 4
267
286
  summary: Active Model style RDF ORM
268
287
  test_files:
288
+ - spec/app/models/dog.rb
269
289
  - spec/app/models/person.rb
270
290
  - spec/app/models/resource.rb
271
291
  - spec/spec_helper.rb
@@ -276,6 +296,7 @@ test_files:
276
296
  - spec/tripod/fields_spec.rb
277
297
  - spec/tripod/finders_spec.rb
278
298
  - spec/tripod/graphs_spec.rb
299
+ - spec/tripod/links_spec.rb
279
300
  - spec/tripod/memcached_cache_store_spec.rb
280
301
  - spec/tripod/persistence_spec.rb
281
302
  - spec/tripod/predicates_spec.rb