Class: Storable
- Inherits:
-
Object
- Object
- Storable
- Extended by:
- DefaultProcessors
- Defined in:
- lib/storable.rb,
lib/storable.rb
Overview
Storable makes data available in multiple formats and can re-create objects from files. Fields are defined using the Storable.field method which tells Storable the order and name.
Defined Under Namespace
Modules: DefaultProcessors Classes: Anonymous, OrderedHash
Constant Summary collapse
- USE_ORDERED_HASH =
(RUBY_VERSION =~ /^1.9/).nil?
- VERSION =
"0.8.5"
- NICE_TIME_FORMAT =
"%Y-%m-%d@%H:%M:%S".freeze
- SUPPORTED_FORMATS =
[:tsv, :csv, :yaml, :json, :s, :string].freeze
Class Attribute Summary collapse
-
.debug ⇒ Object
Returns the value of attribute debug.
-
.field_names ⇒ Object
Returns the value of attribute field_names.
-
.field_types ⇒ Object
Returns the value of attribute field_types.
-
.sensitive_fields(*args) ⇒ Object
Returns the value of attribute sensitive_fields.
Instance Attribute Summary collapse
-
#format ⇒ Object
This value will be used as a default unless provided on-the-fly.
Class Method Summary collapse
- .append_file(path, content, flush = true) ⇒ Object
-
.field(args = {}, &processor) ⇒ Object
Accepts field definitions in the one of the follow formats:.
- .from_array(*from) ⇒ Object
-
.from_csv(from = [], sensitive = false) ⇒ Object
Create a new instance of the object from comma-delimited data.
-
.from_delimited(from = [], delim = ',', sensitive = false) ⇒ Object
Create a new instance of the object from a delimited string.
-
.from_file(file_path, format = 'yaml') ⇒ Object
Create a new instance of the object using data from file.
-
.from_hash(from = {}) ⇒ Object
Create a new instance of the object from a hash.
-
.from_json(*from) ⇒ Object
Create a new instance of the object from a JSON string.
-
.from_tsv(from = [], sensitive = false) ⇒ Object
Create a new instance from tab-delimited data.
-
.from_yaml(*from) ⇒ Object
Create a new instance of the object from YAML.
- .has_field?(n) ⇒ Boolean
-
.inherited(obj) ⇒ Object
Passes along fields to inherited classes.
- .read_file_to_array(path) ⇒ Object
- .sensitive_field?(name) ⇒ Boolean
- .write_file(path, content, flush = true) ⇒ Object
- .write_or_append_file(write_or_append, path, content = '', flush = true) ⇒ Object
Instance Method Summary collapse
- #call(fname) ⇒ Object
-
#dump(format = nil, with_titles = false) ⇒ Object
Dump the object data to the given format.
-
#field_names ⇒ Object
Returns an array of field names defined by self.field.
-
#field_types ⇒ Object
Returns an array of field types defined by self.field.
- #from_array(*from) ⇒ Object
- #from_hash(from = {}) ⇒ Object
- #has_field?(n) ⇒ Boolean
- #has_processor?(fname) ⇒ Boolean
- #init(*args) ⇒ Object
- #postprocess ⇒ Object
- #process(fname, val) ⇒ Object
- #sensitive! ⇒ Object
- #sensitive? ⇒ Boolean
- #sensitive_fields ⇒ Object
- #to_array ⇒ Object
-
#to_csv(with_titles = false) ⇒ Object
Return the object data as a comma delimited string.
-
#to_delimited(with_titles = false, delim = ',') ⇒ Object
Return the object data as a delimited string.
-
#to_file(file_path = nil, with_titles = true) ⇒ Object
Write the object data to the given file.
-
#to_hash ⇒ Object
Return the object data as a hash
with_titles
is ignored. - #to_json(*from, &blk) ⇒ Object
- #to_string(*args) ⇒ Object
-
#to_tsv(with_titles = false) ⇒ Object
Return the object data as a tab delimited string.
- #to_yaml(*from, &blk) ⇒ Object
Methods included from DefaultProcessors
gibbler_id_processor, hash_proc_processor, proc_processor
Class Attribute Details
.debug ⇒ Object
Returns the value of attribute debug.
45 46 47 |
# File 'lib/storable.rb', line 45 def debug @debug end |
.field_names ⇒ Object
Returns the value of attribute field_names.
45 46 47 |
# File 'lib/storable.rb', line 45 def field_names @field_names end |
.field_types ⇒ Object
Returns the value of attribute field_types.
45 46 47 |
# File 'lib/storable.rb', line 45 def field_types @field_types end |
.sensitive_fields(*args) ⇒ Object
Returns the value of attribute sensitive_fields.
45 46 47 |
# File 'lib/storable.rb', line 45 def sensitive_fields @sensitive_fields end |
Instance Attribute Details
#format ⇒ Object
This value will be used as a default unless provided on-the-fly. See SUPPORTED_FORMATS for available values.
126 127 128 |
# File 'lib/storable.rb', line 126 def format @format end |
Class Method Details
.append_file(path, content, flush = true) ⇒ Object
451 452 453 |
# File 'lib/storable.rb', line 451 def self.append_file(path, content, flush=true) write_or_append_file('a', path, content, flush) end |
.field(args = {}, &processor) ⇒ Object
Accepts field definitions in the one of the follow formats:
field :product
field :product => Integer
field :product do |val|
# modify val before it's stored.
end
The order they’re defined determines the order the will be output. The fields data is available by the standard accessors, class.product and class.product= etc… The value of the field will be cast to the type (if provided) when read from a file. The value is not touched when the type is not provided.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/storable.rb', line 69 def self.field(args={}, &processor) # TODO: Examine casting from: http://codeforpeople.com/lib/ruby/fattr/fattr-1.0.3/ args = {args => nil} unless args.kind_of?(Hash) self.field_names ||= [] self.field_types ||= {} args.each_pair do |m,t| self.field_names << m self.field_types[m] = t unless t.nil? # This processor automatically converts a Proc object # to a String of its source. processor = proc_processor if t == Proc && processor.nil? unless processor.nil? define_method("_storable_processor_#{m}", &processor) end if method_defined?(m) # don't redefine the getter method STDERR.puts "method exists: #{self}##{m}" if Storable.debug else define_method(m) do instance_variable_get("@#{m}") end end if method_defined?("#{m}=") # don't redefine the setter methods STDERR.puts "method exists: #{self}##{m}=" if Storable.debug else define_method("#{m}=") do |val| instance_variable_set("@#{m}",val) end end end end |
.from_array(*from) ⇒ Object
211 212 213 214 215 216 217 218 |
# File 'lib/storable.rb', line 211 def self.from_array *from from = from.flatten.compact return nil if !from || from.empty? me = new me.from_array *from me.postprocess me end |
.from_csv(from = [], sensitive = false) ⇒ Object
Create a new instance of the object from comma-delimited data. from
a JSON string split into an array by line.
413 414 415 |
# File 'lib/storable.rb', line 413 def self.from_csv(from=[], sensitive=false) self.from_delimited(from, ',', sensitive) end |
.from_delimited(from = [], delim = ',', sensitive = false) ⇒ Object
Create a new instance of the object from a delimited string. from
a JSON string split into an array by line. delim
is the field delimiter.
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
# File 'lib/storable.rb', line 420 def self.from_delimited(from=[],delim=',',sensitive=false) return if from.empty? from = from.split($/) if String === from hash = {} fnames = sensitive ? (field_names-sensitive_fields) : field_names values = from[0].chomp.split(delim) fnames.each_with_index do |key,index| next unless values[index] hash[key.to_sym] = values[index] end hash = from_hash(hash) if hash.kind_of?(Hash) hash end |
.from_file(file_path, format = 'yaml') ⇒ Object
Create a new instance of the object using data from file.
173 174 175 176 177 178 179 180 |
# File 'lib/storable.rb', line 173 def self.from_file(file_path, format='yaml') raise "Cannot read file (#{file_path})" unless File.exists?(file_path) raise "#{self} doesn't support from_#{format}" unless self.respond_to?("from_#{format}") format = format || File.extname(file_path).tr('.', '') me = send("from_#{format}", read_file_to_array(file_path)) me.format = format me end |
.from_hash(from = {}) ⇒ Object
Create a new instance of the object from a hash.
191 192 193 194 195 196 197 198 |
# File 'lib/storable.rb', line 191 def self.from_hash(from={}) return nil if !from || from.empty? if self == Storable Storable::Anonymous.new from else new.from_hash(from) end end |
.from_json(*from) ⇒ Object
Create a new instance of the object from a JSON string. from
a YAML String or Array (split into by line).
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
# File 'lib/storable.rb', line 363 def self.from_json(*from) from_str = [from].flatten.compact.join('') #from_str.force_encoding("ISO-8859-1") #p [:from, from_str.encoding.name] if from_str.respond_to?(:encoding) if YAJL_LOADED tmp = Yajl::Parser.parse(from_str, :check_utf8 => false) elsif JSON_LOADED tmp = JSON::load(from_str) else raise "JSON parser not loaded" end hash_sym = tmp.keys.inject({}) do |hash, key| hash[key.to_sym] = tmp[key] hash end hash_sym = from_hash(hash_sym) if hash_sym.kind_of?(Hash) hash_sym end |
.from_tsv(from = [], sensitive = false) ⇒ Object
Create a new instance from tab-delimited data.
from
a JSON string split into an array by line.
408 409 410 |
# File 'lib/storable.rb', line 408 def self.from_tsv(from=[], sensitive=false) self.from_delimited(from, "\t", sensitive) end |
.from_yaml(*from) ⇒ Object
Create a new instance of the object from YAML. from
a YAML String or Array (split into by line).
354 355 356 357 358 359 |
# File 'lib/storable.rb', line 354 def self.from_yaml(*from) from_str = [from].flatten.compact.join('') hash = YAML::load(from_str) hash = from_hash(hash) if Hash === hash hash end |
.has_field?(n) ⇒ Boolean
116 117 118 |
# File 'lib/storable.rb', line 116 def self.has_field?(n) field_names.member? n.to_sym end |
.inherited(obj) ⇒ Object
Passes along fields to inherited classes
49 50 51 52 53 54 55 |
# File 'lib/storable.rb', line 49 def self.inherited(obj) unless Storable == self obj.sensitive_fields = self.sensitive_fields.clone if !self.sensitive_fields.nil? obj.field_names = self.field_names.clone if !self.field_names.nil? obj.field_types = self.field_types.clone if !self.field_types.nil? end end |
.read_file_to_array(path) ⇒ Object
436 437 438 439 440 441 442 443 444 445 |
# File 'lib/storable.rb', line 436 def self.read_file_to_array(path) contents = [] return contents unless File.exists?(path) open(path, 'r') do |l| contents = l.readlines end contents end |
.sensitive_field?(name) ⇒ Boolean
111 112 113 114 |
# File 'lib/storable.rb', line 111 def self.sensitive_field?(name) @sensitive_fields ||= [] @sensitive_fields.member?(name) end |
.write_file(path, content, flush = true) ⇒ Object
447 448 449 |
# File 'lib/storable.rb', line 447 def self.write_file(path, content, flush=true) write_or_append_file('w', path, content, flush) end |
.write_or_append_file(write_or_append, path, content = '', flush = true) ⇒ Object
455 456 457 458 459 460 461 462 463 464 |
# File 'lib/storable.rb', line 455 def self.write_or_append_file(write_or_append, path, content = '', flush = true) #STDERR.puts "Writing to #{ path }..." create_dir(File.dirname(path)) open(path, write_or_append) do |f| f.puts content f.flush if flush; end File.chmod(0600, path) end |
Instance Method Details
#call(fname) ⇒ Object
220 221 222 223 224 225 226 |
# File 'lib/storable.rb', line 220 def call(fname) unless field_types[fname.to_sym] == Proc && Proc === self.send(fname) raise "Field #{fname} is not a Proc" end self.instance_eval &self.send(fname) end |
#dump(format = nil, with_titles = false) ⇒ Object
Dump the object data to the given format.
160 161 162 163 164 165 |
# File 'lib/storable.rb', line 160 def dump(format=nil, with_titles=false) format &&= format.to_sym format ||= :s # as in, to_s raise "Format not defined (#{format})" unless SUPPORTED_FORMATS.member?(format) send("to_#{format}") end |
#field_names ⇒ Object
Returns an array of field names defined by self.field
147 148 149 |
# File 'lib/storable.rb', line 147 def field_names self.class.field_names end |
#field_types ⇒ Object
Returns an array of field types defined by self.field. Fields that did not receive a type are set to nil.
152 153 154 |
# File 'lib/storable.rb', line 152 def field_types self.class.field_types end |
#from_array(*from) ⇒ Object
204 205 206 207 208 209 |
# File 'lib/storable.rb', line 204 def from_array *from (self.field_names || []).each_with_index do |n,index| break if index >= from.size send("#{n}=", from[index]) end end |
#from_hash(from = {}) ⇒ Object
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/storable.rb', line 228 def from_hash(from={}) fnames = field_names return from if fnames.nil? || fnames.empty? fnames.each_with_index do |fname,index| ftype = field_types[fname] value_orig = from[fname.to_s] || from[fname.to_s.to_sym] next if value_orig.nil? if ( ftype == String or ftype == Symbol ) && value_orig.to_s.empty? value = '' elsif ftype == Array value = Array === value_orig ? value_orig : [value_orig] elsif ftype == Hash value = value_orig elsif !ftype.nil? value_orig = value_orig.first if Array === value_orig && value_orig.size == 1 if [Time, DateTime].member?(ftype) value = ftype.parse(value_orig) elsif [TrueClass, FalseClass, Boolean].member?(ftype) value = (value_orig.to_s.upcase == "TRUE") elsif ftype == Float value = value_orig.to_f elsif ftype == Integer value = value_orig.to_i elsif ftype == Symbol value = value_orig.to_s.to_sym elsif ftype == Range if Range === value_orig value = value_orig elsif Numeric === value_orig value = value_orig..value_orig else value_orig = value_orig.to_s if value_orig.match(/\.\.\./) el = value_orig.split('...') value = el.first.to_f...el.last.to_f elsif value_orig.match(/\.\./) el = value_orig.split('..') value = el.first.to_f..el.last.to_f else value = value_orig..value_orig end end elsif ftype == Proc && String === value_orig value = Proc.from_string value_orig end end value = value_orig if value.nil? if self.respond_to?("#{fname}=") self.send("#{fname}=", value) else self.instance_variable_set("@#{fname}", value) end end self.postprocess self end |
#has_field?(n) ⇒ Boolean
119 120 121 |
# File 'lib/storable.rb', line 119 def has_field?(n) self.class.field_names.member? n.to_sym end |
#has_processor?(fname) ⇒ Boolean
348 349 350 |
# File 'lib/storable.rb', line 348 def has_processor?(fname) self.respond_to? :"_storable_processor_#{fname}" end |
#init(*args) ⇒ Object
200 201 202 |
# File 'lib/storable.rb', line 200 def init *args from_array *args end |
#postprocess ⇒ Object
135 136 |
# File 'lib/storable.rb', line 135 def postprocess end |
#process(fname, val) ⇒ Object
344 345 346 |
# File 'lib/storable.rb', line 344 def process(fname, val) self.send :"_storable_processor_#{fname}", val end |
#sensitive! ⇒ Object
142 143 144 |
# File 'lib/storable.rb', line 142 def sensitive! @storable_sensitive = true end |
#sensitive? ⇒ Boolean
138 139 140 |
# File 'lib/storable.rb', line 138 def sensitive? @storable_sensitive == true end |
#sensitive_fields ⇒ Object
155 156 157 |
# File 'lib/storable.rb', line 155 def sensitive_fields self.class.sensitive_fields end |
#to_array ⇒ Object
309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/storable.rb', line 309 def to_array preprocess if respond_to? :preprocess fields = sensitive? ? (field_names-sensitive_fields) : field_names fields.collect do |fname| next if sensitive? && self.class.sensitive_field?(fname) v = self.send(fname) v = process(fname, v) if has_processor?(fname) if Array === v v = v.collect { |v2| v2.kind_of?(Storable) ? v2.to_a : v2 } end v end end |
#to_csv(with_titles = false) ⇒ Object
Return the object data as a comma delimited string. with_titles
specifiy whether to include field names (default: false)
403 404 405 |
# File 'lib/storable.rb', line 403 def to_csv(with_titles=false) to_delimited(with_titles, ',') end |
#to_delimited(with_titles = false, delim = ',') ⇒ Object
Return the object data as a delimited string. with_titles
specifiy whether to include field names (default: false) delim
is the field delimiter.
385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/storable.rb', line 385 def to_delimited(with_titles=false, delim=',') preprocess if respond_to? :preprocess values = [] fields = sensitive? ? (field_names-sensitive_fields) : field_names fields.each do |fname| values << self.send(fname.to_s) # TODO: escape values end output = values.join(delim) output = field_names.join(delim) << $/ << output if with_titles output end |
#to_file(file_path = nil, with_titles = true) ⇒ Object
Write the object data to the given file.
182 183 184 185 186 187 188 |
# File 'lib/storable.rb', line 182 def to_file(file_path=nil, with_titles=true) raise "Cannot store to nil path" if file_path.nil? format = File.extname(file_path).tr('.', '') format &&= format.to_sym format ||= @format Storable.write_file(file_path, dump(format, with_titles)) end |
#to_hash ⇒ Object
Return the object data as a hash with_titles
is ignored.
294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/storable.rb', line 294 def to_hash preprocess if respond_to? :preprocess tmp = USE_ORDERED_HASH ? Storable::OrderedHash.new : {} field_names.each do |fname| next if sensitive? && self.class.sensitive_field?(fname) v = self.send(fname) v = process(fname, v) if has_processor?(fname) if Array === v v = v.collect { |v2| v2.kind_of?(Storable) ? v2.to_hash : v2 } end tmp[fname] = v.kind_of?(Storable) ? v.to_hash : v end tmp end |
#to_json(*from, &blk) ⇒ Object
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/storable.rb', line 323 def to_json(*from, &blk) preprocess if respond_to? :preprocess hash = to_hash if YAJL_LOADED # set by Storable ret = Yajl::Encoder.encode(hash) #raise "DELANO" #ret.force_encoding("ISO-8859-1") #p [:to, ret.encoding.name] if ret.respond_to?(:encoding) ret elsif JSON_LOADED JSON.generate(hash, *from, &blk) else raise "no JSON parser loaded" end end |
#to_string(*args) ⇒ Object
167 168 169 170 |
# File 'lib/storable.rb', line 167 def to_string(*args) # TODO: sensitive? to_s(*args) end |
#to_tsv(with_titles = false) ⇒ Object
Return the object data as a tab delimited string. with_titles
specifiy whether to include field names (default: false)
398 399 400 |
# File 'lib/storable.rb', line 398 def to_tsv(with_titles=false) to_delimited(with_titles, "\t") end |
#to_yaml(*from, &blk) ⇒ Object
339 340 341 342 |
# File 'lib/storable.rb', line 339 def to_yaml(*from, &blk) preprocess if respond_to? :preprocess to_hash.to_yaml(*from, &blk) end |