Last active
December 11, 2015 14:29
-
-
Save fallengiants/4614254 to your computer and use it in GitHub Desktop.
I need to modify this thing badly, it's old and not good.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| =begin | |
| dex.rb: Dex Recursive Data Structure | |
| Functionality: | |
| 1. dx = Dex.new( hash of values OR a list of arguments to schematize- see #3) | |
| 2. dx.a.b.c = 5 | |
| [creates Dex children b and b.c and sets b.c to 5] | |
| 3. dx.agelimits.schema(21, :drinking_age, :adulthood) | |
| [creates Dex child agelimits, sets default value to 21, and creates | |
| agelimits.drinking_age and agelimits.adulthood] | |
| 4. dx.agelimits.alert(:change, :adulthood, '@data[:adulthood] = 21; parent.changed += 1') | |
| [adds a proc object equal to the eval'd code in arg[2] to either :change or :fetch | |
| as specified. Note that just setting 'adulthood=21' would trigger infinite recursion. | |
| This example resets the adulthood value to 21, then sets dx.changed to 1 plus its original | |
| value. Note also that dx.changed must already have a numeric value in this case, or you'll | |
| trigger an error.] | |
| 5. dx.save('filename') | |
| Saves as a Base64 encoded, marshalled file. Dex.load(filename) will | |
| reload the object. | |
| Dex instances duplicate the each_key, each, collect, select, and keys | |
| functionality of their @data hashes (more or less). | |
| =end | |
| require 'base64' | |
| class String | |
| def symbolize | |
| tr(' ','_').gsub(/\W/,'').intern | |
| end | |
| end | |
| class Symbol | |
| def symbolize; self; end | |
| def gsub(*args); id2name.gsub(*args).symbolize; end | |
| end | |
| class Object | |
| def meta; class << self; self; end; end | |
| end | |
| #Clean Version of Dex | |
| class Dex | |
| attr_accessor :data | |
| attr_accessor :filename | |
| attr_reader :parent | |
| def initialize(*hs) | |
| @parent = nil | |
| @data = Hash.new(nil) | |
| @alert = {:fetch => Hash.new(lambda{nil}), :change => Hash.new(lambda{nil})} | |
| if hs | |
| if hs[0].is_a?(Hash) | |
| hs[0].each_pair {|k,v| add(k,v)} | |
| else | |
| schema(hs) | |
| end | |
| end | |
| @filename = %Q{./._#{object_id}.dex} | |
| end | |
| def ==(obj); if obj.kind_of?(Dex) then @data == obj.data; end; end | |
| def add(k, v=@data.default) | |
| k = sym( k.to_s.gsub('=','')) | |
| unless self.respond_to?(k) | |
| meta.send(:define_method, k) {trigger(:fetch,k); @data[k]} | |
| meta.send(:define_method, (k.id2name+'=').intern) {|*x| @data[k]=x.shift; trigger(:change,k); @data[k]} | |
| end | |
| @data[k] = (v.kind_of?(Hash) ? Dex.new(v) : v) | |
| end | |
| def adopt(par) | |
| @parent = par | |
| end | |
| def alert(s,md, fn) | |
| alertintegrity | |
| @alert[sym(s)][sym(md)] = Proc.new { instance_eval fn } | |
| end | |
| def collect(&blk) | |
| rv = initialize_copy(@data) | |
| @data.each_pair do |k,v| | |
| k0,v0 = blk.call(k,v) rescue [k,v] | |
| unless k.is_a?(Symbol) && v0 | |
| v0,k0 = k0, k | |
| end | |
| rv[k1]=v1 | |
| end | |
| return rv | |
| end | |
| def collect!(&blk); @data = collect(&blk); end | |
| def default; @data.default; end | |
| def each(&blk); md(&blk); end | |
| def each_key(&blk); md(:each_key, &blk); end | |
| def keys; @data.keys; end | |
| def select(&blk); md(:select, &blk); end | |
| def initialize_copy(orig); super; @data=data.dup; end | |
| def Dex.load(fn); Marshal.load(Base64.decode64(IO.read(fn))); end | |
| def marshal_load(x); @data = x; md(:each_pair) {|k,v| add(k,v)}; end | |
| def method_missing(md, *args) | |
| if @data.has_key?(md) then @data[md].send(*args) | |
| elsif @data.respond_to?(md) then @data.send(md, *args) | |
| else | |
| @data[md] = create | |
| st = md.id2name =~ /=$/ | |
| md = md.gsub(/=/,'') if st | |
| add(md, args[0] || @data[md]) | |
| if st || args.empty? | |
| return @data[md] | |
| else | |
| return @data[md].send(*args) | |
| end | |
| end | |
| end | |
| def save(fn=nil) | |
| @filename = fn unless fn.nil? | |
| tmp = File.open(@filename, 'w') | |
| tmp.write(Base64.encode64(Marshal.dump(self))) | |
| tmp.close | |
| end | |
| def schema(*args) | |
| @data.default=args.shift | |
| args.each {|x| add(x)} | |
| end | |
| def set_default(x); @data.default=x; end | |
| def pass(*args) | |
| md = sym(args.shift) | |
| if data.has_key?(md) | |
| if args.empty? | |
| return @data[md] | |
| else | |
| args.unshift(:pass?) if @data[md].kind_of?(Dex) | |
| @data[md].send(*args) | |
| end | |
| else | |
| false | |
| end | |
| end | |
| alias_method :each_pair, :each | |
| alias_method :marshal_dump, :data | |
| private | |
| def create(*args) | |
| rv = Dex.new(*args) | |
| rv.adopt(self) | |
| return rv | |
| end | |
| def md(md=:each_pair, &blk) | |
| @data.method(md).call {|*x| blk.call(*x)} | |
| end | |
| def sym(x) | |
| x=x.symbolize | |
| raise "Symbol is empty" if x.nil? || x.to_s.length < 1 | |
| x | |
| end | |
| def trigger(sm, key) | |
| return nil unless key.respond_to?(:symbolize) | |
| alertintegrity | |
| key = sym(key) | |
| if @alert[sm].has_key?(key) | |
| @alert[sm][key].call | |
| end | |
| end | |
| def alertintegrity | |
| @alert ||= {} | |
| @alert[:fetch] ||= Hash.new(lambda {nil}) | |
| @alert[:change] ||= Hash.new(lambda {nil}) | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment