active-triples 0.3.0 → 0.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6fe9b5e7c31b1ebd51f2c529d354c27ff5a50461
4
- data.tar.gz: 96039e8d7e41040748705955d064101fce40f236
3
+ metadata.gz: 9e39f588d57dbf6bf017fd8d89be33cc2614cd11
4
+ data.tar.gz: 0cc21f34df785724b9551c5365b8c31e5de7cd12
5
5
  SHA512:
6
- metadata.gz: 6c2474afde1a6b48790bed08a67caf0e413f8339cfa5993f0ef6bcb277cf642c21bc5d0f2e1ce830f735766d704ca35cd6bacc3076a1645d52f96f28ba44cd7a
7
- data.tar.gz: abbe7fd8978112727f975413aa4cb5bb9a20cfc443c4e2f128d4469010391f20bc79e3466575e68153413b1db7763c7be1aa7f633ed6c75796e3213ac460af37
6
+ metadata.gz: ad58904cf28803ef6f977792cce6bc8127de400a7b68121e109011e1bb4bde504fe0d56c971fd26ea1bccc444d23894333a24e44ce06a9f3d8c05da0e05ed347
7
+ data.tar.gz: 3078779a4860330222361efb8e9857f33d71ed76dca1c8934287d13537aa316f71f1bbf060e1a69848d123ea0e0d2b96c4ad84606f358062c9ea5ddcb1fe719c
@@ -16,6 +16,7 @@ module ActiveTriples
16
16
  autoload :Repositories
17
17
  autoload :NodeConfig
18
18
  autoload :NestedAttributes
19
+ autoload :Identifiable
19
20
  end
20
21
 
21
22
  def self.class_from_string(class_name, container_class=Kernel)
@@ -0,0 +1,60 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext/module/delegation'
3
+
4
+ module ActiveTriples::Identifiable
5
+ extend ActiveSupport::Concern
6
+
7
+ delegate :rdf_subject, :type, to: :resource
8
+
9
+ ##
10
+ # @return [ActiveTriples::Resource] a resource that contains this object's
11
+ # graph.
12
+ def resource
13
+ @resource ||= resource_class.new(to_uri)
14
+ end
15
+
16
+ def parent
17
+ @parent ||= resource.parent
18
+ end
19
+
20
+ def parent=(val)
21
+ @parent = val
22
+ end
23
+
24
+ ##
25
+ # @return [String] a uri or slug
26
+ def to_uri
27
+ return id if respond_to? :id and !resource_class.base_uri.nil?
28
+ raise NotImplementedError
29
+ end
30
+
31
+ private
32
+ def resource_class
33
+ self.class.resource_class
34
+ end
35
+
36
+ def update_resource(&block)
37
+ resource_class.properties.each do |name, property|
38
+ if block_given?
39
+ yield name, property
40
+ else
41
+ resource.set_value(property.predicate, self.send(property.term))
42
+ end
43
+ end
44
+ end
45
+
46
+ public
47
+
48
+ module ClassMethods
49
+
50
+ delegate :configure, :property, :properties, to: :resource_class
51
+
52
+ def resource_class
53
+ @resource_class ||= Class.new(ActiveTriples::Resource)
54
+ end
55
+
56
+ def from_uri(uri, *args)
57
+ raise NotImplementedError
58
+ end
59
+ end
60
+ end
@@ -1,12 +1,13 @@
1
1
  module ActiveTriples
2
2
  class NodeConfig
3
- attr_accessor :predicate, :term, :class_name, :type, :behaviors, :multivalue
3
+ attr_accessor :predicate, :term, :class_name, :type, :behaviors, :multivalue, :cast
4
4
 
5
5
  def initialize(term, predicate, args={})
6
6
  self.term = term
7
7
  self.predicate = predicate
8
8
  self.class_name = args.delete(:class_name)
9
9
  self.multivalue = args.delete(:multivalue) { true }
