extlib 0.9.6 → 0.9.7

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/Rakefile CHANGED
@@ -46,7 +46,7 @@ spec = Gem::Specification.new do |s|
46
46
  s.extra_rdoc_files = ["LICENSE", "README.txt"]
47
47
 
48
48
  # Dependencies
49
- s.add_dependency "english", ">=0.2.0"
49
+ # s.add_dependency "english", ">=0.2.0"
50
50
  end
51
51
 
52
52
  Rake::GemPackageTask.new(spec) do |package|
@@ -126,63 +126,37 @@ namespace :ci do
126
126
  out = ENV['CC_BUILD_ARTIFACTS'] || "out"
127
127
  mkdir_p out unless File.directory? out
128
128
 
129
- mv "ci/unit_rspec_report.html", "#{out}/unit_rspec_report.html"
130
- mv "ci/unit_coverage", "#{out}/unit_coverage"
131
- mv "ci/integration_rspec_report.html", "#{out}/integration_rspec_report.html"
132
- mv "ci/integration_coverage", "#{out}/integration_coverage"
129
+ mv "ci/rspec_report.html", "#{out}/rspec_report.html"
130
+ mv "ci/coverage", "#{out}/coverage"
133
131
  mv "ci/doc", "#{out}/doc"
134
132
  mv "ci/cyclomatic", "#{out}/cyclomatic_complexity"
135
133
  mv "ci/token", "#{out}/token_complexity"
136
134
  end
137
135
 
138
-
139
- Spec::Rake::SpecTask.new("spec:unit" => :prepare) do |t|
140
- t.spec_opts = ["--format", "specdoc", "--format", "html:#{ROOT}/ci/unit_rspec_report.html", "--diff"]
141
- t.spec_files = Pathname.glob(ROOT + "spec/unit/**/*_spec.rb")
142
- unless ENV['NO_RCOV']
143
- t.rcov = true
144
- t.rcov_opts << '--exclude' << "spec,gems"
145
- t.rcov_opts << '--text-summary'
146
- t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
147
- t.rcov_opts << '--only-uncovered'
148
- end
149
- end
150
-
151
- Spec::Rake::SpecTask.new("spec:integration" => :prepare) do |t|
152
- t.spec_opts = ["--format", "specdoc", "--format", "html:#{ROOT}/ci/integration_rspec_report.html", "--diff"]
153
- t.spec_files = Pathname.glob(ROOT + "spec/integration/**/*_spec.rb")
154
- unless ENV['NO_RCOV']
155
- t.rcov = true
156
- t.rcov_opts << '--exclude' << "spec,gems"
157
- t.rcov_opts << '--text-summary'
158
- t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
159
- t.rcov_opts << '--only-uncovered'
160
- end
161
- end
162
-
163
- task :spec do
164
- Rake::Task["ci:spec:unit"].invoke
165
- mv ROOT + "coverage", ROOT + "ci/unit_coverage"
166
-
167
- Rake::Task["ci:spec:integration"].invoke
168
- mv ROOT + "coverage", ROOT + "ci/integration_coverage"
136
+ task :spec => :prepare do
137
+ Rake::Task[:spec].invoke
138
+ mv ROOT + "coverage", ROOT + "ci/coverage"
169
139
  end
170
140
 
171
141
  task :doc do
172
- require 'yardoc'
142
+ require 'yard'
173
143
  sh 'yardoc'
174
144
  end
175
145
 
176
- task :saikuro => :prepare do
146
+ task :saikuro do
177
147
  system "saikuro -c -i lib -y 0 -w 10 -e 15 -o ci/cyclomatic"
178
148
  mv 'ci/cyclomatic/index_cyclo.html', 'ci/cyclomatic/index.html'
179
149
 
180
150
  system "saikuro -t -i lib -y 0 -w 20 -e 30 -o ci/token"
181
151
  mv 'ci/token/index_token.html', 'ci/token/index.html'
182
152
  end
153
+
183
154
  end
184
155
 
185
- task :ci => ["ci:spec", "ci:doc", "ci:saikuro", :install, :publish]
156
+ task :ci => ["ci:spec"]
157
+
158
+ desc 'Default: run spec examples'
159
+ task :default => 'spec'
186
160
 
187
161
  ##############################################################################
188
162
  # Benchmarks
@@ -197,4 +171,4 @@ namespace :benchmark do
197
171
  system "ruby #{f}"
198
172
  end
199
173
  end
200
- end
174
+ end
data/lib/extlib.rb CHANGED
@@ -31,8 +31,26 @@ require dir / 'module'
31
31
  require dir / 'nil'
32
32
  require dir / 'numeric'
33
33
  require dir / 'blank'
34
- require dir / 'pooling'
35
34
  require dir / 'simple_set'
36
35
  require dir / 'struct'
37
- require dir / 'hook'
38
36
  require dir / 'symbol'
