Welcome to the "trac"-ing site of soap4r!
[soap4r] [httpclient] [openpgp4u] [pkcs1] [logger] [csv] [vtr]

root/branches/1_5/lib/soap/property.rb

Revision 2015, 7.0 kB (checked in by nahi, 13 hours ago)
  • ruby-1.8.7 warning cleanups. closes #497.
  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 # soap/property.rb: SOAP4R - Property implementation.
2 # Copyright (C) 2000-2007  NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
4 # This program is copyrighted free software by NAKAMURA, Hiroshi.  You can
5 # redistribute it and/or modify it under the same terms of Ruby's license;
6 # either the dual license version in 2003, or any later version.
7
8
9 module SOAP
10
11
12 # Property stream format:
13 #
14 #   line separator is \r?\n.  1 line per a property.
15 #   line which begins with '#' is a comment line.  empty line is ignored, too.
16 #   key/value separator is ':' or '='.
17 #   '\' as escape character.  but line separator cannot be escaped.
18 #   \s at the head/tail of key/value are trimmed.
19 #
20 #   '[' + key + ']' indicates property section.  for example,
21 #
22 #     [aaa.bbb]
23 #     ccc = ddd
24 #     eee.fff = ggg
25 #     []
26 #     aaa.hhh = iii
27 #
28 #   is the same as;
29 #
30 #     aaa.bbb.ccc = ddd
31 #     aaa.bbb.eee.fff = ggg
32 #     aaa.hhh = iii
33 #
34 class Property
35   FrozenError = (RUBY_VERSION >= "1.9.0") ? RuntimeError : TypeError
36
37   include Enumerable
38
39   module Util
40     def const_from_name(fqname)
41       fqname.split("::").inject(Kernel) { |klass, name| klass.const_get(name) }
42     end
43     module_function :const_from_name
44
45     def require_from_name(fqname)
46       require File.join(fqname.split("::").collect { |ele| ele.downcase })
47     end
48     module_function :require_from_name
49   end
50
51   def self.load(stream)
52     new.load(stream)
53   end
54
55   def self.loadproperty(propname)
56     new.loadproperty(propname)
57   end
58
59   def initialize
60     @store = Hash.new
61     @hook = Hash.new
62     @self_hook = Array.new
63     @locked = false
64   end
65
66   KEY_REGSRC = '([^=:\\\\]*(?:\\\\.[^=:\\\\]*)*)'
67   DEF_REGSRC = '\\s*' + KEY_REGSRC + '\\s*[=:]\\s*(.*)'
68   COMMENT_REGEXP = Regexp.new('^(?:#.*|)$', nil, 'u')
69   CATDEF_REGEXP = Regexp.new("^\\[\\s*#{KEY_REGSRC}\\s*\\]$", nil, 'u')
70   LINE_REGEXP = Regexp.new("^#{DEF_REGSRC}$", nil, 'u')
71   def load(stream)
72     key_prefix = ""
73     lineno = 0
74     stream.each_line do |line|
75       line.sub!(/\r?\n\z/u, '')
76       lineno += 1
77       case line
78       when COMMENT_REGEXP
79         next
80       when CATDEF_REGEXP
81         key_prefix = $1.strip
82       when LINE_REGEXP
83         key, value = $1.strip, $2.strip
84         key = "#{key_prefix}.#{key}" unless key_prefix.empty?
85         key, value = loadstr(key), loadstr(value)
86         self[key] = value
87       else
88         raise TypeError.new(
89           "property format error at line #{lineno}: `#{line}'")
90       end
91     end
92     self
93   end
94
95   # find property from $:.
96   def loadproperty(propname)
97     return loadpropertyfile(propname) if File.file?(propname)
98     $:.each do |path|
99       if File.file?(file = File.join(path, propname))
100         return loadpropertyfile(file)
101       end
102     end
103     nil
104   end
105
106   # name: a Symbol, String or an Array
107   def [](name)
108     referent(name_to_a(name))
109   end
110
111   # name: a Symbol, String or an Array
112   # value: an Object
113   def []=(name, value)
114     name_pair = name_to_a(name).freeze
115     hooks = assign(name_pair, value)
116     hooks.each do |hook|
117       hook.call(name_pair, value)
118     end
119     value
120   end
121
122   # value: an Object
123   # key is generated by property
124   def <<(value)
125     self[generate_new_key] = value
126   end
127
128   # name: a Symbol, String or an Array; nil means hook to the root
129   # cascade: true/false; for cascading hook of sub key
130   # hook: block which will be called with 2 args, name and value
131   def add_hook(name = nil, cascade = false, &hook)
132     if name == nil or name == true or name == false
133       cascade = name
134       assign_self_hook(cascade, &hook)
135     else
136       assign_hook(name_to_a(name), cascade, &hook)
137     end
138   end
139
140   def each
141     @store.each do |key, value|
142       yield(key, value)
143     end
144   end
145
146   def empty?
147     @store.empty?
148   end
149
150   def keys
151     @store.keys
152   end
153
154   def values
155     @store.values
156   end
157
158   def lock(cascade = false)
159     if cascade
160       each_key do |key|
161         key.lock(cascade)
162       end
163     end
164     @locked = true
165     self
166   end
167
168   def unlock(cascade = false)
169     @locked = false
170     if cascade
171       each_key do |key|
172         key.unlock(cascade)
173       end
174     end
175     self
176   end
177
178   def locked?
179     @locked
180   end
181
182 protected
183
184   def deref_key(key)
185     check_lock(key)
186     ref = @store[key] ||= self.class.new
187     unless propkey?(ref)
188       raise ArgumentError.new("key `#{key}' already defined as a value")
189     end
190     ref
191   end
192
193   def local_referent(key)
194     check_lock(key)
195     if propkey?(@store[key]) and @store[key].locked?
196       raise FrozenError.new("cannot split any key from locked property")
197     end
198     @store[key]
199   end
200
201   def local_assign(key, value)
202     check_lock(key)
203     if @locked
204       if propkey?(value)
205         raise FrozenError.new("cannot add any key to locked property")
206       elsif propkey?(@store[key])
207         raise FrozenError.new("cannot override any key in locked property")
208       end
209     end
210     @store[key] = value
211   end
212
213   def local_hook(key, direct)
214     hooks = []
215     (@self_hook + (@hook[key] || NO_HOOK)).each do |hook, cascade|
216       hooks << hook if direct or cascade
217     end
218     hooks
219   end
220
221   def local_assign_hook(key, cascade, &hook)
222     check_lock(key)
223     @store[key] ||= nil
224     (@hook[key] ||= []) << [hook, cascade]
225   end
226
227 private
228
229   NO_HOOK = [].freeze
230
231   def referent(ary)
232     ary[0..-2].inject(self) { |ref, name|
233       ref.deref_key(to_key(name))
234     }.local_referent(to_key(ary.last))
235   end
236
237   def assign(ary, value)
238     ref = self
239     hook = NO_HOOK
240     ary[0..-2].each do |name|
241       key = to_key(name)
242       hook += ref.local_hook(key, false)
243       ref = ref.deref_key(key)
244     end
245     last_key = to_key(ary.last)
246     ref.local_assign(last_key, value)
247     hook + ref.local_hook(last_key, true)
248   end
249
250   def assign_hook(ary, cascade, &hook)
251     ary[0..-2].inject(self) { |ref, name|
252       ref.deref_key(to_key(name))
253     }.local_assign_hook(to_key(ary.last), cascade, &hook)
254   end
255
256   def assign_self_hook(cascade, &hook)
257     check_lock(nil)
258     @self_hook << [hook, cascade]
259   end
260
261   def each_key
262     self.each do |key, value|
263       if propkey?(value)
264         yield(value)
265       end
266     end
267   end
268
269   def check_lock(key)
270     if @locked and (key.nil? or !@store.key?(key))
271       raise FrozenError.new("cannot add any key to locked property")
272     end
273   end
274
275   def propkey?(value)
276     value.is_a?(::SOAP::Property)
277   end
278
279   def name_to_a(name)
280     case name
281     when Symbol
282       [name]
283     when String
284       name.scan(/[^.\\]+(?:\\.[^.\\])*/u)       # split with unescaped '.'
285     when Array
286       name
287     else
288       raise ArgumentError.new("Unknown name #{name}(#{name.class})")
289     end
290   end
291
292   def to_key(name)
293     name.to_s.downcase
294   end
295
296   def generate_new_key
297     if @store.empty?
298       "0"
299     else
300       (key_max + 1).to_s
301     end
302   end
303
304   def key_max
305     (@store.keys.max { |l, r| l.to_s.to_i <=> r.to_s.to_i }).to_s.to_i
306   end
307
308   def loadpropertyfile(file)
309     puts "find property at #{file}" if $DEBUG
310     File.open(file) do |f|
311       load(f)
312     end
313   end
314
315   def loadstr(str)
316     str.gsub(/\\./u) { |c| eval("\"#{c}\"") }
317   end
318 end
319
320
321 end
322
323
324 # for ruby/1.6.
325 unless Enumerable.instance_methods.include?('inject')
326   module Enumerable
327     def inject(init)
328       result = init
329       each do |item|
330         result = yield(result, item)
331       end
332       result
333     end
334   end
335 end
Note: See TracBrowser for help on using the browser.