10
+ self.cast = args.delete(:cast) { true }
10
11
  raise ArgumentError, "Invalid arguments for Rdf Node configuration: #{args} on #{predicate}" unless args.empty?
11
12
  yield(self) if block_given?
12
13
  end
@@ -17,6 +17,18 @@ module ActiveTriples
17
17
  initialize_generated_modules
18
18
  end
19
19
 
20
+ ##
21
+ # Registers properties for Resource-like classes
22
+ # @param [Symbol] name of the property (and its accessor methods)
23
+ # @param [Hash] opts for this property, must include a :predicate
24
+ # @yield [index] index sets solr behaviors for the property
25
+ def property(name, opts={}, &block)
26
+ self.config[name] = NodeConfig.new(name, opts[:predicate], opts.except(:predicate)).tap do |config|
27
+ config.with_index(&block) if block_given?
28
+ end
29
+ register_property(name)
30
+ end
31
+
20
32
  module ClassMethods
21
33
  def inherited(child_class) #:nodoc:
22
34
  child_class.initialize_generated_modules
@@ -53,8 +65,8 @@ module ActiveTriples
53
65
  #
54
66
  # @return [ActiveTriples::NodeConfig]
55
67
  def config_for_term_or_uri(term)
56
- return config[term.to_sym] unless term.kind_of? RDF::Resource
57
- config.each_value { |v| return v if v.predicate == term.to_uri }
68
+ return properties[term.to_s] unless term.kind_of? RDF::Resource
69
+ properties.each_value { |v| return v if v.predicate == term.to_uri }
58
70
  end
59
71
 
60
72
  ##
@@ -181,7 +181,7 @@ module ActiveTriples
181
181
  end
182
182
 
183
183
  def type
184
- self.get_values(:type).to_a.map{|x| x.rdf_subject}
184
+ self.get_values(:type).to_a
185
185
  end
186
186
 
187
187
  def type=(type)
@@ -316,8 +316,8 @@ module ActiveTriples
316
316
  def get_term(args)
317
317
  @term_cache ||= {}
318
318
  term = Term.new(self, args)
319
- @term_cache["#{term.rdf_subject}/#{term.property}"] ||= term
320
- @term_cache["#{term.rdf_subject}/#{term.property}"]
319
+ @term_cache["#{term.send(:rdf_subject)}/#{term.property}"] ||= term
320
+ @term_cache["#{term.send(:rdf_subject)}/#{term.property}"]
321
321
  end
322
322
 
323
323
  ##
@@ -30,6 +30,7 @@ module ActiveTriples
30
30
 
31
31
  def set(values)
32
32
  values = [values].compact unless values.kind_of?(Array)
33
+ values = values.to_a if values.class == Term
33
34
  empty_property
34
35
  values.each do |val|
35
36
  set_value(val)
@@ -76,13 +77,20 @@ module ActiveTriples
76
77
 
77
78
  alias_method :push, :<<
78
79
 
80
+ def []=(index, value)
81
+ values = Array.wrap(result)
82
+ raise IndexError, "Index #{index} out of bounds." if values[index].nil?
83
+ values[index] = value
84
+ self.set(values)
85
+ end
86
+
79
87
  def property_config
80
88
  return type_property if (property == RDF.type || property.to_s == "type") && (!reflections.kind_of?(Resource) || !reflections.reflect_on_property(property))
81
89
  reflections.reflect_on_property(property)
82
90
  end
83
91
 
84
92
  def type_property
85
- { :multivalue => true, :predicate => RDF.type }
93
+ { :multivalue => true, :predicate => RDF.type, :cast => false }
86
94
  end
87
95
 
88
96
  def reset!
@@ -92,15 +100,6 @@ module ActiveTriples
92
100
  value_arguments.last
93
101
  end
94
102
 
