thorero 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/History.txt +1 -0
  2. data/LICENSE +20 -0
  3. data/Manifest +45 -0
  4. data/Manifest.txt +29 -0
  5. data/README.txt +3 -0
  6. data/Rakefile +180 -0
  7. data/lib/extlib.rb +32 -0
  8. data/lib/extlib/assertions.rb +8 -0
  9. data/lib/extlib/blank.rb +42 -0
  10. data/lib/extlib/class.rb +175 -0
  11. data/lib/extlib/hash.rb +410 -0
  12. data/lib/extlib/hook.rb +366 -0
  13. data/lib/extlib/inflection.rb +141 -0
  14. data/lib/extlib/lazy_array.rb +106 -0
  15. data/lib/extlib/logger.rb +202 -0
  16. data/lib/extlib/mash.rb +143 -0
  17. data/lib/extlib/module.rb +37 -0
  18. data/lib/extlib/object.rb +165 -0
  19. data/lib/extlib/object_space.rb +13 -0
  20. data/lib/extlib/pathname.rb +5 -0
  21. data/lib/extlib/pooling.rb +233 -0
  22. data/lib/extlib/rubygems.rb +38 -0
  23. data/lib/extlib/simple_set.rb +39 -0
  24. data/lib/extlib/string.rb +132 -0
  25. data/lib/extlib/struct.rb +8 -0
  26. data/lib/extlib/tasks/release.rb +11 -0
  27. data/lib/extlib/time.rb +12 -0
  28. data/lib/extlib/version.rb +3 -0
  29. data/lib/extlib/virtual_file.rb +10 -0
  30. data/spec/blank_spec.rb +85 -0
  31. data/spec/hash_spec.rb +524 -0
  32. data/spec/hook_spec.rb +1198 -0
  33. data/spec/inflection_spec.rb +50 -0
  34. data/spec/lazy_array_spec.rb +896 -0
  35. data/spec/mash_spec.rb +244 -0
  36. data/spec/module_spec.rb +58 -0
  37. data/spec/object_space_spec.rb +9 -0
  38. data/spec/object_spec.rb +98 -0
  39. data/spec/pooling_spec.rb +486 -0
  40. data/spec/simple_set_spec.rb +26 -0
  41. data/spec/spec_helper.rb +8 -0
  42. data/spec/string_spec.rb +200 -0
  43. data/spec/struct_spec.rb +12 -0
  44. data/spec/time_spec.rb +16 -0
  45. data/spec/virtual_file_spec.rb +21 -0
  46. data/thorero.gemspec +147 -0
  47. metadata +146 -0