37
+
38
+ Extlib.autoload("Hook", (dir / 'hook').to_s)
39
+ Extlib.autoload("Pooling", (dir / 'pooling').to_s)
40
+
41
+ module Extlib
42
+
43
+ def self.exiting= bool
44
+ if bool && Extlib.const_defined?("Pooling")
45
+ if Extlib::Pooling.scavenger?
46
+ Extlib::Pooling.scavenger.wakeup
47
+ end
48
+ end
49
+ @exiting = true
50
+ end
51
+
52
+ def self.exiting
53
+ @exiting
54
+ end
55
+
56
+ end
@@ -0,0 +1,433 @@
1
+ # TITLE:
2
+ #
3
+ # Dictionary
4
+ #
5
+ # AUTHORS:
6
+ #
7
+ # - Jan Molic
8
+ # - Thomas Sawyer
9
+ #
10
+ # CREDIT:
11
+ #
12
+ # - Andrew Johnson (merge, to_a, inspect, shift and Hash[])
13
+ # - Jeff Sharpe (reverse and reverse!)
14
+ # - Thomas Leitner (has_key? and key?)
15
+ #
16
+ # LICENSE:
17
+ #
18
+ # Copyright (c) 2005 Jan Molic, Thomas Sawyer
19
+ #
20
+ # Ruby License
21
+ #
22
+ # This module is free software. You may use, modify, and/or redistribute this
23
+ # software under the same terms as Ruby.
24
+ #
25
+ # This program is distributed in the hope that it will be useful, but WITHOUT
26
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
27
+ # FOR A PARTICULAR PURPOSE.
28
+ #
29
+ # Originally ported from OrderHash 2.0, Copyright (c) 2005 jan molic
30
+ #
31
+ # LOG:
32
+ #
33
+ # - 2007.10.31 trans
34
+ # Fixed initialize so the constructor blocks correctly effected dictionary
35
+ # rather then just the internal hash.
36
+
37
+ # = Dictionary
38
+ #
39
+ # The Dictionary class is a Hash that preserves order.
40
+ # So it has some array-like extensions also. By defualt
41
+ # a Dictionary object preserves insertion order, but any
42
+ # order can be specified including alphabetical key order.
43
+ #
44
+ # == Usage
45
+ #
46
+ # Just require this file and use Dictionary instead of Hash.
47
+ #
48
+ # # You can do simply
49
+ # hsh = Dictionary.new
50
+ # hsh['z'] = 1
51
+ # hsh['a'] = 2
52
+ # hsh['c'] = 3
53
+ # p hsh.keys #=> ['z','a','c']
54
+ #
55
+ # # or using Dictionary[] method
56
+ # hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
57
+ # p hsh.keys #=> ['z','a','c']
58
+ #
59
+ # # but this doesn't preserve order
60
+ # hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
61
+ # p hsh.keys #=> ['a','c','z']
62
+ #
63
+ # # Dictionary has useful extensions: push, pop and unshift
64
+ # p hsh.push('to_end', 15) #=> true, key added
65
+ # p hsh.push('to_end', 30) #=> false, already - nothing happen
66
+ # p hsh.unshift('to_begin', 50) #=> true, key added
67
+ # p hsh.unshift('to_begin', 60) #=> false, already - nothing happen
68
+ # p hsh.keys #=> ["to_begin", "a", "c", "z", "to_end"]
69
+ # p hsh.pop #=> ["to_end", 15], if nothing remains, return nil
70
+ # p hsh.keys #=> ["to_begin", "a", "c", "z"]
71
+ # p hsh.shift #=> ["to_begin", 30], if nothing remains, return nil
72
+ #
73
+ # == Usage Notes
74
+ #
75
+ # * You can use #order_by to set internal sort order.
76
+ # * #<< takes a two element [k,v] array and inserts.
77
+ # * Use ::auto which creates Dictionay sub-entries as needed.
78
+ # * And ::alpha which creates a new Dictionary sorted by key.
79
+
80
+ class Dictionary
81
+
82
+ include Enumerable
83
+
84
+ class << self
85
+ #--
86
+ # TODO is this needed? Doesn't the super class do this?
87
+ #++
88
+ def [](*args)
89
+ hsh = new
90
+ if Hash === args[0]
91
+ hsh.replace(args[0])
92
+ elsif (args.size % 2) != 0
93
+ raise ArgumentError, "odd number of elements for Hash"
94
+ else
95
+ while !args.empty?
96
+ hsh[args.shift] = args.shift
97
+ end
98
+ end
99
+ hsh
100
+ end
101
+
102
+ # Like #new but the block sets the order.
103
+ #
104
+ def new_by(*args, &blk)
105
+ new(*args).order_by(&blk)
106
+ end
107
+
108
+ # Alternate to #new which creates a dictionary sorted by key.
109
+ #
110
+ # d = Dictionary.alpha
111
+ # d["z"] = 1
112
+ # d["y"] = 2
113
+ # d["x"] = 3
114
+ # d #=> {"x"=>3,"y"=>2,"z"=>2}
115
+ #
116
+ # This is equivalent to:
117
+ #
118
+ # Dictionary.new.order_by { |key,value| key }
119
+ def alpha(*args, &block)
120
+ new(*args, &block).order_by_key
121
+ end
122
+
123
+ # Alternate to #new which auto-creates sub-dictionaries as needed.
124
+ #
125
+ # d = Dictionary.auto
126
+ # d["a"]["b"]["c"] = "abc" #=> { "a"=>{"b"=>{"c"=>"abc"}}}
127
+ #
128
+ def auto(*args)
129
+ #AutoDictionary.new(*args)
130
+ leet = lambda { |hsh, key| hsh[key] = new(&leet) }
131
+ new(*args, &leet)
132
+ end
133
+ end
134
+
135
+ # New Dictiionary.
136
+ def initialize(*args, &blk)
137
+ @order = []
138
+ @order_by = nil
139
+ if blk
140
+ dict = self # This ensure autmatic key entry effect the
141
+ oblk = lambda{ |hsh, key| blk[dict,key] } # dictionary rather then just the interal hash.
142
+ @hash = Hash.new(*args, &oblk)
143
+ else
144
+ @hash = Hash.new(*args)
145
+ end
146
+ end
147
+
148
+ def order
149
+ reorder if @order_by
150
+ @order
151
+ end
152
+
153
+ # Keep dictionary sorted by a specific sort order.
154
+ def order_by( &block )
155
+ @order_by = block
156
+ order
157
+ self
158
+ end
159
+
160
+ # Keep dictionary sorted by key.
161
+ #
162
+ # d = Dictionary.new.order_by_key
163
+ # d["z"] = 1
164
+ # d["y"] = 2
165
+ # d["x"] = 3
166
+ # d #=> {"x"=>3,"y"=>2,"z"=>2}
167
+ #
168
+ # This is equivalent to:
169
+ #
170
+ # Dictionary.new.order_by { |key,value| key }
171
+ #
172
+ # The initializer Dictionary#alpha also provides this.
173
+ def order_by_key
174
+ @order_by = lambda { |k,v| k }
175
+ order
176
+ self
177
+ end
178
+
179
+ # Keep dictionary sorted by value.
180
+ #
181
+ # d = Dictionary.new.order_by_value
182
+ # d["z"] = 1
183
+ # d["y"] = 2
184
+ # d["x"] = 3
185
+ # d #=> {"x"=>3,"y"=>2,"z"=>2}
186
+ #
187
+ # This is equivalent to:
188
+ #
189
+ # Dictionary.new.order_by { |key,value| value }
190
+ def order_by_value
191
+ @order_by = lambda { |k,v| v }
192
+ order
193
+ self
194
+ end
195
+
196
+ #
197
+ def reorder
198
+ if @order_by
199
+ assoc = @order.collect{ |k| [k,@hash[k]] }.sort_by(&@order_by)
200
+ @order = assoc.collect{ |k,v| k }
201
+ end
202
+ @order
203
+ end
204
+
205
+ def ==(hsh2)
206
+ if hsh2.is_a?( Dictionary )
207
+ @order == hsh2.order &&
208
+ @hash == hsh2.instance_variable_get("@hash")
209
+ else
210
+ false
211
+ end
212
+ end
213
+
214
+ def [] k
215
+ @hash[ k ]
216
+ end
217
+
218
+ def fetch(k, *a, &b)
219
+ @hash.fetch(k, *a, &b)
220
+ end
221
+
222
+ # Store operator.
223
+ #
224
+ # h[key] = value
225
+ #
226
+ # Or with additional index.
227
+ #
228
+ # h[key,index] = value
229
+ def []=(k, i=nil, v=nil)
230
+ if v
231
+ insert(i,k,v)
232
+ else
233
+ store(k,i)
234
+ end
235
+ end
236
+
237
+ def insert( i,k,v )
238
+ @order.insert( i,k )
239
+ @hash.store( k,v )
240
+ end
241
+
242
+ def store( a,b )
243
+ @order.push( a ) unless @hash.has_key?( a )
244
+ @hash.store( a,b )
245
+ end
246
+
247
+ def clear
248
+ @order = []
249
+ @hash.clear
250
+ end
251
+
252
+ def delete( key )
253
+ @order.delete( key )
254
+ @hash.delete( key )
255
+ end
256
+
257
+ def each_key
258
+ order.each { |k| yield( k ) }
259
+ self
260
+ end
261
+
262
+ def each_value
263
+ order.each { |k| yield( @hash[k] ) }
264
+ self
265
+ end
266
+
267
+ def each
268
+ order.each { |k| yield( k,@hash[k] ) }
269
+ self
270
+ end
271
+ alias each_pair each
272
+
273
+ def delete_if
274
+ order.clone.each { |k| delete k if yield(k,@hash[k]) }
275
+ self
276
+ end
277
+
278
+ def values
279
+ ary = []
280
+ order.each { |k| ary.push @hash[k] }
281
+ ary
282
+ end
283
+
284
+ def keys
285
+ order
286
+ end
287
+
288
+ def invert
289
+ hsh2 = self.class.new
290
+ order.each { |k| hsh2[@hash[k]] = k }
291
+ hsh2
292
+ end
293
+
294
+ def reject( &block )
295
+ self.dup.delete_if(&block)
296
+ end
297
+
298
+ def reject!( &block )
299
+ hsh2 = reject(&block)
300
+ self == hsh2 ? nil : hsh2
301
+ end
302
+
303
+ def replace( hsh2 )
304
+ @order = hsh2.order
305
+ @hash = hsh2.hash
306
+ end
307
+
308
+ def shift
309
+ key = order.first
310
+ key ? [key,delete(key)] : super
311
+ end
312
+
313
+ def unshift( k,v )
314
+ unless @hash.include?( k )
315
+ @order.unshift( k )
316
+ @hash.store( k,v )
317
+ true
318
+ else
319
+ false
320
+ end
321
+ end
322
+
323
+ def <<(kv)
324
+ push( *kv )
325
+ end
326
+
327
+ def push( k,v )
328
+ unless @hash.include?( k )
329
+ @order.push( k )
330
+ @hash.store( k,v )
331
+ true
332
+ else
333
+ false
334
+ end
335
+ end
336
+
337
+ def pop
338
+ key = order.last
339
+ key ? [key,delete(key)] : nil
340
+ end
341
+
342
+ def inspect
343
+ ary = []
344
+ each {|k,v| ary << k.inspect + "=>" + v.inspect}
345
+ '{' + ary.join(", ") + '}'
346
+ end
347
+
348
+ def dup
349
+ a = []
350
+ each{ |k,v| a << k; a << v }
351
+ self.class[*a]
352
+ end
353
+
354
+ def update( hsh2 )
355
+ hsh2.each { |k,v| self[k] = v }
356
+ reorder
357
+ self
358
+ end
359
+ alias :merge! update
360
+
361
+ def merge( hsh2 )
362
+ self.dup.update(hsh2)
363
+ end
364
+
365
+ def select
366
+ ary = []
367
+ each { |k,v| ary << [k,v] if yield k,v }
368
+ ary
369
+ end
370
+
371
+ def reverse!
372
+ @order.reverse!
373
+ self
374
+ end
375
+
376
+ def reverse
377
+ dup.reverse!
378
+ end
379
+
380
+ def first
381
+ @hash[order.first]
382
+ end
383
+
384
+ def last
385
+ @hash[order.last]
386
+ end
387
+
388
+ def length
389
+ @order.length
390
+ end
391
+ alias :size :length
392
+
393
+ def empty?
394
+ @hash.empty?
395
+ end
396
+
397
+ def has_key?(key)
398
+ @hash.has_key?(key)
399
+ end
400
+
401
+ def key?(key)
402
+ @hash.key?(key)
403
+ end
404
+
405
+ def to_a
406
+ ary = []
407
+ each { |k,v| ary << [k,v] }
408
+ ary
409
+ end
410
+
411
+ def to_json
412
+ buf = "["
413
+ map do |k,v|
414
+ buf << k.to_json
415
+ buf << ", "
416
+ buf << v.to_json
417
+ end.join(", ")
418
+ buf << "]"
419
+ buf
420
+ end
421
+
422
+ def to_s
423
+ self.to_a.to_s
424
+ end
425
+
426
+ def to_hash
427
+ @hash.dup
428
+ end
429
+
430
+ def to_h
431
+ @hash.dup
432
+ end
433
+ end
data/lib/extlib/hash.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'base64'
2
-
3
1
  class Hash