95
- def rdf_subject
96
- raise ArgumentError, "wrong number of arguments (#{value_arguments.length} for 1-2)" if value_arguments.length < 1 || value_arguments.length > 2
97
- if value_arguments.length > 1
98
- value_arguments.first
99
- else
100
- parent.rdf_subject
101
- end
102
- end
103
-
104
103
  protected
105
104
 
106
105
  def node_cache
@@ -162,6 +161,7 @@ module ActiveTriples
162
161
  # Builds the resource from the class_name specified for the
163
162
  # property.
164
163
  def make_node(value)
164
+ return value unless cast?
165
165
  klass = class_for_value(value)
166
166
  value = RDF::Node.new if value.nil?
167
167
  node = node_cache[value] if node_cache[value]
@@ -171,6 +171,11 @@ module ActiveTriples
171
171
  node
172
172
  end
173
173
 
174
+ def cast?
175
+ return true unless property_config
176
+ !!property_config[:cast]
177
+ end
178
+
174
179
  def final_parent
175
180
  @final_parent ||= begin
176
181
  parent = self.parent
@@ -199,5 +204,14 @@ module ActiveTriples
199
204
  klass
200
205
  end
201
206
 
207
+ def rdf_subject
208
+ raise ArgumentError, "wrong number of arguments (#{value_arguments.length} for 1-2)" if value_arguments.length < 1 || value_arguments.length > 2
209
+ if value_arguments.length > 1
210
+ value_arguments.first
211
+ else
212
+ parent.rdf_subject
213
+ end
214
+ end
215
+
202
216
  end
