From: Casimir Pohjanraito Date: 2008-12-17T03:22:35+09:00 Subject: Persistent Objects with Ruby - simple beginning Object persistence, I want it, without SQL or other schemes that could be implemented in ruby and somewhat automated instead. Smalltalk and Squeak inspired. Objects exist, no CRUD or 'saving' or 'writes'. So, as an attempt to I made this Storage singleton and Storable -class. (code at end or at http://pastie.org/340631 ) It is very simple and crude. I am not a computer scientist, more like an artist with tourettes. Nevertheless, I was hoping to get input. I think perhaps the next step would be changing this into a module. If time and skills permit it, it would be fun to make a ruby fork with persistence similar to Prevayler (mem + snapshots&transactions record) or Squeak (save image of VM), like DHH suggested in 2k8 Rubyconf: Persruby. Have fun, Casimir #start START #See example at end of file. Paste into IRB or save as file and load to test. class Storable #Marshals objects, creates meta-data labels #Intent: abstracts marshaling, unique filename, structured labeling #ORM-like? attr_accessor :filename, :label, :label_str, :user, :private, :tags, :description def initialize(given_name = "unknown_object") @creation_time = Time.now.tv_sec @given_name = given_name.to_s @tags = "storable" #for metainfo @description = "desc" #generate label self.labelize() #generate filename filename_str = @given_name +"."+ self.object_id.to_s # +"."+ @creation_time.to_s filename_str = filename_str.gsub(" ", "_") @filename = filename_str #why this again?!? end #init def labelize #sets storable's attribs for the purposes of labeling them @label = { # - object class :class => self.class.to_s, # - given name :given_name => @given_name, # - object id :object_id => self.object_id.to_s, # - time created :time_created => @creation_time.to_s, #seconds since epoch # - owner :user => "user", # - tags_str :tags => @tags.to_s, # - description_str :description => @description.to_s # - date now (filesystem?) } #Label packaged to a String -form @label_str = "" self.label.each_pair { |key, val| #each pair into string delimited by | label_str << key.to_s + "|" + val.gsub("|", "!").to_s + "\n" } end #end labelize def put_into_storage #marshals, labels and stores object in Storage #future args command_msg_str #set object label labelize() #marshal object package = Marshal.dump(self) #store label and object #resolve 'storage path' relative_path_str = "serialized." + self.class.to_s + "." + @filename.to_s #store label lbl_path = relative_path_str + ".label" Storage.store(lbl_path, @label_str) #store file Storage.store(relative_path_str, package) end def self.find_by_fname(fname) #not implemented found = nil ObjectSpace.each_object(Object) { |o| found = o if o.fname == fname } found end end #Class Storable # Storage.rb - datastore-module # - Storage abstraction 1st. Scalability 2nd. # - gold spike: store marshaled objects # - future store YML -objects in a namespace of some kind. # - future SQL #Roadmap # DB/QL support # I. Marshall objects # - serialize/re-instance # - namespace? # II. Yaml/JSON/XML # III. ODB? # IV. MySQL and so on # #Inteface Roadmap # * Make a module instead of inheritable?!? # * store.config. Configurable parameters. # * Store object. # * Retrieve object. # * Sort/Compare/etc require "singleton.rb" class Storage include Singleton #Instantiated with: #Storage = Storage.instance # #Requires: #writable storage_home_local_path -directory #Object to be stored is of class storable # #Usage: #Storage.store(object, path_str) # #Returns: #future: status code indicating the result #TODO move this to config-file @@storage_home_local_path = '/tmp/' attr_reader :storage_home_local_path ### Constructor ############################### def initialize() end #init def set_storage_configuration() end ### CLASS METHODS ############################# def self.store(relative_path_str, content_string) store_content_string(relative_path_str, content_string) end def self.store_content_string(relative_path_str, content_string) target_path = @@storage_home_local_path + relative_path_str #print target_path wf_io = File.open(target_path, "w") wf_io.write(content_string) unless wf_io.closed? #wf_io.flush() wf_io.close() end end def self.query(selector_str, keyword_str) case selector_str when "by_path" items = Dir[@@storage_home_local_path + keyword_str] labels = Dir[@@storage_home_local_path + keyword_str + ".label"] items = items-labels when "by_class" #etc etc end #case end def self.unpack(relative_path_or_filename_str) #returns unpacked stored object #resolve path relative_path_or_filename_str = relative_path_or_filename_str.gsub (@@storage_home_local_path, "") #remove path in case exists so no dbl path target_path = @@storage_home_local_path + relative_path_or_filename_str.to_s begin open(target_path) do |f| @loaded_object = Marshal.load(f) end rescue StandardError => crash print "Storage unpack: Standard Error opening file " print target_path.to_s + ", returned: " + crash + ". " end return @loaded_object end end #e Storage #Example code Storage.instance #a instance of the Storage class Tester < Storable def initialize(some_str) super() @stuff = Array.new 20.times { @stuff.push(some_str.to_s) } end attr_reader :stuff end this_test = Tester.new("Test for comp.lang.ruby") this_test.put_into_storage() print this_test = "blah" presumed_obj = Dir["/tmp/**unknown_object**"][0].to_s unserred = Storage.unpack(presumed_obj) print unserred.inspect() print unserred.stuff #??!?? #end END END Casimir Pohjanraito - Portfolio http://csmr.dreamhosters.com