diff --git a/Rakefile b/Rakefile index 522e5140c..cadb7b454 100644 --- a/Rakefile +++ b/Rakefile @@ -21,6 +21,7 @@ begin gem.add_dependency('mime-types', '>= 1.16') gem.add_dependency('multipart-post') gem.add_dependency('nokogiri') + gem.add_dependency('yaml') gem.add_development_dependency "rspec", ">= 1.2.9" gem.add_development_dependency "mocha", ">= 1.2.9" diff --git a/lib/active_fedora/metadata_datastream.rb b/lib/active_fedora/metadata_datastream.rb index f95b5bc87..b87340b66 100644 --- a/lib/active_fedora/metadata_datastream.rb +++ b/lib/active_fedora/metadata_datastream.rb @@ -38,15 +38,15 @@ class MetadataDatastream < Datastream # return xml.to_s # end # - # # @tmpl ActiveFedora::MetadataDatastream - # # @node Nokogiri::XML::Node - # def self.from_xml(tmpl, node) # :nodoc: - # node.xpath("./foxml:datastreamVersion[last()]/foxml:xmlContent/fields/node()").each do |f| - # tmpl.send("#{f.name}_append", f.text) unless f.class == Nokogiri::XML::Text - # end - # tmpl.send(:dirty=, false) - # tmpl - # end + # @tmpl ActiveFedora::MetadataDatastream + # @node Nokogiri::XML::Node + def self.from_xml(tmpl, node) # :nodoc: + node.xpath("./foxml:datastreamVersion[last()]/foxml:xmlContent/fields/node()").each do |f| + tmpl.send("#{f.name}_append", f.text) unless f.class == Nokogiri::XML::Text + end + tmpl.send(:dirty=, false) + tmpl + end # This method generates the various accessor and mutator methods on self for the datastream metadata attributes. # each field will have the 3 magic methods: diff --git a/lib/active_fedora/metadata_datastream_helper.rb b/lib/active_fedora/metadata_datastream_helper.rb index 77732bf3a..5874c9cd1 100644 --- a/lib/active_fedora/metadata_datastream_helper.rb +++ b/lib/active_fedora/metadata_datastream_helper.rb @@ -12,16 +12,6 @@ def fields @@classFields end - # @tmpl ActiveFedora::MetadataDatastream - # @node Nokogiri::XML::Node - def from_xml(tmpl, node) # :nodoc: - node.xpath("./foxml:datastreamVersion[last()]/foxml:xmlContent/fields/node()").each do |f| - tmpl.send("#{f.name}_append", f.text) unless f.class == Nokogiri::XML::Text - end - tmpl.send(:dirty=, false) - tmpl - end - end def self.included(klass) diff --git a/lib/active_fedora/nokogiri_datastream.rb b/lib/active_fedora/nokogiri_datastream.rb index b2c1217ad..8b5f57d0c 100644 --- a/lib/active_fedora/nokogiri_datastream.rb +++ b/lib/active_fedora/nokogiri_datastream.rb @@ -1,18 +1,18 @@ require "nokogiri" +require "om" #this class represents a MetadataDatastream, a special case of ActiveFedora::Datastream class ActiveFedora::NokogiriDatastream < ActiveFedora::Datastream - + include ActiveFedora::MetadataDatastreamHelper - - self.xml_model = Nokogiri::XML::Document - + include OM::XML + attr_accessor :ng_xml #constructor, calls up to ActiveFedora::Datastream's constructor def initialize(attrs=nil) super @fields={} - @ng_xml = self.class.xml_model.new() + self.class.from_xml(blob, self) end def to_solr(solr_doc = Solr::Document.new) # :nodoc: @@ -27,34 +27,5 @@ def to_solr(solr_doc = Solr::Document.new) # :nodoc: return solr_doc end - - def to_xml(xml = REXML::Document.new("")) #:nodoc: - fields.each_pair do |field,field_info| - el = REXML::Element.new("#{field.to_s}") - if field_info[:element_attrs] - field_info[:element_attrs].each{|k,v| el.add_attribute(k.to_s, v.to_s)} - end - field_info[:values].each do |val| - el = el.clone - el.text = val.to_s - if xml.class == REXML::Document - xml.root.elements.add(el) - else - xml.add(el) - end - end - end - return xml.to_s - end - - # @tmpl ActiveFedora::MetadataDatastream - # @node Nokogiri::XML::Node - def self.from_xml(tmpl, node) # :nodoc: - node.xpath("./foxml:datastreamVersion[last()]/foxml:xmlContent/fields/node()").each do |f| - tmpl.send("#{f.name}_append", f.text) unless f.class == Nokogiri::XML::Text - end - tmpl.send(:dirty=, false) - tmpl - end end \ No newline at end of file diff --git a/lib/active_fedora/solr_service.rb b/lib/active_fedora/solr_service.rb index 068a3133e..e56324c65 100644 --- a/lib/active_fedora/solr_service.rb +++ b/lib/active_fedora/solr_service.rb @@ -1,5 +1,6 @@ require 'solr' require "active_fedora/solr_mapper" +require "yaml" module ActiveFedora class SolrService diff --git a/lib/hydra_libs/opinionated_mods_document.rb b/lib/hydra_libs/opinionated_mods_document.rb index a767e63e4..9e4436cd1 100644 --- a/lib/hydra_libs/opinionated_mods_document.rb +++ b/lib/hydra_libs/opinionated_mods_document.rb @@ -1,2 +1,14 @@ -class OpinionatedModsDocument +class OpinionatedModsDocument < Nokogiri::XML::Document + + include OM::XML + + self.schema_url = "http://www.loc.gov/standards/mods/v3/mods-3-2.xsd" + + # Could add support for multiple root declarations. + # For now, assume that any modsCollections have already been broken up and fed in as individual mods documents + # root :mods_collection, :path=>"modsCollection", + # :attributes=>[], + # :subelements => :mods + root_property :mods, "mods", "http://www.loc.gov/mods/v3", :attributes=>["id", "version"] + end \ No newline at end of file diff --git a/spec/unit/mods_datastream_spec.rb b/spec/unit/mods_datastream_spec.rb deleted file mode 100644 index ff16190cb..000000000 --- a/spec/unit/mods_datastream_spec.rb +++ /dev/null @@ -1,289 +0,0 @@ -require File.join( File.dirname(__FILE__), "../spec_helper" ) - -require 'active_fedora' -require 'hydra' - -describe ModsDatastream do - - before(:all) do - @sample_fields = {:publisher => {:values => ["publisher1"], :type => :string}, - :coverage => {:values => ["coverage1", "coverage2"], :type => :text}, - :creation_date => {:values => "fake-date", :type => :date}, - :mydate => {:values => "fake-date", :type => :date}, - :empty_field => {:values => {}} - } - @sample_xml = XmlSimple.xml_in("coverage1coverage2fake-datefake-datepublisher1") - - end - - before(:each) do - @test_ds = ModsDatastream.new - end - - after(:each) do - end - - describe '#new' do - it 'should provide #new' do - ModsDatastream.should respond_to(:new) - @test_ds.ng_xml.should be_instance_of(OpinionatedModsDocument) - end - end - - - it 'should provide .fields' do - @test_ds.should respond_to(:fields) - end - - describe '.save' do - it "should provide .save" do - @test_ds.should respond_to(:save) - end - it "should persist the product of .to_xml in fedora" do - Fedora::Repository.instance.expects(:save) - @test_ds.expects(:to_xml).returns("fake xml") - @test_ds.expects(:blob=).with("fake xml") - @test_ds.save - end - end - - describe '.to_xml' do - it "should provide .to_xml" do - @test_ds.should respond_to(:to_xml) - end - it 'should output the fields hash as XML' do - @test_ds.expects(:fields).returns(@sample_fields) - #sample_rexml = REXML::Document.new(sample_xml) - #returned_rexml = REXML::Document.new(@test_ds.to_dc_xml) - #returned_rexml.to_s.should == sample_rexml.to_s - returned_xml = XmlSimple.xml_in(@test_ds.to_xml) - returned_xml.should == @sample_xml - end - - it 'should accept an optional REXML Document as an argument and insert its fields into that' do - @test_ds.expects(:fields).returns(@sample_fields) - rexml = REXML::Document.new("") - rexml.root.elements.expects(:add).times(5) - result = @test_ds.to_xml(rexml) - end - it 'should accept an optional REXML Document as an argument and insert its fields into that' do - @test_ds.expects(:fields).returns(@sample_fields) - rexml = REXML::Document.new("") - result = @test_ds.to_xml(rexml) - XmlSimple.xml_in(rexml.to_s).should == @sample_xml - XmlSimple.xml_in(result).should == @sample_xml - end - - it 'should add to root of REXML::Documents, but add directly to the elements if a REXML::Element is passed in' do - @test_ds.expects(:fields).returns(@sample_fields).times(2) - doc = REXML::Document.new("") - el = REXML::Element.new("") - doc.root.elements.expects(:add).times(5) - el.expects(:add).times(5) - @test_ds.to_xml(doc) - @test_ds.to_xml(el) - end - - end - - describe '.set_blob_for_save' do - it "should provide .set_blob_for_save" do - @test_ds.should respond_to(:set_blob_for_save) - end - - it "should set the blob to to_xml" do - @test_ds.expects(:blob=).with(@test_ds.to_xml) - @test_ds.set_blob_for_save - end - end - - describe '#field' do - - before(:each) do - class SpecDatastream < ActiveFedora::MetadataDatastream - def initialize - super - field :publisher, :string - field :coverage, :text - field :creation_date, :date - field :mydate, :date - field :mycomplicated_field, :string, :multiple=>false, :encoding=>'LCSH', :element_attrs=>{:foo=>:bar, :baz=>:bat} - end - end - end - - after(:each) do - Object.send(:remove_const, :SpecDatastream) - end - - it 'should add corresponding field to the @fields hash and set the field :type ' do - sds = SpecDatastream.new - sds.fields.should_not have_key(:bio) - sds.field :bio, :text - sds.fields.should have_key(:bio) - sds.fields[:bio].should have_key(:type) - sds.fields[:bio][:type].should == :text - sds.fields[:mycomplicated_field][:element_attrs].should == {:foo=>:bar, :baz=>:bat} - end - - # it "should insert custom element attrs into the xml stream" do - # sds = SpecDatastream.new - # sds.mycomplicated_field_values='foo' - # sds.fields[:mycomplicated_field][:element_attrs].should == {:foo=>:bar, :baz=>:bat} - # sds.to_xml.should == 'foo' - # end - - it "should add getters and setters and appenders with field name" do - local_test_ds = SpecDatastream.new - local_test_ds.should respond_to(:publisher_values) - local_test_ds.should respond_to(:publisher_append) - local_test_ds.should respond_to(:publisher_values=) - local_test_ds.publisher_values.class.should == Array - local_test_ds.should respond_to(:coverage_values) - local_test_ds.should respond_to(:coverage_values=) - local_test_ds.should respond_to(:coverage_append) - local_test_ds.should respond_to(:creation_date_values) - local_test_ds.should respond_to(:creation_date_append) - local_test_ds.should respond_to(:creation_date_values=) - local_test_ds.should respond_to(:mydate_values) - local_test_ds.should respond_to(:mydate_append) - local_test_ds.should respond_to(:mydate_values=) - end - - it "should track field values at instance level, not at class level" do - local_test_ds1 = SpecDatastream.new - local_test_ds2 = SpecDatastream.new - local_test_ds1.publisher_values = ["publisher1", "publisher2"] - local_test_ds2.publisher_values = ["publisherA", "publisherB"] - - local_test_ds2.publisher_values.should == ["publisherA", "publisherB"] - local_test_ds1.publisher_values.should == ["publisher1", "publisher2"] - end - - it "should allow you to add field values using <<" do - local_test_ds1 = SpecDatastream.new - local_test_ds1.publisher_values << "publisher1" - local_test_ds1.publisher_values.should == ["publisher1"] - end - - it "should create setter that always turns non-arrays into arrays" do - local_test_ds = SpecDatastream.new - local_test_ds.publisher_values = "Foo" - local_test_ds.publisher_values.should == ["Foo"] - end - - it "should create setter that sets datastream.dirty? to true" do - local_test_ds = SpecDatastream.new - local_test_ds.should_not be_dirty - local_test_ds.publisher_values = "Foo" - local_test_ds.should be_dirty - - # Note: If you use << to append values, the datastream will not be marked as dirty! - #local_test_ds.dirty = false - - #local_test_ds.should_not be_dirty - #local_test_ds.publisher_values << "Foo" - #local_test_ds.should be_dirty - end - - it "should add any extra opts to the field hash" do - local_test_ds = SpecDatastream.new - local_test_ds.field "myfield", :string, :foo => "foo", :bar => "bar" - local_test_ds.fields[:myfield].should have_key(:foo) - local_test_ds.fields[:myfield][:foo].should == "foo" - local_test_ds.fields[:myfield].should have_key(:bar) - local_test_ds.fields[:myfield][:bar].should == "bar" - end - - end - - describe ".to_solr" do - - after(:all) do - # Revert to default mappings after running tests - ActiveFedora::SolrService.load_mappings - end - - it "should provide .to_solr and return a SolrDocument" do - @test_ds.should respond_to(:to_solr) - @test_ds.to_solr.should be_kind_of(Solr::Document) - end - - it "should optionally allow you to provide the Solr::Document to add fields to and return that document when done" do - doc = Solr::Document.new - @test_ds.to_solr(doc).should equal(doc) - end - - it "should iterate through @fields hash" do - @test_ds.expects(:fields).returns(@sample_fields) - solr_doc = @test_ds.to_solr - - solr_doc[:publisher_t].should == "publisher1" - solr_doc[:coverage_t].should == "coverage1" - solr_doc[:creation_date_dt].should == "fake-date" - solr_doc[:mydate_dt].should == "fake-date" - - solr_doc[:empty_field_t].should be_nil - end - - it "should allow multiple values for a single field" - - it 'should append create keys in format field_name + _ + field_type' do - @test_ds.stubs(:fields).returns(@sample_fields) - - #should have these - - @test_ds.to_solr[:publisher_t].should_not be_nil - @test_ds.to_solr[:coverage_t].should_not be_nil - @test_ds.to_solr[:creation_date_dt].should_not be_nil - - #should NOT have these - @test_ds.to_solr[:narrator].should be_nil - @test_ds.to_solr[:title].should be_nil - @test_ds.to_solr[:empty_field].should be_nil - - end - - it "should use Solr mappings to generate field names" do - ActiveFedora::SolrService.load_mappings(File.join(File.dirname(__FILE__), "..", "..", "config", "solr_mappings_af_0.1.yml")) - @test_ds.stubs(:fields).returns(@sample_fields) - solr_doc = @test_ds.to_solr - - #should have these - - solr_doc[:publisher_field].should == "publisher1" - solr_doc[:coverage_field].should == "coverage1" - solr_doc[:creation_date_date].should == "fake-date" - solr_doc[:mydate_date].should == "fake-date" - - solr_doc[:publisher_t].should be_nil - solr_doc[:coverage_t].should be_nil - solr_doc[:creation_date_dt].should be_nil - - # Reload default mappings - ActiveFedora::SolrService.load_mappings - end - - it 'should append _dt to dates' do - @test_ds.expects(:fields).returns(@sample_fields).at_least_once - - #should have these - - @test_ds.to_solr[:creation_date_dt].should_not be_nil - @test_ds.to_solr[:mydate_dt].should_not be_nil - - #should NOT have these - - @test_ds.to_solr[:mydate].should be_nil - @test_ds.to_solr[:creation_date_date].should be_nil - end - - end - - describe '.fields' do - it "should return a Hash" do - @test_ds.fields.should be_instance_of(Hash) - end - end - -end diff --git a/spec/unit/nokogiri_datastream_spec.rb b/spec/unit/nokogiri_datastream_spec.rb index 2549936d6..325c1931f 100644 --- a/spec/unit/nokogiri_datastream_spec.rb +++ b/spec/unit/nokogiri_datastream_spec.rb @@ -15,7 +15,7 @@ end before(:each) do - @test_ds = ActiveFedora::NokogiriDatastream.new + @test_ds = ActiveFedora::NokogiriDatastream.new(:blob=>"") end after(:each) do @@ -26,6 +26,10 @@ ActiveFedora::NokogiriDatastream.should respond_to(:new) @test_ds.ng_xml.should be_instance_of(Nokogiri::XML::Document) end + it 'should load xml from blob if provided' do + test_ds1 = ActiveFedora::NokogiriDatastream.new(:blob=>"") + test_ds1.ng_xml.to_xml.should == "\n\n \n\n" + end end @@ -49,33 +53,35 @@ it "should provide .to_xml" do @test_ds.should respond_to(:to_xml) end - it 'should output the fields hash as XML' do - @test_ds.expects(:fields).returns(@sample_fields) - returned_xml = XmlSimple.xml_in(@test_ds.to_xml) - returned_xml.should == @sample_xml + + it "should ng_xml.to_xml" do + @test_ds.ng_xml.expects(:to_xml).returns("xml") + @test_ds.to_xml.should == "xml" end - it 'should accept an optional REXML Document as an argument and insert its fields into that' do - @test_ds.expects(:fields).returns(@sample_fields) - rexml = REXML::Document.new("") - rexml.root.elements.expects(:add).times(5) - result = @test_ds.to_xml(rexml) + it 'should accept an optional Nokogiri::XML Document as an argument and insert its fields into that (mocked test)' do + doc = Nokogiri::XML::Document.parse("") + doc.root.expects(:add_child).with(@test_ds.ng_xml.root) + @test_ds.to_xml(doc) end - it 'should accept an optional REXML Document as an argument and insert its fields into that' do - @test_ds.expects(:fields).returns(@sample_fields) - rexml = REXML::Document.new("") - result = @test_ds.to_xml(rexml) - XmlSimple.xml_in(rexml.to_s).should == @sample_xml - XmlSimple.xml_in(result).should == @sample_xml + + it 'should accept an optional Nokogiri::XML Document as an argument and insert its fields into that (functional test)' do + expected_result = XmlSimple.xml_in("") + doc = Nokogiri::XML::Document.parse("") + result = @test_ds.to_xml(doc) + XmlSimple.xml_in(doc.to_s).should == expected_result + XmlSimple.xml_in(result).should == expected_result end - it 'should add to root of REXML::Documents, but add directly to the elements if a REXML::Element is passed in' do - @test_ds.expects(:fields).returns(@sample_fields).times(2) - doc = REXML::Document.new("") - el = REXML::Element.new("") - doc.root.elements.expects(:add).times(5) - el.expects(:add).times(5) - @test_ds.to_xml(doc) + it 'should add to root of Nokogiri::XML::Documents, but add directly to the elements if a Nokogiri::XML::Node is passed in' do + mock_new_node = mock("new node") + mock_new_node.stubs(:to_xml).returns("foo") + + doc = Nokogiri::XML::Document.parse("") + el = Nokogiri::XML::Node.new("test_element", Nokogiri::XML::Document.new) + doc.root.expects(:add_child).with(@test_ds.ng_xml.root).returns(mock_new_node) + el.expects(:add_child).with(@test_ds.ng_xml.root).returns(mock_new_node) + @test_ds.to_xml(doc).should @test_ds.to_xml(el) end @@ -92,118 +98,6 @@ end end - describe '#field' do - - before(:each) do - class SpecDatastream < ActiveFedora::MetadataDatastream - def initialize - super - field :publisher, :string - field :coverage, :text - field :creation_date, :date - field :mydate, :date - field :mycomplicated_field, :string, :multiple=>false, :encoding=>'LCSH', :element_attrs=>{:foo=>:bar, :baz=>:bat} - end - end - end - - after(:each) do - Object.send(:remove_const, :SpecDatastream) - end - - - describe ".values" do - it "should call ng_xml.values_for(field_name) OR call corresponding lookup method and build an array of values by calling .value on each node in the set" - end - - describe ".values<<" do - it "should call corresponding builder method" - end - - describe ".values=" do - it "should wipe out any existing nodes, use the corresponding builder, and insert new node(s) as the replacement" - end - - it 'should add corresponding field to the @fields hash and set the field :type ' do - sds = SpecDatastream.new - sds.fields.should_not have_key(:bio) - sds.field :bio, :text - sds.fields.should have_key(:bio) - sds.fields[:bio].should have_key(:type) - sds.fields[:bio][:type].should == :text - sds.fields[:mycomplicated_field][:element_attrs].should == {:foo=>:bar, :baz=>:bat} - end - - # it "should insert custom element attrs into the xml stream" do - # sds = SpecDatastream.new - # sds.mycomplicated_field_values='foo' - # sds.fields[:mycomplicated_field][:element_attrs].should == {:foo=>:bar, :baz=>:bat} - # sds.to_xml.should == 'foo' - # end - - it "should add getters and setters and appenders with field name" do - local_test_ds = SpecDatastream.new - local_test_ds.should respond_to(:publisher_values) - local_test_ds.should respond_to(:publisher_append) - local_test_ds.should respond_to(:publisher_values=) - local_test_ds.publisher_values.class.should == Array - local_test_ds.should respond_to(:coverage_values) - local_test_ds.should respond_to(:coverage_values=) - local_test_ds.should respond_to(:coverage_append) - local_test_ds.should respond_to(:creation_date_values) - local_test_ds.should respond_to(:creation_date_append) - local_test_ds.should respond_to(:creation_date_values=) - local_test_ds.should respond_to(:mydate_values) - local_test_ds.should respond_to(:mydate_append) - local_test_ds.should respond_to(:mydate_values=) - end - - it "should track field values at instance level, not at class level" do - local_test_ds1 = SpecDatastream.new - local_test_ds2 = SpecDatastream.new - local_test_ds1.publisher_values = ["publisher1", "publisher2"] - local_test_ds2.publisher_values = ["publisherA", "publisherB"] - - local_test_ds2.publisher_values.should == ["publisherA", "publisherB"] - local_test_ds1.publisher_values.should == ["publisher1", "publisher2"] - end - - it "should allow you to add field values using <<" do - local_test_ds1 = SpecDatastream.new - local_test_ds1.publisher_values << "publisher1" - local_test_ds1.publisher_values.should == ["publisher1"] - end - - it "should create setter that always turns non-arrays into arrays" do - local_test_ds = SpecDatastream.new - local_test_ds.publisher_values = "Foo" - local_test_ds.publisher_values.should == ["Foo"] - end - - it "should create setter that sets datastream.dirty? to true" do - local_test_ds = SpecDatastream.new - local_test_ds.should_not be_dirty - local_test_ds.publisher_values = "Foo" - local_test_ds.should be_dirty - - # Note: If you use << to append values, the datastream will not be marked as dirty! - #local_test_ds.dirty = false - - #local_test_ds.should_not be_dirty - #local_test_ds.publisher_values << "Foo" - #local_test_ds.should be_dirty - end - - it "should add any extra opts to the field hash" do - local_test_ds = SpecDatastream.new - local_test_ds.field "myfield", :string, :foo => "foo", :bar => "bar" - local_test_ds.fields[:myfield].should have_key(:foo) - local_test_ds.fields[:myfield][:foo].should == "foo" - local_test_ds.fields[:myfield].should have_key(:bar) - local_test_ds.fields[:myfield][:bar].should == "bar" - end - - end describe ".to_solr" do