@@ -0,0 +1,143 @@
1
+ # This class has dubious semantics and we only have it so that people can write
2
+ # params[:key] instead of params['key'].
3
+ class Mash < Hash
4
+
5
+ # @param constructor<Object>
6
+ # The default value for the mash. Defaults to an empty hash.
7
+ #
8
+ # @details [Alternatives]
9
+ # If constructor is a Hash, a new mash will be created based on the keys of
10
+ # the hash and no default value will be set.
11
+ def initialize(constructor = {})
12
+ if constructor.is_a?(Hash)
13
+ super()
14
+ update(constructor)
15
+ else
16
+ super(constructor)
17
+ end
18
+ end
19
+
20
+ # @param key<Object> The default value for the mash. Defaults to nil.
21
+ #
22
+ # @details [Alternatives]
23
+ # If key is a Symbol and it is a key in the mash, then the default value will
24
+ # be set to the value matching the key.
25
+ def default(key = nil)
26
+ if key.is_a?(Symbol) && include?(key = key.to_s)
27
+ self[key]
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
34
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
35
+
36
+ # @param key<Object> The key to set.
37
+ # @param value<Object>
38
+ # The value to set the key to.
39
+ #
40
+ # @see Mash#convert_key
41
+ # @see Mash#convert_value
42
+ def []=(key, value)
43
+ regular_writer(convert_key(key), convert_value(value))
44
+ end
45
+
46
+ # @param other_hash<Hash>
47
+ # A hash to update values in the mash with. The keys and the values will be
48
+ # converted to Mash format.
49
+ #
50
+ # @return <Mash> The updated mash.
51
+ def update(other_hash)
52
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
53
+ self
54
+ end
55
+
56
+ alias_method :merge!, :update
57
+
58
+ # @param key<Object> The key to check for. This will be run through convert_key.
59
+ #
60
+ # @return <TrueClass, FalseClass> True if the key exists in the mash.
61
+ def key?(key)
62
+ super(convert_key(key))
63
+ end
64
+
65
+ # def include? def has_key? def member?
66
+ alias_method :include?, :key?
67
+ alias_method :has_key?, :key?
68
+ alias_method :member?, :key?
69
+
70
+ # @param key<Object> The key to fetch. This will be run through convert_key.
71
+ # @param *extras<Array> Default value.
72
+ #
73
+ # @return <Object> The value at key or the default value.
74
+ def fetch(key, *extras)
75
+ super(convert_key(key), *extras)
76
+ end
77
+
78
+ # @param *indices<Array>
79
+ # The keys to retrieve values for. These will be run through +convert_key+.
80
+ #
81
+ # @return <Array> The values at each of the provided keys
82
+ def values_at(*indices)
83
+ indices.collect {|key| self[convert_key(key)]}
84
+ end
85
+
86
+ # @return <Mash> A duplicate of this mash.
87
+ def dup
88
+ Mash.new(self)
89
+ end
90
+
91
+ # @param hash<Hash> The hash to merge with the mash.
92
+ #
93
+ # @return <Mash> A new mash with the hash values merged in.
94
+ def merge(hash)
95
+ self.dup.update(hash)
96
+ end
97
+
98
+ # @param key<Object>
99
+ # The key to delete from the mash.\
100
+ def delete(key)
101
+ super(convert_key(key))
102
+ end
103
+
104
+ # Used to provide the same interface as Hash.
105
+ #
106
+ # @return <Mash> This mash unchanged.
107
+ def stringify_keys!; self end
108
+
109
+ # @return <Hash> The mash as a Hash with string keys.
110
+ def to_hash
111
+ Hash.new(default).merge(self)
112
+ end
113
+
114
+ protected
115
+ # @param key<Object> The key to convert.
116
+ #
117
+ # @param <Object>
118
+ # The converted key. If the key was a symbol, it will be converted to a
119
+ # string.
120
+ #
121
+ # @api private
122
+ def convert_key(key)
123
+ key.kind_of?(Symbol) ? key.to_s : key
124
+ end
125
+
126
+ # @param value<Object> The value to convert.
127
+ #
128
+ # @return <Object>
129
+ # The converted value. A Hash or an Array of hashes, will be converted to
130
+ # their Mash equivalents.
131
+ #
132
+ # @api private
133
+ def convert_value(value)
134
+ case value
135
+ when Hash
136
+ value.to_mash
137
+ when Array
138
+ value.collect { |e| convert_value(e) }
139
+ else
140
+ value
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,37 @@
1
+ class Module
2
+ def find_const(const_name)
3
+ if const_name[0..1] == '::'
4
+ Object.find_const(const_name[2..-1])
5
+ else
6
+ nested_const_lookup(const_name)
7
+ end
8
+ end
9
+
10
+ private
11
+
12
+ # Doesn't do any caching since constants can change with remove_const
13
+ def nested_const_lookup(const_name)
14
+ constants = [ Object ]
15
+
16
+ unless self == Object
17
+ self.name.split('::').each do |part|
18
+ constants.unshift(constants.first.const_get(part))
19
+ end
20
+ end
21
+
22
+ parts = const_name.split('::')
23
+
24
+ # from most to least specific constant, use each as a base and try
25
+ # to find a constant with the name const_name within them
26
+ constants.each do |const|
27
+ # return the nested constant if available
28
+ return const if parts.all? do |part|
29
+ const = const.const_defined?(part) ? const.const_get(part) : nil
30
+ end
31
+ end
32
+
33
+ # if we get this far then the nested constant was not found
34
+ raise NameError, "uninitialized constant #{const_name}"
35
+ end
36
+
37
+ end # class Module
@@ -0,0 +1,165 @@
1
+ class Object
2
+ # Extracts the singleton class, so that metaprogramming can be done on it.
3
+ #
4
+ # @return <Class> The meta class.
5
+ #
6
+ # @example [Setup]
7
+ # class MyString < String; end
8
+ #
9
+ # MyString.instance_eval do
10
+ # define_method :foo do
11
+ # puts self
12
+ # end
13
+ # end
14
+ #
15
+ # MyString.meta_class.instance_eval do
16
+ # define_method :bar do
17
+ # puts self
18
+ # end
19
+ # end
20
+ #
21
+ # def String.add_meta_var(var)
22
+ # self.meta_class.instance_eval do
23
+ # define_method var do
24
+ # puts "HELLO"
25
+ # end
26
+ # end
27
+ # end
28
+ #
29
+ # @example
30
+ # MyString.new("Hello").foo #=> "Hello"
31
+ # @example
32
+ # MyString.new("Hello").bar
33
+ # #=> NoMethodError: undefined method `bar' for "Hello":MyString
34
+ # @example
35
+ # MyString.foo
36
+ # #=> NoMethodError: undefined method `foo' for MyString:Class
37
+ # @example
38
+ # MyString.bar
39
+ # #=> MyString
40
+ # @example
41
+ # String.bar
42
+ # #=> NoMethodError: undefined method `bar' for String:Class
43
+ # @example
44
+ # MyString.add_meta_var(:x)
45
+ # MyString.x #=> HELLO
46
+ #
47
+ # @details [Description of Examples]
48
+ # As you can see, using #meta_class allows you to execute code (and here,
49
+ # define a method) on the metaclass itself. It also allows you to define
50
+ # class methods that can be run on subclasses, and then be able to execute
51
+ # code on the metaclass of the subclass (here MyString).
52
+ #
53
+ # In this case, we were able to define a class method (add_meta_var) on
54
+ # String that was executable by the MyString subclass. It was then able to
55
+ # define a method on the subclass by adding it to the MyString metaclass.
56
+ #
57
+ # For more information, you can check out _why's excellent article at:
58
+ # http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
59
+ def meta_class() class << self; self end end
60
+
61
+ # @return <TrueClass, FalseClass>
62
+ # True if the empty? is true or if the object responds to strip (e.g. a
63
+ # String) and strip.empty? is true, or if !self is true.
64
+ #
65
+ # @example [].blank? #=> true
66
+ # @example [1].blank? #=> false
67
+ # @example [nil].blank? #=> false
68
+ # @example nil.blank? #=> true
69
+ # @example true.blank? #=> false
70
+ # @example false.blank? #=> true
71
+ # @example "".blank? #=> true
72
+ # @example " ".blank? #=> true
73
+ # @example " hey ho ".blank? #=> false
74
+ def blank?
75
+ if respond_to?(:empty?) && respond_to?(:strip)
76
+ empty? or strip.empty?
77
+ elsif respond_to?(:empty?)
78
+ empty?
79
+ else
80
+ !self
81
+ end
82
+ end
83
+
84
+ # @param name<String> The name of the constant to get, e.g. "Merb::Router".
85
+ #
86
+ # @return <Object> The constant corresponding to the name.
87
+ def full_const_get(name)
88
+ list = name.split("::")
89
+ list.shift if list.first.blank?
90
+ obj = self
91
+ list.each do |x|
92
+ # This is required because const_get tries to look for constants in the
93
+ # ancestor chain, but we only want constants that are HERE
94
+ obj = obj.const_defined?(x) ? obj.const_get(x) : obj.const_missing(x)
95
+ end
96
+ obj
97
+ end
98
+
99
+ # @param name<String> The name of the constant to get, e.g. "Merb::Router".
100
+ # @param value<Object> The value to assign to the constant.
101
+ #
102
+ # @return <Object> The constant corresponding to the name.
103
+ def full_const_set(name, value)
104
+ list = name.split("::")
105
+ toplevel = list.first.blank?
106
+ list.shift if toplevel
107
+ last = list.pop
108
+ obj = list.empty? ? Object : Object.full_const_get(list.join("::"))
109
+ obj.const_set(last, value) if obj && !obj.const_defined?(last)
110
+ end
111
+
112
+ # Defines module from a string name (e.g. Foo::Bar::Baz)
113
+ # If module already exists, no exception raised.
114
+ #
115
+ # @param name<String> The name of the full module name to make
116
+ #
117
+ # @return <NilClass>
118
+ def make_module(str)
119
+ mod = str.split("::")
120
+ start = mod.map {|x| "module #{x}"}.join("; ")
121
+ ender = (["end"] * mod.size).join("; ")
122
+ self.class_eval <<-HERE
123
+ #{start}
124
+ #{ender}
125
+ HERE
126
+ end
127
+
128
+ # @param duck<Symbol, Class, Array> The thing to compare the object to.
129
+ #
130
+ # @note
131
+ # The behavior of the method depends on the type of duck as follows:
132
+ # Symbol:: Check whether the object respond_to?(duck).
133
+ # Class:: Check whether the object is_a?(duck).
134
+ # Array::
135
+ # Check whether the object quacks_like? at least one of the options in the
136
+ # array.
137
+ #
138
+ # @return <TrueClass, FalseClass>
139
+ # True if the object quacks like duck.
140
+ def quacks_like?(duck)
141
+ case duck
142
+ when Symbol
143
+ self.respond_to?(duck)
144
+ when Class
145
+ self.is_a?(duck)
146
+ when Array
147
+ duck.any? {|d| self.quacks_like?(d) }
148
+ else
149
+ false
150
+ end
151
+ end
152
+
153
+ # @param arrayish<#include?> Container to check, to see if it includes the object.
154
+ # @param *more<Array>:: additional args, will be flattened into arrayish
155
+ #
156
+ # @return <TrueClass, FalseClass>
157
+ # True if the object is included in arrayish (+ more)
158
+ #
159
+ # @example 1.in?([1,2,3]) #=> true
160
+ # @example 1.in?(1,2,3) #=> true
161
+ def in?(arrayish,*more)
162
+ arrayish = more.unshift(arrayish) unless more.empty?
163
+ arrayish.include?(self)
164
+ end
165
+ end
@@ -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,5 @@
1
+ class Pathname
2
+ def /(path)
3
+ (self + path).expand_path
4
+ end
5
+ end # class Pathname
@@ -0,0 +1,233 @@
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
+ @scavenger || begin
30
+ @scavenger = Thread.new do
31
+ loop do
32
+ lock.synchronize do
33
+ pools.each do |pool|
34
+ # This is a useful check, but non-essential, and right now it breaks lots of stuff.
35
+ # if pool.expired?
36
+ pool.lock.synchronize do
37
+ if pool.reserved_count == 0
38
+ pool.dispose
39
+ end
40
+ end
41
+ # end
42
+ end
43
+ end
44
+ sleep(scavenger_interval)
45
+ end # loop
46
+ end
47
+
48
+ @scavenger.priority = -10
49
+ @scavenger
50
+ end
51
+ end
52
+
53
+ def self.pools
54
+ @pools ||= Set.new
55
+ end
56
+
57
+ def self.append_pool(pool)
58
+ lock.synchronize do
59
+ pools << pool
60
+ end
61
+ Extlib::Pooling::scavenger
62
+ end
63
+
64
+ def self.lock
65
+ @lock ||= Mutex.new
66
+ end
67
+
68
+ class CrossPoolError < StandardError
69
+ end
70
+
71
+ class OrphanedObjectError < StandardError
72
+ end
73
+
74
+ class ThreadStopError < StandardError
75
+ end
76
+
77
+ def self.included(target)
78
+ target.class_eval do
79
+ class << self
80
+ alias __new new
81
+ end
82
+
83
+ @__pools = Hash.new { |h,k| __pool_lock.synchronize { h[k] = Pool.new(target.pool_size, target, k) } }
84
+ @__pool_lock = Mutex.new
85
+
86
+ def self.__pool_lock
87
+ @__pool_lock
88
+ end
89
+
90
+ def self.new(*args)
91
+ @__pools[args].new
92
+ end
93
+
94
+ def self.__pools
95
+ @__pools
96
+ end
97
+
98
+ def self.pool_size
99
+ 8
100
+ end
101
+ end
102
+ end
103
+
104
+ def release
105
+ @__pool.release(self) unless @__pool.nil?
106
+ end
107
+
108
+ class Pool
109
+ def initialize(max_size, resource, args)
110
+ raise ArgumentError.new("+max_size+ should be a Fixnum but was #{max_size.inspect}") unless Fixnum === max_size
111
+ raise ArgumentError.new("+resource+ should be a Class but was #{resource.inspect}") unless Class === resource
112
+
113
+ @max_size = max_size
114
+ @resource = resource
115
+ @args = args
116
+
117
+ @available = []
118
+ @reserved_count = 0
119
+ end
120
+
121
+ def lock
122
+ @resource.__pool_lock
123
+ end
124
+
125
+ def scavenge_interval
126
+ @resource.scavenge_interval
127
+ end
128
+
129
+ def new
130
+ instance = nil
131
+
132
+ lock.synchronize do
133
+ instance = acquire
134
+ end
135
+
136
+ Extlib::Pooling::append_pool(self)
137
+
138
+ if instance.nil?
139
+ # Account for the current thread, and the pool scavenger.
140
+ if ThreadGroup::Default.list.size == 2 && @reserved_count >= @max_size
141
+ raise ThreadStopError.new(size)
142
+ else
143
+ sleep(0.05)
144
+ new
145
+ end
146
+ else
147
+ instance
148
+ end
149
+ end
150
+
151
+ def release(instance)
152
+ lock.synchronize do
153
+ instance.instance_variable_set(:@__pool, nil)
154
+ @reserved_count -= 1
155
+ @available.push(instance)
156
+ end
157
+ nil
158
+ end
159
+
160
+ def delete(instance)
161
+ lock.synchronize do
162
+ instance.instance_variable_set(:@__pool, nil)
163
+ @reserved_count -= 1
164
+ end
165
+ nil
166
+ end
167
+
168
+ def size
169
+ @available.size + @reserved_count
170
+ end
171
+ alias length size
172
+
173
+ def inspect
174
+ "#<Extlib::Pooling::Pool<#{@resource.name}> available=#{@available.size} reserved_count=#{@reserved_count}>"
175
+ end
176
+
177
+ def flush!
178
+ @available.pop.dispose until @available.empty?
179
+ end
180
+
181
+ def dispose
182
+ flush!
183
+ @resource.__pools.delete(@args)
184
+ !Extlib::Pooling::pools.delete?(self).nil?
185
+ end
186
+
187
+ # Disabled temporarily.
188
+ #
189
+ # def expired?
190
+ # lock.synchronize do
191
+ # @available.each do |instance|
192
+ # if instance.instance_variable_get(:@__allocated_in_pool) + scavenge_interval < Time.now
193
+ # instance.dispose
194
+ # @available.delete(instance)
195
+ # end
196
+ # end
197
+ #
198
+ # size == 0
199
+ # end
200
+ # end
201
+
202
+ def reserved_count
203
+ @reserved_count
204
+ end
205
+
206
+ private
207
+
208
+ def acquire
209
+ instance = if !@available.empty?
210
+ @available.pop
211
+ elsif size < @max_size
212
+ @resource.__new(*@args)
213
+ else
214
+ nil
215
+ end
216
+
217
+ if instance.nil?
218
+ instance
219
+ else
220
+ raise CrossPoolError.new(instance) if instance.instance_variable_get(:@__pool)
221
+ @reserved_count += 1
222
+ instance.instance_variable_set(:@__pool, self)
223
+ instance.instance_variable_set(:@__allocated_in_pool, Time.now)
224
+ instance
225
+ end
226
+ end
227
+ end
228
+
229
+ def self.scavenger_interval
230
+ 60
231
+ end
232
+ end # module Pooling
233
+ end # module Extlib