pax_global_header00006660000000000000000000000064120731054020014504gustar00rootroot0000000000000052 comment=b3ffef7b1e37c8bdb2eeb0b34dbdb082e01e5e09 extlib-0.9.16/000077500000000000000000000000001207310540200130705ustar00rootroot00000000000000extlib-0.9.16/.autotest000066400000000000000000000007671207310540200147530ustar00rootroot00000000000000Autotest.add_hook :initialize do |at| ignore = %w[ .git log plugins script tasks bin CHANGELOG FAQ MIT-LICENSE QUICKLINKS README ] ignore.each do |exception| at.add_exception(exception) end at.clear_mappings at.add_mapping(%r{^spec/.+_spec\.rb$}) do |filename,_| filename end at.add_mapping(%r{^lib/extlib/(.+)\.rb$}) do |_,match| [ "spec/#{match[1]}_spec.rb" ] end at.add_mapping(%r{^spec/spec_helper\.rb$}) do at.files_matching(%r{^spec/.+_spec\.rb$}) end end extlib-0.9.16/.document000066400000000000000000000000741207310540200147100ustar00rootroot00000000000000README.rdoc lib/**/*.rb bin/* features/**/*.feature LICENSE extlib-0.9.16/.gitignore000066400000000000000000000003521207310540200150600ustar00rootroot00000000000000## MAC OS .DS_Store ## TEXTMATE *.tmproj tmtags ## EMACS *~ \#* .\#* ## VIM *.swp ## Rubinius *.rbc ## PROJECT::GENERAL *.gem coverage rdoc pkg tmp doc log .yardoc measurements ## BUNDLER .bundle Gemfile.* ## PROJECT::SPECIFIC extlib-0.9.16/LICENSE000066400000000000000000000043361207310540200141030ustar00rootroot00000000000000Copyright (c) 2010 Dan Kubb Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --- --- Some portions of blank.rb and mash.rb are verbatim copies of software licensed under the MIT license. That license is included below: Copyright (c) 2005-2008 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. extlib-0.9.16/README.rdoc000066400000000000000000000010451207310540200146760ustar00rootroot00000000000000= extlib Support library for DataMapper and Merb. == Note on Patches/Pull Requests * Fork the project. * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. == Copyright Copyright (c) 2010 Dan Kubb. See LICENSE for details. extlib-0.9.16/Rakefile000066400000000000000000000013421207310540200145350ustar00rootroot00000000000000require 'rubygems' require 'rake' begin gem 'jeweler', '~> 1.8.4' require 'jeweler' Jeweler::Tasks.new do |gem| gem.name = 'extlib' gem.summary = 'Support library for Merb' gem.description = gem.summary gem.email = 'dan.kubb@gmail.com' gem.homepage = 'http://github.com/datamapper/extlib' gem.authors = [ 'Dan Kubb' ] gem.rubyforge_project = 'extlib' gem.add_development_dependency 'json_pure', '~> 1.4' gem.add_development_dependency 'rspec', '~> 1.3' end Jeweler::GemcutterTasks.new FileList['tasks/**/*.rake'].each { |task| load task } rescue LoadError puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler -v 1.8.4' end extlib-0.9.16/VERSION000066400000000000000000000000071207310540200141350ustar00rootroot000000000000000.9.16 extlib-0.9.16/extlib.gemspec000066400000000000000000000062061207310540200157300ustar00rootroot00000000000000# Generated by jeweler # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- Gem::Specification.new do |s| s.name = "extlib" s.version = "0.9.16" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Dan Kubb"] s.date = "2013-01-08" s.description = "Support library for Merb" s.email = "dan.kubb@gmail.com" s.extra_rdoc_files = [ "LICENSE", "README.rdoc" ] s.files = [ ".autotest", ".document", "LICENSE", "README.rdoc", "Rakefile", "VERSION", "extlib.gemspec", "lib/extlib.rb", "lib/extlib/array.rb", "lib/extlib/assertions.rb", "lib/extlib/blank.rb", "lib/extlib/boolean.rb", "lib/extlib/byte_array.rb", "lib/extlib/class.rb", "lib/extlib/datetime.rb", "lib/extlib/dictionary.rb", "lib/extlib/hash.rb", "lib/extlib/hook.rb", "lib/extlib/inflection.rb", "lib/extlib/lazy_array.rb", "lib/extlib/lazy_module.rb", "lib/extlib/local_object_space.rb", "lib/extlib/logger.rb", "lib/extlib/mash.rb", "lib/extlib/module.rb", "lib/extlib/nil.rb", "lib/extlib/numeric.rb", "lib/extlib/object.rb", "lib/extlib/object_space.rb", "lib/extlib/pathname.rb", "lib/extlib/pooling.rb", "lib/extlib/rubygems.rb", "lib/extlib/simple_set.rb", "lib/extlib/string.rb", "lib/extlib/struct.rb", "lib/extlib/symbol.rb", "lib/extlib/time.rb", "lib/extlib/try_dup.rb", "lib/extlib/virtual_file.rb", "spec/array_spec.rb", "spec/blank_spec.rb", "spec/byte_array_spec.rb", "spec/class_spec.rb", "spec/datetime_spec.rb", "spec/hash_spec.rb", "spec/hook_spec.rb", "spec/inflection/plural_spec.rb", "spec/inflection/singular_spec.rb", "spec/inflection_extras_spec.rb", "spec/lazy_array_spec.rb", "spec/lazy_module_spec.rb", "spec/mash_spec.rb", "spec/module_spec.rb", "spec/object_space_spec.rb", "spec/object_spec.rb", "spec/pooling_spec.rb", "spec/rcov.opts", "spec/simple_set_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/string_spec.rb", "spec/struct_spec.rb", "spec/symbol_spec.rb", "spec/time_spec.rb", "spec/try_call_spec.rb", "spec/try_dup_spec.rb", "spec/virtual_file_spec.rb", "tasks/ci.rake", "tasks/metrics.rake", "tasks/spec.rake", "tasks/yard.rake", "tasks/yardstick.rake" ] s.homepage = "http://github.com/datamapper/extlib" s.require_paths = ["lib"] s.rubyforge_project = "extlib" s.rubygems_version = "1.8.24" s.summary = "Support library for Merb" if s.respond_to? :specification_version then s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, ["~> 1.4"]) s.add_development_dependency(%q, ["~> 1.3"]) else s.add_dependency(%q, ["~> 1.4"]) s.add_dependency(%q, ["~> 1.3"]) end else s.add_dependency(%q, ["~> 1.4"]) s.add_dependency(%q, ["~> 1.3"]) end end extlib-0.9.16/lib/000077500000000000000000000000001207310540200136365ustar00rootroot00000000000000extlib-0.9.16/lib/extlib.rb000066400000000000000000000021421207310540200154510ustar00rootroot00000000000000require 'pathname' require 'extlib/pathname' require 'extlib/class.rb' require 'extlib/object' require 'extlib/object_space' require 'extlib/local_object_space' require 'extlib/array' require 'extlib/string' require 'extlib/symbol' require 'extlib/hash' require 'extlib/mash' require 'extlib/virtual_file' require 'extlib/logger' require 'extlib/time' require 'extlib/datetime' require 'extlib/assertions' require 'extlib/blank' require 'extlib/boolean' require 'extlib/byte_array' require 'extlib/inflection' require 'extlib/lazy_array' require 'extlib/module' require 'extlib/nil' require 'extlib/numeric' require 'extlib/blank' require 'extlib/simple_set' require 'extlib/struct' require 'extlib/symbol' require 'extlib/try_dup' Extlib.autoload('Hook', 'extlib/hook') Extlib.autoload('Pooling', 'extlib/pooling') module Extlib def self.exiting= bool if bool && Extlib.const_defined?('Pooling') if Extlib::Pooling.scavenger? Extlib::Pooling.scavenger.wakeup end end @exiting = true end def self.exiting return @exiting if defined?(@exiting) @exiting = false end end extlib-0.9.16/lib/extlib/000077500000000000000000000000001207310540200151255ustar00rootroot00000000000000extlib-0.9.16/lib/extlib/array.rb000066400000000000000000000015171207310540200165740ustar00rootroot00000000000000require 'extlib/mash' class Array ## # Transforms an Array of key/value pairs into a Hash # # This is a better idiom than using Hash[*array.flatten] in Ruby 1.8.6 # because it is not possible to limit the flattening to a single # level. # # @return [Hash] # A Hash where each entry in the Array is turned into a key/value # # @api public def to_hash h = {} each { |k,v| h[k] = v } h end ## # Transforms an Array of key/value pairs into a Mash # # This is a better idiom than using Mash[*array.flatten] in Ruby 1.8.6 # because it is not possible to limit the flattening to a single # level. # # @return [Mash] # A Hash where each entry in the Array is turned into a key/value # # @api public def to_mash m = Mash.new each { |k,v| m[k] = v } m end end # class Array extlib-0.9.16/lib/extlib/assertions.rb000066400000000000000000000004311207310540200176420ustar00rootroot00000000000000module Extlib module Assertions def assert_kind_of(name, value, *klasses) klasses.each { |k| return if value.kind_of?(k) } raise ArgumentError, "+#{name}+ should be #{klasses.map { |k| k.name } * ' or '}, but was #{value.class.name}", caller(2) end end end extlib-0.9.16/lib/extlib/blank.rb000066400000000000000000000026431207310540200165460ustar00rootroot00000000000000class Object ## # Returns true if the object is nil or empty (if applicable) # # [].blank? #=> true # [1].blank? #=> false # [nil].blank? #=> false # # @return [TrueClass, FalseClass] # # @api public def blank? nil? || (respond_to?(:empty?) && empty?) end end # class Object class Numeric ## # Numerics are never blank # # 0.blank? #=> false # 1.blank? #=> false # 6.54321.blank? #=> false # # @return [FalseClass] # # @api public def blank? false end end # class Numeric class NilClass ## # Nil is always blank # # nil.blank? #=> true # # @return [TrueClass] # # @api public def blank? true end end # class NilClass class TrueClass ## # True is never blank. # # true.blank? #=> false # # @return [FalseClass] # # @api public def blank? false end end # class TrueClass class FalseClass ## # False is always blank. # # false.blank? #=> true # # @return [TrueClass] # # @api public def blank? true end end # class FalseClass class String ## # Strips out whitespace then tests if the string is empty. # # "".blank? #=> true # " ".blank? #=> true # " hey ho ".blank? #=> false # # @return [TrueClass, FalseClass] # # @api public def blank? strip.empty? end end # class String extlib-0.9.16/lib/extlib/boolean.rb000066400000000000000000000001441207310540200170700ustar00rootroot00000000000000class TrueClass def try_dup self end end class FalseClass def try_dup self end end extlib-0.9.16/lib/extlib/byte_array.rb000066400000000000000000000004061207310540200176130ustar00rootroot00000000000000# This class has exists to represent binary data. This is mainly # used by DataObjects. Binary data sometimes needs to be quoted differently # than regular string data (even if the string is just plain ASCII). module Extlib class ByteArray < ::String; end end extlib-0.9.16/lib/extlib/class.rb000066400000000000000000000150541207310540200165640ustar00rootroot00000000000000require 'extlib/try_dup' # Copyright (c) 2004-2008 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Allows attributes to be shared within an inheritance hierarchy, but where # each descendant gets a copy of their parents' attributes, instead of just a # pointer to the same. This means that the child can add elements to, for # example, an array without those additions being shared with either their # parent, siblings, or children, which is unlike the regular class-level # attributes that are shared across the entire hierarchy. class Class # Defines class-level and instance-level attribute reader. # # @param [*syms] List of attributes that were made into cattr_readers # # @api public # # @todo Is this inconsistent in that it does not allow you to prevent # an instance_reader via :instance_reader => false def cattr_reader(*syms) syms.flatten.each do |sym| next if sym.is_a?(Hash) class_eval(<<-RUBY, __FILE__, __LINE__ + 1) unless defined? @@#{sym} @@#{sym} = nil end def self.#{sym} @@#{sym} end def #{sym} @@#{sym} end RUBY end end # Defines class-level (and optionally instance-level) attribute writer. # # @param [Array<*#to_s, Hash{:instance_writer => Boolean}>] Array of attributes to define writer for. # @option syms :instance_writer if true, instance-level attribute writer is defined. # @return [Array<#to_s>] List of attributes that were made into cattr_writers # # @api public def cattr_writer(*syms) options = syms.last.is_a?(Hash) ? syms.pop : {} syms.flatten.each do |sym| class_eval(<<-RUBY, __FILE__, __LINE__ + 1) unless defined? @@#{sym} @@#{sym} = nil end def self.#{sym}=(obj) @@#{sym} = obj end RUBY unless options[:instance_writer] == false class_eval(<<-RUBY, __FILE__, __LINE__ + 1) def #{sym}=(obj) @@#{sym} = obj end RUBY end end end # Defines class-level (and optionally instance-level) attribute accessor. # # @param *syms Boolean}]> Array of attributes to define accessor for. # @option syms :instance_writer if true, instance-level attribute writer is defined. # @return [Array<#to_s>] List of attributes that were made into accessors # # @api public def cattr_accessor(*syms) cattr_reader(*syms) cattr_writer(*syms) end # Defines class-level inheritable attribute reader. Attributes are available to subclasses, # each subclass has a copy of parent's attribute. # # @param *syms Array of attributes to define inheritable reader for. # @return [Array<#to_s>] Array of attributes converted into inheritable_readers. # # @api public # # @todo Do we want to block instance_reader via :instance_reader => false # @todo It would be preferable that we do something with a Hash passed in # (error out or do the same as other methods above) instead of silently # moving on). In particular, this makes the return value of this function # less useful. def class_inheritable_reader(*ivars) instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash) ivars.each do |ivar| self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 def self.#{ivar} return @#{ivar} if defined?(@#{ivar}) return nil if self.object_id == #{self.object_id} ivar = superclass.#{ivar} return nil if ivar.nil? @#{ivar} = ivar.try_dup end RUBY unless instance_reader == false self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{ivar} self.class.#{ivar} end RUBY end end end # Defines class-level inheritable attribute writer. Attributes are available to subclasses, # each subclass has a copy of parent's attribute. # # @param *syms Boolean}]> Array of attributes to # define inheritable writer for. # @option syms :instance_writer if true, instance-level inheritable attribute writer is defined. # @return [Array<#to_s>] An Array of the attributes that were made into inheritable writers. # # @api public # # @todo We need a style for class_eval <<-HEREDOC. I'd like to make it # class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere. def class_inheritable_writer(*ivars) instance_writer = ivars.pop[:instance_writer] if ivars.last.is_a?(Hash) ivars.each do |ivar| self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 def self.#{ivar}=(obj) @#{ivar} = obj end RUBY unless instance_writer == false self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{ivar}=(obj) self.class.#{ivar} = obj end RUBY end end end # Defines class-level inheritable attribute accessor. Attributes are available to subclasses, # each subclass has a copy of parent's attribute. # # @param *syms Boolean}]> Array of attributes to # define inheritable accessor for. # @option syms :instance_writer if true, instance-level inheritable attribute writer is defined. # @return [Array<#to_s>] An Array of attributes turned into inheritable accessors. # # @api public def class_inheritable_accessor(*syms) class_inheritable_reader(*syms) class_inheritable_writer(*syms) end end extlib-0.9.16/lib/extlib/datetime.rb000066400000000000000000000013451207310540200172510ustar00rootroot00000000000000require 'time' class DateTime ## # Convert to Time object (for DateTime/Time conversion protocol). # # DateTime.now.to_time #=> Wed Nov 19 20:04:51 -0800 2008 # # @return [Time] Time object representing the same moment as receiver # # @api public remove_method :to_time if instance_methods(false).any? { |m| m.to_sym == :to_time } def to_time Time.parse self.to_s end ## # Return receiver (for DateTime/Time conversion protocol). # # DateTime.now.to_datetime #=> # # # @return [DateTime] Receiver # # @api public remove_method :to_datetime if instance_methods(false).any? { |m| m.to_sym == :to_datetime } def to_datetime self end end extlib-0.9.16/lib/extlib/dictionary.rb000066400000000000000000000201551207310540200176220ustar00rootroot00000000000000# TITLE: # # Dictionary # # AUTHORS: # # - Jan Molic # - Thomas Sawyer # # CREDIT: # # - Andrew Johnson (merge, to_a, inspect, shift and Hash[]) # - Jeff Sharpe (reverse and reverse!) # - Thomas Leitner (has_key? and key?) # # LICENSE: # # Copyright (c) 2005 Jan Molic, Thomas Sawyer # # Ruby License # # This module is free software. You may use, modify, and/or redistribute this # software under the same terms as Ruby. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. # # Originally ported from OrderHash 2.0, Copyright (c) 2005 jan molic # # LOG: # # - 2007.10.31 trans # Fixed initialize so the constructor blocks correctly effected dictionary # rather then just the internal hash. # = Dictionary # # The Dictionary class is a Hash that preserves order. # So it has some array-like extensions also. By defualt # a Dictionary object preserves insertion order, but any # order can be specified including alphabetical key order. # # == Usage # # Just require this file and use Dictionary instead of Hash. # # # You can do simply # hsh = Dictionary.new # hsh['z'] = 1 # hsh['a'] = 2 # hsh['c'] = 3 # p hsh.keys #=> ['z','a','c'] # # # or using Dictionary[] method # hsh = Dictionary['z', 1, 'a', 2, 'c', 3] # p hsh.keys #=> ['z','a','c'] # # # but this doesn't preserve order # hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3] # p hsh.keys #=> ['a','c','z'] # # # Dictionary has useful extensions: push, pop and unshift # p hsh.push('to_end', 15) #=> true, key added # p hsh.push('to_end', 30) #=> false, already - nothing happen # p hsh.unshift('to_begin', 50) #=> true, key added # p hsh.unshift('to_begin', 60) #=> false, already - nothing happen # p hsh.keys #=> ["to_begin", "a", "c", "z", "to_end"] # p hsh.pop #=> ["to_end", 15], if nothing remains, return nil # p hsh.keys #=> ["to_begin", "a", "c", "z"] # p hsh.shift #=> ["to_begin", 30], if nothing remains, return nil # # == Usage Notes # # * You can use #order_by to set internal sort order. # * #<< takes a two element [k,v] array and inserts. # * Use ::auto which creates Dictionay sub-entries as needed. # * And ::alpha which creates a new Dictionary sorted by key. class Dictionary include Enumerable class << self #-- # TODO is this needed? Doesn't the super class do this? #++ def [](*args) hsh = new if Hash === args[0] hsh.replace(args[0]) elsif (args.size % 2) != 0 raise ArgumentError, "odd number of elements for Hash" else while !args.empty? hsh[args.shift] = args.shift end end hsh end # Like #new but the block sets the order. # def new_by(*args, &blk) new(*args).order_by(&blk) end # Alternate to #new which creates a dictionary sorted by key. # # d = Dictionary.alpha # d["z"] = 1 # d["y"] = 2 # d["x"] = 3 # d #=> {"x"=>3,"y"=>2,"z"=>2} # # This is equivalent to: # # Dictionary.new.order_by { |key,value| key } def alpha(*args, &block) new(*args, &block).order_by_key end # Alternate to #new which auto-creates sub-dictionaries as needed. # # d = Dictionary.auto # d["a"]["b"]["c"] = "abc" #=> { "a"=>{"b"=>{"c"=>"abc"}}} # def auto(*args) #AutoDictionary.new(*args) leet = lambda { |hsh, key| hsh[key] = new(&leet) } new(*args, &leet) end end # New Dictiionary. def initialize(*args, &blk) @order = [] @order_by = nil if blk dict = self # This ensure autmatic key entry effect the oblk = lambda{ |hsh, key| blk[dict,key] } # dictionary rather then just the interal hash. @hash = Hash.new(*args, &oblk) else @hash = Hash.new(*args) end end def order reorder if @order_by @order end # Keep dictionary sorted by a specific sort order. def order_by( &block ) @order_by = block order self end # Keep dictionary sorted by key. # # d = Dictionary.new.order_by_key # d["z"] = 1 # d["y"] = 2 # d["x"] = 3 # d #=> {"x"=>3,"y"=>2,"z"=>2} # # This is equivalent to: # # Dictionary.new.order_by { |key,value| key } # # The initializer Dictionary#alpha also provides this. def order_by_key @order_by = lambda { |k,v| k } order self end # Keep dictionary sorted by value. # # d = Dictionary.new.order_by_value # d["z"] = 1 # d["y"] = 2 # d["x"] = 3 # d #=> {"x"=>3,"y"=>2,"z"=>2} # # This is equivalent to: # # Dictionary.new.order_by { |key,value| value } def order_by_value @order_by = lambda { |k,v| v } order self end # def reorder if @order_by assoc = @order.collect{ |k| [k,@hash[k]] }.sort_by(&@order_by) @order = assoc.collect{ |k,v| k } end @order end def ==(hsh2) if hsh2.is_a?( Dictionary ) @order == hsh2.order && @hash == hsh2.instance_variable_get("@hash") else false end end def [] k @hash[ k ] end def fetch(k, *a, &b) @hash.fetch(k, *a, &b) end # Store operator. # # h[key] = value # # Or with additional index. # # h[key,index] = value def []=(k, i=nil, v=nil) if v insert(i,k,v) else store(k,i) end end def insert( i,k,v ) @order.insert( i,k ) @hash.store( k,v ) end def store( a,b ) @order.push( a ) unless @hash.has_key?( a ) @hash.store( a,b ) end def clear @order = [] @hash.clear end def delete( key ) @order.delete( key ) @hash.delete( key ) end def each_key order.each { |k| yield( k ) } self end def each_value order.each { |k| yield( @hash[k] ) } self end def each order.each { |k| yield( k,@hash[k] ) } self end alias each_pair each def delete_if order.clone.each { |k| delete k if yield(k,@hash[k]) } self end def values ary = [] order.each { |k| ary.push @hash[k] } ary end def keys order end def invert hsh2 = self.class.new order.each { |k| hsh2[@hash[k]] = k } hsh2 end def reject( &block ) self.dup.delete_if(&block) end def reject!( &block ) hsh2 = reject(&block) self == hsh2 ? nil : hsh2 end def replace( hsh2 ) @order = hsh2.order @hash = hsh2.hash end def shift key = order.first key ? [key,delete(key)] : super end def unshift( k,v ) unless @hash.include?( k ) @order.unshift( k ) @hash.store( k,v ) true else false end end def <<(kv) push( *kv ) end def push( k,v ) unless @hash.include?( k ) @order.push( k ) @hash.store( k,v ) true else false end end def pop key = order.last key ? [key,delete(key)] : nil end def inspect ary = [] each {|k,v| ary << k.inspect + "=>" + v.inspect} '{' + ary.join(", ") + '}' end def dup a = [] each{ |k,v| a << k; a << v } self.class[*a] end def update( hsh2 ) hsh2.each { |k,v| self[k] = v } reorder self end alias :merge! update def merge( hsh2 ) self.dup.update(hsh2) end def select ary = [] each { |k,v| ary << [k,v] if yield k,v } ary end def reverse! @order.reverse! self end def reverse dup.reverse! end def first @hash[order.first] end def last @hash[order.last] end def length @order.length end alias :size :length def empty? @hash.empty? end def has_key?(key) @hash.has_key?(key) end def key?(key) @hash.key?(key) end def to_a ary = [] each { |k,v| ary << [k,v] } ary end def to_json buf = "[" map do |k,v| buf << k.to_json buf << ", " buf << v.to_json end.join(", ") buf << "]" buf end def to_s self.to_a.to_s end def to_hash @hash.dup end def to_h @hash.dup end end extlib-0.9.16/lib/extlib/hash.rb000066400000000000000000000312641207310540200164030ustar00rootroot00000000000000require 'bigdecimal' require 'time' require 'uri' require 'extlib/object' require 'extlib/mash' require 'extlib/class' require 'extlib/string' class Hash URI_ENCODE_PATTERN = Regexp.new("[^#{URI::PATTERN::UNRESERVED}]").freeze class << self # Converts valid XML into a Ruby Hash structure. # # Mixed content is treated as text and any tags in it are left unparsed # # Any attributes other than type on a node containing a text node will be # discarded # # [Typecasting is performed on elements that have a +type+ attribute:] # integer:: Returns an Integer # boolean:: Anything other than "true" evaluates to false. # datetime:: # Returns a Time object. See Time documentation for valid Time strings. # date:: # Returns a Date object. See Date documentation for valid Date strings. # # Keys are automatically converted to +snake_case+ # # [Simple] # # # 35 # Home Simpson # 1988-01-01 # 2000-04-28 23:01 # true # # # Becomes: # # { "user" => { # "gender" => "m", # "age" => 35, # "name" => "Home Simpson", # "dob" => DateObject( 1998-01-01 ), # "joined_at" => TimeObject( 2000-04-28 23:01), # "is_cool" => true # } # } # # [Mixed Content] # # # A Quick brown Fox # # # Evaluates to: # # { "story" => "A Quick brown Fox" } # # [Attributes other than type on a node containing text] # # # A Quick brown Fox # # # Are ignored: # # { "story" => "A Quick brown Fox" } # # [Other attributes in addition to +type+] # # 60 # # Evaluates with a typecast to an integer. But unit attribute is ignored: # # { "bicep" => 60 } # # @param [String] xml A string representation of valid XML. # # @return [Hash] A hash created by parsing +xml+ def from_xml( xml ) ToHashParser.from_xml(xml) end end # Convert to Mash. This class has semantics of ActiveSupport's # HashWithIndifferentAccess and we only have it so that people can write # params[:key] instead of params['key']. # # @return [Mash] This hash as a Mash for string or symbol key access. def to_mash hash = Mash.new(self) hash.default = default hash end ## # Convert to URL query param string # # { :name => "Bob", # :address => { # :street => '111 Ruby Ave.', # :city => 'Ruby Central', # :phones => ['111-111-1111', '222-222-2222'] # } # }.to_params # #=> "name=Bob&address[city]=Ruby Central&address[phones][]=111-111-1111&address[phones][]=222-222-2222&address[street]=111 Ruby Ave." # # @return [String] This hash as a query string # # @api public def to_params params = self.map { |k,v| normalize_param(k,v) }.join params.chop! # trailing & params end ## # Convert a key, value pair into a URL query param string # # normalize_param(:name, "Bob") #=> "name=Bob&" # # @param [Object] key The key for the param. # @param [Object] value The value for the param. # # @return [String] This key value pair as a param # # @api public def normalize_param(key, value) param = '' stack = [] if value.is_a?(Array) param << value.map { |element| normalize_param("#{key}[]", element) }.join elsif value.is_a?(Hash) stack << [key,value] else param << [key,value].map {|v| URI.encode(v.to_s, URI_ENCODE_PATTERN)}.join('=') + '&' end stack.each do |parent, hash| hash.each do |k, v| if v.is_a?(Hash) stack << ["#{parent}[#{k}]", v] else param << normalize_param("#{parent}[#{k}]", v) end end end param end ## # Create a hash with *only* key/value pairs in receiver and +allowed+ # # { :one => 1, :two => 2, :three => 3 }.only(:one) #=> { :one => 1 } # # @param [Array[String, Symbol]] *allowed The hash keys to include. # # @return [Hash] A new hash with only the selected keys. # # @api public def only(*allowed) hash = {} allowed.each {|k| hash[k] = self[k] if self.has_key?(k) } hash end ## # Create a hash with all key/value pairs in receiver *except* +rejected+ # # { :one => 1, :two => 2, :three => 3 }.except(:one) # #=> { :two => 2, :three => 3 } # # @param [Array[String, Symbol]] *rejected The hash keys to exclude. # # @return [Hash] A new hash without the selected keys. # # @api public def except(*rejected) hash = self.dup rejected.each {|k| hash.delete(k) } hash end # @return [String] The hash as attributes for an XML tag. # # @example # { :one => 1, "two"=>"TWO" }.to_xml_attributes # #=> 'one="1" two="TWO"' def to_xml_attributes map do |k,v| %{#{k.to_s.snake_case.sub(/^(.{1,1})/) { |m| m.downcase }}="#{v}"} end.join(' ') end alias_method :to_html_attributes, :to_xml_attributes # @param html_class<#to_s> # The HTML class to add to the :class key. The html_class will be # concatenated to any existing classes. # # @example hash[:class] #=> nil # @example hash.add_html_class!(:selected) # @example hash[:class] #=> "selected" # @example hash.add_html_class!("class1 class2") # @example hash[:class] #=> "selected class1 class2" def add_html_class!(html_class) if self[:class] self[:class] = "#{self[:class]} #{html_class}" else self[:class] = html_class.to_s end end # Converts all keys into string values. This is used during reloading to # prevent problems when classes are no longer declared. # # @return [Array] An array of they hash's keys # # @example # hash = { One => 1, Two => 2 }.proctect_keys! # hash # => { "One" => 1, "Two" => 2 } def protect_keys! keys.each {|key| self[key.to_s] = delete(key) } end # Attempts to convert all string keys into Class keys. We run this after # reloading to convert protected hashes back into usable hashes. # # @example # # Provided that classes One and Two are declared in this scope: # hash = { "One" => 1, "Two" => 2 }.unproctect_keys! # hash # => { One => 1, Two => 2 } def unprotect_keys! keys.each do |key| (self[Object.full_const_get(key)] = delete(key)) rescue nil end end # Destructively and non-recursively convert each key to an uppercase string, # deleting nil values along the way. # # @return [Hash] The newly environmentized hash. # # @example # { :name => "Bob", :contact => { :email => "bob@bob.com" } }.environmentize_keys! # #=> { "NAME" => "Bob", "CONTACT" => { :email => "bob@bob.com" } } def environmentize_keys! keys.each do |key| val = delete(key) next if val.nil? self[key.to_s.upcase] = val end self end end require 'rexml/parsers/streamparser' require 'rexml/parsers/baseparser' require 'rexml/light/node' # This is a slighly modified version of the XMLUtilityNode from # http://merb.devjavu.com/projects/merb/ticket/95 (has.sox@gmail.com) # It's mainly just adding vowels, as I ht cd wth n vwls :) # This represents the hard part of the work, all I did was change the # underlying parser. class REXMLUtilityNode attr_accessor :name, :attributes, :children, :type cattr_accessor :typecasts, :available_typecasts self.typecasts = {} self.typecasts["integer"] = lambda{|v| v.nil? ? nil : v.to_i} self.typecasts["boolean"] = lambda{|v| v.nil? ? nil : (v.strip != "false")} self.typecasts["datetime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc} self.typecasts["date"] = lambda{|v| v.nil? ? nil : Date.parse(v)} self.typecasts["dateTime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc} self.typecasts["decimal"] = lambda{|v| BigDecimal(v)} self.typecasts["double"] = lambda{|v| v.nil? ? nil : v.to_f} self.typecasts["float"] = lambda{|v| v.nil? ? nil : v.to_f} self.typecasts["string"] = lambda{|v| v.to_s} self.typecasts["base64Binary"] = lambda{|v| v.unpack('m').first } self.available_typecasts = self.typecasts.keys def initialize(name, attributes = {}) @name = name.tr("-", "_") # leave the type alone if we don't know what it is @type = self.class.available_typecasts.include?(attributes["type"]) ? attributes.delete("type") : attributes["type"] @nil_element = attributes.delete("nil") == "true" @attributes = undasherize_keys(attributes) @children = [] @text = false end def add_node(node) @text = true if node.is_a? String @children << node end def to_hash if @type == "file" f = StringIO.new((@children.first || '').unpack('m').first) class << f attr_accessor :original_filename, :content_type end f.original_filename = attributes['name'] || 'untitled' f.content_type = attributes['content_type'] || 'application/octet-stream' return {name => f} end if @text return { name => typecast_value( translate_xml_entities( inner_html ) ) } else #change repeating groups into an array groups = @children.inject({}) { |s,e| (s[e.name] ||= []) << e; s } out = nil if @type == "array" out = [] groups.each do |k, v| if v.size == 1 out << v.first.to_hash.entries.first.last else out << v.map{|e| e.to_hash[k]} end end out = out.flatten else # If Hash out = {} groups.each do |k,v| if v.size == 1 out.merge!(v.first) else out.merge!( k => v.map{|e| e.to_hash[k]}) end end out.merge! attributes unless attributes.empty? out = out.empty? ? nil : out end if @type && out.nil? { name => typecast_value(out) } else { name => out } end end end # Typecasts a value based upon its type. For instance, if # +node+ has #type == "integer", # {{[node.typecast_value("12") #=> 12]}} # # @param value The value that is being typecast. # # @details [:type options] # "integer":: # converts +value+ to an integer with #to_i # "boolean":: # checks whether +value+, after removing spaces, is the literal # "true" # "datetime":: # Parses +value+ using Time.parse, and returns a UTC Time # "date":: # Parses +value+ using Date.parse # # @return [Integer, Boolean, Time, Date, Object] # The result of typecasting +value+. # # @note # If +self+ does not have a "type" key, or if it's not one of the # options specified above, the raw +value+ will be returned. def typecast_value(value) return value unless @type proc = self.class.typecasts[@type] proc.nil? ? value : proc.call(value) end # Convert basic XML entities into their literal values. # # @param value<#gsub> An XML fragment. # # @return [#gsub] The XML fragment after converting entities. def translate_xml_entities(value) value.gsub(/</, "<"). gsub(/>/, ">"). gsub(/"/, '"'). gsub(/'/, "'"). gsub(/&/, "&") end # Take keys of the form foo-bar and convert them to foo_bar def undasherize_keys(params) params.keys.each do |key, value| params[key.tr("-", "_")] = params.delete(key) end params end # Get the inner_html of the REXML node. def inner_html @children.join end # Converts the node into a readable HTML node. # # @return [String] The HTML node in text form. def to_html attributes.merge!(:type => @type ) if @type "<#{name}#{attributes.to_xml_attributes}>#{@nil_element ? '' : inner_html}" end # @alias #to_html #to_s def to_s to_html end end class ToHashParser def self.from_xml(xml) stack = [] parser = REXML::Parsers::BaseParser.new(xml) while true event = parser.pull case event[0] when :end_document break when :end_doctype, :start_doctype # do nothing when :start_element stack.push REXMLUtilityNode.new(event[1], event[2]) when :end_element if stack.size > 1 temp = stack.pop stack.last.add_node(temp) end when :text, :cdata stack.last.add_node(event[1]) unless event[1].strip.length == 0 end end stack.pop.to_hash end end extlib-0.9.16/lib/extlib/hook.rb000066400000000000000000000360601207310540200164170ustar00rootroot00000000000000require 'extlib/assertions' require 'extlib/class' require 'extlib/object' require 'extlib/local_object_space' module Extlib # # TODO: Write more documentation! # # Overview # ======== # # The Hook module is a very simple set of AOP helpers. Basically, it # allows the developer to specify a method or block that should run # before or after another method. # # Usage # ===== # # Halting The Hook Stack # # Inheritance # # Other Goodies # # Please bring up any issues regarding Hooks with carllerche on IRC # module Hook def self.included(base) base.extend(ClassMethods) base.const_set("CLASS_HOOKS", {}) unless base.const_defined?("CLASS_HOOKS") base.const_set("INSTANCE_HOOKS", {}) unless base.const_defined?("INSTANCE_HOOKS") base.class_eval do class << self def method_added(name) process_method_added(name, :instance) super end def singleton_method_added(name) process_method_added(name, :class) super end end end end module ClassMethods extend Extlib::LocalObjectSpace include Extlib::Assertions # Inject code that executes before the target class method. # # @param target_method the name of the class method to inject before # @param method_sym the name of the method to run before the # target_method # @param block the code to run before the target_method # # @note # Either method_sym or block is required. # - # @api public def before_class_method(target_method, method_sym = nil, &block) install_hook :before, target_method, method_sym, :class, &block end # # Inject code that executes after the target class method. # # @param target_method the name of the class method to inject after # @param method_sym the name of the method to run after the target_method # @param block the code to run after the target_method # # @note # Either method_sym or block is required. # - # @api public def after_class_method(target_method, method_sym = nil, &block) install_hook :after, target_method, method_sym, :class, &block end # # Inject code that executes before the target instance method. # # @param target_method the name of the instance method to inject before # @param method_sym the name of the method to run before the # target_method # @param block the code to run before the target_method # # @note # Either method_sym or block is required. # - # @api public def before(target_method, method_sym = nil, &block) install_hook :before, target_method, method_sym, :instance, &block end # # Inject code that executes after the target instance method. # # @param target_method the name of the instance method to inject after # @param method_sym the name of the method to run after the # target_method # @param block the code to run after the target_method # # @note # Either method_sym or block is required. # - # @api public def after(target_method, method_sym = nil, &block) install_hook :after, target_method, method_sym, :instance, &block end # Register a class method as hookable. Registering a method means that # before hooks will be run immediately before the method is invoked and # after hooks will be called immediately after the method is invoked. # # @param hookable_method The name of the class method that should # be hookable # - # @api public def register_class_hooks(*hooks) hooks.each { |hook| register_hook(hook, :class) } end # Register aninstance method as hookable. Registering a method means that # before hooks will be run immediately before the method is invoked and # after hooks will be called immediately after the method is invoked. # # @param hookable_method The name of the instance method that should # be hookable # - # @api public def register_instance_hooks(*hooks) hooks.each { |hook| register_hook(hook, :instance) } end # Not yet implemented def reset_hook!(target_method, scope) raise NotImplementedError end # --- Alright kids... the rest is internal stuff --- # Returns the correct HOOKS Hash depending on whether we are # working with class methods or instance methods def hooks_with_scope(scope) case scope when :class then class_hooks when :instance then instance_hooks else raise ArgumentError, 'You need to pass :class or :instance as scope' end end def class_hooks self.const_get("CLASS_HOOKS") end def instance_hooks self.const_get("INSTANCE_HOOKS") end # Registers a method as hookable. Registering hooks involves the following # process # # * Create a blank entry in the HOOK Hash for the method. # * Define the methods that execute the before and after hook stack. # These methods will be no-ops at first, but everytime a new hook is # defined, the methods will be redefined to incorporate the new hook. # * Redefine the method that is to be hookable so that the hook stacks # are invoked approprietly. def register_hook(target_method, scope) if scope == :instance && !method_defined?(target_method) raise ArgumentError, "#{target_method} instance method does not exist" elsif scope == :class && !respond_to?(target_method) raise ArgumentError, "#{target_method} class method does not exist" end hooks = hooks_with_scope(scope) if hooks[target_method].nil? hooks[target_method] = { # We need to keep track of which class in the Inheritance chain the # method was declared hookable in. Every time a child declares a new # hook for the method, the hook stack invocations need to be redefined # in the original Class. See #define_hook_stack_execution_methods :before => [], :after => [], :in => self } define_hook_stack_execution_methods(target_method, scope) define_advised_method(target_method, scope) end end # Is the method registered as a hookable in the given scope. def registered_as_hook?(target_method, scope) ! hooks_with_scope(scope)[target_method].nil? end # Generates names for the various utility methods. We need to do this because # the various utility methods should not end in = so, while we're at it, we # might as well get rid of all punctuation. def hook_method_name(target_method, prefix, suffix) target_method = target_method.to_s case target_method[-1,1] when '?' then "#{prefix}_#{target_method[0..-2]}_ques_#{suffix}" when '!' then "#{prefix}_#{target_method[0..-2]}_bang_#{suffix}" when '=' then "#{prefix}_#{target_method[0..-2]}_eq_#{suffix}" # I add a _nan_ suffix here so that we don't ever encounter # any naming conflicts. else "#{prefix}_#{target_method[0..-1]}_nan_#{suffix}" end end # This will need to be refactored def process_method_added(method_name, scope) hooks_with_scope(scope).each do |target_method, hooks| if hooks[:before].any? { |hook| hook[:name] == method_name } define_hook_stack_execution_methods(target_method, scope) end if hooks[:after].any? { |hook| hook[:name] == method_name } define_hook_stack_execution_methods(target_method, scope) end end end # Defines two methods. One method executes the before hook stack. The other executes # the after hook stack. This method will be called many times during the Class definition # process. It should be called for each hook that is defined. It will also be called # when a hook is redefined (to make sure that the arity hasn't changed). def define_hook_stack_execution_methods(target_method, scope) unless registered_as_hook?(target_method, scope) raise ArgumentError, "#{target_method} has not be registered as a hookable #{scope} method" end hooks = hooks_with_scope(scope) before_hooks = hooks[target_method][:before] before_hooks = before_hooks.map{ |info| inline_call(info, scope) }.join("\n") after_hooks = hooks[target_method][:after] after_hooks = after_hooks.map{ |info| inline_call(info, scope) }.join("\n") before_hook_name = hook_method_name(target_method, 'execute_before', 'hook_stack') after_hook_name = hook_method_name(target_method, 'execute_after', 'hook_stack') hooks[target_method][:in].class_eval <<-RUBY, __FILE__, __LINE__ + 1 #{scope == :class ? 'class << self' : ''} private remove_method :#{before_hook_name} if instance_methods(false).any? { |m| m.to_sym == :#{before_hook_name} } def #{before_hook_name}(*args) #{before_hooks} end remove_method :#{after_hook_name} if instance_methods(false).any? { |m| m.to_sym == :#{after_hook_name} } def #{after_hook_name}(*args) #{after_hooks} end #{scope == :class ? 'end' : ''} RUBY end # Returns ruby code that will invoke the hook. It checks the arity of the hook method # and passes arguments accordingly. def inline_call(method_info, scope) Extlib::Hook::ClassMethods.hook_scopes << method_info[:from] name = method_info[:name] if scope == :instance args = method_defined?(name) && instance_method(name).arity != 0 ? '*args' : '' %(#{name}(#{args}) if self.class <= Extlib::Hook::ClassMethods.object_by_id(#{method_info[:from].object_id})) else args = respond_to?(name) && method(name).arity != 0 ? '*args' : '' %(#{name}(#{args}) if self <= Extlib::Hook::ClassMethods.object_by_id(#{method_info[:from].object_id})) end end def define_advised_method(target_method, scope) args = args_for(method_with_scope(target_method, scope)) renamed_target = hook_method_name(target_method, 'hookable_', 'before_advised') source = <<-EOD def #{target_method}(#{args}) retval = nil catch(:halt) do #{hook_method_name(target_method, 'execute_before', 'hook_stack')}(#{args}) retval = #{renamed_target}(#{args}) #{hook_method_name(target_method, 'execute_after', 'hook_stack')}(retval, #{args}) retval end end EOD if scope == :instance && !instance_methods(false).any? { |m| m.to_sym == target_method } send(:alias_method, renamed_target, target_method) proxy_module = Module.new proxy_module.class_eval(source, __FILE__, __LINE__) self.send(:include, proxy_module) else source = %{alias_method :#{renamed_target}, :#{target_method}\n#{source}} source = %{class << self\n#{source}\nend} if scope == :class class_eval(source, __FILE__, __LINE__) end end # --- Add a hook --- def install_hook(type, target_method, method_sym, scope, &block) assert_kind_of 'target_method', target_method, Symbol assert_kind_of 'method_sym', method_sym, Symbol unless method_sym.nil? assert_kind_of 'scope', scope, Symbol if !block_given? and method_sym.nil? raise ArgumentError, "You need to pass 2 arguments to \"#{type}\"." end if method_sym.to_s[-1,1] == '=' raise ArgumentError, "Methods ending in = cannot be hooks" end unless [ :class, :instance ].include?(scope) raise ArgumentError, 'You need to pass :class or :instance as scope' end if registered_as_hook?(target_method, scope) hooks = hooks_with_scope(scope) #if this hook is previously declared in a sibling or cousin we must move the :in class #to the common ancestor to get both hooks to run. if !(hooks[target_method][:in] <=> self) before_hook_name = hook_method_name(target_method, 'execute_before', 'hook_stack') after_hook_name = hook_method_name(target_method, 'execute_after', 'hook_stack') hooks[target_method][:in].class_eval <<-RUBY, __FILE__, __LINE__ + 1 remove_method :#{before_hook_name} if instance_methods(false).any? { |m| m.to_sym == :#{before_hook_name} } def #{before_hook_name}(*args) super end remove_method :#{after_hook_name} if instance_methods(false).any? { |m| m.to_sym == :#{before_hook_name} } def #{after_hook_name}(*args) super end RUBY while !(hooks[target_method][:in] <=> self) do hooks[target_method][:in] = hooks[target_method][:in].superclass end define_hook_stack_execution_methods(target_method, scope) hooks[target_method][:in].class_eval{define_advised_method(target_method, scope)} end else register_hook(target_method, scope) hooks = hooks_with_scope(scope) end #if we were passed a block, create a method out of it. if block method_sym = "__hooks_#{type}_#{quote_method(target_method)}_#{hooks[target_method][type].length}".to_sym if scope == :class meta_class.instance_eval do define_method(method_sym, &block) end else define_method(method_sym, &block) end end # Adds method to the stack an redefines the hook invocation method hooks[target_method][type] << { :name => method_sym, :from => self } define_hook_stack_execution_methods(target_method, scope) end # --- Helpers --- def args_for(method) if method.arity == 0 "&block" elsif method.arity > 0 "_" << (1 .. method.arity).to_a.join(", _") << ", &block" elsif (method.arity + 1) < 0 "_" << (1 .. (method.arity).abs - 1).to_a.join(", _") << ", *args, &block" else "*args, &block" end end def method_with_scope(name, scope) case scope when :class then method(name) when :instance then instance_method(name) else raise ArgumentError, 'You need to pass :class or :instance as scope' end end def quote_method(name) name.to_s.gsub(/\?$/, '_q_').gsub(/!$/, '_b_').gsub(/=$/, '_eq_') end end end end extlib-0.9.16/lib/extlib/inflection.rb000066400000000000000000000320771207310540200176150ustar00rootroot00000000000000require 'extlib/string' module Extlib # = English Nouns Number Inflection. # # This module provides english singular <-> plural noun inflections. module Inflection class << self # Take an underscored name and make it into a camelized name # # @example # "egg_and_hams".classify #=> "EggAndHam" # "enlarged_testes".classify #=> "EnlargedTestis" # "post".classify #=> "Post" # def classify(name) words = name.to_s.sub(/.*\./, '').split('_') words[-1] = singularize(words[-1]) words.collect { |word| word.capitalize }.join end # By default, camelize converts strings to UpperCamelCase. # # camelize will also convert '/' to '::' which is useful for converting paths to namespaces # # @example # "active_record".camelize #=> "ActiveRecord" # "active_record/errors".camelize #=> "ActiveRecord::Errors" # def camelize(lower_case_and_underscored_word, *args) lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase } end # The reverse of +camelize+. Makes an underscored form from the expression in the string. # # Changes '::' to '/' to convert namespaces to paths. # # @example # "ActiveRecord".underscore #=> "active_record" # "ActiveRecord::Errors".underscore #=> active_record/errors # def underscore(camel_cased_word) camel_cased_word.to_const_path end # Capitalizes the first word and turns underscores into spaces and strips _id. # Like titleize, this is meant for creating pretty output. # # @example # "employee_salary" #=> "Employee salary" # "author_id" #=> "Author" def humanize(lower_case_and_underscored_word) lower_case_and_underscored_word.to_s.gsub(/_id$/, '').tr('_', ' ').capitalize end # Removes the module part from the expression in the string # # @example # "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections" # "Inflections".demodulize #=> "Inflections" def demodulize(class_name_in_module) class_name_in_module.to_s.gsub(/^.*::/, '') end # Create the name of a table like Rails does for models to table names. This method # uses the pluralize method on the last word in the string. # # @example # "RawScaledScorer".tableize #=> "raw_scaled_scorers" # "EnlargedTestis".tableize #=> "enlarged_testes" # "egg_and_ham".tableize #=> "egg_and_hams" # "fancyCategory".tableize #=> "fancy_categories" def tableize(class_name) words = class_name.to_const_path.tr('/', '_').split('_') words[-1] = pluralize(words[-1]) words.join('_') end # Creates a foreign key name from a class name. # # @example # "Message".foreign_key #=> "message_id" # "Admin::Post".foreign_key #=> "post_id" def foreign_key(class_name, key = "id") underscore(demodulize(class_name.to_s)) << "_" << key.to_s end # Constantize tries to find a declared constant with the name specified # in the string. It raises a NameError when the name is not in CamelCase # or is not initialized. # # @example # "Module".constantize #=> Module # "Class".constantize #=> Class def constantize(camel_cased_word) unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!" end Object.module_eval("::#{$1}", __FILE__, __LINE__) end end @singular_of = {} @plural_of = {} @singular_rules = [] @plural_rules = [] class << self # Defines a general inflection exception case. # # ==== Parameters # singular:: # singular form of the word # plural:: # plural form of the word # # ==== Examples # # Here we define erratum/errata exception case: # # English::Inflect.word "erratum", "errata" # # In case singular and plural forms are the same omit # second argument on call: # # English::Inflect.word 'information' def word(singular, plural=nil) plural = singular unless plural singular_word(singular, plural) plural_word(singular, plural) end def clear(type = :all) if type == :singular || type == :all @singular_of = {} @singular_rules = [] @singularization_rules, @singularization_regex = nil, nil end if type == :plural || type == :all @singular_of = {} @singular_rules = [] @singularization_rules, @singularization_regex = nil, nil end end # Define a singularization exception. # # ==== Parameters # singular:: # singular form of the word # plural:: # plural form of the word def singular_word(singular, plural) @singular_of[plural] = singular @singular_of[plural.capitalize] = singular.capitalize end # Define a pluralization exception. # # ==== Parameters # singular:: # singular form of the word # plural:: # plural form of the word def plural_word(singular, plural) @plural_of[singular] = plural @plural_of[singular.capitalize] = plural.capitalize end # Define a general rule. # # ==== Parameters # singular:: # ending of the word in singular form # plural:: # ending of the word in plural form # whole_word:: # for capitalization, since words can be # capitalized (Man => Men) # # ==== Examples # Once the following rule is defined: # English::Inflect.rule 'y', 'ies' # # You can see the following results: # irb> "fly".plural # => flies # irb> "cry".plural # => cries # Define a general rule. def rule(singular, plural, whole_word = false) singular_rule(singular, plural) plural_rule(singular, plural) word(singular, plural) if whole_word end # Define a singularization rule. # # ==== Parameters # singular:: # ending of the word in singular form # plural:: # ending of the word in plural form # # ==== Examples # Once the following rule is defined: # English::Inflect.singular_rule 'o', 'oes' # # You can see the following results: # irb> "heroes".singular # => hero def singular_rule(singular, plural) @singular_rules << [singular, plural] end # Define a plurualization rule. # # ==== Parameters # singular:: # ending of the word in singular form # plural:: # ending of the word in plural form # # ==== Examples # Once the following rule is defined: # English::Inflect.singular_rule 'fe', 'ves' # # You can see the following results: # irb> "wife".plural # => wives def plural_rule(singular, plural) @plural_rules << [singular, plural] end # Read prepared singularization rules. def singularization_rules if defined?(@singularization_regex) && @singularization_regex return [@singularization_regex, @singularization_hash] end # No sorting needed: Regexen match on longest string @singularization_regex = Regexp.new("(" + @singular_rules.map {|s,p| p}.join("|") + ")$", "i") @singularization_hash = Hash[*@singular_rules.flatten].invert [@singularization_regex, @singularization_hash] end # Read prepared pluralization rules. def pluralization_rules if defined?(@pluralization_regex) && @pluralization_regex return [@pluralization_regex, @pluralization_hash] end @pluralization_regex = Regexp.new("(" + @plural_rules.map {|s,p| s}.join("|") + ")$", "i") @pluralization_hash = Hash[*@plural_rules.flatten] [@pluralization_regex, @pluralization_hash] end attr_reader :singular_of, :plural_of # Convert an English word from plural to singular. # # "boys".singular #=> boy # "tomatoes".singular #=> tomato # # ==== Parameters # word:: word to singularize # # ==== Returns # :: singularized form of word # # ==== Notes # Aliased as singularize (a Railism) def singular(word) if result = singular_of[word] return result.dup end result = word.dup regex, hash = singularization_rules result.sub!(regex) {|m| hash[m]} singular_of[word] = result return result end # Alias for #singular (a Railism). # alias_method(:singularize, :singular) # Convert an English word from singular to plural. # # "boy".plural #=> boys # "tomato".plural #=> tomatoes # # ==== Parameters # word:: word to pluralize # # ==== Returns # :: pluralized form of word # # ==== Notes # Aliased as pluralize (a Railism) def plural(word) # special exceptions return "" if word == "" if result = plural_of[word] return result.dup end result = word.dup regex, hash = pluralization_rules result.sub!(regex) {|m| hash[m]} plural_of[word] = result return result end # Alias for #plural (a Railism). alias_method(:pluralize, :plural) end # One argument means singular and plural are the same. word 'equipment' word 'fish' word 'grass' word 'hovercraft' word 'information' word 'milk' word 'money' word 'moose' word 'plurals' word 'postgres' word 'rain' word 'rice' word 'series' word 'sheep' word 'species' word 'status' # Two arguments defines a singular and plural exception. word 'alias' , 'aliases' word 'analysis' , 'analyses' word 'axis' , 'axes' word 'basis' , 'bases' word 'buffalo' , 'buffaloes' word 'cactus' , 'cacti' word 'crisis' , 'crises' word 'criterion' , 'criteria' word 'cross' , 'crosses' word 'datum' , 'data' word 'diagnosis' , 'diagnoses' word 'drive' , 'drives' word 'erratum' , 'errata' word 'goose' , 'geese' word 'index' , 'indices' word 'life' , 'lives' word 'louse' , 'lice' word 'matrix' , 'matrices' word 'medium' , 'media' word 'mouse' , 'mice' word 'movie' , 'movies' word 'octopus' , 'octopi' word 'ox' , 'oxen' word 'phenomenon' , 'phenomena' word 'plus' , 'plusses' word 'potato' , 'potatoes' word 'quiz' , 'quizzes' word 'status' , 'status' word 'status' , 'statuses' word 'Swiss' , 'Swiss' word 'testis' , 'testes' word 'thesaurus' , 'thesauri' word 'thesis' , 'theses' word 'thief' , 'thieves' word 'tomato' , 'tomatoes' word 'torpedo' , 'torpedoes' word 'vertex' , 'vertices' word 'wife' , 'wives' # One-way singularization exception (convert plural to singular). # General rules. rule 'person' , 'people', true rule 'shoe' , 'shoes', true rule 'hive' , 'hives', true rule 'man' , 'men', true rule 'child' , 'children', true rule 'news' , 'news', true rule 'rf' , 'rves' rule 'af' , 'aves' rule 'ero' , 'eroes' rule 'man' , 'men' rule 'ch' , 'ches' rule 'sh' , 'shes' rule 'ss' , 'sses' rule 'ta' , 'tum' rule 'ia' , 'ium' rule 'ra' , 'rum' rule 'ay' , 'ays' rule 'ey' , 'eys' rule 'oy' , 'oys' rule 'uy' , 'uys' rule 'y' , 'ies' rule 'x' , 'xes' rule 'lf' , 'lves' rule 'ffe' , 'ffes' rule 'afe' , 'aves' rule 'ouse' , 'ouses' # more cases of words ending in -oses not being singularized properly # than cases of words ending in -osis # rule 'osis' , 'oses' rule 'ox' , 'oxes' rule 'us' , 'uses' rule '' , 's' # One-way singular rules. singular_rule 'of' , 'ofs' # proof singular_rule 'o' , 'oes' # hero, heroes singular_rule 'f' , 'ves' # One-way plural rules. #plural_rule 'fe' , 'ves' # safe, wife plural_rule 's' , 'ses' plural_rule 'ive' , 'ives' # don't want to snag wife plural_rule 'fe' , 'ves' # don't want to snag perspectives end end class String def singular Extlib::Inflection.singular(self) end alias_method(:singularize, :singular) def plural Extlib::Inflection.plural(self) end alias_method(:pluralize, :plural) end extlib-0.9.16/lib/extlib/lazy_array.rb000066400000000000000000000217541207310540200176400ustar00rootroot00000000000000require 'extlib/try_dup' class LazyArray # borrowed partially from StrokeDB include Enumerable attr_reader :head, :tail def first(*args) if lazy_possible?(@head, *args) @head.first(*args) else lazy_load @array.first(*args) end end def last(*args) if lazy_possible?(@tail, *args) @tail.last(*args) else lazy_load @array.last(*args) end end def at(index) if index >= 0 && lazy_possible?(@head, index + 1) @head.at(index) elsif index < 0 && lazy_possible?(@tail, index.abs) @tail.at(index) else lazy_load @array.at(index) end end def fetch(*args, &block) index = args.first if index >= 0 && lazy_possible?(@head, index + 1) @head.fetch(*args, &block) elsif index < 0 && lazy_possible?(@tail, index.abs) @tail.fetch(*args, &block) else lazy_load @array.fetch(*args, &block) end end def values_at(*args) accumulator = [] lazy_possible = args.all? do |arg| index, length = extract_slice_arguments(arg) if index >= 0 && lazy_possible?(@head, index + length) accumulator.concat(head.values_at(*arg)) elsif index < 0 && lazy_possible?(@tail, index.abs) accumulator.concat(tail.values_at(*arg)) end end if lazy_possible accumulator else lazy_load @array.values_at(*args) end end def index(entry) (lazy_possible?(@head) && @head.index(entry)) || begin lazy_load @array.index(entry) end end def include?(entry) (lazy_possible?(@tail) && @tail.include?(entry)) || (lazy_possible?(@head) && @head.include?(entry)) || begin lazy_load @array.include?(entry) end end def empty? (@tail.nil? || @tail.empty?) && (@head.nil? || @head.empty?) && begin lazy_load @array.empty? end end def any?(&block) (lazy_possible?(@tail) && @tail.any?(&block)) || (lazy_possible?(@head) && @head.any?(&block)) || begin lazy_load @array.any?(&block) end end def [](*args) index, length = extract_slice_arguments(*args) if length == 1 && args.size == 1 && args.first.kind_of?(Integer) return at(index) end if index >= 0 && lazy_possible?(@head, index + length) @head[*args] elsif index < 0 && lazy_possible?(@tail, index.abs - 1 + length) @tail[*args] else lazy_load @array[*args] end end alias slice [] def slice!(*args) index, length = extract_slice_arguments(*args) if index >= 0 && lazy_possible?(@head, index + length) @head.slice!(*args) elsif index < 0 && lazy_possible?(@tail, index.abs - 1 + length) @tail.slice!(*args) else lazy_load @array.slice!(*args) end end def []=(*args) index, length = extract_slice_arguments(*args[0..-2]) if index >= 0 && lazy_possible?(@head, index + length) @head.[]=(*args) elsif index < 0 && lazy_possible?(@tail, index.abs - 1 + length) @tail.[]=(*args) else lazy_load @array.[]=(*args) end end alias splice []= def reverse dup.reverse! end def reverse! # reverse without kicking if possible if loaded? @array = @array.reverse else @head, @tail = @tail.reverse, @head.reverse proc = @load_with_proc @load_with_proc = lambda do |v| proc.call(v) v.instance_variable_get(:@array).reverse! end end self end def <<(entry) if loaded? lazy_load @array << entry else @tail << entry end self end def concat(other) if loaded? lazy_load @array.concat(other) else @tail.concat(other) end self end def push(*entries) if loaded? lazy_load @array.push(*entries) else @tail.push(*entries) end self end def unshift(*entries) if loaded? lazy_load @array.unshift(*entries) else @head.unshift(*entries) end self end def insert(index, *entries) if index >= 0 && lazy_possible?(@head, index) @head.insert(index, *entries) elsif index < 0 && lazy_possible?(@tail, index.abs - 1) @tail.insert(index, *entries) else lazy_load @array.insert(index, *entries) end self end def pop(*args) if lazy_possible?(@tail, *args) @tail.pop(*args) else lazy_load @array.pop(*args) end end def shift(*args) if lazy_possible?(@head, *args) @head.shift(*args) else lazy_load @array.shift(*args) end end def delete_at(index) if index >= 0 && lazy_possible?(@head, index + 1) @head.delete_at(index) elsif index < 0 && lazy_possible?(@tail, index.abs) @tail.delete_at(index) else lazy_load @array.delete_at(index) end end def delete_if(&block) if loaded? lazy_load @array.delete_if(&block) else @reapers << block @head.delete_if(&block) @tail.delete_if(&block) end self end def replace(other) mark_loaded @array.replace(other) self end def clear mark_loaded @array.clear self end def to_a lazy_load @array.to_a end alias to_ary to_a def load_with(&block) @load_with_proc = block self end def loaded? @loaded == true end def kind_of?(klass) super || @array.kind_of?(klass) end alias is_a? kind_of? def respond_to?(method, include_private = false) super || @array.respond_to?(method) end def freeze if loaded? @array.freeze else @head.freeze @tail.freeze end @frozen = true self end def frozen? @frozen == true end def ==(other) if equal?(other) return true end unless other.respond_to?(:to_ary) return false end # if necessary, convert to something that can be compared other = other.to_ary unless other.respond_to?(:[]) cmp?(other, :==) end def eql?(other) if equal?(other) return true end unless other.class.equal?(self.class) return false end cmp?(other, :eql?) end def lazy_possible?(list, need_length = 1) !loaded? && need_length <= list.size end private def initialize @frozen = false @loaded = false @load_with_proc = lambda { |v| v } @head = [] @tail = [] @array = [] @reapers = [] end def initialize_copy(original) @head = @head.try_dup @tail = @tail.try_dup @array = @array.try_dup end def lazy_load return if loaded? mark_loaded @load_with_proc[self] @array.unshift(*@head) @array.concat(@tail) @head = @tail = nil @reapers.each { |r| @array.delete_if(&r) } if @reapers @array.freeze if frozen? end def mark_loaded @loaded = true end ## # Extract arguments for #slice an #slice! and return index and length # # @param [Integer, Array(Integer), Range] *args the index, # index and length, or range indicating first and last position # # @return [Integer] the index # @return [Integer,NilClass] the length, if any # # @api private def extract_slice_arguments(*args) first_arg, second_arg = args if args.size == 2 && first_arg.kind_of?(Integer) && second_arg.kind_of?(Integer) return first_arg, second_arg elsif args.size == 1 if first_arg.kind_of?(Integer) return first_arg, 1 elsif first_arg.kind_of?(Range) index = first_arg.first length = first_arg.last - index length += 1 unless first_arg.exclude_end? return index, length end end raise ArgumentError, "arguments may be 1 or 2 Integers, or 1 Range object, was: #{args.inspect}", caller(1) end def each lazy_load if block_given? @array.each { |entry| yield entry } self else @array.each end end # delegate any not-explicitly-handled methods to @array, if possible. # this is handy for handling methods mixed-into Array like group_by def method_missing(method, *args, &block) if @array.respond_to?(method) lazy_load results = @array.send(method, *args, &block) results.equal?(@array) ? self : results else super end end def cmp?(other, operator) unless loaded? # compare the head against the beginning of other. start at index # 0 and incrementally compare each entry. if other is a LazyArray # this has a lesser likelyhood of triggering a lazy load 0.upto(@head.size - 1) do |i| return false unless @head[i].__send__(operator, other[i]) end # compare the tail against the end of other. start at index # -1 and decrementally compare each entry. if other is a LazyArray # this has a lesser likelyhood of triggering a lazy load -1.downto(@tail.size * -1) do |i| return false unless @tail[i].__send__(operator, other[i]) end lazy_load end @array.send(operator, other.to_ary) end end extlib-0.9.16/lib/extlib/lazy_module.rb000066400000000000000000000005331207310540200177770ustar00rootroot00000000000000class LazyModule < Module def self.new(&blk) # passing no-op block overrides &blk m = super{ } class << m include ClassMethods end m.lazy_evaluated_body = blk m end module ClassMethods attr_accessor :lazy_evaluated_body def included(host) host.class_eval(&@lazy_evaluated_body) end end end extlib-0.9.16/lib/extlib/local_object_space.rb000066400000000000000000000004501207310540200212440ustar00rootroot00000000000000module Extlib module LocalObjectSpace def self.extended(klass) (class << klass; self; end).send :attr_accessor, :hook_scopes klass.hook_scopes = [] end def object_by_id(object_id) self.hook_scopes.detect {|object| object.object_id == object_id} end end end extlib-0.9.16/lib/extlib/logger.rb000077500000000000000000000131451207310540200167400ustar00rootroot00000000000000require "time" # httpdate # ==== Public Extlib Logger API # # To replace an existing logger with a new one: # Extlib::Logger.set_log(log{String, IO},level{Symbol, String}) # # Available logging levels are # Extlib::Logger::{ Fatal, Error, Warn, Info, Debug } # # Logging via: # Extlib.logger.fatal(message,&block) # Extlib.logger.error(message,&block) # Extlib.logger.warn(message,&block) # Extlib.logger.info(message,&block) # Extlib.logger.debug(message,&block) # # Logging with autoflush: # Extlib.logger.fatal!(message,&block) # Extlib.logger.error!(message,&block) # Extlib.logger.warn!(message,&block) # Extlib.logger.info!(message,&block) # Extlib.logger.debug!(message,&block) # # Flush the buffer to # Extlib.logger.flush # # Remove the current log object # Extlib.logger.close # # ==== Private Extlib Logger API # # To initialize the logger you create a new object, proxies to set_log. # Extlib::Logger.new(log{String, IO},level{Symbol, String}) module Extlib class << self attr_accessor :logger end class Logger attr_accessor :level attr_accessor :delimiter attr_accessor :auto_flush attr_reader :buffer attr_reader :log attr_reader :init_args # ==== Notes # Ruby (standard) logger levels: # :fatal:: An unhandleable error that results in a program crash # :error:: A handleable error condition # :warn:: A warning # :info:: generic (useful) information about system operation # :debug:: low-level information for developers Levels = { :fatal => 7, :error => 6, :warn => 4, :info => 3, :debug => 0 } private # Readies a log for writing. # # ==== Parameters # log:: Either an IO object or a name of a logfile. def initialize_log(log) close if @log # be sure that we don't leave open files laying around. if log.respond_to?(:write) @log = log elsif File.exist?(log) @log = open(log, (File::WRONLY | File::APPEND)) @log.sync = true else FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log)) @log = open(log, (File::WRONLY | File::APPEND | File::CREAT)) @log.sync = true @log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n") end end public # To initialize the logger you create a new object, proxies to set_log. # # ==== Parameters # *args:: Arguments to create the log from. See set_logs for specifics. def initialize(*args) @init_args = args set_log(*args) end # Replaces an existing logger with a new one. # # ==== Parameters # log:: Either an IO object or a name of a logfile. # log_level<~to_sym>:: # The log level from, e.g. :fatal or :info. Defaults to :error in the # production environment and :debug otherwise. # delimiter:: # Delimiter to use between message sections. Defaults to " ~ ". # auto_flush:: # Whether the log should automatically flush after new messages are # added. Defaults to false. def set_log(log, log_level = nil, delimiter = " ~ ", auto_flush = false) if log_level && Levels[log_level.to_sym] @level = Levels[log_level.to_sym] else @level = Levels[:debug] end @buffer = [] @delimiter = delimiter @auto_flush = auto_flush initialize_log(log) end # Flush the entire buffer to the log object. def flush return unless @buffer.size > 0 @log.write(@buffer.slice!(0..-1).join) end # Close and remove the current log object. def close flush @log.close if @log.respond_to?(:close) && !@log.tty? @log = nil end # Appends a message to the log. The methods yield to an optional block and # the output of this block will be appended to the message. # # ==== Parameters # string:: The message to be logged. Defaults to nil. # # ==== Returns # String:: The resulting message added to the log file. def <<(string = nil) message = "" message << delimiter message << string if string message << "\n" unless message[-1] == ?\n @buffer << message flush if @auto_flush message end alias :push :<< # Generate the logging methods for Extlib.logger for each log level. Levels.each_pair do |name, number| class_eval <<-LEVELMETHODS, __FILE__, __LINE__ # Appends a message to the log if the log level is at least as high as # the log level of the logger. # # ==== Parameters # string:: The message to be logged. Defaults to nil. # # ==== Returns # self:: The logger object for chaining. def #{name}(message = nil) self << message if #{number} >= level self end # Appends a message to the log if the log level is at least as high as # the log level of the logger. The bang! version of the method also auto # flushes the log buffer to disk. # # ==== Parameters # string:: The message to be logged. Defaults to nil. # # ==== Returns # self:: The logger object for chaining. def #{name}!(message = nil) self << message if #{number} >= level flush if #{number} >= level self end # ==== Returns # Boolean:: True if this level will be logged by this logger. def #{name}? #{number} >= level end LEVELMETHODS end end end extlib-0.9.16/lib/extlib/mash.rb000066400000000000000000000101331207310540200164000ustar00rootroot00000000000000require 'extlib/hash' # This class has dubious semantics and we only have it so that people can write # params[:key] instead of params['key']. class Mash < Hash # @param constructor # The default value for the mash. Defaults to an empty hash. # # @details [Alternatives] # If constructor is a Hash, a new mash will be created based on the keys of # the hash and no default value will be set. def initialize(constructor = {}) if constructor.is_a?(Hash) super() update(constructor) else super(constructor) end end # @param key The default value for the mash. Defaults to nil. # # @details [Alternatives] # If key is a Symbol and it is a key in the mash, then the default value will # be set to the value matching the key. def default(key = nil) if key.is_a?(Symbol) && include?(key = key.to_s) self[key] else super end end alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) alias_method :regular_update, :update unless method_defined?(:regular_update) # @param key The key to set. # @param value # The value to set the key to. # # @see Mash#convert_key # @see Mash#convert_value def []=(key, value) regular_writer(convert_key(key), convert_value(value)) end # @param other_hash # A hash to update values in the mash with. The keys and the values will be # converted to Mash format. # # @return [Mash] The updated mash. def update(other_hash) other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) } self end alias_method :merge!, :update # @param key The key to check for. This will be run through convert_key. # # @return [Boolean] True if the key exists in the mash. def key?(key) super(convert_key(key)) end # def include? def has_key? def member? alias_method :include?, :key? alias_method :has_key?, :key? alias_method :member?, :key? # @param key The key to fetch. This will be run through convert_key. # @param *extras Default value. # # @return [Object] The value at key or the default value. def fetch(key, *extras) super(convert_key(key), *extras) end # @param *indices # The keys to retrieve values for. These will be run through +convert_key+. # # @return [Array] The values at each of the provided keys def values_at(*indices) indices.collect {|key| self[convert_key(key)]} end # @param hash The hash to merge with the mash. # # @return [Mash] A new mash with the hash values merged in. def merge(hash) self.dup.update(hash) end # @param key # The key to delete from the mash.\ def delete(key) super(convert_key(key)) end # @param *rejected 1, :two => 2, :three => 3 }.except(:one) # #=> { "two" => 2, "three" => 3 } def except(*keys) super(*keys.map {|k| convert_key(k)}) end # Used to provide the same interface as Hash. # # @return [Mash] This mash unchanged. def stringify_keys!; self end # @return [Hash] The mash as a Hash with symbolized keys. def symbolize_keys h = Hash.new(default) each { |key, val| h[key.to_sym] = val } h end # @return [Hash] The mash as a Hash with string keys. def to_hash Hash.new(default).merge(self) end protected # @param key The key to convert. # # @param [Object] # The converted key. If the key was a symbol, it will be converted to a # string. # # @api private def convert_key(key) key.kind_of?(Symbol) ? key.to_s : key end # @param value The value to convert. # # @return [Object] # The converted value. A Hash or an Array of hashes, will be converted to # their Mash equivalents. # # @api private def convert_value(value) if value.class == Hash value.to_mash elsif value.is_a?(Array) value.collect { |e| convert_value(e) } else value end end end extlib-0.9.16/lib/extlib/module.rb000066400000000000000000000024531207310540200167430ustar00rootroot00000000000000require 'extlib/object' require 'extlib/class' require 'extlib/blank' class Module def find_const(const_name) if const_name[0..1] == '::' Object.full_const_get(const_name[2..-1]) else nested_const_lookup(const_name) end end def try_dup self end private # Doesn't do any caching since constants can change with remove_const def nested_const_lookup(const_name) unless equal?(Object) constants = [] name.split('::').each do |part| const = constants.last || Object constants << const.const_get(part) end parts = const_name.split('::') # from most to least specific constant, use each as a base and try # to find a constant with the name const_name within them constants.reverse_each do |const| # return the nested constant if available return const if parts.all? do |part| const = if RUBY_VERSION >= '1.9.0' const.const_defined?(part, false) ? const.const_get(part, false) : nil else const.const_defined?(part) ? const.const_get(part) : nil end end end end # no relative constant found, fallback to an absolute lookup and # use const_missing if not found Object.full_const_get(const_name) end end # class Module extlib-0.9.16/lib/extlib/nil.rb000066400000000000000000000000601207310540200162300ustar00rootroot00000000000000class NilClass def try_dup self end end extlib-0.9.16/lib/extlib/numeric.rb000066400000000000000000000000571207310540200171160ustar00rootroot00000000000000class Numeric def try_dup self end end extlib-0.9.16/lib/extlib/object.rb000066400000000000000000000121341207310540200167210ustar00rootroot00000000000000require 'set' require 'extlib/blank' class Object # Extracts the singleton class, so that metaprogramming can be done on it. # # @return [Class] The meta class. # # @example [Setup] # class MyString < String; end # # MyString.instance_eval do # define_method :foo do # puts self # end # end # # MyString.meta_class.instance_eval do # define_method :bar do # puts self # end # end # # def String.add_meta_var(var) # self.meta_class.instance_eval do # define_method var do # puts "HELLO" # end # end # end # # @example # MyString.new("Hello").foo #=> "Hello" # @example # MyString.new("Hello").bar # #=> NoMethodError: undefined method `bar' for "Hello":MyString # @example # MyString.foo # #=> NoMethodError: undefined method `foo' for MyString:Class # @example # MyString.bar # #=> MyString # @example # String.bar # #=> NoMethodError: undefined method `bar' for String:Class # @example # MyString.add_meta_var(:x) # MyString.x #=> HELLO # # @details [Description of Examples] # As you can see, using #meta_class allows you to execute code (and here, # define a method) on the metaclass itself. It also allows you to define # class methods that can be run on subclasses, and then be able to execute # code on the metaclass of the subclass (here MyString). # # In this case, we were able to define a class method (add_meta_var) on # String that was executable by the MyString subclass. It was then able to # define a method on the subclass by adding it to the MyString metaclass. # # For more information, you can check out _why's excellent article at: # http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html def meta_class() class << self; self end end # @param name The name of the constant to get, e.g. "Merb::Router". # # @return [Object] The constant corresponding to the name. def full_const_get(name) list = name.split("::") list.shift if list.first.blank? obj = self list.each do |x| # This is required because const_get tries to look for constants in the # ancestor chain, but we only want constants that are HERE obj = obj.const_defined?(x) ? obj.const_get(x) : obj.const_missing(x) end obj end # @param name The name of the constant to get, e.g. "Merb::Router". # @param value The value to assign to the constant. # # @return [Object] The constant corresponding to the name. def full_const_set(name, value) list = name.split("::") toplevel = list.first.blank? list.shift if toplevel last = list.pop obj = list.empty? ? Object : Object.full_const_get(list.join("::")) obj.const_set(last, value) if obj && !obj.const_defined?(last) end # Defines module from a string name (e.g. Foo::Bar::Baz) # If module already exists, no exception raised. # # @param name The name of the full module name to make # # @return [nil] def make_module(string) current_module = self string.split('::').each do |part| current_module = if current_module.const_defined?(part) current_module.const_get(part) else current_module.const_set(part, Module.new) end end current_module end # @param duck The thing to compare the object to. # # @note # The behavior of the method depends on the type of duck as follows: # Symbol:: Check whether the object respond_to?(duck). # Class:: Check whether the object is_a?(duck). # Array:: # Check whether the object quacks_like? at least one of the options in the # array. # # @return [Boolean] # True if the object quacks like duck. def quacks_like?(duck) case duck when Symbol self.respond_to?(duck) when Class self.is_a?(duck) when Array duck.any? {|d| self.quacks_like?(d) } else false end end # Override this in a child if it cannot be dup'ed # # @return [Object] def try_dup self.dup end # If receiver is callable, calls it and # returns result. If not, just returns receiver # itself # # @return [Object] def try_call(*args) if self.respond_to?(:call) self.call(*args) else self end end # @param arrayish<#include?> Container to check, to see if it includes the object. # @param *more:: additional args, will be flattened into arrayish # # @return [Boolean] # True if the object is included in arrayish (+ more) # # @example 1.in?([1,2,3]) #=> true # @example 1.in?(1,2,3) #=> true def in?(arrayish,*more) arrayish = more.unshift(arrayish) unless more.empty? arrayish.include?(self) end # Add instance_variable_defined? for backward compatibility # @param variable # # @return [Boolean] # True if the object has the given instance variable defined unless respond_to?(:instance_variable_defined?) def instance_variable_defined?(variable) instance_variables.include?(variable.to_s) end end end extlib-0.9.16/lib/extlib/object_space.rb000066400000000000000000000003431207310540200200730ustar00rootroot00000000000000module ObjectSpace class << self # @return [Array] All the classes in the object space. def classes klasses = [] ObjectSpace.each_object(Class) {|o| klasses << o} klasses end end end extlib-0.9.16/lib/extlib/pathname.rb000066400000000000000000000010711207310540200172460ustar00rootroot00000000000000class Pathname # Append path segments and expand to absolute path # # file = Pathname(Dir.pwd) / "subdir1" / :subdir2 / "filename.ext" # # @param [Pathname, String, #to_s] path path segment to concatenate with receiver # # @return [Pathname] # receiver with _path_ appended and expanded to an absolute path # # @api public def /(path) (self + path).expand_path end # alias to_s to to_str when to_str not defined unless public_instance_methods(false).any? { |m| m.to_sym == :to_str } alias to_str to_s end end # class Pathname extlib-0.9.16/lib/extlib/pooling.rb000066400000000000000000000142601207310540200171240ustar00rootroot00000000000000require 'set' require 'thread' module Extlib # ==== Notes # Provides pooling support to class it got included in. # # Pooling of objects is a faster way of aquiring instances # of objects compared to regular allocation and initialization # because instances are keeped in memory reused. # # Classes that include Pooling module have re-defined new # method that returns instances acquired from pool. # # Term resource is used for any type of poolable objects # and should NOT be thought as DataMapper Resource or # ActiveResource resource and such. # # In Data Objects connections are pooled so that it is # unnecessary to allocate and initialize connection object # each time connection is needed, like per request in a # web application. # # Pool obviously has to be thread safe because state of # object is reset when it is released. module Pooling def self.scavenger? defined?(@scavenger) && !@scavenger.nil? && @scavenger.alive? end def self.scavenger unless scavenger? @scavenger = Thread.new do running = true while running do # Sleep before we actually start doing anything. # Otherwise we might clean up something we just made sleep(scavenger_interval) lock.synchronize do pools.each do |pool| # This is a useful check, but non-essential, and right now it breaks lots of stuff. # if pool.expired? pool.lock.synchronize do if pool.expired? pool.dispose end end # end end # The pool is empty, we stop the scavenger # It wil be restarted if new resources are added again if pools.empty? running = false end end end # loop end end @scavenger.priority = -10 @scavenger end def self.pools @pools ||= Set.new end def self.append_pool(pool) lock.synchronize do pools << pool end Extlib::Pooling.scavenger end def self.lock @lock ||= Mutex.new end class InvalidResourceError < StandardError end def self.included(target) target.class_eval do class << self alias __new new end @__pools = {} @__pool_lock = Mutex.new @__pool_wait = ConditionVariable.new def self.__pool_lock @__pool_lock end def self.__pool_wait @__pool_wait end def self.new(*args) (@__pools[args] ||= __pool_lock.synchronize { Pool.new(self.pool_size, self, args) }).new end def self.__pools @__pools end def self.pool_size 8 end end end def release @__pool.release(self) unless @__pool.nil? end def detach @__pool.delete(self) unless @__pool.nil? end class Pool attr_reader :available attr_reader :used def initialize(max_size, resource, args) raise ArgumentError.new("+max_size+ should be a Fixnum but was #{max_size.inspect}") unless Fixnum === max_size raise ArgumentError.new("+resource+ should be a Class but was #{resource.inspect}") unless Class === resource @max_size = max_size @resource = resource @args = args @available = [] @used = {} Extlib::Pooling.append_pool(self) end def lock @resource.__pool_lock end def wait @resource.__pool_wait end def scavenge_interval @resource.scavenge_interval end def new instance = nil begin lock.synchronize do if @available.size > 0 instance = @available.pop @used[instance.object_id] = instance elsif @used.size < @max_size instance = @resource.__new(*@args) raise InvalidResourceError.new("#{@resource} constructor created a nil object") if instance.nil? raise InvalidResourceError.new("#{instance} is already part of the pool") if @used.include? instance instance.instance_variable_set(:@__pool, self) instance.instance_variable_set(:@__allocated_in_pool, Time.now) @used[instance.object_id] = instance else # Wait for another thread to release an instance. # If we exhaust the pool and don't release the active instance, # we'll wait here forever, so it's *very* important to always # release your services and *never* exhaust the pool within # a single thread. wait.wait(lock) end end end until instance instance end def release(instance) lock.synchronize do instance.instance_variable_set(:@__allocated_in_pool, Time.now) @used.delete(instance.object_id) @available.push(instance) wait.signal end nil end def delete(instance) lock.synchronize do instance.instance_variable_set(:@__pool, nil) @used.delete(instance.object_id) wait.signal end nil end def size @used.size + @available.size end alias length size def inspect "# available=#{@available.size} used=#{@used.size} size=#{@max_size}>" end def flush! @available.pop.dispose until @available.empty? end def dispose flush! @resource.__pools.delete(@args) !Extlib::Pooling.pools.delete?(self).nil? end def expired? @available.each do |instance| if Extlib.exiting || instance.instance_variable_get(:@__allocated_in_pool) + Extlib::Pooling.scavenger_interval <= (Time.now + 0.02) instance.dispose @available.delete(instance) end end size == 0 end end def self.scavenger_interval 60 end end # module Pooling end # module Extlib extlib-0.9.16/lib/extlib/rubygems.rb000066400000000000000000000025301207310540200173070ustar00rootroot00000000000000# this is a temporary workaround until rubygems Does the Right thing here require 'rubygems' module Gem class SourceIndex # This is resolved in 1.1 if Version.new(RubyGemsVersion) < Version.new("1.1") # Overwrite this so that a gem of the same name and version won't push one # from the gems directory out entirely. # # @param gem_spec The specification of the gem to add. def add_spec(gem_spec) unless gem_spec.instance_variable_get("@loaded_from") && @gems[gem_spec.full_name].is_a?(Gem::Specification) && @gems[gem_spec.full_name].installation_path == File.join(defined?(Merb) && Merb.respond_to?(:root) ? Merb.root : Dir.pwd,"gems") @gems[gem_spec.full_name] = gem_spec end end end end class Specification # Overwrite this so that gems in the gems directory get preferred over gems # from any other location. If there are two gems of different versions in # the gems directory, the later one will load as usual. # # @return [Array] The object used for sorting gem specs. def sort_obj [@name, installation_path == File.join(defined?(Merb) && Merb.respond_to?(:root) ? Merb.root : Dir.pwd,"gems") ? 1 : -1, @version.to_ints, @new_platform == Gem::Platform::RUBY ? -1 : 1] end end end extlib-0.9.16/lib/extlib/simple_set.rb000066400000000000000000000030731207310540200176210ustar00rootroot00000000000000module Extlib # Simple set implementation # on top of Hash with merging support. # # In particular this is used to store # a set of callable actions of controller. class SimpleSet < Hash ## # Create a new SimpleSet containing the unique members of _arr_ # # @param [Array] arr Initial set values. # # @return [Array] The array the Set was initialized with # # @api public def initialize(arr = []) Array(arr).each {|x| self[x] = true} end ## # Add a value to the set, and return it # # @param [Object] value Value to add to set. # # @return [SimpleSet] Receiver # # @api public def <<(value) self[value] = true self end ## # Merge _arr_ with receiver, producing the union of receiver & _arr_ # # s = Extlib::SimpleSet.new([:a, :b, :c]) # s.merge([:c, :d, :e, f]) #=> # # # @param [Array] arr Values to merge with set. # # @return [SimpleSet] The set after the Array was merged in. # # @api public def merge(arr) super(arr.inject({}) {|s,x| s[x] = true; s }) end ## # Get a human readable version of the set. # # s = SimpleSet.new([:a, :b, :c]) # s.inspect #=> "#" # # @return [String] A human readable version of the set. # # @api public def inspect "#" end # def to_a alias_method :to_a, :keys end # SimpleSet end # Merb extlib-0.9.16/lib/extlib/string.rb000066400000000000000000000106621207310540200167650ustar00rootroot00000000000000require "pathname" class String ## # Escape all regexp special characters. # # "*?{}.".escape_regexp #=> "\\*\\?\\{\\}\\." # # @return [String] Receiver with all regexp special characters escaped. # # @api public def escape_regexp Regexp.escape self end ## # Unescape all regexp special characters. # # "\\*\\?\\{\\}\\.".unescape_regexp #=> "*?{}." # # @return [String] Receiver with all regexp special characters unescaped. # # @api public def unescape_regexp self.gsub(/\\([\.\?\|\(\)\[\]\{\}\^\$\*\+\-])/, '\1') end ## # Convert to snake case. # # "FooBar".snake_case #=> "foo_bar" # "HeadlineCNNNews".snake_case #=> "headline_cnn_news" # "CNN".snake_case #=> "cnn" # # @return [String] Receiver converted to snake case. # # @api public def snake_case return downcase if match(/\A[A-Z]+\z/) gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2'). gsub(/([a-z])([A-Z])/, '\1_\2'). downcase end ## # Convert to camel case. # # "foo_bar".camel_case #=> "FooBar" # # @return [String] Receiver converted to camel case. # # @api public def camel_case return self if self !~ /_/ && self =~ /[A-Z]+.*/ split('_').map{|e| e.capitalize}.join end ## # Convert a path string to a constant name. # # "merb/core_ext/string".to_const_string #=> "Merb::CoreExt::String" # # @return [String] Receiver converted to a constant name. # # @api public def to_const_string gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } end ## # Convert a constant name to a path, assuming a conventional structure. # # "FooBar::Baz".to_const_path # => "foo_bar/baz" # # @return [String] Path to the file containing the constant named by receiver # (constantized string), assuming a conventional structure. # # @api public def to_const_path snake_case.gsub(/::/, "/") end ## # Join with _o_ as a file path. # # "merb"/"core_ext" #=> "merb/core_ext" # # @param [String] o Path component to join with receiver. # # @return [String] Receiver joined with o as a file path. # # @api public def /(o) File.join(self, o.to_s) end ## # Calculate a relative path *from* _other_. # # "/opt/local/lib".relative_path_from("/opt/local/lib/ruby/site_ruby") # => "../.." # # @param [String] other Base path to calculate *from*. # # @return [String] Relative path from _other_ to receiver. # # @api public def relative_path_from(other) Pathname.new(self).relative_path_from(Pathname.new(other)).to_s end # Overwrite this method to provide your own translations. def self.translate(value) translations[value] || value end def self.translations @translations ||= {} end ## # Replace sequences of whitespace (including newlines) with either # a single space or remove them entirely (according to param _spaced_) # # < "SELECT name FROM users" # # @param [TrueClass, FalseClass] spaced (default=true) # Determines whether returned string has whitespace collapsed or removed # # @return [String] Receiver with whitespace (including newlines) replaced # # @api public def compress_lines(spaced = true) split($/).map { |line| line.strip }.join(spaced ? ' ' : '') end ## # Remove whitespace margin. # # @param [Object] indicator ??? # # @return [String] receiver with whitespace margin removed # # @api public def margin(indicator = nil) lines = self.dup.split($/) min_margin = 0 lines.each do |line| if line =~ /^(\s+)/ && (min_margin == 0 || $1.size < min_margin) min_margin = $1.size end end lines.map { |line| line.sub(/^\s{#{min_margin}}/, '') }.join($/) end ## # Formats String for easy translation. Replaces an arbitrary number of # values using numeric identifier replacement. # # "%s %s %s" % %w(one two three) #=> "one two three" # "%3$s %2$s %1$s" % %w(one two three) #=> "three two one" # # @param [#to_s] values # A list of values to translate and interpolate into receiver # # @return [String] # Receiver translated with values translated and interpolated positionally # # @api public def t(*values) self.class::translate(self) % values.collect! { |value| value.frozen? ? value : self.class::translate(value.to_s) } end end # class String extlib-0.9.16/lib/extlib/struct.rb000066400000000000000000000007021207310540200167750ustar00rootroot00000000000000class Struct ## # Get a hash with names and values of all instance variables. # # class Foo < Struct.new(:name, :age, :gender); end # f = Foo.new("Jill", 50, :female) # f.attributes #=> {:name => "Jill", :age => 50, :gender => :female} # # @return [Hash] Hash of instance variables in receiver, keyed by ivar name # # @api public def attributes h = {} each_pair { |k,v| h[k] = v } h end end # class Struct extlib-0.9.16/lib/extlib/symbol.rb000066400000000000000000000006401207310540200167570ustar00rootroot00000000000000class Symbol def try_dup self end ## # Join with _o_ as a file path # # :merb/"core_ext" #=> "merb/core_ext" # :merb / :core_ext / :string #=> "merb/core_ext/string" # # @param [#to_s] o The path component(s) to append. # # @return [String] The receiver (as path string), concatenated with _o_. # # @api public def /(o) File.join(self.to_s, o.to_s) end end extlib-0.9.16/lib/extlib/time.rb000066400000000000000000000021031207310540200164040ustar00rootroot00000000000000require 'date' require 'extlib/datetime' class Time ## # Convert to ISO 8601 representation # # Time.now.to_json #=> "\"2008-03-28T17:54:20-05:00\"" # # @return [String] # ISO 8601 compatible representation of the Time object. # # @api public def to_json(*) self.xmlschema.to_json end ## # Return receiver (for DateTime/Time conversion protocol). # # Time.now.to_time #=> Wed Nov 19 20:08:28 -0800 2008 # # @return [Time] Receiver # # @api public remove_method :to_time if instance_methods(false).any? { |m| m.to_sym == :to_time } def to_time self end ## # Convert to DateTime (for DateTime/Time conversion protocol). # # Time.now.to_datetime #=> # # # @return [DateTime] DateTime object representing the same moment as receiver # # @api public remove_method :to_datetime if instance_methods(false).any? { |m| m.to_sym == :to_datetime } def to_datetime DateTime.new(year, month, day, hour, min, sec, Rational(gmt_offset, 24 * 3600)) end end extlib-0.9.16/lib/extlib/try_dup.rb000066400000000000000000000006431207310540200171430ustar00rootroot00000000000000class Object # Override this in a child if it cannot be dup'ed # # @return [Object] def try_dup self.dup end end class TrueClass def try_dup self end end class FalseClass def try_dup self end end class Module def try_dup self end end class NilClass def try_dup self end end class Numeric def try_dup self end end class Symbol def try_dup self end end extlib-0.9.16/lib/extlib/virtual_file.rb000066400000000000000000000003151207310540200201360ustar00rootroot00000000000000require "stringio" # To use as a parameter to Merb::Template.inline_template class VirtualFile < StringIO attr_accessor :path def initialize(string, path) super(string) @path = path end end extlib-0.9.16/spec/000077500000000000000000000000001207310540200140225ustar00rootroot00000000000000extlib-0.9.16/spec/array_spec.rb000066400000000000000000000014701207310540200165010ustar00rootroot00000000000000require 'spec_helper' require 'extlib/array' describe Array do before :all do @array = [ [ :a, [ 1 ] ], [ :b, [ 2 ] ], [ :c, [ 3 ] ] ].freeze end it { @array.should respond_to(:to_hash) } describe '#to_hash' do before :all do @return = @array.to_hash end it 'should return a Hash' do @return.should be_kind_of(Hash) end it 'should return expected value' do @return.should == { :a => [ 1 ], :b => [ 2 ], :c => [ 3 ] } end end it { @array.should respond_to(:to_mash) } describe '#to_mash' do before :all do @return = @array.to_mash end it 'should return a Mash' do @return.should be_kind_of(Mash) end it 'should return expected value' do @return.should == { 'a' => [ 1 ], 'b' => [ 2 ], 'c' => [ 3 ] } end end end extlib-0.9.16/spec/blank_spec.rb000066400000000000000000000030771207310540200164570ustar00rootroot00000000000000require 'spec_helper' require 'extlib/blank' describe Object do it 'should provide blank?' do Object.new.should respond_to(:blank?) end it 'should be blank if it is nil' do object = Object.new class << object def nil?; true end end object.should be_blank end it 'should be blank if it is empty' do {}.should be_blank [].should be_blank end it 'should not be blank if not nil or empty' do Object.new.should_not be_blank [nil].should_not be_blank { nil => 0 }.should_not be_blank end end describe Numeric do it 'should provide blank?' do 1.should respond_to(:blank?) end it 'should never be blank' do 1.should_not be_blank end end describe NilClass do it 'should provide blank?' do nil.should respond_to(:blank?) end it 'should always be blank' do nil.should be_blank end end describe TrueClass do it 'should provide blank?' do true.should respond_to(:blank?) end it 'should never be blank' do true.should_not be_blank end end describe FalseClass do it 'should provide blank?' do false.should respond_to(:blank?) end it 'should always be blank' do false.should be_blank end end describe String do it 'should provide blank?' do 'string'.should respond_to(:blank?) end it 'should be blank if empty' do ''.should be_blank end it 'should be blank if it only contains whitespace' do ' '.should be_blank " \r \n \t ".should be_blank end it 'should not be blank if it contains non-whitespace' do ' a '.should_not be_blank end end extlib-0.9.16/spec/byte_array_spec.rb000066400000000000000000000002541207310540200175230ustar00rootroot00000000000000require 'spec_helper' require 'extlib/byte_array' describe Extlib::ByteArray do it 'should be a String' do Extlib::ByteArray.new.should be_kind_of(String) end end extlib-0.9.16/spec/class_spec.rb000066400000000000000000000076661207310540200165050ustar00rootroot00000000000000require 'spec_helper' require 'extlib/class' class Grandparent end class Parent < Grandparent end class Child < Parent end class Parent end class Grandparent class_inheritable_accessor :last_name, :_attribute self._attribute = 1900 end class ClassWithInheritableSymbolAccessor class_inheritable_accessor :symbol self.symbol = :foo end class ClassInheritingSymbolAccessor < ClassWithInheritableSymbolAccessor end describe Class, "#inheritable_accessor" do after :each do Grandparent.send(:remove_instance_variable, "@last_name") rescue nil Parent.send(:remove_instance_variable, "@last_name") rescue nil Child.send(:remove_instance_variable, "@last_name") rescue nil end it 'inherits from parent unless overriden' do Parent._attribute.should == 1900 Child._attribute.should == 1900 end it 'inherits from grandparent unless overriden' do Child._attribute.should == 1900 end it "inherits even if the accessor is made after the inheritance" do Grandparent.last_name = "Merb" Parent.last_name.should == "Merb" Child.last_name.should == "Merb" end it "supports ||= to change a child" do Parent.last_name ||= "Merb" Grandparent.last_name.should == nil Parent.last_name.should == "Merb" Child.last_name.should == "Merb" end it "supports << to change a child when the parent is an Array" do Grandparent.last_name = ["Merb"] Parent.last_name << "Core" Grandparent.last_name.should == ["Merb"] Parent.last_name.should == ["Merb", "Core"] end it "supports ! methods on an Array" do Grandparent.last_name = %w(Merb Core) Parent.last_name.reverse! Grandparent.last_name.should == %w(Merb Core) Parent.last_name.should == %w(Core Merb) end it "support modifying a parent Hash" do Grandparent.last_name = {"Merb" => "name"} Parent.last_name["Core"] = "name" Parent.last_name.should == {"Merb" => "name", "Core" => "name"} Grandparent.last_name.should == {"Merb" => "name"} end it "supports hard-merging a parent Hash" do Grandparent.last_name = {"Merb" => "name"} Parent.last_name.merge!("Core" => "name") Parent.last_name.should == {"Merb" => "name", "Core" => "name"} Grandparent.last_name.should == {"Merb" => "name"} end it "supports changes to the parent even if the child has already been read" do Child.last_name Grandparent.last_name = "Merb" Child.last_name.should == "Merb" end it "handles nil being set midstream" do Child.last_name Parent.last_name = nil Grandparent.last_name = "Merb" Child.last_name.should == nil end it "handles false being used in Parent" do Child.last_name Parent.last_name = false Grandparent.last_name = "Merb" Child.last_name.should == false end it "handles the grandparent changing the value (as long as the child isn't read first)" do Grandparent.last_name = "Merb" Grandparent.last_name = "Core" Child.last_name.should == "Core" end end describe Class, "#inheritable_accessor (of type Symbol)" do it "should not raise" do lambda { ClassInheritingSymbolAccessor.symbol }.should_not raise_error(TypeError) end end # # The bug that prompted this estoric spec was found in # the wild when using dm-is-versioned with c_i_w. # module Plugin def self.included(base) base.class_eval do class_inheritable_writer :plugin_options class_inheritable_reader :plugin_options self.plugin_options = :foo end end end class Model def self.new model = Class.new model.send(:include, Plugin) model end include Plugin self.const_set("Version", Model.new) end describe Class, "#inheritable_accessor" do it "uses object_id for comparison" do Model.methods.map { |m| m.to_sym }.should be_include(:plugin_options) Model.plugin_options.should == :foo Model::Version.methods.map { |m| m.to_sym }.should be_include(:plugin_options) Model::Version.plugin_options.should == :foo end end extlib-0.9.16/spec/datetime_spec.rb000066400000000000000000000007321207310540200171570ustar00rootroot00000000000000require 'spec_helper' require 'extlib/datetime' describe DateTime, "#to_time" do before do @expected = Time.now.to_s @datetime = DateTime.parse(@expected) end it "should return a copy of time" do time = @datetime.to_time time.class.should == Time time.to_s.should == @expected end end describe Time, "#to_datetime" do it "should return a copy of its self" do datetime = DateTime.now datetime.to_datetime.should == datetime end end extlib-0.9.16/spec/hash_spec.rb000066400000000000000000000414261207310540200163130ustar00rootroot00000000000000require 'spec_helper' require 'extlib/hash' describe Hash, "environmentize_keys!" do it "should transform keys to uppercase text" do { :test_1 => 'test', 'test_2' => 'test', 1 => 'test' }.environmentize_keys!.should == { 'TEST_1' => 'test', 'TEST_2' => 'test', '1' => 'test' } end it "should only transform one level of keys" do { :test_1 => { :test2 => 'test'} }.environmentize_keys!.should == { 'TEST_1' => { :test2 => 'test'} } end end describe Hash, "only" do before do @hash = { :one => 'ONE', 'two' => 'TWO', 3 => 'THREE', 4 => nil } end it "should return a hash with only the given key(s)" do @hash.only(:not_in_there).should == {} @hash.only(4).should == {4 => nil} @hash.only(:one).should == { :one => 'ONE' } @hash.only(:one, 3).should == { :one => 'ONE', 3 => 'THREE' } end end describe Hash, "except" do before do @hash = { :one => 'ONE', 'two' => 'TWO', 3 => 'THREE' } end it "should return a hash without only the given key(s)" do @hash.except(:one).should == { 'two' => 'TWO', 3 => 'THREE' } @hash.except(:one, 3).should == { 'two' => 'TWO' } end end describe Hash, "to_xml_attributes" do before do @hash = { :one => "ONE", "two" => "TWO" } end it "should turn the hash into xml attributes" do attrs = @hash.to_xml_attributes attrs.should match(/one="ONE"/m) attrs.should match(/two="TWO"/m) end it 'should preserve _ in hash keys' do attrs = { :some_long_attribute => "with short value", :crash => :burn, :merb => "uses extlib" }.to_xml_attributes attrs.should =~ /some_long_attribute="with short value"/ attrs.should =~ /merb="uses extlib"/ attrs.should =~ /crash="burn"/ end end describe Hash, "from_xml" do it "should transform a simple tag with content" do xml = "This is the contents" Hash.from_xml(xml).should == { 'tag' => 'This is the contents' } end it "should work with cdata tags" do xml = <<-END END Hash.from_xml(xml)["tag"].strip.should == "text inside cdata" end it "should transform a simple tag with attributes" do xml = "" hash = { 'tag' => { 'attr1' => '1', 'attr2' => '2' } } Hash.from_xml(xml).should == hash end it "should transform repeating siblings into an array" do xml =<<-XML XML Hash.from_xml(xml)['opt']['user'].should be_an_instance_of(Array) hash = { 'opt' => { 'user' => [{ 'login' => 'grep', 'fullname' => 'Gary R Epstein' },{ 'login' => 'stty', 'fullname' => 'Simon T Tyson' }] } } Hash.from_xml(xml).should == hash end it "should not transform non-repeating siblings into an array" do xml =<<-XML XML Hash.from_xml(xml)['opt']['user'].should be_an_instance_of(Hash) hash = { 'opt' => { 'user' => { 'login' => 'grep', 'fullname' => 'Gary R Epstein' } } } Hash.from_xml(xml).should == hash end it "should typecast an integer" do xml = "10" Hash.from_xml(xml)['tag'].should == 10 end it "should typecast a true boolean" do xml = "true" Hash.from_xml(xml)['tag'].should be(true) end it "should typecast a false boolean" do ["false"].each do |w| Hash.from_xml("#{w}")['tag'].should be(false) end end it "should typecast a datetime" do xml = "2007-12-31 10:32" Hash.from_xml(xml)['tag'].should == Time.parse( '2007-12-31 10:32' ).utc end it "should typecast a date" do xml = "2007-12-31" Hash.from_xml(xml)['tag'].should == Date.parse('2007-12-31') end it "should unescape html entities" do values = { "<" => "<", ">" => ">", '"' => """, "'" => "'", "&" => "&" } values.each do |k,v| xml = "Some content #{v}" Hash.from_xml(xml)['tag'].should match(Regexp.new(k)) end end it "should undasherize keys as tags" do xml = "Stuff" Hash.from_xml(xml).should have_key('tag_1') end it "should undasherize keys as attributes" do xml = "" Hash.from_xml(xml)['tag1'].should have_key('attr_1') end it "should undasherize keys as tags and attributes" do xml = "" Hash.from_xml(xml).should have_key('tag_1' ) Hash.from_xml(xml)['tag_1'].should have_key('attr_1') end it "should render nested content correctly" do xml = "Tag1 Content This is strong" Hash.from_xml(xml)['root']['tag1'].should == "Tag1 Content This is strong" end it "should render nested content with split text nodes correctly" do xml = "Tag1 ContentStuff Hi There" Hash.from_xml(xml)['root'].should == "Tag1 ContentStuff Hi There" end it "should ignore attributes when a child is a text node" do xml = "Stuff" Hash.from_xml(xml).should == { "root" => "Stuff" } end it "should ignore attributes when any child is a text node" do xml = "Stuff in italics" Hash.from_xml(xml).should == { "root" => "Stuff in italics" } end it "should correctly transform multiple children" do xml = <<-XML 35 Home Simpson 1988-01-01 2000-04-28 23:01 true XML hash = { "user" => { "gender" => "m", "age" => 35, "name" => "Home Simpson", "dob" => Date.parse('1988-01-01'), "joined_at" => Time.parse("2000-04-28 23:01"), "is_cool" => true } } Hash.from_xml(xml).should == hash end it "should properly handle nil values (ActiveSupport Compatible)" do topic_xml = <<-EOT EOT expected_topic_hash = { 'title' => nil, 'id' => nil, 'approved' => nil, 'written_on' => nil, 'viewed_at' => nil, 'content' => { 'type' => 'yaml' }, 'parent_id' => nil } Hash.from_xml(topic_xml)["topic"].should == expected_topic_hash end it "should handle a single record from xml (ActiveSupport Compatible)" do topic_xml = <<-EOT The First Topic David 1 true 0 2592000000 2003-07-16 2003-07-16T09:28:00+0000 --- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n david@loudthinking.com 1.5 135 yes EOT expected_topic_hash = { 'title' => "The First Topic", 'author_name' => "David", 'id' => 1, 'approved' => true, 'replies_count' => 0, 'replies_close_in' => 2592000000, 'written_on' => Date.new(2003, 7, 16), 'viewed_at' => Time.utc(2003, 7, 16, 9, 28), # Changed this line where the key is :message. The yaml specifies this as a symbol, and who am I to change what you specify # The line in ActiveSupport is # 'content' => { 'message' => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] }, 'content' => "--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n", 'author_email_address' => "david@loudthinking.com", 'parent_id' => nil, 'ad_revenue' => BigDecimal("1.50"), 'optimum_viewing_angle' => 135.0, 'resident' => 'yes' } Hash.from_xml(topic_xml)["topic"].each do |k,v| v.should == expected_topic_hash[k] end end it "should handle multiple records (ActiveSupport Compatible)" do topics_xml = <<-EOT The First Topic David 1 false 0 2592000000 2003-07-16 2003-07-16T09:28:00+0000 Have a nice day david@loudthinking.com The Second Topic Jason 1 false 0 2592000000 2003-07-16 2003-07-16T09:28:00+0000 Have a nice day david@loudthinking.com EOT expected_topic_hash = { 'title' => "The First Topic", 'author_name' => "David", 'id' => 1, 'approved' => false, 'replies_count' => 0, 'replies_close_in' => 2592000000, 'written_on' => Date.new(2003, 7, 16), 'viewed_at' => Time.utc(2003, 7, 16, 9, 28), 'content' => "Have a nice day", 'author_email_address' => "david@loudthinking.com", 'parent_id' => nil } # puts Hash.from_xml(topics_xml)['topics'].first.inspect Hash.from_xml(topics_xml)["topics"].first.each do |k,v| v.should == expected_topic_hash[k] end end it "should handle a single record from_xml with attributes other than type (ActiveSupport Compatible)" do topic_xml = <<-EOT EOT expected_topic_hash = { 'id' => "175756086", 'owner' => "55569174@N00", 'secret' => "0279bf37a1", 'server' => "76", 'title' => "Colored Pencil PhotoBooth Fun", 'ispublic' => "1", 'isfriend' => "0", 'isfamily' => "0", } Hash.from_xml(topic_xml)["rsp"]["photos"]["photo"].each do |k,v| v.should == expected_topic_hash[k] end end it "should handle an emtpy array (ActiveSupport Compatible)" do blog_xml = <<-XML XML expected_blog_hash = {"blog" => {"posts" => []}} Hash.from_xml(blog_xml).should == expected_blog_hash end it "should handle empty array with whitespace from xml (ActiveSupport Compatible)" do blog_xml = <<-XML XML expected_blog_hash = {"blog" => {"posts" => []}} Hash.from_xml(blog_xml).should == expected_blog_hash end it "should handle array with one entry from_xml (ActiveSupport Compatible)" do blog_xml = <<-XML a post XML expected_blog_hash = {"blog" => {"posts" => ["a post"]}} Hash.from_xml(blog_xml).should == expected_blog_hash end it "should handle array with multiple entries from xml (ActiveSupport Compatible)" do blog_xml = <<-XML a post another post XML expected_blog_hash = {"blog" => {"posts" => ["a post", "another post"]}} Hash.from_xml(blog_xml).should == expected_blog_hash end it "should handle file types (ActiveSupport Compatible)" do blog_xml = <<-XML XML hash = Hash.from_xml(blog_xml) hash.should have_key('blog') hash['blog'].should have_key('logo') file = hash['blog']['logo'] file.original_filename.should == 'logo.png' file.content_type.should == 'image/png' end it "should handle file from xml with defaults (ActiveSupport Compatible)" do blog_xml = <<-XML XML file = Hash.from_xml(blog_xml)['blog']['logo'] file.original_filename.should == 'untitled' file.content_type.should == 'application/octet-stream' end it "should handle xsd like types from xml (ActiveSupport Compatible)" do bacon_xml = <<-EOT 0.5 12.50 1 2007-12-25T12:34:56+0000 YmFiZS5wbmc= EOT expected_bacon_hash = { 'weight' => 0.5, 'chunky' => true, 'price' => BigDecimal("12.50"), 'expires_at' => Time.utc(2007,12,25,12,34,56), 'notes' => "", 'illustration' => "babe.png" } Hash.from_xml(bacon_xml)["bacon"].should == expected_bacon_hash end it "should let type trickle through when unknown (ActiveSupport Compatible)" do product_xml = <<-EOT 0.5 image.gif EOT expected_product_hash = { 'weight' => 0.5, 'image' => {'type' => 'ProductImage', 'filename' => 'image.gif' }, } Hash.from_xml(product_xml)["product"].should == expected_product_hash end it "should handle unescaping from xml (ActiveResource Compatible)" do xml_string = 'First & Last NameFirst &amp; Last Name' expected_hash = { 'bare_string' => 'First & Last Name', 'pre_escaped_string' => 'First & Last Name' } Hash.from_xml(xml_string)['person'].should == expected_hash end end describe Hash, 'to_params' do { { "foo" => "bar", "baz" => "bat" } => "foo=bar&baz=bat", { "foo" => [ "bar", "baz" ] } => "foo%5B%5D=bar&foo%5B%5D=baz", { "foo" => [ {"bar" => "1"}, {"bar" => 2} ] } => "foo%5B%5D%5Bbar%5D=1&foo%5B%5D%5Bbar%5D=2", { "foo" => { "bar" => [ {"baz" => 1}, {"baz" => "2"} ] } } => "foo%5Bbar%5D%5B%5D%5Bbaz%5D=1&foo%5Bbar%5D%5B%5D%5Bbaz%5D=2", { "foo" => {"1" => "bar", "2" => "baz"} } => "foo%5B1%5D=bar&foo%5B2%5D=baz" }.each do |hash, params| it "should covert hash: #{hash.inspect} to params: #{params.inspect}" do hash.to_params.split('&').sort.should == params.split('&').sort end end it 'should not leave a trailing &' do { :name => 'Bob', :address => { :street => '111 Ruby Ave.', :city => 'Ruby Central', :phones => ['111-111-1111', '222-222-2222'] } }.to_params.should_not match(/&$/) end it 'should encode query keys' do { 'First & Last' => 'Alice Smith' }.to_params.should == "First%20%26%20Last=Alice%20Smith" end it 'should encode query values' do { :name => 'Alice & Bob' }.to_params.should == "name=Alice%20%26%20Bob" end end describe Hash, 'to_mash' do before :each do @hash = Hash.new(10) end it "copies default Hash value to Mash" do @mash = @hash.to_mash @mash[:merb].should == 10 end end extlib-0.9.16/spec/hook_spec.rb000066400000000000000000001211231207310540200163210ustar00rootroot00000000000000require 'spec_helper' require 'extlib/hook' describe Extlib::Hook do before do @module = Module.new do def greet; greetings_from_module; end; end @class = Class.new do include Extlib::Hook def hookable; end; def self.clakable; end; def ambiguous; hi_mom!; end; def self.ambiguous; hi_dad!; end; end @another_class = Class.new do include Extlib::Hook end @other = Class.new do include Extlib::Hook def hookable; end def self.clakable; end; end @class.register_instance_hooks :hookable @class.register_class_hooks :clakable end # # Specs out how hookable methods are registered # describe "explicit hookable method registration" do describe "for class methods" do it "shouldn't confuse instance method hooks and class method hooks" do @class.register_instance_hooks :ambiguous @class.register_class_hooks :ambiguous @class.should_receive(:hi_dad!) @class.ambiguous end it "should be able to register multiple hookable methods at once" do %w(method_one method_two method_three).each do |method| @another_class.class_eval %(def self.#{method}; end;) end @another_class.register_class_hooks :method_one, :method_two, :method_three @another_class.class_hooks.should have_key(:method_one) @another_class.class_hooks.should have_key(:method_two) @another_class.class_hooks.should have_key(:method_three) end it "should not allow a method that does not exist to be registered as hookable" do lambda { @another_class.register_class_hooks :method_one }.should raise_error(ArgumentError) end it "should allow hooks to be registered on methods from module extensions" do @class.extend(@module) @class.register_class_hooks :greet @class.class_hooks[:greet].should_not be_nil end it "should allow modules to register hooks in the self.extended method" do @module.class_eval do def self.extended(base) base.register_class_hooks :greet end end @class.extend(@module) @class.class_hooks[:greet].should_not be_nil end it "should be able to register protected methods as hooks" do @class.class_eval %{protected; def self.protected_hookable; end;} lambda { @class.register_class_hooks(:protected_hookable) }.should_not raise_error(ArgumentError) end it "should not be able to register private methods as hooks" do @class.class_eval %{class << self; private; def private_hookable; end; end;} lambda { @class.register_class_hooks(:private_hookable) }.should raise_error(ArgumentError) end it "should allow advising methods ending in ? or !" do @class.class_eval do def self.hookable!; two!; end; def self.hookable?; three!; end; register_class_hooks :hookable!, :hookable? end @class.before_class_method(:hookable!) { one! } @class.after_class_method(:hookable?) { four! } @class.should_receive(:one!).once.ordered @class.should_receive(:two!).once.ordered @class.should_receive(:three!).once.ordered @class.should_receive(:four!).once.ordered @class.hookable! @class.hookable? end it "should allow hooking methods ending in ?, ! or = with method hooks" do @class.class_eval do def self.before_hookable!; one!; end; def self.hookable!; two!; end; def self.hookable?; three!; end; def self.after_hookable?; four!; end; register_class_hooks :hookable!, :hookable? end @class.before_class_method(:hookable!, :before_hookable!) @class.after_class_method(:hookable?, :after_hookable?) @class.should_receive(:one!).once.ordered @class.should_receive(:two!).once.ordered @class.should_receive(:three!).once.ordered @class.should_receive(:four!).once.ordered @class.hookable! @class.hookable? end it "should allow hooking methods that have single character names" do @class.class_eval do def self.a; end; def self.b; end; end @class.before_class_method(:a) { omg! } @class.before_class_method(:b) { hi2u! } @class.should_receive(:omg!).once.ordered @class.should_receive(:hi2u!).once.ordered @class.a @class.b end end describe "for instance methods" do it "shouldn't confuse instance method hooks and class method hooks" do @class.register_instance_hooks :ambiguous @class.register_class_hooks :ambiguous inst = @class.new inst.should_receive(:hi_mom!) inst.ambiguous end it "should be able to register multiple hookable methods at once" do %w(method_one method_two method_three).each do |method| @another_class.send(:define_method, method) {} end @another_class.register_instance_hooks :method_one, :method_two, :method_three @another_class.instance_hooks.should have_key(:method_one) @another_class.instance_hooks.should have_key(:method_two) @another_class.instance_hooks.should have_key(:method_three) end it "should not allow a method that does not exist to be registered as hookable" do lambda { @another_class.register_instance_hooks :method_one }.should raise_error(ArgumentError) end it "should allow hooks to be registered on included module methods" do @class.send(:include, @module) @class.register_instance_hooks :greet @class.instance_hooks[:greet].should_not be_nil end it "should allow modules to register hooks in the self.included method" do @module.class_eval do def self.included(base) base.register_instance_hooks :greet end end @class.send(:include, @module) @class.instance_hooks[:greet].should_not be_nil end it "should be able to register protected methods as hooks" do @class.class_eval %{protected; def protected_hookable; end;} lambda { @class.register_instance_hooks(:protected_hookable) }.should_not raise_error(ArgumentError) end it "should not be able to register private methods as hooks" do @class.class_eval %{private; def private_hookable; end;} lambda { @class.register_instance_hooks(:private_hookable) }.should raise_error(ArgumentError) end it "should allow hooking methods ending in ? or ! with block hooks" do @class.class_eval do def hookable!; two!; end; def hookable?; three!; end; register_instance_hooks :hookable!, :hookable? end @class.before(:hookable!) { one! } @class.after(:hookable?) { four! } inst = @class.new inst.should_receive(:one!).once.ordered inst.should_receive(:two!).once.ordered inst.should_receive(:three!).once.ordered inst.should_receive(:four!).once.ordered inst.hookable! inst.hookable? end it "should allow hooking methods ending in ?, ! or = with method hooks" do @class.class_eval do def before_hookable(val); one!; end; def hookable=(val); two!; end; def hookable?; three!; end; def after_hookable?; four!; end; register_instance_hooks :hookable=, :hookable? end @class.before(:hookable=, :before_hookable) @class.after(:hookable?, :after_hookable?) inst = @class.new inst.should_receive(:one!).once.ordered inst.should_receive(:two!).once.ordered inst.should_receive(:three!).once.ordered inst.should_receive(:four!).once.ordered inst.hookable = 'hello' inst.hookable? end it "should allow hooking methods that have single character names" do @class.class_eval do def a; end; def b; end; end @class.before(:a) { omg! } @class.before(:b) { hi2u! } inst = @class.new inst.should_receive(:omg!).once.ordered inst.should_receive(:hi2u!).once.ordered inst.a inst.b end end end describe "implicit hookable method registration" do describe "for class methods" do it "should implicitly register the method as hookable" do @class.class_eval %{def self.implicit_hook; end;} @class.before_class_method(:implicit_hook) { hello } @class.should_receive(:hello) @class.implicit_hook end end describe "for instance methods" do it "should implicitly register the method as hookable" do @class.class_eval %{def implicit_hook; end;} @class.before(:implicit_hook) { hello } inst = @class.new inst.should_receive(:hello) inst.implicit_hook end it 'should not overwrite methods included by modules after the hook is declared' do my_module = Module.new do # Just another module @another_module = Module.new do def some_method; "Hello " + super; end; end def some_method; "world"; end; def self.included(base) base.before(:some_method, :a_method) base.send(:include, @another_module) end end @class.class_eval { include my_module } inst = @class.new inst.should_receive(:a_method) inst.some_method.should == "Hello world" end end end describe "hook method registration" do describe "for class methods" do it "should complain when only one argument is passed" do lambda { @class.before_class_method(:clakable) }.should raise_error(ArgumentError) lambda { @class.after_class_method(:clakable) }.should raise_error(ArgumentError) end it "should complain when target_method is not a symbol" do lambda { @class.before_class_method("clakable", :ambiguous) }.should raise_error(ArgumentError) lambda { @class.after_class_method("clakable", :ambiguous) }.should raise_error(ArgumentError) end it "should complain when method_sym is not a symbol" do lambda { @class.before_class_method(:clakable, "ambiguous") }.should raise_error(ArgumentError) lambda { @class.after_class_method(:clakable, "ambiguous") }.should raise_error(ArgumentError) end it "should not allow methods ending in = to be hooks" do lambda { @class.before_class_method(:clakable, :annoying=) }.should raise_error(ArgumentError) lambda { @class.after_class_method(:clakable, :annoying=) }.should raise_error(ArgumentError) end end describe "for instance methods" do it "should complain when only one argument is passed" do lambda { @class.before(:hookable) }.should raise_error(ArgumentError) lambda { @class.after(:hookable) }.should raise_error(ArgumentError) end it "should complain when target_method is not a symbol" do lambda { @class.before("hookable", :ambiguous) }.should raise_error(ArgumentError) lambda { @class.after("hookable", :ambiguous) }.should raise_error(ArgumentError) end it "should complain when method_sym is not a symbol" do lambda { @class.before(:hookable, "ambiguous") }.should raise_error(ArgumentError) lambda { @class.after(:hookable, "ambiguous") }.should raise_error(ArgumentError) end it "should not allow methods ending in = to be hooks" do lambda { @class.before(:hookable, :annoying=) }.should raise_error(ArgumentError) lambda { @class.after(:hookable, :annoying=) }.should raise_error(ArgumentError) end end end # # Specs out how hook methods / blocks are invoked when there is no inheritance # involved # describe "hook invocation without inheritance" do describe "for class methods" do it 'should run an advice block' do @class.before_class_method(:clakable) { hi_mom! } @class.should_receive(:hi_mom!) @class.clakable end it 'should run an advice method' do @class.class_eval %{def self.before_method; hi_mom!; end;} @class.before_class_method(:clakable, :before_method) @class.should_receive(:hi_mom!) @class.clakable end it "should not pass any of the hookable method's arguments if the hook block does not accept arguments" do @class.class_eval do def self.method_with_args(one, two, three); end; before_class_method(:method_with_args) { hi_mom! } end @class.should_receive(:hi_mom!) @class.method_with_args(1, 2, 3) end it "should not pass any of the hookable method's arguments if the hook method does not accept arguments" do @class.class_eval do def self.method_with_args(one, two, three); end; def self.before_method_with_args; hi_mom!; end; before_class_method(:method_with_args, :before_method_with_args) end @class.should_receive(:hi_mom!) @class.method_with_args(1, 2, 3) end it "should not pass any of the hookable method's arguments if the hook is declared after it is registered and does not accept arguments" do @class.class_eval do def self.method_with_args(one, two, three); end; before_class_method(:method_with_args, :before_method_with_args) def self.before_method_with_args; hi_mom!; end; end @class.should_receive(:hi_mom!) @class.method_with_args(1, 2, 3) end it "should not pass any of the hookable method's arguments if the hook has been re-defined not to accept arguments" do @class.class_eval do def self.method_with_args(one, two, three); end; def self.before_method_with_args(one, two, three); hi_mom!; end; before_class_method(:method_with_args, :before_method_with_args) orig_verbose, $VERBOSE = $VERBOSE, false def self.before_method_with_args; hi_dad!; end; $VERBOSE = orig_verbose end @class.should_not_receive(:hi_mom1) @class.should_receive(:hi_dad!) @class.method_with_args(1, 2, 3) end it 'should pass the hookable method arguments to the hook method if the hook method takes arguments' do @class.class_eval do def self.hook_this(word, lol); end; register_class_hooks(:hook_this) def self.before_hook_this(word, lol); hi_mom!(word, lol); end; before_class_method(:hook_this, :before_hook_this) end @class.should_receive(:hi_mom!).with("omg", "hi2u") @class.hook_this("omg", "hi2u") end it "should pass the hookable method arguments to the hook block if the hook block takes arguments" do @class.class_eval do def self.method_with_args(word, lol); end; before_class_method(:method_with_args) { |one, two| hi_mom!(one, two) } end @class.should_receive(:hi_mom!).with('omg', 'hi2u') @class.method_with_args('omg', 'hi2u') end it 'should pass the hookable method arguments to the hook method if the hook method was re-defined to accept arguments' do @class.class_eval do def self.method_with_args(word, lol); end; def self.before_method_with_args; hi_mom!; end; before_class_method(:method_with_args, :before_method_with_args) orig_verbose, $VERBOSE = $VERBOSE, false def self.before_method_with_args(word, lol); hi_dad!(word, lol); end; $VERBOSE = orig_verbose end @class.should_not_receive(:hi_mom!) @class.should_receive(:hi_dad!).with("omg", "hi2u") @class.method_with_args("omg", "hi2u") end it 'should work with glob arguments (or whatever you call em)' do @class.class_eval do def self.hook_this(*args); end; def self.before_hook_this(*args); hi_mom!(*args); end; before_class_method(:hook_this, :before_hook_this) end @class.should_receive(:hi_mom!).with("omg", "hi2u", "lolercoaster") @class.hook_this("omg", "hi2u", "lolercoaster") end it 'should allow the use of before and after together' do @class.class_eval %{def self.before_hook; first!; end;} @class.before_class_method(:clakable, :before_hook) @class.after_class_method(:clakable) { last! } @class.should_receive(:first!).once.ordered @class.should_receive(:last!).once.ordered @class.clakable end it 'should be able to use private methods as hooks' do @class.class_eval do class << self private def nike; doit!; end; end before_class_method(:clakable, :nike) end @class.should_receive(:doit!) @class.clakable end end describe "for instance methods" do it 'should run an advice block' do @class.before(:hookable) { hi_mom! } inst = @class.new inst.should_receive(:hi_mom!) inst.hookable end it 'should run an advice method' do @class.send(:define_method, :before_method) { hi_mom! } @class.before(:hookable, :before_method) inst = @class.new inst.should_receive(:hi_mom!) inst.hookable end it "should not pass any of the hookable method's arguments if the hook block does not accept arguments" do @class.class_eval do def method_with_args(one, two, three); end; before(:method_with_args) { hi_mom! } end inst = @class.new inst.should_receive(:hi_mom!) inst.method_with_args(1, 2, 3) end it "should not pass any of the hookable method's arguments if the hook method does not accept arguments" do @class.class_eval do def method_with_args(one, two, three); end; def before_method_with_args; hi_mom!; end; before(:method_with_args, :before_method_with_args) end inst = @class.new inst.should_receive(:hi_mom!) inst.method_with_args(1, 2, 3) end it "should not pass any of the hookable method's arguments if the hook is declared after it is registered and does not accept arguments" do @class.class_eval do def method_with_args(one, two, three); end; before(:method_with_args, :before_method_with_args) def before_method_with_args; hi_mom!; end; end inst = @class.new inst.should_receive(:hi_mom!) inst.method_with_args(1, 2, 3) end it "should not pass any of the hookable method's arguments if the hook has been re-defined not to accept arguments" do @class.class_eval do def method_with_args(one, two, three); end; def before_method_with_args(one, two, three); hi_mom!; end; before(:method_with_args, :before_method_with_args) orig_verbose, $VERBOSE = $VERBOSE, false def before_method_with_args; hi_dad!; end; $VERBOSE = orig_verbose end inst = @class.new inst.should_not_receive(:hi_mom1) inst.should_receive(:hi_dad!) inst.method_with_args(1, 2, 3) end it 'should pass the hookable method arguments to the hook method if the hook method takes arguments' do @class.class_eval do def method_with_args(word, lol); end; def before_method_with_args(one, two); hi_mom!(one, two); end; before(:method_with_args, :before_method_with_args) end inst = @class.new inst.should_receive(:hi_mom!).with("omg", "hi2u") inst.method_with_args("omg", "hi2u") end it "should pass the hookable method arguments to the hook block if the hook block takes arguments" do @class.class_eval do def method_with_args(word, lol); end; before(:method_with_args) { |one, two| hi_mom!(one, two) } end inst = @class.new inst.should_receive(:hi_mom!).with('omg', 'hi2u') inst.method_with_args('omg', 'hi2u') end it 'should pass the hookable method arguments to the hook method if the hook method was re-defined to accept arguments' do @class.class_eval do def method_with_args(word, lol); end; def before_method_with_args; hi_mom!; end; before(:method_with_args, :before_method_with_args) orig_verbose, $VERBOSE = $VERBOSE, false def before_method_with_args(word, lol); hi_dad!(word, lol); end; $VERBOSE = orig_verbose end inst = @class.new inst.should_not_receive(:hi_mom!) inst.should_receive(:hi_dad!).with("omg", "hi2u") inst.method_with_args("omg", "hi2u") end it "should not pass the method return value to the after hook if the method does not take arguments" do @class.class_eval do def method_with_ret_val; 'hello'; end; def after_method_with_ret_val; hi_mom!; end; after(:method_with_ret_val, :after_method_with_ret_val) end inst = @class.new inst.should_receive(:hi_mom!) inst.method_with_ret_val end it 'should work with glob arguments (or whatever you call em)' do @class.class_eval do def hook_this(*args); end; def before_hook_this(*args); hi_mom!(*args) end; before(:hook_this, :before_hook_this) end inst = @class.new inst.should_receive(:hi_mom!).with("omg", "hi2u", "lolercoaster") inst.hook_this("omg", "hi2u", "lolercoaster") end it 'should allow the use of before and after together' do @class.class_eval %{def after_hook; last!; end;} @class.before(:hookable) { first! } @class.after(:hookable, :after_hook) inst = @class.new inst.should_receive(:first!).once.ordered inst.should_receive(:last!).once.ordered inst.hookable end it 'should be able to use private methods as hooks' do @class.class_eval %{private; def nike; doit!; end;} @class.before(:hookable, :nike) inst = @class.new inst.should_receive(:doit!) inst.hookable end end end describe "hook invocation with class inheritance" do describe "for class methods" do it 'should run an advice block when the class is inherited' do @class.before_class_method(:clakable) { hi_mom! } @child = Class.new(@class) @child.should_receive(:hi_mom!) @child.clakable end it 'should run an advice block on child class when hook is registered in parent after inheritance' do @child = Class.new(@class) @class.before_class_method(:clakable) { hi_mom! } @child.should_receive(:hi_mom!) @child.clakable end it 'should be able to declare advice methods in child classes' do @class.class_eval %{def self.before_method; hi_dad!; end;} @class.before_class_method(:clakable, :before_method) @child = Class.new(@class) do def self.child; hi_mom!; end; before_class_method(:clakable, :child) end @child.should_receive(:hi_dad!).once.ordered @child.should_receive(:hi_mom!).once.ordered @child.clakable end it "should not execute hooks added in the child classes when in the parent class" do @child = Class.new(@class) { def self.child; hi_mom!; end; } @child.before_class_method(:clakable, :child) @class.should_not_receive(:hi_mom!) @class.clakable end it 'should not call the hook stack if the hookable method is overwritten and does not call super' do @class.before_class_method(:clakable) { hi_mom! } @child = Class.new(@class) do def self.clakable; end; end @child.should_not_receive(:hi_mom!) @child.clakable end it 'should not call hooks defined in the child class for a hookable method in a parent if the child overwrites the hookable method without calling super' do @child = Class.new(@class) do before_class_method(:clakable) { hi_mom! } def self.clakable; end; end @child.should_not_receive(:hi_mom!) @child.clakable end it 'should not call hooks defined in child class even if hook method exists in parent' do @class.class_eval %{def self.hello_world; hello_world!; end;} @child = Class.new(@class) do before_class_method(:clakable, :hello_world) end @class.should_not_receive(:hello_world!) @class.clakable end end describe "for instance methods" do it 'should run an advice block when the class is inherited' do @inherited_class = Class.new(@class) @class.before(:hookable) { hi_dad! } inst = @inherited_class.new inst.should_receive(:hi_dad!) inst.hookable end it 'should run an advice block on child class when hook is registered in parent after inheritance' do @child = Class.new(@class) @class.before(:hookable) { hi_mom! } inst = @child.new inst.should_receive(:hi_mom!) inst.hookable end it 'should be able to declare advice methods in child classes' do @class.send(:define_method, :before_method) { hi_dad! } @class.before(:hookable, :before_method) @child = Class.new(@class) do def child; hi_mom!; end; before :hookable, :child end inst = @child.new inst.should_receive(:hi_dad!).once.ordered inst.should_receive(:hi_mom!).once.ordered inst.hookable end it "should not execute hooks added in the child classes when in parent class" do @child = Class.new(@class) @child.send(:define_method, :child) { hi_mom! } @child.before(:hookable, :child) inst = @class.new inst.should_not_receive(:hi_mom!) inst.hookable end it 'should not call the hook stack if the hookable method is overwritten and does not call super' do @class.before(:hookable) { hi_mom! } @child = Class.new(@class) do def hookable; end; end inst = @child.new inst.should_not_receive(:hi_mom!) inst.hookable end it 'should not call hooks defined in the child class for a hookable method in a parent if the child overwrites the hookable method without calling super' do @child = Class.new(@class) do before(:hookable) { hi_mom! } def hookable; end; end inst = @child.new inst.should_not_receive(:hi_mom!) inst.hookable end it 'should not call hooks defined in child class even if hook method exists in parent' do @class.send(:define_method, :hello_world) { hello_world! } @child = Class.new(@class) do before(:hookable, :hello_world) end inst = @class.new inst.should_not_receive(:hello_world!) inst.hookable end it 'should call different hooks in different children when they are defined there' do @class.send(:define_method, :hello_world) {} @child1 = Class.new(@class) do before(:hello_world){ hi_dad! } end @child2 = Class.new(@class) do before(:hello_world){ hi_mom! } end @child3 = Class.new(@child1) do before(:hello_world){ hi_grandma! } end inst1 = @child1.new inst2 = @child2.new inst3 = @child3.new inst1.should_receive(:hi_dad!).once inst2.should_receive(:hi_mom!).once inst3.should_receive(:hi_dad!).once inst3.should_receive(:hi_grandma!).once inst1.hello_world inst2.hello_world inst3.hello_world end end end describe "hook invocation with module inclusions / extensions" do describe "for class methods" do it "should not overwrite methods included by extensions after the hook is declared" do @module.class_eval do @another_module = Module.new do def greet; greetings_from_another_module; super; end; end def self.extended(base) base.before_class_method(:clakable, :greet) base.extend(@another_module) end end @class.extend(@module) @class.should_receive(:greetings_from_another_module).once.ordered @class.should_receive(:greetings_from_module).once.ordered @class.clakable end end describe "for instance methods" do it 'should not overwrite methods included by modules after the hook is declared' do @module.class_eval do @another_module = Module.new do def greet; greetings_from_another_module; super; end; end def self.included(base) base.before(:hookable, :greet) base.send(:include, @another_module) end end @class.send(:include, @module) inst = @class.new inst.should_receive(:greetings_from_another_module).once.ordered inst.should_receive(:greetings_from_module).once.ordered inst.hookable end end end describe "hook invocation with unrelated classes" do describe "for class methods" do it "should not execute hooks registered in an unrelated class" do @class.before_class_method(:clakable) { hi_mom! } @other.should_not_receive(:hi_mom!) @other.clakable end end describe "for instance methods" do it "should not execute hooks registered in an unrelated class" do @class.before(:hookable) { hi_mom! } inst = @other.new inst.should_not_receive(:hi_mom!) inst.hookable end end end describe "using before hook" do describe "for class methods" do it 'should run the advice before the advised method' do @class.class_eval %{def self.hook_me; second!; end;} @class.register_class_hooks(:hook_me) @class.before_class_method(:hook_me, :first!) @class.should_receive(:first!).ordered @class.should_receive(:second!).ordered @class.hook_me end it 'should execute all advices once in order' do @class.before_class_method(:clakable, :hook_1) @class.before_class_method(:clakable, :hook_2) @class.before_class_method(:clakable, :hook_3) @class.should_receive(:hook_1).once.ordered @class.should_receive(:hook_2).once.ordered @class.should_receive(:hook_3).once.ordered @class.clakable end end describe "for instance methods" do it 'should run the advice before the advised method' do @class.class_eval %{ def hook_me; second!; end; } @class.register_instance_hooks(:hook_me) @class.before(:hook_me, :first!) inst = @class.new inst.should_receive(:first!).ordered inst.should_receive(:second!).ordered inst.hook_me end it 'should execute all advices once in order' do @class.before(:hookable, :hook_1) @class.before(:hookable, :hook_2) @class.before(:hookable, :hook_3) inst = @class.new inst.should_receive(:hook_1).once.ordered inst.should_receive(:hook_2).once.ordered inst.should_receive(:hook_3).once.ordered inst.hookable end end end describe 'using after hook' do describe "for class methods" do it 'should run the advice after the advised method' do @class.class_eval %{def self.hook_me; first!; end;} @class.register_class_hooks(:hook_me) @class.after_class_method(:hook_me, :second!) @class.should_receive(:first!).ordered @class.should_receive(:second!).ordered @class.hook_me end it 'should execute all advices once in order' do @class.after_class_method(:clakable, :hook_1) @class.after_class_method(:clakable, :hook_2) @class.after_class_method(:clakable, :hook_3) @class.should_receive(:hook_1).once.ordered @class.should_receive(:hook_2).once.ordered @class.should_receive(:hook_3).once.ordered @class.clakable end it "the advised method should still return its normal value" do @class.class_eval %{def self.hello; "hello world"; end;} @class.register_class_hooks(:hello) @class.after_class_method(:hello) { "BAM" } @class.hello.should == "hello world" end it "should pass the return value to a hook method" do @class.class_eval do def self.with_return_val; 'hello'; end; def self.after_with_return_val(retval); retval.should == 'hello'; end; after_class_method(:with_return_val, :after_with_return_val) end @class.with_return_val end it "should pass the return value to a hook block" do @class.class_eval do def self.with_return_val; 'hello'; end; after_class_method(:with_return_val) { |ret| ret.should == 'hello' } end @class.with_return_val end it "should pass the return value and method arguments to a hook block" do @class.class_eval do def self.with_args_and_return_val(world); 'hello'; end; after_class_method(:with_args_and_return_val) do |hello, world| hello.should == "hello" world.should == "world" end end @class.with_args_and_return_val('world') end end describe "for instance methods" do it 'should run the advice after the advised method' do @class.class_eval %{def hook_me; first!; end;} @class.register_instance_hooks(:hook_me) @class.after(:hook_me, :second!) inst = @class.new inst.should_receive(:first!).ordered inst.should_receive(:second!).ordered inst.hook_me end it 'should execute all advices once in order' do @class.after(:hookable, :hook_1) @class.after(:hookable, :hook_2) @class.after(:hookable, :hook_3) inst = @class.new inst.should_receive(:hook_1).once.ordered inst.should_receive(:hook_2).once.ordered inst.should_receive(:hook_3).once.ordered inst.hookable end it "the advised method should still return its normal value" do @class.class_eval %{def hello; "hello world"; end;} @class.register_instance_hooks(:hello) @class.after(:hello) { "BAM" } @class.new.hello.should == "hello world" end it "should return nil if an after hook throws :halt without a return value" do @class.class_eval %{def with_value; "hello"; end;} @class.register_instance_hooks(:with_value) @class.after(:with_value) { throw :halt } @class.new.with_value.should be_nil end it "should pass the return value to a hook method" do @class.class_eval do def with_return_val; 'hello'; end; def after_with_return_val(retval); retval.should == 'hello'; end; after(:with_return_val, :after_with_return_val) end @class.new.with_return_val end it "should pass the return value to a hook block" do @class.class_eval do def with_return_val; 'hello'; end; after(:with_return_val) { |ret| ret.should == 'hello' } end @class.new.with_return_val end it "should pass the return value and method arguments to a hook block" do @class.class_eval do def with_args_and_return_val(world); 'hello'; end; after(:with_args_and_return_val) do |hello, world| hello.should == "hello" world.should == "world" end end @class.new.with_args_and_return_val('world') end end end describe 'aborting' do describe "for class methods" do it "should catch :halt from a before hook and abort the advised method" do @class.class_eval %{def self.no_love; love_me!; end;} @class.register_class_hooks :no_love @class.before_class_method(:no_love) { maybe! } @class.before_class_method(:no_love) { throw :halt } @class.before_class_method(:no_love) { what_about_me? } @class.should_receive(:maybe!) @class.should_not_receive(:what_about_me?) @class.should_not_receive(:love_me!) lambda { @class.no_love }.should_not throw_symbol(:halt) end it "should not run after hooks if a before hook throws :halt" do @class.before_class_method(:clakable) { throw :halt } @class.after_class_method(:clakable) { bam! } @class.should_not_receive(:bam!) lambda { @class.clakable }.should_not throw_symbol(:halt) end it "should return nil from the hookable method if a before hook throws :halt" do @class.class_eval %{def self.with_value; "hello"; end;} @class.register_class_hooks(:with_value) @class.before_class_method(:with_value) { throw :halt } @class.with_value.should be_nil end it "should catch :halt from an after hook and cease the advice" do @class.after_class_method(:clakable) { throw :halt } @class.after_class_method(:clakable) { never_see_me! } @class.should_not_receive(:never_see_me!) lambda { @class.clakable }.should_not throw_symbol(:halt) end it "should return nil if an after hook throws :halt without a return value" do @class.class_eval %{def self.with_value; "hello"; end;} @class.register_class_hooks(:with_value) @class.after_class_method(:with_value) { throw :halt } @class.with_value.should be_nil end end describe "for instance methods" do it "should catch :halt from a before hook and abort the advised method" do @class.class_eval %{def no_love; love_me!; end;} @class.register_instance_hooks :no_love @class.before(:no_love) { maybe! } @class.before(:no_love) { throw :halt } @class.before(:no_love) { what_about_me? } inst = @class.new inst.should_receive(:maybe!) inst.should_not_receive(:what_about_me?) inst.should_not_receive(:love_me!) lambda { inst.no_love }.should_not throw_symbol(:halt) end it "should not run after hooks if a before hook throws :halt" do @class.before(:hookable) { throw :halt } @class.after(:hookable) { bam! } inst = @class.new inst.should_not_receive(:bam!) lambda { inst.hookable }.should_not throw_symbol(:halt) end it "should return nil from the hookable method if a before hook throws :halt" do @class.class_eval %{def with_value; "hello"; end;} @class.register_instance_hooks(:with_value) @class.before(:with_value) { throw :halt } @class.new.with_value.should be_nil end it "should catch :halt from an after hook and cease the advice" do @class.after(:hookable) { throw :halt } @class.after(:hookable) { never_see_me! } inst = @class.new inst.should_not_receive(:never_see_me!) lambda { inst.hookable }.should_not throw_symbol(:halt) end end end describe 'aborting with return values' do describe "for class methods" do it "should be able to abort from a before hook with a return value" do @class.before_class_method(:clakable) { throw :halt, 'omg' } @class.clakable.should == 'omg' end it "should be able to abort from an after hook with a return value" do @class.after_class_method(:clakable) { throw :halt, 'omg' } @class.clakable.should == 'omg' end end describe "for instance methods" do it "should be able to abort from a before hook with a return value" do @class.before(:hookable) { throw :halt, 'omg' } inst = @class.new inst.hookable.should == 'omg' end it "should be able to abort from an after hook with a return value" do @class.after(:hookable) { throw :halt, 'omg' } inst = @class.new inst.hookable.should == 'omg' end end end describe "helper methods" do it 'should generate the correct argument signature' do @class.class_eval do def some_method(a, b, c) [a, b, c] end def yet_another(a, *heh) [a, *heh] end end @class.args_for(@class.instance_method(:hookable)).should == "&block" @class.args_for(@class.instance_method(:some_method)).should == "_1, _2, _3, &block" @class.args_for(@class.instance_method(:yet_another)).should == "_1, *args, &block" end end end extlib-0.9.16/spec/inflection/000077500000000000000000000000001207310540200161545ustar00rootroot00000000000000extlib-0.9.16/spec/inflection/plural_spec.rb000066400000000000000000000272401207310540200210170ustar00rootroot00000000000000require 'spec_helper' require 'extlib/inflection' describe Extlib::Inflection, "#plural" do it "pluralizes equipment => equipment" do "equipment".plural.should == "equipment" end it "pluralizes information => information" do "information".plural.should == "information" end it "pluralizes money => money" do "money".plural.should == "money" end it "pluralizes species => species" do "species".plural.should == "species" end it "pluralizes series => series" do "series".plural.should == "series" end it "pluralizes fish => fish" do "fish".plural.should == "fish" end it "pluralizes sheep => sheep" do "sheep".plural.should == "sheep" end it "pluralizes news => news" do "news".plural.should == "news" end it "pluralizes rain => rain" do "rain".plural.should == "rain" end it "pluralizes milk => milk" do "milk".plural.should == "milk" end it "pluralizes moose => moose" do "moose".plural.should == "moose" end it "pluralizes hovercraft => hovercraft" do "hovercraft".plural.should == "hovercraft" end it "pluralizes cactus => cacti" do "cactus".plural.should == "cacti" end it "pluralizes thesaurus => thesauri" do "thesaurus".plural.should == "thesauri" end it "pluralizes matrix => matrices" do "matrix".plural.should == "matrices" end it "pluralizes Swiss => Swiss" do "Swiss".plural.should == "Swiss" end it "pluralizes life => lives" do "life".plural.should == "lives" end it "pluralizes wife => wives" do "wife".plural.should == "wives" end it "pluralizes goose => geese" do "goose".plural.should == "geese" end it "pluralizes criterion => criteria" do "criterion".plural.should == "criteria" end it "pluralizes alias => aliases" do "alias".plural.should == "aliases" end it "pluralizes status => statuses" do "status".plural.should == "statuses" end it "pluralizes axis => axes" do "axis".plural.should == "axes" end it "pluralizes crisis => crises" do "crisis".plural.should == "crises" end it "pluralizes testis => testes" do "testis".plural.should == "testes" end it "pluralizes child => children" do "child".plural.should == "children" end it "pluralizes person => people" do "person".plural.should == "people" end it "pluralizes potato => potatoes" do "potato".plural.should == "potatoes" end it "pluralizes tomato => tomatoes" do "tomato".plural.should == "tomatoes" end it "pluralizes buffalo => buffaloes" do "buffalo".plural.should == "buffaloes" end it "pluralizes torpedo => torpedoes" do "torpedo".plural.should == "torpedoes" end it "pluralizes quiz => quizzes" do "quiz".plural.should == "quizzes" end # used to be a bug exposed by this specs suite, # this MUST pass or we've got regression it "pluralizes vertex => vertices" do "vertex".plural.should == "vertices" end it "pluralizes index => indices" do "index".plural.should == "indices" end it "pluralizes ox => oxen" do "ox".plural.should == "oxen" end it "pluralizes mouse => mice" do "mouse".plural.should == "mice" end it "pluralizes louse => lice" do "louse".plural.should == "lice" end it "pluralizes thesis => theses" do "thesis".plural.should == "theses" end it "pluralizes thief => thieves" do "thief".plural.should == "thieves" end it "pluralizes analysis => analyses" do "analysis".plural.should == "analyses" end it "pluralizes octopus => octopi" do "octopus".plural.should == "octopi" end it "pluralizes grass => grass" do "grass".plural.should == "grass" end it "pluralizes phenomenon => phenomena" do "phenomenon".plural.should == "phenomena" end it "pluralizes drive => drives" do "drive".plural.should == "drives" end # ==== bugs, typos and reported issues # ==== rules and most common cases it "pluralizes forum => forums" do "forum".plural.should == "forums" end it "pluralizes hive => hives" do "hive".plural.should == "hives" end it "pluralizes athlete => athletes" do "athlete".plural.should == "athletes" end it "pluralizes dwarf => dwarves" do "dwarf".plural.should == "dwarves" end it "pluralizes hero => heroes" do "hero".plural.should == "heroes" end it "pluralizes zero => zeroes" do "zero".plural.should == "zeroes" end it "pluralizes man => men" do "man".plural.should == "men" end it "pluralizes woman => women" do "woman".plural.should == "women" end it "pluralizes sportsman => sportsmen" do "sportsman".plural.should == "sportsmen" end it "pluralizes branch => branches" do "branch".plural.should == "branches" end it "pluralizes crunch => crunches" do "crunch".plural.should == "crunches" end it "pluralizes trash => trashes" do "trash".plural.should == "trashes" end it "pluralizes mash => mashes" do "mash".plural.should == "mashes" end it "pluralizes cross => crosses" do "cross".plural.should == "crosses" end it "pluralizes erratum => errata" do "erratum".plural.should == "errata" end # FIXME: add -ia => -ium cases # FIXME: add -ra => -rum cases it "pluralizes ray => rays" do "ray".plural.should == "rays" end it "pluralizes spray => sprays" do "spray".plural.should == "sprays" end # Merriam-Webster dictionary says # preys is correct, too. it "pluralizes prey => preys" do "prey".plural.should == "preys" end it "pluralizes toy => toys" do "toy".plural.should == "toys" end it "pluralizes joy => joys" do "joy".plural.should == "joys" end it "pluralizes buy => buys" do "buy".plural.should == "buys" end it "pluralizes guy => guys" do "guy".plural.should == "guys" end it "pluralizes cry => cries" do "cry".plural.should == "cries" end it "pluralizes fly => flies" do "fly".plural.should == "flies" end it "pluralizes fox => foxes" do "fox".plural.should == "foxes" end it "pluralizes elf => elves" do "elf".plural.should == "elves" end it "pluralizes shelf => shelves" do "shelf".plural.should == "shelves" end it "pluralizes plus => plusses" do "plus".plural.should == "plusses" end it "pluralizes cat => cats" do "cat".plural.should == "cats" end it "pluralizes rat => rats" do "rat".plural.should == "rats" end it "pluralizes rose => roses" do "rose".plural.should == "roses" end it "pluralizes project => projects" do "project".plural.should == "projects" end it "pluralizes post => posts" do "post".plural.should == "posts" end it "pluralizes article => articles" do "article".plural.should == "articles" end it "pluralizes location => locations" do "location".plural.should == "locations" end it "pluralizes friend => friends" do "friend".plural.should == "friends" end it "pluralizes link => links" do "link".plural.should == "links" end it "pluralizes url => urls" do "url".plural.should == "urls" end it "pluralizes account => accounts" do "account".plural.should == "accounts" end it "pluralizes server => servers" do "server".plural.should == "servers" end it "pluralizes fruit => fruits" do "fruit".plural.should == "fruits" end it "pluralizes map => maps" do "map".plural.should == "maps" end it "pluralizes income => incomes" do "income".plural.should == "incomes" end it "pluralizes ping => pings" do "ping".plural.should == "pings" end it "pluralizes event => events" do "event".plural.should == "events" end it "pluralizes proof => proofs" do "proof".plural.should == "proofs" end it "pluralizes typo => typos" do "typo".plural.should == "typos" end it "pluralizes attachment => attachments" do "attachment".plural.should == "attachments" end it "pluralizes download => downloads" do "download".plural.should == "downloads" end it "pluralizes asset => assets" do "asset".plural.should == "assets" end it "pluralizes job => jobs" do "job".plural.should == "jobs" end it "pluralizes city => cities" do "city".plural.should == "cities" end it "pluralizes package => packages" do "package".plural.should == "packages" end it "pluralizes commit => commits" do "commit".plural.should == "commits" end it "pluralizes version => versions" do "version".plural.should == "versions" end it "pluralizes document => documents" do "document".plural.should == "documents" end it "pluralizes edition => editions" do "edition".plural.should == "editions" end it "pluralizes movie => movies" do "movie".plural.should == "movies" end it "pluralizes song => songs" do "song".plural.should == "songs" end it "pluralizes invoice => invoices" do "invoice".plural.should == "invoices" end it "pluralizes product => products" do "product".plural.should == "products" end it "pluralizes book => books" do "book".plural.should == "books" end it "pluralizes ticket => tickets" do "ticket".plural.should == "tickets" end it "pluralizes game => games" do "game".plural.should == "games" end it "pluralizes tournament => tournaments" do "tournament".plural.should == "tournaments" end it "pluralizes prize => prizes" do "prize".plural.should == "prizes" end it "pluralizes price => prices" do "price".plural.should == "prices" end it "pluralizes installation => installations" do "installation".plural.should == "installations" end it "pluralizes date => dates" do "date".plural.should == "dates" end it "pluralizes schedule => schedules" do "schedule".plural.should == "schedules" end it "pluralizes arena => arenas" do "arena".plural.should == "arenas" end it "pluralizes spam => spams" do "spam".plural.should == "spams" end it "pluralizes bus => buses" do "bus".plural.should == "buses" end it "pluralizes rice => rice" do "rice".plural.should == "rice" end # Some specs from Rails SingularToPlural = { "search" => "searches", "switch" => "switches", "fix" => "fixes", "box" => "boxes", "process" => "processes", "address" => "addresses", "case" => "cases", "stack" => "stacks", "wish" => "wishes", "category" => "categories", "query" => "queries", "ability" => "abilities", "agency" => "agencies", "archive" => "archives", "safe" => "saves", "half" => "halves", "move" => "moves", "salesperson" => "salespeople", "spokesman" => "spokesmen", "basis" => "bases", "diagnosis" => "diagnoses", "diagnosis_a" => "diagnosis_as", "datum" => "data", "medium" => "media", "node_child" => "node_children", "experience" => "experiences", "day" => "days", "comment" => "comments", "foobar" => "foobars", "newsletter" => "newsletters", "old_news" => "old_news", "perspective" => "perspectives", "photo" => "photos", "status_code" => "status_codes", "house" => "houses", "virus" => "viruses", "portfolio" => "portfolios", "matrix_fu" => "matrix_fus", "axis" => "axes", "shoe" => "shoes", "horse" => "horses", "edge" => "edges", "cow" => "cows" # 'kine' is archaic and nobody uses it } SingularToPlural.each do |single_word, plural_word| it "pluralizes #{single_word} => #{plural_word}" do single_word.plural.should == plural_word end end end extlib-0.9.16/spec/inflection/singular_spec.rb000066400000000000000000000254041207310540200213440ustar00rootroot00000000000000require 'spec_helper' require 'extlib/inflection' describe Extlib::Inflection, "#singular" do # ==== exceptional cases it "singularizes equipment => equipment" do "equipment".singular.should == "equipment" end it "singularizes postgres => postgres" do "postgres".singular.should == "postgres" end it "singularizes mysql => mysql" do "mysql".singular.should == "mysql" end it "singularizes information => information" do "information".singular.should == "information" end it "singularizes money => money" do "money".singular.should == "money" end it "singularizes species => species" do "species".singular.should == "species" end it "singularizes series => series" do "series".singular.should == "series" end it "singularizes fish => fish" do "fish".singular.should == "fish" end it "singularizes sheep => sheep" do "sheep".singular.should == "sheep" end it "singularizes news => news" do "news".singular.should == "news" end it "singularizes rain => rain" do "rain".singular.should == "rain" end it "singularizes milk => milk" do "milk".singular.should == "milk" end it "singularizes moose => moose" do "moose".singular.should == "moose" end it "singularizes hovercraft => hovercraft" do "hovercraft".singular.should == "hovercraft" end it "singularizes cacti => cactus" do "cacti".singular.should == "cactus" end it "singularizes cactuses => cactus" do "cactuses".singular.should == "cactus" end it "singularizes thesauri => thesaurus" do "thesauri".singular.should == "thesaurus" end it "singularizes matrices => matrix" do "matrices".singular.should == "matrix" end it "singularizes Swiss => Swiss" do "Swiss".singular.should == "Swiss" end it "singularizes lives => life" do "lives".singular.should == "life" end it "singularizes wives => wife" do "wives".singular.should == "wife" end it "singularizes geese => goose" do "geese".singular.should == "goose" end it "singularizes criteria => criterion" do "criteria".singular.should == "criterion" end it "singularizes aliases => alias" do "aliases".singular.should == "alias" end it "singularizes statuses => status" do "statuses".singular.should == "status" end it "singularizes axes => axis" do "axes".singular.should == "axis" end it "singularizes crises => crisis" do "crises".singular.should == "crisis" end it "singularizes testes => testis" do "testes".singular.should == "testis" end it "singularizes children => child" do "children".singular.should == "child" end it "singularizes people => person" do "people".singular.should == "person" end it "singularizes potatoes => potato" do "potatoes".singular.should == "potato" end it "singularizes tomatoes => tomato" do "tomatoes".singular.should == "tomato" end it "singularizes buffaloes => buffalo" do "buffaloes".singular.should == "buffalo" end it "singularizes torpedoes => torpedo" do "torpedoes".singular.should == "torpedo" end it "singularizes quizzes => quiz" do "quizzes".singular.should == "quiz" end # used to be a bug exposed by this specs suite, # this MUST pass or we've got regression it "singularizes vertices => vertex" do "vertices".singular.should == "vertex" end it "singularizes indices => index" do "indices".singular.should == "index" end it "singularizes indexes => index" do "indexes".singular.should == "index" end it "singularizes oxen => ox" do "oxen".singular.should == "ox" end it "singularizes mice => mouse" do "mice".singular.should == "mouse" end it "singularizes lice => louse" do "lice".singular.should == "louse" end it "singularizes theses => thesis" do "theses".singular.should == "thesis" end it "singularizes thieves => thief" do "thieves".singular.should == "thief" end it "singularizes analyses => analysis" do "analyses".singular.should == "analysis" end it "singularizes octopi => octopus" do "octopi".singular.should == "octopus" end it "singularizes grass => grass" do "grass".singular.should == "grass" end it "singularizes phenomena => phenomenon" do "phenomena".singular.should == "phenomenon" end it "singularizes drives => drive" do "drives".singular.should == "drive" end # ==== bugs, typos and reported issues # ==== rules it "singularizes forums => forum" do "forums".singular.should == "forum" end it "singularizes hives => hive" do "hives".singular.should == "hive" end it "singularizes athletes => athlete" do "athletes".singular.should == "athlete" end it "singularizes dwarves => dwarf" do "dwarves".singular.should == "dwarf" end it "singularizes heroes => hero" do "heroes".singular.should == "hero" end it "singularizes zeroes => zero" do "zeroes".singular.should == "zero" end it "singularizes men => man" do "men".singular.should == "man" end it "singularizes women => woman" do "women".singular.should == "woman" end it "singularizes sportsmen => sportsman" do "sportsmen".singular.should == "sportsman" end it "singularizes branches => branch" do "branches".singular.should == "branch" end it "singularizes crunches => crunch" do "crunches".singular.should == "crunch" end it "singularizes trashes => trash" do "trashes".singular.should == "trash" end it "singularizes mashes => mash" do "mashes".singular.should == "mash" end it "singularizes crosses => cross" do "crosses".singular.should == "cross" end it "singularizes errata => erratum" do "errata".singular.should == "erratum" end # FIXME: add -ia => -ium cases # FIXME: add -ra => -rum cases it "singularizes rays => ray" do "rays".singular.should == "ray" end it "singularizes sprays => spray" do "sprays".singular.should == "spray" end # Merriam-Webster dictionary says # preys is correct, too. it "singularizes preys => prey" do "preys".singular.should == "prey" end it "singularizes toys => toy" do "toys".singular.should == "toy" end it "singularizes joys => joy" do "joys".singular.should == "joy" end it "singularizes buys => buy" do "buys".singular.should == "buy" end it "singularizes guys => guy" do "guys".singular.should == "guy" end it "singularizes cries => cry" do "cries".singular.should == "cry" end it "singularizes flies => fly" do "flies".singular.should == "fly" end it "singularizes foxes => fox" do "foxes".singular.should == "fox" end it "singularizes elves => elf" do "elves".singular.should == "elf" end it "singularizes shelves => shelf" do "shelves".singular.should == "shelf" end it "singularizes pluses => plus" do "pluses".singular.should == "plus" end it "singularizes cats => cat" do "cats".singular.should == "cat" end it "singularizes rats => rat" do "rats".singular.should == "rat" end it "singularizes roses => rose" do "roses".singular.should == "rose" end it "singularizes projects => project" do "projects".singular.should == "project" end it "singularizes posts => post" do "posts".singular.should == "post" end it "singularizes articles => article" do "articles".singular.should == "article" end it "singularizes locations => location" do "locations".singular.should == "location" end it "singularizes friends => friend" do "friends".singular.should == "friend" end it "singularizes links => link" do "links".singular.should == "link" end it "singularizes urls => url" do "urls".singular.should == "url" end it "singularizes accounts => account" do "accounts".singular.should == "account" end it "singularizes servers => server" do "servers".singular.should == "server" end it "singularizes fruits => fruit" do "fruits".singular.should == "fruit" end it "singularizes maps => map" do "maps".singular.should == "map" end it "singularizes incomes => income" do "incomes".singular.should == "income" end it "singularizes pings => ping" do "pings".singular.should == "ping" end it "singularizes events => event" do "events".singular.should == "event" end it "singularizes proofs => proof" do "proofs".singular.should == "proof" end it "singularizes typos => typo" do "typos".singular.should == "typo" end it "singularizes attachments => attachment" do "attachments".singular.should == "attachment" end it "singularizes downloads => download" do "downloads".singular.should == "download" end it "singularizes assets => asset" do "assets".singular.should == "asset" end it "singularizes jobs => job" do "jobs".singular.should == "job" end it "singularizes cities => city" do "cities".singular.should == "city" end it "singularizes packages => package" do "packages".singular.should == "package" end it "singularizes commits => commit" do "commits".singular.should == "commit" end it "singularizes versions => version" do "versions".singular.should == "version" end it "singularizes documents => document" do "documents".singular.should == "document" end it "singularizes editions => edition" do "editions".singular.should == "edition" end it "singularizes movies => movie" do "movies".singular.should == "movie" end it "singularizes songs => song" do "songs".singular.should == "song" end it "singularizes invoices => invoice" do "invoices".singular.should == "invoice" end it "singularizes products => product" do "products".singular.should == "product" end it "singularizes books => book" do "books".singular.should == "book" end it "singularizes tickets => ticket" do "tickets".singular.should == "ticket" end it "singularizes games => game" do "games".singular.should == "game" end it "singularizes tournaments => tournament" do "tournaments".singular.should == "tournament" end it "singularizes prizes => prize" do "prizes".singular.should == "prize" end it "singularizes prices => price" do "prices".singular.should == "price" end it "singularizes installations => installation" do "installations".singular.should == "installation" end it "singularizes dates => date" do "dates".singular.should == "date" end it "singularizes schedules => schedule" do "schedules".singular.should == "schedule" end it "singularizes arenas => arena" do "arenas".singular.should == "arena" end it "singularizes spams => spam" do "spams".singular.should == "spam" end it "singularizes rice => rice" do "rice".singular.should == "rice" end end extlib-0.9.16/spec/inflection_extras_spec.rb000066400000000000000000000076171207310540200211140ustar00rootroot00000000000000require 'spec_helper' require 'extlib/inflection' describe Extlib::Inflection do describe "#classify" do it 'classifies data_mapper as DataMapper' do Extlib::Inflection.classify('data_mapper').should == 'DataMapper' end it "classifies enlarged_testes as EnlargedTestis" do Extlib::Inflection.classify('enlarged_testes').should == 'EnlargedTestis' end it "singularizes string first: classifies data_mappers as egg_and_hams as EggAndHam" do Extlib::Inflection.classify('egg_and_hams').should == 'EggAndHam' end end describe "#camelize" do it 'camelizes data_mapper as DataMapper' do Extlib::Inflection.camelize('data_mapper').should == 'DataMapper' end it "camelizes merb as Merb" do Extlib::Inflection.camelize('merb').should == 'Merb' end it "camelizes data_mapper/resource as DataMapper::Resource" do Extlib::Inflection.camelize('data_mapper/resource').should == 'DataMapper::Resource' end it "camelizes data_mapper/associations/one_to_many as DataMapper::Associations::OneToMany" do Extlib::Inflection.camelize('data_mapper/associations/one_to_many').should == 'DataMapper::Associations::OneToMany' end end describe "#underscore" do it 'underscores DataMapper as data_mapper' do Extlib::Inflection.underscore('DataMapper').should == 'data_mapper' end it 'underscores Merb as merb' do Extlib::Inflection.underscore('Merb').should == 'merb' end it 'underscores DataMapper::Resource as data_mapper/resource' do Extlib::Inflection.underscore('DataMapper::Resource').should == 'data_mapper/resource' end it 'underscores Merb::BootLoader::Rackup as merb/boot_loader/rackup' do Extlib::Inflection.underscore('Merb::BootLoader::Rackup').should == 'merb/boot_loader/rackup' end end describe "#humanize" do it 'replaces _ with space: humanizes employee_salary as Employee salary' do Extlib::Inflection.humanize('employee_salary').should == 'Employee salary' end it "strips _id endings: humanizes author_id as Author" do Extlib::Inflection.humanize('author_id').should == 'Author' end end describe "#demodulize" do it 'demodulizes module name: DataMapper::Inflector => Inflector' do Extlib::Inflection.demodulize('DataMapper::Inflector').should == 'Inflector' end it 'demodulizes module name: A::B::C::D::E => E' do Extlib::Inflection.demodulize('A::B::C::D::E').should == 'E' end end describe "#tableize" do it 'pluralizes last word in snake_case strings: fancy_category => fancy_categories' do Extlib::Inflection.tableize('fancy_category').should == 'fancy_categories' end it 'underscores CamelCase strings before pluralization: enlarged_testis => enlarged_testes' do Extlib::Inflection.tableize('enlarged_testis').should == 'enlarged_testes' end it 'underscores CamelCase strings before pluralization: FancyCategory => fancy_categories' do Extlib::Inflection.tableize('FancyCategory').should == 'fancy_categories' end it 'underscores CamelCase strings before pluralization: EnlargedTestis => enlarged_testes' do Extlib::Inflection.tableize('EnlargedTestis').should == 'enlarged_testes' end it 'replaces :: with underscores: Fancy::Category => fancy_categories' do Extlib::Inflection.tableize('Fancy::Category').should == 'fancy_categories' end it 'underscores CamelCase strings before pluralization: Enlarged::Testis => enlarged_testes' do Extlib::Inflection.tableize('Enlarged::Testis').should == 'enlarged_testes' end end describe "#foreign_key" do it 'adds _id to downcased string: Message => message_id' do Extlib::Inflection.foreign_key('Message').should == 'message_id' end it "demodulizes string first: Admin::Post => post_id" do Extlib::Inflection.foreign_key('Admin::Post').should == 'post_id' end end end extlib-0.9.16/spec/lazy_array_spec.rb000066400000000000000000001541611207310540200175460ustar00rootroot00000000000000require 'spec_helper' require 'extlib/lazy_array' # only needed for specs require 'extlib/class' module LazyArraySpec module GroupMethods def self.extended(base) base.class_inheritable_accessor :loaded, :subject_block, :action_block end def subject(&block) self.subject_block = block end def action(&block) self.action_block = block end def should_respond_to(method) unless loaded it { subject.should respond_to(method) } end end def should_return_expected_value(&block) it 'should return expected value' do action.should eql(instance_eval(&block)) end end def should_return_subject should_return_kind_of(LazyArray) it 'should return self' do action.should equal(subject) end end def should_return_kind_of(klass) it { action.should be_a_kind_of(klass) } end def should_return_copy it 'should not return self' do action.should_not equal(subject) end it 'should eql self' do action.should eql(subject) end end def should_return_true it 'should return true' do action.should be(true) end end def should_return_false it 'should return false' do action.should be(false) end end def should_return_nil it 'should return nil' do action.should be_nil end end def should_raise_error(klass, message = nil) it { lambda { action }.should raise_error(klass, message) } end def should_clear_subject it 'should clear self' do lambda { action }.should change(subject, :empty?).from(false).to(true) end end def should_yield_to_each_entry it 'should yield to each entry' do lambda { action }.should change(@accumulator, :entries).from([]).to(subject.entries) end end def should_not_change_subject it 'should not change self' do # XXX: the following does not work with Array#delete_if, even when nothing removed (ruby bug?) #subject.freeze #lambda { action }.should_not raise_error(RUBY_VERSION >= '1.9.0' ? RuntimeError : TypeError) lambda { action }.should_not change(subject, :entries) end end def should_be_a_kicker unless loaded it 'should be a kicker' do lambda { action }.should change(subject, :loaded?).from(false).to(true) end end end def should_not_be_a_kicker unless loaded it 'should not be a kicker' do subject.should_not be_loaded lambda { action }.should_not change(subject, :loaded?) end end end end module Methods def subject @subject ||= instance_eval(&self.class.subject_block) end def action instance_eval(&self.class.action_block) end end end [ false, true ].each do |loaded| describe LazyArray do extend LazyArraySpec::GroupMethods include LazyArraySpec::Methods self.loaded = loaded # message describing the object state state = "(#{'not ' unless loaded}loaded)" before do @nancy = 'nancy' @bessie = 'bessie' @steve = 'steve' @lazy_array = LazyArray.new @lazy_array.load_with { |la| la.push(@nancy, @bessie) } @other = LazyArray.new @other.load_with { |la| la.push(@steve) } @lazy_array.entries if loaded end subject { @lazy_array } it 'should be an Enumerable' do (Enumerable === subject).should be(true) end describe 'when frozen', state do before { subject.freeze } it 'should still be able to kick' do lambda { subject.entries }.should_not raise_error end it 'should not allow any modifications' do lambda { subject << @steve }.should raise_error(RUBY_VERSION >= '1.9.0' ? RuntimeError : TypeError) end end should_respond_to(:<<) describe '#<<' do action { subject << @steve } should_return_subject should_not_be_a_kicker it 'should append an entry' do (subject << @steve).should == [ @nancy, @bessie, @steve ] end end should_respond_to(:any?) describe '#any?', state do describe 'when not provided a block' do action { subject.any? } describe 'when the subject has entries that are not loaded' do should_return_true should_be_a_kicker should_not_change_subject end describe 'when the subject has entries that are prepended' do subject { LazyArray.new.unshift(@nancy) } should_return_true should_not_be_a_kicker should_not_change_subject end describe 'when the subject has entries that are appended' do subject { LazyArray.new << @nancy } should_return_true should_not_be_a_kicker should_not_change_subject end describe 'when the subject has no entries' do subject { LazyArray.new } should_return_false should_be_a_kicker should_not_change_subject end end describe 'when provided a block that always returns true' do action { subject.any? { true } } describe 'when the subject has entries that are not loaded' do should_return_true should_be_a_kicker should_not_change_subject end describe 'when the subject has entries that are prepended' do subject { LazyArray.new.unshift(@nancy) } should_return_true should_not_be_a_kicker should_not_change_subject end describe 'when the subject has entries that are appended' do subject { LazyArray.new << @nancy } should_return_true should_not_be_a_kicker should_not_change_subject end describe 'when the subject has no entries' do subject { LazyArray.new } should_return_false should_be_a_kicker should_not_change_subject end end end should_respond_to(:at) describe '#at', state do describe 'with positive index' do action { subject.at(0) } should_return_expected_value { @nancy } should_be_a_kicker should_not_change_subject end describe 'with positive index', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.at(0) } should_return_expected_value { @steve } should_not_be_a_kicker should_not_change_subject end describe 'with negative index' do action { subject.at(-1) } should_return_expected_value { @bessie } should_be_a_kicker should_not_change_subject end describe 'with negative index', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.at(-1) } should_return_expected_value { @steve } should_not_be_a_kicker should_not_change_subject end describe 'with an index not within the LazyArray' do action { subject.at(2) } should_return_nil should_be_a_kicker should_not_change_subject end end should_respond_to(:clear) describe '#clear', state do action { subject.clear } should_return_subject should_be_a_kicker # only marks as loadd, does not lazy load should_clear_subject end [ :collect!, :map! ].each do |method| it { @lazy_array.should respond_to(method) } describe "##{method}", state do before { @accumulator = [] } action { subject.send(method) { |e| @accumulator << e; @steve } } should_return_subject should_yield_to_each_entry should_be_a_kicker it 'should update with the block results' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @steve, @steve ]) end end end should_respond_to(:concat) describe '#concat', state do action { subject.concat(@other) } should_return_subject should_not_be_a_kicker it 'should concatenate other Enumerable' do subject.concat(@other).should == [ @nancy, @bessie, @steve ] end end should_respond_to(:delete) describe '#delete', state do describe 'with an object within the LazyArray', state do action { subject.delete(@nancy) } should_return_expected_value { @nancy } should_be_a_kicker it 'should remove the matching entry' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @bessie ]) end end describe 'with an object not within the LazyArray', 'without a default block' do action { subject.delete(@steve) } should_return_nil should_not_change_subject should_be_a_kicker end describe 'with an object not within the LazyArray', 'with a default block' do action { subject.delete(@steve) { @steve } } should_return_expected_value { @steve } should_not_change_subject should_be_a_kicker end end should_respond_to(:delete_at) describe '#delete_at', state do describe 'with a positive index' do action { subject.delete_at(0) } should_return_expected_value { @nancy } should_be_a_kicker it 'should remove the matching entry' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @bessie ]) end end describe 'with a positive index', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.delete_at(0) } should_return_expected_value { @steve } should_not_be_a_kicker it 'should remove the matching entry' do lambda { action }.should change(subject, :entries).from([ @steve, @nancy, @bessie ]).to([ @nancy, @bessie ]) end end describe 'with a negative index' do action { subject.delete_at(-1) } should_return_expected_value { @bessie } should_be_a_kicker it 'should remove the matching entry' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy ]) end end describe 'with a negative index', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.delete_at(-1) } should_return_expected_value { @steve } should_not_be_a_kicker it 'should remove the matching entry' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie, @steve ]).to([ @nancy, @bessie ]) end end describe 'with an index not within the LazyArray' do action { subject.delete_at(2) } should_return_nil should_not_change_subject should_be_a_kicker end end should_respond_to(:delete_if) describe '#delete_if', state do before { @accumulator = [] } describe 'with a block that matches an entry' do action { subject.delete_if { |e| @accumulator << e; true } } should_return_subject should_yield_to_each_entry should_not_be_a_kicker it 'should update with the block results' do lambda { action }.should change(subject, :empty?).from(false).to(true) end end describe 'with a block that does not match an entry' do action { subject.delete_if { |e| @accumulator << e; false } } should_return_subject should_yield_to_each_entry should_not_be_a_kicker should_not_change_subject end end should_respond_to(:dup) describe '#dup', state do action { subject.dup } should_return_kind_of(LazyArray) should_return_copy should_not_be_a_kicker if loaded it 'should be loaded if subject loaded' do action.should be_loaded end end end should_respond_to(:each) describe '#each', state do before { @accumulator = [] } action { subject.each { |e| @accumulator << e } } should_return_subject should_yield_to_each_entry should_be_a_kicker should_not_change_subject end should_respond_to(:each_index) describe '#each_index', state do before { @accumulator = [] } action { subject.each_index { |i| @accumulator << i } } should_return_subject should_be_a_kicker should_not_change_subject it 'should yield to each index' do lambda { action }.should change(@accumulator, :entries).from([]).to([ 0, 1 ]) end end should_respond_to(:each_with_index) describe '#each_with_index', state do before { @accumulator = [] } action { subject.each_with_index { |entry,index| @accumulator << [ entry, index ] } } should_return_subject should_be_a_kicker should_not_change_subject it 'should yield to each entry and index' do lambda { action }.should change(@accumulator, :entries).from([]).to([ [ @nancy, 0 ], [ @bessie, 1 ] ]) end end should_respond_to(:empty?) describe '#empty?', state do describe 'when the subject has entries that are not loaded' do action { subject.empty? } should_return_false should_be_a_kicker should_not_change_subject end describe 'when the subject has entries that are prepended' do subject { LazyArray.new.unshift(@nancy) } action { subject.empty? } should_return_false should_not_be_a_kicker should_not_change_subject end describe 'when the subject has entries that are appended' do subject { LazyArray.new << @nancy } action { subject.empty? } should_return_false should_not_be_a_kicker should_not_change_subject end describe 'when the subject has no entries' do subject { LazyArray.new } action { subject.empty? } should_return_true should_be_a_kicker should_not_change_subject end describe 'when the subject has only nil entries' do subject { LazyArray.new << nil } action { subject.empty? } should_return_false should_not_be_a_kicker should_not_change_subject end end [ :eql?, :== ].each do |method| should_respond_to(method) describe "##{method}", state do describe 'with an Enumerable containing the same entries' do before do if method == :eql? @other = LazyArray.new @other.load_with { |la| la.push(@nancy, @bessie) } else @other = [ @nancy, @bessie ] end end action { subject.send(method, @other) } should_return_true should_be_a_kicker should_not_change_subject end describe 'with an Enumerable containing different entries' do action { subject.send(method, @other) } should_return_false should_be_a_kicker should_not_change_subject end describe 'with an Enumerable with different entries than in head' do before { subject.unshift(@nancy) } action { subject.send(method, [ @steve ]) } should_return_false should_not_be_a_kicker should_not_change_subject end describe 'with an Enumerable with different entries than in tail' do before { subject.push(@nancy) } action { subject.send(method, [ @steve ]) } should_return_false should_not_be_a_kicker should_not_change_subject end end end should_respond_to(:fetch) describe '#fetch', state do describe 'with positive index' do action { subject.fetch(0) } should_return_expected_value { @nancy } should_be_a_kicker should_not_change_subject end describe 'with positive index', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.fetch(0) } should_return_expected_value { @steve } should_not_be_a_kicker should_not_change_subject end describe 'with negative index' do action { subject.fetch(-1) } should_return_expected_value { @bessie } should_be_a_kicker should_not_change_subject end describe 'with negative index', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.fetch(-1) } should_return_expected_value { @steve } should_not_be_a_kicker should_not_change_subject end describe 'with an index not within the LazyArray' do action { subject.fetch(2) } should_raise_error(IndexError) end describe 'with an index not within the LazyArray and default' do action { subject.fetch(2, @steve) } should_return_expected_value { @steve } should_be_a_kicker should_not_change_subject end describe 'with an index not within the LazyArray and default block' do action { subject.fetch(2) { @steve } } should_return_expected_value { @steve } should_be_a_kicker should_not_change_subject end end should_respond_to(:freeze) describe '#freeze', state do action { subject.freeze } should_return_subject should_not_be_a_kicker it { lambda { action }.should change(subject, :frozen?).from(false).to(true) } end should_respond_to(:first) describe '#first', state do describe 'with no arguments' do action { subject.first } should_return_expected_value { @nancy } should_be_a_kicker should_not_change_subject end describe 'with no arguments', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.first } should_return_expected_value { @steve } should_not_be_a_kicker should_not_change_subject end describe 'with length specified' do action { subject.first(1) } it { action.should == [ @nancy ] } should_be_a_kicker should_not_change_subject end describe 'with length specified', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.first(1) } it { action.should == [ @steve ] } should_not_be_a_kicker should_not_change_subject end end should_respond_to(:include?) describe '#include?', state do describe 'with an included entry' do action { subject.include?(@nancy) } should_return_true should_be_a_kicker should_not_change_subject end describe 'with an included entry', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.include?(@steve) } should_return_true should_not_be_a_kicker should_not_change_subject end describe 'with an included entry', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.include?(@steve) } should_return_true should_not_be_a_kicker should_not_change_subject end describe 'with an entry not included' do action { subject.include?(@steve) } should_return_false should_be_a_kicker should_not_change_subject end end should_respond_to(:index) describe '#index', state do describe 'with an included entry' do action { subject.index(@nancy) } should_return_expected_value { 0 } should_be_a_kicker should_not_change_subject end describe 'with an included entry', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.index(@steve) } should_return_expected_value { 0 } should_not_be_a_kicker should_not_change_subject end describe 'with an included entry', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.index(@steve) } should_return_expected_value { 2 } should_be_a_kicker # need to kick because first index could be in lazy array should_not_change_subject end describe 'with an entry not included' do action { subject.index(@steve) } should_return_nil should_be_a_kicker should_not_change_subject end end should_respond_to(:insert) describe '#insert', state do describe 'with an index of 0' do action { subject.insert(0, @steve) } should_return_subject should_not_be_a_kicker it 'should insert the entries before the index' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @steve, @nancy, @bessie ]) end end describe 'with an positive index greater than the head size' do action { subject.insert(1, @steve) } should_return_subject should_be_a_kicker it 'should insert the entries before the index' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @steve, @bessie ]) end end describe 'with an index of -1' do action { subject.insert(-1, @steve) } should_return_subject should_not_be_a_kicker it 'should insert the entries before the index' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @bessie, @steve ]) end end describe 'with a negative index greater than the tail size' do action { subject.insert(-2, @steve) } should_return_subject should_be_a_kicker it 'should insert the entries before the index' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @steve, @bessie ]) end end describe 'with a positive index 1 greater than the maximum index of the LazyArray' do action { subject.insert(2, @steve) } should_return_subject should_be_a_kicker it 'should insert the entries before the index' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @bessie, @steve ]) end end describe 'with a positive index not within the LazyArray' do action { subject.insert(3, @steve) } should_return_subject should_be_a_kicker it 'should insert the entries before the index, expanding the LazyArray' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @bessie, nil, @steve ]) end end describe 'with a negative index 1 greater than the maximum index of the LazyArray' do action { subject.insert(-3, @steve) } should_return_subject should_be_a_kicker it 'should insert the entries before the index, expanding the LazyArray' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @steve, @nancy, @bessie ]) end end describe 'with a negative index not within the LazyArray' do action { subject.insert(-4, @steve) } should_raise_error(IndexError) end end should_respond_to(:kind_of?) describe '#kind_of' do describe 'when provided a class that is a superclass' do action { subject.kind_of?(Object) } should_return_true should_not_be_a_kicker should_not_change_subject end describe 'when provided a class that is a proxy class superclass' do action { subject.kind_of?(Array) } should_return_true should_not_be_a_kicker should_not_change_subject end describe 'when provided a class that is not a superclass' do action { subject.kind_of?(Hash) } should_return_false should_not_be_a_kicker should_not_change_subject end end should_respond_to(:last) describe '#last', state do describe 'with no arguments' do action { subject.last } should_return_expected_value { @bessie } should_be_a_kicker should_not_change_subject end describe 'with no arguments', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.last } should_return_expected_value { @steve } should_not_be_a_kicker should_not_change_subject end describe 'with length specified' do action { subject.last(1) } it { action.should == [ @bessie ] } should_be_a_kicker should_not_change_subject end describe 'with length specified', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.last(1) } it { action.should == [ @steve ] } should_not_be_a_kicker should_not_change_subject end end should_respond_to(:loaded?) describe '#loaded?' do if loaded describe 'when loaded' do action { subject.loaded? } should_return_true should_not_change_subject end else describe 'when not loaded' do action { subject.loaded? } should_return_false should_not_change_subject end end end should_respond_to(:nil?) describe '#nil?' do action { subject.nil? } should_return_expected_value { false } should_not_be_a_kicker end should_respond_to(:pop) describe '#pop', state do describe 'without appending to the LazyArray' do action { subject.pop } should_return_expected_value { @bessie } should_be_a_kicker it 'should remove the last entry' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy ]) end end describe 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.pop } should_return_expected_value { @steve } should_not_be_a_kicker it 'should remove the last entry' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie, @steve ]).to([ @nancy, @bessie ]) end end end should_respond_to(:push) describe '#push', state do action { subject.push(@steve, @steve) } should_return_subject should_not_be_a_kicker it 'should append entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @bessie, @steve, @steve ]) end end should_respond_to(:reject!) describe '#reject!', state do before { @accumulator = [] } describe 'with a block that matches an entry' do action { subject.reject! { |e| @accumulator << e; true } } should_return_subject should_yield_to_each_entry should_be_a_kicker it 'should update with the block results' do lambda { action }.should change(subject, :empty?).from(false).to(true) end end describe 'with a block that does not match an entry' do action { subject.reject! { |e| @accumulator << e; false } } should_return_nil should_yield_to_each_entry should_be_a_kicker should_not_change_subject end end should_respond_to(:replace) describe '#replace' do action { subject.replace(@other) } should_return_subject should_be_a_kicker it 'should replace with other Enumerable' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @steve ]) end end should_respond_to(:reverse) describe '#reverse', state do action { subject.reverse } should_return_kind_of(LazyArray) should_not_be_a_kicker should_not_change_subject it 'should return a reversed LazyArray' do action.should == [ @bessie, @nancy ] end end should_respond_to(:reverse!) describe '#reverse!', state do action { subject.reverse! } should_return_subject should_not_be_a_kicker it 'should return a reversed LazyArray' do action.should == [ @bessie, @nancy ] end end should_respond_to(:reverse_each) describe '#reverse_each', state do before { @accumulator = [] } action { subject.reverse_each { |e| @accumulator << e } } should_return_subject should_be_a_kicker should_not_change_subject it 'should yield to each entry' do lambda { action }.should change(@accumulator, :entries).from([]).to([ @bessie, @nancy ]) end end should_respond_to(:rindex) describe '#rindex', state do describe 'with an included entry' do action { subject.rindex(@nancy) } should_return_expected_value { 0 } should_be_a_kicker # rindex always a kicker should_not_change_subject end describe 'with an included entry', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.rindex(@steve) } should_return_expected_value { 0 } should_be_a_kicker # rindex always a kicker should_not_change_subject end describe 'with an included entry', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.rindex(@steve) } should_return_expected_value { 2 } should_be_a_kicker # rindex always a kicker should_not_change_subject end describe 'with an entry not included' do action { subject.rindex(@steve) } should_return_nil should_be_a_kicker # rindex always a kicker should_not_change_subject end end should_respond_to(:shift) describe '#shift', state do describe 'without prepending to the LazyArray' do action { subject.shift } should_return_expected_value { @nancy } should_be_a_kicker it 'should remove the last entry' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @bessie ]) end end describe 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.shift } should_return_expected_value { @steve } should_not_be_a_kicker it 'should remove the last entry' do lambda { action }.should change(subject, :entries).from([ @steve, @nancy, @bessie ]).to([ @nancy, @bessie ]) end end end [ :slice, :[] ].each do |method| should_respond_to(method) describe "##{method}", state do describe 'with a positive index' do action { subject.send(method, 0) } should_return_expected_value { @nancy } should_be_a_kicker should_not_change_subject end describe 'with a positive index', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.send(method, 0) } should_return_expected_value { @steve } should_not_be_a_kicker should_not_change_subject end describe 'with a positive index and length' do action { subject.send(method, 0, 1) } should_return_kind_of(Array) should_return_expected_value { [ @nancy ] } should_be_a_kicker should_not_change_subject end describe 'with a positive index and length', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.send(method, 0, 1) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker should_not_change_subject end describe 'with a positive range' do action { subject.send(method, 0..0) } should_return_kind_of(Array) should_return_expected_value { [ @nancy ] } should_be_a_kicker should_not_change_subject end describe 'with a positive range', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.send(method, 0..0) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker should_not_change_subject end describe 'with a negative index' do action { subject.send(method, -1) } should_return_expected_value { @bessie } should_be_a_kicker should_not_change_subject end describe 'with a negative index', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.send(method, -1) } should_return_expected_value { @steve } should_not_be_a_kicker should_not_change_subject end describe 'with a negative index and length' do action { subject.send(method, -1, 1) } should_return_kind_of(Array) should_return_expected_value { [ @bessie ] } should_be_a_kicker should_not_change_subject end describe 'with a negative index and length', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.send(method, -1, 1) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker should_not_change_subject end describe 'with a negative range' do action { subject.send(method, -1..-1) } should_return_kind_of(Array) should_return_expected_value { [ @bessie ] } should_be_a_kicker should_not_change_subject end describe 'with a negative range', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.send(method, -1..-1) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker should_not_change_subject end describe 'with an index not within the LazyArray' do action { subject.send(method, 2) } should_return_nil should_be_a_kicker should_not_change_subject end describe 'with an index and length not within the LazyArray' do action { subject.send(method, 2, 1) } should_return_kind_of(Array) should_return_expected_value { [] } should_be_a_kicker should_not_change_subject end describe 'with a range not within the LazyArray' do action { subject.send(method, 2..2) } should_return_kind_of(Array) should_return_expected_value { [] } should_be_a_kicker should_not_change_subject end describe 'with invalid arguments' do action { subject.send(method, 1, 1..1) } should_raise_error(ArgumentError) end end end should_respond_to(:slice!) describe '#slice!', state do describe 'with a positive index' do action { subject.slice!(0) } should_return_expected_value { @nancy } should_be_a_kicker it 'should remove the matching entry' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @bessie ]) end end describe 'with a positive index', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.slice!(0) } should_return_expected_value { @steve } should_not_be_a_kicker it 'should remove the matching entry' do lambda { action }.should change(subject, :entries).from([ @steve, @nancy, @bessie ]).to([ @nancy, @bessie ]) end end describe 'with a positive index and length' do action { subject.slice!(0, 1) } should_return_kind_of(Array) should_return_expected_value { [ @nancy ] } should_be_a_kicker it 'should remove the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @bessie ]) end end describe 'with a positive index and length', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.slice!(0, 1) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker it 'should remove the matching entries' do lambda { action }.should change(subject, :entries).from([ @steve, @nancy, @bessie ]).to([ @nancy, @bessie ]) end end describe 'with a positive range' do action { subject.slice!(0..0) } should_return_kind_of(Array) should_return_expected_value { [ @nancy ] } should_be_a_kicker it 'should remove the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @bessie ]) end end describe 'with a positive range', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.slice!(0..0) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker it 'should remove the matching entry' do lambda { action }.should change(subject, :entries).from([ @steve, @nancy, @bessie ]).to([ @nancy, @bessie ]) end end describe 'with a negative index' do action { subject.slice!(-1) } should_return_expected_value { @bessie } should_be_a_kicker it 'should remove the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy ]) end end describe 'with a negative index', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.slice!(-1) } should_return_expected_value { @steve } should_not_be_a_kicker it 'should remove the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie, @steve ]).to([ @nancy, @bessie ]) end end describe 'with a negative index and length' do action { subject.slice!(-1, 1) } should_return_kind_of(Array) should_return_expected_value { [ @bessie ] } should_be_a_kicker it 'should remove the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy ]) end end describe 'with a negative index and length', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.slice!(-1, 1) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker it 'should remove the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie, @steve ]).to([ @nancy, @bessie ]) end end describe 'with a negative range' do action { subject.slice!(-1..-1) } should_return_kind_of(Array) should_return_expected_value { [ @bessie ] } should_be_a_kicker it 'should remove the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy ]) end end describe 'with a negative range', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.slice!(-1..-1) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker it 'should remove the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie, @steve ]).to([ @nancy, @bessie ]) end end describe 'with an index not within the LazyArray' do action { subject.slice!(2) } should_return_nil should_be_a_kicker should_not_change_subject end describe 'with an index and length not within the LazyArray' do action { subject.slice!(2, 1) } should_return_kind_of(Array) should_return_expected_value { [] } should_be_a_kicker should_not_change_subject end describe 'with a range not within the LazyArray' do action { subject.slice!(2..2) } should_return_kind_of(Array) should_return_expected_value { [] } should_be_a_kicker should_not_change_subject end end should_respond_to(:sort!) describe '#sort!', state do describe 'without a block' do action { subject.sort! } should_return_subject should_be_a_kicker it 'should sort the LazyArray inline using default sort order' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @bessie, @nancy ]) end end describe 'without a block' do action { subject.sort! { |a,b| a <=> b } } should_return_subject should_be_a_kicker it 'should sort the LazyArray inline using block' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @bessie, @nancy ]) end end end [ :splice, :[]= ].each do |method| should_respond_to(method) describe "##{method}", state do before do @jon = 'jon' end describe 'with a positive index and entry' do action { subject.send(method, 0, @jon) } should_return_expected_value { @jon } should_be_a_kicker it 'should change the matching entry' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @jon, @bessie ]) end end describe 'with a positive index', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.send(method, 0, @jon) } should_return_expected_value { @jon } should_not_be_a_kicker it 'should change the matching entry' do lambda { action }.should change(subject, :entries).from([ @steve, @nancy, @bessie ]).to([ @jon, @nancy, @bessie ]) end end describe 'with a positive index and length' do action { subject.send(method, 0, 1, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @jon, @bessie ]) end end describe 'with a positive index and length', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.send(method, 0, 1, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_not_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @steve, @nancy, @bessie ]).to([ @jon, @nancy, @bessie ]) end end describe 'with a positive range' do action { subject.send(method, 0..0, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @jon, @bessie ]) end end describe 'with a positive range', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.send(method, 0..0, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_not_be_a_kicker it 'should change the matching entry' do lambda { action }.should change(subject, :entries).from([ @steve, @nancy, @bessie ]).to([ @jon, @nancy, @bessie ]) end end describe 'with a negative index' do action { subject.send(method, -1, @jon) } should_return_expected_value { @jon } should_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @jon ]) end end describe 'with a negative index', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.send(method, -1, @jon) } should_return_expected_value { @jon } should_not_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie, @steve ]).to([ @nancy, @bessie, @jon ]) end end describe 'with a negative index and length' do action { subject.send(method, -1, 1, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @jon ]) end end describe 'with a negative index and length', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.send(method, -1, 1, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_not_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie, @steve ]).to([ @nancy, @bessie, @jon ]) end end describe 'with a negative range' do action { subject.send(method, -1..-1, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @jon ]) end end describe 'with a negative range', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.send(method, -1..-1, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_not_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie, @steve ]).to([ @nancy, @bessie, @jon ]) end end describe 'with a positive index 1 greater than the maximum index of the LazyArray' do action { subject.send(method, 2, @jon) } should_return_expected_value { @jon } should_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @bessie, @jon ]) end end describe 'with a positive index not within the LazyArray' do action { subject.send(method, 3, @jon) } should_return_expected_value { @jon } should_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @bessie, nil, @jon ]) end end describe 'with a negative index not within the LazyArray' do action { subject.send(method, -3, @jon) } should_raise_error(IndexError) end describe 'with a positive index and length 1 greater than the maximum index of the LazyArray' do action { subject.send(method, 2, 1, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @bessie, @jon ]) end end describe 'with a positive index and length not within the LazyArray' do action { subject.send(method, 3, 1, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @bessie, nil, @jon ]) end end describe 'with a negative index and length not within the LazyArray ' do action { subject.send(method, -3, 1, [ @jon ]) } should_raise_error(IndexError) end describe 'with a positive range 1 greater than the maximum index of the LazyArray' do action { subject.send(method, 2..2, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @bessie, @jon ]) end end describe 'with a positive range not within the LazyArray' do action { subject.send(method, 3..3, [ @jon ]) } should_return_kind_of(Array) should_return_expected_value { [ @jon ] } should_be_a_kicker it 'should change the matching entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @nancy, @bessie, nil, @jon ]) end end describe 'with a negative range not within the LazyArray' do action { subject.send(method, -3..-3, [ @jon ]) } should_raise_error(RangeError) end end end should_respond_to(:to_a) describe '#to_a', state do action { subject.to_a } should_return_kind_of(Array) should_be_a_kicker it 'should be equivalent to self' do action.should == subject end end should_respond_to(:unshift) describe '#unshift', state do action { subject.unshift(@steve, @steve) } should_return_subject should_not_be_a_kicker it 'should prepend entries' do lambda { action }.should change(subject, :entries).from([ @nancy, @bessie ]).to([ @steve, @steve, @nancy, @bessie ]) end end should_respond_to(:values_at) describe '#values_at', state do describe 'with a positive index' do action { subject.values_at(0) } should_return_kind_of(Array) should_return_expected_value { [ @nancy ] } should_be_a_kicker should_not_change_subject end describe 'with a positive index', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.values_at(0) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker should_not_change_subject end describe 'with a positive range' do action { subject.values_at(0..0) } should_return_kind_of(Array) should_return_expected_value { [ @nancy ] } should_be_a_kicker should_not_change_subject end describe 'with a positive range', 'after prepending to the LazyArray' do before { subject.unshift(@steve) } action { subject.values_at(0..0) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker should_not_change_subject end describe 'with a negative index' do action { subject.values_at(-1) } should_return_kind_of(Array) should_return_expected_value { [ @bessie ] } should_be_a_kicker should_not_change_subject end describe 'with a negative index', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.values_at(-1) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker should_not_change_subject end describe 'with a negative range' do action { subject.values_at(-1..-1) } should_return_kind_of(Array) should_return_expected_value { [ @bessie ] } should_be_a_kicker should_not_change_subject end describe 'with a negative range', 'after appending to the LazyArray' do before { subject.push(@steve) } action { subject.values_at(-1..-1) } should_return_kind_of(Array) should_return_expected_value { [ @steve ] } should_not_be_a_kicker should_not_change_subject end describe 'with an index not within the LazyArray' do action { subject.values_at(2) } should_return_kind_of(Array) should_return_expected_value { [ nil ] } should_be_a_kicker should_not_change_subject end describe 'with a range not within the LazyArray' do action { subject.values_at(2..2) } should_return_kind_of(Array) should_return_expected_value { [ nil ] } should_be_a_kicker should_not_change_subject end end describe 'a method mixed into Array' do before :all do Enumerable.class_eval do remove_method :lazy_spec if instance_methods(false).any? { |m| m.to_sym == :lazy_spec } def lazy_spec true end end end it 'should delegate to the Array' do subject.lazy_spec.should be(true) end end describe 'an unknown method' do action { subject.unknown } should_raise_error(NoMethodError) end end end extlib-0.9.16/spec/lazy_module_spec.rb000066400000000000000000000013701207310540200177060ustar00rootroot00000000000000require 'spec_helper' require 'extlib/lazy_module' describe LazyModule do describe "instantiated with a block" do it "defers block body evaluation" do lambda do LazyModule.new do raise "Will only be evaluated when mixed in" end end.should_not raise_error end end describe "included into hosting class" do before :all do KlazzyLazyModule = LazyModule.new do def self.klassy "Klazz" end def instancy "Instanzz" end end @klass = Class.new do include KlazzyLazyModule end end it "class evals block body" do @klass.klassy.should == "Klazz" @klass.new.instancy.should == "Instanzz" end end end extlib-0.9.16/spec/mash_spec.rb000066400000000000000000000164231207310540200163170ustar00rootroot00000000000000require 'spec_helper' require 'extlib/mash' class AwesomeHash < Hash end describe Mash do before(:each) do @hash = { "mash" => "indifferent", :hash => "different" } @sub = AwesomeHash.new("mash" => "indifferent", :hash => "different") end describe "#initialize" do it 'converts all keys into strings when param is a Hash' do mash = Mash.new(@hash) mash.keys.any? { |key| key.is_a?(Symbol) }.should be(false) end it 'converts all pure Hash values into Mashes if param is a Hash' do mash = Mash.new :hash => @hash mash["hash"].should be_an_instance_of(Mash) # sanity check mash["hash"]["hash"].should == "different" end it 'doesn not convert Hash subclass values into Mashes' do mash = Mash.new :sub => @sub mash["sub"].should be_an_instance_of(AwesomeHash) end it 'converts all value items if value is an Array' do mash = Mash.new :arry => { :hash => [@hash] } mash["arry"].should be_an_instance_of(Mash) # sanity check mash["arry"]["hash"].first["hash"].should == "different" end it 'delegates to superclass constructor if param is not a Hash' do mash = Mash.new("dash berlin") mash["unexisting key"].should == "dash berlin" end end # describe "#initialize" describe "#update" do it 'converts all keys into strings when param is a Hash' do mash = Mash.new(@hash) mash.update("starry" => "night") mash.keys.any? { |key| key.is_a?(Symbol) }.should be(false) end it 'converts all Hash values into Mashes if param is a Hash' do mash = Mash.new :hash => @hash mash.update(:hash => { :hash => "is buggy in Ruby 1.8.6" }) mash["hash"].should be_an_instance_of(Mash) end end # describe "#update" describe "#[]=" do it 'converts key into string' do mash = Mash.new(@hash) mash[:hash] = { "starry" => "night" } mash.keys.any? { |key| key.is_a?(Symbol) }.should be(false) end it 'converts all Hash value into Mash' do mash = Mash.new :hash => @hash mash[:hash] = { :hash => "is buggy in Ruby 1.8.6" } mash["hash"].should be_an_instance_of(Mash) end end # describe "#[]=" describe "#key?" do before(:each) do @mash = Mash.new(@hash) end it 'converts key before lookup' do @mash.key?("mash").should be(true) @mash.key?(:mash).should be(true) @mash.key?("hash").should be(true) @mash.key?(:hash).should be(true) @mash.key?(:rainclouds).should be(false) @mash.key?("rainclouds").should be(false) end it 'is aliased as include?' do @mash.include?("mash").should be(true) @mash.include?(:mash).should be(true) @mash.include?("hash").should be(true) @mash.include?(:hash).should be(true) @mash.include?(:rainclouds).should be(false) @mash.include?("rainclouds").should be(false) end it 'is aliased as member?' do @mash.member?("mash").should be(true) @mash.member?(:mash).should be(true) @mash.member?("hash").should be(true) @mash.member?(:hash).should be(true) @mash.member?(:rainclouds).should be(false) @mash.member?("rainclouds").should be(false) end end # describe "#key?" describe "#dup" do it 'returns instance of Mash' do Mash.new(@hash).dup.should be_an_instance_of(Mash) end it 'preserves keys' do mash = Mash.new(@hash) dup = mash.dup mash.keys.sort.should == dup.keys.sort end it 'preserves value' do mash = Mash.new(@hash) dup = mash.dup mash.values.sort.should == dup.values.sort end end describe "#to_hash" do it 'returns instance of Mash' do Mash.new(@hash).to_hash.should be_an_instance_of(Hash) end it 'preserves keys' do mash = Mash.new(@hash) converted = mash.to_hash mash.keys.sort.should == converted.keys.sort end it 'preserves value' do mash = Mash.new(@hash) converted = mash.to_hash mash.values.sort.should == converted.values.sort end end describe "#symbolize_keys" do it 'returns instance of Mash' do Mash.new(@hash).symbolize_keys.should be_an_instance_of(Hash) end it 'converts keys to symbols' do mash = Mash.new(@hash) converted = mash.symbolize_keys converted_keys = converted.keys.sort{|k1, k2| k1.to_s <=> k2.to_s} orig_keys = mash.keys.map{|k| k.to_sym}.sort{|i1, i2| i1.to_s <=> i2.to_s} converted_keys.should == orig_keys end it 'preserves value' do mash = Mash.new(@hash) converted = mash.symbolize_keys mash.values.sort.should == converted.values.sort end end describe "#delete" do it 'converts Symbol key into String before deleting' do mash = Mash.new(@hash) mash.delete(:hash) mash.key?("hash").should be(false) end it 'works with String keys as well' do mash = Mash.new(@hash) mash.delete("mash") mash.key?("mash").should be(false) end end describe "#except" do it "converts Symbol key into String before calling super" do mash = Mash.new(@hash) hashless_mash = mash.except(:hash) hashless_mash.key?("hash").should be(false) end it "works with String keys as well" do mash = Mash.new(@hash) mashless_mash = mash.except("mash") mashless_mash.key?("mash").should be(false) end it "works with multiple keys" do mash = Mash.new(@hash) mashless = mash.except("hash", :mash) mashless.key?(:hash).should be(false) mashless.key?("mash").should be(false) end it "should return a mash" do mash = Mash.new(@hash) hashless_mash = mash.except(:hash) hashless_mash.class.should be(Mash) end end describe "#merge" do before(:each) do @mash = Mash.new(@hash).merge(:no => "in between") end it 'returns instance of Mash' do @mash.should be_an_instance_of(Mash) end it 'merges in give Hash' do @mash["no"].should == "in between" end end describe "#fetch" do before(:each) do @mash = Mash.new(@hash).merge(:no => "in between") end it 'converts key before fetching' do @mash.fetch("no").should == "in between" end it 'returns alternative value if key lookup fails' do @mash.fetch("flying", "screwdriver").should == "screwdriver" end end describe "#default" do before(:each) do @mash = Mash.new(:yet_another_technical_revolution) end it 'returns default value unless key exists in mash' do @mash.default("peak oil is now behind, baby").should == :yet_another_technical_revolution end it 'returns existing value if key is Symbol and exists in mash' do @mash.update(:no => "in between") @mash.default(:no).should == "in between" end end describe "#values_at" do before(:each) do @mash = Mash.new(@hash).merge(:no => "in between") end it 'is indifferent to whether keys are strings or symbols' do @mash.values_at("hash", :mash, :no).should == ["different", "indifferent", "in between"] end end describe "#stringify_keys!" do it 'returns the mash itself' do mash = Mash.new(@hash) mash.stringify_keys!.object_id.should == mash.object_id end end end extlib-0.9.16/spec/module_spec.rb000066400000000000000000000034511207310540200166510ustar00rootroot00000000000000require 'spec_helper' require 'extlib/module' describe Module do before(:all) do module ::Foo module ModBar module Noo module Too module Boo end end end end class Zed end end class ::Bas end class ::Bar end end it "should raise NameError for a missing constant" do lambda { Foo.find_const('Moo') }.should raise_error(NameError) lambda { Object.find_const('MissingConstant') }.should raise_error(NameError) end it "should be able to get a recursive constant" do Object.find_const('Foo::ModBar').should == Foo::ModBar end it "should ignore get Constants from the Kernel namespace correctly" do Object.find_const('::Foo::ModBar').should == ::Foo::ModBar end it "should find relative constants" do Foo.find_const('ModBar').should == Foo::ModBar Foo.find_const('Bas').should == Bas end it "should find sibling constants" do Foo::ModBar.find_const("Zed").should == Foo::Zed end it "should find nested constants on nested constants" do Foo::ModBar.find_const('Noo::Too').should == Foo::ModBar::Noo::Too end it "should find constants outside of nested constants" do Foo::ModBar::Noo::Too.find_const("Zed").should == Foo::Zed end it 'should be able to find past the second nested level' do Foo::ModBar::Noo.find_const('Too').should == Foo::ModBar::Noo::Too Foo::ModBar::Noo::Too.find_const('Boo').should == Foo::ModBar::Noo::Too::Boo end it "should be able to deal with constants being added and removed" do Object.find_const('Bar') # First we load Bar with find_const Object.module_eval { remove_const('Bar') } # Now we delete it module ::Bar; end; # Now we redefine it Object.find_const('Bar').should == Bar end end extlib-0.9.16/spec/object_space_spec.rb000066400000000000000000000003351207310540200200030ustar00rootroot00000000000000require 'spec_helper' require 'extlib/object_space' describe ObjectSpace, "#classes" do it 'returns only classes, nothing else' do ObjectSpace.classes.each do |klass| Class.should === klass end end end extlib-0.9.16/spec/object_spec.rb000066400000000000000000000060221207310540200166270ustar00rootroot00000000000000require 'spec_helper' require 'extlib/object' module HactiveSupport class MemoizeConsideredUseless end end class SymbolicDuck def quack end end class ClassyDuck end module Foo class Bar end end class Oi attr_accessor :foo end describe Object do describe "#full_const_get" do it 'returns constant by FQ name in receiver namespace' do Object.full_const_get("Oi").should == Oi Object.full_const_get("Foo::Bar").should == Foo::Bar end end describe "#full_const_set" do it 'sets constant value by FQ name in receiver namespace' do Object.full_const_set("HactiveSupport::MCU", HactiveSupport::MemoizeConsideredUseless) Object.full_const_get("HactiveSupport::MCU").should == HactiveSupport::MemoizeConsideredUseless HactiveSupport.full_const_get("MCU").should == HactiveSupport::MemoizeConsideredUseless end end describe "#make_module" do it 'creates a module from string FQ name' do Object.make_module("Milano") Object.make_module("Norway::Oslo") Object.const_defined?("Milano").should == true Norway.const_defined?("Oslo").should == true end it "handles the case where we already have a class in the heirarchy" do Object.make_module("Foo::Bar::Baz") Object.const_defined?("Foo").should == true Foo.const_defined?("Bar").should == true Foo::Bar.const_defined?("Baz").should == true Foo::Bar::Baz.should be_kind_of(Module) end end describe "#quacks_like?" do it 'returns true if duck is a Symbol and receiver responds to it' do SymbolicDuck.new.quacks_like?(:quack).should be(true) end it 'returns false if duck is a Symbol and receiver DOES NOT respond to it' do SymbolicDuck.new.quacks_like?(:wtf).should be(false) end it 'returns true if duck is a class and receiver is its instance' do receiver = ClassyDuck.new receiver.quacks_like?(ClassyDuck).should be(true) end it 'returns false if duck is a class and receiver IS NOT its instance' do receiver = ClassyDuck.new receiver.quacks_like?(SymbolicDuck).should be(false) end it 'returns true if duck is an array and at least one of its members quacks like this duck' do receiver = ClassyDuck.new ary = [ClassyDuck, SymbolicDuck] receiver.quacks_like?(ary).should be(true) end it 'returns false if duck is an array and none of its members quacks like this duck' do receiver = ClassyDuck.new ary = [SymbolicDuck.new, SymbolicDuck] receiver.quacks_like?(ary).should be(false) end end describe "#in?" do it 'returns true if object is included in collection' do @ary = [1, 2, 3] @set = Set.new([2, 3, 5]) 1.in?(@ary).should be(true) 2.in?(@ary).should be(true) 3.in?(@ary).should be(true) 4.in?(@ary).should be(false) 1.in?(@set).should be(false) 2.in?(@set).should be(true) 3.in?(@set).should be(true) 4.in?(@set).should be(false) 5.in?(@set).should be(true) end end end extlib-0.9.16/spec/pooling_spec.rb000066400000000000000000000322731207310540200170370ustar00rootroot00000000000000require 'spec_helper' require 'extlib' # extlib/pooling is enough but specs rely on methods defined in extlib.rb module Extlib::Pooling class << self remove_method :scavenger_interval if instance_methods(false).any? { |m| m.to_sym == :scavenger_interval } def scavenger_interval 1 end end end describe "Extlib::Pooling" do before do Object.send(:remove_const, :Person) if defined?(Person) class ::Person include Extlib::Pooling attr_accessor :name def initialize(name) @name = name end def dispose @name = nil end end Object.send(:remove_const, :Overwriter) if defined?(Overwriter) class ::Overwriter def self.new(*args) instance = allocate instance.send(:initialize, *args) instance.overwritten = true instance end include Extlib::Pooling attr_accessor :name def initialize(name) @name = name @overwritten = false end def overwritten? @overwritten end def overwritten=(value) @overwritten = value end class << self remove_method :pool_size if instance_methods(false).any? { |m| m.to_sym == :pool_size } def pool_size pool_size = if RUBY_PLATFORM =~ /java/ 20 else 2 end pool_size end end def dispose @name = nil end end end after :each do Extlib::Pooling.lock.synchronize do Extlib::Pooling.pools.each do |pool| pool.lock.synchronize do pool.dispose end end end end it "should track the initialized pools" do bob = Person.new('Bob') # Ensure the pool is "primed" bob.name.should == 'Bob' bob.instance_variable_get(:@__pool).should_not be_nil Person.__pools.size.should == 1 bob.release Person.__pools.size.should == 1 Extlib::Pooling::pools.should_not be_empty sleep(1.2) Extlib::Pooling::pools.should be_empty bob.name.should be_nil end it "should maintain a size of 1" do bob = Person.new('Bob') fred = Person.new('Fred') ted = Person.new('Ted') Person.__pools.each do |args, pool| pool.size.should == 1 end bob.release fred.release ted.release Person.__pools.each do |args, pool| pool.size.should == 1 end end it "should allow you to overwrite Class#new" do bob = Overwriter.new('Bob') bob.should be_overwritten bob.release end it "should allow multiple threads to access the pool" do t1 = Thread.new do bob = Person.new('Bob') sleep(1) bob.release end lambda do bob = Person.new('Bob') t1.join bob.release end.should_not raise_error end it "should allow you to flush a pool" do bob = Overwriter.new('Bob') Overwriter.new('Bob').release bob.release bob.name.should == 'Bob' Overwriter.__pools[['Bob']].size.should == 2 Overwriter.__pools[['Bob']].flush! Overwriter.__pools[['Bob']].size.should == 0 bob.name.should be_nil end it "should wake up the scavenger thread when exiting" do bob = Person.new('Bob') bob.release Extlib.exiting = true sleep(0.1) Extlib::Pooling.scavenger?.should be(false) end it "should be able to detach an instance from the pool" do bob = Person.new('Bob') Person.__pools[['Bob']].size.should == 1 bob.detach Person.__pools[['Bob']].size.should == 0 end end # describe Extlib::Pooling::ResourcePool do # before :each do # @pool = Extlib::Pooling::ResourcePool.new(7, DisposableResource, :expiration_period => 50) # end # # it "responds to flush!" do # @pool.should respond_to(:flush!) # end # # it "responds to acquire" do # @pool.should respond_to(:acquire) # end # # it "responds to release" do # @pool.should respond_to(:release) # end # # it "responds to :available?" do # @pool.should respond_to(:available?) # end # # it "has a size limit" do # @pool.size_limit.should == 7 # end # # it "has initial size of zero" do # @pool.size.should == 0 # end # # it "has a set of reserved resources" do # @pool.instance_variable_get("@reserved").should be_empty # end # # it "has a set of available resources" do # @pool.instance_variable_get("@available").should be_empty # end # # it "knows class of resources (objects) it works with" do # @pool.class_of_resources.should == DisposableResource # end # # it "raises exception when given anything but class for resources class" do # lambda { # @pool = Extlib::Pooling::ResourcePool.new(7, "Hooray!", {}) # }.should raise_error(ArgumentError, /class/) # end # # it "requires class of resources (objects) it works with to have a dispose instance method" do # lambda { # @pool = Extlib::Pooling::ResourcePool.new(3, UndisposableResource, {}) # }.should raise_error(ArgumentError, /dispose/) # end # # it "may take initialization arguments" do # @pool = Extlib::Pooling::ResourcePool.new(7, DisposableResource, { :initialization_args => ["paper"] }) # @pool.instance_variable_get("@initialization_args").should == ["paper"] # end # # it "may take expiration period option" do # @pool = Extlib::Pooling::ResourcePool.new(7, DisposableResource, { :expiration_period => 100 }) # @pool.expiration_period.should == 100 # end # # it "has default expiration period of one minute" do # @pool = Extlib::Pooling::ResourcePool.new(7, DisposableResource, {}) # @pool.expiration_period.should == 60 # end # # it "spawns a thread to dispose objects haven't been used for a while" do # @pool = Extlib::Pooling::ResourcePool.new(7, DisposableResource, {}) # @pool.instance_variable_get("@pool_expiration_thread").should be_an_instance_of(Thread) # end # end # # # # describe "Acquire from constant size pool" do # before :each do # DisposableResource.initialize_pool(2) # end # # after :each do # DisposableResource.instance_variable_set("@__pool", nil) # end # # it "increased size of the pool" do # @time = DisposableResource.pool.acquire # DisposableResource.pool.size.should == 1 # end # # it "places initialized instance in the reserved set" do # @time = DisposableResource.pool.acquire # DisposableResource.pool.instance_variable_get("@reserved").size.should == 1 # end # # it "raises an exception when pool size limit is hit" do # @t1 = DisposableResource.pool.acquire # @t2 = DisposableResource.pool.acquire # # lambda { DisposableResource.pool.acquire }.should raise_error(RuntimeError) # end # # it "returns last released resource" do # @t1 = DisposableResource.pool.acquire # @t2 = DisposableResource.pool.acquire # DisposableResource.pool.release(@t1) # # DisposableResource.pool.acquire.should == @t1 # end # # it "really truly returns last released resource" do # @t1 = DisposableResource.pool.acquire # DisposableResource.pool.release(@t1) # # @t2 = DisposableResource.pool.acquire # DisposableResource.pool.release(@t2) # # @t3 = DisposableResource.pool.acquire # DisposableResource.pool.release(@t3) # # DisposableResource.pool.acquire.should == @t1 # @t1.should == @t3 # end # # it "sets allocation timestamp on resource instance" do # @t1 = DisposableResource.new # @t1.instance_variable_get("@__pool_acquire_timestamp").should be_close(Time.now, 2) # end # end # # # # describe "Releasing from constant size pool" do # before :each do # DisposableResource.initialize_pool(2) # end # # after :each do # DisposableResource.instance_variable_set("@__pool", nil) # end # # it "decreases size of the pool" do # @t1 = DisposableResource.pool.acquire # @t2 = DisposableResource.pool.acquire # DisposableResource.pool.release(@t1) # # DisposableResource.pool.size.should == 1 # end # # it "raises an exception on attempt to releases object not in pool" do # @t1 = DisposableResource.new # @t2 = Set.new # # DisposableResource.pool.release(@t1) # lambda { DisposableResource.pool.release(@t2) }.should raise_error(RuntimeError) # end # # it "removes released object from reserved set" do # @t1 = DisposableResource.pool.acquire # # lambda { # DisposableResource.pool.release(@t1) # }.should change(DisposableResource.pool.instance_variable_get("@reserved"), :size).by(-1) # end # # it "returns released object back to available set" do # @t1 = DisposableResource.pool.acquire # # lambda { # DisposableResource.pool.release(@t1) # }.should change(DisposableResource.pool.instance_variable_get("@available"), :size).by(1) # end # # it "updates acquire timestamp on already allocated resource instance" do # # acquire it once # @t1 = DisposableResource.new # # wait a bit # sleep 3 # # # check old timestamp # @t1.instance_variable_get("@__pool_acquire_timestamp").should be_close(Time.now, 4) # # # re-acquire # DisposableResource.pool.release(@t1) # @t1 = DisposableResource.new # # see timestamp is updated # @t1.instance_variable_get("@__pool_acquire_timestamp").should be_close(Time.now, 2) # end # end # # # # describe Extlib::Pooling::ResourcePool, "#available?" do # before :each do # DisposableResource.initialize_pool(2) # DisposableResource.new # end # # after :each do # DisposableResource.instance_variable_set("@__pool", nil) # end # # it "returns true when pool has available instances" do # DisposableResource.pool.should be_available # end # # it "returns false when pool is exhausted" do # # acquires the last available resource # DisposableResource.new # DisposableResource.pool.should_not be_available # end # end # # # # describe "Flushing of constant size pool" do # before :each do # DisposableResource.initialize_pool(2) # # @t1 = DisposableResource.new # @t2 = DisposableResource.new # # # sanity check # DisposableResource.pool.instance_variable_get("@reserved").should_not be_empty # end # # after :each do # DisposableResource.instance_variable_set("@__pool", nil) # end # # it "disposes all pooled objects" do # [@t1, @t2].each { |instance| instance.should_receive(:dispose) } # # DisposableResource.pool.flush! # end # # it "empties reserved set" do # DisposableResource.pool.flush! # # DisposableResource.pool.instance_variable_get("@reserved").should be_empty # end # # it "returns all instances to available set" do # DisposableResource.pool.flush! # # DisposableResource.pool.instance_variable_get("@available").size.should == 2 # end # end # # # # describe "Poolable resource class" do # before :each do # DisposableResource.initialize_pool(3, :initialization_args => ["paper"]) # end # # after :each do # DisposableResource.instance_variable_set("@__pool", nil) # end # # it "acquires new instances from pool" do # @instance_one = DisposableResource.new # # DisposableResource.pool.acquired?(@instance_one).should be(true) # end # # it "flushed existing pool on re-initialization" do # DisposableResource.pool.should_receive(:flush!) # DisposableResource.initialize_pool(5) # end # # it "replaces pool on re-initialization" do # DisposableResource.initialize_pool(5) # DisposableResource.pool.size_limit.should == 5 # end # # it "passes initialization parameters to newly created resource instances" do # DisposableResource.new.name.should == "paper" # end # end # # # # describe "Pooled object", "on initialization" do # after :each do # DisposableResource.instance_variable_set("@__pool", nil) # end # # it "does not flush pool" do # # using pool here initializes the pool first # # so we use instance variable directly # DisposableResource.instance_variable_get("@__pool").should_not_receive(:flush!) # DisposableResource.initialize_pool(23) # end # # it "flushes pool first when re-initialized" do # DisposableResource.initialize_pool(5) # DisposableResource.pool.should_receive(:flush!) # DisposableResource.initialize_pool(23) # end # end # # # # describe Extlib::Pooling::ResourcePool, "#time_to_dispose?" do # before :each do # DisposableResource.initialize_pool(7, :expiration_period => 2) # end # # after :each do # DisposableResource.instance_variable_set("@__pool", nil) # end # # it "returns true when object's last aquisition time is greater than limit" do # @t1 = DisposableResource.new # DisposableResource.pool.time_to_release?(@t1).should be(false) # # sleep 3 # DisposableResource.pool.time_to_release?(@t1).should be(true) # end # end # # # # describe Extlib::Pooling::ResourcePool, "#dispose_outdated" do # before :each do # DisposableResource.initialize_pool(7, :expiration_period => 2) # end # # after :each do # DisposableResource.instance_variable_set("@__pool", nil) # end # # it "releases and thus disposes outdated instances" do # @t1 = DisposableResource.new # DisposableResource.pool.should_receive(:time_to_release?).with(@t1).and_return(true) # DisposableResource.pool.should_receive(:release).with(@t1) # # DisposableResource.pool.dispose_outdated # end # end extlib-0.9.16/spec/rcov.opts000066400000000000000000000001211207310540200156740ustar00rootroot00000000000000--exclude "spec,^/" --sort coverage --callsites --xrefs --profile --text-summary extlib-0.9.16/spec/simple_set_spec.rb000066400000000000000000000024441207310540200175310ustar00rootroot00000000000000require 'spec_helper' require 'extlib/simple_set' describe Extlib::SimpleSet do before do @s = Extlib::SimpleSet.new("Initial") end describe "#initialize" do it 'adds passed value to the set' do @new_generation_vms = Extlib::SimpleSet.new(["Rubinius", "PyPy", "Parrot"]) @new_generation_vms.should have_key("Rubinius") @new_generation_vms.should have_key("PyPy") @new_generation_vms.should have_key("Parrot") end end describe "#<<" do it "adds value to the set" do @s << "Hello" @s.to_a.should be_include("Hello") end it 'sets true mark on the key' do @s << "Fun" @s["Fun"].should be(true) end end describe "#merge(other)" do it "preserves old values when values do not overlap" do @s.should have_key("Initial") end it 'adds new values from merged set' do @t = @s.merge(["Merged value"]) @t.should have_key("Merged value") end it 'returns a SimpleSet instance' do @s.merge(["Merged value"]).should be_kind_of(Extlib::SimpleSet) end end describe "#inspect" do it "lists set values" do @s.inspect.should == "#" end end describe "#keys" do it 'is aliased as to_a' do @s.to_a.should === @s.keys end end end extlib-0.9.16/spec/spec.opts000066400000000000000000000000661207310540200156650ustar00rootroot00000000000000--colour --loadby random --format profile --backtrace extlib-0.9.16/spec/spec_helper.rb000066400000000000000000000002611207310540200166370ustar00rootroot00000000000000$TESTING=true require "rubygems" require "spec" require "yaml" lib_path = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path) extlib-0.9.16/spec/string_spec.rb000066400000000000000000000137371207310540200167020ustar00rootroot00000000000000require 'spec_helper' require 'extlib/string' describe String, "#to_const_string" do it "swaps slashes with ::" do "foo/bar".to_const_string.should == "Foo::Bar" end it "replaces snake_case with CamelCase" do "foo/bar/baz_bat".to_const_string.should == "Foo::Bar::BazBat" end it "leaves constant string as is" do "Merb::Test".to_const_string.should == "Merb::Test" end end describe String, "#to_const_path" do it "swaps :: with slash" do "Foo::Bar".to_const_path.should == "foo/bar" end it "snake_cases string" do "Merb::Test::ViewHelper".to_const_path.should == "merb/test/view_helper" end it "leaves slash-separated snake case string as is" do "merb/test/view_helper".to_const_path.should == "merb/test/view_helper" end end describe String, "#camel_case" do it "handles lowercase without underscore" do "merb".camel_case.should == "Merb" end it "handles lowercase with 1 underscore" do "merb_core".camel_case.should == "MerbCore" end it "handles lowercase with more than 1 underscore" do "so_you_want_contribute_to_merb_core".camel_case.should == "SoYouWantContributeToMerbCore" end it "handles lowercase with more than 1 underscore in a row" do "__python__is__like__this".camel_case.should == "PythonIsLikeThis" end it "handle first capital letter with underscores" do "Python__Is__Like__This".camel_case.should == "PythonIsLikeThis" end it "leaves CamelCase as is" do "TestController".camel_case.should == "TestController" end end describe String, "#snake_case" do it "lowercases one word CamelCase" do "Merb".snake_case.should == "merb" end it "makes one underscore snake_case two word CamelCase" do "MerbCore".snake_case.should == "merb_core" end it "handles CamelCase with more than 2 words" do "SoYouWantContributeToMerbCore".snake_case.should == "so_you_want_contribute_to_merb_core" end it "handles CamelCase with more than 2 capital letter in a row" do "CNN".snake_case.should == "cnn" "CNNNews".snake_case.should == "cnn_news" "HeadlineCNNNews".snake_case.should == "headline_cnn_news" "NameACRONYM".snake_case.should == "name_acronym" end it "does NOT change one word lowercase" do "merb".snake_case.should == "merb" end it "leaves snake_case as is" do "merb_core".snake_case.should == "merb_core" end end describe String, "#escape_regexp" do it "escapes all * in a string" do "*and*".escape_regexp.should == "\\*and\\*" end it "escapes all ? in a string" do "?and?".escape_regexp.should == "\\?and\\?" end it "escapes all { in a string" do "{and{".escape_regexp.should == "\\{and\\{" end it "escapes all } in a string" do "}and}".escape_regexp.should == "\\}and\\}" end it "escapes all . in a string" do ".and.".escape_regexp.should == "\\.and\\." end it "escapes all regexp special characters used in a string" do "*?{}.".escape_regexp.should == "\\*\\?\\{\\}\\." end end describe String, "#unescape_regexp" do it "unescapes all \\* in a string" do "\\*and\\*".unescape_regexp.should == "*and*" end it "unescapes all \\? in a string" do "\\?and\\?".unescape_regexp.should == "?and?" end it "unescapes all \\{ in a string" do "\\{and\\{".unescape_regexp.should == "{and{" end it "unescapes all \\} in a string" do "\\}and\\}".unescape_regexp.should == "}and}" end it "unescapes all \\. in a string" do "\\.and\\.".unescape_regexp.should == ".and." end it "unescapes all regexp special characters used in a string" do "\\*\\?\\{\\}\\.".unescape_regexp.should == "*?{}." end end describe String, "#/" do it "concanates operands with File::SEPARATOR" do ("merb" / "core").should == "merb#{File::SEPARATOR}core" end end require 'rbconfig' describe String, "#relative_path_from" do it "uses other operand as base for path calculation" do site_dir = Config::CONFIG["sitedir"] two_levels_up = site_dir.split(File::SEPARATOR) 2.times { two_levels_up.pop } # remove two deepest directories two_levels_up = two_levels_up.join(File::SEPARATOR) two_levels_up.relative_path_from(site_dir).should == "../.." end end describe String, ".translate" do before(:each) do String.stub!(:translations).and_return({ "on snakes and rubies" => "a serpenti e rubini" }) end it 'looks up for translation in translations dictionary' do String.translate("on snakes and rubies").should == "a serpenti e rubini" end it 'returns string that has no translations as it is' do String.translate("shapes").should == "shapes" String.translate("kalopsia").should == "kalopsia" String.translate("holding on to nothing").should == "holding on to nothing" end end describe String, ".t" do before(:each) do String.stub!(:translations).and_return({ '%s must not be blank' => "%s moet ingevuld worden", 'username' => 'gebruikersnaam', '%s must be between %s and %s characters long' => '%s moet tussen %s en %s tekens lang zijn'}) end it 'looks up for translation in translations dictionary and translates parameters as well' do "%s must not be blank".t(:username).should == "gebruikersnaam moet ingevuld worden" "%s must not be blank".t('username').should == "gebruikersnaam moet ingevuld worden" "%s must be between %s and %s characters long".t(:password, 5, 9).should == "password moet tussen 5 en 9 tekens lang zijn" end it 'returns string that has no translations as it is' do "password".t.should == "password" end it 'should not translate when freezed' do "%s must not be blank".t('username'.freeze).should == "username moet ingevuld worden" end end describe String, ".translations" do before(:each) do end it 'returns empty hash by default' do String.translations.should == {} end it 'returns @translations if set' do pending "is it @translations on metaclass or @@translations? leaving it out for now" end end extlib-0.9.16/spec/struct_spec.rb000066400000000000000000000003041207310540200167020ustar00rootroot00000000000000require 'spec_helper' require 'extlib/struct' describe Struct do it "should have attributes" do s = Struct.new(:name).new('bob') s.attributes.should == { :name => 'bob' } end end extlib-0.9.16/spec/symbol_spec.rb000066400000000000000000000003741207310540200166720ustar00rootroot00000000000000require 'spec_helper' require 'extlib/symbol' describe Symbol, "#/" do it "concanates operands with File::SEPARATOR" do (:merb / "core").should == "merb#{File::SEPARATOR}core" (:merb / :core).should == "merb#{File::SEPARATOR}core" end end extlib-0.9.16/spec/time_spec.rb000066400000000000000000000013531207310540200163210ustar00rootroot00000000000000require 'spec_helper' require 'extlib/time' require 'json' describe Time, "#to_json" do before do @expected = "\"2008-03-28T22:54:20Z\"" end it "should transform itself into a ISO 8601 compatible string" do Time.utc(2008, 3, 28, 22, 54, 20).to_json.should == @expected Time.xmlschema("2008-03-28T22:54:20Z").to_json.should == @expected Time.xmlschema("2008-03-28T17:54:20-05:00").to_json.should == @expected end end describe Time, "#to_time" do it "should return a copy of its self" do time = Time.now time.to_time.should == time end end describe Time, "#to_datetime" do it "should return an equivalent DateTime" do time = Time.now time.to_datetime.should == DateTime.parse(time.to_s) end end extlib-0.9.16/spec/try_call_spec.rb000066400000000000000000000030461207310540200171750ustar00rootroot00000000000000require 'spec_helper' require 'extlib' describe "#try_call" do describe "with an Object" do before :all do @receiver = Object.new end it "returns receiver itself" do @receiver.try_call.should == @receiver end end describe "with a number" do before :all do @receiver = 42 end it "returns receiver itself" do @receiver.try_call.should == @receiver end end describe "with a String" do before :all do @receiver = "Ruby, programmer's best friend" end it "returns receiver itself" do @receiver.try_call.should == @receiver end end describe "with a hash" do before :all do @receiver = { :functional_programming => "FTW" } end it "returns receiver itself" do @receiver.try_call.should == @receiver end end describe "with a Proc" do before :all do @receiver = Proc.new { 5 * 7 } end it "returns result of calling of a proc" do @receiver.try_call.should == 35 end end describe "with a Proc that takes 2 arguments" do before :all do @receiver = Proc.new { |one, other| one + other } end it "passes arguments to #call, returns result of calling of a proc" do @receiver.try_call(10, 20).should == 30 end end describe "with a Proc that takes 3 arguments" do before :all do @receiver = Proc.new { |a, b, c| (a + b) * c } end it "passes arguments to #call, returns result of calling of a proc" do @receiver.try_call(10, 20, 3).should == 90 end end end extlib-0.9.16/spec/try_dup_spec.rb000066400000000000000000000014751207310540200170560ustar00rootroot00000000000000require 'spec_helper' require 'extlib/try_dup' describe "try_dup" do it "returns a duplicate version on regular objects" do obj = Object.new oth = obj.try_dup obj.should_not === oth end it "returns self on Numerics" do obj = 12 oth = obj.try_dup obj.should === oth end it "returns self on Symbols" do obj = :test oth = obj.try_dup obj.should === oth end it "returns self on true" do obj = true oth = obj.try_dup obj.should === oth end it "returns self on false" do obj = false oth = obj.try_dup obj.should === oth end it "returns self on nil" do obj = nil oth = obj.try_dup obj.should === oth end it "returns self on modules" do obj = Module.new oth = obj.try_dup obj.object_id.should == oth.object_id end end extlib-0.9.16/spec/virtual_file_spec.rb000066400000000000000000000007641207310540200200550ustar00rootroot00000000000000require 'spec_helper' require 'extlib/virtual_file' describe VirtualFile do it 'inherits from StringIO' do VirtualFile.superclass.should == StringIO end it 'has path reader' do @vf = VirtualFile.new("virtual", "elvenpath") # =~ /#{Dir::tmpdir}/ causes RegExp to fail with nested *?+ on 1.8.6 @vf.path.should == "elvenpath" end it 'has path writer' do @vf = VirtualFile.new("virtual", "elvenpath") @vf.path = "newbase" @vf.path.should == "newbase" end end extlib-0.9.16/tasks/000077500000000000000000000000001207310540200142155ustar00rootroot00000000000000extlib-0.9.16/tasks/ci.rake000066400000000000000000000000641207310540200154540ustar00rootroot00000000000000task :ci => [ :verify_measurements, 'metrics:all' ] extlib-0.9.16/tasks/metrics.rake000066400000000000000000000013361207310540200165320ustar00rootroot00000000000000begin require 'metric_fu' rescue LoadError namespace :metrics do task :all do abort 'metric_fu is not available. In order to run metrics:all, you must: gem install metric_fu' end end end begin require 'reek/adapters/rake_task' Reek::RakeTask.new do |t| t.fail_on_error = true t.verbose = false t.source_files = 'lib/**/*.rb' end rescue LoadError task :reek do abort 'Reek is not available. In order to run reek, you must: gem install reek' end end begin require 'roodi' require 'roodi_task' RoodiTask.new do |t| t.verbose = false end rescue LoadError task :roodi do abort 'Roodi is not available. In order to run roodi, you must: gem install roodi' end end extlib-0.9.16/tasks/spec.rake000066400000000000000000000017131207310540200160150ustar00rootroot00000000000000spec_defaults = lambda do |spec| spec.pattern = 'spec/**/*_spec.rb' spec.libs << 'lib' << 'spec' spec.spec_opts << '--options' << 'spec/spec.opts' end begin require 'spec/rake/spectask' Spec::Rake::SpecTask.new(:spec, &spec_defaults) rescue LoadError task :spec do abort 'rspec is not available. In order to run spec, you must: gem install rspec' end end begin require 'rcov' require 'spec/rake/verify_rcov' Spec::Rake::SpecTask.new(:rcov) do |rcov| spec_defaults.call(rcov) rcov.rcov = true rcov.rcov_opts = File.read('spec/rcov.opts').split(/\s+/) end RCov::VerifyTask.new(:verify_rcov => :rcov) do |rcov| rcov.threshold = 100 end rescue LoadError %w[ rcov verify_rcov ].each do |name| task name do abort "rcov is not available. In order to run #{name}, you must: gem install rcov" end end end task :spec => :check_dependencies task :rcov => :check_dependencies task :default => :spec extlib-0.9.16/tasks/yard.rake000066400000000000000000000002641207310540200160220ustar00rootroot00000000000000begin require 'yard' YARD::Rake::YardocTask.new rescue LoadError task :yard do abort 'YARD is not available. In order to run yard, you must: gem install yard' end end extlib-0.9.16/tasks/yardstick.rake000066400000000000000000000007451207310540200170640ustar00rootroot00000000000000begin require 'pathname' require 'yardstick/rake/measurement' require 'yardstick/rake/verify' # yardstick_measure task Yardstick::Rake::Measurement.new # verify_measurements task Yardstick::Rake::Verify.new do |verify| verify.threshold = 100 end rescue LoadError %w[ yardstick_measure verify_measurements ].each do |name| task name.to_s do abort "Yardstick is not available. In order to run #{name}, you must: gem install yardstick" end end end