lib/origen/sub_blocks.rb in origen-0.27.0 vs lib/origen/sub_blocks.rb in origen-0.28.0
- old
+ new
@@ -210,11 +210,11 @@
sub_block(*args)
end
end
alias_method :children, :sub_blocks
- # Delete all sub_blocks by emptyig the Hash
+ # Delete all sub_blocks by emptying the Hash
def delete_sub_blocks
@sub_blocks = {}
end
def sub_blocks_array
@@ -263,59 +263,144 @@
define_singleton_method "#{name}s" do
a
end
a
else
- class_name = options.delete(:class_name)
- if class_name
- if eval("defined? ::#{namespace}::#{class_name}")
- klass = eval("::#{namespace}::#{class_name}")
+ block = Placeholder.new(self, name, options)
+ if sub_blocks[name]
+ # Allow additional attributes to be added to an existing sub-block if it hasn't
+ # been instantiated yet. This is not supported yet for instantiated sub-blocks since
+ # there are probably a lot more corner-cases to consider, and hopefully no one will
+ # really need this anyway.
+ if sub_blocks[name].is_a?(Placeholder)
+ sub_blocks[name].add_attributes(options)
else
- if eval("defined? #{class_name}")
- klass = eval(class_name)
- else
- if eval("defined? #{self.class}::#{class_name}")
- klass = eval("#{self.class}::#{class_name}")
- else
- puts "Could not find class: #{class_name}"
- fail 'Unknown sub block class!'
- end
- end
+ fail "You have already defined a sub-block named #{name} within class #{self.class}"
end
else
- klass = Origen::SubBlock
- end
- unless klass.respond_to?(:includes_origen_model)
- puts 'Any class which is to be instantiated as a sub_block must include Origen::Model,'
- puts "add this to #{klass}:"
- puts ''
- puts ' include Origen::Model'
- puts ''
- fail 'Sub block does not include Origen::Model!'
- end
- block = klass.new(options.merge(parent: self, name: name))
- if sub_blocks[name]
- fail "You have already defined a sub-block named #{name} within class #{self.class}"
- else
sub_blocks[name] = block
- if respond_to?(name)
- # puts "Tried to create a sub-block named #{name} in #{self.class}, but it already has a method with this name!"
- # puts "To avoid confusion rename one of them and try again!"
- # raise "Non-unique sub-block name!"
- else
- define_singleton_method name do
- sub_blocks[name]
- end
- end
end
+ define_singleton_method name do
+ get_sub_block(name)
+ end
block
end
end
def namespace
self.class.to_s.sub(/::[^:]*$/, '')
end
+
+ private
+
+ def get_sub_block(name)
+ sub_blocks[name]
+ end
+
+ def instantiate_sub_block(name, klass, options)
+ return sub_blocks[name] unless sub_blocks[name].is_a?(Placeholder)
+ sub_blocks[name] = klass.new(options.merge(parent: self, name: name))
+ end
+
+ class Placeholder
+ attr_reader :name, :owner, :attributes
+
+ def initialize(owner, name, attributes)
+ @owner = owner
+ @name = name
+ @attributes = attributes
+ end
+
+ def add_attributes(attrs)
+ @attributes = @attributes.merge(attrs)
+ end
+
+ # Make this appear like a sub-block to any application code
+ def is_a?(klass)
+ klass == self.klass || klass == self.class
+ end
+
+ # Make it look like a sub-block in the console to avoid confusion
+ def inspect
+ "<SubBlock: #{name}>"
+ end
+
+ def method_missing(method, *args, &block)
+ materialize.send(method, *args, &block)
+ end
+
+ def respond_to?(method, include_private = false)
+ materialize.respond_to?(method, include_private)
+ end
+
+ def materialize
+ file = attributes.delete(:file)
+ block = owner.send(:instantiate_sub_block, name, klass, attributes)
+ if file
+ require File.join(owner.send(:export_dir), file)
+ block.extend owner.send(:export_module_names_from_path, file).join('::').constantize
+ end
+ block
+ end
+
+ def ==(obj)
+ materialize == obj
+ end
+
+ def !=(obj)
+ materialize != obj
+ end
+
+ def freeze
+ materialize.freeze
+ end
+
+ def clone
+ materialize.clone
+ end
+
+ def dup
+ materialize.dup
+ end
+
+ def to_json(*args)
+ materialize.to_json(*args)
+ end
+
+ def klass
+ @klass ||= begin
+ class_name = attributes.delete(:class_name)
+ if class_name
+ if eval("defined? ::#{owner.namespace}::#{class_name}")
+ klass = eval("::#{owner.namespace}::#{class_name}")
+ else
+ if eval("defined? #{class_name}")
+ klass = eval(class_name)
+ else
+ if eval("defined? #{owner.class}::#{class_name}")
+ klass = eval("#{owner.class}::#{class_name}")
+ else
+ puts "Could not find class: #{class_name}"
+ fail 'Unknown sub block class!'
+ end
+ end
+ end
+ else
+ klass = Origen::SubBlock
+ end
+ unless klass.respond_to?(:includes_origen_model)
+ puts 'Any class which is to be instantiated as a sub_block must include Origen::Model,'
+ puts "add this to #{klass}:"
+ puts ''
+ puts ' include Origen::Model'
+ puts ''
+ fail 'Sub block does not include Origen::Model!'
+ end
+ klass
+ end
+ end
+ end
end
# A simple class that will be instantiated by default when a sub block is
# defined without another class name specified
#
@@ -326,9 +411,11 @@
# Used to create attribute accessors on the fly.
#
# On first call of a missing method a method is generated to avoid the missing lookup
# next time, this should be faster for repeated lookups of the same method, e.g. reg
def method_missing(method, *args, &block)
+ super
+ rescue NoMethodError
return regs(method) if self.has_reg?(method)
return ports(method) if self.has_port?(method)
if method.to_s =~ /=$/
define_singleton_method(method) do |val|
instance_variable_set("@#{method.to_s.sub('=', '')}", val)