require 'spec_helper'
describe ActiveFedora::Base do
context "with an om datastream" do
before :all do
class BarStream2 < ActiveFedora::OmDatastream
set_terminology do |t|
t.root(path: "animals", xmlns: "urn:zoobar")
t.waterfowl do
t.ducks do
t.duck
end
end
t.donkey
t.cow
t.pig
t.horse
end
def self.xml_template
Nokogiri::XML::Document.parse '
'
end
end
end
after :all do
Object.send(:remove_const, :BarStream2)
end
describe "#property" do
context "with an xml property (default cardinality)" do
before do
class BarHistory4 < ActiveFedora::Base
has_subresource 'xmlish', class_name: 'BarStream2'
Deprecation.silence(ActiveFedora::Attributes) do
property :cow, delegate_to: 'xmlish'
end
end
end
after do
Object.send(:remove_const, :BarHistory4)
end
let(:obj) { BarHistory4.new }
before { obj.cow = ['one', 'two'] }
describe "the object accessor" do
subject { obj.cow }
it { is_expected.to eq ['one', 'two'] }
end
describe "the datastream accessor" do
subject { obj.xmlish.cow }
it { is_expected.to eq ['one', 'two'] }
end
end
context "with multiple set to false" do
before do
class BarHistory4 < ActiveFedora::Base
has_subresource 'xmlish', class_name: 'BarStream2'
Deprecation.silence(ActiveFedora::Attributes) do
property :cow, delegate_to: 'xmlish', multiple: false
end
end
end
after do
Object.send(:remove_const, :BarHistory4)
end
let(:obj) { BarHistory4.new }
before { obj.cow = 'one' }
describe "the object accessor" do
subject { obj.cow }
it { is_expected.to eq 'one' }
end
end
context "when updating and saving a property" do
before do
class BarHistory4 < ActiveFedora::Base
has_subresource 'xmlish', class_name: 'BarStream2'
Deprecation.silence(ActiveFedora::Attributes) do
property :cow, delegate_to: 'xmlish', multiple: false
end
end
end
after do
Object.send(:remove_const, :BarHistory4)
end
let(:obj) { BarHistory4.new }
before do
obj.cow = 'two'
obj.save
obj.attached_files[:xmlish].content
obj.cow = 'three'
obj.save
end
describe "the attached datastream" do
subject { obj.attached_files[:xmlish].content }
it { is_expected.to include 'three' }
end
end
end
describe "first level delegation" do
before do
class MyDS1 < ActiveFedora::NtriplesRDFDatastream
property :animal_id, predicate: ::RDF::Vocab::DC.publisher
end
class MyDS2 < ActiveFedora::OmDatastream
set_terminology do |t|
t.root(path: "durh")
t.fubar
end
end
class BarHistory2 < ActiveFedora::Base
has_subresource 'someData', class_name: 'MyDS1'
has_subresource "withText", class_name: 'MyDS2'
has_subresource 'xmlish', class_name: 'BarStream2'
Deprecation.silence(ActiveFedora::Attributes) do
has_attributes :cow, datastream: 'xmlish' # for testing the default value of multiple
has_attributes :fubar, datastream: 'withText', multiple: true # test alternate datastream
has_attributes :pig, datastream: 'xmlish', multiple: false
has_attributes :horse, datastream: 'xmlish', multiple: true
has_attributes :duck, datastream: 'xmlish', at: [:waterfowl, :ducks, :duck], multiple: true
has_attributes :animal_id, datastream: 'someData', multiple: false
end
property :goose, predicate: ::RDF::URI.new('http://example.com#hasGoose')
end
end
after do
Object.send(:remove_const, :BarHistory2)
end
subject { BarHistory2.new }
describe "#attribute_names" do
context "on an instance" do
it "lists the attributes" do
expect(subject.attribute_names).to eq ["cow", "fubar", "pig", "horse", "duck", "animal_id", "goose"]
end
end
context "on a class" do
it "lists the attributes" do
expect(BarHistory2.attribute_names).to eq ["cow", "fubar", "pig", "horse", "duck", "animal_id", "goose"]
end
end
end
describe "inspect" do
it "shows the attributes" do
expect(subject.inspect).to eq "#"
end
describe "with a id" do
before { allow(subject).to receive(:id).and_return('test:123') }
it "shows a id" do
expect(subject.inspect).to eq "#"
end
end
describe "with no attributes" do
subject { described_class.new }
it "shows a id" do
expect(subject.inspect).to eq "#"
end
end
describe "with relationships" do
before do
class BarHistory3 < BarHistory2
belongs_to :library, predicate: ActiveFedora::RDF::Fcrepo::RelsExt.hasConstituent, class_name: 'BarHistory2'
end
subject.library = library
end
let(:library) { BarHistory2.create }
subject { BarHistory3.new }
after do
Object.send(:remove_const, :BarHistory3)
end
it "shows the library_id" do
expect(subject.inspect).to eq "#"
end
end
end
it "reveals the unique properties" do
expect(BarHistory2.unique?(:horse)).to be false
expect(BarHistory2.unique?(:pig)).to be true
expect(BarHistory2.unique?(:cow)).to be true
end
it "saves a delegated property" do
subject.fubar = ["Quack"]
expect(subject.fubar).to eq ["Quack"]
expect(subject.withText.get_values(:fubar).first).to eq 'Quack'
subject.cow = "Low"
expect(subject.cow).to eq "Low"
expect(subject.xmlish.term_values(:cow).first).to eq 'Low'
subject.pig = "Oink"
expect(subject.pig).to eq "Oink"
end
it "allows passing parameters to the delegate accessor" do
subject.fubar = ["one", "two"]
expect(subject.fubar(1)).to eq ['two']
end
describe "assigning wrong cardinality" do
it "does not allow passing a string to a multiple attribute writer" do
expect { subject.fubar = "Quack" }.to raise_error ArgumentError
expect { subject.fubar = ["Quack"] }.not_to raise_error
expect { subject.fubar = nil }.not_to raise_error
end
it "does not allow passing an enumerable to a unique attribute writer" do
expect { subject.cow = "Low" }.not_to raise_error
expect { subject.cow = ["Low"]
}.to raise_error ArgumentError, "You attempted to set the attribute `cow' on `BarHistory2' to an enumerable value. However, this attribute is declared as being singular."
expect { subject.cow = nil }.not_to raise_error
end
end
it "returns an array if marked as multiple" do
subject.horse = ["neigh", "whinny"]
expect(subject.horse).to eq ["neigh", "whinny"]
end
it "is able to delegate deeply into the terminology" do
subject.duck = ["Quack", "Peep"]
expect(subject.duck).to eq ["Quack", "Peep"]
end
context "change tracking" do
it "works for delegated attributes" do
expect {
subject.fubar = ["Meow"]
}.to change { subject.fubar_changed? }.from(false).to(true)
end
context "when a change is made to a property" do
it "is marked changed" do
expect {
subject.goose = ["honk!"]
}.to change { subject.goose_changed? }.from(false).to(true)
end
end
context "when a property is set to the same value" do
before do
subject.goose = ['honk!', 'Honk']
if ActiveModel.version < Gem::Version.new('4.2.0')
subject.send(:reset_changes)
else
subject.send(:clear_changes_information)
end
end
it "is not marked changed" do
subject.goose = ['honk!', 'Honk']
expect(subject.goose_changed?).to be false
end
end
end
describe "hash getters and setters" do
it "accepts symbol keys" do
subject[:duck] = ["Cluck", "Gobble"]
expect(subject[:duck]).to eq ["Cluck", "Gobble"]
end
it "accepts string keys" do
subject['duck'] = ["Cluck", "Gobble"]
expect(subject['duck']).to eq ["Cluck", "Gobble"]
end
it "accepts field names with _id that are not associations" do
subject['animal_id'] = "lemur"
expect(subject['animal_id']).to eq "lemur"
end
it "raises an error on the reader when the field isn't delegated" do
expect { subject['donkey'] }.to raise_error ActiveFedora::UnknownAttributeError, "unknown attribute 'donkey' for BarHistory2."
end
it "raises an error on the setter when the field isn't delegated" do
expect { subject['donkey'] = "bray" }.to raise_error ActiveFedora::UnknownAttributeError, "unknown attribute 'donkey' for BarHistory2."
end
end
describe "attributes=" do
it "raises an error on an invalid attribute" do
expect { subject.attributes = { 'donkey' => "bray" } }.to raise_error ActiveFedora::UnknownAttributeError, "unknown attribute 'donkey' for BarHistory2."
end
end
describe "attributes" do
let(:vals) { { 'cow' => "moo", 'pig' => 'oink', 'horse' => ['neigh'], "fubar" => [], 'duck' => ['quack'], 'animal_id' => '', 'goose' => [] } }
before { subject.attributes = vals }
it "returns a hash" do
expect(subject.attributes).to eq(vals.merge('id' => nil))
end
end
describe '.multiple?', focus: true do
it 'returns false if attribute has not been defined as multi-valued' do
expect(BarHistory2.multiple?(:pig)).to be false
end
it 'returns true if attribute is a ActiveTriples property' do
expect(BarHistory2.multiple?(:goose)).to be true
end
it 'returns true if attribute has been defined as multi-valued' do
expect(BarHistory2.multiple?(:horse)).to be true
end
it 'raises an error if the attribute does not exist' do
expect { BarHistory2.multiple?(:arbitrary_nonexistent_attribute) }.to raise_error ActiveFedora::UnknownAttributeError, "unknown attribute 'arbitrary_nonexistent_attribute' for BarHistory2."
end
end
describe ".datastream_class_for_name" do
it "returns the specifed class" do
expect(BarHistory2.send(:datastream_class_for_name, 'someData')).to eq MyDS1
end
end
end
describe "with a superclass" do
before :all do
class BarHistory2 < ActiveFedora::Base
has_subresource 'xmlish', class_name: 'BarStream2'
Deprecation.silence(ActiveFedora::Attributes) do
has_attributes :donkey, :cow, datastream: 'xmlish', multiple: true
end
end
class BarHistory3 < BarHistory2
end
end
after :all do
Object.send(:remove_const, :BarHistory3)
Object.send(:remove_const, :BarHistory2)
end
subject { BarHistory3.new }
it "is able to delegate deeply into the terminology" do
subject.donkey = ["Bray", "Hee-haw"]
expect(subject.donkey).to eq ["Bray", "Hee-haw"]
end
it "is able to track change status" do
expect {
subject.cow = ["Moo"]
}.to change { subject.cow_changed? }.from(false).to(true)
end
end
end
context "with a RDF datastream" do
before :all do
class BarRdfDatastream < ActiveFedora::NtriplesRDFDatastream
property :title, predicate: ::RDF::Vocab::DC.title
property :description, predicate: ::RDF::Vocab::DC.description
end
class BarHistory4 < ActiveFedora::Base
has_subresource 'rdfish', class_name: 'BarRdfDatastream'
Deprecation.silence(ActiveFedora::Attributes) do
has_attributes :title, datastream: 'rdfish', multiple: true
has_attributes :description, datastream: 'rdfish', multiple: false
end
end
end
after :all do
Object.send(:remove_const, :BarHistory4)
Object.send(:remove_const, :BarRdfDatastream)
end
subject { BarHistory4.new }
context "with a multivalued field" do
it "is able to track change status" do
expect {
subject.title = ["Title1", "Title2"]
}.to change { subject.title_changed? }.from(false).to(true)
end
end
context "with a single-valued field" do
it "is able to track change status" do
expect {
subject.description = "A brief description"
}.to change { subject.description_changed? }.from(false).to(true)
end
end
end
context "without a datastream" do
before :all do
class BarHistory4 < ActiveFedora::Base
end
end
after :all do
Object.send(:remove_const, :BarHistory4)
end
subject { BarHistory4 }
describe "has_attributes" do
it "raises an error" do
Deprecation.silence(ActiveFedora::Attributes) do
expect { subject.has_attributes :title, :description, multiple: true }.to raise_error
end
end
end
end
context "when an unknown datastream is specified" do
before :all do
class BarHistory4 < ActiveFedora::Base
Deprecation.silence(ActiveFedora::Attributes) do
has_attributes :description, datastream: 'rdfish', multiple: true
end
end
end
after :all do
Object.send(:remove_const, :BarHistory4)
end
subject { BarHistory4.new }
let(:error_message) { "Undefined file: `rdfish' in property description" }
it "raises an error on get" do
expect { subject.description }.to raise_error(ArgumentError, error_message)
end
it "raises an error on set" do
expect { subject.description = ['Neat'] }.to raise_error(ArgumentError, error_message)
end
describe ".datastream_class_for_name" do
it "returns the default class" do
expect(BarHistory4.send(:datastream_class_for_name, 'content')).to eq ActiveFedora::File
end
end
end
context "when a datastream is specified as a symbol" do
before :all do
class BarRdfDatastream < ActiveFedora::NtriplesRDFDatastream
property :title, predicate: ::RDF::Vocab::DC.title
property :description, predicate: ::RDF::Vocab::DC.description
end
class BarHistory4 < ActiveFedora::Base
has_subresource 'rdfish', class_name: 'BarRdfDatastream'
Deprecation.silence(ActiveFedora::Attributes) do
has_attributes :description, datastream: :rdfish
end
end
end
after :all do
Object.send(:remove_const, :BarHistory4)
Object.send(:remove_const, :BarRdfDatastream)
end
subject { BarHistory4.new(description: 'test1') }
it "is able to access the attributes" do
expect(subject.description).to eq 'test1'
end
end
context "when properties are defined on an object" do
before :all do
class BarHistory4 < ActiveFedora::Base
property :title, predicate: ::RDF::Vocab::DC.title do |index|
index.as :symbol
end
property :abstract, predicate: ::RDF::Vocab::DC.abstract, multiple: false
end
end
after :all do
Object.send(:remove_const, :BarHistory4)
end
let(:obj) { BarHistory4.new(title: ['test1']) }
subject { obj }
describe "accessing attributes" do
context "using generated methods" do
it "returns values" do
expect(subject.title).to eq ['test1']
end
end
context "using hash accessors" do
context "on single value fields" do
it "has a default value" do
expect(subject[:abstract]).to be_nil
end
context "when there are two assertions for the predicate" do
before do
subject.resource[:abstract] = ['foo', 'bar']
end
it "raises an error if just returning the first value would cause data loss" do
expect { subject[:abstract] }.to raise_error ActiveFedora::ConstraintError, "Expected \"abstract\" to have 0-1 statements, but there are 2"
end
end
end
context "multiple values" do
it "returns values" do
expect(subject[:title]).to eq ['test1']
end
end
context "on Fedora attributes" do
it "return values" do
expect(subject[:type]).to be_empty
expect(subject[:rdf_label]).to contain_exactly("test1")
end
end
end
end
context "indexing" do
let(:solr_doc) { obj.to_solr }
it "indexs the attributes" do
expect(solr_doc['title_ssim']).to eq ['test1']
end
end
describe "when an object of the wrong cardinality is set" do
it "does not allow passing a string to a multiple property writer" do
expect { subject.title = "Quack" }.to raise_error ArgumentError
expect { subject.title = ["Quack"] }.not_to raise_error
expect { subject.title = nil }.not_to raise_error
end
it "does not allow an enumerable to a unique attribute writer" do
expect { subject.abstract = "Low" }.not_to raise_error
expect { subject.abstract = ["Low"]
}.to raise_error ArgumentError, "You attempted to set the property `abstract' to an enumerable value. However, this property is declared as singular."
expect { subject.abstract = nil }.not_to raise_error
end
end
end
end