thorero 0.9.4

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 (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