skn_utils 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,199 @@
1
+
2
+ module SknUtils
3
+ module Lists
4
+ # Singly Linked List
5
+ # Forward or #next navigation only
6
+ # Head is absolute via #first
7
+ # Tail when (next == nil)
8
+ class LinkedList
9
+ attr_accessor :size
10
+
11
+ def initialize(*values)
12
+ @current = nil
13
+ @head = nil
14
+ @tail = nil
15
+ @size = 0
16
+ values.each {|value| insert(value) }
17
+ end
18
+
19
+ #
20
+ # Navigation
21
+ #
22
+
23
+ # return values and position current to last node accessed
24
+ def first
25
+ @current = head if head
26
+ @current.value rescue nil
27
+ end
28
+
29
+ def next
30
+ @current = @current.next if @current and @current.next
31
+ @current.value rescue nil
32
+ end
33
+
34
+ def current
35
+ @current.value rescue nil
36
+ end
37
+
38
+ def last
39
+ @current = tail if tail
40
+ @current.value rescue nil
41
+ end
42
+
43
+ # -+ int position from current node
44
+ def nth(index)
45
+ node = @current
46
+ while index > 1 and node and node.next
47
+ node = node.next
48
+ index -= 1
49
+ @current = node
50
+ end
51
+ # no reverse or prev for Single List
52
+ current
53
+ rescue NoMethodError
54
+ nil
55
+ end
56
+
57
+ # return node at positive index from head
58
+ def at_index(index)
59
+ find_by_index(index)
60
+ current
61
+ end
62
+
63
+ def empty?
64
+ size == 0
65
+ end
66
+
67
+ #
68
+ # Modifications
69
+ #
70
+
71
+ # return new size
72
+ def insert(value)
73
+ temp = @current.value rescue nil
74
+ insert_after(temp, value)
75
+ end
76
+
77
+ # return new size
78
+ def prepend(value)
79
+ temp = head.value rescue nil
80
+ insert_before(temp, value)
81
+ end
82
+
83
+ # return new size
84
+ def append(value)
85
+ temp = tail.value rescue nil
86
+ insert_after(temp, value)
87
+ end
88
+
89
+ # return new size
90
+ def insert_before(position_value, value)
91
+ prior, target = find_by_value(position_value)
92
+ node = LinkNode.new(value, prior, :single)
93
+ node.next = target if target
94
+ self.head = node if head === target
95
+ self.tail = node if tail.nil?
96
+ @current = node
97
+ self.size += 1
98
+ end
99
+
100
+ # return new size
101
+ def insert_after(position_value, value)
102
+ prior, target = find_by_value(position_value)
103
+ node = LinkNode.new(value, target, :single)
104
+ self.head = node if head.nil?
105
+ self.tail = node if tail === target
106
+ @current = node
107
+ self.size += 1
108
+ end
109
+
110
+ # return remaining size
111
+ def remove(value)
112
+ @current, target_node = find_by_value(value)
113
+ @current.next = target_node.remove!
114
+ tail = @current.next if tail === target_node
115
+ head = @current if head === target_node
116
+ self.size -= 1
117
+ end
118
+
119
+ # return number cleared
120
+ def clear
121
+ rc = 0
122
+ node = head
123
+ position = head
124
+ while node do
125
+ node = node.remove!
126
+ rc += 1
127
+ break if position === node
128
+ end
129
+
130
+ @current = nil
131
+ self.head = nil
132
+ self.tail = nil
133
+ self.size = 0
134
+ rc
135
+ end
136
+
137
+ #
138
+ # Enumerate
139
+ #
140
+
141
+ # perform each() or return enumerator
142
+ def each(&block)
143
+ @current = head
144
+ position = head
145
+ if block_given?
146
+ while position do
147
+ yield position.value.dup
148
+ position = position.next
149
+ end
150
+ else
151
+ Enumerator.new do |yielder, val|
152
+ while position do
153
+ yielder << position.value.dup
154
+ position = position.next
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ # convert self to a value array
161
+ def to_a
162
+ @current = head
163
+ position = head
164
+ result = []
165
+ while position do
166
+ result << position.value.dup
167
+ position = position.next
168
+ break if position === @current
169
+ end
170
+ result
171
+ end
172
+
173
+ private
174
+
175
+ attr_accessor :head, :tail
176
+
177
+ def find_by_value(value)
178
+ return [nil,nil] if head.nil?
179
+ prior = head
180
+ target = prior
181
+ while not target.match_by_value(value)
182
+ prior = target
183
+ target = prior.next
184
+ @current = target if target
185
+ end
186
+ [prior, target]
187
+ end
188
+
189
+ def find_by_index(index)
190
+ return nil if head.nil? or index < 1 or index > size
191
+ node = head
192
+ node = node.next while ((index -= 1) > 0 and node.next)
193
+ @current = node if node
194
+ node
195
+ end
196
+
197
+ end # end class
198
+ end # end module
199
+ end # end module
@@ -23,16 +23,16 @@ module SknUtils
23
23
  def self.attribute(*attrs)