4
2
  class << self
5
3
  # Converts valid XML into a Ruby Hash structure.
@@ -162,7 +160,7 @@ class Hash
162
160
  # #=> 'one="1" two="TWO"'
163
161
  def to_xml_attributes
164
162
  map do |k,v|
165
- %{#{k.to_s.camel_case.sub(/^(.{1,1})/) { |m| m.downcase }}="#{v}"}
163
+ %{#{k.to_s.snake_case.sub(/^(.{1,1})/) { |m| m.downcase }}="#{v}"}
166
164
  end.join(' ')
167
165
  end
168
166
 
@@ -253,7 +251,7 @@ class REXMLUtilityNode
253
251
  self.typecasts["symbol"] = lambda{|v| v.to_sym}
254
252
  self.typecasts["string"] = lambda{|v| v.to_s}
255
253
  self.typecasts["yaml"] = lambda{|v| v.nil? ? nil : YAML.load(v)}
256
- self.typecasts["base64Binary"] = lambda{|v| Base64.decode64(v)}
254
+ self.typecasts["base64Binary"] = lambda{|v| v.unpack('m').first }
257
255
 
258
256
  self.available_typecasts = self.typecasts.keys
259
257
 
@@ -275,7 +273,7 @@ class REXMLUtilityNode
275
273
 
276
274
  def to_hash
277
275
  if @type == "file"
278
- f = StringIO.new(::Base64.decode64(@children.first || ""))
276
+ f = StringIO.new((@children.first || '').unpack('m').first)
279
277
  class << f
280
278
  attr_accessor :original_filename, :content_type
281
279
  end
@@ -1,19 +1,10 @@
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
- English::Inflect.singular_word "status", "status"
14
-
15
1
  module Extlib
2
+
3
+ # = English Nouns Number Inflection.
4
+ #
5
+ # This module provides english singular <-> plural noun inflections.
16
6
  module Inflection
7
+
17
8
  class << self
18
9
  # Take an underscored name and make it into a camelized name
19
10
  #
@@ -103,36 +94,342 @@ module Extlib
103
94
 
104
95
  Object.module_eval("::#{$1}", __FILE__, __LINE__)
105
96
  end
97
+ end
98
+
99
+ @singular_of = {}
100
+ @plural_of = {}
106
101
 
107
- # The reverse of pluralize, returns the singular form of a word in a string.
108
- # Wraps the English gem
102
+ @singular_rules = []
103
+ @plural_rules = []
104
+
105
+ class << self
106
+ # Defines a general inflection exception case.
109
107
  #
110
- # @example
111
- # "posts".singularize #=> "post"
112
- # "octopi".singularize #=> "octopus"
113
- # "sheep".singluarize #=> "sheep"
114
- # "word".singluarize #=> "word"
115
- # "the blue mailmen".singularize #=> "the blue mailman"
116
- # "CamelOctopi".singularize #=> "CamelOctopus"
108
+ # ==== Parameters
109
+ # singular<String>::
110
+ # singular form of the word
111
+ # plural<String>::
112
+ # plural form of the word
113
+ #
114
+ # ==== Examples
117
115
  #
118
- def singularize(word)
119
- English::Inflect.singular(word)
116
+ # Here we define erratum/errata exception case:
117
+ #
118
+ # English::Inflect.word "erratum", "errata"
119
+ #
120
+ # In case singular and plural forms are the same omit
121
+ # second argument on call:
122
+ #
123
+ # English::Inflect.word 'information'
124
+ def word(singular, plural=nil)
125
+ plural = singular unless plural
126
+ singular_word(singular, plural)
127
+ plural_word(singular, plural)
120
128
  end
121
129
 
122
- # Returns the plural form of the word in the string.
130
+ def clear(type = :all)
131
+ if type == :singular || type == :all
132
+ @singular_of = {}
133
+ @singular_rules = []
134
+ @singularization_rules, @singularization_regex = nil, nil
135
+ end
136
+ if type == :plural || type == :all
137
+ @singular_of = {}
138
+ @singular_rules = []
139
+ @singularization_rules, @singularization_regex = nil, nil
140
+ end
141
+ end
142
+
143
+
144
+ # Define a singularization exception.
123
145
  #
124
- # @example
125
- # "post".pluralize #=> "posts"
126
- # "octopus".pluralize #=> "octopi"
127
- # "sheep".pluralize #=> "sheep"
128
- # "words".pluralize #=> "words"
129
- # "the blue mailman".pluralize #=> "the blue mailmen"
130
- # "CamelOctopus".pluralize #=> "CamelOctopi"
146
+ # ==== Parameters
147
+ # singular<String>::
148
+ # singular form of the word
149
+ # plural<String>::
150
+ # plural form of the word
151
+ def singular_word(singular, plural)
152
+ @singular_of[plural] = singular
153
+ @singular_of[plural.capitalize] = singular.capitalize
154
+ end
155
+
156
+ # Define a pluralization exception.
157
+ #
158
+ # ==== Parameters
159
+ # singular<String>::
160
+ # singular form of the word
161
+ # plural<String>::
162
+ # plural form of the word
163
+ def plural_word(singular, plural)
164
+ @plural_of[singular] = plural
165
+ @plural_of[singular.capitalize] = plural.capitalize
166
+ end
167
+
168
+ # Define a general rule.
169
+ #
170
+ # ==== Parameters
171
+ # singular<String>::
172
+ # ending of the word in singular form
173
+ # plural<String>::
174
+ # ending of the word in plural form
175
+ # whole_word<Boolean>::
176
+ # for capitalization, since words can be
177
+ # capitalized (Man => Men) #
178
+ # ==== Examples
179
+ # Once the following rule is defined:
180
+ # English::Inflect.rule 'y', 'ies'
181
+ #
182
+ # You can see the following results:
183
+ # irb> "fly".plural
184
+ # => flies
185
+ # irb> "cry".plural
186
+ # => cries
187
+ # Define a general rule.
188
+
189
+ def rule(singular, plural, whole_word = false)
190
+ singular_rule(singular, plural)
191
+ plural_rule(singular, plural)
192
+ word(singular, plural) if whole_word
193
+ end
194
+
195
+ # Define a singularization rule.
196
+ #
197
+ # ==== Parameters
198
+ # singular<String>::
199
+ # ending of the word in singular form
200
+ # plural<String>::
201
+ # ending of the word in plural form
202
+ #
203
+ # ==== Examples
204
+ # Once the following rule is defined:
205
+ # English::Inflect.singular_rule 'o', 'oes'
206
+ #
207
+ # You can see the following results:
208
+ # irb> "heroes".singular
209
+ # => hero
210
+ def singular_rule(singular, plural)
211
+ @singular_rules << [singular, plural]
212
+ end
213
+
214
+ # Define a plurualization rule.
215
+ #
216
+ # ==== Parameters
217
+ # singular<String>::
218
+ # ending of the word in singular form
219
+ # plural<String>::
220
+ # ending of the word in plural form
221
+ #
222
+ # ==== Examples
223
+ # Once the following rule is defined:
224
+ # English::Inflect.singular_rule 'fe', 'ves'
131
225
  #
132
- def pluralize(word)
133
- English::Inflect.plural(word)
226
+ # You can see the following results:
227
+ # irb> "wife".plural
228
+ # => wives
229
+ def plural_rule(singular, plural)
230
+ @plural_rules << [singular, plural]
134
231
  end
135
232
 
233
+ # Read prepared singularization rules.
234
+ def singularization_rules
235
+ if defined?(@singularization_regex) && @singularization_regex
236
+ return [@singularization_regex, @singularization_hash]
237
+ end
238
+ # No sorting needed: Regexen match on longest string
239
+ @singularization_regex = Regexp.new("(" + @singular_rules.map {|s,p| p}.join("|") + ")$", "i")
240
+ @singularization_hash = Hash[*@singular_rules.flatten].invert
241
+ [@singularization_regex, @singularization_hash]
242
+ end
243
+
244
+ # Read prepared pluralization rules.
245
+ def pluralization_rules
246
+ if defined?(@pluralization_regex) && @pluralization_regex
247
+ return [@pluralization_regex, @pluralization_hash]
248
+ end
249
+ @pluralization_regex = Regexp.new("(" + @plural_rules.map {|s,p| s}.join("|") + ")$", "i")
250
+ @pluralization_hash = Hash[*@plural_rules.flatten]
251
+ [@pluralization_regex, @pluralization_hash]
252
+ end
253
+
254
+ attr_reader :singular_of, :plural_of
255
+
256
+ # Convert an English word from plurel to singular.
257
+ #
258
+ # "boys".singular #=> boy
259
+ # "tomatoes".singular #=> tomato
260
+ #
261
+ # ==== Parameters
262
+ # word<String>:: word to singularize
263
+ #
264
+ # ==== Returns
265
+ # <String>:: singularized form of word
266
+ #
267
+ # ==== Notes
268
+ # Aliased as singularize (a Railism)
269
+ def singular(word)
270
+ if result = singular_of[word]
271
+ return result.dup
272
+ end
273
+ result = word.dup
274
+ regex, hash = singularization_rules
275
+ result.sub!(regex) {|m| hash[m]}
276
+ singular_of[word] = result
277
+ return result
278
+ end
279
+
280
+ # Alias for #singular (a Railism).
281
+ #
282
+ alias_method(:singularize, :singular)
283
+
284
+ # Convert an English word from singular to plurel.
285
+ #
286
+ # "boy".plural #=> boys
287
+ # "tomato".plural #=> tomatoes
288
+ #
289
+ # ==== Parameters
290
+ # word<String>:: word to pluralize
291
+ #
292
+ # ==== Returns
293
+ # <String>:: pluralized form of word
294
+ #
295
+ # ==== Notes
296
+ # Aliased as pluralize (a Railism)
297
+ def plural(word)
298
+ # special exceptions
299
+ return "" if word == ""
300
+ if result = plural_of[word]
301
+ return result.dup
302
+ end
303
+ result = word.dup
304
+ regex, hash = pluralization_rules
305
+ result.sub!(regex) {|m| hash[m]}
306
+ plural_of[word] = result
307
+ return result
308
+ end
309
+
310
+ # Alias for #plural (a Railism).
311
+ alias_method(:pluralize, :plural)
136
312
  end
137
- end # module Inflection
138
- end # module Extlib
313
+
314
+ # One argument means singular and plural are the same.
315
+
316
+ word 'equipment'
317
+ word 'information'
318
+ word 'money'
319
+ word 'species'
320
+ word 'series'
321
+ word 'fish'
322
+ word 'sheep'
323
+ word 'moose'
324
+ word 'hovercraft'
325
+ word 'grass'
326
+ word 'rain'
327
+ word 'milk'
328
+ word 'rice'
329
+ word 'plurals'
330
+ word 'postgres'
331
+ word 'status'
332
+
333
+ # Two arguments defines a singular and plural exception.
334
+ word 'status' , 'status'
335
+ word 'Swiss' , 'Swiss'
336
+ word 'life' , 'lives'
337
+ word 'wife' , 'wives'
338
+ word 'goose' , 'geese'
339
+ word 'criterion' , 'criteria'
340
+ word 'alias' , 'aliases'
341
+ word 'status' , 'statuses'
342
+ word 'axis' , 'axes'
343
+ word 'crisis' , 'crises'
344
+ word 'testis' , 'testes'
345
+ word 'potato' , 'potatoes'
346
+ word 'tomato' , 'tomatoes'
347
+ word 'buffalo' , 'buffaloes'
348
+ word 'torpedo' , 'torpedoes'
349
+ word 'quiz' , 'quizzes'
350
+ word 'matrix' , 'matrices'
351
+ word 'vertex' , 'vertices'
352
+ word 'index' , 'indices'
353
+ word 'ox' , 'oxen'
354
+ word 'mouse' , 'mice'
355
+ word 'louse' , 'lice'
356
+ word 'thesis' , 'theses'
357
+ word 'thief' , 'thieves'
358
+ word 'analysis' , 'analyses'
359
+ word 'erratum' , 'errata'
360
+ word 'phenomenon', 'phenomena'
361
+ word 'octopus' , 'octopi'
362
+ word 'thesaurus' , 'thesauri'
363
+ word 'movie' , 'movies'
364
+ word 'cactus' , 'cacti'
365
+ word 'plus' , 'plusses'
366
+ word 'cross' , 'crosses'
367
+ word 'medium' , 'media'
368
+ word 'cow' , 'kine'
369
+ word 'datum' , 'data'
370
+ word 'basis' , 'bases'
371
+ word 'diagnosis' , 'diagnoses'
372
+
373
+ # One-way singularization exception (convert plural to singular).
374
+
375
+ # General rules.
376
+ rule 'person' , 'people', true
377
+ rule 'shoe' , 'shoes', true
378
+ rule 'hive' , 'hives', true
379
+ rule 'man' , 'men', true
380
+ rule 'child' , 'children', true
381
+ rule 'news' , 'news', true
382
+ rule 'rf' , 'rves'
383
+ rule 'af' , 'aves'
384
+ rule 'ero' , 'eroes'
385
+ rule 'man' , 'men'
386
+ rule 'ch' , 'ches'
387
+ rule 'sh' , 'shes'
388
+ rule 'ss' , 'sses'
389
+ rule 'ta' , 'tum'
390
+ rule 'ia' , 'ium'
391
+ rule 'ra' , 'rum'
392
+ rule 'ay' , 'ays'
393
+ rule 'ey' , 'eys'
394
+ rule 'oy' , 'oys'
395
+ rule 'uy' , 'uys'
396
+ rule 'y' , 'ies'
397
+ rule 'x' , 'xes'
398
+ rule 'lf' , 'lves'
399
+ rule 'ffe' , 'ffes'
400
+ rule 'afe' , 'aves'
401
+ rule 'ouse' , 'ouses'
402
+ # more cases of words ending in -oses not being singularized properly
403
+ # than cases of words ending in -osis
404
+ # rule 'osis' , 'oses'
405
+ rule 'ox' , 'oxes'
406
+ rule 'us' , 'uses'
407
+ rule '' , 's'
408
+
409
+ # One-way singular rules.
410
+
411
+ singular_rule 'of' , 'ofs' # proof
412
+ singular_rule 'o' , 'oes' # hero, heroes
413
+ singular_rule 'f' , 'ves'
414
+
415
+ # One-way plural rules.
416
+
417
+ #plural_rule 'fe' , 'ves' # safe, wife
418
+ plural_rule 's' , 'ses'
419
+ plural_rule 'ive' , 'ives' # don't want to snag wife
420
+ plural_rule 'fe' , 'ves' # don't want to snag perspectives
421
+
422
+
423
+ end
424
+ end
425
+
426
+ class String
427
+ def singular
428
+ Extlib::Inflection.singular(self)
429
+ end
430
+ alias_method(:singularize, :singular)
431
+ def plural
432
+ Extlib::Inflection.plural(self)
433
+ end
434
+ alias_method(:pluralize, :plural)
435
+ end
@@ -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 extend ].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.to_s) }
3
3
 
