gxg-framework/lib/gxg/gxg_augmented.rb
2025-08-07 09:39:33 -07:00

4418 lines
No EOL
135 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# To overcome load-order issues:
module GxG
module Database
class Database
#
end
class PersistedHash
#
end
class PersistedArray
#
end
end
end
# ---------------------------------------------------------------------------------------------------------------------
# Additional alternations to Standard element classes: expanded functionality
# (Tier 1 Augmentation)
#
# Class extensions:
#
class Class
# Attribution : http://stackoverflow.com/questions/2393697/look-up-all-descendants-of-a-class-in-ruby
def descendants
result = []
ObjectSpace.each_object(::Class) do |the_class|
if the_class < self
result << the_class
end
end
result
end
#
end
# require facet classes here (prior to overrides), and ensure smooth integration with overrides.
#
# ### dup and clone support for Fixnum, Integer, Float, (and BigDecimal at some point) : don't even bother to tell me I gain nothing -- I've heard this before.
class NilClass
public
def initialize_clone
nil
end
alias :initialize_dup :initialize_clone
alias :dup :initialize_clone
def clone()
initialize_clone
end
end
#
#class FalseClass
# public
# def initialize_clone
# false
# end
# alias :initialize_dup :initialize_clone
# alias :dup :initialize_clone
# def clone()
# initialize_clone
# end
#end
#
#class TrueClass
# public
# def initialize_clone
# true
# end
# alias :initialize_dup :initialize_clone
# alias :dup :initialize_clone
# def clone()
# initialize_clone
# end
#end
#
class Float
public
def initialize_clone
(self + 0.0)
end
alias :initialize_dup :initialize_clone
alias :dup :initialize_clone
def clone()
initialize_clone
end
def to_d()
::BigDecimal.new(self.to_s)
end
end
#
class Symbol
def initialize_clone
(self.to_s.dup.to_sym)
end
alias :initialize_dup :initialize_clone
alias :dup :initialize_clone
def clone()
initialize_clone
end
end
#
class DateTime
def to_d()
self.to_time.to_d
end
end
#
class Time
def to_datetime()
::DateTime::parse(self.to_s)
end
def to_d()
self.to_f.to_d
end
end
#
# class Set
# public
# def initialize_clone
# # TODO: flesh this out to return a copy Set
# @self
# end
# alias :initialize_dup :initialize_clone
# alias :dup :initialize_clone
# def clone()
# initialize_clone
# end
# end
# IO/StringIO/File alterations
# Fill in missing constants according to platform.
# ### win32 stuff
#006 #define F_SETFL 1
#007 #define F_SETFD 2
#008 #define F_GETFL 3
#009
#010 #define _O_RDONLY 0x0000 /* open for reading only */
#011 #define _O_WRONLY 0x0001 /* open for writing only */
#012 #define _O_RDWR 0x0002 /* open for reading and writing */
#013
#014 #define _O_NONBLOCK 0x0004
#015
#016 #define _O_APPEND 0x0008 /* writes done at eof */
#017 #define _O_CREAT 0x0100 /* create and open file */
#018 #define _O_TRUNC 0x0200 /* open and truncate */
#019 #define _O_EXCL 0x0400 /* open only if file doesn't already exist */
#020 #define _O_TEXT 0x4000 /* file mode is text (translated) */
#021 #define _O_BINARY 0x8000 /* file mode is binary (untranslated) */
#022 #define _O_ACCMODE 0x10000
#023
#024 #define _O_NOINHERIT 0
#025 #define O_NOINHERIT _O_NOINHERIT
#026
#027 #define O_RDONLY _O_RDONLY
#028 #define O_WRONLY _O_WRONLY
#029 #define O_RDWR _O_RDWR
#030
#031 #define O_NONBLOCK _O_NONBLOCK
#032
#033 #define O_APPEND _O_APPEND
#034 #define O_CREAT _O_CREAT
#035 #define O_TRUNC _O_TRUNC
#036 #define O_EXCL _O_EXCL
#037 #define O_TEXT _O_TEXT
#038 #define O_BINARY _O_BINARY
#039 #define O_ACCMODE _O_ACCMODE
# ### end win32 stuff
# @@valid_modes[:flags][:io][:tty] = 0x00000010
# @@valid_modes[:flags][:io][:noctty] = ::IO::NOCTTY
# @@valid_modes[:flags][:io][:duplex] = 0x00000020
# @@valid_modes[:flags][:io][:append] = ::IO::APPEND
# @@valid_modes[:flags][:io][:create] = ::IO::CREAT
# @@valid_modes[:flags][:io][:exclusive] = ::IO::EXCL
# @@valid_modes[:flags][:io][:wsplit] = 0x00000200
# @@valid_modes[:flags][:io][:wsplit_initialized] = 0x00000400
# @@valid_modes[:flags][:io][:trunc] = ::IO::TRUNC
# @@valid_modes[:flags][:io][:text] = ::IO::TEXT
# @@valid_modes[:flags][:io][:setenc_by_bom] = 0x00100000
# See : http://boop.ankhcraft.com/library/ruby-api/dc/dac/io_8h.html
# Also : http://boop.ankhcraft.com/library/ruby-api/d2/d4d/file_8h.html
# also : http://boop.ankhcraft.com/library/ruby-api/d6/d13/file_8c.html
#
class IO
## TRACK: io.h
#define FMODE_READABLE 0x00000001
#@@valid_modes[:flags][:io][:read] = 0x00000001
#define FMODE_WRITABLE 0x00000002
#@@valid_modes[:flags][:io][:write] = 0x00000002
#define FMODE_READWRITE (FMODE_READABLE|FMODE_WRITABLE)
#@@valid_modes[:flags][:io][:readwrite] = (@@valid_modes[:flags][:io][:read] | @@valid_modes[:flags][:io][:write])
#define FMODE_BINMODE 0x00000004
#@@valid_modes[:flags][:io][:binary] = 0x00000004
#define FMODE_SYNC 0x00000008
#@@valid_modes[:flags][:io][:sync] = 0x00000008
#define FMODE_TTY 0x00000010
#@@valid_modes[:flags][:io][:tty] = 0x00000010
#define FMODE_DUPLEX 0x00000020
#@@valid_modes[:flags][:io][:duplex] = 0x00000020
#define FMODE_APPEND 0x00000040
#@@valid_modes[:flags][:io][:append] = 0x00000040
#define FMODE_CREATE 0x00000080
#@@valid_modes[:flags][:io][:create] = 0x00000080
#define FMODE_WSPLIT 0x00000200
# related to io object's buffer management (position offsets and buffer lengths)
#@@valid_modes[:flags][:io][:wsplit] = 0x00000200
#define FMODE_WSPLIT_INITIALIZED 0x00000400
#@@valid_modes[:flags][:io][:wsplit_initialized] = 0x00000400
#define FMODE_TRUNC 0x00000800
#@@valid_modes[:flags][:io][:trunc] = 0x00000800
#define FMODE_TEXTMODE 0x00001000
#@@valid_modes[:flags][:io][:text] = 0x00001000
#define FMODE_SETENC_BY_BOM 0x00100000
#@@valid_modes[:flags][:io][:setenc_by_bom] = 0x00100000
# [:SEEK_SET, :SEEK_CUR, :SEEK_END, :LOCK_SH, :LOCK_EX, :LOCK_UN, :LOCK_NB, :RDONLY, :WRONLY, :RDWR, :APPEND, :CREAT, :EXCL, :NONBLOCK, :TRUNC, :NOCTTY, :BINARY, :SYNC, :DSYNC, :RSYNC, :NOFOLLOW, :NOATIME, :FNM_NOESCAPE, :FNM_PATHNAME, :FNM_DOTMATCH, :FNM_CASEFOLD, :FNM_SYSCASE]
unless defined?(TTY)
# Note: need to verify this int value before using:
TTY = 0x00000010
end
unless defined?(DUPLEX)
# Note: need to verify this int value before using:
DUPLEX = 0x00000020
end
unless defined?(WSPLIT)
# Note: need to verify this int value before using:
WSPLIT = 0x00000200
end
# Not included in jruby
unless defined?(DSYNC)
# Note: need to verify this int value before using:
DSYNC = 4096
end
unless defined?(RSYNC)
# Note: need to verify this int value before using:
RSYNC = 1052672
end
unless defined?(NOATIME)
# Note: need to verify this int value before using:
NOATIME = 262144
end
#
# Not included in rbx
unless defined?(NOFOLLOW)
# Note: need to verify this int value before using:
NOFOLLOW = 131072
end
#
unless defined?(WSPLIT_INITIALIZED)
# Note: need to verify this int value before using:
WSPLIT_INITIALIZED = 0x00000400
end
unless defined?(TEXT)
# What is the flag value under Win32?
TEXT = 0
end
unless defined?(SETENC_BY_BOM)
# Note: need to verify this int value before using:
#SETENC_BY_BOM = 0x00100000
SETENC_BY_BOM = 0
end
#
def write_latency()
unless self.instance_variable_defined?(:@write_latency)
@write_latency= {:low => nil, :last => nil, :high => nil}
end
@write_latency.clone
end
#
def read_latency()
unless self.instance_variable_defined?(:@read_latency)
@read_latency= {:low => nil, :last => nil, :high => nil}
end
@read_latency.clone
end
#
def latency_reading(old_reading=nil,&block)
unless old_reading.is_a?(::Hash)
old_reading = {:low => nil, :last => nil, :high => nil}
end
new_reading = {:low => nil, :last => nil, :high => nil}
reading = millisecond_latency(&block)
if old_reading[:low]
if reading[:milliseconds] < old_reading[:low]
new_reading[:low] = reading[:milliseconds].dup
else
new_reading[:low] = old_reading[:low]
end
else
new_reading[:low] = reading[:milliseconds].dup
end
new_reading[:last] = reading[:milliseconds].dup
if old_reading[:high]
if reading[:milliseconds] > old_reading[:high]
new_reading[:high] = reading[:milliseconds].dup
else
new_reading[:high] = old_reading[:high]
end
else
new_reading[:high] = reading[:milliseconds].dup
end
#
{:result => reading[:result], :reading => new_reading}
end
#
def flags()
result = []
current = self.fcntl(::Fcntl::F_GETFL)
flags = {}
if self.binmode?()
result << :binary
if self.external_encoding == ::Encoding::ASCII_8BIT
result << :setenc_by_bom
end
else
result << :text
result << :wsplit
end
if self.tty?
result << :tty
end
#
#flag[:read] = 0x00000001
#flag[:write] = 0x00000002
#flag[:readwrite] = (flag[:read] | flag[:write])
#flag[:binmode] = 0x00000004
#flag[:readwrite] = (flag[:read] | flag[:write])
flags[:read] = ::Fcntl::O_RDONLY
flags[:write] = ::Fcntl::O_WRONLY
flags[:readwrite] = ::Fcntl::O_RDWR
#flags[:binary] = 0x00000004
flags[:sync] = ::File::SYNC # no internal buffering -- The file will be opened for synchronous I/O. No write operation will complete until the data has been physically written to disk.
flags[:dsync] = ::File::DSYNC # only normal data be synchronized after each write operation, not metadata.
flags[:rsync] = ::File::RSYNC # the synchronization of read requests as well as write requests. It must be used with one of IO::SYNC or IO::DSYNC
#flags[:tty] = 0x00000010
flags[:notcontrol_tty] = ::Fcntl::O_NOCTTY
flags[:duplex] = 0x00000020
flags[:append] = ::Fcntl::O_APPEND
flags[:create] = ::Fcntl::O_CREAT
flags[:exclusive] = ::Fcntl::O_EXCL
# flags[:wsplit] = 0x00000200
flags[:wsplit_initialized] = 0x00000400
flags[:trunc] = ::Fcntl::O_TRUNC
#flags[:text] = 0x00001000
#flags[:setenc_by_bom] = 0x00100000
flags[:seek_set] = ::File::SEEK_SET
flags[:seek_current] = ::File::SEEK_CUR
flags[:seek_end] = ::File::SEEK_END
# ### file region locking:
flags[:shared_lock] = ::File::LOCK_SH # for reading
flags[:exclusive_lock] = ::File::LOCK_EX # for writing (implies blocking)
flags[:nonblock_lock] = ::File::LOCK_NB # combine with exclusive_lock for immediate non-blocking lock for writing.
flags[:unlock] = ::File::LOCK_UN
#
flags[:nonblocking] = ::Fcntl::O_NONBLOCK
flags[:ndelay] = ::Fcntl::O_NDELAY
flags[:nofollow] = ::File::NOFOLLOW # Do not follow symlinks.
flags[:noaccesstime] = ::File::NOATIME # Do not update the access time (atime) of the file.
flags[:match_noescape] = ::File::FNM_NOESCAPE #
flags[:match_pathname] = ::File::FNM_PATHNAME #
flags[:match_dotmatch] = ::File::FNM_DOTMATCH #
flags[:match_casefold] = ::File::FNM_CASEFOLD #
flags[:match_systemcase] = ::File::FNM_SYSCASE #
#
flags.keys.to_enum.each do |the_flag_key|
if (current & flags[(the_flag_key)]) == flags[(the_flag_key)]
case the_flag_key
when :readwrite
if result.index(:write)
result << :read
else
if result.index(:read)
result << :write
end
end
when :wsplit_initialized
if result.index(:wsplit)
result[(result.index(:wsplit))] = :wsplit_initialized
else
result << :wsplit_initialized
end
else
result << the_flag_key
end
end
end
#
result
end
#
def profile(params={:follow_symlinks => true})
platform_details = GxG::SYSTEM.platform()
result = {}
#
if params[:follow_symlinks]
raw_stat = self.to_io.stat
else
raw_stat = self.to_io.lstat
end
raw_mode = raw_stat.mode.to_s(base=8)
if raw_mode.size < 7
raw_mode = ("0" << raw_mode)
end
set_user_id = false
set_group_id = false
set_sticky_bit = false
if (raw_mode[3].to_i & 4) == 4
set_user_id = true
end
if (raw_mode[3].to_i & 2) == 2
set_group_id = true
end
if (raw_mode[3].to_i & 1) == 1
set_sticky_bit = true
end
#
#
raw_permission = { :execute => false, :rename => false, :move => false, :destroy => false, :create => false, :write => false, :read => false }
#
file_handle = nil
GxG::Engine::file_descriptors.to_enum.each do |the_handle|
if self.fileno == the_handle.fileno
file_handle = the_handle
break
end
end
# LATER: GxG::IO::IO.profile : patch into file/stream type recognition engine.
case raw_stat.ftype.to_s.downcase.to_sym
when :link
# are .lnk files handled properly on win32 for this?
result[:type] = :symlink
when :file
if file_handle
if self.tty?
result[:type] = :tty
else
the_extension = ::File::basename(file_handle.to_path).split(".")[-1].to_s.downcase
if the_extension == "lnk"
result[:type] = :symlink
else
if ["so","la","a","dylib","dll","class"].include?(the_extension)
result[:type] = :library
else
if (["out","exe","misc","java","jar", "sh", "rb", "py", "pyc"].include?(the_extension) || raw_stat.executable_real?)
result[:type] = :application
else
result[:type] = :file
end
end
end
end
end
when :fifo
result[:type] = :fifo
when :characterspecial
result[:type] = :character_device
when :blockspecial
result[:type] = :block_device
when :socket
result[:type] = :socket
when :directory
result[:type] = :directory
when :unknown
result[:type] = :unknown
if raw_stat.pipe?
result[:type] = :pipe
end
else
if raw_stat.pipe?
result[:type] = :pipe
end
end
result[:owner_type] = result[:type]
result[:on_device] = raw_stat.dev.to_s(base=10).to_i
if result[:on_device]
result[:on_device_major] = raw_stat.dev_major
result[:on_device_minor] = raw_stat.dev_minor
end
result[:is_device] = raw_stat.rdev
if result[:is_device]
result[:is_device_major] = raw_stat.rdev_major
result[:is_device_minor] = raw_stat.rdev_minor
end
result[:inode] = raw_stat.ino.to_i
#
result[:flags] = self.flags()
#
if file_handle
# man fstat : mode decoder-ring table:
#
# S_IFMT 0170000 bit mask for the file type bit fields
# S_IFSOCK 0140000 socket
# S_IFLNK 0120000 symbolic link
# S_IFREG 0100000 regular file
# S_IFBLK 0060000 block device
# S_IFDIR 0040000 directory
# S_IFCHR 0020000 character device
# S_IFIFO 0010000 FIFO
#
# S_ISUID 0004000 set UID bit
# S_ISGID 0002000 set-group-ID bit (see below)
# S_ISVTX 0001000 sticky bit (see below)
#
# S_IRWXU 00700 mask for file owner permissions
# S_IRUSR 00400 owner has read permission
# S_IWUSR 00200 owner has write permission
# S_IXUSR 00100 owner has execute permission
#
# S_IRWXG 00070 mask for group permissions
# S_IRGRP 00040 group has read permission
# S_IWGRP 00020 group has write permission
# S_IXGRP 00010 group has execute permission
#
# S_IRWXO 00007 mask for permissions for others (not in group)
# S_IROTH 00004 others have read permission
# S_IWOTH 00002 others have write permission
# S_IXOTH 00001 others have execute permission
#
# <file>.stat table: (mode example : 0100664)
# 0700 rwx mask for owner
# 0400 r for owner
# 0200 w for owner
# 0100 x for owner
# 0070 rwx mask for group
# 0040 r for group
# 0020 w for group
# 0010 x for group
# 0007 rwx mask for other
# 0004 r for other
# 0002 w for other
# 0001 x for other
# 4000 Set user ID on execution
# 2000 Set group ID on execution
# 1000 Save swapped text, even after use
result[:hardlinks_to] = raw_stat.nlink.to_i
result[:user_id] = raw_stat.uid.to_i
result[:group_id] = raw_stat.gid.to_i
result[:size] = raw_stat.size
result[:block_size] = raw_stat.blksize
result[:blocks] = raw_stat.blocks
result[:accessed] = DateTime::parse(raw_stat.atime.to_s)
result[:modified] = DateTime::parse(raw_stat.mtime.to_s)
result[:status_modified] = DateTime::parse(raw_stat.ctime.to_s)
# File permissions work in an ACL style, even if based only upon Unix-style permissions.
#
result[:permissions] = {:effective => raw_permission.clone, :owner => raw_permission.clone, :group => raw_permission.clone, :other => raw_permission.clone}
if set_user_id
result[:flags] << :user_id_on_execute
end
if set_group_id
result[:flags] << :group_id_on_execute
end
if set_sticky_bit
result[:flags] << :sticky_bit
end
# owner permissions
if (raw_mode[4].to_i & 4) == 4
result[:permissions][:owner][:read] = true
end
if (raw_mode[4].to_i & 2) == 2
result[:permissions][:owner][:write] = true
if platform_details[:platform] == :windows
# :windows way of reading :rename, :move, and :destroy permission for file/directory.
else
result[:permissions][:owner][:rename] = true
result[:permissions][:owner][:move] = true
result[:permissions][:owner][:destroy] = true
result[:permissions][:owner][:create] = true
end
end
if (raw_mode[4].to_i & 1) == 1
result[:permissions][:owner][:execute] = true
end
# group permissions
if (raw_mode[5].to_i & 4) == 4
result[:permissions][:owner][:read] = true
end
if (raw_mode[5].to_i & 2) == 2
result[:permissions][:group][:write] = true
if platform_details[:platform] == :windows
# :windows way of reading :rename, :move, and :destroy permission for file/directory.
else
result[:permissions][:group][:rename] = true
result[:permissions][:group][:move] = true
result[:permissions][:group][:destroy] = true
result[:permissions][:group][:create] = true
end
end
if (raw_mode[5].to_i & 1) == 1
result[:permissions][:group][:execute] = true
end
# other permissions
if (raw_mode[6].to_i & 4) == 4
result[:permissions][:other][:read] = true
end
if (raw_mode[6].to_i & 2) == 2
result[:permissions][:other][:write] = true
if platform_details[:platform] == :windows
# :windows way of reading :rename, :move, and :destroy permission for file/directory.
else
result[:permissions][:other][:rename] = true
result[:permissions][:other][:move] = true
result[:permissions][:other][:destroy] = true
result[:permissions][:other][:create] = true
end
end
if (raw_mode[6].to_i & 1) == 1
result[:permissions][:other][:execute] = true
end
# effective (this user) permissions (while opened, supercedes normal effective file system permissions)
if result[:flags].index(:read)
result[:permissions][:effective][:read] = true
#
end
if result[:flags].index(:write)
result[:permissions][:effective][:write] = true
if platform_details[:platform] == :windows
# :windows way of reading :rename, :move, and :destroy permission for file/directory.
else
result[:permissions][:effective][:rename] = true
result[:permissions][:effective][:move] = true
result[:permissions][:effective][:destroy] = true
result[:permissions][:effective][:create] = true
end
end
if raw_stat.executable?
result[:permissions][:effective][:execute] = true
end
#
else
# in-memory object - no fs involvement
# ok ... why? Well, I want objects to be able to pass through my unwritten system stuff : experimental, w/o special handling.
result[:permissions] = {:effective => raw_permission.clone, :owner => raw_permission.clone, :group => raw_permission.clone, :other => raw_permission.clone}
#
if result[:flags].index(:read)
result[:permissions][:effective][:read] = true
result[:permissions][:owner][:read] = true
result[:permissions][:group][:read] = true
result[:permissions][:other][:read] = true
#
end
#
if result[:flags].index(:write)
result[:permissions][:effective][:write] = true
result[:permissions][:owner][:write] = true
result[:permissions][:group][:write] = true
result[:permissions][:other][:write] = true
end
#
end
#
result[:mode] = {}
#
result
end
#
def as_segments(segment_size=65536, as_bytearrays=false, &block)
result = []
unless self.closed?
if segment_size.is_any?(::TrueClass, ::FalseClass, NilClass)
as_bytearrays = segment_size
segment_size = 65536
end
saved_position = self.pos
self.seek(0, :END)
current_size = self.pos + 1
#
self.rewind
if block.respond_to?(:call)
GxG::apportioned_ranges(current_size, segment_size).each do |portion_range|
self.seek(portion_range.first)
if as_bytearrays
block.call(::GxG::ByteArray.new(self.read(portion_range.size)))
else
block.call(self.ead(portion_range.size))
end
end
else
GxG::apportioned_ranges(current_size, segment_size).each do |portion_range|
self.seek(portion_range.first)
if as_bytearrays
result << ::GxG::ByteArray.new(self.read(portion_range.size))
else
result << self.read(portion_range.size)
end
end
end
#
self.pos = saved_position
end
result
end
end
#
class StringIO
#
public
#
def binmode?()
if self.external_encoding == ::Encoding::ASCII_8BIT
true
else
false
end
end
#
def binmode()
# Question: ok, so the cosmetics are that it is in binmode ... but what will be its actual behavior??
if self.binmode?()
self
else
self.set_encoding(::Encoding::ASCII_8BIT)
end
end
#
def write_latency()
# Just in StringIO for interface compatibility.
unless self.instance_variable_defined?(:@write_latency)
@write_latency= {:low => nil, :last => nil, :high => nil}
end
@write_latency.clone
end
#
def read_latency()
# Just in StringIO for interface compatibility.
unless self.instance_variable_defined?(:@read_latency)
@read_latency= {:low => nil, :last => nil, :high => nil}
end
@read_latency.clone
end
#
def latency_reading(old_reading=nil,&block)
# Just in StringIO for interface compatibility.
unless old_reading.is_a?(::Hash)
old_reading = {:low => nil, :last => nil, :high => nil}
end
new_reading = {:low => nil, :last => nil, :high => nil}
reading = millisecond_latency(&block)
if old_reading[:low]
if reading[:milliseconds] < old_reading[:low]
new_reading[:low] = reading[:milliseconds].dup
else
new_reading[:low] = old_reading[:low]
end
else
new_reading[:low] = reading[:milliseconds].dup
end
new_reading[:last] = reading[:milliseconds].dup
if old_reading[:high]
if reading[:milliseconds] > old_reading[:high]
new_reading[:high] = reading[:milliseconds].dup
else
new_reading[:high] = old_reading[:high]
end
else
new_reading[:high] = reading[:milliseconds].dup
end
#
{:result => reading[:result], :reading => new_reading}
end
#
def fcntl(*args)
if args[0] == ::Fcntl::F_GETFL
current = ::Fcntl::O_NONBLOCK
if self.closed_read?()
unless self.closed_write?()
current = (current | ::Fcntl::O_WRONLY)
end
else
if self.closed_write?()
current = (current | ::Fcntl::O_RDONLY)
else
current = (current | ::Fcntl::O_RDWR)
end
end
current
else
0
end
end
#
def flags()
result = []
current = self.fcntl(::Fcntl::F_GETFL)
flags = {}
if self.binmode?()
result << :binary
if self.external_encoding == ::Encoding::ASCII_8BIT
result << :setenc_by_bom
end
else
result << :text
result << :wsplit
end
if self.tty?
result << :tty
end
#
#flag[:read] = 0x00000001
#flag[:write] = 0x00000002
#flag[:readwrite] = (flag[:read] | flag[:write])
#flag[:binmode] = 0x00000004
#flag[:readwrite] = (flag[:read] | flag[:write])
flags[:read] = ::Fcntl::O_RDONLY
flags[:write] = ::Fcntl::O_WRONLY
flags[:readwrite] = ::Fcntl::O_RDWR
#flags[:binary] = 0x00000004
flags[:sync] = ::File::SYNC # no internal buffering -- The file will be opened for synchronous I/O. No write operation will complete until the data has been physically written to disk.
flags[:dsync] = ::File::DSYNC # only normal data be synchronized after each write operation, not metadata.
flags[:rsync] = ::File::RSYNC # the synchronization of read requests as well as write requests. It must be used with one of IO::SYNC or IO::DSYNC
#flags[:tty] = 0x00000010
flags[:notcontrol_tty] = ::Fcntl::O_NOCTTY
flags[:duplex] = 0x00000020
flags[:append] = ::Fcntl::O_APPEND
flags[:create] = ::Fcntl::O_CREAT
flags[:exclusive] = ::Fcntl::O_EXCL
# flags[:wsplit] = 0x00000200
flags[:wsplit_initialized] = 0x00000400
flags[:trunc] = ::Fcntl::O_TRUNC
#flags[:text] = 0x00001000
#flags[:setenc_by_bom] = 0x00100000
flags[:seek_set] = ::File::SEEK_SET
flags[:seek_current] = ::File::SEEK_CUR
flags[:seek_end] = ::File::SEEK_END
# ### file region locking:
flags[:shared_lock] = ::File::LOCK_SH # for reading
flags[:exclusive_lock] = ::File::LOCK_EX # for writing (implies blocking)
flags[:nonblock_lock] = ::File::LOCK_NB # combine with exclusive_lock for immediate non-blocking lock for writing.
flags[:unlock] = ::File::LOCK_UN
#
flags[:nonblocking] = ::Fcntl::O_NONBLOCK
flags[:ndelay] = ::Fcntl::O_NDELAY
# these do not apply to in-memory IO-like things at all:
# flags[:nofollow] = ::File::NOFOLLOW # Do not follow symlinks.
# flags[:noaccesstime] = ::File::NOATIME # Do not update the access time (atime) of the file.
# flags[:match_noescape] = ::File::FNM_NOESCAPE #
# flags[:match_pathname] = ::File::FNM_PATHNAME #
# flags[:match_dotmatch] = ::File::FNM_DOTMATCH #
# flags[:match_casefold] = ::File::FNM_CASEFOLD #
# flags[:match_systemcase] = ::File::FNM_SYSCASE #
#
flags.keys.to_enum.each do |the_flag_key|
if (current & flags[(the_flag_key)]) == flags[(the_flag_key)]
case the_flag_key
when :readwrite
if result.index(:write)
result << :read
else
if result.index(:read)
result << :write
end
end
when :wsplit_initialized
if result.index(:wsplit)
result[(result.index(:wsplit))] = :wsplit_initialized
else
result << :wsplit_initialized
end
else
result << the_flag_key
end
end
end
#
result
end
#
def stat(*args)
nil
end
alias :lstat :stat
#
def profile()
current = self.flags()
result = {:type => :memory, :flags => current, :permissions => {:effective=>{:execute=>false, :rename=>false, :move=>false, :destroy=>false, :create=>false, :write=>false, :read=>false}}}
#
if current.include?(:read)
result[:permissions][:effective][:read] = true
end
if current.include?(:write)
result[:permissions][:effective][:write] = true
result[:permissions][:effective][:destroy] = true
result[:permissions][:effective][:create] = true
end
#
result
end
#
end
#
class File
# [:SEPARATOR, :ALT_SEPARATOR, :PATH_SEPARATOR, :Stat,, :SEEK_SET, :SEEK_CUR, :SEEK_END, :LOCK_SH, :LOCK_EX, :LOCK_UN, :LOCK_NB, :RDONLY, :WRONLY, :RDWR, :APPEND, :CREAT, :EXCL, :NONBLOCK, :TRUNC, :NOCTTY, :BINARY, :SYNC, :DSYNC, :RSYNC, :NOFOLLOW, :NOATIME, :FNM_NOESCAPE, :FNM_PATHNAME, :FNM_DOTMATCH, :FNM_CASEFOLD, :FNM_SYSCASE]
unless defined?(TTY)
TTY = ::IO::TTY
end
unless defined?(DUPLEX)
DUPLEX = ::IO::DUPLEX
end
unless defined?(WSPLIT)
WSPLIT = ::IO::WSPLIT
end
unless defined?(WSPLIT_INITIALIZED)
WSPLIT_INITIALIZED = ::IO::WSPLIT_INITIALIZED
end
unless defined?(TEXT)
# What is the flag value under Win32?
TEXT = 0
end
unless defined?(SETENC_BY_BOM)
# Note: need to verify ::IO::SETENC_BY_BOM value before using:
SETENC_BY_BOM = ::IO::SETENC_BY_BOM
end
#
def self.find_file_mimetype(the_file=nil)
if the_file.is_a?(::String)
if (/.*(?:\\|\/)(.+)$/.match(the_file.to_s) || the_file.to_s.split("/").size > 0 || the_file == "/")
if ::File.directory?(the_file)
raise ArgumentError, "You tried to look at a directory, not a File. (#{the_file.inspect})."
else
the_file = ::File.open(the_file, "r")
end
else
raise ArgumentError, "You MUST provide a valid File path."
end
end
if the_file.is_a?(::File)
the_file.file_type()
else
raise ArgumentError, "You MUST provide a valid File object."
end
end
#
def self.mode_permission_to_gxg(the_mode=0)
result = {}
if the_mode.is_a?(::Numeric)
raw_mode = the_mode.to_s(base=8)
set_user_id = false
set_group_id = false
set_sticky_bit = false
while raw_mode.size < 4
raw_mode = ("0" << raw_mode)
end
if raw_mode.size > 4
raw_mode = raw_mode[((raw_mode.size - 4)..-1)].to_s
end
#
if (raw_mode[0].to_i & 4) == 4
set_user_id = true
end
if (raw_mode[0].to_i & 2) == 2
set_group_id = true
end
if (raw_mode[0].to_i & 1) == 1
set_sticky_bit = true
end
raw_permission = { :execute => false, :rename => false, :move => false, :destroy => false, :create => false, :write => false, :read => false }
result = {:effective => nil, :owner => raw_permission.clone, :group => raw_permission.clone, :other => raw_permission.clone}
#
# puts "using: #{raw_mode.inspect}"
# owner permissions
if (raw_mode[1].to_i & 4) == 4
result[:owner][:read] = true
end
if (raw_mode[1].to_i & 2) == 2
result[:owner][:write] = true
result[:owner][:rename] = true
result[:owner][:move] = true
result[:owner][:destroy] = true
result[:owner][:create] = true
end
if (raw_mode[1].to_i & 1) == 1
result[:owner][:execute] = true
end
# group permissions
if (raw_mode[2].to_i & 4) == 4
result[:group][:read] = true
end
if (raw_mode[2].to_i & 2) == 2
result[:group][:write] = true
result[:group][:rename] = true
result[:group][:move] = true
result[:group][:destroy] = true
result[:group][:create] = true
end
if (raw_mode[2].to_i & 1) == 1
result[:group][:execute] = true
end
# other permissions
if (raw_mode[3].to_i & 4) == 4
result[:other][:read] = true
end
if (raw_mode[3].to_i & 2) == 2
result[:other][:write] = true
result[:other][:rename] = true
result[:other][:move] = true
result[:other][:destroy] = true
result[:other][:create] = true
end
if (raw_mode[3].to_i & 1) == 1
result[:other][:execute] = true
end
else
raise ArgumentError, "You MUST provide an Integer"
end
result
end
#
def self.mode_permission_to_unix(the_mode=0, type = :file)
result = "----------"
if the_mode.is_a?(::Numeric)
accumulator = ""
raw_mode = the_mode.to_s(base=8)
if raw_mode.size < 7
raw_mode = ("0" << raw_mode)
end
set_user_id = false
set_group_id = false
set_sticky_bit = false
if (raw_mode[3].to_i & 4) == 4
set_user_id = true
end
if (raw_mode[3].to_i & 2) == 2
set_group_id = true
end
if (raw_mode[3].to_i & 1) == 1
set_sticky_bit = true
end
#
case type.to_s.to_sym
when :directory
accumulator << "d"
when :symlink
accumulator << "l"
when :file
accumulator << "-"
else
raise ArgumentError, "You MUST provide a valid file system entity type: :file, :directory or :symlink."
end
# owner permissions
if (raw_mode[1].to_i & 4) == 4
accumulator << "r"
else
accumulator << "-"
end
if (raw_mode[1].to_i & 2) == 2
accumulator << "w"
else
accumulator << "-"
end
if (raw_mode[1].to_i & 1) == 1
accumulator << "x"
else
accumulator << "-"
end
# group permissions
if (raw_mode[2].to_i & 4) == 4
accumulator << "r"
else
accumulator << "-"
end
if (raw_mode[2].to_i & 2) == 2
accumulator << "w"
else
accumulator << "-"
end
if (raw_mode[2].to_i & 1) == 1
accumulator << "x"
else
accumulator << "-"
end
# other permissions
if (raw_mode[3].to_i & 4) == 4
accumulator << "r"
else
accumulator << "-"
end
if (raw_mode[3].to_i & 2) == 2
accumulator << "w"
else
accumulator << "-"
end
if (raw_mode[3].to_i & 1) == 1
accumulator << "x"
else
accumulator << "-"
end
result = accumulator
else
raise ArgumentError, "You MUST provide an Integer"
end
result
end
#
def self.gxg_permissions_to_unix(permissions={:owner => { :execute => false, :rename => false, :move => false, :destroy => false, :create => false, :write => false, :read => false }, :group => { :execute => false, :rename => false, :move => false, :destroy => false, :create => false, :write => false, :read => false }, :other => { :execute => false, :rename => false, :move => false, :destroy => false, :create => false, :write => false, :read => false }}, type = :file)
result = "----------"
if permissions.is_a?(::Hash)
accumulator = ""
raw_data = "----------"
write_permissions = [:write, :create, :destroy, :rename, :move]
case type
when :directory
accumulator << "d"
when :symlink
accumulator << "l"
when :file
accumulator << "-"
else
raise ArgumentError, "You MUST provide a valid file system entity type: :file, :directory or :symlink."
end
raw_data.each_char_with_index do |character, index|
case index
when 1
if permissions[:owner][:read]
accumulator << "r"
else
accumulator << character
end
when 2
write_permissions.each do |the_perm|
if permissions[:owner][(the_perm)]
accumulator << "w"
break
end
end
if accumulator[-1] != "w"
accumulator << character
end
when 3
if permissions[:owner][:execute]
accumulator << "x"
else
accumulator << character
end
when 4
if permissions[:group][:read]
accumulator << "r"
else
accumulator << character
end
when 5
write_permissions.each do |the_perm|
if permissions[:group][(the_perm)]
accumulator << "w"
break
end
end
if accumulator[-1] != "w"
accumulator << character
end
when 6
if permissions[:group][:execute]
accumulator << "x"
else
accumulator << character
end
when 7
if permissions[:other][:read]
accumulator << "r"
else
accumulator << character
end
when 8
write_permissions.each do |the_perm|
if permissions[:other][(the_perm)]
accumulator << "w"
break
end
end
if accumulator[-1] != "w"
accumulator << character
end
when 9
if permissions[:other][:execute]
accumulator << "x"
else
accumulator << character
end
end
end
result = accumulator
else
raise ArgumentError, "You MUST provide a Hash with :owner, :group, and :other permission sub-hashes of true/false flags"
end
result
end
#
def self.gxg_permissions_to_mode(permissions={:owner => { :execute => false, :rename => false, :move => false, :destroy => false, :create => false, :write => false, :read => false }, :group => { :execute => false, :rename => false, :move => false, :destroy => false, :create => false, :write => false, :read => false }, :other => { :execute => false, :rename => false, :move => false, :destroy => false, :create => false, :write => false, :read => false }}, type = :file)
result = 0
if permissions.is_a?(::Hash)
octet = 0
octet_string = "0"
unix_permissions = ::File.gxg_permissions_to_unix(permissions,type)
#
unix_permissions.each_char_with_index do |character, index|
#
if (1..3).include?(index)
case index
when 1
unless character == "-"
octet += 4
end
when 2
unless character == "-"
octet += 2
end
when 3
unless character == "-"
octet += 1
end
end
if index == 3
octet_string << octet.to_s
octet = 0
end
end
#
if (4..6).include?(index)
case index
when 4
unless character == "-"
octet += 4
end
when 5
unless character == "-"
octet += 2
end
when 6
unless character == "-"
octet += 1
end
end
if index == 6
octet_string << octet.to_s
octet = 0
end
end
#
if (7..9).include?(index)
case index
when 7
unless character == "-"
octet += 4
end
when 8
unless character == "-"
octet += 2
end
when 9
unless character == "-"
octet += 1
end
end
if index == 9
octet_string << octet.to_s
octet = 0
end
end
#
result = octet_string.to_i(base=8)
end
#
else
raise ArgumentError, "You MUST provide a Hash with :owner, :group, and :other permission sub-hashes of true/false flags"
end
result
end
#
def self.unix_permissions_to_gxg(permission_string="")
raw_permission = { :execute => false, :rename => false, :move => false, :destroy => false, :create => false, :write => false, :read => false }
blank_permissions = {:effective => nil, :owner => raw_permission.clone, :group => raw_permission.clone, :other => raw_permission.clone}
permission_record = blank_permissions.clone
#
if permission_string.is_a?(::String)
permission_string.each_char_with_index do |character, index|
#
case index
when 1
if character == "r"
permission_record[:owner][:read] = true
end
when 2
if character == "w"
permission_record[:owner][:write] = true
permission_record[:owner][:rename] = true
permission_record[:owner][:move] = true
permission_record[:owner][:destroy] = true
permission_record[:owner][:create] = true
end
when 3
if character == "x"
permission_record[:owner][:execute] = true
end
when 4
if character == "r"
permission_record[:group][:read] = true
end
when 5
if character == "w"
permission_record[:group][:write] = true
permission_record[:group][:rename] = true
permission_record[:group][:move] = true
permission_record[:group][:destroy] = true
permission_record[:group][:create] = true
end
when 6
if character == "x"
permission_record[:group][:execute] = true
end
when 7
if character == "r"
permission_record[:other][:read] = true
end
when 8
if character == "w"
permission_record[:other][:write] = true
permission_record[:other][:rename] = true
permission_record[:other][:move] = true
permission_record[:other][:destroy] = true
permission_record[:other][:create] = true
end
when 9
if character == "x"
permission_record[:other][:execute] = true
end
end
# No way to calculate Effective Permissions - it will be nil consequently.
#
end
result = permission_record
else
result = nil
raise ArgumentError, "You MUST supply a valid unix permission String"
end
result
end
#
def file_type()
# TODO: return GxG-UFS value as well.
type = ::MimeMagic.by_path(self.path)
{:mime_type => type.type(), :subtype => type.subtype(), :mediatype => type.mediatype(), :extensions => type.extensions(), :comment => type.comment(), :ufs => nil}
end
#
end
#
module Fcntl
# [:F_DUPFD, :F_GETFD, :F_GETLK, :F_SETFD, :F_GETFL, :F_SETFL, :F_SETLK, :F_SETLKW, :FD_CLOEXEC, :F_RDLCK, :F_UNLCK, :F_WRLCK, :O_CREAT, :O_EXCL, :O_NOCTTY, :O_TRUNC, :O_APPEND, :O_NONBLOCK, :O_NDELAY, :O_RDONLY, :O_RDWR, :O_WRONLY, :O_ACCMODE]
unless const_defined?(:O_TEXT)
O_TEXT = ::File::TEXT
end
unless const_defined?(:O_BINARY)
O_BINARY = ::File::BINARY
end
end
# ### URI alterations
# module URI
# #
# def self.parse(*args)
# #::URI::Generic.new(*(::URI::split(*args)))
# ::URI::Generic::parse(*args)
# end
# #
# class Generic
# #
# def set_scheme(v)
# if v.is_a?(String)
# @scheme = v
# end
# v
# end
# def scheme=(scheme="")
# set_scheme(scheme)
# scheme
# end
# def scheme()
# @scheme
# end
# def set_userinfo(user, password = nil)
# unless password
# user, password = split_userinfo(user)
# end
# if user.is_a?(String)
# @user = URI::escape(user)
# else
# @user = user
# end
# if password.is_a?(String)
# @password = URI::escape(password)
# else
# @password = password
# end
# result = []
# if @user.is_a?(String)
# result << URI::unescape(@user)
# else
# result << @user
# end
# if @password.is_a?(String)
# result << URI::unescape(@password)
# else
# result << @password
# end
# result
# end
# def userinfo
# if @user.nil?
# nil
# elsif @password.nil?
# URI::unescape(@user)
# else
# URI::unescape(@user) + ':' + URI::unescape(@password)
# end
# end
# def user=(user)
# if user.is_a?(String)
# user = (URI::escape(user))
# end
# set_user(user)
# self.user
# # returns user
# end
# def user
# if @user.is_a?(String)
# URI::unescape(@user)
# else
# @user
# end
# end
# def set_password(v)
# if v.is_a?(String)
# @password = URI::escape(v)
# end
# if @password.is_a?(String)
# URI::unescape(@password)
# else
# @password
# end
# end
# def password=(password)
# if password.is_a?(String)
# password = (URI::escape(password))
# end
# set_password(password)
# # returns password
# end
# def password
# if @password.is_a?(String)
# URI::unescape(@password)
# else
# @password
# end
# end
# def set_host(v)
# if v.is_a?(String)
# @host = URI::escape(v)
# else
# @host = v
# end
# end
# #
# def host=(v)
# if v.is_a?(String)
# v = (URI::escape(v))
# end
# set_host(v)
# self.host
# end
# #
# def host()
# if @host.is_a?(String)
# URI::unescape(@host)
# else
# @host
# end
# end
# #
# def hostname()
# if @host.to_s.size > 0
# self.host().to_s.dup.gsub("[","").gsub("]","")
# else
# nil
# end
# end
# #
# def hostname=(v)
# if (v.to_s.include?(":") || (v.to_s.include?("[") && v.to_s.include?("]")))
# # ipv6 ??
# if v.to_s.include?("[") && v.to_s.include?("]")
# self.host = v.to_s
# else
# self.host = "[" << v.to_s << "]"
# end
# else
# self.host = v.to_s
# end
# self.host()
# end
# #
# # def set_registry(v)
# # if v.is_a?(String)
# # @registry = URI::escape(v)
# # else
# # @registry = v
# # end
# # end
# # #
# # def registry=(v)
# # if v.is_a?(String)
# # v = (URI::escape(v))
# # end
# # set_registry(v)
# # self.registry
# # end
# # def registry
# # if @registry.is_a?(String)
# # URI::unescape(@registry)
# # else
# # @registry
# # end
# # end
# # def set_path(v)
# # if v.is_a?(String)
# # @path = URI::escape(v)
# # else
# # @path = v
# # end
# # end
# # def path=(v)
# # if v.is_a?(String)
# # v = (URI::escape(v))
# # end
# # set_path(v)
# # self.path
# # end
# # def path
# # if @path.is_a?(String)
# # URI::unescape(@path)
# # else
# # @path
# # end
# # end
# # def set_opaque(v)
# # if v.is_a?(String)
# # @opaque = URI::escape(v)
# # else
# # @opaque = v
# # end
# # end
# # def opaque=(v)
# # if v.is_a?(String)
# # v = (URI::escape(v))
# # end
# # set_opaque(v)
# # self.opaque
# # end
# # def opaque
# # if @opaque.is_a?(String)
# # URI::unescape(@opaque)
# # else
# # @opaque
# # end
# # end
# # Review - query modifications bugger MatrixClient logins.
# # def set_query(v)
# # if v.is_a?(String)
# # @query = URI::escape(v)
# # else
# # @query = v
# # end
# # end
# # def query=(v)
# # if v.is_a?(String)
# # v = (URI::escape(v))
# # end
# # set_query(v)
# # self.query
# # end
# # def query
# # if @query.is_a?(String)
# # URI::unescape(@query)
# # else
# # @query
# # end
# # end
# # def set_fragment(v)
# # if v.is_a?(String)
# # @fragment = URI::escape(v)
# # else
# # @fragment = v
# # end
# # end
# # def fragment=(v)
# # if v.is_a?(String)
# # v = (URI::escape(v))
# # end
# # set_fragment(v)
# # self.fragment
# # end
# # def fragment
# # if @fragment.is_a?(String)
# # URI::unescape(@fragment)
# # else
# # @fragment
# # end
# # end
# #
# # def to_s
# # str = ''
# # if @scheme
# # str << @scheme
# # str << ':'
# # end
# # if @opaque
# # str << @opaque
# # else
# # if @registry
# # str << @registry
# # else
# # if @host
# # str << '//'
# # end
# # if self.userinfo
# # str << self.userinfo
# # str << '@'
# # end
# # if @host
# # str << @host
# # end
# # if @port && @port != self.default_port
# # str << ':'
# # str << @port.to_s
# # end
# # end
# # str << @path
# # if @query
# # str << '?'
# # str << @query
# # end
# # end
# # if @fragment
# # str << '#'
# # str << @fragment
# # end
# # URI::escape(str)
# # end
# #
# def from_hash(the_hash={})
# if the_hash[:scheme]
# self.scheme = the_hash[:scheme]
# else
# @scheme = nil
# end
# if the_hash[:opaque]
# self.opaque = the_hash[:opaque]
# @registry = nil
# @user = nil
# @password = nil
# @host = nil
# @port = nil
# @path = nil
# @query = nil
# else
# @opaque = nil
# if the_hash[:registry]
# self.registry = the_hash[:registry]
# @user = nil
# @password = nil
# @host = nil
# @port = nil
# else
# @registry = nil
# if the_hash[:user]
# self.user = the_hash[:user]
# else
# @user = nil
# end
# if the_hash[:password]
# self.password = the_hash[:password]
# else
# @password = nil
# end
# if the_hash[:host]
# self.host = the_hash[:host]
# else
# @host = nil
# end
# if the_hash[:port] && the_hash[:port] != self.default_port
# self.port = the_hash[:port].to_i
# else
# @port = nil
# end
# end
# if the_hash[:path]
# self.path = the_hash[:path]
# else
# @path = nil
# end
# if the_hash[:query]
# self.query = the_hash[:query]
# else
# @query = nil
# end
# end
# if the_hash[:fragment]
# self.fragment = the_hash[:fragment]
# else
# @fragment = nil
# end
# self
# end
# #
# def to_hash()
# result = {}
# if @scheme
# result[:scheme] = @scheme
# end
# if @opaque
# result[:opaque] = URI::unescape(@opaque)
# else
# if @registry
# result[:registry] = URI::unescape(@registry)
# else
# if @user
# result[:user] = URI::unescape(@user)
# end
# if @password
# result[:password] = URI::unescape(@password)
# end
# if @host
# result[:host] = URI::unescape(@host)
# end
# if @port && @port != self.default_port
# result[:port] = URI::unescape(@port).to_i
# end
# end
# if @path
# result[:path] = URI::unescape(@path)
# end
# if @query
# result[:query] = URI::unescape(@query)
# end
# end
# if @fragment
# result[:fragment] = URI::unescape(@fragment)
# end
# result
# end
# #
# def address_info()
# ::Addrinfo::parse(self)
# end
# #
# def resolve_host()
# case @scheme
# when "inproc"
# # no-op
# when "ipc", "unix"
# # no-op
# when "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6"
# self.hostname = ::TCPSocket.getaddress(self.hostname)
# end
# end
# #
# end
# def self.parse(uri)
# # Alterations:
# # leading/trailing whitespace removal - intended to reduce false exception conditions (missing regexp bits)
# # I think first reported in 2007, but still not fixed. dunno why not.
# while uri[0] == 32
# uri[0] = ""
# end
# if uri.size > 0
# while uri[-1] == 32
# uri[-1] = ""
# end
# end
# # attempt to escape all strings going INTO the URI object?
# uri = URI::escape(uri)
# # End Alterations
# scheme, userinfo, host, port,
# registry, path, opaque, query, fragment = self.split(uri)
# if scheme && @@schemes.include?(scheme.upcase)
# @@schemes[scheme.upcase].new(scheme, userinfo, host, port,
# registry, path, opaque, query,
# fragment)
# else
# Generic.new(scheme, userinfo, host, port,
# registry, path, opaque, query,
# fragment)
# end
# end
# end
#
class Addrinfo
#
def self.parse(*args)
# Attempts to create an address object for the given URI/URL
address = nil
if args[0].is_a?(::URI::Generic)
raw_uri = args[0]
else
raw_uri = ::URI::parse(*args)
end
#
the_protocol = nil
the_port = nil
service_details = ::GxG::SYSTEM.service_ports_search({:scheme => raw_uri.scheme()})
if service_details.keys().size > 0
service_details = service_details[(service_details.keys()[0])]
if service_details[:usage_preference]
if service_details[:usage][(service_details[:usage_preference])].is_a?(::Array)
the_port = service_details[:usage][(service_details[:usage_preference])][0]
if the_port.is_a?(::Range)
the_protocol = (service_details[:usage_preference])
the_port = the_port.first
end
end
else
if service_details[:usage][(service_details[:usage].keys()[0])].is_a?(::Array)
the_port = service_details[:usage][(service_details[:usage].keys()[0])][0]
if the_port.is_a?(::Range)
the_protocol = (service_details[:usage].keys()[0])
the_port = the_port.first
end
end
end
end
unless the_protocol
the_protocol = raw_uri.scheme().to_sym
end
#
case the_protocol
when :inproc
#
when :ipc
# LATER: Anything to be done for IPC stuff here??? (Not available on winderz)
when :pgm
#
when :epgm
#
when :unix, :socket
if (raw_uri.hostname().to_s.size == 0)
# Unix socket
address = ::Addrinfo.unix(raw_uri.path())
else
# tcp used by default for ipc
if raw_uri.hostname().to_s.size > 0
address = ::Addrinfo.tcp(raw_uri.hostname(), (raw_uri.port() || raw_uri.default_port() || the_port || "*"))
else
address = ::Addrinfo.tcp("0.0.0.0", (raw_uri.port() || raw_uri.default_port() || the_port || "*"))
end
end
when :tcp, :tcp4, :tcp6
if raw_uri.hostname().to_s.size > 0
address = ::Addrinfo.tcp(raw_uri.hostname(), (raw_uri.port() || raw_uri.default_port() || the_port || "*"))
else
address = ::Addrinfo.tcp("0.0.0.0", (raw_uri.port() || raw_uri.default_port() || the_port || "*"))
end
when :udp, :udp4, :udp6
if raw_uri.hostname().to_s.size > 0
address = ::Addrinfo.udp(raw_uri.hostname(), (raw_uri.port() || raw_uri.default_port() || the_port || "*"))
else
address = ::Addrinfo.udp("0.0.0.0", (raw_uri.port() || raw_uri.default_port() || the_port || "*"))
end
end
#
address
end
#
def uri_info()
result = nil
if self.ip?()
case self.protocol
when ::Socket::IPPROTO_TCP
result = ::URI::parse("tcp://#{self.ip_address()}:#{(self.ip_port() || '*')}")
when ::Socket::IPPROTO_UDP
result = ::URI::parse("udp://#{self.ip_address()}:#{(self.ip_port() || '*')}")
end
else
if self.unix?()
result = ::URI::parse("unix://#{self.unix_path()}")
else
# not sure what to do here
end
end
result
end
end
#
# Alteration to <string> class
class String
# :macos text handling notes: http://hints.macworld.com/article.php?story=20060825071728278
# Also, research UTF8_MAC <-> UTF8_HFS <-> UTF8 normalization conversions.
def self.transcode_options(destination_encoding, source_encoding, options={})
# TODO: ::String::transcode_options : complete transcoding back-end supports.
# select a default encoding_option set given the two encodings, then merge options over that and return. Explore Hash deep-merge ideas.
#
new_options = {}
# fallback set/method or replace char
if options[:fallback]
# Note: I *confirmed* (thanks VGoff) that :invalid/:undef/:replace and :fallback are exclusive - So maybe this is really an optimized :fallback thing?
# Sets the replacement string by the given object for undefined character. The object should be a Hash, a Proc, a Method,
# or an object which has [] method. Its key is an undefined character encoded in the source encoding of current transcoder.
# Its value can be any encoding until it can be converted into the destination encoding of the transcoder.
if (options[:fallback].is_any?(::Hash, ::Struct, ::Proc, ::Method) || options[:fallback].respond_to?(:[]))
# if :fallback will not process an Array as mentioned above ... remove :[] respond_to? condition.
new_options[:fallback] = options[:fallback].dup
end
else
#
if options[:invalid] == :replace
# If the value is :replace, encode replaces invalid byte sequences in str with the replacement character. The default is to
# raise the Encoding::InvalidByteSequenceError exception
new_options[:invalid] = :replace
end
#
if options[:undef] == :replace
# If the value is :replace, encode replaces characters which are undefined in the destination encoding with the replacement character.
# The default is to raise the Encoding::UndefinedConversionError.
new_options[:undef] = :replace
end
#
if options[:replace]
# Sets the replacement string to the given value. The default replacement string is “uFFFD” for Unicode encoding forms, and “?” otherwise.
new_options[:replace] = options[:replace].dup
end
#
end
# xml
if options[:xml]
# The value must be :text or :attr. If the value is :text encode replaces undefined characters with their (upper-case hexadecimal) numeric character
# references. &, <, and > are converted to “&amp;”, “&lt;”, and “&gt;”, respectively. If the value is :attr, encode also quotes the replacement
# result (using ‘“’), and replaces ‘”’ with “&quot;”.
if [:text, :attr].include?(options[:xml])
new_options[:xml] = options[:xml].dup
end
end
#
if options[:universal_newline]
# Replaces CRLF (“rn”) and CR (“r”) with LF (“n”) if value is true.
unless (new_options[:cr_newline] || new_options[:crlf_newline])
if options[:universal_newline] == true
new_options[:universal_newline] = true
end
end
else
if options[:crlf_newline]
# Replaces LF (“n”) with CRLF (“rn”) if value is true.
unless (new_options[:cr_newline] || new_options[:universal_newline])
if options[:crlf_newline] == true
new_options[:crlf_newline] = true
end
end
else
if options[:cr_newline]
# Replaces LF (“n”) with CR (“r”) if value is true.
unless (new_options[:crlf_newline] || new_options[:universal_newline])
if options[:cr_newline] == true
new_options[:cr_newline] = true
end
end
else
# default
end
end
end
#
default_options = {}
# find the default conversion options given the destination and source encodings
# then ...
new_options
# deep-merge new_options into a provided default_options
# then return default_options
# default_options
end
#
def transcode(*args)
destination_encoding = nil
conversion_options = nil
if args.size > 0
if args[0].is_a?(::Hash)
conversion_options = args[0]
if args[1].is_a?(::Encoding)
destination_encoding = args[1]
else
destination_encoding = ::Encoding.default_external
end
else
#
if args[0].is_a?(::Encoding)
destination_encoding = args[0]
if args[1].is_a?(::Hash)
conversion_options = args[1]
else
conversion_options = {}
end
end
end
args = nil
end
#
unless destination_encoding.is_a?(::Encoding)
raise ArgumentError, "You must provide a valid Encoding, instead you provided #{destination_encoding.class}"
end
unless conversion_options.is_a?(::Hash)
raise ArgumentError, "You must provide a Hash for conversion options, instead you provided #{conversion_options.class}"
end
in_options = {}
out_options = {}
#
if (conversion_options[:invalid] || conversion_options[:undef] || conversion_options[:replace])
# Note: I *confirmed* (thanks VGoff) that :invalid/:undef/:replace and :fallback are exclusive.
#
# If the value is :replace, encode replaces invalid byte sequences in str with the replacement character. The default is to
# raise the Encoding::InvalidByteSequenceError exception
if conversion_options[:invalid] == :replace
in_options[:invalid] = :replace
out_options[:invalid] = :replace
end
# If the value is :replace, encode replaces characters which are undefined in the destination encoding with the replacement character.
# The default is to raise the Encoding::UndefinedConversionError.
if conversion_options[:undef] == :replace
in_options[:undef] = :replace
out_options[:undef] = :replace
end
#
# Sets the replacement string to the given value. The default replacement string is “uFFFD” for Unicode encoding forms, and “?” otherwise.
if conversion_options[:replace].is_a?(::String)
in_options[:replace] = conversion_options[:replace]
out_options[:replace] = conversion_options[:replace]
end
#
else
# Sets the replacement string by the given object for undefined character. The object should be a Hash, a Proc, a Method,
# or an object which has [] method. Its key is an undefined character encoded in the source encoding of current transcoder.
# Its value can be any encoding until it can be converted into the destination encoding of the transcoder.
if (conversion_options[:fallback].is_any?(::Hash, ::Struct, ::Proc, ::Method) || conversion_options[:fallback].respond_to?(:[]))
# if :fallback will not process an Array as mentioned above ... remove :[] respond_to? condition.
if conversion_options[:fallback].is_any?(::Proc, ::Method)
# TODO: study arguments and output expected, and construct one in and one out proc as wrapper.
# FIX: Odd Bug : MRI 1.9.3 will attempt to convert a Proc into a Hash directly and raise exception, instead of calling the proc.
# Also, when a method is supplied: it does call it, but passes no data and I don't think even collects a response. (wtf?) what is the point then??
# So ... as a work around (FORNOW) I will simply construct a Hash and use that resultant Hash as fallback.
# LATER: I think I'd like to add the above comparison to simply check for a .respond_to?(:call) on action objects.
in_fallback = {}
out_fallback = {}
self.each_char do |the_char|
dst_defined_char = conversion_options[:fallback].call(the_char).to_s.chars.first
if (dst_defined_char.size > 0 && the_char != dst_defined_char)
utf_bridge = the_char.encode(::Encoding::UTF_8)
in_fallback[(the_char)] = utf_bridge
out_fallback[(utf_bridge)] = dst_defined_char
end
end
in_options[:fallback] = in_fallback
out_options[:fallback] = out_fallback
else
if conversion_options[:fallback].is_any?(::Hash, ::Struct)
#
in_fallback = {}
out_fallback = {}
#
conversion_options[:fallback].to_enum(:each_pair).each do |src_undefined_char,dst_defined_char|
utf_bridge = src_undefined_char.encode(::Encoding::UTF_8)
in_fallback[(src_undefined_char)] = utf_bridge
out_fallback[(utf_bridge)] = dst_defined_char
end
#
in_options[:fallback] = in_fallback
out_options[:fallback] = out_fallback
else
if (conversion_options[:fallback].respond_to?(:[]) && conversion_options[:fallback].respond_to?(:to_a))
in_fallback = {}
out_fallback = {}
the_data = conversion_options[:fallback].to_a.flatten
the_size = the_data.size
if (the_size.even? && the_size > 0)
(0..(the_size)).step(2) do |index|
src_undefined_char = the_data[(index)]
dst_defined_char = the_data[(index + 1)]
utf_bridge = src_undefined_char.encode(::Encoding::UTF_8)
in_fallback[(src_undefined_char)] = utf_bridge
out_fallback[(utf_bridge)] = dst_defined_char
end
else
# raise error? this really requires matched pairs of things
end
in_options[:fallback] = in_fallback
out_options[:fallback] = out_fallback
end
end
end
end
end
#
# The value must be :text or :attr. If the value is :text encode replaces undefined characters with their (upper-case hexadecimal) numeric character
# references. &, <, and > are converted to “&amp;”, “&lt;”, and “&gt;”, respectively. If the value is :attr, encode also quotes the replacement
# result (using ‘“’), and replaces ‘”’ with “&quot;”.
if [:text, :attr].include?(conversion_options[:xml])
in_options[:xml] = conversion_options[:xml]
out_options[:xml] = conversion_options[:xml]
end
#
# Replaces LF (“n”) with CR (“r”) if value is true.
unless (conversion_options[:crlf_newline] || conversion_options[:universal_newline])
if conversion_options[:cr_newline] == true
in_options[:cr_newline] = true
out_options[:cr_newline] = true
end
end
#
# Replaces LF (“n”) with CRLF (“rn”) if value is true.
unless (conversion_options[:cr_newline] || conversion_options[:universal_newline])
if conversion_options[:crlf_newline] == true
in_options[:crlf_newline] = true
out_options[:crlf_newline] = true
end
end
#
# Replaces CRLF (“rn”) and CR (“r”) with LF (“n”) if value is true.
unless (conversion_options[:cr_newline] || conversion_options[:crlf_newline])
if conversion_options[:universal_newline] == true
in_options[:universal_newline] = true
out_options[:universal_newline] = true
end
end
# ###
# the_string = self.encode(::Encoding::UTF_8,self.external_encoding,in_options)
# the_string.encode!(destination_encoding,::Encoding::UTF_8,out_options)
# --or--
if self.encoding == ::Encoding::UTF_8
the_string = self.dup
else
the_string = self.encode(::Encoding::UTF_8,self.encoding,in_options)
end
if destination_encoding != ::Encoding::UTF_8
the_string.encode!(destination_encoding,::Encoding::UTF_8,out_options)
end
#
the_string
end
#
def transcode!(*args)
data = self.transcode(*args)
if data.is_a?(::String)
self.force_encoding(data.encoding)
self.replace(data)
end
self
end
#
def to_d()
::BigDecimal.new(self)
end
#
def numeric_values(categories=:any,delimiter="\t",locale=:en_US, numeric_base=10)
results = []
prepared = []
# SOMEDAY: <string>.numeric_values: formulate regex patterns per locale and numeric_base
quanta_pattern = /[a-zA-Z]*/
# proposed: /^-{0,1}\d*\.{0,1}\d+$/
# /^[-+]{0,1}\d*\.{0,1}\d+$/
# old: /[0-9.,]*/
numerical_pattern = /[-+0-9.,]*/
self.to_enum(:each_line).each do |row_text|
# ?? allow empty lines ?? I'm just not sure if I really *want* to attempt to preserve the structure that much.
row_prep = []
row_text.split(delimiter).to_enum.each do |column_text|
if column_text.size > 0
column_entry = {:text => column_text, :denomination => nil, :multiplier => nil}
column_text.scan(quanta_pattern).to_enum.each do |quanta|
if quanta.size > 0
#
interpretation = ::GxG::Units::interpret_units({:text => quanta,:categories => categories,:locale => locale, :base => numeric_base})
#
if interpretation[:error]
raise interpretation[:error]
else
# SOMEDAY: <string>.numeric_values: a bit more discernment here than just the first interpretation.
interpretation = interpretation[:result]
if interpretation[0].is_a?(Hash)
if interpretation[0][:quantum].is_a?(Hash)
denomination = interpretation[0][:quantum].keys[0]
multiplier = interpretation[0][:quantum][(denomination)]
column_entry = {:text => column_text, :denomination => denomination, :multiplier => multiplier}
if interpretation[0][:attributes].is_a?(Hash)
column_entry[:attributes] = interpretation[0][:attributes]
end
break
end
end
end
#
end
#
end
#
row_prep << column_entry
#
else
row_prep << nil
end
end
prepared << row_prep
#
end
# process prepared array
prepared.to_enum.each do |row|
results << row.to_enum.map do |column|
result = nil
if column.is_a?(Hash)
column[:text].scan(numerical_pattern).to_enum.each do |text|
text.gsub!(",","")
if text.size > 0
denomination = column[:denomination]
multiplier = column[:multiplier]
if text.include?(".")
# Note: defaults *assume* base-10 numbers
unless denomination
denomination = :float
end
unless multiplier
multiplier = 1.0
end
result = {}
result[(denomination)] = text.to_f * multiplier.to_f
else
# Note: defaults *assume* base-10 numbers
unless denomination
denomination = :integer
end
unless multiplier
multiplier = 1
end
result = {}
result[(denomination)] = text.to_i * multiplier.to_i
end
if column[:attributes].is_a?(Hash)
result.merge(column[:attributes])
end
break
end
end
end
result
end
end
# unless it is a multi-line, or multi-column result, pass only the found denomination/value Hash.
if results.size == 1
# single row
results = results[0]
if results.size == 1
# single column
results = results[0]
end
end
#
results
end
#
def valid_time?()
# Parse for Time class
instance = nil
begin
instance = ::Chronic::parse(self)
rescue Exception
end
if instance.is_a?(::Time)
true
else
false
end
end
#
def valid_date?()
# Match for Date pattern
if (/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/.match(self))
true
else
false
end
end
#
def valid_datetime?()
# Match for ISO 8601 pattern
if (/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9][+-][0-9][0-9]:[0-9][0-9]/.match(self))
true
else
false
end
end
#
def valid_datetime_nolocale?()
# Match for not-so-much ISO 8601-ish pattern
if (/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]/.match(self))
true
else
false
end
end
#
def valid_path?()
if (/.*(?:\\|\/)(.+)$/.match(self) || (self.split(" ").size == 1 || self.split("/").size > 1 || self == "/"))
true
else
false
end
end
#
def valid_jid?()
# From Blather: /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?$/
# From xmpp4r: /^(?:([^@\/<>\'\"]+)@)?([^@\/<>\'\"]+)(?:\/([^<>\'\"]*))?$/
# Note: for now - using the one from Blather
if /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?$/.match(self)
true
else
false
end
# if /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?$/.match(self)
# true
# else
# false
# end
end
#
def valid_mxid?()
if /^[!@$#+][^:]+:.*$/.match(self)
true
else
false
end
end
#
def camel_case?()
# self.match(/([A-Z][a-z]+[A-Z][a-zA-Z]+)/)
if self.match(/[A-Z]([A-Z0-9]*[a-z][a-z0-9]*[A-Z]|[a-z0-9]*[A-Z][A-Z0-9]*[a-z])[A-Za-z0-9]*/)
true
else
false
end
end
#
def split_camelcase()
if self.camel_case?()
self.split(/(?=[A-Z])/)
else
self
end
end
#
def mime_type()
result = nil
raw = ::MimeMagic.by_magic(::StringIO.new(self.clone))
if raw
result = {:type => (raw.type), :mediatype => (raw.mediatype), :subtype => (raw.subtype)}
end
result
end
#
def xml?()
declaration_test = self.include?("<?xml version=")
parse_test = false
if declaration_test
begin
mime = self.mime_type()
if mime.is_a?(::Hash)
if mime[:subtype] == "xml" || mime[:subtype] = "xhtml+xml"
parse_test = true
end
end
rescue Exception => the_error
end
end
(declaration_test && parse_test)
end
#
# def from_xml(options={})
# #
# unless options[:no_test] == true
# unless self.xml?()
# raise Exception, "String is NOT formatted as XML."
# end
# end
# result = nil
# visit = Proc.new do |the_node=nil, accumulator=[]|
# node_stack = []
# if the_node
# node_stack.push(the_node)
# while (node_stack.size > 0) do
# a_node = node_stack.pop()
# # process(a_node)
# if a_node.is_a?(::Oga::XML::Text)
# accumulator << a_node.text()
# end
# if a_node.is_any?(::Oga::XML::Element, ::Oga::XML::Document)
# record = {:name => "document_root", :attributes => {}, :parent => nil, :object => nil, :path => "/", :assembled => false}
# if a_node.respond_to?(:name)
# record[:name] = a_node.name()
# end
# if a_node.respond_to?(:attributes)
# a_node.attributes.each do |the_attr|
# record[:attributes][(the_attr.name.to_s.to_sym)] = the_attr.value
# end
# end
# if a_node.respond_to?(:parent)
# record[:parent] = a_node.parent()
# else
# record[:parent] = a_node
# end
# record[:object] = a_node
# unless a_node.is_a?(::Oga::XML::Document)
# # set accumulator path
# #
# end
# accumulator << record
# end
# if a_node.children.size > 0
# a_node.children.each do |entry|
# node_stack.push(entry)
# end
# end
# end
# end
# accumulator
# end
# #
# begin
# if options[:sax] == true
# database = visit.call(::Oga::sax_parse_xml(self.transcode({:replace => "."},::Encoding::UTF_8)),[])
# else
# database = visit.call(::Oga::parse_xml(self.transcode({:replace => "."},::Encoding::UTF_8)),[])
# end
# link_db = []
# accumulator = []
# # Build link_db
# database.each do |the_node|
# if the_node.is_a?(::Hash)
# node_record = {}
# as_key = the_node[:name].to_s.to_sym
# if the_node[:object].is_a?(::Oga::XML::Document)
# node_record[(as_key)] = {:attributes => (the_node[:attributes]), :text => "", :children => []}
# else
# node_record[(as_key)] = {:attributes => (the_node[:attributes]), :text => (the_node[:object].text), :children => []}
# end
# link_record = {:desendents => [], :accumulator => (node_record[(as_key)][:children])}
# #
# database.each do |a_node|
# if a_node.is_a?(::Hash)
# if a_node[:parent].object_id == the_node[:object].object_id
# link_record[:desendents] << a_node[:object]
# end
# end
# end
# link_db << link_record
# if the_node[:parent].is_a?(::Oga::XML::Document)
# accumulator << node_record
# end
# else
# accumulator << the_node
# end
# end
# #
# while (link_db.size > 0) do
# entry = link_db.shift
# if entry.is_a?(::Hash)
# entry[:desendents].each do |node|
# unless node.is_a?(::Oga::XML::Document)
# if node.is_a?(::Oga::XML::Text)
# node_record = node.text
# else
# if node.is_a?(::Oga::XML::Comment)
# node_record = {:comment => node.text}
# else
# node_record = {}
# as_key = node.name.to_s.to_sym
# node_record[(as_key)] = {:attributes => {}, :text => (node.text), :children => []}
# node.attributes.each do |the_attr|
# node_record[(as_key)][:attributes][(the_attr.name.to_s.to_sym)] = the_attr.value
# end
# link_record = {:desendents => [], :accumulator => (node_record[(as_key)][:children])}
# node.children.each do |node_descendent|
# link_record[:desendents] << node_descendent
# end
# if link_record[:desendents].size > 0
# link_db.push(link_record)
# end
# end
# end
# #
# entry[:accumulator] << node_record
# end
# end
# #
# end
# end
# #
# result = accumulator
# rescue Exception => the_error
# end
# #
# result
# end
#
# def from_xml_simple()
# if self.xml?
# the_hash = ::XmlSimple.xml_in(self)
# if the_hash.is_a?(::Hash)
# the_hash.symbolize_keys
# the_hash.process! do |value, selector, container|
# if value.is_a?(::String)
# item = value.numeric_values()
# if item.is_a?(::Hash)
# if item[:integer]
# item = item[:integer]
# container[(selector)] = item
# else
# if item[:float]
# item = item[:float]
# container[(selector)] = item
# end
# end
# else
# # Cast all time elements to ISO-8601
# if (value.valid_time? || value.valid_date? || value.valid_datetime? || value.valid_datetime_nolocale?)
# container[(selector)] = ::DateTime::parse(value.to_s)
# end
# end
# end
# nil
# end
# end
# the_hash
# else
# nil
# end
# end
#
def html?()
declaration_test = (self.include?('<!DOCTYPE HTML') || self.include?('<!DOCTYPE html') || self.include?('<!DOCTYPE xhtml'))
parse_test = false
if declaration_test
begin
mime = self.mime_type()
if mime.is_a?(::Hash)
if ["html", "xhtml", "xhtml+xml"].include?(mime[:subtype])
parse_test = true
end
end
rescue Exception => the_error
end
end
(declaration_test && parse_test)
end
#
# def from_html(options={})
# #
# unless options[:no_test] == true
# unless self.html?()
# raise Exception, "String is NOT formatted as HTML."
# end
# end
# result = nil
# visit = Proc.new do |the_node=nil, accumulator=[]|
# node_stack = []
# if the_node
# node_stack.push(the_node)
# while (node_stack.size > 0) do
# a_node = node_stack.pop()
# # process(a_node)
# if a_node.is_a?(::Oga::XML::Text)
# accumulator << a_node.text()
# end
# if a_node.is_any?(::Oga::XML::Element, ::Oga::XML::Document)
# record = {:name => "document_root", :attributes => {}, :parent => nil, :object => nil, :path => "/", :assembled => false}
# if a_node.respond_to?(:name)
# record[:name] = a_node.name()
# end
# if a_node.respond_to?(:attributes)
# a_node.attributes.each do |the_attr|
# record[:attributes][(the_attr.name.to_s.to_sym)] = the_attr.value
# end
# end
# if a_node.respond_to?(:parent)
# record[:parent] = a_node.parent()
# else
# record[:parent] = a_node
# end
# record[:object] = a_node
# unless a_node.is_a?(::Oga::XML::Document)
# # set accumulator path
# #
# end
# accumulator << record
# end
# if a_node.children.size > 0
# a_node.children.each do |entry|
# node_stack.push(entry)
# end
# end
# end
# end
# accumulator
# end
# #
# begin
# if options[:sax] == true
# database = visit.call(::Oga::sax_parse_html(self.transcode({:replace => "."},::Encoding::UTF_8)),[])
# else
# database = visit.call(::Oga::parse_html(self.transcode({:replace => "."},::Encoding::UTF_8)),[])
# end
# link_db = []
# accumulator = []
# # Build link_db
# database.each do |the_node|
# if the_node.is_a?(::Hash)
# node_record = {}
# as_key = the_node[:name].to_s.to_sym
# if the_node[:object].is_a?(::Oga::XML::Document)
# node_record[(as_key)] = {:attributes => (the_node[:attributes]), :text => "", :children => []}
# else
# node_record[(as_key)] = {:attributes => (the_node[:attributes]), :text => (the_node[:object].text), :children => []}
# end
# link_record = {:desendents => [], :accumulator => (node_record[(as_key)][:children])}
# #
# database.each do |a_node|
# if a_node.is_a?(::Hash)
# if a_node[:parent].object_id == the_node[:object].object_id
# link_record[:desendents] << a_node[:object]
# end
# end
# end
# link_db << link_record
# if the_node[:parent].is_a?(::Oga::XML::Document)
# accumulator << node_record
# end
# else
# accumulator << the_node
# end
# end
# #
# while (link_db.size > 0) do
# entry = link_db.shift
# if entry.is_a?(::Hash)
# entry[:desendents].each do |node|
# unless node.is_a?(::Oga::XML::Document)
# if node.is_a?(::Oga::XML::Text)
# node_record = node.text
# else
# if node.is_a?(::Oga::XML::Comment)
# node_record = {:comment => node.text}
# else
# node_record = {}
# as_key = node.name.to_s.to_sym
# node_record[(as_key)] = {:attributes => {}, :text => (node.text), :children => []}
# node.attributes.each do |the_attr|
# node_record[(as_key)][:attributes][(the_attr.name.to_s.to_sym)] = the_attr.value
# end
# link_record = {:desendents => [], :accumulator => (node_record[(as_key)][:children])}
# node.children.each do |node_descendent|
# link_record[:desendents] << node_descendent
# end
# if link_record[:desendents].size > 0
# link_db.push(link_record)
# end
# end
# end
# #
# entry[:accumulator] << node_record
# end
# end
# #
# end
# end
# #
# result = accumulator
# rescue Exception => the_error
# end
# #
# result
# end
#
def valid_uri?()
result = false
begin
if ::URI::parse(self).is_a?(::URI::Generic)
result = true
end
rescue Exception
end
result
end
#
def to_uri()
::URI::parse(self)
end
#
def slice_bytes(*args)
# Return an ASCII_8BIT encoded sub-string
result = nil
the_range = nil
if args[0].is_a?(Numeric)
args[0] = args[0].to_i
if args[0] < 0
args[0] = self.bytesize + args[0]
end
the_range = ((args[0])..(args[0]))
if args[1].is_a?(Numeric)
args[1] = args[1].to_i
if args[1] < 1
raise ArgumentError.new("Second parameter needs to be a Numeric greater than 0, you provided #{args[1].inspect}")
else
the_range = ((args[0])..(args[0] + (args[1] - 1)))
end
end
else
if args[0].is_a?(Range)
if args[0].first < 0
args[1] = self.bytesize + args[0].first
else
args[1] = args[0].first
end
if args[0].last < 0
args[2] = self.bytesize + args[0].last
else
args[2] = args[0].last
end
if args[1] <= args[2]
the_range = ((args[1])..(args[2]))
else
the_range = ((args[2])..(args[1]))
end
else
raise ArgumentError.new("First parameter needs to be a Numeric or a Range, you provided #{args[0].inspect}")
end
end
#
if the_range
result = ""
result.force_encoding(::Encoding::ASCII_8BIT)
#
if self.encoding == ::Encoding::ASCII_8BIT
if self.bytesize > 0
if the_range.min != the_range.max
result << self.slice(the_range)
else
result << self[(the_range.min)]
end
end
else
if self.bytesize > 0
raw_text = self.clone
raw_text.force_encoding(::Encoding::ASCII_8BIT)
if the_range.min != the_range.max
result << raw_text.slice(the_range)
else
result << raw_text[(the_range.min)]
end
end
end
#
end
result
end
#
def byte_at(*args)
# Random-access indexed byte value retrieval. Returns a Integer or nil.
# why? This is to pluck a single byte value out of a string as if it were merely a string of bytes. 1.8.7 used to provide a [] method for this purpose.
if args[0].is_a?(Numeric)
args[0] = args[0].to_i
else
raise ArgumentError.new("The parameter needs to be a Numeric, you provided #{args[0].inspect}")
end
if self.bytesize > 0
if self.encoding == ::Encoding::ASCII_8BIT
self[(args[0])].ord
else
self.slice_bytes(args[0])[0].ord
end
else
nil
end
end
#
def bytes_at(*args)
# Returns GxG::ByteArray
unless args.size > 0
args = [(0..-1)]
end
GxG::ByteArray.new(1,self.slice_bytes(*args))
end
#
def unpack_bytes(format_template_string="")
# See : http://ruby-doc.org/core-1.9.3/String.html#method-i-byteslice#This%20table%20summarizes
GxG::ByteArray.new(self.unpack(format_template_string))
end
#
# Note: documentation: recommend use of these methods and NOT those which they call to ensure cooperative event processing.
# LATER: String: look into c code : is there a way to pluck a char off a string w/o using chars,bytes,codepoints,lines ?
# This would afford an opportunity to override chars,bytes,codepoints and lines so that cooperative event processing was truly and invisibly supported.
# This would be slower than the c code, but would let you load up gems w/o overriding another author's code to make it cooperative. (i.e. facets, etc)
def each_line(separator=$/,&block)
enumerator = self.lines(separator).to_enum
if block.respond_to?(:call)
enumerator.each do |item|
block.call(item)
end
self
else
enumerator
end
end
#
def each_line_with_index(separator=$/,offset=0,&block)
enumerator = self.lines(separator).to_enum.with_index(offset)
if block.respond_to?(:call)
enumerator.each do |item,index|
block.call(item,index)
end
self
else
enumerator
end
end
#
def each_char(&block)
enumerator = self.to_enum(:chars)
if block.respond_to?(:call)
enumerator.each do |item|
block.call(item)
end
self
else
enumerator
end
end
#
def each_char_with_index(offset=0,&block)
# Thanks to Hanmac from IRC :)
enumerator = self.to_enum(:chars).with_index(offset)
if block.respond_to?(:call)
enumerator.each do |item,index|
block.call(item,index)
end
self
else
enumerator
end
end
#
def each_byte(&block)
enumerator = self.to_enum(:bytes)
if block.respond_to?(:call)
enumerator.each do |item|
block.call(item)
end
self
else
enumerator
end
end
#
def each_byte_with_index(offset=0,&block)
# Thanks to Hanmac from IRC :)
enumerator = self.to_enum(:bytes).with_index(offset)
if block.respond_to?(:call)
enumerator.each do |item,index|
block.call(item,index)
end
self
else
enumerator
end
end
#
def each_codepoint(&block)
# String.codepoint will not return a GxG::Enumerator (non-blocking), so I'm providing these methods.
enumerator = self.to_enum(:codepoints)
if block.respond_to?(:call)
enumerator.each do |item|
block.call(item)
end
self
else
enumerator
end
end
#
def each_codepoint_with_index(offset=0,&block)
enumerator = self.to_enum(:codepoints).with_index(offset)
if block.respond_to?(:call)
enumerator.each do |item,index|
block.call(item,index)
end
self
else
enumerator
end
end
# Encoding format detection and handling: Serialized, JSON & binhex(base64)
def serialized?()
if ((self[0..7].to_s == "marshal:" || self[0..9].to_s == "structure:") && (self[8..-1].to_s.base64?() || self[10..-1].to_s.base64?()))
true
else
false
end
end
#
def unserialize()
if self.serialized?()
::GxG::reconstitute(self)
else
self
end
end
#
def json_1?()
self.slice(0,1) == '{'
end
#
def json_2?()
(self.json_1?() && (self.match('"apiVersion"\s?:\s?"2.0"') || false))
end
#
def json?()
self.json_1?() || self.json_2?()
end
#
def from_json(symbolize_names = true)
if self.json?
the_object = ::JSON::parse(self,{:symbolize_names => symbolize_names})
if the_object.is_any?(::Hash, ::Array)
the_object.process! do |value, selector, container|
if value.is_a?(::String)
item = value.numeric_values()
if item.is_a?(::Hash)
if item[:integer]
item = item[:integer]
container[(selector)] = item
else
if item[:float]
item = item[:float]
container[(selector)] = item
end
end
else
if (value.valid_datetime? || value.valid_datetime_nolocale?)
container[(selector)] = ::DateTime::parse(value)
end
end
#
if value[0..6] == "binary:" && value[7..-1].base64?
container[(selector)] = GxG::ByteArray.new(value[7..-1].decode64)
end
end
nil
end
#
end
else
self
end
end
# Base64 Stuff:
def base64?()
# RFC 4648
# SOMEDAY: use regex based detection, current is open to some bugs.
# See: http://ruby.about.com/od/advancedruby/ss/Base64-In-Ruby.htm
# See: http://www.perlmonks.org/?node_id=775820
# See: http://mattfaus.com/blog/2007/02/14/base64-regular-expression/
# See: http://stackoverflow.com/questions/475074/regex-to-parse-or-validate-base64-data
# Regex Testing: http://www.myregexp.com/signedJar.html
# For now:
begin
# Review : So, for a work-around - keep adding exclusion tests to this conditional set as you discover them.
if (self.size > 0) && (not ["html","form","page","head","body","show","hide","play"].include?(self.downcase))
# Review : *almost perfect* - words like 'html' 'form' 'page' get mangled because this regex senses they are base64 when they are not! (consult regex expert for remedy)
if self.match(/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/)
true
else
false
end
else
false
end
rescue Exception => the_error
false
end
end
#
def encode64()
# RFC 4648
::Base64::strict_encode64(self)
end
#
def decode64()
# RFC 4648
if self.base64?()
::Base64::strict_decode64(self)
else
self
end
end
#
def encode64!()
data = self.encode64()
if data != self
self.replace(data)
end
self
end
#
def decode64!()
data = self.decode64()
if data != self
self.replace(data)
end
self
end
#
def encrypt(withkey="")
#
if withkey.to_s.size > 0
the_encoding = self.encoding()
keybytes = ::GxG::ByteArray.new(withkey.to_s)
container = ::GxG::ByteArray.new(self)
#
container.each_index do |index|
the_value = container[(index)]
#
keybytes.each do |the_byte|
[127,63,31,15,7,3,1,0].each do |the_bit|
if the_byte > the_bit
the_value += 1
if the_value > 255
the_value = 0
end
else
the_value -= 1
if the_value < 0
the_value = 255
end
end
end
end
#
container[(index)] = the_value
end
#
result = container.to_s
result.force_encoding(the_encoding)
result
else
self.dup
end
end
#
def encrypt_aes(withkey="",iv=nil)
digest = Digest::SHA256.new
digest.update(withkey)
aes = OpenSSL::Cipher.new("AES-256-CBC")
iv = OpenSSL::Cipher.new("AES-256-CBC").random_iv
aes.encrypt
aes.key = digest.digest
aes.iv = iv
cipher = aes.update(self)
cipher << aes.final
{:vector => iv, :data => cipher}
end
#
def decrypt(withkey="")
#
if withkey.to_s.size > 0
the_encoding = self.encoding()
keybytes = ::GxG::ByteArray.new(withkey.to_s)
container = ::GxG::ByteArray.new(self)
#
container.each_index do |index|
the_value = container[(index)]
#
keybytes.each do |the_byte|
[127,63,31,15,7,3,1,0].each do |the_bit|
if the_byte > the_bit
the_value -= 1
if the_value < 0
the_value = 255
end
else
the_value += 1
if the_value > 255
the_value = 0
end
end
end
end
#
container[(index)] = the_value
end
#
result = container.to_s
result.force_encoding(the_encoding)
result
else
self.dup
end
end
#
def decrypt_aes(withkey="", iv=nil)
digest = Digest::SHA256.new
digest.update(withkey)
decode_cipher = OpenSSL::Cipher.new("AES-256-CBC")
decode_cipher.decrypt
decode_cipher.key = digest.digest
decode_cipher.iv = iv
plain = decode_cipher.update(self)
plain << decode_cipher.final
plain
end
end
# Alteration to <Integer>.times block call loop
class Integer
#
# def times(&block)
# if self > 0
# if block
# (1..(self)).to_enum.each do
# block.call
# # *should* use GxG::Enumerator, so no pause command needed here.
# end
# self
# else
# (1..(self)).to_enum(:times)
# end
# else
# self
# end
# end
# #
# def upto(number=nil,&block)
# if block.respond_to?(:call)
# if number.is_a?(::Numeric)
# number = number.to_i
# if self <= number
# ((self)..(number)).to_enum.each do |the_number|
# block.call(the_number)
# end
# self
# else
# number
# end
# else
# raise ArgumentError, "comparison of Fixnum and #{number.class} failed"
# end
# else
# self.to_enum(:upto,number)
# end
# end
# #
# def step(number=nil,skip=nil,&block)
# if block.respond_to?(:call)
# if number.is_a?(::Numeric)
# number = number.to_i
# if skip.is_any?(::Numeric,::NilClass)
# if skip
# skip = skip.to_i
# else
# skip = 1
# end
# #
# if self <= number
# current = self.dup
# while current <= number
# block.call(current)
# current += skip
# pause
# end
# self
# else
# number
# end
# #
# else
# raise ArgumentError, "comparison of #{number.class} and 0 failed"
# end
# else
# raise ArgumentError, "comparison of Fixnum and #{number.class} failed"
# end
# else
# if skip
# self.to_enum(:skip,number,skip)
# else
# self.to_enum(:skip,number)
# end
# end
# end
#
def to_d()
::BigDecimal.new(("%#{::Float::DIG}f" % self.to_f))
end
end
# Alteration to Struct for support of structural processing:
#
class Struct
# FIXME: dup collision on mail gem
# NoMethodError: private method 'dup' called for an instance of Net::IMAP::Config::AttrAccessors::Struct
# initialize_dup at /opt/jruby-gems/shared/gems/net-imap-0.5.9/lib/net/imap/config/attr_accessors.rb:69
# public
# def initialize_clone
# result = self.class.new
# self.each_pair do |key, value|
# result[(key)] = value.clone
# end
# result
# end
# def clone()
# initialize_clone
# end
# private
# alias :initialize_dup :initialize_clone
# alias :dup :initialize_clone
#
def self.process(the_struct=::Struct.new(nil),&block)
new_struct = ::Struct.new(nil)
if block.respond_to?(:call)
if the_struct.is_any?(::Array, ::Hash, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
new_struct = the_struct.process!(&block)
else
raise ArgumentError, "you must pass a Hash, or an Array, or a ByteArray, or a Set, or a Struct"
end
end
new_struct
end
#
def self.search(the_struct=::Struct.new(nil),&block)
new_struct = ::Struct.new(nil)
if block.respond_to?(:call)
if the_struct.is_any?(::Array, ::Hash, ::Set, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
new_struct = the_struct.search(&block)
else
raise ArgumentError, "you must pass a Hash, or an Array, or a ByteArray, or a Set, or a Struct"
end
end
new_struct
end
#
# def each_pair(&block)
# # ok, bizarre : this method is listed in instance_methods but is missing when called. hrm.
# collection = {}
# self.members.to_enum.each do |key|
# collection[(key)] = (self[(key)])
# end
# if block.respond_to?(:call)
# collection.to_enum(:each_pair).each do |key,value|
# block.call(key,value)
# end
# else
# collection.to_enum(:each_pair)
# end
# end
#
def iterative(&block)
result = []
visit = Proc.new do |the_node=nil, accumulator=[]|
node_stack = []
if the_node
node_stack << ({:parent => nil, :parent_selector => nil, :object => (the_node)})
while (node_stack.size > 0) do
a_node = node_stack.shift
#
if a_node[:object].is_any?(::Hash, ::Struct, ::GxG::Database::PersistedHash)
a_node[:object].each_pair do |the_key, the_value|
node_stack << ({:parent => a_node[:object], :parent_selector => the_key, :object => the_value})
end
end
if a_node[:object].is_any?(::Array, ::GxG::Database::PersistedArray)
a_node[:object].each_with_index do |the_value, the_index|
node_stack << ({:parent => a_node[:object], :parent_selector => the_index, :object => the_value})
end
end
#
accumulator << a_node
end
end
accumulator
end
#
children_of = Proc.new do |the_db=[], the_parent=nil|
list = []
the_db.each do |node|
if node[:parent].object_id == the_parent.object_id
list << node
end
end
list
end
#
begin
database = visit.call(self,[])
link_db = children_of.call(database, self)
if block.respond_to?(:call)
while (link_db.size > 0) do
entry = link_db.shift
unless entry[:object].object_id == self.object_id
# calls with parameters: the_value, the_key/the_index (the_selector), the_container
raw_result = block.call(entry[:object], entry[:parent_selector], entry[:parent])
if raw_result
result << raw_result
end
end
if entry[:object].object_id != nil.object_id
children = children_of.call(database, entry[:object])
children.each do |child|
link_db << child
end
end
end
end
#
rescue Exception => the_error
log_error({:error => the_error, :parameters => {}})
end
#
result
end
#
def process(&block)
result = self.clone
result.iterative(&block)
result
end
#
def process!(&block)
self.iterative(&block)
self
end
#
def search(&block)
results = []
if block.respond_to?(:call)
results = self.iterative(&block)
end
results
end
#
end
# Alteration to Hash and Array for support of structural processing:
# additional methods to address elements of, and process, structural combinations of Hashes, Arrays, and ByteArrays
# SOMEDAY: @Hash extend these to the pain-in-the-ass Struct class as well.
class Hash
#
def self.process(the_hash={},&block)
new_hash = {}
if block.respond_to?(:call)
if the_hash.is_any?(::Array, ::Hash, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
new_hash = the_hash.process!(&block)
else
raise ArgumentError, "you must pass a Hash or an Array, or a ByteArray"
end
end
new_hash
end
#
def self.search(the_hash={},&block)
new_hash = {}
if block.respond_to?(:call)
if the_hash.is_any?(::Array, ::Hash, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
new_hash = the_hash.search(&block)
else
raise ArgumentError, "you must pass a Hash or an Array, or a ByteArray"
end
end
new_hash
end
# ### OpenStruct Integration
def as_structure()
::OpenStruct.new(self)
end
#
def iterative(&block)
result = []
visit = Proc.new do |the_node=nil, accumulator=[]|
node_stack = []
if the_node
node_stack << ({:parent => nil, :parent_selector => nil, :object => (the_node)})
while (node_stack.size > 0) do
a_node = node_stack.shift
#
if a_node[:object].is_any?(::Hash, ::Struct, ::GxG::Database::PersistedHash)
a_node[:object].each_pair do |the_key, the_value|
node_stack << ({:parent => a_node[:object], :parent_selector => the_key, :object => the_value})
end
end
if a_node[:object].is_any?(::Array, ::GxG::Database::PersistedArray)
a_node[:object].each_with_index do |the_value, the_index|
node_stack << ({:parent => a_node[:object], :parent_selector => the_index, :object => the_value})
end
end
#
accumulator << a_node
end
end
accumulator
end
#
children_of = Proc.new do |the_db=[], the_parent=nil|
list = []
the_db.each do |node|
if node[:parent].object_id == the_parent.object_id
list << node
end
end
list
end
#
begin
database = visit.call(self,[])
link_db = children_of.call(database, self)
if block.respond_to?(:call)
while (link_db.size > 0) do
entry = link_db.shift
unless entry[:object].object_id == self.object_id
# calls with parameters: the_value, the_key/the_index (the_selector), the_container
raw_result = block.call(entry[:object], entry[:parent_selector], entry[:parent])
if raw_result
result << raw_result
end
end
if entry[:object].object_id != nil.object_id
children = children_of.call(database, entry[:object])
children.each do |child|
link_db << child
end
end
end
end
#
rescue Exception => the_error
log_error({:error => the_error, :parameters => {}})
end
#
result
end
#
def process(&block)
result = self.clone
result.iterative(&block)
result
end
#
def process!(&block)
self.iterative(&block)
self
end
#
def search(&block)
results = []
if block.respond_to?(:call)
results = self.iterative(&block)
end
results
end
#
def paths_to(the_object=nil,base_path="")
# new idea here:
search_results = []
unless base_path[0] == "/"
base_path = ("/" << base_path)
end
if base_path.size > 1
path_stack = base_path.split("/")[1..-1].reverse
else
path_stack = []
end
origin = self.get_at_path(base_path)
container_stack = [{:selector => nil, :container => origin, :prefix => "/"}]
find_container = Proc.new do |the_container|
result = nil
container_stack.each_with_index do |entry, index|
if entry[:container] == the_container
result = entry
break
end
end
result
end
last_container = origin
found = false
# tester = {:a=>1, :b=>2, :c=>[0, 5], :testing=>{:d=>4.0, :e=>0.9, :f => nil}}
if origin.is_any?(::Hash, ::Array, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
origin.process! do |the_value, selector, container|
if last_container.object_id != container.object_id
container_record = find_container.call(container)
if container_record
path_stack = container_record[:prefix].split("/").reverse
if path_stack.size == 0
path_stack << ""
end
end
last_container = container
end
if selector.is_a?(Symbol)
safe_key = (":" + selector.to_s)
else
safe_key = selector.to_s
end
safe_key = safe_key.gsub("/","%2f")
path_stack.unshift(safe_key)
# compare the_value
found = false
if (the_value == the_object)
found = true
end
if found
search_results << ("/" << path_stack.reverse.join("/"))
end
#
if the_value.is_any?(::Array, ::Hash, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
container_stack.unshift({:selector => selector, :container => the_value, :prefix => (path_stack.reverse.join("/"))})
end
path_stack.shift
#
nil
end
else
search_results << ("/" << path_stack.join("/"))
end
search_results
end
#
def get_at_path(the_path="/")
result = nil
if the_path == "/"
result = self
else
object_stack = [(self)]
path_stack = the_path.split("/")
path_stack.to_enum.each do |path_element|
element = nil
if path_element.size > 0
if (path_element =~ /^(?:[0-9])*[0-9](?:[0-9])*$/) == 0
element = path_element.to_i
else
element = path_element
element.gsub!("%2f","/")
if element[0] == ":"
element = element[(1..-1)].to_sym
end
end
end
if element
result = object_stack.first[(element)]
if result.is_a?(NilClass)
break
else
object_stack.unshift(result)
end
else
# ignore double slashes? '//'
# break
end
end
end
result
end
#
def set_at_path(the_path="/",the_value=nil)
result = nil
if the_path != "/"
container = self.get_at_path(::File::dirname(the_path))
if container
raw_selector = ::File::basename(the_path)
selector = nil
if raw_selector.size > 0
if (raw_selector =~ /^(?:[0-9])*[0-9](?:[0-9])*$/) == 0
selector = raw_selector.to_i
else
selector = raw_selector
selector.gsub!("%2f","/")
if selector[0] == ":"
selector = selector[(1..-1)].to_sym
end
end
end
if selector
container[(selector)] = the_value
result = container[(selector)]
else
# ignore double slashes? '//'
# break
end
#
end
end
result
end
#
def gxg_export()
result = {:type => "Hash", :content => {}}
export_db = [{:parent => nil, :parent_selector => nil, :object => self, :record => result}]
children_of = Proc.new do |the_parent=nil|
list = []
export_db.each do |node|
if node[:parent].object_id == the_parent.object_id
list << node
end
end
list
end
export_record = Proc.new do |the_value|
if the_value.is_any?(Integer, Float, String)
{:type => (the_value.class.to_s), :content => the_value}
else
{:type => (the_value.class.to_s), :content => the_value.to_s}
end
end
# Build up export_db:
self.search do |the_value, the_selector, the_container|
if the_value.is_a?(::Hash)
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {:type => "Hash", :content => {}}}
else
if the_value.is_a?(::Array)
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {:type => "Array", :content => []}}
else
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => export_record.call(the_value)}
end
end
end
# Collect children export content:
link_db =[(export_db[0])]
while link_db.size > 0 do
entry = link_db.shift
children_of.call(entry[:object]).each do |the_child|
entry[:record][:content][(the_child[:parent_selector])] = the_child[:record]
if the_child[:object].is_any?(Hash, Array)
link_db << the_child
end
end
end
#
result
end
#
def self.gxg_import(the_exported_record=nil)
result = nil
if the_exported_record.is_a?(Hash)
if the_exported_record[:type] == "Array"
result = ::Array::gxg_import(the_exported_record)
else
import_value = Proc.new do |type,value|
if value.is_a?(String)
begin
the_class = eval(type)
if the_class.respond_to?(:parse)
value = the_class.parse(value)
else
if the_class.respond_to?(:new)
value = the_class.new(value)
else
if the_class.respond_to?(:try_convert)
value = the_class.try_convert(value)
end
end
end
rescue Exception => the_error
end
end
value
end
if the_exported_record[:type] == "Hash"
result = {}
import_db = [{:parent => nil, :parent_selector => nil, :object => result, :record => the_exported_record}]
while import_db.size > 0 do
entry = import_db.shift
if entry[:record][:content].is_a?(Hash)
entry[:record][:content].each_pair do |selector, value|
if value[:type] == "Hash"
entry[:object][(selector)] = {}
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
if value[:type] == "Array"
entry[:object][(selector)] = []
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
entry[:object][(selector)] = import_value.call(value[:type], value[:content])
end
end
end
else
if entry[:record][:content].is_a?(Array)
entry[:record][:content].each_with_index do |value, selector|
if value[:type] == "Hash"
entry[:object][(selector)] = {}
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
if value[:type] == "Array"
entry[:object][(selector)] = []
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
entry[:object][(selector)] = import_value.call(value[:type], value[:content])
end
end
end
end
end
end
else
result = import_value.call(the_exported_record[:type], the_exported_record[:content])
end
end
end
result
end
#
# def to_xml_simple(options={})
# # keyattr keeproot contentkey noattr rootname
# # xmldeclaration outputfile noescape suppressempty
# # anonymoustag indent grouptags noindent attrprefix
# the_options = {:xmldeclaration => true, :rootname => :root, :keeproot => false}
# if options.is_a?(::Hash)
# if options[:XmlDeclaration].is_any?(::TrueClass, ::FalseClass)
# the_options[:xmldeclaration] = options[:XmlDeclaration]
# end
# if options[:xmldeclaration].is_any?(::TrueClass, ::FalseClass)
# the_options[:xmldeclaration] = options[:xmldeclaration]
# end
# if options[:RootName].is_any?(::String, ::Symbol)
# the_options[:rootname] = options[:RootName]
# end
# if options[:rootname].is_any?(::String, ::Symbol)
# the_options[:rootname] = options[:rootname]
# end
# if options[:KeepRoot].is_any?(::TrueClass, ::FalseClass)
# the_options[:keeproot] = options[:KeepRoot]
# end
# if options[:keeproot].is_any?(::TrueClass, ::FalseClass)
# the_options[:keeproot] = options[:keeproot]
# end
# else
# raise ArgumentError, "You MUST provide options in the form of a Hash."
# end
# ::XmlSimple.xml_out(self, the_options).gsub("\n", "")
# end
#
# def to_json()
# ::JSON.generate(self)
# end
#
def symbolize_keys()
self.process! do |value, selector, container|
if container.is_a?(::Hash)
unless selector.is_a?(::Symbol)
container[(selector.to_sym)] = container.delete(selector)
end
end
nil
end
self
end
#
#
end
#
class Array
# Interesting research on extending ruby classes '|' for instance: http://verboselogging.com/2011/05/06/simple-ruby-pipes
# Also: http://stackoverflow.com/questions/4234119/ruby-pipes-how-do-i-tie-the-output-of-two-subprocesses-together
# And a *really* nice one: http://jstorimer.com/2012/03/18/a-unix-shell-in-ruby-pipes.html#Shell%20Pipelines%20Demystified
#
def self.process(the_array=[],&block)
new_array = []
if block.respond_to?(:call)
if the_array.is_any?(::Array, ::Hash, ::GxG::ByteArray, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
new_array = the_array.process(&block)
else
raise ArgumentError, "you must pass a Array, a Hash, or a ByteArray"
end
end
new_array
end
#
def self.search(the_array=[],&block)
new_array = []
if block.respond_to?(:call)
if the_array.is_any?(::Array, ::Hash, ::GxG::ByteArray, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
new_array = the_array.search(&block)
else
raise ArgumentError, "you must pass a Array, a Hash, or a ByteArray"
end
end
new_array
end
#
def iterative(&block)
result = []
visit = Proc.new do |the_node=nil, accumulator=[]|
node_stack = []
if the_node
node_stack << ({:parent => nil, :parent_selector => nil, :object => (the_node)})
while (node_stack.size > 0) do
a_node = node_stack.shift
#
if a_node[:object].is_any?(::Hash, ::Struct, ::GxG::Database::PersistedHash)
a_node[:object].each_pair do |the_key, the_value|
node_stack << ({:parent => a_node[:object], :parent_selector => the_key, :object => the_value})
end
end
if a_node[:object].is_any?(::Array, ::GxG::Database::PersistedArray)
a_node[:object].each_with_index do |the_value, the_index|
node_stack << ({:parent => a_node[:object], :parent_selector => the_index, :object => the_value})
end
end
#
accumulator << a_node
end
end
accumulator
end
#
children_of = Proc.new do |the_db=[], the_parent=nil|
list = []
the_db.each do |node|
if node[:parent].object_id == the_parent.object_id
list << node
end
end
list
end
#
begin
database = visit.call(self,[])
link_db = children_of.call(database, self)
if block.respond_to?(:call)
while (link_db.size > 0) do
entry = link_db.shift
unless entry[:object].object_id == self.object_id
# calls with parameters: the_value, the_key/the_index (the_selector), the_container
raw_result = block.call(entry[:object], entry[:parent_selector], entry[:parent])
if raw_result
result << raw_result
end
end
if entry[:object].object_id != nil.object_id
children = children_of.call(database, entry[:object])
children.each do |child|
link_db << child
end
end
end
end
#
rescue Exception => the_error
log_error({:error => the_error, :parameters => {}})
end
#
result
end
#
def process(&block)
result = self.clone
result.iterative(&block)
result
end
#
def process!(&block)
self.iterative(&block)
self
end
#
def search(&block)
results = []
if block.respond_to?(:call)
results = self.iterative(&block)
end
results
end
#
#
def paths_to(the_object=nil,base_path="")
# new idea here:
search_results = []
unless base_path[0] == "/"
base_path = ("/" << base_path)
end
if base_path.size > 1
path_stack = base_path.split("/")[1..-1].reverse
else
path_stack = []
end
origin = self.get_at_path(base_path)
container_stack = [{:selector => nil, :container => origin}]
find_container = Proc.new do |the_container|
result = nil
container_stack.each_with_index do |entry, index|
if entry[:container] == the_container
result = entry
break
end
end
result
end
last_container = origin
found = false
# tester = {:a=>1, :b=>2, :c=>[0, 5], :testing=>{:d=>4.0, :e=>0.9, :f => nil}}
if origin.is_any?(::Hash, ::Array, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
origin.process! do |the_value, selector, container|
if last_container.object_id != container.object_id
container_record = find_container.call(container)
if container_record
path_stack = container_record[:prefix].split("/").reverse
if path_stack.size == 0
path_stack << ""
end
end
last_container = container
end
if selector.is_a?(Symbol)
safe_key = (":" + selector.to_s)
else
safe_key = selector.to_s
end
safe_key.gsub!("/","%2f")
path_stack.unshift(safe_key)
# compare the_value
found = false
if (the_value == the_object)
found = true
end
if found
search_results << ("/" << path_stack.reverse.join("/"))
end
#
if the_value.is_any?(::Array, ::Hash, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
container_stack.unshift({:selector => selector, :container => the_value, :prefix => (path_stack.reverse.join("/"))})
end
path_stack.shift
#
nil
end
else
search_results << ("/" << path_stack.join("/"))
end
search_results
end
#
def get_at_path(the_path="/")
# /^(?:[0-9])*[0-9](?:[0-9])*$/ = nil if an alpha present there, else 0 only numeric
# Attribution : http://stackoverflow.com/questions/1240674/regex-match-a-string-containing-numbers-and-letters-but-not-a-string-of-just-nu
#
# if ":" detected do: (str.gsub("%2f","/").to_sym) as key else (str.gsub("%2f","/"))
result = nil
if the_path == "/"
result = self
else
object_stack = [(self)]
path_stack = the_path.split("/")
path_stack.to_enum.each do |path_element|
element = nil
if path_element.size > 0
if (path_element =~ /^(?:[0-9])*[0-9](?:[0-9])*$/) == 0
element = path_element.to_i
else
element = path_element
element.gsub!("%2f","/")
if element[0] == ":"
element = element[(1..-1)].to_sym
end
end
end
if element
result = object_stack.first[(element)]
if result.is_a?(NilClass)
break
else
object_stack.unshift(result)
end
else
# ignore double slashes? '//'
# break
end
end
end
result
end
#
def set_at_path(the_path="/",the_value=nil)
result = nil
if the_path != "/"
container = self.get_at_path(::File::dirname(the_path))
if container
raw_selector = ::File::basename(the_path)
selector = nil
if raw_selector.size > 0
if (raw_selector =~ /^(?:[0-9])*[0-9](?:[0-9])*$/) == 0
selector = raw_selector.to_i
else
selector = raw_selector
selector.gsub!("%2f","/")
if selector[0] == ":"
selector = selector[(1..-1)].to_sym
end
end
end
if selector
container[(selector)] = the_value
result = container[(selector)]
else
# ignore double slashes? '//'
# break
end
#
end
end
result
end
#
def gxg_export()
result = {:type => "Array", :content => []}
export_db = [{:parent => nil, :parent_selector => nil, :object => self, :record => result}]
children_of = Proc.new do |the_parent=nil|
list = []
export_db.each do |node|
if node[:parent].object_id == the_parent.object_id
list << node
end
end
list
end
export_record = Proc.new do |the_value|
if the_value.is_any?(Integer, Float, String)
{:type => (the_value.class.to_s), :content => the_value}
else
{:type => (the_value.class.to_s), :content => the_value.to_s}
end
end
# Build up export_db:
self.search do |the_value, the_selector, the_container|
if the_value.is_a?(::Hash)
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {:type => "Hash", :content => {}}}
else
if the_value.is_a?(::Array)
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {:type => "Array", :content => []}}
else
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => export_record.call(the_value)}
end
end
end
# Collect children export content:
link_db =[(export_db[0])]
while link_db.size > 0 do
entry = link_db.shift
children_of.call(entry[:object]).each do |the_child|
entry[:record][:content][(the_child[:parent_selector])] = the_child[:record]
if the_child[:object].is_any?(Hash, Array)
link_db << the_child
end
end
end
#
result
end
#
def self.gxg_import(the_exported_record=nil)
result = nil
if the_exported_record.is_a?(Hash)
if the_exported_record[:type] == "Hash"
result = ::Hash::gxg_import(the_exported_record)
else
import_value = Proc.new do |type,value|
if value.is_a?(String)
begin
the_class = eval(type)
if the_class.respond_to?(:parse)
value = the_class.parse(value)
else
if the_class.respond_to?(:new)
value = the_class.new(value)
else
if the_class.respond_to?(:try_convert)
value = the_class.try_convert(value)
end
end
end
rescue Exception => the_error
end
end
value
end
if the_exported_record[:type] == "Array"
result = []
import_db = [{:parent => nil, :parent_selector => nil, :object => result, :record => the_exported_record}]
while import_db.size > 0 do
entry = import_db.shift
if entry[:record][:content].is_a?(Hash)
entry[:record][:content].each_pair do |selector, value|
if value[:type] == "Hash"
entry[:object][(selector)] = {}
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
if value[:type] == "Array"
entry[:object][(selector)] = []
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
entry[:object][(selector)] = import_value.call(value[:type], value[:content])
end
end
end
else
if entry[:record][:content].is_a?(Array)
entry[:record][:content].each_with_index do |value, selector|
if value[:type] == "Hash"
entry[:object][(selector)] = {}
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
if value[:type] == "Array"
entry[:object][(selector)] = []
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
entry[:object][(selector)] = import_value.call(value[:type], value[:content])
end
end
end
end
end
end
else
result = import_value.call(the_exported_record[:type], the_exported_record[:content])
end
end
end
result
end
#
def average()
# See: https://stackoverflow.com/questions/1341271/how-do-i-create-an-average-from-a-ruby-array
self.instance_eval { reduce(:+).to_f / size.to_f }
end
#
end
# OpenStruct support for Arrays.
module GxG
class ArrayWrapper
# Review : match all public methods of a conventional array.
def initialize(the_array=nil)
@data = the_array
end
#
def table()
@data
end
#
def [](*args)
result = @data[*args]
#
if result.is_any?(::Array, ::GxG::Database::PersistedArray, ::GxG::Database::DetachedArray)
result = ::GxG::ArrayWrapper.new(result)
else
if result.is_any?(::Hash, ::GxG::Database::PersistedHash, ::GxG::Database::DetachedHash)
result = ::OpenStruct.new(result)
end
end
#
result
end
#
def []=(the_index, the_value)
if the_value.is_any?(::OpenStruct, ::GxG::ArrayWrapper)
@data[(the_index)] = the_value.table
else
@data[(the_index)] = the_value
end
end
#
def shift()
result = @data.shift
#
if result.is_any?(::Array, ::GxG::Database::PersistedArray, ::GxG::Database::DetachedArray)
result = ::GxG::ArrayWrapper.new(result)
else
if result.is_any?(::Hash, ::GxG::Database::PersistedHash, ::GxG::Database::DetachedHash)
result = ::OpenStruct.new(result)
end
end
#
result
end
#
def unshift(the_value)
if the_value.is_any?(::OpenStruct, ::GxG::ArrayWrapper)
@data.unshift the_value.table
else
@data.unshift the_value
end
end
#
def pop()
result = @data.pop
#
if result.is_any?(::Array, ::GxG::Database::PersistedArray, ::GxG::Database::DetachedArray)
result = ::GxG::ArrayWrapper.new(result)
else
if result.is_any?(::Hash, ::GxG::Database::PersistedHash, ::GxG::Database::DetachedHash)
result = ::OpenStruct.new(result)
end
end
#
result
end
#
def push(the_value)
if the_value.is_any?(::OpenStruct, ::GxG::ArrayWrapper)
@data.push the_value.table
else
@data.push the_value
end
end
#
def each(&block)
if block.respond_to?(:call)
@data.each_index do |index|
block.call(self[(index)])
end
self
else
self.to_enum(:each)
end
end
#
def each_index(&block)
if block.respond_to?(:call)
@data.each_index do |index|
block.call(index)
end
self
else
self.to_enum(:each_index)
end
end
#
def each_with_index(&block)
if block.respond_to?(:call)
@data.each_index do |index|
block.call(self[(index)], index)
end
self
else
self.to_enum(:each_with_index)
end
end
#
def first()
self[0]
end
#
def last()
self[-1]
end
#
def include?(the_value)
result = false
if the_value.is_any?(::OpenStruct, ::GxG::ArrayWrapper)
the_value = the_value.table
end
@data.each do |element|
if element == the_value
result = true
break
end
end
result
end
#
def find_index(the_value)
result = nil
if the_value.is_any?(::OpenStruct, ::GxG::ArrayWrapper)
the_value = the_value.table
end
@data.each_with_index do |element, indexer|
if element == the_value
result = indexer
break
end
end
result
end
#
def method_missing(the_method, *args)
unless the_method.to_sym == :"[]" || the_method == :"[]="
if @data.respond_to?(the_method.to_sym)
@data.send(the_method.to_sym, *args)
else
begin
super
rescue NoMethodError => err
err.backtrace.shift
raise!
end
end
end
end
end
#
end
#
# OpenStruct Modifications to support nested structures.
class OpenStruct
def new_ostruct_member!(name) # :nodoc:
unless @table.key?(name) || is_method_protected!(name)
define_singleton_method!(name) { self[name] }
define_singleton_method!("#{name}=") {|x| self[name] = x}
end
end
private :new_ostruct_member!
#
private def method_missing(mid, *args) # :nodoc:
len = args.length
if mname = mid[/.*(?==\z)/m]
if len != 1
raise! ArgumentError, "wrong number of arguments (given #{len}, expected 1)", caller(1)
end
if @table.respond_to?(mname.to_sym)
@table.send(mname.to_sym, args[0])
else
set_ostruct_member_value!(mname, args[0])
end
elsif len == 0
self[(mid)]
else
begin
super
rescue NoMethodError => err
err.backtrace.shift
raise!
end
end
end
#
public
#
def [](name)
if @table[name.to_sym].is_any?(::Hash, ::GxG::Database::PersistedHash, ::GxG::Database::DetachedHash)
OpenStruct.new(@table[name.to_sym])
else
if @table[name.to_sym].is_any?(::GxG::Database::PersistedArray, ::GxG::Database::DetachedArray)
@table[name.to_sym].as_structure(true)
end
@table[name.to_sym]
end
end
#
def []=(name, value)
name = name.to_sym
new_ostruct_member!(name)
if @table[name].is_a?(::GxG::Database::PersistedHash)
if ! value.is_a?(::GxG::Database::DetachedHash)
if value.is_a?(::OpenStruct)
if ! value.table.is_a?(::GxG::Database::DetachedHash)
@table[name] = value.table
else
# makes a copy of the detached data
@table[name] = value.table.unpersist()
end
else
@table[name] = value
end
else
# makes a copy of the detached data
@table[name] = value.unpersist()
end
else
if @table[name].is_a?(::GxG::Database::DetachedHash)
if ! value.is_a?(::GxG::Database::PersistedHash)
if value.is_a?(::OpenStruct)
if ! value.table.is_a?(::GxG::Database::PersistedHash)
@table[name] = value.table
else
# makes a copy of the persisted data
@table[name] = value.table.unpersist()
end
else
@table[name] = value
end
else
# makes a copy of the persisted data
@table[name] = value.unpersist()
end
else
@table[name] = value
end
end
end
alias_method :set_ostruct_member_value!, :[]=
private :set_ostruct_member_value!
#
def save()
if @table.respond_to?(:save)
@table.save()
else
false
end
end
#
end
#