sundbp-extlib 0.9.14

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.
Files changed (72) hide show
  1. data/.autotest +21 -0
  2. data/.document +5 -0
  3. data/.gitignore +22 -0
  4. data/LICENSE +47 -0
  5. data/README.rdoc +17 -0
  6. data/Rakefile +28 -0
  7. data/VERSION +1 -0
  8. data/extlib.gemspec +146 -0
  9. data/lib/extlib.rb +50 -0
  10. data/lib/extlib/array.rb +36 -0
  11. data/lib/extlib/assertions.rb +8 -0
  12. data/lib/extlib/blank.rb +89 -0
  13. data/lib/extlib/boolean.rb +11 -0
  14. data/lib/extlib/byte_array.rb +6 -0
  15. data/lib/extlib/class.rb +177 -0
  16. data/lib/extlib/datetime.rb +29 -0
  17. data/lib/extlib/dictionary.rb +433 -0
  18. data/lib/extlib/hash.rb +442 -0
  19. data/lib/extlib/hook.rb +403 -0
  20. data/lib/extlib/inflection.rb +440 -0
  21. data/lib/extlib/lazy_array.rb +451 -0
  22. data/lib/extlib/lazy_module.rb +18 -0
  23. data/lib/extlib/logger.rb +198 -0
  24. data/lib/extlib/mash.rb +155 -0
  25. data/lib/extlib/module.rb +47 -0
  26. data/lib/extlib/nil.rb +5 -0
  27. data/lib/extlib/numeric.rb +5 -0
  28. data/lib/extlib/object.rb +175 -0
  29. data/lib/extlib/object_space.rb +13 -0
  30. data/lib/extlib/pathname.rb +20 -0
  31. data/lib/extlib/pooling.rb +235 -0
  32. data/lib/extlib/rubygems.rb +38 -0
  33. data/lib/extlib/simple_set.rb +66 -0
  34. data/lib/extlib/string.rb +176 -0
  35. data/lib/extlib/struct.rb +17 -0
  36. data/lib/extlib/symbol.rb +21 -0
  37. data/lib/extlib/time.rb +43 -0
  38. data/lib/extlib/virtual_file.rb +10 -0
  39. data/spec/array_spec.rb +39 -0
  40. data/spec/blank_spec.rb +85 -0
  41. data/spec/byte_array_spec.rb +7 -0
  42. data/spec/class_spec.rb +157 -0
  43. data/spec/datetime_spec.rb +22 -0
  44. data/spec/hash_spec.rb +537 -0
  45. data/spec/hook_spec.rb +1234 -0
  46. data/spec/inflection/plural_spec.rb +564 -0
  47. data/spec/inflection/singular_spec.rb +497 -0
  48. data/spec/inflection_extras_spec.rb +110 -0
  49. data/spec/lazy_array_spec.rb +1957 -0
  50. data/spec/lazy_module_spec.rb +38 -0
  51. data/spec/mash_spec.rb +311 -0
  52. data/spec/module_spec.rb +70 -0
  53. data/spec/object_space_spec.rb +9 -0
  54. data/spec/object_spec.rb +114 -0
  55. data/spec/pooling_spec.rb +511 -0
  56. data/spec/rcov.opts +6 -0
  57. data/spec/simple_set_spec.rb +57 -0
  58. data/spec/spec.opts +4 -0
  59. data/spec/spec_helper.rb +10 -0
  60. data/spec/string_spec.rb +221 -0
  61. data/spec/struct_spec.rb +12 -0
  62. data/spec/symbol_spec.rb +8 -0
  63. data/spec/time_spec.rb +29 -0
  64. data/spec/try_call_spec.rb +73 -0
  65. data/spec/try_dup_spec.rb +45 -0
  66. data/spec/virtual_file_spec.rb +21 -0
  67. data/tasks/ci.rake +1 -0
  68. data/tasks/metrics.rake +36 -0
  69. data/tasks/spec.rake +25 -0
  70. data/tasks/yard.rake +9 -0
  71. data/tasks/yardstick.rake +19 -0
  72. metadata +180 -0
