sundbp-extlib 0.9.14

Sign up to get free protection for your applications and to get access to all the features.
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,18 @@
1
+ class LazyModule < Module
2
+ def self.new(&blk)
3
+ # passing no-op block overrides &blk
4
+ m = super{ }
5
+ class << m
6
+ include ClassMethods
7
+ end
8
+ m.lazy_evaluated_body = blk
9
+ m
10
+ end
11
+
12
+ module ClassMethods
13
+ attr_accessor :lazy_evaluated_body
14
+ def included(host)
15
+ host.class_eval(&@lazy_evaluated_body)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,198 @@
1
+ require "time" # httpdate
2
+ # ==== Public Extlib Logger API
3
+ #
4
+ # To replace an existing logger with a new one:
5
+ # Extlib::Logger.set_log(log{String, IO},level{Symbol, String})
6
+ #
7
+ # Available logging levels are
8
+ # Extlib::Logger::{ Fatal, Error, Warn, Info, Debug }
9
+ #
10
+ # Logging via:
11
+ # Extlib.logger.fatal(message<String>,&block)
12
+ # Extlib.logger.error(message<String>,&block)
13
+ # Extlib.logger.warn(message<String>,&block)
14
+ # Extlib.logger.info(message<String>,&block)
15
+ # Extlib.logger.debug(message<String>,&block)
16
+ #
17
+ # Logging with autoflush:
18
+ # Extlib.logger.fatal!(message<String>,&block)
19
+ # Extlib.logger.error!(message<String>,&block)
20
+ # Extlib.logger.warn!(message<String>,&block)
21
+ # Extlib.logger.info!(message<String>,&block)
22
+ # Extlib.logger.debug!(message<String>,&block)
23
+ #
24
+ # Flush the buffer to
25
+ # Extlib.logger.flush
26
+ #
27
+ # Remove the current log object
28
+ # Extlib.logger.close
29
+ #
30
+ # ==== Private Extlib Logger API
31
+ #
32
+ # To initialize the logger you create a new object, proxies to set_log.
33
+ # Extlib::Logger.new(log{String, IO},level{Symbol, String})
34
+ module Extlib
35
+
36
+ class << self
37
+ attr_accessor :logger
38
+ end
39
+
40
+ class Logger
41
+
42
+ attr_accessor :level
43
+ attr_accessor :delimiter
44
+ attr_accessor :auto_flush
45
+ attr_reader :buffer
46
+ attr_reader :log
47
+ attr_reader :init_args
48
+
49
+ # ==== Notes
50
+ # Ruby (standard) logger levels:
51
+ # :fatal:: An unhandleable error that results in a program crash
52
+ # :error:: A handleable error condition
53
+ # :warn:: A warning
54
+ # :info:: generic (useful) information about system operation
55
+ # :debug:: low-level information for developers
56
+ Levels =
57
+ {
58
+ :fatal => 7,
59
+ :error => 6,
60
+ :warn => 4,
61
+ :info => 3,
62
+ :debug => 0
63
+ }
64
+
65
+ private
66
+
67
+ # Readies a log for writing.
68
+ #
69
+ # ==== Parameters
70
+ # log<IO, String>:: Either an IO object or a name of a logfile.
71
+ def initialize_log(log)
72
+ close if @log # be sure that we don't leave open files laying around.
73
+
74
+ if log.respond_to?(:write)
75
+ @log = log
76
+ elsif File.exist?(log)
77
+ @log = open(log, (File::WRONLY | File::APPEND))
78
+ @log.sync = true
79
+ else
80
+ FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log))
81
+ @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
82
+ @log.sync = true
83
+ @log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n")
84
+ end
85
+ end
86
+
87
+ public
88
+
89
+ # To initialize the logger you create a new object, proxies to set_log.
90
+ #
91
+ # ==== Parameters
92
+ # *args:: Arguments to create the log from. See set_logs for specifics.
93
+ def initialize(*args)
94
+ @init_args = args
95
+ set_log(*args)
96
+ end
97
+
98
+ # Replaces an existing logger with a new one.
99
+ #
100
+ # ==== Parameters
101
+ # log<IO, String>:: Either an IO object or a name of a logfile.
102
+ # log_level<~to_sym>::
103
+ # The log level from, e.g. :fatal or :info. Defaults to :error in the
104
+ # production environment and :debug otherwise.
105
+ # delimiter<String>::
106
+ # Delimiter to use between message sections. Defaults to " ~ ".
107
+ # auto_flush<Boolean>::
108
+ # Whether the log should automatically flush after new messages are
109
+ # added. Defaults to false.
110
+ def set_log(log, log_level = nil, delimiter = " ~ ", auto_flush = false)
111
+ if log_level && Levels[log_level.to_sym]
112
+ @level = Levels[log_level.to_sym]
113
+ else
114
+ @level = Levels[:debug]
115
+ end
116
+ @buffer = []
117
+ @delimiter = delimiter
118
+ @auto_flush = auto_flush
119
+
120
+ initialize_log(log)
121
+ end
122
+
123
+ # Flush the entire buffer to the log object.
124
+ def flush
125
+ return unless @buffer.size > 0
126
+ @log.write(@buffer.slice!(0..-1).join)
127
+ end
128
+
129
+ # Close and remove the current log object.
130
+ def close
131
+ flush
132
+ @log.close if @log.respond_to?(:close) && !@log.tty?
133
+ @log = nil
134
+ end
135
+
136
+ # Appends a message to the log. The methods yield to an optional block and
137
+ # the output of this block will be appended to the message.
138
+ #
139
+ # ==== Parameters
140
+ # string<String>:: The message to be logged. Defaults to nil.
141
+ #
142
+ # ==== Returns
143
+ # String:: The resulting message added to the log file.
144
+ def <<(string = nil)
145
+ message = ""
146
+ message << delimiter
147
+ message << string if string
148
+ message << "\n" unless message[-1] == ?\n
149
+ @buffer << message
150
+ flush if @auto_flush
151
+
152
+ message
153
+ end
154
+ alias :push :<<
155
+
156
+ # Generate the logging methods for Extlib.logger for each log level.
157
+ Levels.each_pair do |name, number|
158
+ class_eval <<-LEVELMETHODS, __FILE__, __LINE__
159
+
160
+ # Appends a message to the log if the log level is at least as high as
161
+ # the log level of the logger.
162
+ #
163
+ # ==== Parameters
164
+ # string<String>:: The message to be logged. Defaults to nil.
165
+ #
166
+ # ==== Returns
167
+ # self:: The logger object for chaining.
168
+ def #{name}(message = nil)
169
+ self << message if #{number} >= level
170
+ self
171
+ end
172
+
173
+ # Appends a message to the log if the log level is at least as high as
174
+ # the log level of the logger. The bang! version of the method also auto
175
+ # flushes the log buffer to disk.
176
+ #
177
+ # ==== Parameters
178
+ # string<String>:: The message to be logged. Defaults to nil.
179
+ #
180
+ # ==== Returns
181
+ # self:: The logger object for chaining.
182
+ def #{name}!(message = nil)
183
+ self << message if #{number} >= level
184
+ flush if #{number} >= level
185
+ self
186
+ end
187
+
188
+ # ==== Returns
189
+ # Boolean:: True if this level will be logged by this logger.
190
+ def #{name}?
191
+ #{number} >= level
192
+ end
193
+ LEVELMETHODS
194
+ end
195
+
196
+ end
197
+
198
+ end
@@ -0,0 +1,155 @@
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 [Boolean] 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
+ # @param hash<Hash> The hash to merge with the mash.
87
+ #
88
+ # @return [Mash] A new mash with the hash values merged in.
89
+ def merge(hash)
90
+ self.dup.update(hash)
91
+ end
92
+
93
+ # @param key<Object>
94
+ # The key to delete from the mash.\
95
+ def delete(key)
96
+ super(convert_key(key))
97
+ end
98
+
99
+ # @param *rejected<Array[(String, Symbol)] The mash keys to exclude.
100
+ #
101
+ # @return [Mash] A new mash without the selected keys.
102
+ #
103
+ # @example
104
+ # { :one => 1, :two => 2, :three => 3 }.except(:one)
105
+ # #=> { "two" => 2, "three" => 3 }
106
+ def except(*keys)
107
+ super(*keys.map {|k| convert_key(k)})
108
+ end
109
+
110
+ # Used to provide the same interface as Hash.
111
+ #
112
+ # @return [Mash] This mash unchanged.
113
+ def stringify_keys!; self end
114
+
115
+ # @return [Hash] The mash as a Hash with symbolized keys.
116
+ def symbolize_keys
117
+ h = Hash.new(default)
118
+ each { |key, val| h[key.to_sym] = val }
119
+ h
120
+ end
121
+
122
+ # @return [Hash] The mash as a Hash with string keys.
123
+ def to_hash
124
+ Hash.new(default).merge(self)
125
+ end
126
+
127
+ protected
128
+ # @param key<Object> The key to convert.
129
+ #
130
+ # @param [Object]
131
+ # The converted key. If the key was a symbol, it will be converted to a
132
+ # string.
133
+ #
134
+ # @api private
135
+ def convert_key(key)
136
+ key.kind_of?(Symbol) ? key.to_s : key
137
+ end
138
+
139
+ # @param value<Object> The value to convert.
140
+ #
141
+ # @return [Object]
142
+ # The converted value. A Hash or an Array of hashes, will be converted to
143
+ # their Mash equivalents.
144
+ #
145
+ # @api private
146
+ def convert_value(value)
147
+ if value.class == Hash
148
+ value.to_mash
149
+ elsif value.is_a?(Array)
150
+ value.collect { |e| convert_value(e) }
151
+ else
152
+ value
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,47 @@
1
+ class Module
2
+ def find_const(const_name)
3
+ if const_name[0..1] == '::'
4
+ Object.full_const_get(const_name[2..-1])
5
+ else
6
+ nested_const_lookup(const_name)
7
+ end
8
+ end
9
+
10
+ def try_dup
11
+ self
12
+ end
13
+
14
+ private
15
+
16
+ # Doesn't do any caching since constants can change with remove_const
17
+ def nested_const_lookup(const_name)
18
+ unless equal?(Object)
19
+ constants = []
20
+
21
+ name.split('::').each do |part|
22
+ const = constants.last || Object
23
+ constants << const.const_get(part)
24
+ end
25
+
26
+ parts = const_name.split('::')
27
+
28
+ # from most to least specific constant, use each as a base and try
29
+ # to find a constant with the name const_name within them
30
+ constants.reverse_each do |const|
31
+ # return the nested constant if available
32
+ return const if parts.all? do |part|
33
+ const = if RUBY_VERSION >= '1.9.0'
34
+ const.const_defined?(part, false) ? const.const_get(part, false) : nil
35
+ else
36
+ const.const_defined?(part) ? const.const_get(part) : nil
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ # no relative constant found, fallback to an absolute lookup and
43
+ # use const_missing if not found
44
+ Object.full_const_get(const_name)
45
+ end
46
+
47
+ end # class Module
@@ -0,0 +1,5 @@
1
+ class NilClass
2
+ def try_dup
3
+ self
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Numeric
2
+ def try_dup
3
+ self
4
+ end
5
+ end
@@ -0,0 +1,175 @@
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
+ # @param name<String> The name of the constant to get, e.g. "Merb::Router".
62
+ #
63
+ # @return [Object] The constant corresponding to the name.
64
+ def full_const_get(name)
65
+ list = name.split("::")
66
+ list.shift if list.first.blank?
67
+ obj = self
68
+ list.each do |x|
69
+ # This is required because const_get tries to look for constants in the
70
+ # ancestor chain, but we only want constants that are HERE
71
+ obj = obj.const_defined?(x) ? obj.const_get(x) : obj.const_missing(x)
72
+ end
73
+ obj
74
+ end
75
+
76
+ # @param name<String> The name of the constant to get, e.g. "Merb::Router".
77
+ # @param value<Object> The value to assign to the constant.
78
+ #
79
+ # @return [Object] The constant corresponding to the name.
80
+ def full_const_set(name, value)
81
+ list = name.split("::")
82
+ toplevel = list.first.blank?
83
+ list.shift if toplevel
84
+ last = list.pop
85
+ obj = list.empty? ? Object : Object.full_const_get(list.join("::"))
86
+ obj.const_set(last, value) if obj && !obj.const_defined?(last)
87
+ end
88
+
89
+ # Defines module from a string name (e.g. Foo::Bar::Baz)
90
+ # If module already exists, no exception raised.
91
+ #
92
+ # @param name<String> The name of the full module name to make
93
+ #
94
+ # @return [nil]
95
+ def make_module(str)
96
+ mod = str.split("::")
97
+ current_module = self
98
+ mod.each do |x|
99
+ unless current_module.const_defined?(x)
100
+ current_module.class_eval "module #{x}; end", __FILE__, __LINE__
101
+ end
102
+ current_module = current_module.const_get(x)
103
+ end
104
+ current_module
105
+ end
106
+
107
+ # @param duck<Symbol, Class, Array> The thing to compare the object to.
108
+ #
109
+ # @note
110
+ # The behavior of the method depends on the type of duck as follows:
111
+ # Symbol:: Check whether the object respond_to?(duck).
112
+ # Class:: Check whether the object is_a?(duck).
113
+ # Array::
114
+ # Check whether the object quacks_like? at least one of the options in the
115
+ # array.
116
+ #
117
+ # @return [Boolean]
118
+ # True if the object quacks like duck.
119
+ def quacks_like?(duck)
120
+ case duck
121
+ when Symbol
122
+ self.respond_to?(duck)
123
+ when Class
124
+ self.is_a?(duck)
125
+ when Array
126
+ duck.any? {|d| self.quacks_like?(d) }
127
+ else
128
+ false
129
+ end
130
+ end
131
+
132
+ # Override this in a child if it cannot be dup'ed
133
+ #
134
+ # @return [Object]
135
+ def try_dup
136
+ self.dup
137
+ end
138
+
139
+ # If receiver is callable, calls it and
140
+ # returns result. If not, just returns receiver
141
+ # itself
142
+ #
143
+ # @return [Object]
144
+ def try_call(*args)
145
+ if self.respond_to?(:call)
146
+ self.call(*args)
147
+ else
148
+ self
149
+ end
150
+ end
151
+
152
+ # @param arrayish<#include?> Container to check, to see if it includes the object.
153
+ # @param *more<Array>:: additional args, will be flattened into arrayish
154
+ #
155
+ # @return [Boolean]
156
+ # True if the object is included in arrayish (+ more)
157
+ #
158
+ # @example 1.in?([1,2,3]) #=> true
159
+ # @example 1.in?(1,2,3) #=> true
160
+ def in?(arrayish,*more)
161
+ arrayish = more.unshift(arrayish) unless more.empty?
162
+ arrayish.include?(self)
163
+ end
164
+
165
+ # Add instance_variable_defined? for backward compatibility
166
+ # @param variable<Symbol, String>
167
+ #
168
+ # @return [Boolean]
169
+ # True if the object has the given instance variable defined
170
+ unless respond_to?(:instance_variable_defined?)
171
+ def instance_variable_defined?(variable)
172
+ instance_variables.include?(variable.to_s)
173
+ end
174
+ end
175
+ end