4
4
  include Enumerable
5
5
 
data/lib/extlib/mash.rb CHANGED
@@ -96,6 +96,17 @@ class Mash < Hash
96
96
  super(convert_key(key))
97
97
  end
98
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
+
99
110
  # Used to provide the same interface as Hash.
100
111
  #
101
112
  # @return <Mash> This mash unchanged.
@@ -126,13 +137,12 @@ class Mash < Hash
126
137
  #
127
138
  # @api private
128
139
  def convert_value(value)
129
- case value
130
- when Hash
140
+ if value.class == Hash
131
141
  value.to_mash
132
- when Array
142
+ elsif value.is_a?(Array)
133
143
  value.collect { |e| convert_value(e) }
134
- else
135
- value
144
+ else
145
+ value
136
146
  end
137
147
  end
138
148
  end
@@ -25,31 +25,43 @@ module Extlib
25
25
  # object is reset when it is released.
26
26
  module Pooling
27
27
 
28
+ def self.scavenger?
29
+ @scavenger && @scavenger.alive?
30
+ end
31
+
28
32
  def self.scavenger
29
- @scavenger || begin
33
+ if @scavenger.nil? || !@scavenger.alive?
30
34
  @scavenger = Thread.new do
31
- loop do
35
+ running = true
36
+ while running do
32
37
  # Sleep before we actually start doing anything.
