require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') require "om" describe "OM::XML::PropertyValueOperators" do before(:all) do #ModsHelpers.name_("Beethoven, Ludwig van", :date=>"1770-1827", :role=>"creator") class PropertiesValueOperatorsTest include OM::XML::Container include OM::XML::Properties include OM::XML::PropertyValueOperators # 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"], :schema=>"http://www.loc.gov/standards/mods/v3/mods-3-2.xsd" property :name_, :path=>"name", :attributes=>[:xlink, :lang, "xml:lang", :script, :transliteration, {:type=>["personal", "enumerated", "corporate"]} ], :subelements=>["namePart", "displayForm", "affiliation", :role, "description"], :default_content_path => "namePart", :convenience_methods => { :date => {:path=>"namePart", :attributes=>{:type=>"date"}}, :family_name => {:path=>"namePart", :attributes=>{:type=>"family"}}, :given_name => {:path=>"namePart", :attributes=>{:type=>"given"}}, :terms_of_address => {:path=>"namePart", :attributes=>{:type=>"termsOfAddress"}} } property :person, :variant_of=>:name_, :attributes=>{:type=>"personal"} property :role, :path=>"role", :parents=>[:name_], :attributes=>[ { "type"=>["text", "code"] } , "authority"], :default_content_path => "roleTerm" end end before(:each) do @sample = PropertiesValueOperatorsTest.from_xml( fixture( File.join("test_dummy_mods.xml") ) ) end after(:all) do Object.send(:remove_const, :PropertiesValueOperatorsTest) end describe ".property_values" do it "should call .lookup and then build an array of values from the returned nodeset (using default_node, etc as nessesary)" do lookup_opts = "insert args here" mock_node = mock("node") mock_node.expects(:text).returns("sample value").times(3) mock_nodeset = [mock_node, mock_node, mock_node] @sample.expects(:lookup).with(lookup_opts).returns(mock_nodeset) @sample.property_values(lookup_opts).should == ["sample value","sample value","sample value"] end end describe ".property_values_append" do it "looks up the parent using :parent_select, uses :child_index to choose the parent node from the result set, uses :template to build the node(s) to be inserted, inserts the :values(s) into the node(s) and adds the node(s) to the parent" do @sample.property_values_append( :parent_select => [:person, {:given_name=>"Tim", :family_name=>"Berners-Lee"}] , :child_index => :first, :template => [:person, :affiliation], :values => ["my new value", "another new value"] ) end it "should accept parent_select and template [property_reference, lookup_opts] as argument arrays for generators/lookups" do # this appends two affiliation nodes into the first person node whose name is Tim Berners-Lee expected_result = ' Berners-Lee Tim creator cre my new valueanother new value' @sample.property_values_append( :parent_select => [:person, {:given_name=>"Tim", :family_name=>"Berners-Lee"}] , :child_index => :first, :template => [:person, :affiliation], :values => ["my new value", "another new value"] ).to_xml.should == expected_result @sample.lookup(:person, {:given_name=>"Tim", :family_name=>"Berners-Lee"}).first.to_xml.should == expected_result end it "should accept symbols as arguments for generators/lookups" do # this appends a role of "my role" into the third "person" node in the document expected_result = "\n Klimt\n Gustav\n \n creator\n cre\n \n \n visionary\n vry\n \n my role" @sample.property_values_append( :parent_select => :person , :child_index => 3, :template => :role, :values => "my role" ).to_xml.should == expected_result @sample.lookup(:person)[3].to_xml.should == expected_result end it "should accept parent_select as an (xpath) string and template as a (template) string" do # this uses the provided template to add a node into the first node resulting from the xpath '//oxns:name[@type="personal"]' expected_result = "\n Berners-Lee\n Tim\n \n creator\n cre\n \n creator" @sample.ng_xml.xpath('//oxns:name[@type="personal" and position()=1]/oxns:role', @sample.ox_namespaces).length.should == 1 @sample.property_values_append( :parent_select =>'//oxns:name[@type="personal"]', :child_index => 0, :template => 'xml.role { xml.roleTerm( \'#{builder_new_value}\', :type=>\'code\', :authority=>\'marcrelator\') }', :values => "founder" ) @sample.ng_xml.xpath('//oxns:name[@type="personal" and position()=1]/oxns:role', @sample.ox_namespaces).length.should == 2 @sample.ng_xml.xpath('//oxns:name[@type="personal" and position()=1]/oxns:role[last()]/oxns:roleTerm', @sample.ox_namespaces).first.text.should == "founder" # @sample.lookup(:person).first.to_xml.should == expected_result end it "should support more complex mixing & matching" do expected_result = "\n Jobs\n Steve\n 2004\n \n creator\n cre\n \n foo" @sample.ng_xml.xpath('//oxns:name[@type="personal" and position()=2]/oxns:role', @sample.ox_namespaces).length.should == 1 @sample.property_values_append( :parent_select =>'//oxns:name[@type="personal"]', :child_index => 1, :template => [ :person, :role, {:attributes=>{"type"=>"code", "authority"=>"marcrelator"}} ], :values => "foo" ) @sample.ng_xml.xpath('//oxns:name[@type="personal" and position()=2]/oxns:role', @sample.ox_namespaces).length.should == 2 @sample.lookup(:person)[1].search("./oxns:role[last()]/oxns:roleTerm", @sample.ox_namespaces).first.text.should == "foo" end it "should raise exception if no node corresponds to the provided :parent_select and :child_index" end describe ".property_value_update" do it "should accept an xpath as :parent_select" do sample_xpath = '//oxns:name[@type="personal"]/oxns:role/oxns:roleTerm[@type="text"]' @sample.property_value_update( :parent_select =>sample_xpath, :child_index => 1, :value => "donor" ) @sample.ng_xml.xpath(sample_xpath, @sample.ox_namespaces)[1].text.should == "donor" end it "if :select is provided, should update the first node provided by that xpath statement" do sample_xpath = '//oxns:name[@type="personal" and position()=1]/oxns:namePart[@type="given"]' @sample.property_value_update( :select =>sample_xpath, :value => "Timmeh" ) @sample.ng_xml.xpath(sample_xpath, @sample.ox_namespaces).first.text.should == "Timmeh" end it "should replace the existing node if you pass a template and values" do pending @sample.property_value_update( :parent_select =>'//oxns:name[@type="personal"]', :child_index => 1, :template => [ :person, :role, {:attributes=>{"type"=>"code", "authority"=>"marcrelator"}} ], :value => "foo" ) 1.should == 2 end end describe ".property_value_delete" do it "should accept an xpath query as :select option" do generic_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role' specific_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role[oxns:roleTerm="visionary"]' select_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role[last()]' # Check that we're starting with 2 roles # Check that the specific node we want to delete exists @sample.lookup(generic_xpath).length.should == 2 @sample.lookup(specific_xpath).length.should == 1 @sample.property_value_delete( :select =>select_xpath ) # Check that we're finishing with 1 role @sample.lookup(generic_xpath).length.should == 1 # Check that the specific node we want to delete no longer exists @sample.lookup(specific_xpath).length.should == 0 end it "should accept :parent_select, :parent_index and :child_index options instead of a :select" do generic_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role/oxns:roleTerm' specific_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role[oxns:roleTerm="visionary"]' # Check that we're starting with 2 roles # Check that the specific node we want to delete exists @sample.lookup(generic_xpath).length.should == 4 @sample.lookup(specific_xpath).length.should == 1 # this is attempting to delete the last child (in this case roleTerm) from the 3rd role in the document. @sample.property_value_delete( :parent_select => [:person, :role], :parent_index => 3, :child_index => :last ) # Check that we're finishing with 1 role @sample.lookup(generic_xpath).length.should == 3 # Check that the specific node we want to delete no longer exists @sample.lookup(specific_xpath).length.should == 1 end it "should work if only :parent_select and :child_index are provided" do generic_xpath = '//oxns:name[@type="personal"]/oxns:role' # specific_xpath = '//oxns:name[@type="personal"]/oxns:role' # Check that we're starting with 2 roles # Check that the specific node we want to delete exists @sample.lookup(generic_xpath).length.should == 4 # @sample.lookup(specific_xpath).length.should == 1 @sample.property_value_delete( :parent_select => [:person, :role], :child_index => 3 ) # Check that we're finishing with 1 role @sample.lookup(generic_xpath).length.should == 3 end end end