Skip to content

Commit

Permalink
Refactors TypeSpecimen to TypeMaterial, adds basic tests for TypeMate…
Browse files Browse the repository at this point in the history
…rial. Code organization in Protonym.
  • Loading branch information
mjy committed Jan 28, 2014
1 parent 66474a5 commit 918f9ad
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 117 deletions.
2 changes: 1 addition & 1 deletion app/models/person.rb
Expand Up @@ -26,7 +26,7 @@ class Person < ActiveRecord::Base
has_many :collecting_events, through: :collector_roles, source: :role_object, source_type: 'CollectingEvent'
has_many :taxon_determinations, through: :determiner_roles, source: :role_object, source_type: 'TaxonDetermination'
has_many :taxon_name_authors, through: :taxon_name_author_roles, source: :role_object, source_type: 'TaxonName'
has_many :type_specimens, through: :type_designator_roles, source: :role_object, source_type: 'TypeSpecimen'
has_many :type_material, through: :type_designator_roles, source: :role_object, source_type: 'TypeMaterial'

#scope :named, -> (name) {where(name: name)}
#scope :named_smith, where(last_name: 'Smith')
Expand Down
91 changes: 46 additions & 45 deletions app/models/protonym.rb
Expand Up @@ -13,6 +13,8 @@ class Protonym < TaxonName
where("taxon_name_relationships.type LIKE 'TaxonNameRelationship::OriginalCombination::%'")
}, class_name: 'TaxonNameRelationship', foreign_key: :object_taxon_name_id

has_many :type_material

# subject object
# Aus original_genus of bus
# aus type_species of Bus
Expand Down Expand Up @@ -45,7 +47,6 @@ class Protonym < TaxonName
has_one d.inverse_assignment_method.to_sym, through: relationship, source: :subject_taxon_name
end
end

end

scope :named, -> (name) {where(name: name)}
Expand All @@ -62,7 +63,6 @@ class Protonym < TaxonName
scope :with_taxon_name_classification_array, -> (taxon_name_class_name_base_array) { includes(:taxon_name_classifications).where('taxon_name_classifications.type in (?)', taxon_name_class_name_base_array).references(:taxon_name_classifications) }
scope :without_taxon_name_classification, -> (taxon_name_class_name) { where('id not in (SELECT taxon_name_id FROM taxon_name_classifications WHERE type LIKE ?)', "#{taxon_name_class_name}")}
scope :without_taxon_name_classification_array, -> (taxon_name_class_name_array) { where('id not in (SELECT taxon_name_id FROM taxon_name_classifications WHERE type in (?))', taxon_name_class_name_array) }

scope :without_taxon_name_classifications, -> { includes(:taxon_name_classifications).where(taxon_name_classifications: {taxon_name_id: nil}) }