33
38
  # Otherwise we might clean up something we just made
34
39
  sleep(scavenger_interval)
40
+
35
41
  lock.synchronize do
36
42
  pools.each do |pool|
37
43
  # This is a useful check, but non-essential, and right now it breaks lots of stuff.
38
44
  # if pool.expired?
39
45
  pool.lock.synchronize do
40
- if pool.used.size == 0
46
+ if pool.expired?
41
47
  pool.dispose
42
48
  end
43
49
  end
44
50
  # end
45
51
  end
52
+
53
+ # The pool is empty, we stop the scavenger
54
+ # It wil be restarted if new resources are added again
55
+ if pools.empty?
56
+ running = false
57
+ end
46
58
  end
47
59
  end # loop
48
60
  end
49
-
50
- @scavenger.priority = -10
51
- @scavenger
52
61
  end
62
+
63
+ @scavenger.priority = -10
64
+ @scavenger
53
65
  end
54
66
 
55
67
  def self.pools
@@ -79,7 +91,7 @@ module Extlib
79
91
  alias __new new
80
92
  end
81
93
 
82
- @__pools = Hash.new { |h,k| __pool_lock.synchronize { h[k] = Pool.new(target.pool_size, target, k) } }
94
+ @__pools = {}
83
95
  @__pool_lock = Mutex.new
