quality_extensions 1.1.4 → 1.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|