1require 'yaml' 2require 'dbm' 3 4module YAML 5 6# YAML + DBM = YDBM 7# 8# YAML::DBM provides the same interface as ::DBM. 9# 10# However, while DBM only allows strings for both keys and values, 11# this library allows one to use most Ruby objects for values 12# by first converting them to YAML. Keys must be strings. 13# 14# Conversion to and from YAML is performed automatically. 15# 16# See the documentation for ::DBM and ::YAML for more information. 17class DBM < ::DBM 18 VERSION = "0.1" 19 20 # Return value associated with +key+ from database. 21 # 22 # Returns +nil+ if there is no such +key+. 23 def []( key ) 24 fetch( key ) 25 end 26 27 # :call-seq: 28 # []=( key, value ) 29 # 30 # Set +key+ to +value+ in database. 31 # 32 # +value+ will be converted to YAML before storage. 33 def []=( key, val ) 34 store( key, val ) 35 end 36 37 # :call-seq: 38 # fetch( key, ifnone = nil ) 39 # fetch( key, &block ) 40 # 41 # Return value associated with +key+. 42 # 43 # If there is no value for +key+ and no block is given, returns +ifnone+. 44 # 45 # Otherwise, calls block passing in the given +key+. 46 def fetch( keystr, ifnone = nil ) 47 begin 48 val = super( keystr ) 49 return YAML.load( val ) if String === val 50 rescue IndexError 51 end 52 if block_given? 53 yield keystr 54 else 55 ifnone 56 end 57 end 58 59 # Deprecated, used YAML::DBM#key instead. 60 # ---- 61 # Note: 62 # YAML::DBM#index makes warning from internal of ::DBM#index. 63 # It says 'DBM#index is deprecated; use DBM#key', but DBM#key 64 # behaves not same as DBM#index. 65 # 66 def index( keystr ) 67 super( keystr.to_yaml ) 68 end 69 70 def key( keystr ) 71 invert[keystr] 72 end 73 74 # Returns an array containing the values associated with the given keys. 75 def values_at( *keys ) 76 keys.collect { |k| fetch( k ) } 77 end 78 79 # Deletes value from database associated with +key+. 80 # 81 # Returns value or +nil+. 82 def delete( key ) 83 v = super( key ) 84 if String === v 85 v = YAML.load( v ) 86 end 87 v 88 end 89 90 # Calls the given block once for each +key+, +value+ pair in the database. 91 # Deletes all entries for which the block returns true. 92 # 93 # Returns +self+. 94 def delete_if # :yields: [key, value] 95 del_keys = keys.dup 96 del_keys.delete_if { |k| yield( k, fetch( k ) ) == false } 97 del_keys.each { |k| delete( k ) } 98 self 99 end 100 101 # Converts the contents of the database to an in-memory Hash, then calls 102 # Hash#reject with the specified code block, returning a new Hash. 103 def reject 104 hsh = self.to_hash 105 hsh.reject { |k,v| yield k, v } 106 end 107 108 # Calls the given block once for each +key+, +value+ pair in the database. 109 # 110 # Returns +self+. 111 def each_pair # :yields: [key, value] 112 keys.each { |k| yield k, fetch( k ) } 113 self 114 end 115 116 # Calls the given block for each value in database. 117 # 118 # Returns +self+. 119 def each_value # :yields: value 120 super { |v| yield YAML.load( v ) } 121 self 122 end 123 124 # Returns an array of values from the database. 125 def values 126 super.collect { |v| YAML.load( v ) } 127 end 128 129 # Returns true if specified value is found in the database. 130 def has_value?( val ) 131 each_value { |v| return true if v == val } 132 return false 133 end 134 135 # Returns a Hash (not a DBM database) created by using each value in the 136 # database as a key, with the corresponding key as its value. 137 # 138 # Note that all values in the hash will be Strings, but the keys will be 139 # actual objects. 140 def invert 141 h = {} 142 keys.each { |k| h[ self.fetch( k ) ] = k } 143 h 144 end 145 146 # Replaces the contents of the database with the contents of the specified 147 # object. Takes any object which implements the each_pair method, including 148 # Hash and DBM objects. 149 def replace( hsh ) 150 clear 151 update( hsh ) 152 end 153 154 # Removes a [key, value] pair from the database, and returns it. 155 # If the database is empty, returns +nil+. 156 # 157 # The order in which values are removed/returned is not guaranteed. 158 def shift 159 a = super 160 a[1] = YAML.load( a[1] ) if a 161 a 162 end 163 164 # :call-seq: 165 # select( &block ) 166 # select( *keys ) 167 # 168 # If a block is provided, returns a new array containing [key, value] pairs 169 # for which the block returns true. 170 # 171 # Otherwise, same as #values_at 172 def select( *keys ) 173 if block_given? 174 self.keys.collect { |k| v = self[k]; [k, v] if yield k, v }.compact 175 else 176 values_at( *keys ) 177 end 178 end 179 180 # :call-seq: 181 # store( key, value ) 182 # 183 #Stores +value+ in database with +key+ as the index. +value+ is converted 184 #to YAML before being stored. 185 # 186 #Returns +value+ 187 def store( key, val ) 188 super( key, val.to_yaml ) 189 val 190 end 191 192 # Updates the database with multiple values from the specified object. 193 # Takes any object which implements the each_pair method, including 194 # Hash and DBM objects. 195 # 196 # Returns +self+. 197 def update( hsh ) 198 hsh.each_pair do |k,v| 199 self.store( k, v ) 200 end 201 self 202 end 203 204 # Converts the contents of the database to an array of [key, value] arrays, 205 # and returns it. 206 def to_a 207 a = [] 208 keys.each { |k| a.push [ k, self.fetch( k ) ] } 209 a 210 end 211 212 213 # Converts the contents of the database to an in-memory Hash object, and 214 # returns it. 215 def to_hash 216 h = {} 217 keys.each { |k| h[ k ] = self.fetch( k ) } 218 h 219 end 220 221 alias :each :each_pair 222end 223 224end 225