84
96
  @__pool_wait = ConditionVariable.new
85
97
 
@@ -92,7 +104,7 @@ module Extlib
92
104
  end
93
105
 
94
106
  def self.new(*args)
95
- @__pools[args].new
107
+ (@__pools[args] ||= __pool_lock.synchronize { Pool.new(self.pool_size, self, args) }).new
96
108
  end
97
109
 
98
110
  def self.__pools
@@ -122,7 +134,7 @@ module Extlib
122
134
  @args = args
123
135
 
124
136
  @available = []
125
- @used = Set.new
137
+ @used = {}
126
138
  Extlib::Pooling::append_pool(self)
127
139
  end
128
140
 
@@ -144,14 +156,14 @@ module Extlib
144
156
  lock.synchronize do
145
157
  if @available.size > 0
146
158
  instance = @available.pop
147
- @used.add instance
159
+ @used[instance.object_id] = instance
148
160
  elsif @used.size < @max_size
149
161
  instance = @resource.__new(*@args)
150
162
  raise InvalidResourceError.new("#{@resource} constructor created a nil object") if instance.nil?
151
163
  raise InvalidResourceError.new("#{instance} is already part of the pool") if @used.include? instance
152
164
  instance.instance_variable_set(:@__pool, self)
153
165
  instance.instance_variable_set(:@__allocated_in_pool, Time.now)
