class SOAP::Property

Property stream format:

line separator is \r?\n.  1 line per a property.
line which begins with '#' is a comment line.  empty line is ignored, too.
key/value separator is ':' or '='.
'\' as escape character.  but line separator cannot be escaped.
\s at the head/tail of key/value are trimmed.

'[' + key + ']' indicates property section.  for example,

  [aaa.bbb]
  ccc = ddd
  eee.fff = ggg
  []
  aaa.hhh = iii

is the same as;

  aaa.bbb.ccc = ddd
  aaa.bbb.eee.fff = ggg
  aaa.hhh = iii

Constants

CATDEF_REGEXP
COMMENT_REGEXP
DEF_REGSRC
FrozenError
KEY_REGSRC
LINE_REGEXP
NO_HOOK

Public Class Methods

load(stream) click to toggle source
# File lib/soap/property.rb, line 51
def self.load(stream)
  new.load(stream)
end
loadproperty(propname) click to toggle source
# File lib/soap/property.rb, line 55
def self.loadproperty(propname)
  new.loadproperty(propname)
end
new() click to toggle source
# File lib/soap/property.rb, line 59
def initialize
  @store = Hash.new
  @hook = Hash.new
  @self_hook = Array.new
  @locked = false
end

Public Instance Methods

<<(value) click to toggle source

value: an Object key is generated by property

# File lib/soap/property.rb, line 122
def <<(value)
  self[generate_new_key] = value
end
[](name) click to toggle source

name: a Symbol, String or an Array

# File lib/soap/property.rb, line 105
def [](name)
  referent(name_to_a(name))
end
[]=(name, value) click to toggle source

name: a Symbol, String or an Array value: an Object

# File lib/soap/property.rb, line 111
def []=(name, value)
  name_pair = name_to_a(name).freeze
  hooks = assign(name_pair, value)
  hooks.each do |hook|
    hook.call(name_pair, value)
  end
  value
end
add_hook(name = nil, cascade = false, &hook) click to toggle source

name: a Symbol, String or an Array; nil means hook to the root cascade: true/false; for cascading hook of sub key hook: block which will be called with 2 args, name and value

# File lib/soap/property.rb, line 129
def add_hook(name = nil, cascade = false, &hook)
  if name == nil or name == true or name == false
    cascade = name
    assign_self_hook(cascade, &hook)
  else
    assign_hook(name_to_a(name), cascade, &hook)
  end
end
each() { |key, value| ... } click to toggle source
# File lib/soap/property.rb, line 138
def each
  @store.each do |key, value|
    yield(key, value)
  end
end
empty?() click to toggle source
# File lib/soap/property.rb, line 144
def empty?
  @store.empty?
end
keys() click to toggle source
# File lib/soap/property.rb, line 148
def keys
  @store.keys
end
load(stream) click to toggle source
# File lib/soap/property.rb, line 71
def load(stream)
  key_prefix = ""
  stream.readlines.each_with_index do |line, lineno|
    line.sub!(/\r?\n\z/u, '')
    case line
    when COMMENT_REGEXP
      next
    when CATDEF_REGEXP
      key_prefix = $1.strip
    when LINE_REGEXP
      key, value = $1.strip, $2.strip
      key = "#{key_prefix}.#{key}" unless key_prefix.empty?
      key, value = loadstr(key), loadstr(value)
      self[key] = value
    else
      raise TypeError.new(
        "property format error at line #{lineno + 1}: `#{line}'")
    end
  end
  self
end
loadproperty(propname) click to toggle source

find property from $:.

# File lib/soap/property.rb, line 94
def loadproperty(propname)
  return loadpropertyfile(propname) if File.file?(propname)
  $:.each do |path|
    if File.file?(file = File.join(path, propname))
      return loadpropertyfile(file)
    end
  end
  nil
end
lock(cascade = false) click to toggle source
# File lib/soap/property.rb, line 156
def lock(cascade = false)
  if cascade
    each_key do |key|
      key.lock(cascade)
    end
  end
  @locked = true
  self
end
locked?() click to toggle source
# File lib/soap/property.rb, line 176
def locked?
  @locked
end
unlock(cascade = false) click to toggle source
# File lib/soap/property.rb, line 166
def unlock(cascade = false)
  @locked = false
  if cascade
    each_key do |key|
      key.unlock(cascade)
    end
  end
  self
end
values() click to toggle source
# File lib/soap/property.rb, line 152
def values
  @store.values
end

Protected Instance Methods

