in_order 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +759 -0
- data/Rakefile +32 -0
- data/app/assets/config/in_order_manifest.js +2 -0
- data/app/assets/javascripts/in_order/application.js +15 -0
- data/app/assets/stylesheets/in_order/application.css +15 -0
- data/app/controllers/in_order/application_controller.rb +5 -0
- data/app/controllers/in_order/concerns/response_helpers.rb +33 -0
- data/app/controllers/in_order/elements_controller.rb +47 -0
- data/app/controllers/in_order/lists_controller.rb +86 -0
- data/app/helpers/in_order/application_helper.rb +4 -0
- data/app/jobs/in_order/application_job.rb +4 -0
- data/app/mailers/in_order/application_mailer.rb +6 -0
- data/app/models/in_order/add.rb +51 -0
- data/app/models/in_order/application_record.rb +5 -0
- data/app/models/in_order/aux/create_element.rb +17 -0
- data/app/models/in_order/aux/element_iterator.rb +39 -0
- data/app/models/in_order/aux/get_element.rb +17 -0
- data/app/models/in_order/aux/get_keys.rb +27 -0
- data/app/models/in_order/aux/keys.rb +58 -0
- data/app/models/in_order/aux/poly_find.rb +24 -0
- data/app/models/in_order/aux/poly_key.rb +117 -0
- data/app/models/in_order/aux/position_base.rb +25 -0
- data/app/models/in_order/aux/repair.rb +71 -0
- data/app/models/in_order/aux/sort_elements.rb +32 -0
- data/app/models/in_order/aux/var_keys.rb +20 -0
- data/app/models/in_order/create.rb +62 -0
- data/app/models/in_order/element.rb +119 -0
- data/app/models/in_order/fetch.rb +33 -0
- data/app/models/in_order/insert.rb +42 -0
- data/app/models/in_order/move.rb +15 -0
- data/app/models/in_order/purge.rb +71 -0
- data/app/models/in_order/queue.rb +65 -0
- data/app/models/in_order/remove.rb +29 -0
- data/app/models/in_order/stack.rb +12 -0
- data/app/models/in_order/trim.rb +74 -0
- data/app/models/in_order/update.rb +83 -0
- data/app/models/in_order.rb +9 -0
- data/app/views/in_order/lists/_list.html.erb +16 -0
- data/app/views/in_order/lists/index.html.erb +9 -0
- data/app/views/layouts/in_order/application.html.erb +16 -0
- data/config/routes.rb +10 -0
- data/db/migrate/20190814101433_create_in_order_elements.rb +15 -0
- data/lib/in_order/engine.rb +5 -0
- data/lib/in_order/version.rb +3 -0
- data/lib/in_order.rb +5 -0
- data/lib/tasks/in_order_tasks.rake +4 -0
- metadata +119 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
|
2
|
+
module InOrder
|
3
|
+
module Aux
|
4
|
+
class Repair
|
5
|
+
attr_accessor :dry_run, :owners, :elements
|
6
|
+
alias dry_run? dry_run
|
7
|
+
|
8
|
+
def initialize(_dry_run=false, dry_run: _dry_run)
|
9
|
+
self.dry_run = dry_run
|
10
|
+
|
11
|
+
self.owners = []
|
12
|
+
self.elements = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
InOrder::Element.transaction do
|
17
|
+
unreferenced :owner do |type_id|
|
18
|
+
owners << type_id * '-'
|
19
|
+
|
20
|
+
InOrder::Element.by_owner_keys(*type_id).delete_all unless dry_run?
|
21
|
+
end
|
22
|
+
|
23
|
+
unlinked :subject do |element|
|
24
|
+
elements << element
|
25
|
+
|
26
|
+
InOrder::Remove.new(element).call unless dry_run?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
return owners, elements
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
def ic
|
35
|
+
owners, elements = new(true).call
|
36
|
+
|
37
|
+
puts show 'Owners', owners
|
38
|
+
puts show 'Elements', elements
|
39
|
+
end
|
40
|
+
|
41
|
+
def show(name, list)
|
42
|
+
out = name << ' ' << list.size.to_s << "\n"
|
43
|
+
|
44
|
+
out << list.map(&:to_param) * " " << "\n"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def unlinked(name)
|
49
|
+
unreferenced(name).map do |type_id|
|
50
|
+
InOrder::Element.by_subject_keys(*type_id).first.tap do |element|
|
51
|
+
yield element if block_given?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def unreferenced(name)
|
57
|
+
linked(name).reject do |(type, id)|
|
58
|
+
type.constantize.where(id: id).exists?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def linked(name)
|
63
|
+
fields = [ :"#{name}_type", :"#{name}_id" ]
|
64
|
+
|
65
|
+
InOrder::Element.select(*fields).distinct.pluck(*fields)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
module InOrder
|
3
|
+
module Aux
|
4
|
+
module SortElements
|
5
|
+
module_function \
|
6
|
+
def sort_elements(elements)
|
7
|
+
index = elements.size
|
8
|
+
|
9
|
+
sorted = Array.new(index)
|
10
|
+
|
11
|
+
element_id = nil
|
12
|
+
|
13
|
+
while index > 0
|
14
|
+
index -= 1
|
15
|
+
|
16
|
+
element = elements.find do |element|
|
17
|
+
element.element_id == element_id
|
18
|
+
end
|
19
|
+
|
20
|
+
if element
|
21
|
+
sorted[index] = element
|
22
|
+
|
23
|
+
element_id = element.id
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
sorted
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
module InOrder
|
3
|
+
module Aux
|
4
|
+
module VarKeys
|
5
|
+
include GetKeys
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.instance_eval { attr_accessor :keys }
|
9
|
+
end
|
10
|
+
|
11
|
+
# +keys+ InOrder::Keys or an Array, used as constructor args to former
|
12
|
+
def initialize(*keys)
|
13
|
+
super()
|
14
|
+
|
15
|
+
self.keys = get_keys(keys)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
module InOrder
|
3
|
+
# Creates a list of elements for a given Owner and/or Scope,
|
4
|
+
# which make up the key.
|
5
|
+
# This list links a number of arbitrary *ActiveRecords* together,
|
6
|
+
# retaining the given order (sequence).
|
7
|
+
# Owner and Record are any ActiveRecord model (linked polymorphically).
|
8
|
+
# The Scope is a string with an application-dependent value.
|
9
|
+
# At least one of Owner and Scope must be given.
|
10
|
+
# If a list of elememts with the same Owner/Scope combo already exists,
|
11
|
+
# then the new elements will be appended to them,
|
12
|
+
# unless the 'append' parameter is given as *false*,
|
13
|
+
# in which case, they'll be prepended.
|
14
|
+
class Create
|
15
|
+
include Aux::VarKeys
|
16
|
+
include Aux::CreateElement
|
17
|
+
|
18
|
+
# +models+ are an Araay of ActiveRecord models to be linked
|
19
|
+
def call(models, append: true)
|
20
|
+
InOrder::Element.transaction do
|
21
|
+
if append
|
22
|
+
previous_last_element = InOrder::Element.last_element(keys)
|
23
|
+
else
|
24
|
+
previous_first_element = InOrder::Element.first_element(keys)
|
25
|
+
end
|
26
|
+
|
27
|
+
add_elements(models.dup).tap do |created_elements|
|
28
|
+
if created_elements.any?
|
29
|
+
if previous_first_element
|
30
|
+
InOrder::Element.link_elements created_elements.last,
|
31
|
+
previous_first_element
|
32
|
+
elsif previous_last_element
|
33
|
+
InOrder::Element.link_elements previous_last_element,
|
34
|
+
created_elements.first
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
alias elements call
|
41
|
+
|
42
|
+
def subjects(*args)
|
43
|
+
call(*args).map &:subject
|
44
|
+
end
|
45
|
+
alias records subjects
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def add_elements(models, created=[], element_id=nil)
|
50
|
+
if record = models&.pop
|
51
|
+
element = create_element(record, keys, element_id)
|
52
|
+
|
53
|
+
add_elements models, created, element.id
|
54
|
+
|
55
|
+
created << element
|
56
|
+
else
|
57
|
+
created
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,119 @@
|
|
1
|
+
|
2
|
+
module InOrder
|
3
|
+
# An element of a persisted linked-list
|
4
|
+
class Element < ApplicationRecord
|
5
|
+
extend Aux::GetKeys
|
6
|
+
|
7
|
+
self.table_name_prefix = 'in_order_'
|
8
|
+
|
9
|
+
belongs_to :owner, optional: true, polymorphic: true
|
10
|
+
|
11
|
+
belongs_to :subject, polymorphic: true
|
12
|
+
|
13
|
+
belongs_to :element, optional: true
|
14
|
+
|
15
|
+
scope :by_scope, -> (scope=nil) { where scope: scope }
|
16
|
+
|
17
|
+
scope :by_owner, -> owner { where owner: owner }
|
18
|
+
|
19
|
+
scope :by_owner_keys, -> (type, id) { where owner_type: type, owner_id: id }
|
20
|
+
|
21
|
+
scope :by_subject, -> subject { where subject: subject }
|
22
|
+
|
23
|
+
scope :by_subject_keys, -> (type, id) { where subject_type: type, subject_id: id }
|
24
|
+
|
25
|
+
scope :by_keys, -> keys { by_scope(keys.scope).by_owner(keys.owner) }
|
26
|
+
|
27
|
+
scope :by_element, -> element { where element_id: element.to_param }
|
28
|
+
|
29
|
+
scope :include_all, -> { includes :subject, :owner }
|
30
|
+
|
31
|
+
scope :fetch, -> keys { by_keys(keys).include_all }
|
32
|
+
|
33
|
+
scope :null_element, -> { where element_id: nil }
|
34
|
+
|
35
|
+
validate :validate_has_key_fields_valued
|
36
|
+
|
37
|
+
alias call subject
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
call.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def as_json(*)
|
44
|
+
super except: %i(created_at updated_at),
|
45
|
+
#include: :subject,
|
46
|
+
methods: :value
|
47
|
+
end
|
48
|
+
def value
|
49
|
+
subject.as_json
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_keys
|
53
|
+
Aux::Keys.new(owner, scope)
|
54
|
+
end
|
55
|
+
|
56
|
+
def iterator
|
57
|
+
Aux::ElementIterator.new(to_keys)
|
58
|
+
end
|
59
|
+
|
60
|
+
class << self
|
61
|
+
def find_with_keys(*keys)
|
62
|
+
keys = get_keys(keys)
|
63
|
+
|
64
|
+
by_owner_keys(*keys.owner_key.to_a).by_scope(keys.scope)
|
65
|
+
end
|
66
|
+
alias find_with_key find_with_keys
|
67
|
+
|
68
|
+
def fetch_with_keys(*keys)
|
69
|
+
find_with_keys(keys).include_all
|
70
|
+
end
|
71
|
+
alias fetch_with_key fetch_with_keys
|
72
|
+
|
73
|
+
def delete_elements(*keys)
|
74
|
+
transaction do
|
75
|
+
by_keys(get_keys keys).map {|element| element.destroy }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def delete_list(*keys)
|
80
|
+
by_keys(get_keys keys).delete_all
|
81
|
+
end
|
82
|
+
|
83
|
+
def first_element(*keys)
|
84
|
+
keyed = by_keys(get_keys keys)
|
85
|
+
|
86
|
+
if keyed.exists?
|
87
|
+
keyed.where.not(id: keyed.pluck(:element_id)).first
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def last_element(*keys)
|
92
|
+
by_keys(get_keys keys).null_element.first
|
93
|
+
end
|
94
|
+
|
95
|
+
def for_subject(*keys)
|
96
|
+
poly_key = Aux::PolyKey.new(yield)
|
97
|
+
|
98
|
+
by_keys(get_keys keys).by_subject_keys(*poly_key.to_a)
|
99
|
+
end
|
100
|
+
|
101
|
+
def has_subject?(*keys, &block)
|
102
|
+
for_subject(keys, &block).exists?
|
103
|
+
end
|
104
|
+
|
105
|
+
def link_elements(end_of_former, beginning_of_latter)
|
106
|
+
end_of_former.update_attribute :element, beginning_of_latter
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def validate_has_key_fields_valued
|
113
|
+
unless scope.present? or owner.present?
|
114
|
+
errors[:base] << "Cannot leave both scope and owner blank"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
module InOrder
|
3
|
+
# Returns a list of elements in order, given a key of Owner and/or Scope.
|
4
|
+
class Fetch
|
5
|
+
include Aux::VarKeys
|
6
|
+
|
7
|
+
include Aux::SortElements
|
8
|
+
|
9
|
+
def call
|
10
|
+
elements.map &:subject
|
11
|
+
end
|
12
|
+
|
13
|
+
def elements
|
14
|
+
sort_elements fetch
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch
|
18
|
+
InOrder::Element.fetch_with_key(keys).to_a
|
19
|
+
end
|
20
|
+
|
21
|
+
def repair
|
22
|
+
InOrder::Element.transaction do
|
23
|
+
fetch.each do |element|
|
24
|
+
if element.subject.nil?
|
25
|
+
Remove.new(element).call
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
module InOrder
|
3
|
+
# Puts a new element anywhere in a pre-exising list.
|
4
|
+
class Insert < Aux::PositionBase
|
5
|
+
extend Aux::CreateElement
|
6
|
+
extend Aux::GetElement
|
7
|
+
|
8
|
+
def call
|
9
|
+
InOrder::Element.transaction do
|
10
|
+
if marker
|
11
|
+
if after?
|
12
|
+
target.update element_id: marker.element_id
|
13
|
+
|
14
|
+
marker.update element_id: target.id
|
15
|
+
else
|
16
|
+
if previous = InOrder::Element.find_by(element_id: marker.id)
|
17
|
+
previous.update element_id: target.id
|
18
|
+
end
|
19
|
+
|
20
|
+
target.update element_id: marker.id
|
21
|
+
end
|
22
|
+
end
|
23
|
+
target
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.call(record, marker, adjacency, keys: nil)
|
28
|
+
keys = InOrder::Aux::Keys.new(*keys) if Array === keys
|
29
|
+
|
30
|
+
unless ActiveRecord::Base === record
|
31
|
+
record = InOrder::Aux::PolyFind.new(record).call
|
32
|
+
end
|
33
|
+
|
34
|
+
marker = get_element(marker)
|
35
|
+
|
36
|
+
element = create_element(record, keys || marker.to_keys)
|
37
|
+
|
38
|
+
new(element, marker, adjacency).call
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
module InOrder
|
3
|
+
# Repositions an existing list element.
|
4
|
+
# Can be used in drag'n'drop lists.
|
5
|
+
class Move < Aux::PositionBase
|
6
|
+
def call
|
7
|
+
InOrder::Element.transaction do
|
8
|
+
Remove.new(target, destroy: false).call
|
9
|
+
|
10
|
+
Insert.new(target, marker, adjacency).call
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
|
2
|
+
module InOrder
|
3
|
+
# Removes unwanted subjects from a list.
|
4
|
+
# Identified (i.e. keyed) by an Owner and/or Scope.
|
5
|
+
class Purge
|
6
|
+
include Aux::VarKeys
|
7
|
+
|
8
|
+
# +keep_last+ determines whether the first or last occurrence remains
|
9
|
+
def call(keep_last: false)
|
10
|
+
elements = get_elements
|
11
|
+
|
12
|
+
elements.reverse! if keep_last
|
13
|
+
|
14
|
+
traverse elements do |element, found_subjects, subject|
|
15
|
+
Remove.new(element).call if found_subjects.include?(subject)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
alias uniq call
|
19
|
+
|
20
|
+
def remove(*subjects, elements: get_elements)
|
21
|
+
did_remove = false
|
22
|
+
|
23
|
+
subjects.flatten! if Array === subjects.first
|
24
|
+
|
25
|
+
traverse elements do |element|
|
26
|
+
if subjects.include?(element.subject)
|
27
|
+
Remove.new(element).call
|
28
|
+
|
29
|
+
did_remove = true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
did_remove
|
34
|
+
end
|
35
|
+
|
36
|
+
def exists?(subject)
|
37
|
+
InOrder::Element.has_subject?(keys) { subject }
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.delete_by_subject(subject)
|
41
|
+
InOrder::Element.transaction do
|
42
|
+
InOrder::Element.by_subject(subject).each do |element|
|
43
|
+
Remove.new(element).call
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def traverse(elements)
|
51
|
+
found_subjects = []
|
52
|
+
|
53
|
+
InOrder::Element.transaction do
|
54
|
+
elements.each do |element|
|
55
|
+
subject = element.subject
|
56
|
+
|
57
|
+
yield element, found_subjects, subject
|
58
|
+
|
59
|
+
found_subjects << subject
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def get_elements
|
67
|
+
Fetch.new(keys).elements
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
# A first in, last out queue.
|
3
|
+
module InOrder
|
4
|
+
class Queue
|
5
|
+
include Aux::VarKeys
|
6
|
+
|
7
|
+
def join(record, max=nil)
|
8
|
+
adder.prepend(record).tap { trim(max) if max }
|
9
|
+
end
|
10
|
+
alias add join
|
11
|
+
|
12
|
+
def call
|
13
|
+
remove_element
|
14
|
+
end
|
15
|
+
alias leave call
|
16
|
+
alias take call
|
17
|
+
|
18
|
+
def peek
|
19
|
+
last_element&.subject
|
20
|
+
end
|
21
|
+
|
22
|
+
def trim(max, take_last=is_queue?)
|
23
|
+
if size > max
|
24
|
+
if take_last
|
25
|
+
remove_element
|
26
|
+
else
|
27
|
+
remove_element { first_element }
|
28
|
+
end
|
29
|
+
|
30
|
+
trim max, take_last
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def size
|
35
|
+
InOrder::Element.by_keys(keys).count
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def last_element
|
41
|
+
InOrder::Element.last_element(keys)
|
42
|
+
end
|
43
|
+
|
44
|
+
def first_element
|
45
|
+
InOrder::Element.first_element(keys)
|
46
|
+
end
|
47
|
+
|
48
|
+
def remove_element
|
49
|
+
if element = (block_given? ? yield : last_element)
|
50
|
+
Remove.new(element).call
|
51
|
+
|
52
|
+
element.subject
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def adder
|
57
|
+
Add.new(keys)
|
58
|
+
end
|
59
|
+
|
60
|
+
def is_queue?
|
61
|
+
InOrder::Queue == self.class
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module InOrder
|
3
|
+
# Takes an element out of a list
|
4
|
+
class Remove
|
5
|
+
include Aux::GetElement
|
6
|
+
|
7
|
+
attr_accessor :element_id, :destroy
|
8
|
+
alias destroy? destroy
|
9
|
+
|
10
|
+
def initialize(element_id, destroy: true)
|
11
|
+
self.element_id = element_id
|
12
|
+
|
13
|
+
self.destroy = destroy
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
InOrder::Element.transaction do
|
18
|
+
element = get_element element_id
|
19
|
+
|
20
|
+
if previous = InOrder::Element.find_by(element_id: element.id)
|
21
|
+
previous.update element_id: element.element_id
|
22
|
+
end
|
23
|
+
|
24
|
+
element.tap {|element| element.destroy if destroy? }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
module InOrder
|
3
|
+
# Ensures a list does not exceed a specified length
|
4
|
+
class Trim
|
5
|
+
include Aux::SortElements
|
6
|
+
|
7
|
+
attr_accessor :destroy, :max, :take_from
|
8
|
+
alias destroy? destroy
|
9
|
+
|
10
|
+
def initialize(_max=nil, destroy: true, take_from: :bottom, max: _max)
|
11
|
+
self.destroy = destroy
|
12
|
+
|
13
|
+
self.max = max&.to_i
|
14
|
+
|
15
|
+
self.take_from = take_from
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(elements, max_size=max)
|
19
|
+
return unless max_size
|
20
|
+
|
21
|
+
elements = elements.dup
|
22
|
+
|
23
|
+
InOrder::Element.transaction do
|
24
|
+
while elements.size > max_size.to_i
|
25
|
+
if take_from == :bottom
|
26
|
+
delete elements.pop
|
27
|
+
|
28
|
+
unlink elements.last
|
29
|
+
else
|
30
|
+
delete elements.shift
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
elements
|
35
|
+
end
|
36
|
+
|
37
|
+
class << self
|
38
|
+
mattr_accessor :maximum, default: 10
|
39
|
+
|
40
|
+
def call(*keys)
|
41
|
+
elements = InOrder::Fetch.new(*keys).elements
|
42
|
+
|
43
|
+
args = block_given? ? yield : [ maximum, destroy: false ]
|
44
|
+
|
45
|
+
InOrder::Trim.new(*args).call(elements).map(&:subject)
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_max(max)
|
49
|
+
self.maximum = max
|
50
|
+
|
51
|
+
self
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def delete(element)
|
58
|
+
element.destroy if destroy? and is_an_element!(element)
|
59
|
+
end
|
60
|
+
|
61
|
+
def unlink(element)
|
62
|
+
element.update(element_id: nil) if destroy?
|
63
|
+
end
|
64
|
+
|
65
|
+
def is_an_element!(element)
|
66
|
+
if InOrder::Element === element
|
67
|
+
true
|
68
|
+
else
|
69
|
+
raise "Cannot trim: #{element.class} is not an instance of InOrder::Element"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|