Module: ApplicationEnumeration
- Defined in:
- lib/application_enumeration.rb
Overview
Methods for enumerating models, tables, columns etc.
!! If you think that a method belongs here chances are it already exists in a Rails extension.
Note the use of Module.nesting (urbanautomaton.com/blog/2013/08/27/rails-autoloading-hell/)
Constant Summary collapse
- EXCLUDE_RELATIONS_FOR_RELATED_DATA =
TODO: DRY with Unify::EXCLUDE_RELATIONS
[ :versions, :dwc_occurrence, :pinboard_items, :cached_map_register, :cached_map_items, :cached_maps ].freeze
Class Method Summary collapse
-
.all_submodels(klass) ⇒ Array of Classes
!! See the built in self.descendants for actual inheritance tracking, this is path based.
-
.alternate_value_attributes(object) ⇒ Array
A list symbols that represent populated, non “cached”, non “_id”, non reserved attributes.
-
.annotatable_attributes(object) ⇒ Array of Symbols
!! Some models have blacklists (e.g. Serial).
-
.attributes(target) ⇒ Array of Symbol
A list attributes except “id”, ‘md5_“, and postfixed ”_id“, ”_at“ This is an arbitrary convention, wrap this to further refine.
- .citable_relations(klass, relationship_type = :all) ⇒ Object
-
.community_data_classes ⇒ Array
All superclass models that are community/shared.
-
.community_models ⇒ Array
All superclass data models.
-
.data_models ⇒ Array
All superclass data models.
-
.filter_sti_relations(klass, relation_names) ⇒ Hash
Filter out STI subclass relations that are redundant with their parent class (e.g., ‘protonym` belongs_to duplicates `taxon_name` belongs_to).
-
.klass_reflections(klass, relationship_type = :all) ⇒ Object
Array of AR associations.
-
.model_from_file_name(file_name) ⇒ Class
e.g.
- .nested_subclasses(parent = self) ⇒ Hash
-
.no_related_data?(object, ignore: []) ⇒ Boolean
True if object has no data in any has_many or has_one associations Excludes ‘through` associations and cached/computed relations.
-
.non_project_data_classes ⇒ Array of Classes
Data models that do not have a project_id attribute.
-
.project_data_classes ⇒ Array of Classes
All models with a project_id attribute.
- .relation_targets_community?(relation) ⇒ Boolean
-
.relationship_type(relation) ⇒ Object
collection?, has_mone? belongs_to?.
-
.superclass_models ⇒ Array
All models that inherit directly from ApplicationRecord.
Class Method Details
.all_submodels(klass) ⇒ Array of Classes
!! See the built in self.descendants for actual inheritance tracking, this is path based. Used in Ranks.
82 83 84 |
# File 'lib/application_enumeration.rb', line 82 def self.all_submodels(klass) Dir.glob(Rails.root + "app/models/#{klass.name.underscore}/**/*.rb").collect{|a| self.model_from_file_name(a) } end |
.alternate_value_attributes(object) ⇒ Array
Returns a list symbols that represent populated, non “cached”, non “_id”, non reserved attributes.
25 26 27 28 29 30 31 |
# File 'lib/application_enumeration.rb', line 25 def self.alternate_value_attributes(object) if object.class::ALTERNATE_VALUES_FOR.blank? raise("#{object.class} attempted to annotate a class without ALTERNATE_VALUES_FOR - please inform the programmers") else object.attributes.select{|k,v| v.present? && object.class::ALTERNATE_VALUES_FOR.include?(k.to_sym)}.keys.map(&:to_sym) end end |
.annotatable_attributes(object) ⇒ Array of Symbols
!! Some models have blacklists (e.g. Serial)
37 38 39 |
# File 'lib/application_enumeration.rb', line 37 def self.annotatable_attributes(object) object.attributes.select{|k,v| v.present? && !(k =~ /.*_id\z|cached_*.*/)}.keys.map(&:to_sym) - ( RESERVED_ATTRIBUTES - [:parent_id]) end |
.attributes(target) ⇒ Array of Symbol
Returns a list attributes except “id”, ‘md5_“, and postfixed ”_id“, ”_at“ This is an arbitrary convention, wrap this to further refine.
19 20 21 |
# File 'lib/application_enumeration.rb', line 19 def self.attributes(target) target.attributes.select{|k,v| !(k =~ /\Amd5_|_id\z|\Aid\z|_at\z/)}.symbolize_keys.keys.sort end |
.citable_relations(klass, relationship_type = :all) ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/application_enumeration.rb', line 145 def self.citable_relations(klass, relationship_type = :all) types = relationship_type == :all ? [:has_many, :has_one, :belongs_to] : [relationship_type] h = {} types.each do |t| h[t] = klass.reflect_on_all_associations(t).filter_map do |r| if r.klass.ancestors.include?(Shared::Citations) if t == :has_many r.plural_name.to_sym else r.name.to_sym end else nil end end end h end |
.community_data_classes ⇒ Array
Returns all superclass models that are community/shared.
61 62 63 |
# File 'lib/application_enumeration.rb', line 61 def self.community_data_classes superclass_models.select{|a| a < Shared::SharedAcrossProjects } end |
.community_models ⇒ Array
Returns all superclass data models.
73 74 75 |
# File 'lib/application_enumeration.rb', line 73 def self.community_models superclass_models.select{|a| a < Shared::SharedAcrossProjects} end |
.data_models ⇒ Array
Returns all superclass data models.
67 68 69 |
# File 'lib/application_enumeration.rb', line 67 def self.data_models superclass_models.select{|a| a < Shared::IsData} end |
.filter_sti_relations(klass, relation_names) ⇒ Hash
Filter out STI subclass relations that are redundant with their parent class (e.g., ‘protonym` belongs_to duplicates `taxon_name` belongs_to). A subclass relation is only dropped when the parent association has no scope
-
an unscoped parent always covers all records the subclass relation would
return.
!! There’s no general way to compare scoped STI relations, so this method does not filter out STI relations when the parent relation is scoped. !! TODO: maybe we return a separate list of those if any occur.
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/application_enumeration.rb', line 219 def self.filter_sti_relations(klass, relation_names) reflections = relation_names.map { |name| [name, klass.reflect_on_association(name)] }.to_h filtered_names = relation_names.reject do |relation_name| reflection = reflections[relation_name] next false if reflection.nil? || reflection.[:polymorphic] # Drop this relation if its class is a subclass of an unscoped parent relation, # or if it has the same class but is scoped while the other is not. reflections.any? do |other_name, other_reflection| next false if relation_name == other_name next false if other_reflection.nil? || other_reflection.[:polymorphic] next false unless other_reflection.scope.nil? reflection.klass < other_reflection.klass || (reflection.klass == other_reflection.klass && reflection.scope.present?) end end reflections.slice(*filtered_names) end |
.klass_reflections(klass, relationship_type = :all) ⇒ Object
Returns Array of AR associations.
103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/application_enumeration.rb', line 103 def self.klass_reflections(klass, relationship_type = :all) relationship_types = relationship_type == :all ? [:belongs_to, :has_one, :has_many] : [relationship_type] a = [] relationship_types.each do |t| a += klass.reflect_on_all_associations(t).sort{ |a, b| a.name <=> b.name } end a end |
.model_from_file_name(file_name) ⇒ Class
e.g. given ‘app/models/specimen.rb’ the Specimen class is returned
90 91 92 |
# File 'lib/application_enumeration.rb', line 90 def self.model_from_file_name(file_name) file_name.split(/app\/models\//).last[0..-4].split(/\\/).collect{|b| b.camelize}.join('::').safe_constantize end |
.nested_subclasses(parent = self) ⇒ Hash
96 97 98 99 100 |
# File 'lib/application_enumeration.rb', line 96 def self.nested_subclasses(parent = self) parent.subclasses.inject({}) { | hsh, subclass | hsh.merge!(subclass.name => nested_subclasses(subclass)) } end |
.no_related_data?(object, ignore: []) ⇒ Boolean
Returns true if object has no data in any has_many or has_one associations Excludes ‘through` associations and cached/computed relations.
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/application_enumeration.rb', line 183 def self.(object, ignore: []) excluded = EXCLUDE_RELATIONS_FOR_RELATED_DATA + ignore (klass_reflections(object.class, :has_many) + klass_reflections(object.class, :has_one)).each do |relation| next if relation.[:through].present? next if excluded.include?(relation.name) next if relation.[:foreign_key]&.match?(/cache/) = object.send(relation.name) has_data = if relation.collection? .any? else .present? end return false if has_data end true end |
.non_project_data_classes ⇒ Array of Classes
Returns data models that do not have a project_id attribute.
55 56 57 |
# File 'lib/application_enumeration.rb', line 55 def self.non_project_data_classes data_models - project_data_classes end |
.project_data_classes ⇒ Array of Classes
Returns all models with a project_id attribute.
49 50 51 |
# File 'lib/application_enumeration.rb', line 49 def self.project_data_classes superclass_models.select{|a| a.column_names.include?('project_id') } end |
.relation_targets_community?(relation) ⇒ Boolean
117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/application_enumeration.rb', line 117 def self.relation_targets_community?(relation) case relationship_type(relation) when :has_many relation.class_name.safe_constantize.is_community? when :has_one raise TaxonWorks::Error, "Has one support not implemented in unify, throw eggs at the devs." when :belongs_to if k = relation.[:class_name] k.safe_constantize.is_community? else raise TaxonWorks::Error, "Missing attribute class_name on #{relation.name}." end end end |
.relationship_type(relation) ⇒ Object
collection?, has_mone? belongs_to?
133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/application_enumeration.rb', line 133 def self.relationship_type(relation) if relation.collection? # class.name.match('HasMany') return :has_many elsif relation.has_one? # class.name.match('HasOne') return :has_one elsif relation.belongs_to? # class.name.match('BelongsTo') return :belongs_to end raise TaxonWorks::Error, "Unknown relationship type for #{relation.name}." end |
.superclass_models ⇒ Array
Returns all models that inherit directly from ApplicationRecord.
43 44 45 |
# File 'lib/application_enumeration.rb', line 43 def self.superclass_models ApplicationRecord.descendants.select{|a| a.superclass == ApplicationRecord } end |