@@ -0,0 +1,13 @@
1
+ module ObjectSpace
2
+
3
+ class << self
4
+
5
+ # @return [Array<Class>] All the classes in the object space.
6
+ def classes
7
+ klasses = []
8
+ ObjectSpace.each_object(Class) {|o| klasses << o}
9
+ klasses
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,20 @@
1
+ class Pathname
2
+ # Append path segments and expand to absolute path
3
+ #
4
+ # file = Pathname(Dir.pwd) / "subdir1" / :subdir2 / "filename.ext"
5
+ #
6
+ # @param [Pathname, String, #to_s] path path segment to concatenate with receiver
7
+ #
8
+ # @return [Pathname]
9
+ # receiver with _path_ appended and expanded to an absolute path
10
+ #
11
+ # @api public
12
+ def /(path)
13
+ (self + path).expand_path
14
+ end
15
+
16
+ # alias to_s to to_str when to_str not defined
17
+ unless public_instance_methods(false).any? { |m| m.to_sym == :to_str }
18
+ alias to_str to_s
19
+ end
20
+ end # class Pathname
@@ -0,0 +1,235 @@
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 acquired 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
+ defined?(@scavenger) && !@scavenger.nil? && @scavenger.alive?
30
+ end
31
+
32
+ def self.scavenger
33
+ unless scavenger?
34
+ @scavenger = Thread.new do
35
+ running = true
36
+ while running do
37
+ # Sleep before we actually start doing anything.
38
+ # Otherwise we might clean up something we just made
39
+ sleep(scavenger_interval)
40
+
41
+ lock.synchronize do
42
+ pools.each do |pool|
43
+ # This is a useful check, but non-essential, and right now it breaks lots of stuff.
44
+ # if pool.expired?
45
+ pool.lock.synchronize do
46
+ if pool.expired?
47
+ pool.dispose
48
+ end
49
+ end
50
+ # end
51
+ end
52
+
53
+ # The pool is empty, we stop the scavenger
54
+ # It wil be restarted if new resources are added again
55
+ if pools.empty?
56
+ running = false
57
+ end
58
+ end
59
+ end # loop
60
+ end
61
+ end
62
+
63
+ @scavenger.priority = -10
64
+ @scavenger
65
+ end
66
+
67
+ def self.pools
68
+ @pools ||= Set.new
69
+ end
70
+
71
+ def self.append_pool(pool)
72
+ lock.synchronize do
73
+ pools << pool
74
+ end
75
+ Extlib::Pooling.scavenger
76
+ end
77
+
78
+ def self.lock
79
+ @lock ||= Mutex.new
80
+ end
81
+
82
+ class InvalidResourceError < StandardError
83
+ end
84
+
85
+ def self.included(target)
86
+ target.class_eval do
87
+ class << self
88
+ alias __new new
89
+ end
90
+
91
+ @__pools = {}
92
+ @__pool_lock = Mutex.new
93
+ @__pool_wait = ConditionVariable.new
94
+
95
+ def self.__pool_lock
96
+ @__pool_lock
97
+ end
98
+
99
+ def self.__pool_wait
100
+ @__pool_wait
101
+ end
102
+
103
+ def self.new(*args)
104
+ (@__pools[args] ||= __pool_lock.synchronize { Pool.new(self.pool_size, self, args) }).new
105
+ end
106
+
107
+ def self.__pools
108
+ @__pools
109
+ end
110
+
111
+ def self.pool_size
112
+ 8
113
+ end
114
+ end
115
+ end
116
+
117
+ def release
118
+ @__pool.release(self) unless @__pool.nil?
119
+ end
120
+
121
+ def detach
122
+ @__pool.delete(self) unless @__pool.nil?
123
+ end
124
+
125
+ class Pool
126
+ attr_reader :available
127
+ attr_reader :used
128
+
129
+ def initialize(max_size, resource, args)
130
+ raise ArgumentError.new("+max_size+ should be a Fixnum but was #{max_size.inspect}") unless Fixnum === max_size
131
+ raise ArgumentError.new("+resource+ should be a Class but was #{resource.inspect}") unless Class === resource
132
+
133
+ @max_size = max_size
134
+ @resource = resource
135
+ @args = args
136
+
137
+ @available = []
138
+ @used = {}
139
+ Extlib::Pooling.append_pool(self)
140
+ end
141
+
142
+ def lock
143
+ @resource.__pool_lock
144
+ end
145
+
146
+ def wait
147
+ @resource.__pool_wait
148
+ end
149
+
150
+ def scavenge_interval
151
+ @resource.scavenge_interval
152
+ end
153
+
154
+ def new
155
+ instance = nil
156
+ begin
157
+ lock.synchronize do
158
+ if @available.size > 0
159
+ instance = @available.pop
160
+ @used[instance.object_id] = instance
161
+ elsif @used.size < @max_size
162
+ instance = @resource.__new(*@args)
163
+ raise InvalidResourceError.new("#{@resource} constructor created a nil object") if instance.nil?
164
+ raise InvalidResourceError.new("#{instance} is already part of the pool") if @used.include? instance
165
+ instance.instance_variable_set(:@__pool, self)
166
+ instance.instance_variable_set(:@__allocated_in_pool, Time.now)
167
+ @used[instance.object_id] = instance
168
+ else
169
+ # Wait for another thread to release an instance.
170
+ # If we exhaust the pool and don't release the active instance,
171
+ # we'll wait here forever, so it's *very* important to always
172
+ # release your services and *never* exhaust the pool within
173
+ # a single thread.
174
+ wait.wait(lock)
175
+ end
176
+ end
177
+ end until instance
178
+ instance
179
+ end
180
+
181
+ def release(instance)
182
+ lock.synchronize do
183
+ instance.instance_variable_set(:@__allocated_in_pool, Time.now)
184
+ @used.delete(instance.object_id)
185
+ @available.push(instance)
186
+ wait.signal
187
+ end
188
+ nil
189
+ end
190
+
191
+ def delete(instance)
192
+ lock.synchronize do
193
+ instance.instance_variable_set(:@__pool, nil)
194
+ @used.delete(instance.object_id)
195
+ wait.signal
196
+ end
197
+ nil
198
+ end
199
+
200
+ def size
201
+ @used.size + @available.size
202
+ end
203
+ alias length size
204
+
205
+ def inspect
206
+ "#<Extlib::Pooling::Pool<#{@resource.name}> available=#{@available.size} used=#{@used.size} size=#{@max_size}>"
207
+ end
208
+
209
+ def flush!
210
+ @available.pop.dispose until @available.empty?
211
+ end
212
+
213
+ def dispose
214
+ flush!
215
+ @resource.__pools.delete(@args)
216
+ !Extlib::Pooling.pools.delete?(self).nil?
217
+ end
218
+
219
+ def expired?
220
+ @available.each do |instance|
221
+ if Extlib.exiting || instance.instance_variable_get(:@__allocated_in_pool) + Extlib::Pooling.scavenger_interval <= (Time.now + 0.02)
222
+ instance.dispose
223
+ @available.delete(instance)
224
+ end
225
+ end
226
+ size == 0
227
+ end
228
+
229
+ end
230
+
231
+ def self.scavenger_interval
232
+ 60
233
+ end
234
+ end # module Pooling
235
+ end # module Extlib
@@ -0,0 +1,38 @@
1
+ # this is a temporary workaround until rubygems Does the Right thing here
2
+ require 'rubygems'
3
+ module Gem
4
+ class SourceIndex
5
+
6
+ # This is resolved in 1.1
7
+ if Version.new(RubyGemsVersion) < Version.new("1.1")
8
+
9
+ # Overwrite this so that a gem of the same name and version won't push one
10
+ # from the gems directory out entirely.
11
+ #
12
+ # @param gem_spec<Gem::Specification> The specification of the gem to add.
13
+ def add_spec(gem_spec)
14
+ unless gem_spec.instance_variable_get("@loaded_from") &&
15
+ @gems[gem_spec.full_name].is_a?(Gem::Specification) &&
16
+ @gems[gem_spec.full_name].installation_path ==
17
+ File.join(defined?(Merb) && Merb.respond_to?(:root) ? Merb.root : Dir.pwd,"gems")
18
+
19
+ @gems[gem_spec.full_name] = gem_spec
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ class Specification
28
+
29
+ # Overwrite this so that gems in the gems directory get preferred over gems
30
+ # from any other location. If there are two gems of different versions in
31
+ # the gems directory, the later one will load as usual.
32
+ #
33
+ # @return [Array<Array>] The object used for sorting gem specs.
34
+ def sort_obj
35
+ [@name, installation_path == File.join(defined?(Merb) && Merb.respond_to?(:root) ? Merb.root : Dir.pwd,"gems") ? 1 : -1, @version.to_ints, @new_platform == Gem::Platform::RUBY ? -1 : 1]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,66 @@
1
+ module Extlib
2
+ # Simple set implementation
3
+ # on top of Hash with merging support.
4
+ #
5
+ # In particular this is used to store
6
+ # a set of callable actions of controller.
7
+ class SimpleSet < Hash
8
+
9
+ ##
10
+ # Create a new SimpleSet containing the unique members of _arr_
11
+ #
12
+ # @param [Array] arr Initial set values.
13
+ #
14
+ # @return [Array] The array the Set was initialized with
15
+ #
16
+ # @api public
17
+ def initialize(arr = [])
18
+ Array(arr).each {|x| self[x] = true}
19
+ end
20
+
21
+ ##
22
+ # Add a value to the set, and return it
23
+ #
24
+ # @param [Object] value Value to add to set.
25
+ #
26
+ # @return [SimpleSet] Receiver
27
+ #
28
+ # @api public
29
+ def <<(value)
30
+ self[value] = true
31
+ self
32
+ end
33
+
34
+ ##
35
+ # Merge _arr_ with receiver, producing the union of receiver & _arr_
36
+ #
37
+ # s = Extlib::SimpleSet.new([:a, :b, :c])
38
+ # s.merge([:c, :d, :e, f]) #=> #<SimpleSet: {:e, :c, :f, :a, :d, :b}>
39
+ #
40
+ # @param [Array] arr Values to merge with set.
41
+ #
42
+ # @return [SimpleSet] The set after the Array was merged in.
43
+ #
44
+ # @api public
45
+ def merge(arr)
46
+ super(arr.inject({}) {|s,x| s[x] = true; s })
47
+ end
48
+
49
+ ##
50
+ # Get a human readable version of the set.
51
+ #
52
+ # s = SimpleSet.new([:a, :b, :c])
53
+ # s.inspect #=> "#<SimpleSet: {:c, :a, :b}>"
54
+ #
55
+ # @return [String] A human readable version of the set.
56
+ #
57
+ # @api public
58
+ def inspect
59
+ "#<SimpleSet: {#{keys.map {|x| x.inspect}.join(", ")}}>"
60
+ end
61
+
62
+ # def to_a
63
+ alias_method :to_a, :keys
64
+
65
+ end # SimpleSet
66
+ end # Merb
@@ -0,0 +1,176 @@
1
+ require "pathname"
2
+
3
+ class String
4
+ ##
5
+ # Escape all regexp special characters.
6
+ #
7
+ # "*?{}.".escape_regexp #=> "\\*\\?\\{\\}\\."
8
+ #
9
+ # @return [String] Receiver with all regexp special characters escaped.
10
+ #
11
+ # @api public
12
+ def escape_regexp
13
+ Regexp.escape self
14
+ end
15
+
16
+ ##
17
+ # Unescape all regexp special characters.
18
+ #
19
+ # "\\*\\?\\{\\}\\.".unescape_regexp #=> "*?{}."
20
+ #
21
+ # @return [String] Receiver with all regexp special characters unescaped.
22
+ #
23
+ # @api public
24
+ def unescape_regexp
25
+ self.gsub(/\\([\.\?\|\(\)\[\]\{\}\^\$\*\+\-])/, '\1')
26
+ end
27
+
28
+ ##
29
+ # Convert to snake case.
30
+ #
31
+ # "FooBar".snake_case #=> "foo_bar"
32
+ # "HeadlineCNNNews".snake_case #=> "headline_cnn_news"
33
+ # "CNN".snake_case #=> "cnn"
34
+ #
35
+ # @return [String] Receiver converted to snake case.
36
+ #
37
+ # @api public
38
+ def snake_case
39
+ return downcase if match(/\A[A-Z]+\z/)
40
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
41
+ gsub(/([a-z])([A-Z])/, '\1_\2').
42
+ downcase
43
+ end
44
+
45
+ ##
46
+ # Convert to camel case.
47
+ #
48
+ # "foo_bar".camel_case #=> "FooBar"
49
+ #
50
+ # @return [String] Receiver converted to camel case.
51
+ #
52
+ # @api public
53
+ def camel_case
54
+ return self if self !~ /_/ && self =~ /[A-Z]+.*/
55
+ split('_').map{|e| e.capitalize}.join
56
+ end
57
+
58
+ ##
59
+ # Convert a path string to a constant name.
60
+ #
61
+ # "merb/core_ext/string".to_const_string #=> "Merb::CoreExt::String"
62
+ #
63
+ # @return [String] Receiver converted to a constant name.
64
+ #
65
+ # @api public
66
+ def to_const_string
67
+ gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
68
+ end
69
+
70
+ ##
71
+ # Convert a constant name to a path, assuming a conventional structure.
72
+ #
73
+ # "FooBar::Baz".to_const_path # => "foo_bar/baz"
74
+ #
75
+ # @return [String] Path to the file containing the constant named by receiver
76
+ # (constantized string), assuming a conventional structure.
77
+ #
78
+ # @api public
79
+ def to_const_path
80
+ snake_case.gsub(/::/, "/")
81
+ end
82
+
83
+ ##
84
+ # Join with _o_ as a file path.
85
+ #
86
+ # "merb"/"core_ext" #=> "merb/core_ext"
87
+ #
88
+ # @param [String] o Path component to join with receiver.
89
+ #
90
+ # @return [String] Receiver joined with o as a file path.
91
+ #
92
+ # @api public
93
+ def /(o)
94
+ File.join(self, o.to_s)
95
+ end
96
+
97
+ ##
98
+ # Calculate a relative path *from* _other_.
99
+ #
100
+ # "/opt/local/lib".relative_path_from("/opt/local/lib/ruby/site_ruby") # => "../.."
101
+ #
102
+ # @param [String] other Base path to calculate *from*.
103
+ #
104
+ # @return [String] Relative path from _other_ to receiver.
105
+ #
106
+ # @api public
107
+ def relative_path_from(other)
108
+ Pathname.new(self).relative_path_from(Pathname.new(other)).to_s
109
+ end
110
+
111
+ # Overwrite this method to provide your own translations.
112
+ def self.translate(value)
113
+ translations[value] || value
114
+ end
115
+
116
+ def self.translations
117
+ @translations ||= {}
118
+ end
119
+
120
+ ##
121
+ # Replace sequences of whitespace (including newlines) with either
122
+ # a single space or remove them entirely (according to param _spaced_)
123
+ #
124
+ # <<QUERY.compress_lines
125
+ # SELECT name
126
+ # FROM users
127
+ # QUERY => "SELECT name FROM users"
128
+ #
129
+ # @param [TrueClass, FalseClass] spaced (default=true)
130
+ # Determines whether returned string has whitespace collapsed or removed
131
+ #
132
+ # @return [String] Receiver with whitespace (including newlines) replaced
133
+ #
134
+ # @api public
135
+ def compress_lines(spaced = true)
136
+ split($/).map { |line| line.strip }.join(spaced ? ' ' : '')
137
+ end
138
+
139
+ ##
140
+ # Remove whitespace margin.
141
+ #
142
+ # @param [Object] indicator ???
143
+ #
144
+ # @return [String] receiver with whitespace margin removed
145
+ #
146
+ # @api public
147
+ def margin(indicator = nil)
148
+ lines = self.dup.split($/)
149
+
150
+ min_margin = 0
151
+ lines.each do |line|
152
+ if line =~ /^(\s+)/ && (min_margin == 0 || $1.size < min_margin)
153
+ min_margin = $1.size
154
+ end
155
+ end
156
+ lines.map { |line| line.sub(/^\s{#{min_margin}}/, '') }.join($/)
157
+ end
158
+
159
+ ##
160
+ # Formats String for easy translation. Replaces an arbitrary number of
161
+ # values using numeric identifier replacement.
162
+ #
163
+ # "%s %s %s" % %w(one two three) #=> "one two three"
164
+ # "%3$s %2$s %1$s" % %w(one two three) #=> "three two one"
165
+ #
166
+ # @param [#to_s] values
167
+ # A list of values to translate and interpolate into receiver
168
+ #
169
+ # @return [String]
170
+ # Receiver translated with values translated and interpolated positionally
171
+ #
172
+ # @api public
173
+ def t(*values)
174
+ self.class::translate(self) % values.collect! { |value| value.frozen? ? value : self.class::translate(value.to_s) }
175
+ end
176
+ end # class String