active-triples 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.travis.yml +12 -0
- data/AUTHORS +2 -0
- data/Gemfile +3 -0
- data/LICENSE +24 -0
- data/README.md +38 -0
- data/active-triples.gemspec +35 -0
- data/lib/active_triples.rb +29 -0
- data/lib/active_triples/configurable.rb +61 -0
- data/lib/active_triples/indexing.rb +76 -0
- data/lib/active_triples/list.rb +155 -0
- data/lib/active_triples/nested_attributes.rb +132 -0
- data/lib/active_triples/node_config.rb +56 -0
- data/lib/active_triples/properties.rb +96 -0
- data/lib/active_triples/repositories.rb +36 -0
- data/lib/active_triples/resource.rb +369 -0
- data/lib/active_triples/term.rb +188 -0
- data/lib/active_triples/version.rb +3 -0
- data/spec/rdf_list_spec.rb +183 -0
- data/spec/rdf_nested_attributes_spec.rb +183 -0
- data/spec/rdf_properties_spec.rb +81 -0
- data/spec/rdf_repositories_spec.rb +28 -0
- data/spec/rdf_resource_spec.rb +447 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/active_model_lint.rb +81 -0
- metadata +197 -0
@@ -0,0 +1,188 @@
|
|
1
|
+
module ActiveTriples
|
2
|
+
class Term
|
3
|
+
attr_accessor :parent, :value_arguments, :node_cache
|
4
|
+
delegate *(Array.public_instance_methods - [:__send__, :__id__, :class, :object_id] + [:as_json]), :to => :result
|
5
|
+
def initialize(parent, value_arguments)
|
6
|
+
self.parent = parent
|
7
|
+
self.value_arguments = value_arguments
|
8
|
+
end
|
9
|
+
|
10
|
+
def clear
|
11
|
+
set(nil)
|
12
|
+
end
|
13
|
+
|
14
|
+
def result
|
15
|
+
result = parent.query(:subject => rdf_subject, :predicate => predicate)
|
16
|
+
.map{|x| convert_object(x.object)}
|
17
|
+
.reject(&:nil?)
|
18
|
+
return result if !property_config || property_config[:multivalue]
|
19
|
+
result.first
|
20
|
+
end
|
21
|
+
|
22
|
+
def set(values)
|
23
|
+
values = [values].compact unless values.kind_of?(Array)
|
24
|
+
empty_property
|
25
|
+
values.each do |val|
|
26
|
+
set_value(val)
|
27
|
+
end
|
28
|
+
parent.persist! if parent.class.repository == :parent && parent.send(:repository)
|
29
|
+
end
|
30
|
+
|
31
|
+
def empty_property
|
32
|
+
parent.query([rdf_subject, predicate, nil]).each_statement do |statement|
|
33
|
+
if !uri_class(statement.object) || uri_class(statement.object) == class_for_property
|
34
|
+
parent.delete(statement)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def build(attributes={})
|
40
|
+
new_subject = attributes.key?('id') ? attributes.delete('id') : RDF::Node.new
|
41
|
+
node = make_node(new_subject)
|
42
|
+
node.attributes = attributes
|
43
|
+
if parent.kind_of? List::ListResource
|
44
|
+
parent.list << node
|
45
|
+
return node
|
46
|
+
elsif node.kind_of? RDF::List
|
47
|
+
self.push node.rdf_subject
|
48
|
+
return node
|
49
|
+
end
|
50
|
+
self.push node
|
51
|
+
node
|
52
|
+
end
|
53
|
+
|
54
|
+
def first_or_create(attributes={})
|
55
|
+
result.first || build(attributes)
|
56
|
+
end
|
57
|
+
|
58
|
+
def delete(*values)
|
59
|
+
values.each do |value|
|
60
|
+
parent.delete([rdf_subject, predicate, value])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def << (values)
|
65
|
+
values = Array.wrap(result) | Array.wrap(values)
|
66
|
+
self.set(values)
|
67
|
+
end
|
68
|
+
|
69
|
+
alias_method :push, :<<
|
70
|
+
|
71
|
+
def property_config
|
72
|
+
return type_property if (property == RDF.type || property.to_s == "type") && !parent.send(:properties)[property]
|
73
|
+
parent.send(:properties)[property]
|
74
|
+
end
|
75
|
+
|
76
|
+
def type_property
|
77
|
+
{:multivalue => true, :predicate => RDF.type}
|
78
|
+
end
|
79
|
+
|
80
|
+
def reset!
|
81
|
+
end
|
82
|
+
|
83
|
+
def property
|
84
|
+
value_arguments.last
|
85
|
+
end
|
86
|
+
|
87
|
+
def rdf_subject
|
88
|
+
raise ArgumentError("wrong number of arguments (#{value_arguments.length} for 1-2)") if value_arguments.length < 1 || value_arguments.length > 2
|
89
|
+
if value_arguments.length > 1
|
90
|
+
value_arguments.first
|
91
|
+
else
|
92
|
+
parent.rdf_subject
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
protected
|
97
|
+
|
98
|
+
def node_cache
|
99
|
+
@node_cache ||= {}
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_value(val)
|
103
|
+
object = val
|
104
|
+
val = val.resource if val.respond_to?(:resource)
|
105
|
+
val = value_to_node(val)
|
106
|
+
if val.kind_of? Resource
|
107
|
+
node_cache[val.rdf_subject] = nil
|
108
|
+
add_child_node(val, object)
|
109
|
+
return
|
110
|
+
end
|
111
|
+
val = val.to_uri if val.respond_to? :to_uri
|
112
|
+
raise 'value must be an RDF URI, Node, Literal, or a valid datatype. See RDF::Literal' unless
|
113
|
+
val.kind_of? RDF::Value or val.kind_of? RDF::Literal
|
114
|
+
parent.insert [rdf_subject, predicate, val]
|
115
|
+
end
|
116
|
+
|
117
|
+
def value_to_node(val)
|
118
|
+
valid_datatype?(val) ? RDF::Literal(val) : val
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_child_node(resource,object=nil)
|
122
|
+
parent.insert [rdf_subject, predicate, resource.rdf_subject]
|
123
|
+
resource.parent = parent unless resource.frozen?
|
124
|
+
self.node_cache[resource.rdf_subject] = (object ? object : resource)
|
125
|
+
resource.persist! if resource.class.repository == :parent
|
126
|
+
end
|
127
|
+
|
128
|
+
def predicate
|
129
|
+
return property_config[:predicate] unless property.kind_of? RDF::URI
|
130
|
+
property
|
131
|
+
end
|
132
|
+
|
133
|
+
def valid_datatype?(val)
|
134
|
+
val.is_a? String or val.is_a? Date or val.is_a? Time or val.is_a? Numeric or val.is_a? Symbol or val == !!val
|
135
|
+
end
|
136
|
+
|
137
|
+
# Converts an object to the appropriate class.
|
138
|
+
def convert_object(value)
|
139
|
+
value = value.object if value.kind_of? RDF::Literal
|
140
|
+
value = make_node(value) if value.kind_of? RDF::Resource
|
141
|
+
value
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
# Build a child resource or return it from this object's cache
|
146
|
+
#
|
147
|
+
# Builds the resource from the class_name specified for the
|
148
|
+
# property.
|
149
|
+
def make_node(value)
|
150
|
+
klass = class_for_value(value)
|
151
|
+
value = RDF::Node.new if value.nil?
|
152
|
+
node = node_cache[value] if node_cache[value]
|
153
|
+
node ||= klass.from_uri(value,parent)
|
154
|
+
return nil if (property_config && property_config[:class_name]) && (class_for_value(value) != class_for_property)
|
155
|
+
self.node_cache[value] ||= node
|
156
|
+
node
|
157
|
+
end
|
158
|
+
|
159
|
+
def final_parent
|
160
|
+
@final_parent ||= begin
|
161
|
+
parent = self.parent
|
162
|
+
while parent != parent.parent && parent.parent
|
163
|
+
parent = parent.parent
|
164
|
+
end
|
165
|
+
return parent.datastream if parent.respond_to?(:datastream) && parent.datastream
|
166
|
+
parent
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def class_for_value(v)
|
171
|
+
uri_class(v) || class_for_property
|
172
|
+
end
|
173
|
+
|
174
|
+
def uri_class(v)
|
175
|
+
v = RDF::URI.new(v) if v.kind_of? String
|
176
|
+
type_uri = parent.query([v, RDF.type, nil]).to_a.first.try(:object)
|
177
|
+
Resource.type_registry[type_uri]
|
178
|
+
end
|
179
|
+
|
180
|
+
def class_for_property
|
181
|
+
klass = property_config[:class_name] if property_config
|
182
|
+
klass ||= Resource
|
183
|
+
klass = ActiveTriples.class_from_string(klass, final_parent.class) if klass.kind_of? String
|
184
|
+
klass
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'linkeddata'
|
4
|
+
|
5
|
+
describe ActiveTriples::List do
|
6
|
+
before :each do
|
7
|
+
class MADS < RDF::Vocabulary("http://www.loc.gov/mads/rdf/v1#")
|
8
|
+
property :complexSubject
|
9
|
+
property :authoritativeLabel
|
10
|
+
property :elementList
|
11
|
+
property :elementValue
|
12
|
+
property :TopicElement
|
13
|
+
property :TemporalElement
|
14
|
+
end
|
15
|
+
class DemoList < ActiveTriples::Resource
|
16
|
+
property :elementList, :predicate => MADS.elementList, :class_name => 'DemoList::List'
|
17
|
+
class List < ActiveTriples::List
|
18
|
+
property :topicElement, :predicate => MADS.TopicElement, :class_name => 'DemoList::List::TopicElement'
|
19
|
+
property :temporalElement, :predicate => MADS.TemporalElement, :class_name => 'DemoList::List::TemporalElement'
|
20
|
+
|
21
|
+
class TopicElement < ActiveTriples::Resource
|
22
|
+
configure :type => MADS.TopicElement
|
23
|
+
property :elementValue, :predicate => MADS.elementValue
|
24
|
+
end
|
25
|
+
class TemporalElement < ActiveTriples::Resource
|
26
|
+
configure :type => MADS.TemporalElement
|
27
|
+
property :elementValue, :predicate => MADS.elementValue
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
after(:each) do
|
33
|
+
Object.send(:remove_const, :DemoList)
|
34
|
+
Object.send(:remove_const, :MADS)
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "a new list" do
|
38
|
+
let (:ds) { DemoList.new('http://example.org/foo') }
|
39
|
+
subject { ds.elementList.build}
|
40
|
+
|
41
|
+
it "should insert at the end" do
|
42
|
+
subject.should be_kind_of DemoList::List
|
43
|
+
subject.size.should == 0
|
44
|
+
subject[1] = DemoList::List::TopicElement.new
|
45
|
+
subject.size.should == 2
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should insert at the head" do
|
49
|
+
subject.should be_kind_of DemoList::List
|
50
|
+
subject.size.should == 0
|
51
|
+
subject[0] = DemoList::List::TopicElement.new
|
52
|
+
subject.size.should == 1
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "that has 4 elements" do
|
56
|
+
before do
|
57
|
+
subject[3] = DemoList::List::TopicElement.new
|
58
|
+
subject.size.should == 4
|
59
|
+
end
|
60
|
+
it "should insert in the middle" do
|
61
|
+
subject[1] = DemoList::List::TopicElement.new
|
62
|
+
subject.size.should == 4
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "return updated xml" do
|
67
|
+
it "should be built" do
|
68
|
+
subject[0] = RDF::URI.new "http://library.ucsd.edu/ark:/20775/bbXXXXXXX6"
|
69
|
+
subject[1] = DemoList::List::TopicElement.new
|
70
|
+
subject[1].elementValue = "Relations with Mexican Americans"
|
71
|
+
subject[2] = RDF::URI.new "http://library.ucsd.edu/ark:/20775/bbXXXXXXX4"
|
72
|
+
subject[3] = DemoList::List::TemporalElement.new
|
73
|
+
subject[3].elementValue = "20th century"
|
74
|
+
ds.elementList = subject
|
75
|
+
doc = Nokogiri::XML(ds.dump(:rdfxml))
|
76
|
+
ns = {rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", mads: "http://www.loc.gov/mads/rdf/v1#"}
|
77
|
+
expect(doc.xpath('/rdf:RDF/rdf:Description/@rdf:about', ns).map(&:value)).to eq ["http://example.org/foo"]
|
78
|
+
expect(doc.xpath('//rdf:Description/mads:elementList/@rdf:parseType', ns).map(&:value)).to eq ["Collection"]
|
79
|
+
expect(doc.xpath('//rdf:Description/mads:elementList/*[position() = 1]/@rdf:about', ns).map(&:value)).to eq ["http://library.ucsd.edu/ark:/20775/bbXXXXXXX6"]
|
80
|
+
expect(doc.xpath('//rdf:Description/mads:elementList/*[position() = 2]/mads:elementValue', ns).map(&:text)).to eq ["Relations with Mexican Americans"]
|
81
|
+
expect(doc.xpath('//rdf:Description/mads:elementList/*[position() = 3]/@rdf:about', ns).map(&:value)).to eq ["http://library.ucsd.edu/ark:/20775/bbXXXXXXX4"]
|
82
|
+
expect(doc.xpath('//rdf:Description/mads:elementList/*[position() = 4]/mads:elementValue', ns).map(&:text)).to eq ["20th century"]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "an empty list" do
|
88
|
+
subject { DemoList.new.elementList.build }
|
89
|
+
it "should have to_ary" do
|
90
|
+
subject.to_ary.should == []
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "a list that has a constructed element" do
|
95
|
+
let(:ds) { DemoList.new('http://example.org/foo') }
|
96
|
+
let(:list) { ds.elementList.build }
|
97
|
+
let!(:topic) { list.topicElement.build }
|
98
|
+
|
99
|
+
it "should have to_ary" do
|
100
|
+
list.to_ary.size.should == 1
|
101
|
+
list.to_ary.first.class.should == DemoList::List::TopicElement
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should be able to be cleared" do
|
105
|
+
list.topicElement.build
|
106
|
+
list.topicElement.build
|
107
|
+
list.topicElement.build
|
108
|
+
list.size.should == 4
|
109
|
+
list.clear
|
110
|
+
list.size.should == 0
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "a list with content" do
|
115
|
+
subject do
|
116
|
+
subject = DemoList.new('http://example.org/foo')
|
117
|
+
subject << RDF::RDFXML::Reader.for(:rdfxml).new(<<END
|
118
|
+
<rdf:RDF
|
119
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:mads="http://www.loc.gov/mads/rdf/v1#">
|
120
|
+
|
121
|
+
<mads:ComplexSubject rdf:about="http://example.org/foo">
|
122
|
+
<mads:elementList rdf:parseType="Collection">
|
123
|
+
<rdf:Description rdf:about="http://library.ucsd.edu/ark:/20775/bbXXXXXXX6"/>
|
124
|
+
<mads:TopicElement>
|
125
|
+
<mads:elementValue>Relations with Mexican Americans</mads:elementValue>
|
126
|
+
</mads:TopicElement>
|
127
|
+
<rdf:Description rdf:about="http://library.ucsd.edu/ark:/20775/bbXXXXXXX4"/>
|
128
|
+
<mads:TemporalElement>
|
129
|
+
<mads:elementValue>20th century</mads:elementValue>
|
130
|
+
</mads:TemporalElement>
|
131
|
+
</mads:elementList>
|
132
|
+
</mads:ComplexSubject>
|
133
|
+
</rdf:RDF>
|
134
|
+
END
|
135
|
+
)
|
136
|
+
|
137
|
+
subject
|
138
|
+
end
|
139
|
+
it "should have a subject" do
|
140
|
+
subject.rdf_subject.to_s.should == "http://example.org/foo"
|
141
|
+
end
|
142
|
+
|
143
|
+
let (:list) { subject.elementList.first }
|
144
|
+
|
145
|
+
it "should have fields" do
|
146
|
+
list.first.rdf_subject.should == "http://library.ucsd.edu/ark:/20775/bbXXXXXXX6"
|
147
|
+
list[1].should be_kind_of DemoList::List::TopicElement
|
148
|
+
list[1].elementValue.should == ["Relations with Mexican Americans"]
|
149
|
+
list[2].rdf_subject.should == "http://library.ucsd.edu/ark:/20775/bbXXXXXXX4"
|
150
|
+
list[3].should be_kind_of DemoList::List::TemporalElement
|
151
|
+
list[3].elementValue.should == ["20th century"]
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should have each" do
|
155
|
+
foo = []
|
156
|
+
list.each { |n| foo << n.class }
|
157
|
+
foo.should == [ActiveTriples::Resource, DemoList::List::TopicElement, ActiveTriples::Resource, DemoList::List::TemporalElement]
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should have to_ary" do
|
161
|
+
ary = list.to_ary
|
162
|
+
ary.size.should == 4
|
163
|
+
ary[1].elementValue.should == ['Relations with Mexican Americans']
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should have size" do
|
167
|
+
list.size.should == 4
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should update fields" do
|
171
|
+
list[3].elementValue = ["1900s"]
|
172
|
+
doc = Nokogiri::XML(subject.dump :rdfxml)
|
173
|
+
ns = {rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", mads: "http://www.loc.gov/mads/rdf/v1#"}
|
174
|
+
expect(doc.xpath('/rdf:RDF/mads:ComplexSubject/@rdf:about', ns).map(&:value)).to eq ["http://example.org/foo"]
|
175
|
+
expect(doc.xpath('//mads:ComplexSubject/mads:elementList/@rdf:parseType', ns).map(&:value)).to eq ["Collection"]
|
176
|
+
expect(doc.xpath('//mads:ComplexSubject/mads:elementList/*[position() = 1]/@rdf:about', ns).map(&:value)).to eq ["http://library.ucsd.edu/ark:/20775/bbXXXXXXX6"]
|
177
|
+
expect(doc.xpath('//mads:ComplexSubject/mads:elementList/*[position() = 2]/mads:elementValue', ns).map(&:text)).to eq ["Relations with Mexican Americans"]
|
178
|
+
expect(doc.xpath('//mads:ComplexSubject/mads:elementList/*[position() = 3]/@rdf:about', ns).map(&:value)).to eq ["http://library.ucsd.edu/ark:/20775/bbXXXXXXX4"]
|
179
|
+
expect(doc.xpath('//mads:ComplexSubject/mads:elementList/*[position() = 4]/mads:elementValue', ns).map(&:text)).to eq ["1900s"]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "nesting attribute behavior" do
|
4
|
+
describe ".attributes=" do
|
5
|
+
describe "complex properties" do
|
6
|
+
before do
|
7
|
+
class DummyMADS < RDF::Vocabulary("http://www.loc.gov/mads/rdf/v1#")
|
8
|
+
# componentList and Types of components
|
9
|
+
property :componentList
|
10
|
+
property :Topic
|
11
|
+
property :Temporal
|
12
|
+
property :PersonalName
|
13
|
+
property :CorporateName
|
14
|
+
property :ComplexSubject
|
15
|
+
|
16
|
+
|
17
|
+
# elementList and elementList values
|
18
|
+
property :elementList
|
19
|
+
property :elementValue
|
20
|
+
property :TopicElement
|
21
|
+
property :TemporalElement
|
22
|
+
property :NameElement
|
23
|
+
property :FullNameElement
|
24
|
+
property :DateNameElement
|
25
|
+
end
|
26
|
+
|
27
|
+
class ComplexResource < ActiveTriples::Resource
|
28
|
+
property :topic, predicate: DummyMADS.Topic, class_name: "Topic"
|
29
|
+
property :personalName, predicate: DummyMADS.PersonalName, class_name: "PersonalName"
|
30
|
+
property :title, predicate: RDF::DC.title
|
31
|
+
|
32
|
+
|
33
|
+
accepts_nested_attributes_for :topic, :personalName
|
34
|
+
|
35
|
+
class Topic < ActiveTriples::Resource
|
36
|
+
property :elementList, predicate: DummyMADS.elementList, class_name: "ComplexResource::ElementList"
|
37
|
+
accepts_nested_attributes_for :elementList
|
38
|
+
end
|
39
|
+
class PersonalName < ActiveTriples::Resource
|
40
|
+
property :elementList, predicate: DummyMADS.elementList, class_name: "ComplexResource::ElementList"
|
41
|
+
property :extraProperty, predicate: DummyMADS.elementValue, class_name: "ComplexResource::Topic"
|
42
|
+
accepts_nested_attributes_for :elementList, :extraProperty
|
43
|
+
end
|
44
|
+
class ElementList < ActiveTriples::List
|
45
|
+
configure type: DummyMADS.elementList
|
46
|
+
property :topicElement, predicate: DummyMADS.TopicElement, class_name: "ComplexResource::MadsTopicElement"
|
47
|
+
property :temporalElement, predicate: DummyMADS.TemporalElement
|
48
|
+
property :fullNameElement, predicate: DummyMADS.FullNameElement
|
49
|
+
property :dateNameElement, predicate: DummyMADS.DateNameElement
|
50
|
+
property :nameElement, predicate: DummyMADS.NameElement
|
51
|
+
property :elementValue, predicate: DummyMADS.elementValue
|
52
|
+
accepts_nested_attributes_for :topicElement
|
53
|
+
end
|
54
|
+
class MadsTopicElement < ActiveTriples::Resource
|
55
|
+
configure :type => DummyMADS.TopicElement
|
56
|
+
property :elementValue, predicate: DummyMADS.elementValue
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
after do
|
61
|
+
Object.send(:remove_const, :ComplexResource)
|
62
|
+
Object.send(:remove_const, :DummyMADS)
|
63
|
+
end
|
64
|
+
subject { ComplexResource.new }
|
65
|
+
let(:params) do
|
66
|
+
{ myResource:
|
67
|
+
{
|
68
|
+
topic_attributes: {
|
69
|
+
'0' =>
|
70
|
+
{
|
71
|
+
elementList_attributes: [{
|
72
|
+
topicElement_attributes: [{
|
73
|
+
id: 'http://library.ucsd.edu/ark:/20775/bb3333333x',
|
74
|
+
elementValue:"Cosmology"
|
75
|
+
}]
|
76
|
+
}]
|
77
|
+
},
|
78
|
+
'1' =>
|
79
|
+
{
|
80
|
+
elementList_attributes: [{
|
81
|
+
topicElement_attributes: {'0' => {elementValue:"Quantum Behavior"}}
|
82
|
+
}]
|
83
|
+
}
|
84
|
+
},
|
85
|
+
personalName_attributes: [
|
86
|
+
{
|
87
|
+
id: 'http://library.ucsd.edu/ark:20775/jefferson',
|
88
|
+
elementList_attributes: [{
|
89
|
+
fullNameElement: "Jefferson, Thomas",
|
90
|
+
dateNameElement: "1743-1826"
|
91
|
+
}]
|
92
|
+
}
|
93
|
+
#, "Hemings, Sally"
|
94
|
+
],
|
95
|
+
}
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "on lists" do
|
100
|
+
subject { ComplexResource::PersonalName.new }
|
101
|
+
it "should accept a hash" do
|
102
|
+
subject.elementList_attributes = [{ topicElement_attributes: {'0' => { elementValue:"Quantum Behavior" }, '1' => { elementValue:"Wave Function" }}}]
|
103
|
+
subject.elementList.first[0].elementValue.should == ["Quantum Behavior"]
|
104
|
+
subject.elementList.first[1].elementValue.should == ["Wave Function"]
|
105
|
+
|
106
|
+
end
|
107
|
+
it "should accept an array" do
|
108
|
+
subject.elementList_attributes = [{ topicElement_attributes: [{ elementValue:"Quantum Behavior" }, { elementValue:"Wave Function" }]}]
|
109
|
+
subject.elementList.first[0].elementValue.should == ["Quantum Behavior"]
|
110
|
+
subject.elementList.first[1].elementValue.should == ["Wave Function"]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "from nested objects" do
|
115
|
+
before do
|
116
|
+
# Replace the graph's contents with the Hash
|
117
|
+
subject.attributes = params[:myResource]
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should have attributes' do
|
121
|
+
subject.topic[0].elementList.first[0].elementValue.should == ["Cosmology"]
|
122
|
+
subject.topic[1].elementList.first[0].elementValue.should == ["Quantum Behavior"]
|
123
|
+
subject.personalName.first.elementList.first.fullNameElement.should == ["Jefferson, Thomas"]
|
124
|
+
subject.personalName.first.elementList.first.dateNameElement.should == ["1743-1826"]
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should build nodes with ids' do
|
128
|
+
expect(subject.topic[0].elementList.first[0].rdf_subject).to eq 'http://library.ucsd.edu/ark:/20775/bb3333333x'
|
129
|
+
expect(subject.personalName.first.rdf_subject).to eq 'http://library.ucsd.edu/ark:20775/jefferson'
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should fail when writing to a non-predicate' do
|
133
|
+
attributes = { topic_attributes: { '0' => { elementList_attributes: [{ topicElement_attributes: [{ fake_predicate:"Cosmology" }] }]}}}
|
134
|
+
expect{ subject.attributes = attributes }.to raise_error ArgumentError
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should fail when writing to a non-predicate with a setter method' do
|
138
|
+
attributes = { topic_attributes: { '0' => { elementList_attributes: [{ topicElement_attributes: [{ name:"Cosmology" }] }]}}}
|
139
|
+
expect{ subject.attributes = attributes }.to raise_error ArgumentError
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "with an existing object" do
|
145
|
+
before(:each) do
|
146
|
+
class SpecResource < ActiveTriples::Resource
|
147
|
+
property :parts, predicate: RDF::DC.hasPart, :class_name=>'Component'
|
148
|
+
accepts_nested_attributes_for :parts, allow_destroy: true
|
149
|
+
|
150
|
+
class Component < ActiveTriples::Resource
|
151
|
+
property :label, predicate: RDF::DC.title
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
after(:each) do
|
158
|
+
Object.send(:remove_const, :SpecResource)
|
159
|
+
end
|
160
|
+
subject { SpecResource.new }
|
161
|
+
before do
|
162
|
+
subject.attributes = { parts_attributes: [
|
163
|
+
{label: 'Alternator'},
|
164
|
+
{label: 'Distributor'},
|
165
|
+
{label: 'Transmission'},
|
166
|
+
{label: 'Fuel Filter'}]}
|
167
|
+
end
|
168
|
+
let (:replace_object_id) { subject.parts[1].rdf_subject.to_s }
|
169
|
+
let (:remove_object_id) { subject.parts[3].rdf_subject.to_s }
|
170
|
+
|
171
|
+
it "should update nested objects" do
|
172
|
+
subject.parts_attributes= [{id: replace_object_id, label: "Universal Joint"}, {label:"Oil Pump"}, {id: remove_object_id, _destroy: '1', label: "bar1 uno"}]
|
173
|
+
|
174
|
+
subject.parts.map{|p| p.label.first}.should == ['Alternator', 'Universal Joint', 'Transmission', 'Oil Pump']
|
175
|
+
|
176
|
+
end
|
177
|
+
it "create a new object when the id is provided" do
|
178
|
+
subject.parts_attributes= [{id: 'http://example.com/part#1', label: "Universal Joint"}]
|
179
|
+
expect(subject.parts.last.rdf_subject).to eq RDF::URI('http://example.com/part#1')
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|