extlib 0.9.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of extlib might be problematic. Click here for more details.

@@ -0,0 +1,104 @@
1
+ class LazyArray # borrowed partially from StrokeDB
2
+ include Enumerable
3
+
4
+ # these methods should return self or nil
5
+ RETURN_SELF = [ :<<, :clear, :concat, :collect!, :each, :each_index,
6
+ :each_with_index, :insert, :map!, :push, :replace, :reject!,
7
+ :reverse!, :reverse_each, :sort!, :unshift ]
8
+
9
+ RETURN_SELF.each do |method|
10
+ class_eval <<-EOS, __FILE__, __LINE__
11
+ def #{method}(*args, &block)
12
+ lazy_load
13
+ results = @array.#{method}(*args, &block)
14
+ results.kind_of?(Array) ? self : results
15
+ end
16
+ EOS
17
+ end
18
+
19
+ (Array.public_instance_methods(false).map { |m| m.to_sym } - RETURN_SELF - [ :taguri= ]).each do |method|
20
+ class_eval <<-EOS, __FILE__, __LINE__
21
+ def #{method}(*args, &block)
22
+ lazy_load
23
+ @array.#{method}(*args, &block)
24
+ end
25
+ EOS
26
+ end
27
+
28
+ def replace(other)
29
+ mark_loaded
30
+ @array.replace(other.entries)
31
+ self
32
+ end
33
+
34
+ def clear
35
+ mark_loaded
36
+ @array.clear
37
+ self
38
+ end
39
+
40
+ def eql?(other)
41
+ lazy_load
42
+ @array.eql?(other.entries)
43
+ end
44
+
45
+ alias == eql?
46
+
47
+ def load_with(&block)
48
+ @load_with_proc = block
49
+ self
50
+ end
51
+
52
+ def loaded?
53
+ @loaded == true
54
+ end
55
+
56
+ def unload
57
+ clear
58
+ @loaded = false
59
+ self
60
+ end
61
+
62
+ def respond_to?(method, include_private = false)
63
+ super || @array.respond_to?(method, include_private)
64
+ end
65
+
66
+ def to_proc
67
+ @load_with_proc
68
+ end
69
+
70
+ private
71
+
72
+ def initialize(*args, &block)
73
+ @loaded = false
74
+ @load_with_proc = proc { |v| v }
75
+ @array = Array.new(*args, &block)
76
+ end
77
+
78
+ def initialize_copy(original)
79
+ @array = original.entries
80
+ load_with(&original)
81
+ mark_loaded if @array.any?
82
+ end
83
+
84
+ def lazy_load
85
+ return if loaded?
86
+ mark_loaded
87
+ @load_with_proc[self]
88
+ end
89
+
90
+ def mark_loaded
91
+ @loaded = true
92
+ end
93
+
94
+ # delegate any not-explicitly-handled methods to @array, if possible.
95
+ # this is handy for handling methods mixed-into Array like group_by
96
+ def method_missing(method, *args, &block)
97
+ if @array.respond_to?(method)
98
+ lazy_load
99
+ @array.send(method, *args, &block)
100
+ else
101
+ super
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,19 @@
1
+ class Module
2
+ def find_const(nested_name)
3
+ self.__nested_constants__[nested_name]
4
+ rescue NameError
5
+ Object::__nested_constants__[nested_name]
6
+ end
7
+
8
+ protected
9
+ def __nested_constants__
10
+ @__nested_constants__ ||= Hash.new do |h,k|
11
+ klass = self
12
+ k.split('::').each do |c|
13
+ klass = klass.const_get(c) unless c.empty?
14
+ end
15
+ h[k] = klass
16
+ end
17
+ end
18
+
19
+ end # class Module
@@ -0,0 +1,7 @@
1
+ class Object
2
+ unless instance_methods.include?('instance_variable_defined?')
3
+ def instance_variable_defined?(method)
4
+ instance_variables.include?(method.to_s)
5
+ end
6
+ end
7
+ end # class Object
@@ -0,0 +1,5 @@
1
+ class Pathname
2
+ def /(path)
3
+ (self + path).expand_path
4
+ end
5
+ end # class Pathname
@@ -0,0 +1,227 @@
1
+ require 'set'
2
+ require 'thread'
3
+
4
+ module Extlib
5
+ # ==== Notes
6
+ # Provides pooling support to class it got included in.
7
+ #
8
+ # Pooling of objects is a faster way of aquiring instances
9
+ # of objects compared to regular allocation and initialization
10
+ # because instances are keeped in memory reused.
11
+ #
12
+ # Classes that include Pooling module have re-defined new
13
+ # method that returns instances aquired from pool.
14
+ #
15
+ # Term resource is used for any type of poolable objects
16
+ # and should NOT be thought as DataMapper Resource or
17
+ # ActiveResource resource and such.
18
+ #
19
+ # In Data Objects connections are pooled so that it is
20
+ # unnecessary to allocate and initialize connection object
21
+ # each time connection is needed, like per request in a
22
+ # web application.
23
+ #
24
+ # Pool obviously has to be thread safe because state of
25
+ # object is reset when it is released.
26
+ module Pooling
27
+
28
+ def self.scavenger
29
+ @scavenger || begin
30
+ @scavenger = Thread.new do
31
+ loop do
32
+ lock.synchronize do
33
+ pools.each do |pool|
34
+ if pool.expired?
35
+ pool.lock.synchronize do
36
+ if pool.size == 0
37
+ pool.dispose
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ sleep(scavenger_interval)
44
+ end # loop
45
+ end
46
+
47
+ @scavenger.priority = -10
48
+ @scavenger
49
+ end
50
+ end
51
+
52
+ def self.pools
53
+ @pools ||= Set.new
54
+ end
55
+
56
+ def self.append_pool(pool)
57
+ lock.synchronize do
58
+ pools << pool
59
+ end
60
+ Extlib::Pooling::scavenger
61
+ end
62
+
63
+ def self.lock
64
+ @lock ||= Mutex.new
65
+ end
66
+
67
+ class CrossPoolError < StandardError
68
+ end
69
+
70
+ class OrphanedObjectError < StandardError
71
+ end
72
+
73
+ class ThreadStopError < StandardError
74
+ end
75
+
76
+ def self.included(target)
77
+ target.class_eval do
78
+ class << self
79
+ alias __new new
80
+ end
81
+
82
+ @__pools = Hash.new { |h,k| __pool_lock.synchronize { h[k] = Pool.new(target.pool_size, target, k) } }
83
+ @__pool_lock = Mutex.new
84
+
85
+ def self.__pool_lock
86
+ @__pool_lock
87
+ end
88
+
89
+ def self.new(*args)
90
+ @__pools[args].new
91
+ end
92
+
93
+ def self.__pools
94
+ @__pools
95
+ end
96
+
97
+ def self.pool_size
98
+ 1
99
+ end
100
+
101
+ def self.scavenge_interval
102
+ 10
103
+ end
104
+ end
105
+ end
106
+
107
+ def release
108
+ @__pool.release(self)
109
+ end
110
+
111
+ class Pool
112
+ def initialize(max_size, resource, args)
113
+ raise ArgumentError.new("+max_size+ should be a Fixnum but was #{max_size.inspect}") unless Fixnum === max_size
114
+ raise ArgumentError.new("+resource+ should be a Class but was #{resource.inspect}") unless Class === resource
115
+
116
+ @max_size = max_size
117
+ @resource = resource
118
+ @args = args
119
+
120
+ @available = []
121
+ @reserved = Set.new
122
+
123
+ Extlib::Pooling::append_pool(self)
124
+ end
125
+
126
+ def lock
127
+ @resource.__pool_lock
128
+ end
129
+
130
+ def scavenge_interval
131
+ @resource.scavenge_interval
132
+ end
133
+
134
+ def new
135
+ instance = nil
136
+
137
+ lock.synchronize do
138
+ instance = aquire
139
+ end
140
+
141
+ if instance.nil?
142
+ # Account for the current thread, and the pool scavenger.
143
+ if ThreadGroup::Default.list.size == 2 && @reserved.size >= @max_size
144
+ raise ThreadStopError.new(size)
145
+ else
146
+ sleep(0.01)
147
+ new
148
+ end
149
+ else
150
+ instance
151
+ end
152
+ end
153
+
154
+ def release(instance)
155
+ lock.synchronize do
156
+ raise OrphanedObjectError.new(instance) unless @reserved.delete?(instance)
157
+ instance.instance_variable_set(:@__pool, nil)
158
+ @available.push(instance)
159
+ end
160
+ nil
161
+ end
162
+
163
+ def size
164
+ @available.size + @reserved.size
165
+ end
166
+ alias length size
167
+
168
+ def inspect
169
+ "#<Extlib::Pooling::Pool<#{@resource.name}> available=#{@available.size} reserved=#{@reserved.size}>"
170
+ end
171
+
172
+ def flush!
173
+ lock.synchronize do
174
+ @available.each do |instance|
175
+ instance.dispose
176
+ end
177
+ @available.clear
178
+ end
179
+ end
180
+
181
+ def dispose
182
+ @resource.__pools.delete(@args)
183
+ !Extlib::Pooling::pools.delete?(self).nil?
184
+ end
185
+
186
+ def expired?
187
+ lock.synchronize do
188
+ @available.each do |instance|
189
+ if instance.instance_variable_get(:@__allocated_in_pool) + scavenge_interval < Time.now
190
+ instance.dispose
191
+ @available.delete(instance)
192
+ end
193
+ end
194
+
195
+ size == 0
196
+ end
197
+ end
198
+
199
+ private
200
+
201
+ def aquire
202
+ instance = if !@available.empty?
203
+ @available.pop
204
+ elsif size < @max_size
205
+ @resource.__new(*@args)
206
+ else
207
+ nil
208
+ end
209
+
210
+ if instance.nil?
211
+ instance
212
+ else
213
+ raise CrossPoolError.new(instance) if instance.instance_variable_get(:@__pool)
214
+ @reserved << instance
215
+ instance.instance_variable_set(:@__pool, self)
216
+ instance.instance_variable_set(:@__allocated_in_pool, Time.now)
217
+ instance
218
+ end
219
+ end
220
+ end
221
+
222
+ private
223
+ def self.scavenger_interval
224
+ 60
225
+ end
226
+ end # module Pooling
227
+ end # module Extlib
@@ -0,0 +1,45 @@
1
+ class String
2
+ # Overwrite this method to provide your own translations.
3
+ def self.translate(value)
4
+ translations[value] || value
5
+ end
6
+
7
+ def self.translations
8
+ @translations ||= {}
9
+ end
10
+
11
+ # Matches any whitespace (including newline) and replaces with a single space
12
+ #
13
+ # @example
14
+ # <<QUERY.compress_lines
15
+ # SELECT name
16
+ # FROM users
17
+ # QUERY
18
+ # => "SELECT name FROM users"
19
+ def compress_lines(spaced = true)
20
+ split($/).map { |line| line.strip }.join(spaced ? ' ' : '')
21
+ end
22
+
23
+ # Useful for heredocs - removes whitespace margin.
24
+ def margin(indicator = nil)
25
+ lines = self.dup.split($/)
26
+
27
+ min_margin = 0
28
+ lines.each do |line|
29
+ if line =~ /^(\s+)/ && (min_margin == 0 || $1.size < min_margin)
30
+ min_margin = $1.size
31
+ end
32
+ end
33
+ lines.map { |line| line.sub(/^\s{#{min_margin}}/, '') }.join($/)
34
+ end
35
+
36
+ # Formats String for easy translation. Replaces an arbitrary number of
37
+ # values using numeric identifier replacement.
38
+ #
39
+ # @example
40
+ # "%s %s %s" % %w(one two three) #=> "one two three"
41
+ # "%3$s %2$s %1$s" % %w(one two three) #=> "three two one"
42
+ def t(*values)
43
+ self.class::translate(self) % values
44
+ end
45
+ end # class String
@@ -0,0 +1,8 @@
1
+ class Struct
2
+ # Returns a hash containing the names and values for all instance variables in the Struct.
3
+ def attributes
4
+ h = {}
5
+ each_pair { |k,v| h[k] = v }
6
+ h
7
+ end
8
+ end # class Struct
@@ -0,0 +1,85 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe Object do
4
+ it 'should provide blank?' do
5
+ Object.new.should respond_to(:blank?)
6
+ end
7
+
8
+ it 'should be blank if it is nil' do
9
+ object = Object.new
10
+ class << object
11
+ def nil?; true end
12
+ end
13
+ object.should be_blank
14
+ end
15
+
16
+ it 'should be blank if it is empty' do
17
+ {}.should be_blank
18
+ [].should be_blank
19
+ end
20
+
21
+ it 'should not be blank if not nil or empty' do
22
+ Object.new.should_not be_blank
23
+ [nil].should_not be_blank
24
+ { nil => 0 }.should_not be_blank
25
+ end
26
+ end
27
+
28
+ describe Numeric do
29
+ it 'should provide blank?' do
30
+ 1.should respond_to(:blank?)
31
+ end
32
+
33
+ it 'should never be blank' do
34
+ 1.should_not be_blank
35
+ end
36
+ end
37
+
38
+ describe NilClass do
39
+ it 'should provide blank?' do
40
+ nil.should respond_to(:blank?)
41
+ end
42
+
43
+ it 'should always be blank' do
44
+ nil.should be_blank
45
+ end
46
+ end
47
+
48
+ describe TrueClass do
49
+ it 'should provide blank?' do
50
+ true.should respond_to(:blank?)
51
+ end
52
+
53
+ it 'should never be blank' do
54
+ true.should_not be_blank
55
+ end
56
+ end
57
+
58
+ describe FalseClass do
59
+ it 'should provide blank?' do
60
+ false.should respond_to(:blank?)
61
+ end
62
+
63
+ it 'should always be blank' do
64
+ false.should be_blank
65
+ end
66
+ end
67
+
68
+ describe String do
69
+ it 'should provide blank?' do
70
+ 'string'.should respond_to(:blank?)
71
+ end
72
+
73
+ it 'should be blank if empty' do
74
+ ''.should be_blank
75
+ end
76
+
77
+ it 'should be blank if it only contains whitespace' do
78
+ ' '.should be_blank
79
+ " \r \n \t ".should be_blank
80
+ end
81
+
82
+ it 'should not be blank if it contains non-whitespace' do
83
+ ' a '.should_not be_blank
84
+ end
85
+ end