active-triples 0.5.0 → 0.6.0

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: 42a735382f3d5ad34bb75398f7dbe41bc4092ed0
4
- data.tar.gz: 64e572f52e992165f32a84202a73b6266032ce81
3
+ metadata.gz: 18f7fdc54422676d984f7e33ae9867636610e4ce
4
+ data.tar.gz: 6961ae10dc7196fd88d6cff90d353f1605bafd04
5
5
  SHA512:
6
- metadata.gz: 8e1c1c02dc97fddfb50dd818fd97087a9d8032d8e0552dc5d712da15c5f7363ed0a90eada66d2dfa28692049a6fb1ab6427ffe6167115565ca2cb66537a41d7f
7
- data.tar.gz: 4fc879ad051dd585c8eb4b1570f4b1530002e7e2b91a15ee2511b1771d38c3edf4074ee550123598e74d591173483c79141b8d174cb255ae6a385c682fcb67cd
6
+ metadata.gz: d64fbb4b48e730f62905b1dda9091ea2f43135e7c3aff5b8a2397323d2c521f79b1bb54abb52cbac4a1d786b879e1860491537b65d424dac2b001de64b79047c
7
+ data.tar.gz: 58393069ffdabc2010f82eced08a5500fe1cac7b34f73a50448065a580b241250cac5ee3acf8f0f3dd3c798b19640ac9de35b914cdd65339d4f424188e5f6712
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore CHANGED
@@ -4,3 +4,7 @@ pkg/
4
4
  Gemfile.lock
5
5
  tmp/*
6
6
  .sass-cache
7
+ .idea
8
+ coverage
9
+ .ruby-version
10
+ .ruby-gemset
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  Description
2
2
  -----------
3
3
 
4
- [![Build Status](https://travis-ci.org/no-reply/ActiveTriples.png?branch=master)](https://travis-ci.org/no-reply/ActiveTriples)
4
+ [![Build Status](https://travis-ci.org/ActiveTriples/ActiveTriples.png?branch=master)](https://travis-ci.org/ActiveTriples/ActiveTriples)
5
+ [![Coverage Status](https://coveralls.io/repos/ActiveTriples/ActiveTriples/badge.png?branch=master)](https://coveralls.io/r/ActiveTriples/ActiveTriples?branch=master)
6
+ [![Gem Version](https://badge.fury.io/rb/active-triples.svg)](http://badge.fury.io/rb/active-triples)
5
7
 
6
8
  An ActiveModel-like interface for RDF data. Models graphs as Resources with property/attribute configuration, accessors, and other methods to support Linked Data in a Ruby/Rails enviornment.
7
9
 
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rake/clean'
2
+ require 'bundler'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.add_development_dependency('rdoc')
24
24
  s.add_development_dependency('rspec')
25
+ s.add_development_dependency('coveralls')
25
26
  s.add_development_dependency('guard-rspec')
26
27
  s.add_development_dependency('webmock')
27
28
  s.add_development_dependency('nokogiri')
@@ -8,7 +8,6 @@ module ActiveTriples
8
8
  autoload :Resource
9
9
  autoload :List
10
10
  autoload :Term
11
- autoload :Indexing
12
11
  autoload :Configurable
13
12
  autoload :Properties
14
13
  autoload :PropertyBuilder
@@ -19,6 +18,10 @@ module ActiveTriples
19
18
  autoload :Identifiable
20
19
  end
21
20
 
21
+ # Raised when a declared repository doesn't have a definition
22
+ class RepositoryNotFoundError < StandardError
23
+ end
24
+
22
25
  def self.class_from_string(class_name, container_class=Kernel)
23
26
  container_class = container_class.name if container_class.is_a? Module
24
27
  container_parts = container_class.split('::')
@@ -33,4 +36,22 @@ module ActiveTriples
33
36
  end
34
37
  end
35
38
  end
39
+
40
+ def self.ActiveTripels
41
+ puts <<-eos
42
+
43
+ ###########
44
+ ******o****
45
+ **o******
46
+ *******
47
+ \\***/
48
+ | |
49
+ ( )
50
+ / \\
51
+ ,---------.
52
+
53
+ eos
54
+ "Yum"
55
+ end
56
+
36
57
  end
@@ -51,13 +51,12 @@ module ActiveTriples
51
51
  attributes_collection.each do |attributes|
52
52
  attributes = attributes.with_indifferent_access
53
53
 
