ss-se_gem 0.0.6 → 1.0.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.
- data/lib/core_ext.rb +2 -1
- data/lib/se/aspect.rb +118 -0
- data/lib/se/core_ext/benchmark.rb +11 -0
- data/lib/se/core_ext/date.rb +10 -0
- data/lib/se/core_ext/regexp.rb +20 -0
- data/lib/se/core_ext/retryable.rb +32 -0
- data/lib/se/core_ext/socket.rb +24 -0
- data/lib/se/core_ext/yaml.rb +13 -0
- data/lib/se/memory_profiler.rb +93 -0
- data/lib/se/negator.rb +23 -0
- data/lib/se/pager.rb +39 -0
- data/lib/se/sleeper.rb +105 -0
- data/lib/se/timer.rb +67 -0
- data/lib/se/unicode.rb +28 -0
- data/test/se/aspect_test.rb +43 -0
- data/test/se/core_ext/date_test.rb +15 -0
- data/test/se/core_ext/regexp_test.rb +15 -0
- data/test/se/core_ext/retryable_test.rb +16 -0
- data/test/se/{debug/debug_test.rb → debug_test.rb} +1 -1
- data/test/se/memory_profiler_test.rb +19 -0
- data/test/se/negator_test.rb +13 -0
- data/test/se/sleeper_test.rb +54 -0
- data/test/se/timer_test.rb +37 -0
- data/test/se/unicode_test.rb +16 -0
- metadata +27 -5
- /data/lib/se/{debug/debug.rb → debug.rb} +0 -0
- /data/lib/se/{core_ext/meta_class.rb → metaclass.rb} +0 -0
data/lib/core_ext.rb
CHANGED
data/lib/se/aspect.rb
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#
|
|
2
|
+
# I had a look at AspectR [1], and it looked awfully confusing.
|
|
3
|
+
# I then had a look at AOP on wikipedia [2], I think the people
|
|
4
|
+
# who contributed to that have possibly gone whacko. An excerpt:
|
|
5
|
+
#
|
|
6
|
+
# "Since the risk is to code written by others, code weaving can
|
|
7
|
+
# be emotional for the authors of the original code. There is
|
|
8
|
+
# little moral grounding to guide programmers in these matters
|
|
9
|
+
# because morality isn't something often applied to coding practices"
|
|
10
|
+
#
|
|
11
|
+
# What??!
|
|
12
|
+
#
|
|
13
|
+
# Here is my non-academic completely morality-free implementation.
|
|
14
|
+
#
|
|
15
|
+
# See line 99 for the API. Feedback is appreciated.
|
|
16
|
+
#
|
|
17
|
+
# Ryan Allen (aspect@yeahnah.org)
|
|
18
|
+
#
|
|
19
|
+
# [1] http://aspectr.sourceforge.net/
|
|
20
|
+
# [2] http://en.wikipedia.org/wiki/Aspect-oriented_programming
|
|
21
|
+
#
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# Example:
|
|
25
|
+
#
|
|
26
|
+
# class Foo
|
|
27
|
+
# def self.class_method
|
|
28
|
+
# puts 'class_method'
|
|
29
|
+
# end
|
|
30
|
+
# def instance_method
|
|
31
|
+
# puts 'instance_method'
|
|
32
|
+
# end
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# SE::Aspect.define do
|
|
36
|
+
# with_class(Foo).before(:class_method) do |klass|
|
|
37
|
+
# puts 'before class_method'
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# with_instance_of(Foo).before(:instance_method) do |instance|
|
|
41
|
+
# puts 'before instance_method'
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
# with_class(Foo).after(:class_method) do | klass|
|
|
45
|
+
# puts 'after class_method'
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# with_instance_of(Foo).after(:instance_method) do |instance|
|
|
49
|
+
# puts 'after instance_method'
|
|
50
|
+
# end
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# Foo.class_method
|
|
54
|
+
# #=> before class_method
|
|
55
|
+
# #=> class_method
|
|
56
|
+
# #=> after class_method
|
|
57
|
+
#
|
|
58
|
+
# Foo.new.instance_method
|
|
59
|
+
# #=> before instance_method
|
|
60
|
+
# #=> instance_method
|
|
61
|
+
# #=> after instance_method
|
|
62
|
+
#
|
|
63
|
+
module SE
|
|
64
|
+
module Aspect
|
|
65
|
+
|
|
66
|
+
class << self
|
|
67
|
+
|
|
68
|
+
def define(&aspects)
|
|
69
|
+
instance_eval(&aspects)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def with_instance_of(klass)
|
|
73
|
+
@class = klass
|
|
74
|
+
@subject = :instance
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def with_class(klass)
|
|
79
|
+
@class = klass
|
|
80
|
+
@subject = :class
|
|
81
|
+
self
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def before(method_name, &perform_before)
|
|
85
|
+
with_scope do
|
|
86
|
+
method_prior = instance_method(method_name)
|
|
87
|
+
define_method method_name do |*args|
|
|
88
|
+
perform_before.call(self, *args)
|
|
89
|
+
method_prior.bind(self).call(*args)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def after(method_name, &perform_after)
|
|
95
|
+
with_scope do
|
|
96
|
+
method_prior = instance_method(method_name)
|
|
97
|
+
define_method method_name do |*args|
|
|
98
|
+
return_value = method_prior.bind(self).call(*args)
|
|
99
|
+
perform_after.call(self, *args)
|
|
100
|
+
return_value
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
def with_scope(&code)
|
|
108
|
+
if @subject == :class
|
|
109
|
+
@class.class_eval { class << self; self; end }.instance_eval(&code)
|
|
110
|
+
else
|
|
111
|
+
@class.class_eval(&code)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end # class << self
|
|
116
|
+
|
|
117
|
+
end # module Aspect
|
|
118
|
+
end # module SE
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class Regexp
|
|
2
|
+
|
|
3
|
+
def blank?
|
|
4
|
+
false
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# Allow a Regexp to be used instead of an Array for matching a value.
|
|
9
|
+
#
|
|
10
|
+
# Instead of:
|
|
11
|
+
# ['a-1','a-2'].include?(value)
|
|
12
|
+
#
|
|
13
|
+
# We can:
|
|
14
|
+
# /a-[12]/.include?(value)
|
|
15
|
+
#
|
|
16
|
+
def include?(value)
|
|
17
|
+
not match(value.to_s).nil?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# From: http://blog.codefront.net/2008/01/14/retrying-code-blocks-in-ruby-on-exceptions-whatever/
|
|
2
|
+
#
|
|
3
|
+
# Adds +retryable+ method to Kernel.
|
|
4
|
+
#
|
|
5
|
+
module Kernel
|
|
6
|
+
|
|
7
|
+
# Options:
|
|
8
|
+
# * :tries - Number of retries to perform. Defaults to 1.
|
|
9
|
+
# * :on - The Exception on which a retry will be performed. (default Exception)
|
|
10
|
+
#
|
|
11
|
+
# Example
|
|
12
|
+
# =======
|
|
13
|
+
# retryable(:tries => 1, :on => OpenURI::HTTPError) do
|
|
14
|
+
# # your code here
|
|
15
|
+
# end
|
|
16
|
+
def retryable(options = {}, &block)
|
|
17
|
+
opts = {:tries => 1}.merge!(options)
|
|
18
|
+
|
|
19
|
+
retry_exception = opts[:on] || Exception
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
return yield
|
|
23
|
+
rescue retry_exception
|
|
24
|
+
(opts[:tries] - 1).times do
|
|
25
|
+
return yield rescue retry_exception
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
yield
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
# from http://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/
|
|
4
|
+
#
|
|
5
|
+
# This method does NOT make a connection or send any packets to 64.233.187.99
|
|
6
|
+
# (Google). Since UDP is a stateless protocol connect() merely makes a
|
|
7
|
+
# system call which figures out how to route the packets based on the address
|
|
8
|
+
# and what interface (and therefore IP address) it should bind to. addr()
|
|
9
|
+
# returns an array containing the family (AF_INET), local port, and local
|
|
10
|
+
# address (which is what we want) of the socket.
|
|
11
|
+
#
|
|
12
|
+
def Socket.local_ip
|
|
13
|
+
@local_ip ||= begin
|
|
14
|
+
# turn off reverse DNS resolution temporarily
|
|
15
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true
|
|
16
|
+
UDPSocket.open do |s|
|
|
17
|
+
s.connect '64.233.187.99', 1
|
|
18
|
+
s.addr.last
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
ensure
|
|
22
|
+
Socket.do_not_reverse_lookup = orig
|
|
23
|
+
end
|
|
24
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Read a file, run it through ERB, then YAML::load.
|
|
5
|
+
#
|
|
6
|
+
# The caller is expected to have loaded ERB.
|
|
7
|
+
#
|
|
8
|
+
# +filename+ - name of file
|
|
9
|
+
# +args+ - arguments for ERB.new call
|
|
10
|
+
#
|
|
11
|
+
def YAML.load_erb_file(filename, *args)
|
|
12
|
+
YAML::load(ERB.new(IO.read(filename), *args).result)
|
|
13
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# From http://scottstuff.net/blog/articles/2006/08/17/memory-leak-profiling-with-rails
|
|
2
|
+
|
|
3
|
+
# Simple memory profiler.
|
|
4
|
+
#
|
|
5
|
+
# Calling SE::MemoryProfiler.start will spin up a thread that writes
|
|
6
|
+
# instance counts to +log_file_name+ and optionally writes String
|
|
7
|
+
# values to +string_log_file_name+.
|
|
8
|
+
#
|
|
9
|
+
# Example:
|
|
10
|
+
#
|
|
11
|
+
# # write to log every 3 seconds
|
|
12
|
+
# SE::MemoryProfiler.start(:every=>3)
|
|
13
|
+
#
|
|
14
|
+
module SE
|
|
15
|
+
class MemoryProfiler
|
|
16
|
+
DEFAULTS = {:every => 10, :string_debug => false}
|
|
17
|
+
|
|
18
|
+
# Start the profiler.
|
|
19
|
+
#
|
|
20
|
+
# opts can be one of:
|
|
21
|
+
# +every+ => delay in seconds between output to +log_file_name+ (default 10)
|
|
22
|
+
# +string_debug+ => whether to write string values to +string_log_file_name+ (default false)
|
|
23
|
+
#
|
|
24
|
+
def self.start(opt={})
|
|
25
|
+
opt = DEFAULTS.dup.merge(opt)
|
|
26
|
+
|
|
27
|
+
Thread.new do
|
|
28
|
+
prev = Hash.new(0)
|
|
29
|
+
curr = Hash.new(0)
|
|
30
|
+
curr_strings = []
|
|
31
|
+
delta = Hash.new(0)
|
|
32
|
+
|
|
33
|
+
file = File.open(log_file_name,'w')
|
|
34
|
+
|
|
35
|
+
loop do
|
|
36
|
+
begin
|
|
37
|
+
GC.start
|
|
38
|
+
curr.clear
|
|
39
|
+
|
|
40
|
+
curr_strings = [] if opt[:string_debug]
|
|
41
|
+
|
|
42
|
+
ObjectSpace.each_object do |o|
|
|
43
|
+
curr[o.class] += 1 #Marshal.dump(o).size rescue 1
|
|
44
|
+
if opt[:string_debug] and o.class == String
|
|
45
|
+
curr_strings.push o
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if opt[:string_debug]
|
|
50
|
+
File.open(string_log_file_name, 'w') do |f|
|
|
51
|
+
curr_strings.sort.each do |s|
|
|
52
|
+
f.puts s
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
curr_strings.clear
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
delta.clear
|
|
59
|
+
(curr.keys + delta.keys).uniq.each do |k,v|
|
|
60
|
+
delta[k] = curr[k]-prev[k]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
file.puts "Top 20"
|
|
64
|
+
delta.sort_by { |k,v| -v.abs }[0..19].sort_by { |k,v| -v}.each do |k,v|
|
|
65
|
+
file.printf "%+5d: %s (%d)\n", v, k.name, curr[k] unless v == 0
|
|
66
|
+
end
|
|
67
|
+
file.flush
|
|
68
|
+
|
|
69
|
+
delta.clear
|
|
70
|
+
prev.clear
|
|
71
|
+
prev.update curr
|
|
72
|
+
GC.start
|
|
73
|
+
rescue Exception => err
|
|
74
|
+
STDERR.puts "** memory_profiler error: #{err}"
|
|
75
|
+
end
|
|
76
|
+
sleep opt[:every].to_i
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# log file where object counts are written
|
|
83
|
+
def self.log_file_name
|
|
84
|
+
'memory_profiler.log'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# log file where string values are written
|
|
88
|
+
def self.string_log_file_name(time=Time.now)
|
|
89
|
+
"memory_profiler_strings.log.#{time.to_i}"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
end # class MemoryProfile
|
|
93
|
+
end # module SE
|
data/lib/se/negator.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# http://probablycorey.wordpress.com/2008/03/28/abusing-rubys-question-mark-methods/
|
|
2
|
+
#
|
|
3
|
+
# Takes an object as a parameter and will invert the return values of all its
|
|
4
|
+
# methods.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
module SE
|
|
8
|
+
class Negator
|
|
9
|
+
def initialize(object)
|
|
10
|
+
@object = object
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def method_missing(symbol, *args)
|
|
14
|
+
!@object.send(symbol, *args)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Object
|
|
20
|
+
def not
|
|
21
|
+
SE::Negator.new(self)
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/se/pager.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# From: http://nex-3.com/posts/73-git-style-automatic-paging-in-ruby
|
|
2
|
+
#
|
|
3
|
+
# Route STDOUT and STDERR through +less+ if output is more than one screen
|
|
4
|
+
#
|
|
5
|
+
# Call +SE::Pager.run_pager+ before you emit output in your program.
|
|
6
|
+
#
|
|
7
|
+
module SE
|
|
8
|
+
module Pager
|
|
9
|
+
def self.run_pager(less_opts='FRSX')
|
|
10
|
+
return if PLATFORM =~ /win32/
|
|
11
|
+
return unless STDOUT.tty?
|
|
12
|
+
|
|
13
|
+
read, write = IO.pipe
|
|
14
|
+
|
|
15
|
+
if Kernel.fork.nil?
|
|
16
|
+
# Child process
|
|
17
|
+
STDOUT.reopen(write)
|
|
18
|
+
STDERR.reopen(write) if STDERR.tty?
|
|
19
|
+
read.close
|
|
20
|
+
write.close
|
|
21
|
+
return
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Parent process, become pager
|
|
25
|
+
STDIN.reopen(read)
|
|
26
|
+
read.close
|
|
27
|
+
write.close
|
|
28
|
+
|
|
29
|
+
# set less options via envar
|
|
30
|
+
ENV['LESS'] = less_opts
|
|
31
|
+
|
|
32
|
+
# Wait until we have input before we start the pager
|
|
33
|
+
Kernel.select [STDIN]
|
|
34
|
+
|
|
35
|
+
pager = ENV['PAGER'] || 'less'
|
|
36
|
+
exec pager rescue exec "/bin/sh", "-c", pager
|
|
37
|
+
end
|
|
38
|
+
end # module Pager
|
|
39
|
+
end # module SE
|
data/lib/se/sleeper.rb
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Sleep, execute block, repeat.
|
|
2
|
+
# Allow resetting of the timer.
|
|
3
|
+
#
|
|
4
|
+
# An array of sleepers is stored in +Thread.main[:sleepers]+ to make
|
|
5
|
+
# sure we terminate all sleeper threads when a signal is trapped.
|
|
6
|
+
#
|
|
7
|
+
class Sleeper
|
|
8
|
+
|
|
9
|
+
# a hash of signals and their old handlers
|
|
10
|
+
OLD_HANDLERS = {
|
|
11
|
+
'INT' => nil,
|
|
12
|
+
'TERM' => nil,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class Reset < Exception ; end
|
|
16
|
+
|
|
17
|
+
attr_reader :thread
|
|
18
|
+
|
|
19
|
+
# Return an array of Sleepers in this process.
|
|
20
|
+
def self.sleepers
|
|
21
|
+
Thread.main[:sleepers] ||= begin
|
|
22
|
+
set_traps
|
|
23
|
+
[]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Initialize the sleeper by passing a +timetout+ in seconds and an
|
|
29
|
+
# optional block.
|
|
30
|
+
#
|
|
31
|
+
def initialize(timeout, &block)
|
|
32
|
+
after_wake &block if block_given?
|
|
33
|
+
|
|
34
|
+
Sleeper.sleepers << self
|
|
35
|
+
|
|
36
|
+
@thread = Thread.new do
|
|
37
|
+
while true do
|
|
38
|
+
begin
|
|
39
|
+
sleep(timeout.to_i)
|
|
40
|
+
# TODO: investigate using Thread.critical
|
|
41
|
+
Thread.current[:awake] = true
|
|
42
|
+
@after_wake.each {|block| block.call} if @after_wake
|
|
43
|
+
rescue Reset
|
|
44
|
+
@after_reset.each {|block| block.call} if @after_reset
|
|
45
|
+
# re-start sleep cycle
|
|
46
|
+
ensure
|
|
47
|
+
Thread.current[:awake] = false
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Reset the sleeper.
|
|
55
|
+
#
|
|
56
|
+
# This will cause all +after_reset+ handlers to be executed.
|
|
57
|
+
#
|
|
58
|
+
def reset
|
|
59
|
+
thread.raise(Reset.new) unless thread[:awake]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Terminate the Sleeper.
|
|
63
|
+
#
|
|
64
|
+
# This will cause all +after_terminate+ handlers to be executed.
|
|
65
|
+
#
|
|
66
|
+
def terminate
|
|
67
|
+
Sleeper.sleepers.delete(self)
|
|
68
|
+
thread.terminate
|
|
69
|
+
@after_terminate.each {|block| block.call} if @after_terminate
|
|
70
|
+
@thread = nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Add a block to be executed after the Sleeper wakes up.
|
|
74
|
+
def after_wake(&block)
|
|
75
|
+
(@after_wake ||= []) << block
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Add a block to be executed after the Sleeper is reset.
|
|
79
|
+
def after_reset(&block)
|
|
80
|
+
(@after_reset ||= []) << block
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Add a block to be executed after the Sleeper is terminated.
|
|
84
|
+
def after_terminate(&block)
|
|
85
|
+
(@after_terminate ||= []) << block
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def self.set_traps
|
|
91
|
+
OLD_HANDLERS.keys.each do |sig|
|
|
92
|
+
OLD_HANDLERS[sig] = Signal.trap(sig) do
|
|
93
|
+
kill_all_sleepers
|
|
94
|
+
OLD_HANDLERS[sig].call unless 'DEFAULT' == OLD_HANDLERS[sig]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def self.kill_all_sleepers
|
|
100
|
+
while to_kill = Thread.main[:sleepers].pop do
|
|
101
|
+
to_kill.terminate
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
data/lib/se/timer.rb
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require 'lib/se/aspect'
|
|
2
|
+
|
|
3
|
+
module SE
|
|
4
|
+
class Timer
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
attr_accessor :stats, :timers, :enabled
|
|
8
|
+
|
|
9
|
+
def start(item)
|
|
10
|
+
return unless enabled
|
|
11
|
+
timers[item] = Time.now
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def end(item)
|
|
15
|
+
return unless enabled
|
|
16
|
+
return unless timers[item]
|
|
17
|
+
self.stats[item][:calls] += 1
|
|
18
|
+
self.stats[item][:time] += Time.now - timers[item]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def time_block(item)
|
|
22
|
+
self.start(item)
|
|
23
|
+
retval = yield
|
|
24
|
+
self.end(item)
|
|
25
|
+
return retval
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Note: Does not work on methods that take blocks.
|
|
29
|
+
def time_method(klass, method, item, type="instance")
|
|
30
|
+
item ||= "#{klass}::#{method}"
|
|
31
|
+
SE::Aspect.define do
|
|
32
|
+
if type == "instance"
|
|
33
|
+
with_instance_of(klass).before(method.to_sym) {SE::Timer.start(item)}
|
|
34
|
+
with_instance_of(klass).after(method.to_sym) {SE::Timer.end(item)}
|
|
35
|
+
else
|
|
36
|
+
with_class(klass).before(method.to_sym) {SE::Timer.start(item)}
|
|
37
|
+
with_class(klass).after(method.to_sym) {SE::Timer.end(item)}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def reset
|
|
43
|
+
stats.clear
|
|
44
|
+
timers.clear
|
|
45
|
+
enabled = true
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def stats
|
|
49
|
+
Thread.current['SE::Timer.stats'] ||= Hash.new {|hash, key| hash[key] = Hash.new(0.0)}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def timers
|
|
53
|
+
Thread.current['Worfkeed::Timer.timers'] ||= {}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def enabled
|
|
57
|
+
Thread.current['SE::timer.enabled'] ||= false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def enabled=(value)
|
|
61
|
+
Thread.current['SE::timer.enabled'] = value
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end # class Timer
|
|
67
|
+
end # module SE
|
data/lib/se/unicode.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Source: http://snippets.dzone.com/posts/show/4546
|
|
2
|
+
#
|
|
3
|
+
# This module lazily defines constants of the form Uxxxx for all Unicode
|
|
4
|
+
# codepoints from +U0000+ to +U10FFFF+. The value of each constant is the
|
|
5
|
+
# UTF-8 string for the codepoint.
|
|
6
|
+
#
|
|
7
|
+
# Examples:
|
|
8
|
+
# copyright = SE::Unicode::U00A9
|
|
9
|
+
# euro = SE::Unicode::U20AC
|
|
10
|
+
# infinity = SE::Unicode::U221E
|
|
11
|
+
#
|
|
12
|
+
module SE
|
|
13
|
+
module Unicode
|
|
14
|
+
|
|
15
|
+
def self.const_missing(name)
|
|
16
|
+
# Check that the constant name is of the right form: U0000 to U10FFFF
|
|
17
|
+
if name.to_s =~ /^U([0-9a-fA-F]{4,5}|10[0-9a-fA-F]{4})$/
|
|
18
|
+
# Convert the codepoint to an immutable UTF-8 string,
|
|
19
|
+
# define a real constant for that value and return the value
|
|
20
|
+
#p name, name.class
|
|
21
|
+
const_set(name, [$1.to_i(16)].pack("U").freeze)
|
|
22
|
+
else # Raise an error for constants that are not Unicode.
|
|
23
|
+
raise NameError, "Uninitialized constant: Unicode::#{name}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end # module Unicode
|
|
28
|
+
end # module SE
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require "#{File.dirname(__FILE__)}/../../lib/se/aspect"
|
|
3
|
+
|
|
4
|
+
class AspectTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
class Foo < Array
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# need to do all tests at once or we'll have side effects between tests
|
|
11
|
+
def test_aspect
|
|
12
|
+
before_class = false
|
|
13
|
+
before_instance = false
|
|
14
|
+
after_instance = false
|
|
15
|
+
after_class = false
|
|
16
|
+
|
|
17
|
+
SE::Aspect.define do
|
|
18
|
+
with_class(Foo).before(:new) do |klass|
|
|
19
|
+
before_class = true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
with_instance_of(Foo).before(:empty?) do |instance|
|
|
23
|
+
before_instance = true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
with_class(Foo).after(:new) do |klass|
|
|
27
|
+
after_instance = true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
with_instance_of(Foo).after(:empty?) do |instance|
|
|
31
|
+
after_class = true
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Foo.new.empty?
|
|
36
|
+
assert_equal true, before_class, 'before class method failed'
|
|
37
|
+
assert_equal true, after_class, 'after class method failed'
|
|
38
|
+
assert_equal true, before_instance, 'before instance method failed'
|
|
39
|
+
assert_equal true, after_instance, 'after instance method failed'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require "#{File.dirname(__FILE__)}/../../../lib/se/core_ext/date"
|
|
3
|
+
|
|
4
|
+
class DateTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_days_in_year_for_normal_year
|
|
7
|
+
assert_equal 365, Date.parse('2009-01-01').days_in_year
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_days_in_year_for_leap_year
|
|
11
|
+
assert_equal 366, Date.parse('2000-01-01').days_in_year
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require "#{File.dirname(__FILE__)}/../../../lib/se/core_ext/regexp"
|
|
3
|
+
|
|
4
|
+
class RegexpTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_blank
|
|
7
|
+
assert_equal false, /abc/.blank?, 'blank? should always return false'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_include
|
|
11
|
+
assert_equal true, /a|b|c/.include?('a'), '/a|b|c/ should match a'
|
|
12
|
+
assert_equal false, /a|b|c/.include?('x'), '/a|b|c/ should not match a'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require "#{File.dirname(__FILE__)}/../../../lib/se/core_ext/retryable"
|
|
3
|
+
|
|
4
|
+
class RetryableTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_retryable_smoke_test
|
|
7
|
+
flag = true
|
|
8
|
+
Kernel.retryable(:tries => 2) do
|
|
9
|
+
if flag
|
|
10
|
+
flag = false
|
|
11
|
+
raise 'oops!'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require "#{File.dirname(__FILE__)}/../../lib/se/memory_profiler"
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
class MemoryProfilerTest < Test::Unit::TestCase
|
|
6
|
+
|
|
7
|
+
def teardown
|
|
8
|
+
FileUtils.rm(SE::MemoryProfiler.log_file_name)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_memory_profiler
|
|
12
|
+
SE::MemoryProfiler.start(:every=>1)
|
|
13
|
+
sleep 3
|
|
14
|
+
assert_equal true, File.exists?(SE::MemoryProfiler.log_file_name)
|
|
15
|
+
lines = File.open(SE::MemoryProfiler.log_file_name, 'r') {|f| f.readlines}
|
|
16
|
+
assert_equal 3, lines.grep(/Top 20/).size
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require "#{File.dirname(__FILE__)}/../../lib/se/sleeper"
|
|
3
|
+
|
|
4
|
+
class SleeperTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def teardown
|
|
7
|
+
Sleeper.send(:kill_all_sleepers)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_initialize_accepts_no_block
|
|
11
|
+
@sleeper = Sleeper.new(1)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_block_executed_multiple_times
|
|
15
|
+
wakes = 0
|
|
16
|
+
@sleeper = Sleeper.new(1) {wakes += 1}
|
|
17
|
+
sleep(3)
|
|
18
|
+
assert_equal 2, wakes
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_reset_invokes_after_reset_blocks
|
|
22
|
+
resets = 0
|
|
23
|
+
@sleeper = Sleeper.new(1)
|
|
24
|
+
@sleeper.after_reset {resets += 1}
|
|
25
|
+
@sleeper.reset
|
|
26
|
+
assert_equal 1, resets, 'Invalid number of resets'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_reset_prevents_after_wake_execution
|
|
30
|
+
@sleeper = Sleeper.new(2) {raise %q{after_wake shouldn't be called}}
|
|
31
|
+
3.times do
|
|
32
|
+
sleep(1)
|
|
33
|
+
@sleeper.reset
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_terminate_invokes_after_terminate_blocks
|
|
38
|
+
terminates = 0
|
|
39
|
+
sleeper = Sleeper.new(5)
|
|
40
|
+
sleeper.after_terminate {terminates += 1}
|
|
41
|
+
sleeper.terminate
|
|
42
|
+
assert_equal 1, terminates, 'Invalid number of terminates'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_signals_kills_sleeper_threads
|
|
46
|
+
count = Thread.list.size
|
|
47
|
+
Sleeper::OLD_HANDLERS.keys.each do |sig|
|
|
48
|
+
5.times {Sleeper.new(30)}
|
|
49
|
+
Process.kill(sig, $$)
|
|
50
|
+
assert_equal count, Thread.list.size, 'too many threads left'
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require "#{File.dirname(__FILE__)}/../../lib/se/timer"
|
|
3
|
+
|
|
4
|
+
class TimerTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_thread_safety
|
|
7
|
+
threads = []
|
|
8
|
+
3.times do
|
|
9
|
+
threads << Thread.new do
|
|
10
|
+
SE::Timer.enabled = true
|
|
11
|
+
SE::Timer.start(:thread)
|
|
12
|
+
sleep(1)
|
|
13
|
+
SE::Timer.end(:thread)
|
|
14
|
+
assert_equal 1, SE::Timer.stats[:thread][:calls], 'Incorrect call count'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
threads.each {|ii| ii.join}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class Base
|
|
21
|
+
def foo
|
|
22
|
+
:foo
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class Foo < Base
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_inherited_class_timing
|
|
30
|
+
SE::Timer.time_method(Base, :foo, 'base.foo')
|
|
31
|
+
SE::Timer.time_method(Foo, :foo, 'foo.foo' )
|
|
32
|
+
SE::Timer.enabled = true
|
|
33
|
+
Foo.new.foo
|
|
34
|
+
assert_equal ['base.foo','foo.foo'], SE::Timer.stats.keys.sort
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end # class TimerTest
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require "#{File.dirname(__FILE__)}/../../lib/se/unicode"
|
|
3
|
+
|
|
4
|
+
class UnicodeTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_unicode
|
|
7
|
+
assert_equal "a", "#{SE::Unicode::U0061}"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_excepton_raised_on_invalid_unicode_value
|
|
11
|
+
assert_raise NameError do
|
|
12
|
+
"#{SE::Unicode::U65}"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ss-se_gem
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Scott Steadman
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2009-07-
|
|
12
|
+
date: 2009-07-12 00:00:00 -07:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies: []
|
|
15
15
|
|
|
@@ -25,12 +25,25 @@ extra_rdoc_files:
|
|
|
25
25
|
files:
|
|
26
26
|
- lib/core_ext.rb
|
|
27
27
|
- lib/debug.rb
|
|
28
|
+
- lib/se/aspect.rb
|
|
28
29
|
- lib/se/core_ext/array.rb
|
|
30
|
+
- lib/se/core_ext/benchmark.rb
|
|
31
|
+
- lib/se/core_ext/date.rb
|
|
29
32
|
- lib/se/core_ext/dir.rb
|
|
30
|
-
- lib/se/core_ext/meta_class.rb
|
|
31
33
|
- lib/se/core_ext/object.rb
|
|
32
|
-
- lib/se/
|
|
34
|
+
- lib/se/core_ext/regexp.rb
|
|
35
|
+
- lib/se/core_ext/retryable.rb
|
|
36
|
+
- lib/se/core_ext/socket.rb
|
|
37
|
+
- lib/se/core_ext/yaml.rb
|
|
38
|
+
- lib/se/debug.rb
|
|
39
|
+
- lib/se/memory_profiler.rb
|
|
40
|
+
- lib/se/metaclass.rb
|
|
41
|
+
- lib/se/negator.rb
|
|
42
|
+
- lib/se/pager.rb
|
|
43
|
+
- lib/se/sleeper.rb
|
|
33
44
|
- lib/se/test/difference.rb
|
|
45
|
+
- lib/se/timer.rb
|
|
46
|
+
- lib/se/unicode.rb
|
|
34
47
|
- lib/se_gem.rb
|
|
35
48
|
- lib/test.rb
|
|
36
49
|
- LICENSE
|
|
@@ -62,7 +75,16 @@ signing_key:
|
|
|
62
75
|
specification_version: 3
|
|
63
76
|
summary: A collection of handy scripts.
|
|
64
77
|
test_files:
|
|
65
|
-
- test/se/
|
|
78
|
+
- test/se/sleeper_test.rb
|
|
79
|
+
- test/se/aspect_test.rb
|
|
80
|
+
- test/se/negator_test.rb
|
|
81
|
+
- test/se/unicode_test.rb
|
|
66
82
|
- test/se/core_ext/dir_test.rb
|
|
83
|
+
- test/se/core_ext/retryable_test.rb
|
|
67
84
|
- test/se/core_ext/object_test.rb
|
|
85
|
+
- test/se/core_ext/date_test.rb
|
|
68
86
|
- test/se/core_ext/array_test.rb
|
|
87
|
+
- test/se/core_ext/regexp_test.rb
|
|
88
|
+
- test/se/timer_test.rb
|
|
89
|
+
- test/se/memory_profiler_test.rb
|
|
90
|
+
- test/se/debug_test.rb
|
|
File without changes
|
|
File without changes
|