extlib 0.9.3 → 0.9.4

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.

@@ -104,8 +104,8 @@ module Extlib
104
104
  end
105
105
 
106
106
  # Register a class method as hookable. Registering a method means that
107
- # before hooks will be run immedietly before the method is invoked and
108
- # after hooks will be called immedietly after the method is invoked.
107
+ # before hooks will be run immediately before the method is invoked and
108
+ # after hooks will be called immediately after the method is invoked.
109
109
  #
110
110
  # @param hookable_method<Symbol> The name of the class method that should
111
111
  # be hookable
@@ -116,8 +116,8 @@ module Extlib
116
116
  end
117
117
 
118
118
  # Register aninstance method as hookable. Registering a method means that
119
- # before hooks will be run immedietly before the method is invoked and
120
- # after hooks will be called immedietly after the method is invoked.
119
+ # before hooks will be run immediately before the method is invoked and
120
+ # after hooks will be called immediately after the method is invoked.
121
121
  #
122
122
  # @param hookable_method<Symbol> The name of the instance method that should
123
123
  # be hookable
@@ -10,6 +10,7 @@ gem 'english', '>=0.2.0'
10
10
  require 'english/inflect'
11
11
 
12
12
  English::Inflect.word 'postgres'
13
+ English::Inflect.singular_word "status", "status"
13
14
 
14
15
  module Extlib
15
16
  module Inflection
@@ -46,11 +47,7 @@ module Extlib
46
47
  # "ActiveRecord::Errors".underscore #=> active_record/errors
47
48
  #
48
49
  def underscore(camel_cased_word)
49
- camel_cased_word.to_s.gsub(/::/, '/').
50
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
51
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
52
- tr("-", "_").
53
- downcase
50
+ camel_cased_word.to_const_path
54
51
  end
55
52
 
56
53
  # Capitalizes the first word and turns underscores into spaces and strips _id.
@@ -80,7 +77,7 @@ module Extlib
80
77
  # "egg_and_ham".tableize #=> "egg_and_hams"
81
78
  # "fancyCategory".tableize #=> "fancy_categories"
82
79
  def tableize(class_name)
83
- pluralize(underscore(class_name))
80
+ pluralize(class_name.to_const_path.gsub(/\//, '_'))
84
81
  end
85
82
 
86
83
  # Creates a foreign key name from a class name.
@@ -1,5 +1,5 @@
1
1
  class LazyArray # borrowed partially from StrokeDB
2
- instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ send dup class object_id kind_of? respond_to? assert_kind_of should should_not instance_variable_set instance_variable_get ].include?(m) }
2
+ instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ send dup class object_id kind_of? respond_to? assert_kind_of should should_not instance_variable_set instance_variable_get extend ].include?(m) }
3
3
 
4
4
  include Enumerable
5
5
 