203
217
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveTriples
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+ require 'active_model'
3
+
4
+ describe ActiveTriples::Identifiable do
5
+ before do
6
+ class ActiveExample
7
+ include ActiveTriples::Identifiable
8
+
9
+ def self.property(*args)
10
+ prop = args.first
11
+
12
+ define_method prop.to_s do
13
+ resource.get_values(prop)
14
+ end
15
+
16
+ define_method "#{prop.to_s}=" do |*args|
17
+ resource.set_value(prop, *args)
18
+ end
19
+
20
+ resource_class.property(*args)
21
+ end
22
+
23
+ end
24
+ end
25
+
26
+ after do
27
+ Object.send(:remove_const, 'ActiveExample')
28
+ end
29
+
30
+ subject { ActiveExample.new }
31
+ let(:klass) { ActiveExample }
32
+
33
+ shared_context 'with data' do
34
+ let(:parent) { MyResource.new }
35
+
36
+ before do
37
+ class MyResource < ActiveTriples::Resource
38
+ property :relation, predicate: RDF::DC.relation, class_name: 'ActiveExample'
39
+ end
40
+
41
+ klass.property :title, predicate: RDF::DC.title
42
+ klass.property :identifier, predicate: RDF::DC.identifier
43
+ klass.property :description, predicate: RDF::DC.description
44
+
45
+ subject.resource.title = 'Moomin Valley in November'
46
+ subject.resource.identifier = 'moomvember'
47
+ subject.resource.description = 'The ninth and final book in the Moomin series by Finnish author Tove Jansson'
48
+ parent.relation = subject
49
+ end
50
+
51
+ after do
52
+ Object.send(:remove_const, 'MyResource')
53
+ end
54
+ end
55
+
56
+ context 'without implementation' do
57
+ describe '::from_uri' do
58
+ it 'raises a NotImplementedError' do
59
+ expect{ klass.from_uri(RDF::URI('http://example.org/blah')) }.to raise_error NotImplementedError
60
+ end
61
+ end
62
+
63
+ describe '#to_uri' do
64
+ it 'raises a NotImplementedError' do
65
+ expect{ subject.to_uri }.to raise_error NotImplementedError
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'with implementation' do
71
+ before do
72
+ class ActiveExample
73
+ attr_accessor :id
74
+ configure base_uri: 'http://example.org/ns/'
75
+
76
+ def self.from_uri(uri, *args)
77
+ item = self.new
78
+ item.parent = args.first unless args.empty? or args.first.is_a?(Hash)
79
+ item
80
+ end
81
+
82
+ def self.property(*args)
83
+ prop = args.first
84
+
85
+ define_method prop.to_s do
86
+ resource.get_values(prop)
87
+ end
88
+
89
+ define_method "#{prop.to_s}=" do |*args|
90
+ resource.set_value(prop, *args)
91
+ end
92
+
93
+ resource_class.property(*args)
94
+ end
95
+
96
+ end
97
+
98
+ subject.id = '123'
99
+ end
100
+
101
+ describe '::properties' do
102
+ before do
103
+ klass.property :title, :predicate => RDF::DC.title
104
+ end
105
+ it 'can be set' do
106
+ expect(klass.properties).to include 'title'
107
+ end
108
+
109
+ it 'sets property values' do
110
+ subject.title = 'Finn Family Moomintroll'
111
+ expect(subject.resource.title).to eq ['Finn Family Moomintroll']
112
+ end
113
+
114
+ it 'appends property values' do
115
+ subject.title << 'Finn Family Moomintroll'
116
+ expect(subject.resource.title).to eq ['Finn Family Moomintroll']
117
+ end
118
+
119
+ it 'returns correct values in property getters' do
120
+ subject.resource.title = 'Finn Family Moomintroll'
121
+ expect(subject.title).to eq subject.resource.title
122
+ end
123
+
124
+ context 'with other identifiable classes' do
125
+ before do
126
+ class ActiveExampleTwo
127
+ include ActiveTriples::Identifiable
128
+ end
129
+ end
130
+ after do
131
+ Object.send(:remove_const, 'ActiveExampleTwo')
132
+ end
133
+
134
+ it 'does not effect other classes' do
135
+ klass.property :identifier, :predicate => RDF::DC.identifier
136
+ expect(ActiveExampleTwo.properties).to be_empty
137
+ end
138
+ end
139
+ end
140
+
141
+ describe '::configure' do
142
+ it 'allows configuration' do
143
+ klass.configure type: RDF::OWL.Thing
144
+ expect(subject.resource.type).to eq [RDF::OWL.Thing]
145
+ end
146
+ end
147
+
148
+ describe '#parent' do
149
+ it 'is nil' do
150
+ expect(subject.parent).to be_nil
151
+ end
152
+
153
+ context 'with relationships' do
154
+ include_context 'with data'
155
+
156
+ it 'has a parent' do
157
+ expect(parent.relation.first.parent).to eq parent
158
+ end
159
+
160
+ it 'has a parent after reload' do
161
+ parent.relation.node_cache = {}
162
+ expect(parent.relation.first.parent).to eq parent
163
+ end
164
+ end
165
+ end
166
+
167
+ describe '#rdf_subject' do
168
+ it 'has a subject' do
169
+ expect(subject.rdf_subject).to eq 'http://example.org/ns/123'
170
+ end
171
+ end
172
+
173
+ describe '#to_uri' do
174
+ it 'has a subject' do
175
+ expect(subject.rdf_subject).to eq 'http://example.org/ns/123'
176
+ end
177
+ end
178
+ end
179
+ end
@@ -40,6 +40,35 @@ describe ActiveTriples::Properties do
40
40
  end
41
41
  end
42
42
 
43
+ describe '#config_for_term_or_uri' do
44
+ before do
45
+ DummyProperties.property :title, :predicate => RDF::DC.title
46
+ end
47
+
48
+ it 'finds property configuration by term symbol' do
49
+ expect(DummyProperties.config_for_term_or_uri(:title)).to eq DummyProperties.properties['title']
50
+ end
51
+
52
+ it 'finds property configuration by term string' do
53
+ expect(DummyProperties.config_for_term_or_uri('title')).to eq DummyProperties.properties['title']
54
+ end
55
+
56
+ it 'finds property configuration by term URI' do
57
+ expect(DummyProperties.config_for_term_or_uri(RDF::DC.title)).to eq DummyProperties.properties['title']
58
+ end
59
+ end
60
+
61
+ describe '#fields' do
62
+ before do
63
+ DummyProperties.property :title, :predicate => RDF::DC.title
64
+ DummyProperties.property :name, :predicate => RDF::FOAF.name
65
+ end
66
+
67
+ it 'lists its terms' do
68
+ expect(DummyProperties.fields).to eq [:title, :name]
69
+ end
70
+ end
71
+
43
72
  context "when using a subclass" do