154
- @used.add instance
166
+ @used[instance.object_id] = instance
155
167
  else
156
168
  # Let's see whether we have multiple threads
157
169
  # If we do, there is a chance we might be released
@@ -171,8 +183,9 @@ module Extlib
171
183
  end
172
184
 
173
185
  def release(instance)
186
+ instance.instance_variable_set(:@__allocated_in_pool, Time.now)
174
187
  lock.synchronize do
175
- @used.delete instance
188
+ @used.delete(instance.object_id)
176
189
  @available.push(instance)
177
190
  wait.signal
178
191
  end
@@ -182,7 +195,7 @@ module Extlib
182
195
  def delete(instance)
183
196
  lock.synchronize do
184
197
  instance.instance_variable_set(:@__pool, nil)
185
- used.delete instance
198
+ @used.delete(instance.object_id)
186
199
  end
187
200
  nil
188
201
  end
@@ -206,20 +219,16 @@ module Extlib
206
219
  !Extlib::Pooling::pools.delete?(self).nil?
207
220
  end
208
221
 
209
- # Disabled temporarily.
210
- #
211
- # def expired?
212
- # lock.synchronize do
213
- # @available.each do |instance|
214
- # if instance.instance_variable_get(:@__allocated_in_pool) + scavenge_interval < Time.now
215
- # instance.release
216
- # @available.delete(instance)
217
- # end
218
- # end
219
- #
220
- # size == 0
221
- # end
222
- # end
222
+ def expired?
223
+ @available.each do |instance|
224
+ if Extlib.exiting || instance.instance_variable_get(:@__allocated_in_pool) + Extlib::Pooling.scavenger_interval <= Time.now
225
+ instance.dispose
226
+ @available.delete(instance)
227
+ end
228
+ end
229
+
230
+ size == 0
231
+ end
223
232
 