24
24
  attrs.each do |attr|
25
25
  instance_variable_set("@#{attr}", nil)
26
- define_method(attr) {
26
+ define_method(attr) do
27
27
  instance_variable_get("@#{attr}")
28
- }
28
+ end
29
29
  define_method("#{attr}=") do |value|
30
30
  old_value = instance_variable_get("@#{attr}")
31
31
  return if (value == old_value)
32
- @listeners.each { |listener|
33
- listener.attribute_changed(attr, old_value, value)
34
- }
35
32
  instance_variable_set("@#{attr}", value)
33
+ @listeners.each do |listener|
34
+ listener.attribute_changed(attr, old_value, value)
35
+ end
36
36
  end
37
37
  end # loop on attrs
38
38
  end # end of attribute method
@@ -2,8 +2,8 @@
2
2
  module SknUtils
3
3
  class Version
4
4
  MAJOR = 3
5
- MINOR = 2
6
- PATCH = 1
5
+ MINOR = 3
6
+ PATCH = 0
7
7
 
8
8
  def self.to_s
9
9
  [MAJOR, MINOR, PATCH].join('.')
data/lib/skn_utils.rb CHANGED
@@ -5,6 +5,10 @@ require 'skn_utils/page_controls'
5
5
  require 'skn_utils/null_object'
6
6
  require 'skn_utils/notifier_base'
7
7
  require 'skn_utils/skn_configuration'
8
+ require 'skn_utils/lists/link_node'
9
+ require 'skn_utils/lists/linked_list'
10
+ require 'skn_utils/lists/doubly_linked_list'
11
+ require 'skn_utils/lists/circular_linked_list'
8
12
  require 'skn_utils/exploring/commander'
9
13
  require 'skn_utils/exploring/action_service'
10
14
  require 'psych'
data/skn_utils.gemspec CHANGED
@@ -13,7 +13,7 @@ SknUtils contains a small collection of Ruby utilities, the first being a Nested
13
13
  EOF
14
14
 
15
15
  spec.description = <<-EOF
