summaryrefslogtreecommitdiff
path: root/ruby/spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'ruby/spec.rb')
-rw-r--r--ruby/spec.rb291
1 files changed, 291 insertions, 0 deletions
diff --git a/ruby/spec.rb b/ruby/spec.rb
new file mode 100644
index 0000000000..ad04a4a91f
--- /dev/null
+++ b/ruby/spec.rb
@@ -0,0 +1,291 @@
+#
+# Copyright (c) 2006 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "rexml/document"
+require "set"
+require "fields"
+require "traverse"
+require "delegate"
+
+module Spec
+
+ include REXML
+
+ class Container < Array
+
+ def initialize()
+ @cache = {}
+ end
+
+ def [](key)
+ return @cache[key] if @cache.include?(key)
+
+ case key
+ when String
+ value = find {|x| x.name == key.intern()}
+ when Symbol
+ value = find {|x| x.name == key}
+ when Integer
+ value = find {|x| x.id == key}
+ else
+ raise Exception.new("invalid key: #{key}")
+ end
+
+ @cache[key] = value
+ return value
+ end
+
+ end
+
+ class Root
+ fields(:major, :minor, :classes, :constants, :domains)
+
+ def ruby_method(name)
+ classes.each do |c|
+ c.methods.each do |m|
+ if name == m.ruby_name
+ return m
+ end
+ end
+ end
+ end
+ end
+
+ class Constant
+ fields(:name, :id, :type, :docs)
+ end
+
+ class Domain
+ fields(:name, :type)
+ end
+
+ class Class
+ fields(:name, :id, :handler, :fields, :methods, :docs)
+ end
+
+ class Method
+ fields(:name, :id, :content?, :responses, :synchronous?, :fields,
+ :docs)
+
+ def init()
+ @response = false
+ end
+
+ attr :parent, true
+
+ def response?; @response end
+ def response=(b); @response = b end
+
+ def ruby_name
+ Spec.rubyize(:"#{parent.name}_#{name}")
+ end
+ end
+
+ class Field
+ fields(:name, :id, :type, :docs)
+
+ def ruby_name
+ Spec.rubyize(name)
+ end
+
+ def default
+ case type
+ when :bit then false
+ when :octet, :short, :long, :longlong then 0
+ when :shortstr, :longstr then ""
+ when :table then {}
+ end
+ end
+
+ end
+
+ class Doc
+ fields(:type, :text)
+ end
+
+ class Reference
+
+ fields(:name)
+
+ def init(&block)
+ @resolver = block
+ end
+
+ def resolve(spec, klass)
+ @resolver.call(spec, klass)
+ end
+
+ end
+
+ class Loader
+
+ def initialize()
+ @stack = []
+ end
+
+ def load(obj)
+ case obj
+ when String
+ elem = @stack[-1]
+ result = Container.new()
+ elem.elements.each(obj) {|e|
+ @index = result.size
+ result << load(e)
+ }
+ @index = nil
+ return result
+ else
+ elem = obj
+ @stack << elem
+ begin
+ result = send(:"load_#{elem.name}")
+ ensure
+ @stack.pop()
+ end
+ return result
+ end
+ end
+
+ def element
+ @stack[-1]
+ end
+
+ def text
+ element.text
+ end
+
+ def attr(name, type = :string, default = nil)
+ value = element.attributes[name]
+ value = value.strip() unless value.nil?
+ value = nil unless value.nil? or value.any?
+ if value.nil? and not default.nil? then
+ default
+ else
+ send(:"parse_#{type}", value)
+ end
+ end
+
+ def parse_int(value)
+ value.to_i
+ end
+
+ TRUE = ["yes", "true", "1"].to_set
+ FALSE = ["no", "false", "0", nil].to_set
+
+ def parse_bool(value)
+ if TRUE.include?(value)
+ true
+ elsif FALSE.include?(value)
+ false
+ else
+ raise Exception.new("parse error, expecting boolean: #{value}")
+ end
+ end
+
+ def parse_string(value)
+ value.to_s
+ end
+
+ def parse_symbol(value)
+ value.intern() unless value.nil?
+ end
+
+ def load_amqp()
+ Root.new(attr("major", :int), attr("minor", :int), load("class"),
+ load("constant"), load("domain"))
+ end
+
+ def load_class()
+ Class.new(attr("name", :symbol), attr("index", :int), attr("handler", :symbol),
+ load("field"), load("method"), load("doc"))
+ end
+
+ def load_method()
+ Method.new(attr("name", :symbol), attr("index", :int),
+ attr("content", :bool), load("response"),
+ attr("synchronous", :bool), load("field"), load("docs"))
+ end
+
+ def load_response()
+ name = attr("name", :symbol)
+ Reference.new {|spec, klass|
+ response = klass.methods[name]
+ if response.nil?
+ raise Exception.new("no such method: #{name}")
+ end
+ response
+ }
+ end
+
+ def load_field()
+ type = attr("type", :symbol)
+ if type.nil?
+ domain = attr("domain", :symbol)
+ type = Reference.new {|spec, klass|
+ spec.domains[domain].type
+ }
+ end
+ Field.new(attr("name", :symbol), @index, type, load("docs"))
+ end
+
+ def load_constant()
+ Constant.new(attr("name", :symbol), attr("value", :int), attr("class", :symbol),
+ load("doc"))
+ end
+
+ def load_domain()
+ Domain.new(attr("name", :symbol), attr("type", :symbol))
+ end
+
+ def load_doc()
+ Doc.new(attr("type", :symbol), text)
+ end
+
+ end
+
+ def Spec.load(spec)
+ case spec
+ when String
+ spec = File.new(spec)
+ end
+ doc = Document.new(spec)
+ spec = Loader.new().load(doc.root)
+ spec.classes.each do |klass|
+ klass.traverse! do |o|
+ case o
+ when Reference
+ o.resolve(spec, klass)
+ else
+ o
+ end
+ end
+ klass.methods.each do |m|
+ m.parent = klass
+ m.responses.each do |r|
+ r.response = true
+ end
+ end
+ end
+ spec
+ end
+
+ private
+
+ def Spec.rubyize(name)
+ name.to_s.gsub(/[\s-]/, '_').intern()
+ end
+
+end