/usr/lib/ruby/vendor_ruby/sequel/plugins/list.rb is in ruby-sequel 3.36.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | module Sequel
module Plugins
# The list plugin allows for model instances to be part of an ordered list,
# based on a position field in the database. It can either consider all
# rows in the table as being from the same list, or you can specify scopes
# so that multiple lists can be kept in the same table.
#
# Basic Example:
#
# class Item < Sequel::Model(:items)
# plugin :list # will use :position field for position
# plugin :list, :field=>:pos # will use :pos field for position
# end
#
# item = Item[1]
#
# # Get the next or previous item in the list
#
# item.next
# item.prev
#
# # Modify the item's position, which may require modifying other items in
# # the same list
#
# item.move_to(3)
# item.move_to_top
# item.move_to_bottom
# item.move_up
# item.move_down
#
# You can provide a <tt>:scope</tt> option to scope the list. This option
# can be a symbol or array of symbols specifying column name(s), or a proc
# that accepts a model instance and returns a dataset representing the list
# the object is in.
#
# For example, if each item has a +user_id+ field, and you want every user
# to have their own list:
#
# Item.plugin :list, :scope=>:user_id
#
# Note that using this plugin modifies the order of the model's dataset to
# sort by the position and scope fields. Also note that this plugin is subject to
# race conditions, and is not safe when concurrent modifications are made
# to the same list.
#
# Additionally, note that unlike ruby arrays, the list plugin assumes that the
# first entry in the list has position 1, not position 0.
#
# Copyright (c) 2007-2010 Sharon Rosner, Wayne E. Seguin, Aman Gupta, Adrian Madrid, Jeremy Evans
module List
# Set the +position_field+ and +scope_proc+ attributes for the model,
# using the <tt>:field</tt> and <tt>:scope</tt> options, respectively.
# The <tt>:scope</tt> option can be a symbol, array of symbols, or a proc that
# accepts a model instance and returns a dataset representing the list.
# Also, modify the model dataset's order to order by the position and scope fields.
def self.configure(model, opts = {})
model.position_field = opts[:field] || :position
model.dataset = model.dataset.order_prepend(model.position_field)
model.scope_proc = case scope = opts[:scope]
when Symbol
model.dataset = model.dataset.order_prepend(scope)
proc{|obj| obj.model.filter(scope=>obj.send(scope))}
when Array
model.dataset = model.dataset.order_prepend(*scope)
proc{|obj| obj.model.filter(scope.map{|s| [s, obj.send(s)]})}
else
scope
end
end
module ClassMethods
# The column name holding the position in the list, as a symbol.
attr_accessor :position_field
# A proc that scopes the dataset, so that there can be multiple positions
# in the list, but the positions are unique with the scoped dataset. This
# proc should accept an instance and return a dataset representing the list.
attr_accessor :scope_proc
# Copy the +position_field+ and +scope_proc+ to the subclass.
def inherited(subclass)
super
subclass.position_field = position_field
subclass.scope_proc = scope_proc
end
end
module InstanceMethods
# The model object at the given position in the list containing this instance.
def at_position(p)
list_dataset.first(position_field => p)
end
# Set the value of the position_field to the maximum value plus 1 unless the
# position field already has a value.
def before_create
unless send(position_field)
send("#{position_field}=", list_dataset.max(position_field).to_i+1)
end
end
# Find the last position in the list containing this instance.
def last_position
list_dataset.max(position_field).to_i
end
# A dataset that represents the list containing this instance.
def list_dataset
model.scope_proc ? model.scope_proc.call(self) : model.dataset
end
# Move this instance down the given number of places in the list,
# or 1 place if no argument is specified.
def move_down(n = 1)
move_to(position_value + n)
end
# Move this instance to the given place in the list. Raises an
# exception if target is less than 1 or greater than the last position in the list.
def move_to(target, lp = nil)
current = position_value
if target != current
checked_transaction do
ds = list_dataset
op, ds = if target < current
raise(Sequel::Error, "Moving too far up (target = #{target})") if target < 1
[:+, ds.filter(position_field=>target...current)]
else
lp ||= last_position
raise(Sequel::Error, "Moving too far down (target = #{target}, last_position = #{lp})") if target > lp
[:-, ds.filter(position_field=>(current + 1)..target)]
end
ds.update(position_field => Sequel::SQL::NumericExpression.new(op, position_field, 1))
update(position_field => target)
end
end
self
end
# Move this instance to the bottom (last position) of the list.
def move_to_bottom
lp = last_position
move_to(lp, lp)
end
# Move this instance to the top (first position, position 1) of the list.
def move_to_top
move_to(1)
end
# Move this instance the given number of places up in the list, or 1 place
# if no argument is specified.
def move_up(n = 1)
move_to(position_value - n)
end
# The model instance the given number of places below this model instance
# in the list, or 1 place below if no argument is given.
def next(n = 1)
n == 0 ? self : at_position(position_value + n)
end
# The value of the model's position field for this instance.
def position_value
send(position_field)
end
# The model instance the given number of places below this model instance
# in the list, or 1 place below if no argument is given.
def prev(n = 1)
self.next(n * -1)
end
private
# The model's position field, an instance method for ease of use.
def position_field
model.position_field
end
end
end
end
end
|