16
- The intent of the NestedResult class is to be a container of data results or key/value pairs,
16
+ The intent of the NestedResult class is to be a container for data values composed of key/value pairs,
17
17
  with easy access to its contents, and on-demand transformation back to the hash (#to_hash).
18
18
 
19
19
  Review the RSpec tests, and or review the README for more details.
@@ -24,13 +24,6 @@ SknUtils::NestedResult class. SknUtils::NestedResult replaces those original cl
24
24
 
25
25
  Please update your existing code in consideration of the above change, or use the prior version 2.0.6.
26
26
 
27
- ATTENTION: ****************************************************************
28
- This version may require the following be added to your Rails Application 'Gemfile',
29
- if you are using the SknSettings configuration class.
30
-
31
- gem 'deep_merge', '~> 1.1'
32
-
33
- ************************************************************************
34
27
  EOF
35
28
  spec.homepage = "https://github.com/skoona/skn_utils"
36
29
  spec.license = "MIT"
@@ -0,0 +1,242 @@
1
+ ##
2
+ # spec/lib/skn_utils/circular_linked_list_spec.rb
3
+ #
4
+
5
+ RSpec.describe SknUtils::Lists::CircularLinkedList, "Circular LinkedList " do
6
+
7
+ context "Initialization" do
8
+ it "can be initialized without params" do
9
+ expect(subject).to be
10
+ end
11
+ it "can insert the first value" do
12
+ expect(subject.empty?).to be true
13
+ expect(subject.insert(101)).to eq(1)
14
+ end
15
+ it "can be cleared" do
16
+ subject.insert(101)
17
+ expect(subject.clear).to eq(1)
18
+ end
19
+ it "can be initialized with one or more initial values" do
20
+ list = described_class.new(10,100,100)
21
+ expect(list.current).to eq(100)
22
+ end
23
+ it "is initially empty?" do
24
+ expect(subject.empty?).to be true
25
+ end
26
+ end
27
+
28
+ context "Navigation" do
29
+ let(:list) { described_class.new(10,20, 30, 40, 50, 60, 70, 80, 90, 100) }
30
+
31
+ it "#first returns the first value" do
32
+ expect(list.first).to eq(10)
33
+ end
34
+ it "#next returns the second value" do
35
+ expect(list.first).to eq(10)
36
+ expect(list.next).to eq(20)
37
+ end
38
+ it "#current returns the last value as a side-effect of initialization via new" do
39
+ expect(list.current).to eq(100)
40
+ end
41
+ it "#prev returns the prior value" do
42
+ expect(list.prev).to eq(90)
43
+ end
44
+ it "#last returns the last value" do
45
+ expect(list.last).to eq(100)
46
+ end
47
+ it "#nth(6) returns the sixth value" do
48
+ expect(list.first).to eq(10)
49
+ expect(list.nth(6)).to eq(60)
50
+ expect(list.nth(-2)).to eq(40)
51
+ end
52
+ it "#at_index(6) returns the sixth value" do
53
+ expect(list.at_index(6)).to eq(60)
54
+ end
55
+
56
+ end
57
+ context "Insertions" do
58
+ it "#insert(value) indicates a value was added" do
59
+ bsize = subject.size
60
+ expect(subject.insert(110)).to eq(bsize + 1)
61
+ end
62
+ it "#prepend(value) indicates a value was added" do
63
+ bsize = subject.size
64
+ expect(subject.prepend(110)).to eq(bsize + 1)
65
+ end
66
+ it "#append(value) indicates a value was added" do
67
+ bsize = subject.size
68
+ expect(subject.append(110)).to eq(bsize + 1)
69
+ end
70
+ it "#insert_before(pvalue,value) indicates a value was added" do
71
+ subject.insert(120)
72
+ bsize = subject.size
73
+ expect(subject.insert_before(120, 110)).to eq(bsize + 1)
74
+ expect(subject.to_a).to eq([110,120])
75
+ end
76
+ it "#insert_after(value) indicates a value was added" do
77
+ subject.insert(120)
78
+ bsize = subject.size
79
+ expect(subject.insert_after(120, 125)).to eq(bsize + 1)
80
+ expect(subject.to_a).to eq([120,125])
81
+ end
82
+ end
83
+
84
+ context "Removals" do
85
+ let(:list) { described_class.new(10,20, 30, 40, 50, 60, 70, 80, 90, 100) }
86
+
87
+ it "#remove(value) removes first occurance of that value" do
88
+ bsize = list.size
89
+ expect(list.remove(30)).to eq(bsize - 1)
90
+ expect(list.to_a).to eq([10,20, 40, 50, 60, 70, 80, 90, 100])
91
+ end
92
+
93
+ it "#clear removes all elements from list" do
94
+ expect(list.clear).to eq(10)
95
+ expect(list.empty?).to be true
96
+ end
97
+ end
98
+
99
+ context "Enumeration" do
100
+ let(:list) { described_class.new(10,20, 30, 40, 50, 60, 70, 80, 90, 100) }
101
+ it "#each works as expected when block is provided" do
102
+ x = []
103
+ list.each {|r| x << r}
104
+ expect(x).to be_a(Array)
105
+ expect(x).to eq([10,20, 30, 40, 50, 60, 70, 80, 90, 100])
106
+ end
107
+ it "#each works as expected when no block is offered" do
108
+ expect(list.each).to be_a(Enumerator)
109
+ expect(list.each.first).to eq(10)
110
+ end
111
+ it "#to_a returns the contents of linkedlist as an Array" do
112
+ base = list.to_a
113
+ expect(base).to be_a(Array)
114
+ expect(base).to eq([10,20, 30, 40, 50, 60, 70, 80, 90, 100])
115
+ end
116
+ end
117
+
118
+ context "Edge cases " do
119
+ let(:list) { described_class.new(10,20, 30, 40, 50, 60, 70, 80, 90, 100) }
120
+
121
+ it "#at_index(-999) fails and returns the current element. " do
122
+ expect(list.at_index(-999)).to eq(100)
123
+ end
124
+ it "#at_index(0) fails and returns the current element. " do
125
+ expect(list.at_index(0)).to eq(100)
126
+ end
127
+ it "#at_index(999) fails and returns the current element. " do
128
+ expect(list.at_index(999)).to eq(100)
129
+ end
130
+ it "#at_index(n) returns the proper element. " do
131
+ expect(list.at_index(1)).to eq(10)
132
+ expect(list.at_index(list.size / 2)).to eq(50)
133
+ expect(list.at_index(list.size)).to eq(100)
134
+ end
135
+ it "#at_index(n) returns the proper element for linkedlist with one element. " do
136
+ only = described_class.new(55)
137
+ expect(only.at_index(1)).to eq(55)
138
+ expect(only.at_index(10)).to eq(55)
139
+ expect(only.at_index(-10)).to eq(55)
140
+ end
141
+
142
+ it "#nth(-999) returns wraps to starting place, or last initialization value." do
143
+ expect(list.nth(-999)).to eq(100)
144
+ end
145
+ it "#nth(0) returns current value, or last initialization value." do
146
+ expect(list.nth(0)).to eq(100)
147
+ end
148
+ it "#nth(999) returns last initialization value." do
149
+ expect(list.nth(999)).to eq(100)
150
+ end
151
+ it "#current equals last initialization value." do
152
+ expect(list.current).to eq(100)
153
+ end
154
+ it "#next after initialization wraps to first initialization value. " do
155
+ expect(list.next).to eq(10)
156
+ expect(list.next).to eq(20)
157
+ expect(list.next).to eq(30)
158
+ end
159
+ it "#prev after first returns proper sequence of values. " do
160
+ expect(list.first).to eq(10)
161
+ expect(list.prev).to eq(100)
162
+ expect(list.prev).to eq(90)
163
+ end
164
+ it "#first, #next, #current, #prev, #nth, and #last return same value after initialization with one value. " do
165
+ only = described_class.new(55)
166
+ expect(only.first).to eq(55)
167
+ expect(only.next).to eq(55)
168
+ expect(only.prev).to eq(55)
169
+ expect(only.last).to eq(55)
170
+ expect(only.current).to eq(55)
171
+ expect(only.nth(1)).to eq(55)
172
+ expect(only.nth(11)).to eq(55)
173
+ end
174
+ it "#first, #next, #current, #prev, #nth, and #last return same value after initialization with no values. " do
175
+ only = described_class.new
176
+ expect(only.first).to be nil
177
+ expect(only.next).to be nil
178
+ expect(only.prev).to be nil
179
+ expect(only.last).to be nil
180
+ expect(only.current).to be nil
181
+ expect(only.nth(1)).to be nil
182
+ expect(only.nth(-1)).to be nil
183
+ end
184
+ it "#prepend enables navigation methods normal operations. " do
185
+ only = described_class.new
186
+ only.prepend(55)
187
+ expect(only.first).to eq(55)
188
+ expect(only.next).to eq(55)
189
+ expect(only.prev).to eq(55)
190
+ expect(only.last).to eq(55)
191
+ expect(only.current).to eq(55)
192
+ expect(only.nth(1)).to eq(55)
193
+ expect(only.nth(11)).to eq(55)
194
+ end
195
+ it "#append enables navigation methods normal operations. " do
196
+ only = described_class.new
197
+ only.append(55)
198
+ expect(only.first).to eq(55)
199
+ expect(only.next).to eq(55)
200
+ expect(only.prev).to eq(55)
201
+ expect(only.last).to eq(55)
202
+ expect(only.current).to eq(55)
203
+ expect(only.nth(1)).to eq(55)
204
+ expect(only.nth(11)).to eq(55)
205
+ end
206
+ it "#insert_before enables navigation methods normal operations. " do
207
+ only = described_class.new
208
+ only.insert_before(nil, 55)
209
+ expect(only.first).to eq(55)
210
+ expect(only.next).to eq(55)
211
+ expect(only.prev).to eq(55)
212
+ expect(only.last).to eq(55)
213
+ expect(only.current).to eq(55)
214
+ expect(only.nth(1)).to eq(55)
215
+ expect(only.nth(11)).to eq(55)
216
+ end
217
+ it "#insert_after enables navigation methods normal operations. " do
218
+ only = described_class.new
219
+ only.insert_after(nil, 55)
220
+ expect(only.first).to eq(55)
221
+ expect(only.next).to eq(55)
222
+ expect(only.prev).to eq(55)
223
+ expect(only.last).to eq(55)
224
+ expect(only.current).to eq(55)
225
+ expect(only.nth(1)).to eq(55)
226
+ expect(only.nth(11)).to eq(55)
227
+ end
228
+ it "#remove does not make navigation methods unstable if only element. " do
229
+ only = described_class.new(55)
230
+ only.remove(55)
231
+ expect(only.first).to be nil
232
+ expect(only.next).to be nil
233
+ expect(only.prev).to be nil
234
+ expect(only.last).to be nil
235
+ expect(only.current).to be nil
236
+ expect(only.nth(1)).to be nil
237
+ expect(only.nth(-1)).to be nil
238
+ end
239
+
240
+ end
241
+
242
+ end