deref_key(key) click to toggle source
# File lib/soap/property.rb, line 182
def deref_key(key)
  check_lock(key)
  ref = @store[key] ||= self.class.new
  unless propkey?(ref)
    raise ArgumentError.new("key `#{key}' already defined as a value")
  end
  ref
end
local_assign(key, value) click to toggle source
# File lib/soap/property.rb, line 199
def local_assign(key, value)
  check_lock(key)
  if @locked
    if propkey?(value)
      raise FrozenError.new("cannot add any key to locked property")
    elsif propkey?(@store[key])
      raise FrozenError.new("cannot override any key in locked property")
    end
  end
  @store[key] = value
end
local_assign_hook(key, cascade, &hook) click to toggle source
# File lib/soap/property.rb, line 219
def local_assign_hook(key, cascade, &hook)
  check_lock(key)
  @store[key] ||= nil
  (@hook[key] ||= []) << [hook, cascade]
end
local_hook(key, direct) click to toggle source
# File lib/soap/property.rb, line 211
def local_hook(key, direct)
  hooks = []
  (@self_hook + (@hook[key] || NO_HOOK)).each do |hook, cascade|
    hooks << hook if direct or cascade
  end
  hooks
end
local_referent(key) click to toggle source
# File lib/soap/property.rb, line 191
def local_referent(key)
  check_lock(key)
  if propkey?(@store[key]) and @store[key].locked?
    raise FrozenError.new("cannot split any key from locked property")
  end
  @store[key]
end

Private Instance Methods

assign(ary, value) click to toggle source
# File lib/soap/property.rb, line 235
def assign(ary, value)
  ref = self
  hook = NO_HOOK
  ary[0..-2].each do |name|
    key = to_key(name)
    hook += ref.local_hook(key, false)
    ref = ref.deref_key(key)
  end
  last_key = to_key(ary.last)
  ref.local_assign(last_key, value)
  hook + ref.local_hook(last_key, true)
end
assign_hook(ary, cascade, &hook) click to toggle source
# File lib/soap/property.rb, line 248
def assign_hook(ary, cascade, &hook)
  ary[0..-2].inject(self) { |ref, name|
    ref.deref_key(to_key(name))
  }.local_assign_hook(to_key(ary.last), cascade, &hook)
end
assign_self_hook(cascade, &hook) click to toggle source
# File lib/soap/property.rb, line 254
def assign_self_hook(cascade, &hook)
  check_lock(nil)
  @self_hook << [hook, cascade]
end
check_lock(key) click to toggle source
# File lib/soap/property.rb, line 267
def check_lock(key)
  if @locked and (key.nil? or !@store.key?(key))
    raise FrozenError.new("cannot add any key to locked property")
  end
end
each_key() { |value| ... } click to toggle source
# File lib/soap/property.rb, line 259
def each_key
  self.each do |key, value|
    if propkey?(value)
      yield(value)
    end
  end
end
generate_new_key() click to toggle source
# File lib/soap/property.rb, line 294
def generate_new_key
  if @store.empty?
    "0"
  else
    (key_max + 1).to_s
  end
end
key_max() click to toggle source
# File lib/soap/property.rb, line 302
def key_max
  (@store.keys.max { |l, r| l.to_s.to_i <=> r.to_s.to_i }).to_s.to_i
end
loadpropertyfile(file) click to toggle source
# File lib/soap/property.rb, line 306
def loadpropertyfile(file)
  puts "find property at #{file}" if $DEBUG
  File.open(file) do |f|
    load(f)
  end
end
loadstr(str) click to toggle source
# File lib/soap/property.rb, line 313
def loadstr(str)
  str.gsub(/\\./u) { |c| eval("\"#{c}\"") }
end
name_to_a(name) click to toggle source
# File lib/soap/property.rb, line 277
def name_to_a(name)
  case name
  when Symbol
    [name]
  when String
    name.scan(/[^.\\]+(?:\\.[^.\\])*/u)       # split with unescaped '.'
  when Array
    name
  else
    raise ArgumentError.new("Unknown name #{name}(#{name.class})")
  end
end
propkey?(value) click to toggle source
# File lib/soap/property.rb, line 273
def propkey?(value)
  value.is_a?(::SOAP::Property)
end
referent(ary) click to toggle source
# File lib/soap/property.rb, line 229
def referent(ary)
  ary[0..-2].inject(self) { |ref, name|
    ref.deref_key(to_key(name))
  }.local_referent(to_key(ary.last))
end
to_key(name) click to toggle source
# File lib/soap/property.rb, line 290
def to_key(name)
  name.to_s.downcase
end