From efff98eb321f06169a2c8e1e645d06311d74ba06 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Fri, 11 Mar 2011 13:27:17 -0500 Subject: [PATCH] read one creator --- lib/avm/creator.rb | 46 +++++++++++++++++--- lib/avm/image.rb | 8 ++++ lib/avm/xmp.rb | 53 ++++++++++++++++------- spec/avm/creator_spec.rb | 45 +++++++++++++++++++ spec/avm/xmp_spec.rb | 48 +++++++++++++++++++- spec/sample_files/creator/no_creator.xmp | 14 ++++++ spec/sample_files/creator/one_creator.xmp | 25 +++++++++++ spec/spec_helper.rb | 1 - 8 files changed, 217 insertions(+), 23 deletions(-) create mode 100644 spec/sample_files/creator/no_creator.xmp create mode 100644 spec/sample_files/creator/one_creator.xmp diff --git a/lib/avm/creator.rb b/lib/avm/creator.rb index fe3c6a8..05512e8 100644 --- a/lib/avm/creator.rb +++ b/lib/avm/creator.rb @@ -7,10 +7,13 @@ module AVM IPTC_CORE_FIELDS = [ :address, :city, :state, :zip, :country ] PRIMARY_CONTACT_FIELDS = IPTC_CORE_FIELDS + [ :province, :postal_code ] + IPTC_MULTI_FIELD_MAP = [ [ :telephone, 'CiTelWork' ], [ :email, 'CiEmailWork' ] ] + IPTC_CORE_FIELD_ELEMENT_NAMES = %w{CiAdrExtadr CiAdrCity CiAdrRegion CiAdrPcode CiAdrCtry} + IPTC_CORE_FIELDS_AND_NAMES = IPTC_CORE_FIELDS.zip(IPTC_CORE_FIELD_ELEMENT_NAMES) - def initialize(image) + def initialize(image, given_contacts = []) @options = {} - @contacts = [] + @contacts = given_contacts @image = image end @@ -18,6 +21,14 @@ module AVM @options.merge!(hash) end + def length + contacts.length + end + + def [](which) + contacts[which] + end + def method_missing(key, *opts) if key.to_s[-1..-1] == '=' @options[key.to_s[0..-2].to_sym] = opts.first @@ -31,7 +42,7 @@ module AVM end def add_to_document(document) - document.add_to_doc do |refs| + document.get_refs do |refs| creator = refs[:dublin_core].add_child('') list = creator.at_xpath('.//rdf:Seq') @@ -42,13 +53,13 @@ module AVM end if primary_contact - [ [ :telephone, 'CiTelWork' ], [ :email, 'CiEmailWork' ] ].each do |key, element_name| + IPTC_MULTI_FIELD_MAP.each do |key, element_name| contact_info.add_child "#{contacts.sort.collect(&key).join(',')}" end iptc_namespace = document.doc.root.namespace_scopes.find { |ns| ns.prefix == 'Iptc4xmpCore' } - IPTC_CORE_FIELDS.zip(%w{CiAdrExtadr CiAdrCity CiAdrRegion CiAdrPcode CiAdrCtry}).each do |key, element_name| + IPTC_CORE_FIELDS_AND_NAMES.each do |key, element_name| node = contact_info.document.create_element(element_name, primary_contact.send(key)) node.namespace = iptc_namespace contact_info.add_child node @@ -57,6 +68,31 @@ module AVM end end + def from_xml(image, document) + contacts = [] + document.get_refs do |refs| + refs[:dublin_core].search('.//rdf:li').each do |name| + contacts << { :name => name.text } + end + + IPTC_MULTI_FIELD_MAP.each do |key, element_name| + if node = refs[:iptc].at_xpath("//Iptc4xmpCore:#{element_name}") + node.text.split(',').collect(&:strip).each_with_index do |value, index| + contacts[index][key] = value + end + end + end + + IPTC_CORE_FIELDS_AND_NAMES.each do |key, element_name| + if node = refs[:iptc].at_xpath("//Iptc4xmpCore:#{element_name}") + contacts.first[key] = node.text.strip + end + end + end + + @contacts = contacts.collect { |contact| Contact.new(contact) } + end + def primary_contact @contacts.find(&:primary) || @contacts.sort.first end diff --git a/lib/avm/image.rb b/lib/avm/image.rb index 3f89423..4fbc93d 100644 --- a/lib/avm/image.rb +++ b/lib/avm/image.rb @@ -16,6 +16,14 @@ module AVM document.doc end + + def self.from_xml(string) + document = AVM::XMP.from_string(string) + + image = new + image.creator.from_xml(self, document) + image + end end end diff --git a/lib/avm/xmp.rb b/lib/avm/xmp.rb index aab7e77..6ce82e9 100644 --- a/lib/avm/xmp.rb +++ b/lib/avm/xmp.rb @@ -4,15 +4,49 @@ module AVM class XMP attr_reader :doc - def initialize - @doc = empty_xml_doc + def initialize(doc = nil) + @doc = doc || empty_xml_doc + ensure_namespaces! + ensure_descriptions_findable! end - def add_to_doc + def get_refs yield Hash[[ :dublin_core, :iptc ].collect { |key| [ key, send(key) ] }] end + def self.from_string(string) + new(Nokogiri::XML(string)) + end + private + def ensure_namespaces! + { + :x => "adobe:ns:meta/", + :rdf => "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + :dc => "http://purl.org/dc/elements/1.1/", + :photoshop => "http://ns.adobe.com/photoshop/1.0/", + :avm => "http://www.communicatingastronomy.org/avm/1.0/", + :Iptc4xmpCore => "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" + }.each do |namespace, url| + doc.root.add_namespace_definition(namespace.to_s, url) + end + end + + def ensure_descriptions_findable! + doc.search('//rdf:Description').each do |description| + if first_child = description.first_element_child + if first_child.namespace + case first_child.namespace.prefix + when 'dc' + description['about'] = 'Dublin Core' + when 'Iptc4xmpCore' + description['about'] = 'IPTC' + end + end + end + end + end + def dublin_core at_rdf_description "Dublin Core" end @@ -26,7 +60,7 @@ module AVM end def empty_xml_doc - document = Nokogiri::XML(<<-XML) + Nokogiri::XML(<<-XML) @@ -38,17 +72,6 @@ module AVM XML - - { - :dc => "http://purl.org/dc/elements/1.1/", - :photoshop => "http://ns.adobe.com/photoshop/1.0/", - :avm => "http://www.communicatingastronomy.org/avm/1.0/", - :Iptc4xmpCore => "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" - }.each do |namespace, url| - document.root.add_namespace_definition(namespace.to_s, url) - end - - document end end end diff --git a/spec/avm/creator_spec.rb b/spec/avm/creator_spec.rb index dfcd79e..2341200 100644 --- a/spec/avm/creator_spec.rb +++ b/spec/avm/creator_spec.rb @@ -91,6 +91,51 @@ describe AVM::Creator do its(:name) { should == first_name } end + describe '#from_xml' do + let(:document) { image.to_xml } + let(:image) { AVM::Image.from_xml(File.read(file_path)) } + + subject { image } + + context 'no creator' do + let(:file_path) { 'spec/sample_files/creator/no_creator.xmp' } + + its('creator.length') { should == 0 } + end + + context 'one creator' do + let(:file_path) { 'spec/sample_files/creator/one_creator.xmp' } + + its('creator.length') { should == 1 } + + context 'creator one' do + subject { image.creator[0] } + + let(:name) { 'John Bintz' } + let(:email) { 'bintz@stsci.edu' } + let(:telephone) { '800-555-1234' } + let(:address) { '3700 San Martin Drive' } + let(:city) { 'Baltimore' } + let(:state) { 'Maryland' } + let(:zip) { '21218' } + let(:country) { 'USA' } + + its(:name) { should == name } + its(:address) { should == address } + its(:city) { should == city } + its(:state) { should == state } + its(:zip) { should == zip } + its(:country) { should == country } + its(:email) { should == email } + its(:telephone) { should == telephone } + end + end + + context 'two creators' do + let(:file_path) { 'spec/sample_files/creator/two_creators.xmp' } + end + end + describe 'contact name node' do let(:first_name) { 'John' } let(:second_name) { 'Zohn' } diff --git a/spec/avm/xmp_spec.rb b/spec/avm/xmp_spec.rb index 20cc1f0..9ddcb56 100644 --- a/spec/avm/xmp_spec.rb +++ b/spec/avm/xmp_spec.rb @@ -4,9 +4,11 @@ require 'avm/xmp' describe AVM::XMP do let(:xmp) { self.class.describes.new } - describe '#add_to_doc' do + subject { xmp } + + describe '#get_refs' do before { - xmp.add_to_doc do |refs| + xmp.get_refs do |refs| refs[:dublin_core] << "" refs[:iptc] << "" end @@ -15,4 +17,46 @@ describe AVM::XMP do specify { xmp.doc.at_xpath('//rdf:Description[@about="Dublin Core"]//rdf:addedToDublinCore').should_not be_nil } specify { xmp.doc.at_xpath('//rdf:Description[@about="IPTC"]//rdf:addedToIPTC').should_not be_nil } end + + describe '.from_string' do + let(:xmp) { self.class.describes.from_string(string) } + let(:string) { '' } + + specify { xmp.doc.at_xpath('//node').should_not be_nil } + end + + describe '#ensure_descriptions_findable!' do + let(:document) { <<-XML } + + + #{content} + + + XML + + let(:xmp) { self.class.describes.new(Nokogiri::XML(document)) } + + context 'no nodes within' do + let(:content) { '' } + + [ 'Dublin Core', 'IPTC' ].each do |which| + specify { xmp.doc.at_xpath(%{//rdf:Description[@about="#{which}"]}).should be_nil } + end + end + + context 'has identifying nodes within' do + let(:content) { <<-XML } + + + + + + + XML + + [ 'Dublin Core', 'IPTC' ].each do |which| + specify { xmp.doc.at_xpath(%{//rdf:Description[@about="#{which}"]}).should_not be_nil } + end + end + end end diff --git a/spec/sample_files/creator/no_creator.xmp b/spec/sample_files/creator/no_creator.xmp new file mode 100644 index 0000000..f2aa172 --- /dev/null +++ b/spec/sample_files/creator/no_creator.xmp @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/spec/sample_files/creator/one_creator.xmp b/spec/sample_files/creator/one_creator.xmp new file mode 100644 index 0000000..25e4af3 --- /dev/null +++ b/spec/sample_files/creator/one_creator.xmp @@ -0,0 +1,25 @@ + + + + + John Bintz + + + + + 3700 San Martin Drive + Baltimore + Maryland + 21218 + USA + 800-555-1234 + bintz@stsci.edu + http://hubblesite.org + + + + + + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ae9d63c..274d037 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,3 @@ - RSpec.configure do |config| config.mock_with :mocha end