54
- if attributes['id'] && existing_record = association.detect { |record| record.rdf_subject.to_s == attributes['id'].to_s }
55
- if !call_reject_if(association_name, attributes)
56
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
54
+ if !call_reject_if(association_name, attributes)
55
+ if attributes['id'] && existing_record = association.detect { |record| record.rdf_subject.to_s == attributes['id'].to_s }
56
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
57
+ else
58
+ association.build(attributes.except(*UNASSIGNABLE_KEYS))
57
59
  end
58
- else
59
- attributes = attributes.with_indifferent_access
60
- association.build(attributes.except(*UNASSIGNABLE_KEYS))
61
60
  end
62
61
  end
63
62
  end
@@ -17,14 +17,15 @@ module ActiveTriples
17
17
  def self.build(model, name, options, &block)
18
18
  builder = create_builder name, options, &block
19
19
  reflection = builder.build(&block)
20
- define_accessors model, reflection
20
+ define_accessors model, reflection, options
21
21
  reflection
22
22
  end
23
23
 
24
- def self.define_accessors(model, reflection)
24
+ def self.define_accessors(model, reflection, options={})
25
25
  mixin = model.generated_property_methods
26
26
  name = reflection.term
27
27
  define_readers(mixin, name)
28
+ define_id_reader(model, name) unless options[:cast] == false
28
29
  define_writers(mixin, name)
29
30
  end
30
31
 
@@ -36,6 +37,14 @@ module ActiveTriples
36
37
  CODE
37
38
  end
38
39
 
