code cleanup thanks to reek
This commit is contained in:
parent
6ca0ba2297
commit
8c65729a51
57
README.md
57
README.md
@ -1 +1,56 @@
|
|||||||
ruby-avm-library
|
## The Ruby AVM Library
|
||||||
|
|
||||||
|
The Astronomy Visualization Metadata (AVM) standard is an extension of the Adobe XMP format. This
|
||||||
|
extension adds information to an astronomical image that describes the scientific data and methods
|
||||||
|
of collection that went in to producing the image. This Ruby library assists in reading the metadata from
|
||||||
|
XMP documents and writing out AVM data as a new XMP file.
|
||||||
|
|
||||||
|
## Installing the library
|
||||||
|
|
||||||
|
### From Bundler
|
||||||
|
|
||||||
|
In your Gemfile:
|
||||||
|
|
||||||
|
gem 'ruby-avm-library'
|
||||||
|
|
||||||
|
To use the current development version:
|
||||||
|
|
||||||
|
gem 'ruby-avm-library', :git => ''
|
||||||
|
|
||||||
|
### From RubyGems
|
||||||
|
|
||||||
|
gem install ruby-avm-library
|
||||||
|
|
||||||
|
## Basic usage
|
||||||
|
|
||||||
|
### Reading an XMP file
|
||||||
|
|
||||||
|
require 'avm/image'
|
||||||
|
|
||||||
|
image = AVM::Image.from_xml(File.read('my-file.xmp'))
|
||||||
|
|
||||||
|
puts image.title #=> "The title of the image"
|
||||||
|
|
||||||
|
### Writing XML data
|
||||||
|
|
||||||
|
image.to_xml #=> <xmp data in xml format />
|
||||||
|
|
||||||
|
### Creating an Image from scratch
|
||||||
|
|
||||||
|
image = AVM::Image.new
|
||||||
|
image.title = "The title of the image"
|
||||||
|
|
||||||
|
observation = image.create_observation(:instrument => 'HST', :color_assignment => 'Green')
|
||||||
|
contact = image.creator.create_contact(:name => 'John Bintz')
|
||||||
|
|
||||||
|
## Command line tool
|
||||||
|
|
||||||
|
`avm2avm` currently performs one function: take an XMP file from stdin and pretty print the image as a Hash:
|
||||||
|
|
||||||
|
avm2avm < my-file.xmp
|
||||||
|
|
||||||
|
## More resources
|
||||||
|
|
||||||
|
* RDoc: http://ruby-docs.info/blah
|
||||||
|
* AVM Standard: http://www.virtualastronomy.org/
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ require 'avm/image'
|
|||||||
require 'pp'
|
require 'pp'
|
||||||
|
|
||||||
module AVM
|
module AVM
|
||||||
|
# The CLI interface
|
||||||
class CLI < ::Thor
|
class CLI < ::Thor
|
||||||
default_task :convert
|
default_task :convert
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
module AVM
|
module AVM
|
||||||
|
# A contributor to an image
|
||||||
class Contact
|
class Contact
|
||||||
FIELD_MAP = {
|
FIELD_MAP = {
|
||||||
:zip => :postal_code,
|
:zip => :postal_code,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
module AVM
|
module AVM
|
||||||
|
# Build a ControlledVocabulary set of classes for use with CV fields
|
||||||
module ControlledVocabulary
|
module ControlledVocabulary
|
||||||
class << self
|
class << self
|
||||||
def included(klass)
|
def included(klass)
|
||||||
|
@ -2,6 +2,7 @@ require 'avm/contact'
|
|||||||
require 'nokogiri'
|
require 'nokogiri'
|
||||||
|
|
||||||
module AVM
|
module AVM
|
||||||
|
# A container for Contacts (contributors to an image)
|
||||||
class Creator
|
class Creator
|
||||||
attr_reader :contacts, :image
|
attr_reader :contacts, :image
|
||||||
|
|
||||||
@ -34,8 +35,8 @@ module AVM
|
|||||||
end
|
end
|
||||||
|
|
||||||
def method_missing(key, *opts)
|
def method_missing(key, *opts)
|
||||||
if key.to_s[-1..-1] == '='
|
if (key_to_s = key.to_s)[-1..-1] == '='
|
||||||
@options[key.to_s[0..-2].to_sym] = opts.first
|
@options[key_to_s[0..-2].to_sym] = opts.first
|
||||||
else
|
else
|
||||||
if PRIMARY_CONTACT_FIELDS.include?(key)
|
if PRIMARY_CONTACT_FIELDS.include?(key)
|
||||||
primary_contact_field key
|
primary_contact_field key
|
||||||
|
@ -8,6 +8,7 @@ require 'avm/coordinate_frame'
|
|||||||
require 'avm/observation'
|
require 'avm/observation'
|
||||||
|
|
||||||
module AVM
|
module AVM
|
||||||
|
# A single image, which has Observations, Contacts, and other metadata
|
||||||
class Image
|
class Image
|
||||||
DUBLIN_CORE_FIELDS = [ :title, :description ]
|
DUBLIN_CORE_FIELDS = [ :title, :description ]
|
||||||
|
|
||||||
@ -151,16 +152,15 @@ module AVM
|
|||||||
@options = options
|
@options = options
|
||||||
|
|
||||||
AVM_TO_FLOAT.each do |field|
|
AVM_TO_FLOAT.each do |field|
|
||||||
if @options[field]
|
@options[field] = case (value = @options[field])
|
||||||
case @options[field]
|
|
||||||
when Array
|
when Array
|
||||||
@options[field].collect!(&:to_f)
|
value.collect(&:to_f)
|
||||||
else
|
else
|
||||||
@options[field] = @options[field].to_f
|
value ? value.to_f : nil
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@observations = []
|
@observations = []
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -336,11 +336,9 @@ module AVM
|
|||||||
end
|
end
|
||||||
|
|
||||||
def string_date_or_nil(field)
|
def string_date_or_nil(field)
|
||||||
return nil if !send(field)
|
(value = send(field)) ? value.strftime('%Y-%m-%d') : nil
|
||||||
send(field).strftime('%Y-%m-%d')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def alt_li_tag(text)
|
def alt_li_tag(text)
|
||||||
%{<rdf:Alt><rdf:li xml:lang="x-default">#{text}</rdf:li></rdf:Alt>}
|
%{<rdf:Alt><rdf:li xml:lang="x-default">#{text}</rdf:li></rdf:Alt>}
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
require 'delegate'
|
require 'delegate'
|
||||||
|
|
||||||
module AVM
|
module AVM
|
||||||
|
# Delegate of Nokogiri::XML::Node which fixes XPath queries to use the correct namespace prefixes
|
||||||
class Node < DelegateClass(Nokogiri::XML::Node)
|
class Node < DelegateClass(Nokogiri::XML::Node)
|
||||||
def initialize(xmp, node)
|
def initialize(xmp, node)
|
||||||
@xmp, @node = xmp, node
|
@xmp, @node = xmp, node
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
module AVM
|
module AVM
|
||||||
|
# An individual observation by a single instrument w/ specific settings.
|
||||||
|
# Astronomical images are made of one or more Observations.
|
||||||
class Observation
|
class Observation
|
||||||
AVM_SINGLE_FIELDS = %w{Facility Instrument Spectral.ColorAssignment Spectral.Band Spectral.Bandpass Spectral.CentralWavelength Temporal.StartTime Temporal.IntegrationTime DatasetID}
|
AVM_SINGLE_FIELDS = %w{Facility Instrument Spectral.ColorAssignment Spectral.Band Spectral.Bandpass Spectral.CentralWavelength Temporal.StartTime Temporal.IntegrationTime DatasetID}
|
||||||
AVM_SINGLE_METHODS = [ :facility, :instrument, :color_assignment, :band, :bandpass, :wavelength, :string_start_time, :integration_time, :dataset_id ]
|
AVM_SINGLE_METHODS = [ :facility, :instrument, :color_assignment, :band, :bandpass, :wavelength, :string_start_time, :integration_time, :dataset_id ]
|
||||||
@ -7,9 +9,8 @@ module AVM
|
|||||||
attr_reader :image, :options
|
attr_reader :image, :options
|
||||||
|
|
||||||
def initialize(image, options = {})
|
def initialize(image, options = {})
|
||||||
options[:start_time] = options[:string_start_time] if options[:string_start_time]
|
|
||||||
|
|
||||||
@image, @options = image, options
|
@image, @options = image, options
|
||||||
|
@options[:start_time] = @options[:string_start_time] || @options[:start_time]
|
||||||
end
|
end
|
||||||
|
|
||||||
def method_missing(method)
|
def method_missing(method)
|
||||||
@ -17,7 +18,7 @@ module AVM
|
|||||||
end
|
end
|
||||||
|
|
||||||
def wavelength
|
def wavelength
|
||||||
@options[:wavelength] ? @options[:wavelength].to_f : nil
|
(wavelength = @options[:wavelength]) ? wavelength.to_f : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_time
|
def start_time
|
||||||
@ -25,11 +26,7 @@ module AVM
|
|||||||
end
|
end
|
||||||
|
|
||||||
def string_start_time
|
def string_start_time
|
||||||
if start_time
|
start_time ? start_time.strftime('%Y-%m-%dT%H:%M') : nil
|
||||||
start_time.strftime('%Y-%m-%dT%H:%M')
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_h
|
def to_h
|
||||||
|
@ -2,6 +2,7 @@ require 'nokogiri'
|
|||||||
require 'avm/node'
|
require 'avm/node'
|
||||||
|
|
||||||
module AVM
|
module AVM
|
||||||
|
# An XMP document wrapper, providing namespace handling and document reference assistance.
|
||||||
class XMP
|
class XMP
|
||||||
PREFIXES = {
|
PREFIXES = {
|
||||||
'dc' => 'Dublin Core',
|
'dc' => 'Dublin Core',
|
||||||
@ -58,20 +59,26 @@ module AVM
|
|||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def prefix_map
|
def current_namespaces
|
||||||
@prefix_map ||= Hash[doc.document.collect_namespaces.collect { |prefix, namespace|
|
doc.document.collect_namespaces
|
||||||
prefix = prefix.gsub('xmlns:', '')
|
end
|
||||||
result = nil
|
|
||||||
|
|
||||||
|
def prefix_map
|
||||||
|
@prefix_map ||= Hash[current_namespaces.collect { |prefix, namespace|
|
||||||
|
self.class.get_required_namespace(namespace, prefix.gsub('xmlns:', ''))
|
||||||
|
}.compact]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_required_namespace(namespace, prefix)
|
||||||
|
result = nil
|
||||||
REQUIRED_NAMESPACES.each do |original_prefix, target_namespace|
|
REQUIRED_NAMESPACES.each do |original_prefix, target_namespace|
|
||||||
result = [ original_prefix.to_s, prefix ] if namespace == target_namespace
|
result = [ original_prefix.to_s, prefix ] if namespace == target_namespace
|
||||||
end
|
end
|
||||||
result
|
result
|
||||||
}.compact]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_namespaces!
|
def ensure_namespaces!
|
||||||
existing = doc.document.collect_namespaces
|
existing = current_namespaces
|
||||||
|
|
||||||
REQUIRED_NAMESPACES.each do |namespace, url|
|
REQUIRED_NAMESPACES.each do |namespace, url|
|
||||||
doc.root.add_namespace_definition(namespace.to_s, url) if !existing.values.include?(url)
|
doc.root.add_namespace_definition(namespace.to_s, url) if !existing.values.include?(url)
|
||||||
@ -83,8 +90,8 @@ module AVM
|
|||||||
|
|
||||||
search('//rdf:Description').each do |description|
|
search('//rdf:Description').each do |description|
|
||||||
if first_child = description.first_element_child
|
if first_child = description.first_element_child
|
||||||
if first_child.namespace
|
if namespace = first_child.namespace
|
||||||
prefix = first_child.namespace.prefix
|
prefix = namespace.prefix
|
||||||
|
|
||||||
if prefix_description = PREFIXES[prefix_map.index(prefix)]
|
if prefix_description = PREFIXES[prefix_map.index(prefix)]
|
||||||
description[self % 'rdf:about'] = prefix_description
|
description[self % 'rdf:about'] = prefix_description
|
||||||
@ -94,13 +101,17 @@ module AVM
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if !at_xpath('//rdf:RDF')
|
ensure_rdf!
|
||||||
doc.first_element_child.add_child(self % '<rdf:RDF />')
|
ensure_missing_descriptions!(added)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ensure_rdf!
|
||||||
|
doc.first_element_child.add_child(self % '<rdf:RDF />') if !at_xpath('//rdf:RDF')
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_missing_descriptions!(already_added)
|
||||||
PREFIXES.each do |prefix, about|
|
PREFIXES.each do |prefix, about|
|
||||||
if !added.include?(prefix)
|
if !already_added.include?(prefix)
|
||||||
at_xpath('//rdf:RDF').add_child(self % %{<rdf:Description rdf:about="#{about}" />})
|
at_xpath('//rdf:RDF').add_child(self % %{<rdf:Description rdf:about="#{about}" />})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
12
reek.watchr
Normal file
12
reek.watchr
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
watch('lib/(.*)\.rb') { |file| reek(file[0]) }
|
||||||
|
watch('spec/(.*)_spec\.rb') { |file| reek("lib/#{file[1]}.rb") }
|
||||||
|
|
||||||
|
def reek(file = nil)
|
||||||
|
file ||= Dir['lib/**/*.rb'].join(' ')
|
||||||
|
spec_file = file.gsub('lib/', 'spec/').gsub('.rb', '_spec.rb')
|
||||||
|
|
||||||
|
system %{bundle exec rspec -c #{spec_file}}
|
||||||
|
system %{reek #{file}}
|
||||||
|
end
|
||||||
|
|
||||||
|
reek
|
@ -290,15 +290,15 @@ describe AVM::Image do
|
|||||||
it "should have the spatial tags" do
|
it "should have the spatial tags" do
|
||||||
xpath_text(avm, './avm:Spatial.CoordinateFrame').should == coordinate_frame
|
xpath_text(avm, './avm:Spatial.CoordinateFrame').should == coordinate_frame
|
||||||
xpath_text(avm, './avm:Spatial.Equinox').should == equinox
|
xpath_text(avm, './avm:Spatial.Equinox').should == equinox
|
||||||
xpath_list(avm, './avm:Spatial.ReferenceValue').should == reference_value.collect(&:to_s)
|
xpath_list(avm, './avm:Spatial.ReferenceValue').should == reference_value.collect { |v| v.to_f.to_s }
|
||||||
xpath_list(avm, './avm:Spatial.ReferenceDimension').should == reference_dimension.collect(&:to_s)
|
xpath_list(avm, './avm:Spatial.ReferenceDimension').should == reference_dimension.collect { |v| v.to_f.to_s }
|
||||||
xpath_list(avm, './avm:Spatial.ReferencePixel').should == reference_pixel.collect(&:to_s)
|
xpath_list(avm, './avm:Spatial.ReferencePixel').should == reference_pixel.collect { |v| v.to_f.to_s }
|
||||||
xpath_list(avm, './avm:Spatial.Scale').should == spatial_scale.collect(&:to_s)
|
xpath_list(avm, './avm:Spatial.Scale').should == spatial_scale.collect { |v| v.to_f.to_s }
|
||||||
xpath_text(avm, './avm:Spatial.CoordsystemProjection').should == coordinate_system_projection
|
xpath_text(avm, './avm:Spatial.CoordsystemProjection').should == coordinate_system_projection
|
||||||
xpath_text(avm, './avm:Spatial.Quality').should == spatial_quality
|
xpath_text(avm, './avm:Spatial.Quality').should == spatial_quality
|
||||||
xpath_text(avm, './avm:Spatial.Notes').should == spatial_notes
|
xpath_text(avm, './avm:Spatial.Notes').should == spatial_notes
|
||||||
xpath_text(avm, './avm:Spatial.FITSheader').should == fits_header
|
xpath_text(avm, './avm:Spatial.FITSheader').should == fits_header
|
||||||
xpath_list(avm, './avm:Spatial.CDMatrix').should == spatial_cd_matrix.collect(&:to_s)
|
xpath_list(avm, './avm:Spatial.CDMatrix').should == spatial_cd_matrix.collect { |v| v.to_f.to_s }
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should have the publisher tags" do
|
it "should have the publisher tags" do
|
||||||
|
Loading…
Reference in New Issue
Block a user