scope :that_is_valid, -> {
Expand All @@ -84,6 +84,50 @@ class Protonym < TaxonName
:validate_source_type,
:new_parent_taxon_name



def list_of_coordinated_names
if self.incorrect_original_spelling.nil?
search_rank = NomenclaturalRank::Iczn.group_base(self.rank_string)
if !!search_rank
if search_rank =~ /Family/
z = Protonym.family_group_base(self.name)
search_name = z.nil? ? nil : NomenclaturalRank::Iczn::FamilyGroup::ENDINGS.collect{|i| z+i}
#search_name = z.nil? ? nil : "#{z}(ini|ina|inae|idae|oidae|odd|ad|oidea)"
else
search_name = self.name
end
else
search_name = nil
end

unless search_name.nil?
list = Protonym.ancestors_and_descendants_of(self).
with_rank_class_including(search_rank).
with_name_in_array(search_name).
as_subject_without_taxon_name_relationship_base('TaxonNameRelationship::Iczn::Invalidating::Synonym') # <- use this
#list1 = self.ancestors_and_descendants # scope with parens
#list1 = list1.select{|i| /#{search_rank}.*/.match(i.rank_class.to_s)} # scope on rank_class
#list1 = list1.select{|i| /#{search_name}/.match(i.name)} # scope on named
#list1 = list1.reject{|i| i.unavailable_or_invalid?} # scope with join on taxon_name_relationships and where > 1 on them
else
list = []
end
else
list = [self.incorrect_original_spelling.object_taxon_name]
end
return list
end

def ancestors_and_descendants
Protonym.ancestors_and_descendants_of(self).to_a
end

def self.family_group_base(name_string)
name_string.match(/(^.*)(ini|ina|inae|idae|oidae|odd|ad|oidea)/)
$1
end

protected

def incorrect_original_spelling
Expand Down Expand Up @@ -221,49 +265,6 @@ def sv_fix_coordinated_names
return fixed
end

def list_of_coordinated_names

if self.incorrect_original_spelling.nil?
search_rank = NomenclaturalRank::Iczn.group_base(self.rank_string)
if !!search_rank
if search_rank =~ /Family/
z = Protonym.family_group_base(self.name)
search_name = z.nil? ? nil : NomenclaturalRank::Iczn::FamilyGroup::ENDINGS.collect{|i| z+i}
#search_name = z.nil? ? nil : "#{z}(ini|ina|inae|idae|oidae|odd|ad|oidea)"
else
search_name = self.name
end
else
search_name = nil
end

unless search_name.nil?
list = Protonym.ancestors_and_descendants_of(self).
with_rank_class_including(search_rank).
with_name_in_array(search_name).
as_subject_without_taxon_name_relationship_base('TaxonNameRelationship::Iczn::Invalidating::Synonym') # <- use this
#list1 = self.ancestors_and_descendants # scope with parens
#list1 = list1.select{|i| /#{search_rank}.*/.match(i.rank_class.to_s)} # scope on rank_class
#list1 = list1.select{|i| /#{search_name}/.match(i.name)} # scope on named
#list1 = list1.reject{|i| i.unavailable_or_invalid?} # scope with join on taxon_name_relationships and where > 1 on them
else
list = []
end
else
list = [self.incorrect_original_spelling.object_taxon_name]
end
return list
end

def ancestors_and_descendants
Protonym.ancestors_and_descendants_of(self).to_a
end

def self.family_group_base(name_string)
name_string.match(/(^.*)(ini|ina|inae|idae|oidae|odd|ad|oidea)/)
$1
end

def sv_type_placement
# type of this taxon is not included in this taxon
if !!self.type_taxon_name
Expand Down
19 changes: 7 additions & 12 deletions app/models/taxon_name.rb
@@ -1,4 +1,3 @@

class TaxonName < ActiveRecord::Base

include Housekeeping
Expand All @@ -9,7 +8,6 @@ class TaxonName < ActiveRecord::Base
acts_as_nested_set scope: [:project_id]

belongs_to :source

has_many :taxon_name_classifications

#relationships as a subject
Expand Down Expand Up @@ -66,12 +64,6 @@ class TaxonName < ActiveRecord::Base
where('tnr1.subject_taxon_name_id IS NULL AND tnr2.object_taxon_name_id IS NULL')
}

soft_validate(:sv_missing_fields, set: :missing_fields)
soft_validate(:sv_parent_is_valid_name, set: :parent_is_valid_name)
soft_validate(:sv_source_older_then_description, set: :source_older_then_description)



validates_presence_of :type, message: 'Type is not specified'
validates_presence_of :rank_class, message: 'Rank is a required field', if: Proc.new { |tn| [Protonym].include?(tn.class)}
validates_presence_of :name, message: 'Name is a required field', if: Proc.new { |tn| [Protonym].include?(tn.class)}
Expand All @@ -83,11 +75,14 @@ class TaxonName < ActiveRecord::Base
:validate_parent_is_set,
:check_new_rank_class,
:check_new_parent_class,
:validate_source_type
:validate_source_type,
:set_cached_name,
:set_cached_author_year,
:set_cached_higher_classification

before_validation :set_cached_name,
:set_cached_author_year,
:set_cached_higher_classification
soft_validate(:sv_missing_fields, set: :missing_fields)
soft_validate(:sv_parent_is_valid_name, set: :parent_is_valid_name)
soft_validate(:sv_source_older_then_description, set: :source_older_then_description)

def all_taxon_name_relationships
# (self.taxon_name_relationships & self.related_taxon_name_relationships)
Expand Down
1 change: 0 additions & 1 deletion app/models/type_designator.rb
@@ -1,4 +1,3 @@
class TypeDesignator < Role::ProjectRole

include Housekeeping
end
40 changes: 40 additions & 0 deletions app/models/type_material.rb
@@ -0,0 +1,40 @@
class TypeMaterial < ActiveRecord::Base
belongs_to :source

include Housekeeping
include Shared::Citable

ICZN_TYPES = {
'holotype' => Specimen,
'paratype' => Specimen,
'neotype' => Specimen,
'lectotype' => Specimen,
'paratypes' => Lot,
'syntypes' => Lot
}

ICN_TYPES = {}

belongs_to :material, foreign_key: :biological_object_id, class_name: 'CollectionObject'
belongs_to :protonym
has_many :type_designator_roles, class_name: 'TypeDesignator', as: :role_object
has_many :type_designators, through: :type_designator_roles, source: :person

validates :protonym, presence: true
validates :material, presence: true
validates_presence_of :type_type

validate :check_type_type

protected

def check_type_type
if self.protonym
code = self.protonym.rank_class.nomenclatural_code
if (code == :iczn && !ICZN_TYPES.keys.include?(self.type_type)) || (code == :icn && !ICN_TYPES.keys.include?(self.type_type))
errors.add(:type_type, 'Not a legal type for the nomenclatural code provided')
end
end
end

