module Lexoranking::Model
Allows your models to sort elements using lexographical sorting
Options: self.ranking_scope - Determine if the elements should be scoped to a specific association before sorting.
Public Instance Methods
Ranks the record to the first postiion of the list and saves it.
# File lib/lexoranking/model.rb, line 32 def rank_first rank_to(0) end
Ranks the record to the last position of the list and saves it.
# File lib/lexoranking/model.rb, line 27 def rank_last rank_to(:last) end
Ranks the record to a specific position in the list and saves it.
# File lib/lexoranking/model.rb, line 37 def rank_to(position) model_collection = get_collection # This return statement handle the case of when there is only one # element in the scoped collection and we call this method. Since # there is only one element in the collection and the rank column is # present there is no reason to calculate the ranking value again. return if model_collection.size == 1 && rank.present? position = position == :last ? model_collection.size-1 : position.to_i previous, nextt = get_prev_next_elements(position, model_collection) ranking = calculate_ranking(previous, nextt) send("#{self.class.ranking_column}=", ranking) save end
Rank the record to the last position of the list before saving it.
# File lib/lexoranking/model.rb, line 21 def save rank_to(:last) if rank.nil? super end
Private Instance Methods
# File lib/lexoranking/model.rb, line 111 def _raise_ranking_scope_not_valid raise ::Lexoranking::RankingScopeNotValid.new( "The #{ranking_model_scope} column does not exists in this model #{self.class.name}", self ) end
Calculated the ranking value for the current record(self) based on the previous and nextt elements in the list.
# File lib/lexoranking/model.rb, line 99 def calculate_ranking(prev, nextt) Lexoranking::Main.perform( prev&.send(self.class.ranking_column), nextt&.send(self.class.ranking_column) ) end
Returns the model collection scoped to the ranking scope column in the model
If the ranking scope colunm does not exists in the model or it is not an association it will raise an exception error.
raise {Lexoranking::RankingScopeNotValid}
# File lib/lexoranking/model.rb, line 60 def collection_by_ranking_scope model_scope = ranking_model_scope validate_ranking_scope_column!(model_scope) || _raise_ranking_scope_not_valid self.class.ranked.where("#{model_scope}": send(model_scope)) end
Returns a ranked model collection of elements if the ranking model scope is not present otherwise it will return a models collection scoped to the ranking scope column.
# File lib/lexoranking/model.rb, line 69 def get_collection @get_collection ||= ranking_model_scope.present? ? collection_by_ranking_scope : self.class.ranked end
Returns the previous and next elements where the new element will be position inside the list.
If the position we want to sort the element is position zero, it will return nil for the previous element and the the first element of the list.
If the position is less than the index of the current element(self) that means that we try to move the element from a higher position to a lower position in the list and in this case we decrement the position by 1 so that we keep the zero base logic, otherwise we are trying to move the element from a lower position of the list to a higher position and in this case we keep the position value as it is. We use the position to offset those number of elements in the list and then take the next two. That way we will have the two element where we want to position the record in between.
# File lib/lexoranking/model.rb, line 90 def get_prev_next_elements(position, collection) return [collection[0], nil] if position == 0 && collection.size == 1 return [nil, collection[0]] if position <= 0 position -= 1 if (collection.map(&:id).index(id) || 0) > position collection.offset(position).limit(2) end
Returns the ranking scope column
# File lib/lexoranking/model.rb, line 74 def ranking_model_scope @ranking_model_scope ||= self.class.ranking_scope end
Validates that the ranking scope column exists in the model or is an association for the model.
# File lib/lexoranking/model.rb, line 107 def validate_ranking_scope_column!(model_scope) self.class.columns.map(&:name).include?(model_scope.to_s) || self.class.reflect_on_association(model_scope) end