224
233
  end
225
234
 
data/lib/extlib/string.rb CHANGED
@@ -127,6 +127,6 @@ class String
127
127
  # "%s %s %s" % %w(one two three) #=> "one two three"
128
128
  # "%3$s %2$s %1$s" % %w(one two three) #=> "three two one"
129
129
  def t(*values)
130
- self.class::translate(self) % values
130
+ self.class::translate(self) % values.collect! { |value| value.frozen? ? value : self.class::translate(value.to_s) }
131
131
  end
132
132
  end # class String
data/lib/extlib/time.rb CHANGED
@@ -5,7 +5,7 @@ class Time
5
5
  #
6
6
  # @example
7
7
  # Time.now.to_json # => "\"2008-03-28T17:54:20-05:00\""
8
- def to_json
8
+ def to_json(*)
9
9
  self.xmlschema.to_json
10
10
  end
11
11
 
@@ -1,3 +1,3 @@
1
1
  module Extlib
2
- VERSION = "0.9.6"
2
+ VERSION = "0.9.7"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: extlib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.6
4
+ version: 0.9.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Smoot
@@ -9,19 +9,10 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-09-09 00:00:00 +03:00
12
+ date: 2008-10-06 00:00:00 +03:00
13
13
  default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: english
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 0.2.0
24
- version:
14
+ dependencies: []
15
+
25
16
  description: Support library for DataMapper and Merb.
26
17
  email: ssmoot@gmail.com
27
18
  executables: []
@@ -41,6 +32,7 @@ files:
41
32
  - lib/extlib/boolean.rb
42
33
  - lib/extlib/class.rb
43
34
  - lib/extlib/datetime.rb
35
+ - lib/extlib/dictionary.rb
44
36
  - lib/extlib/hash.rb
45
37
  - lib/extlib/hook.rb
46
38
  - lib/extlib/inflection.rb