skn_utils 3.2.1 → 3.3.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.
@@ -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