extlib 0.9.2

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.

data/README ADDED
File without changes
@@ -0,0 +1,19 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+
4
+ # for Pathname /
5
+ require File.expand_path(File.join(File.dirname(__FILE__), 'extlib', 'pathname'))
6
+
7
+ dir = Pathname(__FILE__).dirname.expand_path / 'extlib'
8
+
9
+ require dir / 'assertions'
10
+ require dir / 'blank'
11
+ require dir / 'inflection'
12
+ require dir / 'lazy_array'
13
+ require dir / 'object'
14
+ require dir / 'module'
15
+ require dir / 'blank'
16
+ require dir / 'pooling'
17
+ require dir / 'string'
18
+ require dir / 'struct'
19
+ require dir / 'hook'
@@ -0,0 +1,8 @@
1
+ module Extlib
2
+ module Assertions
3
+ def assert_kind_of(name, value, *klasses)
4
+ klasses.each { |k| return if value.kind_of?(k) }
5
+ raise ArgumentError, "+#{name}+ should be #{klasses.map { |k| k.name } * ' or '}, but was #{value.class.name}", caller(2)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,42 @@
1
+ # blank? methods for several different class types
2
+ class Object
3
+ # Returns true if the object is nil or empty (if applicable)
4
+ def blank?
5
+ nil? || (respond_to?(:empty?) && empty?)
6
+ end
7
+ end # class Object
8
+
9
+ class Numeric
10
+ # Numerics can't be blank
11
+ def blank?
12
+ false
13
+ end
14
+ end # class Numeric
15
+
16
+ class NilClass
17
+ # Nils are always blank
18
+ def blank?
19
+ true
20
+ end
21
+ end # class NilClass
22
+
23
+ class TrueClass
24
+ # True is not blank.
25
+ def blank?
26
+ false
27
+ end
28
+ end # class TrueClass
29
+
30
+ class FalseClass
31
+ # False is always blank.
32
+ def blank?
33
+ true
34
+ end
35
+ end # class FalseClass
36
+
37
+ class String
38
+ # Strips out whitespace then tests if the string is empty.
39
+ def blank?
40
+ strip.empty?
41
+ end
42
+ end # class String
@@ -0,0 +1,313 @@
1
+ module Extlib
2
+ #
3
+ # TODO: Write more documentation!
4
+ #
5
+ # Currently, before you can set a before or after hook on a method, you must
6
+ # register that method as hookable; otherwise, their invocation of the hook
7
+ # stack is not created.
8
+ #
9
+ # This can be done by calling #register_class_hooks to register one or more
10
+ # class methods as hookable or #register_instance_hooks to register one or
11
+ # more instance methods as hookable.
12
+ #
13
+ # Eventually, I'll probably have #before, #after, #before_class_method, and
14
+ # #after_class_method implicitly call #register_instance_hooks and
15
+ # #register_class_hooks respectivly. That way, if the exact hook insertion
16
+ # location does not need to be specified, hooks can be added on the fly.
17
+ #
18
+ # Please bring up any issues regarding Hooks with carllerche on IRC
19
+ #
20
+ module Hook
21
+
22
+ def self.included(base)
23
+ base.extend(ClassMethods)
24
+ base.const_set("CLASS_HOOKS", {}) unless base.const_defined?("CLASS_HOOKS")
25
+ base.const_set("INSTANCE_HOOKS", {}) unless base.const_defined?("INSTANCE_HOOKS")
26
+ end
27
+
28
+ module ClassMethods
29
+ include Extlib::Assertions
30
+ # Inject code that executes before the target class method.
31
+ #
32
+ # @param target_method<Symbol> the name of the class method to inject before
33
+ # @param method_sym<Symbol> the name of the method to run before the
34
+ # target_method
35
+ # @param block<Block> the code to run before the target_method
36
+ #
37
+ # @note
38
+ # Either method_sym or block is required.
39
+ # -
40
+ # @api public
41
+ def before_class_method(target_method, method_sym = nil, &block)
42
+ install_hook :before, target_method, method_sym, :class, &block
43
+ end
44
+
45
+ #
46
+ # Inject code that executes after the target class method.
47
+ #
48
+ # @param target_method<Symbol> the name of the class method to inject after
49
+ # @param method_sym<Symbol> the name of the method to run after the target_method
50
+ # @param block<Block> the code to run after the target_method
51
+ #
52
+ # @note
53
+ # Either method_sym or block is required.
54
+ # -
55
+ # @api public
56
+ def after_class_method(target_method, method_sym = nil, &block)
57
+ install_hook :after, target_method, method_sym, :class, &block
58
+ end
59
+
60
+ #
61
+ # Inject code that executes before the target instance method.
62
+ #
63
+ # @param target_method<Symbol> the name of the instance method to inject before
64
+ # @param method_sym<Symbol> the name of the method to run before the
65
+ # target_method
66
+ # @param block<Block> the code to run before the target_method
67
+ #
68
+ # @note
69
+ # Either method_sym or block is required.
70
+ # -
71
+ # @api public
72
+ def before(target_method, method_sym = nil, &block)
73
+ install_hook :before, target_method, method_sym, :instance, &block
74
+ end
75
+
76
+ #
77
+ # Inject code that executes after the target instance method.
78
+ #
79
+ # @param target_method<Symbol> the name of the instance method to inject after
80
+ # @param method_sym<Symbol> the name of the method to run after the
81
+ # target_method
82
+ # @param block<Block> the code to run after the target_method
83
+ #
84
+ # @note
85
+ # Either method_sym or block is required.
86
+ # -
87
+ # @api public
88
+ def after(target_method, method_sym = nil, &block)
89
+ install_hook :after, target_method, method_sym, :instance, &block
90
+ end
91
+
92
+ # Register a class method as hookable. Registering a method means that
93
+ # before hooks will be run immedietly before the method is invoked and
94
+ # after hooks will be called immedietly after the method is invoked.
95
+ #
96
+ # @param hookable_method<Symbol> The name of the class method that should
97
+ # be hookable
98
+ # -
99
+ # @api public
100
+ def register_class_hooks(*hooks)
101
+ hooks.each { |hook| register_hook(hook, :class) }
102
+ end
103
+
104
+ # Register aninstance method as hookable. Registering a method means that
105
+ # before hooks will be run immedietly before the method is invoked and
106
+ # after hooks will be called immedietly after the method is invoked.
107
+ #
108
+ # @param hookable_method<Symbol> The name of the instance method that should
109
+ # be hookable
110
+ # -
111
+ # @api public
112
+ def register_instance_hooks(*hooks)
113
+ hooks.each { |hook| register_hook(hook, :instance) }
114
+ end
115
+
116
+ # Not yet implemented
117
+ def reset_hook!(target_method, scope)
118
+ raise NotImplementedError
119
+ end
120
+
121
+ # --- Alright kids... the rest is internal stuff ---
122
+
123
+ def hooks_with_scope(scope)
124
+ case scope
125
+ when :class then class_hooks
126
+ when :instance then instance_hooks
127
+ else raise ArgumentError, 'You need to pass :class or :instance as scope'
128
+ end
129
+ end
130
+
131
+ def class_hooks
132
+ self.const_get("CLASS_HOOKS")
133
+ end
134
+
135
+ def instance_hooks
136
+ self.const_get("INSTANCE_HOOKS")
137
+ end
138
+
139
+ def register_hook(target_method, scope)
140
+ if scope == :instance && !method_defined?(target_method)
141
+ raise ArgumentError, "#{target_method} instance method does not exist"
142
+ elsif scope == :class && !respond_to?(target_method)
143
+ raise ArgumentError, "#{target_method} class method does not exist"
144
+ end
145
+
146
+ hooks = hooks_with_scope(scope)
147
+
148
+ if hooks[target_method].nil?
149
+ hooks[target_method] = {
150
+ :before => [], :after => [], :in => self
151
+ }
152
+
153
+ define_hook_stack_execution_methods(target_method, scope)
154
+ define_advised_method(target_method, scope)
155
+ end
156
+ end
157
+
158
+ def registered_as_hook?(target_method, scope)
159
+ ! hooks_with_scope(scope)[target_method].nil?
160
+ end
161
+
162
+ def hook_method_name(target_method, prefix, suffix)
163
+ target_method = target_method.to_s
164
+
165
+ case target_method[-1,1]
166
+ when '?' then "#{prefix}_#{target_method[0..-2]}_ques_#{suffix}"
167
+ when '!' then "#{prefix}_#{target_method[0..-2]}_bang_#{suffix}"
168
+ when '=' then "#{prefix}_#{target_method[0..-2]}_eq_#{suffix}"
169
+ else "#{prefix}_#{target_method[0..-2]}_nan_#{suffix}"
170
+ end
171
+ end
172
+
173
+ def define_hook_stack_execution_methods(target_method, scope)
174
+ unless registered_as_hook?(target_method, scope)
175
+ raise ArgumentError, "#{target_method} has not be registered as a hookable #{scope} method"
176
+ end
177
+
178
+ hooks = hooks_with_scope(scope)
179
+
180
+ # before_hook_stack = "execute_before_" + "#{target_method}".sub(/([?!=]?$)/, '_hook_stack\1')
181
+ # after_hook_stack = "execute_after_" + "#{target_method}".sub(/([?!=]?$)/, '_hook_stack\1')
182
+
183
+ before_hooks = hooks[target_method][:before]
184
+ before_hooks = before_hooks.map{ |info| inline_call(info, scope) }.join("\n")
185
+
186
+ after_hooks = hooks[target_method][:after]
187
+ after_hooks = after_hooks.map{ |info| inline_call(info, scope) }.join("\n")
188
+
189
+ source = %{
190
+ private
191
+
192
+ def #{hook_method_name(target_method, 'execute_before', 'hook_stack')}(*args)
193
+ #{before_hooks}
194
+ end
195
+
196
+ def #{hook_method_name(target_method, 'execute_after', 'hook_stack')}(*args)
197
+ #{after_hooks}
198
+ end
199
+ }
200
+
201
+ source = %{class << self\n#{source}\nend} if scope == :class
202
+
203
+ hooks[target_method][:in].class_eval(source)
204
+ end
205
+
206
+ def inline_call(method_info, scope)
207
+ if scope == :instance
208
+ %(#{method_info[:name]}(*args) if self.class <= ObjectSpace._id2ref(#{method_info[:from].object_id}))
209
+ else
210
+ %(#{method_info[:name]}(*args) if self <= ObjectSpace._id2ref(#{method_info[:from].object_id}))
211
+ end
212
+ end
213
+
214
+ def define_advised_method(target_method, scope)
215
+ args = args_for(method_with_scope(target_method, scope))
216
+
217
+ renamed_target = hook_method_name(target_method, 'hookable_', 'before_advised')
218
+ # before_hook_stack = "execute_before_" + "#{target_method}".sub(/([?!=]?$)/, '_hook_stack\1')
219
+ # after_hook_stack = "execute_after_" + "#{target_method}".sub(/([?!=]?$)/, '_hook_stack\1')
220
+
221
+ source = <<-EOD
222
+ def #{target_method}(#{args})
223
+ retval = nil
224
+ catch(:halt) do
225
+ #{hook_method_name(target_method, 'execute_before', 'hook_stack')}(#{args})
226
+ retval = #{renamed_target}(#{args})
227
+ #{hook_method_name(target_method, 'execute_after', 'hook_stack')}(#{args})
228
+ end
229
+ retval
230
+ end
231
+ EOD
232
+
233
+ if scope == :instance && !instance_methods(false).include?(target_method.to_s)
234
+ send(:alias_method, renamed_target, target_method)
235
+
236
+ proxy_module = Module.new
237
+ proxy_module.class_eval(source)
238
+ self.send(:include, proxy_module)
239
+ else
240
+ source = %{alias_method :#{renamed_target}, :#{target_method}\n#{source}}
241
+ source = %{class << self\n#{source}\nend} if scope == :class
242
+ class_eval(source)
243
+ end
244
+ end
245
+
246
+ # --- Add a hook ---
247
+
248
+ def install_hook(type, target_method, method_sym, scope, &block)
249
+ assert_kind_of 'target_method', target_method, Symbol
250
+ assert_kind_of 'method_sym', method_sym, Symbol unless method_sym.nil?
251
+ assert_kind_of 'scope', scope, Symbol
252
+
253
+ if !block_given? and method_sym.nil?
254
+ raise ArgumentError, "You need to pass 2 arguments to \"#{type}\"."
255
+ end
256
+
257
+ if method_sym.to_s[-1,1] == '='
258
+ raise ArgumentError, "Methods ending in = cannot be hooks"
259
+ end
260
+
261
+ unless [ :class, :instance ].include?(scope)
262
+ raise ArgumentError, 'You need to pass :class or :instance as scope'
263
+ end
264
+
265
+ register_hook(target_method, scope) unless registered_as_hook?(target_method, scope)
266
+
267
+ hooks = hooks_with_scope(scope)
268
+
269
+ if block
270
+ method_sym = "__hooks_#{type}_#{quote_method(target_method)}_#{hooks[target_method][type].length}".to_sym
271
+ if scope == :class
272
+ (class << self; self; end;).instance_eval do
273
+ define_method(method_sym, &block)
274
+ end
275
+ else
276
+ define_method(method_sym, &block)
277
+ end
278
+ end
279
+
280
+ # Adds method to the stack an redefines the hook invocation method
281
+ hooks[target_method][type] << { :name => method_sym, :from => self }
282
+ define_hook_stack_execution_methods(target_method, scope)
283
+ end
284
+
285
+ # --- Helpers ---
286
+
287
+ def args_for(method)
288
+ if method.arity == 0
289
+ ""
290
+ elsif method.arity > 0
291
+ "_" << (1 .. method.arity).to_a.join(", _")
292
+ elsif (method.arity + 1) < 0
293
+ "_" << (1 .. (method.arity).abs - 1).to_a.join(", _") << ", *args"
294
+ else
295
+ "*args"
296
+ end
297
+ end
298
+
299
+ def method_with_scope(name, scope)
300
+ case scope
301
+ when :class then method(name)
302
+ when :instance then instance_method(name)
303
+ else raise ArgumentError, 'You need to pass :class or :instance as scope'
304
+ end
305
+ end
306
+
307
+ def quote_method(name)
308
+ name.to_s.gsub(/\?$/, '_q_').gsub(/!$/, '_b_').gsub(/=$/, '_eq_')
309
+ end
310
+ end
311
+
312
+ end
313
+ end
@@ -0,0 +1,141 @@
1
+ # The original of this file was copied for the ActiveSupport project which is
2
+ # part of the Ruby On Rails web-framework (http://rubyonrails.org)
3
+ #
4
+ # Methods have been modified or removed. English inflection is now provided via
5
+ # the english gem (http://english.rubyforge.org)
6
+ #
7
+ # sudo gem install english
8
+ #
9
+ gem 'english', '>=0.2.0'
10
+ require 'english/inflect'
11
+
12
+ English::Inflect.word 'postgres'
13
+
14
+ module Extlib
15
+ module Inflection
16
+ class << self
17
+ # Take an underscored name and make it into a camelized name
18
+ #
19
+ # @example
20
+ # "egg_and_hams".classify #=> "EggAndHam"
21
+ # "post".classify #=> "Post"
22
+ #
23
+ def classify(name)
24
+ camelize(singularize(name.to_s.sub(/.*\./, '')))
25
+ end
26
+
27
+ # By default, camelize converts strings to UpperCamelCase.
28
+ #
29
+ # camelize will also convert '/' to '::' which is useful for converting paths to namespaces
30
+ #
31
+ # @example
32
+ # "active_record".camelize #=> "ActiveRecord"
33
+ # "active_record/errors".camelize #=> "ActiveRecord::Errors"
34
+ #
35
+ def camelize(lower_case_and_underscored_word, *args)
36
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
37
+ end
38
+
39
+
40
+ # The reverse of +camelize+. Makes an underscored form from the expression in the string.
41
+ #
42
+ # Changes '::' to '/' to convert namespaces to paths.
43
+ #
44
+ # @example
45
+ # "ActiveRecord".underscore #=> "active_record"
46
+ # "ActiveRecord::Errors".underscore #=> active_record/errors
47
+ #
48
+ 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
54
+ end
55
+
56
+ # Capitalizes the first word and turns underscores into spaces and strips _id.
57
+ # Like titleize, this is meant for creating pretty output.
58
+ #
59
+ # @example
60
+ # "employee_salary" #=> "Employee salary"
61
+ # "author_id" #=> "Author"
62
+ def humanize(lower_case_and_underscored_word)
63
+ lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
64
+ end
65
+
66
+ # Removes the module part from the expression in the string
67
+ #
68
+ # @example
69
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
70
+ # "Inflections".demodulize #=> "Inflections"
71
+ def demodulize(class_name_in_module)
72
+ class_name_in_module.to_s.gsub(/^.*::/, '')
73
+ end
74
+
75
+ # Create the name of a table like Rails does for models to table names. This method
76
+ # uses the pluralize method on the last word in the string.
77
+ #
78
+ # @example
79
+ # "RawScaledScorer".tableize #=> "raw_scaled_scorers"
80
+ # "egg_and_ham".tableize #=> "egg_and_hams"
81
+ # "fancyCategory".tableize #=> "fancy_categories"
82
+ def tableize(class_name)
83
+ pluralize(underscore(class_name))
84
+ end
85
+
86
+ # Creates a foreign key name from a class name.
87
+ #
88
+ # @example
89
+ # "Message".foreign_key #=> "message_id"
90
+ # "Admin::Post".foreign_key #=> "post_id"
91
+ def foreign_key(class_name, key = "id")
92
+ underscore(demodulize(class_name.to_s)) << "_" << key.to_s
93
+ end
94
+
95
+ # Constantize tries to find a declared constant with the name specified
96
+ # in the string. It raises a NameError when the name is not in CamelCase
97
+ # or is not initialized.
98
+ #
99
+ # @example
100
+ # "Module".constantize #=> Module
101
+ # "Class".constantize #=> Class
102
+ def constantize(camel_cased_word)
103
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
104
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
105
+ end
106
+
107
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
108
+ end
109
+
110
+ # The reverse of pluralize, returns the singular form of a word in a string.
111
+ # Wraps the English gem
112
+ #
113
+ # @example
114
+ # "posts".singularize #=> "post"
115
+ # "octopi".singularize #=> "octopus"
116
+ # "sheep".singluarize #=> "sheep"
117
+ # "word".singluarize #=> "word"
118
+ # "the blue mailmen".singularize #=> "the blue mailman"
119
+ # "CamelOctopi".singularize #=> "CamelOctopus"
120
+ #
121
+ def singularize(word)
122
+ English::Inflect.singular(word)
123
+ end
124
+
125
+ # Returns the plural form of the word in the string.
126
+ #
127
+ # @example
128
+ # "post".pluralize #=> "posts"
129
+ # "octopus".pluralize #=> "octopi"
130
+ # "sheep".pluralize #=> "sheep"
131
+ # "words".pluralize #=> "words"
132
+ # "the blue mailman".pluralize #=> "the blue mailmen"
133
+ # "CamelOctopus".pluralize #=> "CamelOctopi"
134
+ #
135
+ def pluralize(word)
136
+ English::Inflect.plural(word)
137
+ end
138
+
139
+ end
140
+ end # module Inflection
141
+ end # module Extlib