44
73
  before do
45
74
  DummyProperties.property :title, :predicate => RDF::DC.title
@@ -300,6 +300,61 @@ describe ActiveTriples::Resource do
300
300
  end
301
301
  end
302
302
 
303
+ describe 'array setters' do
304
+ before do
305
+ DummyResource.property :aggregates, :predicate => RDF::DC.relation
306
+ end
307
+
308
+ it "should be empty array if we haven't set it" do
309
+ expect(subject.aggregates).to match_array([])
310
+ end
311
+
312
+ it "should be set to a URI producing an ActiveTriple::Resource" do
313
+ subject.aggregates = RDF::URI("http://example.org/b1")
314
+ expect(subject.aggregates.first).to be_a ActiveTriples::Resource
315
+ end
316
+
317
+ it "should be settable" do
318
+ subject.aggregates = RDF::URI("http://example.org/b1")
319
+ expect(subject.aggregates.first.rdf_subject).to eq RDF::URI("http://example.org/b1")
320
+ ['id']
321
+ end
322
+
323
+ context 'with values' do
324
+ let(:bib1) { RDF::URI("http://example.org/b1") }
325
+ let(:bib2) { RDF::URI("http://example.org/b2") }
326
+ let(:bib3) { RDF::URI("http://example.org/b3") }
327
+
328
+ before do
329
+ subject.aggregates = bib1
330
+ subject.aggregates << bib2
331
+ subject.aggregates << bib3
332
+ end
333
+
334
+ it 'raises error when trying to set nil value' do
335
+ expect { subject.aggregates[1] = nil }.to raise_error /value must be an RDF URI, Node, Literal, or a valid datatype/
336
+ end
337
+
338
+ it "should be changeable for multiple values" do
339
+ new_bib1 = RDF::URI("http://example.org/b1_NEW")
340
+ new_bib3 = RDF::URI("http://example.org/b3_NEW")
341
+
342
+ aggregates = subject.aggregates.dup
343
+ aggregates[0] = new_bib1
344
+ aggregates[2] = new_bib3
345
+ subject.aggregates = aggregates
346
+
347
+ expect(subject.aggregates[0].rdf_subject).to eq new_bib1
348
+ expect(subject.aggregates[1].rdf_subject).to eq bib2
349
+ expect(subject.aggregates[2].rdf_subject).to eq new_bib3
350
+ end
351
+
352
+ it "raises an error for out of bounds index" do
353
+ expect { subject.aggregates[4] = 'blah' }.to raise_error IndexError
354
+ end
355
+ end
356
+ end
357
+
303
358
  describe 'child nodes' do
304
359
  it 'should return an object of the correct class when the value is a URI' do
305
360
  subject.license = DummyLicense.new('http://example.org/license')
@@ -325,6 +380,38 @@ describe ActiveTriples::Resource do
325
380
  end
326
381
  end
327
382
 
