module ActiveSorting::Model::ClassMethods
Patches ActiveRecord models
Public Instance Methods
active_sorting_calculate_changes(old_list, new_list, changes = [])
click to toggle source
Calculate the possible changes required to reorder items in old_list
to match new_list
order
# File lib/active_sorting/model.rb, line 91 def active_sorting_calculate_changes(old_list, new_list, changes = []) new_list.each_with_index do |id, index| next unless old_list[index] != id # This item has changed changes << id # Remove it from both lists, rinse and repeat new_list.delete(id) old_list.delete(id) # Recur... active_sorting_calculate_changes(old_list, new_list, changes) break end changes end
active_sorting_changes_required(old_list, new_list)
click to toggle source
Calculate the least possible changes required to reorder items in old_list
to match new_list
order by comparing two proposals from active_sorting_calculate_changes
# File lib/active_sorting/model.rb, line 76 def active_sorting_changes_required(old_list, new_list) raise Exceptions::InvalidListSize, "Sortable new and old lists should be of the same length" if old_list.count != new_list.count changes = [] proposal1 = active_sorting_calculate_changes(old_list.dup, new_list.dup) if proposal1.count >= (new_list.count / 4) proposal2 = active_sorting_calculate_changes(old_list.dup.reverse, new_list.dup.reverse) changes = proposal1.count < proposal2.count ? proposal1 : proposal2 else changes = proposal1 end changes end
active_sorting_check_options()
click to toggle source
Check provided options
# File lib/active_sorting/model.rb, line 52 def active_sorting_check_options # TODO: columns_hash breaks when database has no tables # field_type = columns_hash[active_sorting_field.to_s].type # unless field_type == :integer # raise ArgumentError, "Sortable field should be of type Integer, #{field_type} where given" # end unless active_sorting_step.is_a?(Fixnum) raise ArgumentError, "Sortable step should be of type Fixnum, #{active_sorting_step.class.name} where given" end unless active_sorting_scope.respond_to?(:each) raise ArgumentError, "Sortable step should be of type Enumerable, #{active_sorting_scope.class.name} where given" end end
active_sorting_default_options()
click to toggle source
Default sorting options
# File lib/active_sorting/model.rb, line 43 def active_sorting_default_options { order: :asc, step: 500, scope: [] } end
active_sorting_default_scope()
click to toggle source
# File lib/active_sorting/model.rb, line 66 def active_sorting_default_scope conditions = {} conditions[active_sorting_field] = active_sorting_order order(conditions) end
active_sorting_field()
click to toggle source
# File lib/active_sorting/model.rb, line 130 def active_sorting_field active_sorting_options[:name] end
active_sorting_find_by(id_column, value)
click to toggle source
# File lib/active_sorting/model.rb, line 146 def active_sorting_find_by(id_column, value) conditions = {} conditions[id_column] = value find_by(conditions) end
active_sorting_make_changes(old_list, new_list, changes, id_column)
click to toggle source
Commit changes to database
# File lib/active_sorting/model.rb, line 107 def active_sorting_make_changes(old_list, new_list, changes, id_column) new_list.each_with_index do |id, index| next unless changes.include?(id) if index == new_list.count.pred # We're moving an item to last position, # increase the count of last item's position # by the step n1 = active_sorting_find_by(id_column, new_list[index.pred]).active_sorting_value n2 = n1 + active_sorting_step elsif index == 0 # We're moving an item to first position # Calculate the gap between following 2 items n1 = 0 n2 = active_sorting_find_by(id_column, old_list[index]).active_sorting_value else # We're moving a non-terminal item n1 = active_sorting_find_by(id_column, new_list[index.pred]).active_sorting_value n2 = active_sorting_find_by(id_column, new_list[index.next]).active_sorting_value end active_sorting_find_by(id_column, id).active_sorting_center_item(n1, n2) end end
active_sorting_order()
click to toggle source
# File lib/active_sorting/model.rb, line 138 def active_sorting_order active_sorting_options[:order] end
active_sorting_scope()
click to toggle source
# File lib/active_sorting/model.rb, line 142 def active_sorting_scope active_sorting_options[:scope] end
active_sorting_step()
click to toggle source
# File lib/active_sorting/model.rb, line 134 def active_sorting_step active_sorting_options[:step] end
sort_list(new_list, id_column = :id)
click to toggle source
Sorts and updates the database with the given list of items in the given order.
new_list
List of ids of records in the desired order id_column
the field used for fetching records from the databse,
defaults to :id
# File lib/active_sorting/model.rb, line 33 def sort_list(new_list, id_column = :id) raise ArgumentError, "Sortable list should not be empty" if new_list.empty? conditions = { id_column => new_list } old_list = unscoped.active_sorting_default_scope.where(conditions).pluck(id_column) raise Exceptions::RecordsNotFound, "Sortable list should be persisted to database with #{name} Model" if old_list.empty? changes = active_sorting_changes_required(old_list, new_list) active_sorting_make_changes(old_list, new_list, changes, id_column) end
sortable(name, opts = {})
click to toggle source
Sets the sortable options
name
sortable field name Accepts a Hash of options: order
sorting direction, defaults to :asc step
stepping value, defaults to 500 scope
scope field name, defaults to []
# File lib/active_sorting/model.rb, line 18 def sortable(name, opts = {}) self.active_sorting_options = active_sorting_default_options.merge(opts) active_sorting_options[:name] = name active_sorting_check_options validates active_sorting_options[:name], presence: true default_scope { active_sorting_default_scope } before_validation :active_sorting_callback_before_validation end