end
12 changes: 0 additions & 12 deletions app/models/type_specimen.rb

This file was deleted.

15 changes: 15 additions & 0 deletions db/migrate/20140128221223_create_type_materials.rb
@@ -0,0 +1,15 @@
class CreateTypeMaterials < ActiveRecord::Migration
def change
create_table :type_materials do |t|
t.integer :protonym_id
t.integer :biological_object_id
t.string :type_type
t.references :source, index: true
t.integer :created_by_id
t.integer :updated_by_id
t.integer :project_id

t.timestamps
end
end
end
5 changes: 5 additions & 0 deletions db/migrate/20140128221713_drop_type_specimen_table.rb
@@ -0,0 +1,5 @@
class DropTypeSpecimenTable < ActiveRecord::Migration
def change
drop_table :type_specimens
end
end
10 changes: 10 additions & 0 deletions spec/factories/type_material_factory.rb
@@ -0,0 +1,10 @@
FactoryGirl.define do
factory :type_material, traits: [:housekeeping] do
factory :valid_type_material do
type_type 'holotype'
association :protonym, factory: :iczn_species
association :material, factory: :valid_specimen
end
end
end

9 changes: 0 additions & 9 deletions spec/factories/type_specimen_factory.rb

This file was deleted.

8 changes: 4 additions & 4 deletions spec/models/person_spec.rb
Expand Up @@ -69,7 +69,7 @@
end

specify 'type_designations' do
expect(person).to respond_to(:type_specimens)
expect(person).to respond_to(:type_material)
end
end

Expand Down Expand Up @@ -177,9 +177,9 @@
specify 'is_type_designator?' do
expect(@vp).to respond_to(:is_type_designator?)
expect(@vp.is_type_designator?).to be_false
type_specimen = FactoryGirl.create(:type_specimen)
type_specimen.type_designators << @vp
type_specimen.save!
type_material = FactoryGirl.create(:valid_type_material)
type_material.type_designators << @vp
type_material.save!
@vp.reload
expect(@vp.is_type_designator?).to be_true
end
Expand Down
92 changes: 92 additions & 0 deletions spec/models/type_material_spec.rb
@@ -0,0 +1,92 @@
require 'spec_helper'

describe TypeMaterial do
let(:type_material) { FactoryGirl.build(:type_material) }

context 'associations' do
context 'belongs to' do
specify 'protonym' do
expect(type_material).to respond_to(:protonym)
end
specify 'material' do
expect(type_material).to respond_to(:material)
end
specify 'source' do
expect(type_material).to respond_to(:source)
end
end
end

context 'validations' do
context 'require' do
before(:each) {
type_material.valid?
}
specify 'protonym' do
expect(type_material.errors.include?(:protonym)).to be_true
end

specify 'material' do
expect(type_material.errors.include?(:material)).to be_true
end

specify 'type_type' do
expect(type_material.errors.include?(:type_type)).to be_true
end
end

context 'Protonym restrictions and linkages' do
before(:each) {
@iczn_type = FactoryGirl.build(:type_material, protonym: FactoryGirl.build(:iczn_species))
@icn_type = FactoryGirl.build(:type_material, protonym: FactoryGirl.build(:icn_species))
}

specify 'type_type is one of ICZN_TYPES.keys for ICZN name' do
@iczn_type.type_type = 'foo'
@iczn_type.valid?
expect(@iczn_type.errors.include?(:type_type)).to be_true
expect(@iczn_type.errors.messages[:type_type]).to eq(['Not a legal type for the nomenclatural code provided'])
@iczn_type.type_type = 'holotype'
@iczn_type.valid?
expect(@iczn_type.errors.include?(:type_type)).to be_false
end

specify 'type_type is one of ICN_TYPES.keys for ICN name' do
pending
end
end

context 'Material restrictions' do
before(:each) {
type_material.protonym = FactoryGirl.build(:iczn_species)
}

specify 'type_type restricts the BiologicalObject subclass to an _TYPES.value' do
end

specify 'collection_object is a BiologicalCollectionObject' do
end
end

context 'type_type restrictions' do
pending 'only one of holotype, lectotype, neotype per iczn species'
end

end

context 'methods' do
before(:each) {
iczn_type = FactoryGirl.build(:valid_type_material)
icn_type = FactoryGirl.build(:valid_type_material, protonym: FactoryGirl.build(:icn_species))
}
pending 'Source is the Protonym source when not provided locally.'
pending 'TypeDesignator role(s) should be possible when a specific person needs to be identified as the person who designated the type'
pending 'a source citation can identify (override the source_id in Protonym) where the type designation was made'
end

context 'soft validation' do
pending 'no source provided if self.source.nil && self.protonym.source.nil?'
end

end

0 comments on commit 918f9ad

Please sign in to comment.