40
+ def self.define_id_reader(mixin, name)
41
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
42
+ def #{name}_ids(*args)
43
+ get_values(:#{name}, :cast => false)
44
+ end
45
+ CODE
46
+ end
47
+
39
48
  def self.define_writers(mixin, name)
40
49
  mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
41
50
  def #{name}=(value)
@@ -7,7 +7,7 @@ module ActiveTriples
7
7
  # RDF::Repository implementation to be used for persistence of
8
8
  # resources that will be shared between ActiveFedora::Base objects.
9
9
  #
10
- # ActiveFedora::Rdf::Repositories.add_repository :blah, RDF::Repository.new
10
+ # ActiveTriples::Repositories.add_repository :blah, RDF::Repository.new
11
11
  #
12
12
  # Multiple repositories can be registered to keep different kinds of
13
13
  # resources seperate. This is configurable on subclasses of Resource
@@ -20,12 +20,12 @@ module ActiveTriples
20
20
  # end
21
21
  # end
22
22
  class Resource < RDF::Graph
23
- @@type_registry
24
23
  extend Configurable
25
24
  include Properties
26
25
  extend Deprecation
27
26
  extend ActiveModel::Naming
28
27
  extend ActiveModel::Translation
28
+ extend ActiveModel::Callbacks
29
29
  include ActiveModel::Validations
30
30
  include ActiveModel::Conversion
31
31
  include ActiveModel::Serialization
@@ -33,6 +33,7 @@ module ActiveTriples
33
33
  include NestedAttributes
34
34
  include Reflection
35
35
  attr_accessor :parent
36
+ define_model_callbacks :persist
36
37
 
37
38
  class << self
38
39
  def type_registry
@@ -227,11 +228,18 @@ module ActiveTriples
227
228
  self
228
229
  end
229
230
 
230
- def persist!
231
- raise "failed when trying to persist to non-existant repository or parent resource" unless repository
232
- erase_old_resource
233
- repository << self
234
- @persisted = true
231
+ def persist!(opts={})
232
+ return if @persisting
233
+ return false if opts[:validate] && !valid?
234
+ @persisting = true
235
+ run_callbacks :persist do
236
+ raise "failed when trying to persist to non-existant repository or parent resource" unless repository
237
+ erase_old_resource
238
+ repository << self
239
+ @persisted = true
240
+ end
241
+ @persisting = false
242
+ true
235
243
  end
236
244
 
237
245
  ##
@@ -251,9 +259,7 @@ module ActiveTriples
251
259
  # @return [true, false]
252
260
  def reload
253
261
  @term_cache ||= {}
254
- if self.class.repository == :parent
255
- return false if final_parent.nil?
256
- end
262
+ return false unless repository
257
263
  self << repository.query(subject: rdf_subject)
258
264
  unless empty?
259
265
  @persisted = true
@@ -504,7 +510,9 @@ module ActiveTriples
504
510
  if self.class.repository == :parent
505
511
  final_parent
506
512
  else
507
- Repositories.repositories[self.class.repository]
513
+ repo = Repositories.repositories[self.class.repository]
514
+ raise RepositoryNotFoundError, "The class #{self.class} expects a repository called #{self.class.repository}, but none was declared" unless repo
515
+ repo
508
516
  end
509
517
  end
510
518
 
@@ -11,6 +11,7 @@ module ActiveTriples
11
11
  def initialize(parent_resource, value_arguments)
12
12
  self.parent = parent_resource
13
13
  @reflections = parent_resource.reflections
14
+ self.term_args ||= {}
14
15
  self.value_arguments = value_arguments
15
16
  end
16
17
 
@@ -177,7 +178,8 @@ module ActiveTriples
177
178
  end
178
179
 
179
180
  def cast?
180
- return true unless property_config
181
+ return true unless property_config || (term_args && term_args[:cast])
182
+ return term_args[:cast] if term_args.has_key?(:cast)
181
183
  !!property_config[:cast]
182
184
  end
183
185
 
@@ -1,3 +1,3 @@
1
1
  module ActiveTriples
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -141,8 +141,8 @@ describe "nesting attribute behavior" do
141
141
  end
142
142
  end
143
143
 
144
- describe "with an existing object" do
145
- before(:each) do
144
+ context "a simple model" do
145
+ before do
146
146
  class SpecResource < ActiveTriples::Resource
147
147
  property :parts, predicate: RDF::DC.hasPart, :class_name=>'Component'
148
148
  accepts_nested_attributes_for :parts, allow_destroy: true
@@ -152,31 +152,57 @@ describe "nesting attribute behavior" do
152
152
  end
153
153
  end
154
154
 
155
+ SpecResource.accepts_nested_attributes_for *args
155
156
  end
157
+ after { Object.send(:remove_const, :SpecResource) }
156
158
 
157
- after(:each) do
158
- Object.send(:remove_const, :SpecResource)
159
- end
159
+ let(:args) { [:parts] }
160
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
161
 
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"}]
162
+ context "for an existing object" do
163
+ before do
164
+ subject.attributes = { parts_attributes: [
165
+ {label: 'Alternator'},
166
+ {label: 'Distributor'},
167
+ {label: 'Transmission'},
168
+ {label: 'Fuel Filter'}]}
169
+ subject.parts_attributes = new_attributes
170
+ end
171
+
172
+ context "that allows destroy" do
173
+ let(:args) { [:parts, allow_destroy: true] }
174
+ let (:replace_object_id) { subject.parts[1].rdf_subject.to_s }
175
+ let (:remove_object_id) { subject.parts[3].rdf_subject.to_s }
173
176
 
174
- expect(subject.parts.map{|p| p.label.first}).to eq ['Alternator', 'Universal Joint', 'Transmission', 'Oil Pump']
177
+ let(:new_attributes) { [{ id: replace_object_id, label: "Universal Joint" },
178
+ { label:"Oil Pump" },
179
+ { id: remove_object_id, _destroy: '1', label: "bar1 uno" }] }
175
180
 
181
+ it "should update nested objects" do
182
+ expect(subject.parts.map{|p| p.label.first}).to eq ['Alternator', 'Universal Joint', 'Transmission', 'Oil Pump']
183
+ end
184
+ end
185
+
186
+ context "when an id is provided" do
187
+ let(:new_attributes) { [{ id: 'http://example.com/part#1', label: "Universal Joint" }] }
188
+
189
+ it "creates a new statement" do
190
+ expect(subject.parts.last.rdf_subject).to eq RDF::URI('http://example.com/part#1')
191
+ end
192
+ end
176
193
  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')
194
+
195
+ context "for a new B-node" do
196
+ context "when called with reject_if" do
197
+ let(:args) { [:parts, reject_if: reject_proc] }
198
+ let(:reject_proc) { lambda { |attributes| attributes[:label] == 'Bar' } }
199
+ let(:new_attributes) { [{ label: "Universal Joint" }, { label: 'Bar'} ] }
200
+ before { subject.parts_attributes = new_attributes }
201
+
202
+ it "should call the reject if proc" do
203
+ expect(subject.parts.map(&:label)).to eq [['Universal Joint']]
204
+ end
205
+ end
180
206
  end
181
207
  end
182
208
  end
@@ -135,13 +135,18 @@ describe ActiveTriples::Resource do
135
135
  context "and the item is not a blank node" do
136
136
 
137
137
  subject {DummyResource.new("info:fedora/example:pid")}
138
+ let(:result) { subject.persist! }
138
139
 
139
140
  before do
140
141
  @repo = RDF::Repository.new
141
142
  allow(subject.class).to receive(:repository).and_return(nil)
142
143
  allow(subject).to receive(:repository).and_return(@repo)
143
144
  subject.title = "bla"
144
- subject.persist!
145
+ result
146
+ end
147
+
148
+ it "should return true" do
149
+ expect(result).to eq true
145
150
  end
146
151
 
147
152
  it "should persist to the repository" do
@@ -158,6 +163,28 @@ describe ActiveTriples::Resource do
158
163
  expect(subject.title).to eq []
159
164
  expect(@repo.statements.to_a.length).to eq 1 # Only the type statement
160
165
  end
166
+
167
+ context "and validations are checked" do
168
+ let(:result) { subject.persist!(:validate => true) }
169
+ context "and it's valid" do
170
+ it "should return true" do
171
+ expect(result).to eq true
172
+ end
173
+ end
174
+ context "and it's invalid" do
175
+ subject do
176
+ a = DummyResource.new("info:fedora/example:pid")
177
+ allow(a).to receive(:valid?).and_return(false)
178
+ a
179
+ end
180
+ it "should return false" do
181
+ expect(result).to eq false
182
+ end
183
+ it "should not be persisted" do
184
+ expect(subject).not_to be_persisted
185
+ end
186
+ end
187
+ end
161
188
  end
162
189
  end
163
190
  end
@@ -254,6 +281,15 @@ describe ActiveTriples::Resource do
254
281
  end
255
282
  end
256
283
 
284
+ describe '#repository' do
285
+ subject { DummyLicense.new('http://example.org/cc')}
286
+
287
+ it "should warn when the repo doesn't exist" do
288
+ allow(DummyLicense).to receive(:repository).and_return('repo2')
289
+ expect { subject }.to raise_error ActiveTriples::RepositoryNotFoundError, 'The class DummyLicense expects a repository called repo2, but none was declared'
290
+ end
291
+ end
292
+
257
293
  describe '#destroy!' do
258
294
  before do
259
295
  subject.title = 'Creative Commons'
@@ -266,7 +302,7 @@ describe ActiveTriples::Resource do
266
302
  expect(subject.destroy!).to be true
267
303
  expect(subject.destroy).to be true
268
304
  end
269
-
305
+
270
306
  it 'should delete the graph' do
271
307
  subject.destroy
272
308
  expect(subject).to be_empty
@@ -400,10 +436,18 @@ describe ActiveTriples::Resource do
400
436
  it "should be empty array if we haven't set it" do
401
437
  expect(subject.aggregates).to match_array([])
402
438
  end
403
-
404
- it "should be set to a URI producing an ActiveTriple::Resource" do
405
- subject.aggregates = RDF::URI("http://example.org/b1")
406
- expect(subject.aggregates.first).to be_a ActiveTriples::Resource
439
+
440
+ context "when set to a URI" do
441
+ let(:aggregates_uri) { RDF::URI("http://example.org/b1") }
442
+ before do
443
+ subject.aggregates = aggregates_uri
444
+ end
445
+ it "produce an ActiveTriple::Resource" do
446
+ expect(subject.aggregates.first).to be_a ActiveTriples::Resource
447
+ end
448
+ it "should have an ID accessor" do
449
+ expect(subject.aggregates_ids).to eq [aggregates_uri]
450
+ end
407
451
  end
408
452
 
409
453
  it "should be settable" do
@@ -729,4 +773,24 @@ END
729
773
  expect(subject.item.first.creator.first.knows.first.foaf_name).to eq ['Bob']
730
774
  end
731
775
  end
776
+
777
+ describe "callbacks" do
778
+ describe ".before_persist" do
779
+ before do
780
+ class DummyResource < ActiveTriples::Resource
781
+ def bla
782
+ self.title = "test"
783
+ end
784
+ end
785
+ DummyResource.before_persist :bla
786
+ repository = RDF::Repository.new
787
+ allow(subject).to receive(:repository).and_return(repository)
788
+ end
789
+ it "should call prior to persisting" do
790
+ expect(subject.title).to be_blank
791
+ subject.persist!
792
+ expect(subject.title).to eq ["test"]
793
+ end
794
+ end
795
+ end
732
796
  end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,6 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
1
4
  require 'bundler/setup'
2
5
  Bundler.setup
3
6
 
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.5.0
4
+ version: 0.6.0
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-12-08 00:00:00.000000000 Z
12
+ date: 2015-01-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rdf
@@ -109,6 +109,20 @@ dependencies:
109
109
  - - ">="
110
110
  - !ruby/object:Gem::Version
111
111
  version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: coveralls
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
112
126
  - !ruby/object:Gem::Dependency
113
127
  name: guard-rspec
114
128
  requirement: !ruby/object:Gem::Requirement
@@ -173,6 +187,7 @@ extra_rdoc_files:
173
187
  - LICENSE
174
188
  - README.md
175
189
  files:
190
+ - ".coveralls.yml"
176
191
  - ".gitignore"
177
192
  - ".travis.yml"
178
193
  - AUTHORS
@@ -180,12 +195,12 @@ files:
180
195
  - Guardfile
181
196
  - LICENSE
182
197
  - README.md
198
+ - Rakefile
183
199
  - active-triples.gemspec
184
200
  - lib/active/triples.rb
185
201
  - lib/active_triples.rb
186
202
  - lib/active_triples/configurable.rb
187
203
  - lib/active_triples/identifiable.rb
188
- - lib/active_triples/indexing.rb
189
204
  - lib/active_triples/list.rb
190
205
  - lib/active_triples/nested_attributes.rb
191
206
  - lib/active_triples/node_config.rb
@@ -1,76 +0,0 @@
1
- module ActiveFedora
2
- module Rdf
3
- module Indexing
4
- extend Deprecation
5
- extend ActiveSupport::Concern
6
-
7
- # In active_fedora 8, we can get the prefix part from Datastream.prefix
8
- def apply_prefix(name)
9
- "#{dsid.underscore}__#{name}"
10
- end
11
-
12
- def prefix(name)
13
- Deprecation.warn Indexing, "prefix is deprecated. Use apply_prefix instead. In active-fedora 8, the prefix method will just return the prefix to be applied, and will not do the applying. This will enable conformity between OmDatastream and RdfDatastream"
14
- apply_prefix(name)
15
- end
16
-
17
- def to_solr(solr_doc = Hash.new) # :nodoc:
18
- fields.each do |field_key, field_info|
19
- values = resource.get_values(field_key)
20
- Array(values).each do |val|
21
- if val.kind_of? RDF::URI
22
- val = val.to_s
23
- elsif val.kind_of? Rdf::Resource
24
- val = val.solrize
25
- end
26
- self.class.create_and_insert_terms(apply_prefix(field_key), val, field_info[:behaviors], solr_doc)
27
- end
28
- end
29
- solr_doc
30
- end
31
-
32
- # Gives the primary solr name for a column. If there is more than one indexer on the field definition, it gives the first
33
- def primary_solr_name(field)
34
- config = self.class.config_for_term_or_uri(field)
35
- return nil unless config # punt on index names for deep nodes!
36
- if behaviors = config.behaviors
37
- behaviors.each do |behavior|
38
- result = ActiveFedora::SolrService.solr_name(apply_prefix(field), behavior, type: config.type)
39
- return result if Solrizer::DefaultDescriptors.send(behavior).evaluate_suffix(:text).stored?
40
- end
41
- raise RuntimeError "no stored fields were found"
42
- end
43
- end
44
-
45
- module ClassMethods
46
- def prefix(dsid, name)
47
- Deprecation.warn Indexing, "prefix is deprecated and will be removed in active-fedora 8.0.0.", caller
48
- "#{dsid.underscore}__#{name}".to_sym
49
- end
50
-
51
- # Gives the datatype for a column.
52
- def type(field)
53
- config_for_term_or_uri(field).type
54
- end
55
- end
56
-
57
- private
58
- # returns a Hash, e.g.: {field => {:values => [], :type => :something, :behaviors => []}, ...}
59
- def fields
60
- field_map = {}.with_indifferent_access
61
-
62
- self.class.properties.each do |name, config|
63
- type = config[:type]
64
- behaviors = config[:behaviors]
65
- next unless type and behaviors
66
- next if config[:class_name] && config[:class_name] < ActiveFedora::Base
67
- resource.query(:subject => rdf_subject, :predicate => config[:predicate]).each_statement do |statement|
68
- field_map[name] ||= {:values => [], :type => type, :behaviors => behaviors}
69
- field_map[name][:values] << statement.object.to_s
70
- end
71
- end
72
- field_map
73
- end
74
- end
75
- end
76
- end