@@ -0,0 +1,202 @@
1
+ require "time" # httpdate
2
+ # ==== Public Merb Logger API
3
+ #
4
+ # To replace an existing logger with a new one:
5
+ # Merb::Logger.set_log(log{String, IO},level{Symbol, String})
6
+ #
7
+ # Available logging levels are
8
+ # Merb::Logger::{ Fatal, Error, Warn, Info, Debug }
9
+ #
10
+ # Logging via:
11
+ # Merb.logger.fatal(message<String>,&block)
12
+ # Merb.logger.error(message<String>,&block)
13
+ # Merb.logger.warn(message<String>,&block)
14
+ # Merb.logger.info(message<String>,&block)
15
+ # Merb.logger.debug(message<String>,&block)
16
+ #
17
+ # Logging with autoflush:
18
+ # Merb.logger.fatal!(message<String>,&block)
19
+ # Merb.logger.error!(message<String>,&block)
20
+ # Merb.logger.warn!(message<String>,&block)
21
+ # Merb.logger.info!(message<String>,&block)
22
+ # Merb.logger.debug!(message<String>,&block)
23
+ #
24
+ # Flush the buffer to
25
+ # Merb.logger.flush
26
+ #
27
+ # Remove the current log object
28
+ # Merb.logger.close
29
+ #
30
+ # ==== Private Merb Logger API
31
+ #
32
+ # To initialize the logger you create a new object, proxies to set_log.
33
+ # Merb::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
+ elsif Merb.environment == "production"
114
+ @level = Levels[:warn]
115
+ else
116
+ @level = Levels[:debug]
117
+ end
118
+ @buffer = []
119
+ @delimiter = delimiter
120
+ @auto_flush = auto_flush
121
+
122
+ initialize_log(log)
123
+
124
+ Merb.logger = self
125
+ end
126
+
127
+ # Flush the entire buffer to the log object.
128
+ def flush
129
+ return unless @buffer.size > 0
130
+ @log.write(@buffer.slice!(0..-1).to_s)
131
+ end
132
+
133
+ # Close and remove the current log object.
134
+ def close
135
+ flush
136
+ @log.close if @log.respond_to?(:close) && !@log.tty?
137
+ @log = nil
138
+ end
139
+
140
+ # Appends a message to the log. The methods yield to an optional block and
141
+ # the output of this block will be appended to the message.
142
+ #
143
+ # ==== Parameters
144
+ # string<String>:: The message to be logged. Defaults to nil.
145
+ #
146
+ # ==== Returns
147
+ # String:: The resulting message added to the log file.
148
+ def <<(string = nil)
149
+ message = ""
150
+ message << delimiter
151
+ message << string if string
152
+ message << "\n" unless message[-1] == ?\n
153
+ @buffer << message
154
+ flush if @auto_flush
155
+
156
+ message
157
+ end
158
+ alias :push :<<
159
+
160
+ # Generate the logging methods for Merb.logger for each log level.
161
+ Levels.each_pair do |name, number|
162
+ class_eval <<-LEVELMETHODS, __FILE__, __LINE__
163
+
164
+ # Appends a message to the log if the log level is at least as high as
165
+ # the log level of the logger.
166
+ #
167
+ # ==== Parameters
168
+ # string<String>:: The message to be logged. Defaults to nil.
169
+ #
170
+ # ==== Returns
171
+ # self:: The logger object for chaining.
172
+ def #{name}(message = nil)
173
+ self << message if #{number} >= level
174
+ self
175
+ end
176
+
177
+ # Appends a message to the log if the log level is at least as high as
178
+ # the log level of the logger. The bang! version of the method also auto
179
+ # flushes the log buffer to disk.
180
+ #
181
+ # ==== Parameters
182
+ # string<String>:: The message to be logged. Defaults to nil.
183
+ #
184
+ # ==== Returns
185
+ # self:: The logger object for chaining.
186
+ def #{name}!(message = nil)
187
+ self << message if #{number} >= level
188
+ flush if #{number} >= level
189
+ self
190
+ end
191
+
192
+ # ==== Returns
193
+ # Boolean:: True if this level will be logged by this logger.
194
+ def #{name}?
195
+ #{number} >= level
196
+ end
197
+ LEVELMETHODS
198
+ end
199
+
200
+ end
201
+
202
+ end
@@ -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
@@ -1,7 +1,144 @@
1
1
  class Object
2
- unless instance_methods.include?('instance_variable_defined?')
3
- def instance_variable_defined?(method)
4
- instance_variables.include?(method.to_s)
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)
5
72
  end
73
+ obj
6
74
  end
7
- end # class Object
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 <NilClass>
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"
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 <TrueClass, FalseClass>
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
+ # @param arrayish<#include?> Container to check, to see if it includes the object.
133
+ # @param *more<Array>:: additional args, will be flattened into arrayish
134
+ #
135
+ # @return <TrueClass, FalseClass>
136
+ # True if the object is included in arrayish (+ more)
137
+ #
138
+ # @example 1.in?([1,2,3]) #=> true
139
+ # @example 1.in?(1,2,3) #=> true
140
+ def in?(arrayish,*more)
141
+ arrayish = more.unshift(arrayish) unless more.empty?
142
+ arrayish.include?(self)
143
+ end
144
+ end