383
+ context "when given a URI" do
384
+ before do
385
+ subject.set_value(RDF::DC.title, RDF::URI("http://opaquenamespace.org/jokes/1"))
386
+ end
387
+ it "should return a resource" do
388
+ expect(subject.title.first).to be_kind_of(ActiveTriples::Resource)
389
+ end
390
+ context "and it's configured to not cast" do
391
+ before do
392
+ subject.class.property :title, predicate: RDF::DC.title, cast: false
393
+ end
394
+ it "should return a URI" do
395
+ expect(subject.title.first).to be_kind_of(RDF::URI)
396
+ end
397
+ end
398
+ end
399
+
400
+ it "safely handles terms passed in" do
401
+ vals = subject.get_values('license')
402
+ vals << "foo"
403
+ subject.set_value('license',vals)
404
+ expect(subject.get_values('license')).to eq ["foo"]
405
+ end
406
+
407
+ it "safely handles terms passed in with pre-existing values" do
408
+ subject.license = "foo"
409
+ vals = subject.get_values('license')
410
+ vals << "bar"
411
+ subject.set_value('license',vals)
412
+ expect(subject.get_values('license')).to eq ["foo","bar"]
413
+ end
414
+
328
415
  it 'should set a value in the when given a registered property symbol' do
329
416
  subject.set_value(:title, 'Comet in Moominland')
330
417
  expect(subject.title).to eq ['Comet in Moominland']
@@ -339,6 +426,7 @@ describe ActiveTriples::Resource do
339
426
  expect(subject.query(:subject => RDF::URI("http://opaquenamespace.org/jokes"), :predicate => RDF::DC.title).statements.to_a.length).to eq 1
340
427
  end
341
428
  end
429
+
342
430
  describe '#[]=' do
343
431
  it 'should set a value in the graph' do
344
432
  subject[RDF::DC.title] = 'Comet in Moominland'
@@ -11,7 +11,7 @@ describe ActiveTriples::Term do
11
11
  context "when term has 0 value arguments" do
12
12
  before { subject.value_arguments = double(length: 0) }
13
13
  it "should raise an error" do
14
- expect { subject.rdf_subject }.to raise_error
14
+ expect { subject.send(:rdf_subject) }.to raise_error
15
15
  end
16
16
  end
17
17
  context "when term has 1 value argument" do
@@ -20,19 +20,22 @@ describe ActiveTriples::Term do
20
20
  subject.value_arguments = double(length: 1)
21
21
  end
22
22
  it "should call `rdf_subject' on the parent" do
23
- expect(subject.rdf_subject).to eq "parent subject"
23
+ expect(subject.send(:rdf_subject) ).to eq "parent subject"
24
+ end
25
+ it " is a private method" do
26
+ expect { subject.rdf_subject }.to raise_error NoMethodError
24
27
  end
25
28
  end
26
29
  context "when term has 2 value arguments" do
27
30
  before { subject.value_arguments = double(length: 2, first: "first") }
28
31
  it "should return the first value argument" do
29
- expect(subject.rdf_subject).to eq "first"
32
+ expect(subject.send(:rdf_subject) ).to eq "first"
30
33
  end
31
34
  end
32
35
  context "when term has 3 value arguments" do
33
36
  before { subject.value_arguments = double(length: 3) }
34
37
  it "should raise an error" do
35
- expect { subject.rdf_subject }.to raise_error
38
+ expect { subject.send(:rdf_subject) }.to raise_error
36
39
  end
37
40
  end
38
41
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active-triples
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Johnson
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-10-02 00:00:00.000000000 Z
12
+ date: 2014-10-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rdf
@@ -184,6 +184,7 @@ files:
184
184
  - lib/active/triples.rb
185
185
  - lib/active_triples.rb
186
186
  - lib/active_triples/configurable.rb
187
+ - lib/active_triples/identifiable.rb
187
188
  - lib/active_triples/indexing.rb
188
189
  - lib/active_triples/list.rb
189
190
  - lib/active_triples/nested_attributes.rb
@@ -196,6 +197,7 @@ files:
196
197
  - lib/active_triples/term.rb
197
198
  - lib/active_triples/version.rb
198
199
  - spec/active_triples/configurable_spec.rb
200
+ - spec/active_triples/identifiable_spec.rb
199
201
  - spec/active_triples/list_spec.rb
200
202
  - spec/active_triples/nested_attributes_spec.rb
201
203
  - spec/active_triples/properties_spec.rb