quality_extensions 1.1.4 → 1.1.6
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.
- data/lib/Xfind_bug_test.rb +28 -0
- data/lib/quality_extensions/array/all_same.rb +40 -0
- data/lib/quality_extensions/array/delete_if_bang.rb +145 -0
- data/lib/quality_extensions/array/expand_ranges.rb +3 -53
- data/lib/quality_extensions/array/include_any_of.rb +23 -0
- data/lib/quality_extensions/array/justify.rb +305 -0
- data/lib/quality_extensions/array/{average.rb → mean.rb} +1 -1
- data/lib/quality_extensions/array/select_if_bang.rb +0 -0
- data/lib/quality_extensions/array/sum.rb +30 -0
- data/lib/quality_extensions/enumerable/all_same.rb +43 -0
- data/lib/quality_extensions/enumerable/group_by_and_map.rb +110 -0
- data/lib/quality_extensions/enumerable/select_bang.rb +49 -0
- data/lib/quality_extensions/enumerable/select_while.rb +337 -52
- data/lib/quality_extensions/enumerable/select_with_index.rb +145 -0
- data/lib/quality_extensions/hash/hash_select.rb +5 -2
- data/lib/quality_extensions/hash/merge_if.rb +48 -0
- data/lib/quality_extensions/kernel/example_printer.rb +10 -3
- data/lib/quality_extensions/kernel/require_all.rb +24 -8
- data/lib/quality_extensions/kernel/sleep_loudly.rb +108 -0
- data/lib/quality_extensions/kernel/uninterruptable.rb +22 -0
- data/lib/quality_extensions/matrix/indexable.rb +68 -0
- data/lib/quality_extensions/matrix/linked_vectors.rb +137 -0
- data/lib/quality_extensions/module/class_methods.rb +2 -0
- data/lib/quality_extensions/object/non.rb +12 -0
- data/lib/quality_extensions/pathname.rb +435 -7
- data/lib/quality_extensions/range_list.rb +222 -0
- data/lib/quality_extensions/safe_nil.rb +5 -3
- data/lib/quality_extensions/string/each_char_with_index.rb +1 -2
- data/lib/quality_extensions/string/integer_eh.rb +3 -0
- data/lib/quality_extensions/string/numeric_eh.rb +74 -0
- data/lib/quality_extensions/string/safe_in_comment.rb +38 -0
- data/lib/quality_extensions/string/safe_numeric_conversion.rb +102 -0
- data/lib/quality_extensions/string/shell_escape.rb +4 -4
- data/lib/quality_extensions/string/with_knowledge_of_color.rb +8 -0
- data/lib/quality_extensions/table.rb +116 -0
- data/lib/quality_extensions/template.rb +4 -5
- data/lib/quality_extensions/template.rb_test_unit.rb +33 -0
- data/lib/quality_extensions/test/difference_highlighting-minitest.rb +321 -0
- data/lib/quality_extensions/test/difference_highlighting-test_unit.rb +325 -0
- data/lib/quality_extensions/test/difference_highlighting.rb +6 -314
- data/lib/quality_extensions/timeout/countdown_timer.rb +1 -0
- data/lib/quality_extensions/vector/enumerable.rb +51 -0
- metadata +35 -5
@@ -0,0 +1,145 @@
|
|
1
|
+
#--
|
2
|
+
# Author:: Tyler Rick
|
3
|
+
# Inspired by: http://snippets.dzone.com/posts/show/3746 (ntk on Fri Mar 30 10:00:43 -0400 2007)
|
4
|
+
# Copyright:: Copyright (c) 2009, Tyler Rick
|
5
|
+
# License:: Ruby License
|
6
|
+
# Submit to Facets?:: Yes
|
7
|
+
# Developer notes::
|
8
|
+
# History::
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'facets/kernel/require_local'
|
12
|
+
require_local 'select_bang'
|
13
|
+
|
14
|
+
module Enumerable
|
15
|
+
# def reject!
|
16
|
+
# if self.is_a? Range
|
17
|
+
# to_a.reject! { yield }
|
18
|
+
# else
|
19
|
+
# raise NoMethodError
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
|
23
|
+
def select_with_index
|
24
|
+
index = -1
|
25
|
+
if block_given?
|
26
|
+
#select { |x| index += 1; yield(x, index) } # not hash friendly?
|
27
|
+
select { |*args| index += 1; yield(*(args + [index])) }
|
28
|
+
else
|
29
|
+
self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def select_with_index!
|
34
|
+
index = -1
|
35
|
+
if block_given?
|
36
|
+
select! { |x| index += 1; yield(x, index) }
|
37
|
+
else
|
38
|
+
self
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def reject_with_index
|
43
|
+
index = -1
|
44
|
+
if block_given?
|
45
|
+
reject { |x| index += 1; yield(x, index) }
|
46
|
+
else
|
47
|
+
self
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def reject_with_index!
|
52
|
+
index = -1
|
53
|
+
if block_given?
|
54
|
+
reject! { |x| index += 1; yield(x, index) }
|
55
|
+
else
|
56
|
+
self
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# _____ _
|
64
|
+
# |_ _|__ ___| |_
|
65
|
+
# | |/ _ \/ __| __|
|
66
|
+
# | | __/\__ \ |_
|
67
|
+
# |_|\___||___/\__|
|
68
|
+
#
|
69
|
+
=begin test
|
70
|
+
require 'spec'
|
71
|
+
|
72
|
+
describe 'Enumerable#select_with_index' do
|
73
|
+
it 'select_with_index without a block, return self' do
|
74
|
+
('a'..'e').select_with_index.should == ('a'..'e')
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'reject_with_index without a block, return self' do
|
78
|
+
('a'..'n').reject_with_index.should == ('a'..'n')
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'select_with_index with i % 2 == 0' do
|
82
|
+
('a'..'e').select_with_index { |x, i| x if i % 2 == 0 }.should == ["a", "c", "e"]
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'reject_with_index with i % 2 == 0' do
|
86
|
+
('a'..'n').reject_with_index { |x, i| x if i % 2 == 0 }.should == ["b", "d", "f", "h", "j", "l", "n"]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'Enumerable#select_with_index!' do
|
91
|
+
it 'reject!' do
|
92
|
+
a = %w[a b c d]
|
93
|
+
a.reject! {|e| e =~ /[ab]/}
|
94
|
+
a.should == %w[c d]
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'select!' do
|
98
|
+
a = %w[a b c d]
|
99
|
+
a.select! {|e| e =~ /[ab]/}
|
100
|
+
a.should == %w[a b]
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'select_with_index! modifies receiver' do
|
104
|
+
a = %w[a b c d]
|
105
|
+
a.select_with_index! { |x, i| x if i % 2 == 0 }.should == a
|
106
|
+
a.should == %w[a c]
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'reject_with_index! modifies receiver' do
|
110
|
+
a = %w[a b c d]
|
111
|
+
a.reject_with_index! { |x, i| x if i % 2 == 0 }.should == a
|
112
|
+
a.should == %w[b d]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'Enumerable#select_with_index! with hashes' do
|
117
|
+
it 'reject!' do
|
118
|
+
pending
|
119
|
+
a = %w[a b c d]
|
120
|
+
a.reject! {|e| e =~ /[ab]/}
|
121
|
+
a.should == %w[c d]
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'select!' do
|
125
|
+
pending
|
126
|
+
a = %w[a b c d]
|
127
|
+
a.select! {|e| e =~ /[ab]/}
|
128
|
+
a.should == %w[a b]
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'select_with_index! modifies receiver' do
|
132
|
+
pending
|
133
|
+
a = %w[a b c d]
|
134
|
+
a.select_with_index! { |x, i| x if i % 2 == 0 }.should == a
|
135
|
+
a.should == %w[a c]
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'reject_with_index! modifies receiver' do
|
139
|
+
pending
|
140
|
+
a = %w[a b c d]
|
141
|
+
a.reject_with_index! { |x, i| x if i % 2 == 0 }.should == a
|
142
|
+
a.should == %w[b d]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
=end
|
@@ -2,11 +2,13 @@
|
|
2
2
|
# Author:: Tyler Rick
|
3
3
|
# Copyright:: Copyright (c) 2007 QualitySmith, Inc.
|
4
4
|
# License:: Ruby License
|
5
|
-
# Submit to Facets?::
|
5
|
+
# Submit to Facets?:: No.
|
6
|
+
# Deprecated. Ruby 1.9 implements #select correctly.
|
6
7
|
#++
|
7
8
|
|
8
9
|
require "rubygems"
|
9
10
|
|
11
|
+
if RUBY_VERSION < '1.9'
|
10
12
|
class Hash
|
11
13
|
# call-seq:
|
12
14
|
# hash.hash_select {| key, value | block } -> hash
|
@@ -24,6 +26,7 @@ class Hash
|
|
24
26
|
alias_method :hash_find_all, :hash_select
|
25
27
|
alias_method :delete_unless, :hash_select
|
26
28
|
end
|
29
|
+
end
|
27
30
|
|
28
31
|
# _____ _
|
29
32
|
# |_ _|__ ___| |_
|
@@ -34,7 +37,7 @@ end
|
|
34
37
|
=begin test
|
35
38
|
require 'test/unit'
|
36
39
|
|
37
|
-
class
|
40
|
+
class HashSelectTest < Test::Unit::TestCase
|
38
41
|
def test_1
|
39
42
|
hash_copy = hash = {:a => 1, :b => 2}
|
40
43
|
assert_equal hash.reject {|k,v| k != :b},
|
@@ -0,0 +1,48 @@
|
|
1
|
+
duplicates = {}
|
2
|
+
|
3
|
+
people_by_name = {}
|
4
|
+
@people.each do |person|
|
5
|
+
people_by_name[person.name] ||= []
|
6
|
+
people_by_name[person.name] << person
|
7
|
+
end
|
8
|
+
people_by_name.reject! {|name, people| people.size < 2}
|
9
|
+
# Merge elements of this hash back into duplicates, unless it already contains a value (a duplicate) for that value (even if it's for a different key). We don't want to have duplicate duplicates!
|
10
|
+
#duplicates.merge_if(people_by_name) {|k, v| !duplicates.values.include?(v) }
|
11
|
+
require 'ruby-debug'; debugger
|
12
|
+
people_by_name.each {|k, v| duplicates[k] = v unless duplicates.values.include?(v) }
|
13
|
+
|
14
|
+
people_by_wwu_id = {}
|
15
|
+
@people.each do |person|
|
16
|
+
people_by_wwu_id[person.wwu_id] ||= []
|
17
|
+
people_by_wwu_id[person.wwu_id] << person
|
18
|
+
end
|
19
|
+
people_by_wwu_id.reject! {|wwu_id, people| people.size < 2}
|
20
|
+
people_by_name.each {|k, v|
|
21
|
+
duplicates[k] = v unless duplicates.values.include?(v)
|
22
|
+
}
|
23
|
+
|
24
|
+
people_by_email = {}
|
25
|
+
@people.each do |person|
|
26
|
+
people_by_wwu_id[person.email_address] ||= []
|
27
|
+
people_by_wwu_id[person.email_address] << person
|
28
|
+
end
|
29
|
+
people_by_email.reject! {|wwu_id, people| people.size < 2}
|
30
|
+
people_by_name.each {|k, v|
|
31
|
+
duplicates[k] = v unless duplicates.values.include?(v)
|
32
|
+
}
|
33
|
+
|
34
|
+
puts "These people have duplicates:"
|
35
|
+
duplicates.each do |key, people|
|
36
|
+
next if key.blank?
|
37
|
+
puts
|
38
|
+
puts key
|
39
|
+
people.each do |person|
|
40
|
+
puts "#{person.id} ('#{person.first_name}' '#{person.last_name}')".ljust(27) + ' ' +
|
41
|
+
(person.username || '').ljust(17) + ' ' +
|
42
|
+
(person.wwu_id || '').ljust(9) + ' ' +
|
43
|
+
person.created_at.strftime("%Y-%m-%d %H:%M") + ' ' +
|
44
|
+
(person.last_successful_login_at && person.last_successful_login_at.strftime("%Y-%m-%d %H:%M")).to_s.ljust(16) + ' ' +
|
45
|
+
'Profiles: ' + person.profiles.map {|p| "#{p.volume.name} (#{p.user_type.name}, #{p.photos.size} photos)"}.join(', ')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -21,11 +21,12 @@ require 'quality_extensions/module/attribute_accessors'
|
|
21
21
|
# alternative is dirt simple, and it still works.
|
22
22
|
module ExamplePrinter
|
23
23
|
# Prints the given statement (+code+ -- a string) before evaluating it.
|
24
|
-
# Same as xmp only it doesn't print the return value.
|
24
|
+
# Same as xmp only it doesn't print the return value. Is it instead of xmp when the return value isn't interesting or might even be distracting to the reader.
|
25
25
|
#
|
26
26
|
# o = nil
|
27
|
-
#
|
28
|
-
# #
|
27
|
+
# put_statement 'o = C.new', binding
|
28
|
+
# # Outputs:
|
29
|
+
# # o = C.new
|
29
30
|
def put_statement(code, binding = nil, file = __FILE__, line = __LINE__)
|
30
31
|
|
31
32
|
# We'd like to be able to just use the binding of the caller without passing it in, but unfortunately I don't know how (yet)...
|
@@ -46,6 +47,12 @@ module ExamplePrinter
|
|
46
47
|
# Pretty much compatible with irb/xmp. But you have currently have to pass
|
47
48
|
# in the binding manually if you have any local variables/methods that xmp
|
48
49
|
# should have access to.
|
50
|
+
#
|
51
|
+
# o = nil
|
52
|
+
# xmp '3 + x', binding
|
53
|
+
# # Outputs:
|
54
|
+
# # 3 + x
|
55
|
+
# # => 4
|
49
56
|
def xmp(code, binding = nil, options = {})
|
50
57
|
result = put_statement(code, binding)
|
51
58
|
puts "=> #{result}"
|
@@ -38,8 +38,14 @@ module Kernel
|
|
38
38
|
else
|
39
39
|
raise ArgumentError.new("Expected a String or a FileList")
|
40
40
|
end
|
41
|
-
|
42
|
-
|
41
|
+
if (exclusions = options.delete(:exclude))
|
42
|
+
exclusions = [exclusions] if exclusions.is_a? String
|
43
|
+
files = files.exclude(*exclusions)
|
44
|
+
end
|
45
|
+
if (exclusions = options.delete(:exclude_files))
|
46
|
+
exclusions = [exclusions] if exclusions.is_a? String
|
47
|
+
files = files.exclude(*exclusions.map {|a| File.exact_match_regexp(a) })
|
48
|
+
end
|
43
49
|
|
44
50
|
files.each do |filename|
|
45
51
|
# puts "requiring #{filename}" if filename =~ /test/
|
@@ -106,12 +112,13 @@ class TheTest < Test::Unit::TestCase
|
|
106
112
|
assert_equal ['moo.rb'], $loaded
|
107
113
|
|
108
114
|
# But, we can still trick it!
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
+
# Update: Apparently, in Ruby 1.9.1, this no longer works. moo.rb will only be required once.
|
116
|
+
# $LOAD_PATH << @base_dir
|
117
|
+
# require "moo"
|
118
|
+
# assert_equal ['moo.rb', 'moo.rb'], $loaded
|
119
|
+
#
|
120
|
+
# load "moo.rb"
|
121
|
+
# assert_equal ['moo.rb', 'moo.rb', 'moo.rb'], $loaded
|
115
122
|
end
|
116
123
|
|
117
124
|
def test_deep_subdir
|
@@ -172,6 +179,15 @@ class TheTest < Test::Unit::TestCase
|
|
172
179
|
assert_equal ['yes.rb'], $loaded
|
173
180
|
end
|
174
181
|
|
182
|
+
def test_exclude_filename_string
|
183
|
+
create_ruby_file 'yes2.rb'
|
184
|
+
create_ruby_file 'all2.rb'
|
185
|
+
|
186
|
+
require_all File.dirname(@base_dir), :exclude_files => 'all2.rb'
|
187
|
+
|
188
|
+
assert_equal ['yes2.rb'], $loaded
|
189
|
+
end
|
190
|
+
|
175
191
|
#-------------------------------------------------------------------------------------------------------------------------------
|
176
192
|
# Helpers
|
177
193
|
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Kernel
|
2
|
+
|
3
|
+
# Sleeps for integer +n+ number of seconds, by default counting down from +n+ (inclusive) to 0, with a +step+ size of -1, printing the value of the counter at each step (3, 2, 1, 0 (output 4 times) if +n+ is 3), each time separated by a ', '.
|
4
|
+
#
|
5
|
+
# In effect, it is a simple on-screen countdown (or count-up) timer.
|
6
|
+
#
|
7
|
+
# To change the step size, supply a value for +step+ other than -1 (the default). It will sleep for +step.abs+ seconds between each iteration, and at each iteration will either yield to the supplied block or (the default) output the current value of the counter).
|
8
|
+
#
|
9
|
+
# The value of +step+ also determines in which *direction* to count:
|
10
|
+
# If +step+ is negative (the default), it counts *down* from +n+ down to 0 (inclusive).
|
11
|
+
# If +step+ is positive, it counts *up* from 0 up to +n+ (inclusive).
|
12
|
+
#
|
13
|
+
# +step+ does not need to be an integer value.
|
14
|
+
#
|
15
|
+
# If +n+ is not evenly divisible by +step+ (that is, if step * floor( / ? ) > n), the final step size will be shorter to ensure that the total amount slept is +n+ seconds. More precisely, the amount of time it sleeps before the final iteration (during which it won't sleepat all) will
|
16
|
+
#
|
17
|
+
# If a block is provided, all of the default output is overridden, and the block will be yielded with the value of the counter i once every second instead of the default behavior, allowing you to customize what gets output, if anything, or what else happens, every +n.abs+ seconds.
|
18
|
+
#
|
19
|
+
# Note that it produces output (or executes your block, if supplied) n+1 times, *not* n times. This allows you to output (or not) both when the timer is first started *and* when it finishes. But because it sleeps for 1 second after the first n iterations only and *not* after the last, the total delay is still only n seconds.
|
20
|
+
#
|
21
|
+
# Examples:
|
22
|
+
#
|
23
|
+
# sleep_loudly(3)
|
24
|
+
# 3<sleep 1>2, <sleep 1>1, <sleep 1>0
|
25
|
+
#
|
26
|
+
# sleep_loudly(3) {|i| puts(i == 0 ? 'Done' : i)}
|
27
|
+
# 3<sleep 1>
|
28
|
+
# 2<sleep 1>
|
29
|
+
# 1<sleep 1>
|
30
|
+
# Done
|
31
|
+
#
|
32
|
+
# sleep_loudly(10*60, :up, 60) {|i| print i*60, ", "} # sleep for 10 minutes, outputting after every 60 seconds
|
33
|
+
# 0<sleep 60>2, <sleep 60>2, <sleep 60>3,
|
34
|
+
#
|
35
|
+
# sleep_loudly(3, :up) {|i| print i}
|
36
|
+
# 0<sleep 1>1<sleep 1>2<sleep 1>3
|
37
|
+
#
|
38
|
+
# sleep_loudly(3, :up) {|i| print i+1 unless i==3}
|
39
|
+
# 1<sleep 1>2<sleep 1>3<sleep 1>
|
40
|
+
#
|
41
|
+
def sleep_loudly(n, step = -1, options = {}, &block)
|
42
|
+
debug = options[:debug] == true ? 1 : 0
|
43
|
+
#debug = 1
|
44
|
+
|
45
|
+
old_sync, STDOUT.sync = STDOUT.sync, true
|
46
|
+
if step < 0
|
47
|
+
starti, endi = n, 0
|
48
|
+
elsif step > 0
|
49
|
+
starti, endi = 0, n
|
50
|
+
else
|
51
|
+
raise ArgumentError, "step must be positive or negative, not 0"
|
52
|
+
end
|
53
|
+
|
54
|
+
puts "Counting from #{starti} to #{endi} in increments of #{step} (total time should be n=#{n})" if debug
|
55
|
+
|
56
|
+
i = starti
|
57
|
+
final = false
|
58
|
+
loop do
|
59
|
+
print 'final' if final
|
60
|
+
if block_given?
|
61
|
+
yield *[i, final][0..block.arity-1]
|
62
|
+
else
|
63
|
+
print "#{i}"
|
64
|
+
end
|
65
|
+
|
66
|
+
break if final
|
67
|
+
|
68
|
+
remaining = (i - endi).abs
|
69
|
+
|
70
|
+
# if n was a multiple of step, remaining will eventually be 0, telling us that there is one final iteration to go
|
71
|
+
# if n was not a multiple of step, use a different, smaller step as the final step; and we know that there is one final iteration to go
|
72
|
+
if remaining < step.abs
|
73
|
+
s = (step < 0 ? i-endi : endi-i)
|
74
|
+
print " (using smaller final step #{s}) " if debug
|
75
|
+
final = true # the next iteration is the final one
|
76
|
+
else
|
77
|
+
s = step
|
78
|
+
end
|
79
|
+
i += s
|
80
|
+
print " (+#{s}=#{i}) " if debug
|
81
|
+
|
82
|
+
print ", " unless block_given?
|
83
|
+
print " (sleeping for #{s.abs}) " if debug
|
84
|
+
sleep s.abs
|
85
|
+
end
|
86
|
+
|
87
|
+
print "\n" unless block_given?
|
88
|
+
STDOUT.sync = old_sync
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
=begin tests
|
93
|
+
# To do: convert to use test framework
|
94
|
+
debug = 1
|
95
|
+
require 'benchmark'
|
96
|
+
def benchmark(&block)
|
97
|
+
puts(Benchmark.measure { yield })
|
98
|
+
end
|
99
|
+
benchmark { sleep_loudly(2, 1, :debug => true) }
|
100
|
+
#benchmark { sleep_loudly(3, -1) }
|
101
|
+
#benchmark { sleep_loudly(3.2) }
|
102
|
+
#benchmark { sleep_loudly(3) {|i| print i}; puts }
|
103
|
+
#benchmark { sleep_loudly(3) {|i| print i+1 unless i==3} }
|
104
|
+
#benchmark { sleep_loudly(3) {|i| puts(i == 0 ? 'Done' : i)} }
|
105
|
+
benchmark { sleep_loudly(n=4, 1.5) {|i| print i; print ", "}; puts }
|
106
|
+
benchmark { sleep_loudly(n=5, 2) {|i, final| break if final; print i/2; print ", "}; puts }
|
107
|
+
#benchmark { sleep_loudly(n=5, 2) }
|
108
|
+
=end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Source: http://svn.idaemons.org/repos/inplace/trunk/inplace.rb
|
2
|
+
|
3
|
+
$uninterruptible = false
|
4
|
+
|
5
|
+
[:SIGINT, :SIGQUIT, :SIGTERM].each { |sig|
|
6
|
+
trap(sig) {
|
7
|
+
unless $uninterruptible
|
8
|
+
STDERR.puts "Interrupted."
|
9
|
+
exit 130
|
10
|
+
end
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
def uninterruptible
|
15
|
+
orig = $uninterruptible
|
16
|
+
$uninterruptible = true
|
17
|
+
|
18
|
+
yield
|
19
|
+
ensure
|
20
|
+
$uninterruptible = orig
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
#--
|
2
|
+
# Author:: Tyler Rick
|
3
|
+
# Copyright:: Copyright (c) 2009, Tyler Rick
|
4
|
+
# License:: Ruby License
|
5
|
+
# Submit to Facets?:: Yes
|
6
|
+
# Developer notes::
|
7
|
+
# History::
|
8
|
+
#++
|
9
|
+
|
10
|
+
require 'matrix'
|
11
|
+
|
12
|
+
class Matrix
|
13
|
+
#
|
14
|
+
# Changes element (+i+,+j+) of the matrix. (That is: row +i+, column +j+.)
|
15
|
+
#
|
16
|
+
def []=(i, j, new)
|
17
|
+
@rows[i][j] = new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Vector
|
22
|
+
#
|
23
|
+
# Changes element number +i+ (starting at zero) of the vector.
|
24
|
+
#
|
25
|
+
def []=(i, new)
|
26
|
+
@elements[i] = new
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# _____ _
|
31
|
+
# |_ _|__ ___| |_
|
32
|
+
# | |/ _ \/ __| __|
|
33
|
+
# | | __/\__ \ |_
|
34
|
+
# |_|\___||___/\__|
|
35
|
+
#
|
36
|
+
=begin test
|
37
|
+
require 'spec'
|
38
|
+
|
39
|
+
describe 'Matrix' do
|
40
|
+
it '#[]=' do
|
41
|
+
m = Matrix[['a', 'b'], ['c', 'd']]
|
42
|
+
m[0, 0] = 'new'
|
43
|
+
m.should == Matrix[['new', 'b'], ['c', 'd']]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'Vector' do
|
48
|
+
it '#[]=' do
|
49
|
+
v = Vector[1, 2]
|
50
|
+
v[0] = 10
|
51
|
+
v.should == Vector[10, 2]
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'Chanhing vector returned by Matrix#column by using Vector#[]= will not change the values in the Matrix' do
|
55
|
+
m = Matrix[['a', 'b'], ['c', 'd']]
|
56
|
+
|
57
|
+
v = m.column(0)
|
58
|
+
v[1] = 'new'
|
59
|
+
v.should == Vector['a', 'new']
|
60
|
+
m.column(0).should == Vector['a', 'c'] # []= only changes the vector
|
61
|
+
|
62
|
+
m.column(0)[1].replace 'new' # changes the string object itself, which both vector and matrix share
|
63
|
+
m.column(0).should == Vector['a', 'new']
|
64
|
+
m.column(0).should == Vector['a', 'new']
|
65
|
+
end
|
66
|
+
end
|
67
|
+
=end
|
68
|
+
|
@@ -0,0 +1,137 @@
|
|
1
|
+
#--
|
2
|
+
# Author:: Tyler Rick
|
3
|
+
# Copyright:: Copyright (c) 2009, Tyler Rick
|
4
|
+
# License:: Ruby License
|
5
|
+
# Submit to Facets?:: Yes
|
6
|
+
# Developer notes::
|
7
|
+
# User notes:: Only changes done via []= are currenly propagated back to the parent matrix!
|
8
|
+
# History::
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'matrix'
|
12
|
+
require File.dirname(__FILE__) + "/indexable"
|
13
|
+
|
14
|
+
# Idea: could also hide this functionality in a module which is then included as needed
|
15
|
+
#class Matrix
|
16
|
+
# module VectorsLinkedToParentMatrix
|
17
|
+
# end
|
18
|
+
#end
|
19
|
+
|
20
|
+
#---------------------------------------------------------------------------------------------------
|
21
|
+
class Matrix
|
22
|
+
MatrixDetails = Struct.new(:matrix, :i)
|
23
|
+
|
24
|
+
#
|
25
|
+
# Returns row vector number +i+ of the matrix as a Vector (starting at 0 like
|
26
|
+
# an array). When a block is given, the elements of that vector are iterated.
|
27
|
+
#
|
28
|
+
def row(i) # :yield: e
|
29
|
+
if block_given?
|
30
|
+
for e in @rows[i]
|
31
|
+
yield e
|
32
|
+
end
|
33
|
+
else
|
34
|
+
# Because an array of rows happens to be the native internal format of a matrix, the only thing changed was passing copy = false instead of copy = true. Then the rows are passed by reference instead of dup'd and any changes made to them will automatically be made in the matrix as well.
|
35
|
+
Vector.elements(@rows[i], false)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Returns column vector number +j+ of the matrix as a Vector (starting at 0
|
41
|
+
# like an array). When a block is given, the elements of that vector are
|
42
|
+
# iterated.
|
43
|
+
#
|
44
|
+
def column(j) # :yield: e
|
45
|
+
if block_given?
|
46
|
+
0.upto(row_size - 1) do
|
47
|
+
|i|
|
48
|
+
yield @rows[i][j]
|
49
|
+
end
|
50
|
+
else
|
51
|
+
col = (0 .. row_size - 1).collect {
|
52
|
+
|i|
|
53
|
+
@rows[i][j]
|
54
|
+
}
|
55
|
+
# With column vectors, it's a bit trickier to link changes so they propagate to the matrix. The matrix doesn't natively store an array of column arrays. The column array constructed here is already a copy. So we pass the matrix by reference and the column number so that we can later use Matrix#[]= to propagate changes to the vector back to the matrix.
|
56
|
+
Vector.elements(col, false, MatrixDetails.new(self, j))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Vector
|
62
|
+
#
|
63
|
+
# Creates a vector from an Array. The optional second argument specifies
|
64
|
+
# whether the array itself or a copy is used internally.
|
65
|
+
#
|
66
|
+
def Vector.elements(array, copy = true, matrix_details = nil)
|
67
|
+
new(:init_elements, array, copy, matrix_details)
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# For internal use.
|
72
|
+
#
|
73
|
+
def initialize(method, array, copy, matrix_details = nil)
|
74
|
+
self.send(method, array, copy)
|
75
|
+
@matrix_details = matrix_details
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Updates element number +i+ (starting at zero) of the vector.
|
80
|
+
#
|
81
|
+
# If this vector is linked with (a row or column of) a matrix, it changes the corresponding element of that matrix too.
|
82
|
+
#
|
83
|
+
def []=(i, new)
|
84
|
+
@elements[i] = new
|
85
|
+
|
86
|
+
# Update linked matrix too
|
87
|
+
if @matrix_details.respond_to? :matrix
|
88
|
+
#puts "@matrix_details.matrix[#{i}, #{@matrix_details.i}] = #{new.inspect}"
|
89
|
+
@matrix_details.matrix[i, @matrix_details.i] = new
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# _____ _
|
95
|
+
# |_ _|__ ___| |_
|
96
|
+
# | |/ _ \/ __| __|
|
97
|
+
# | | __/\__ \ |_
|
98
|
+
# |_|\___||___/\__|
|
99
|
+
#
|
100
|
+
=begin test
|
101
|
+
require 'spec'
|
102
|
+
|
103
|
+
describe 'Matrix' do
|
104
|
+
it '#[]=' do
|
105
|
+
m = Matrix[['a', 'b'], ['c', 'd']]
|
106
|
+
m[0, 0] = 'new'
|
107
|
+
m.should == Matrix[['new', 'b'], ['c', 'd']]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'Changing vector returned by Matrix#column/row by using Vector#[]=' do
|
112
|
+
it '#[]=' do
|
113
|
+
v = Vector[1, 2]
|
114
|
+
v[0] = 10
|
115
|
+
v.should == Vector[10, 2]
|
116
|
+
end
|
117
|
+
|
118
|
+
it '(column) *will* change the values in the linked Matrix' do
|
119
|
+
m = Matrix[['a', 'b'], ['c', 'd']]
|
120
|
+
|
121
|
+
v = m.column(0)
|
122
|
+
v[0] = 'new'
|
123
|
+
v.should == Vector['new', 'c']
|
124
|
+
m.column(0).should == Vector['new', 'c']
|
125
|
+
end
|
126
|
+
|
127
|
+
it '(row) *will* change the values in the linked Matrix' do
|
128
|
+
m = Matrix[['a', 'b'], ['c', 'd']]
|
129
|
+
|
130
|
+
v = m.row(0)
|
131
|
+
v[0] = 'new'
|
132
|
+
v.should == Vector['new', 'b']
|
133
|
+
m.row(0).should == Vector['new', 'b']
|
134
|
+
end
|
135
|
+
end
|
136
|
+
=end
|
137
|
+
|