Models
Generic Components
So far we’ve covered SubBlocks, Fuses, Power Domains and other objects. Each of these has its particular use, and its instantiation and customized behavior reflects that, but what about a generic component container?
Origen has one of those too, and it behaves similarly to the aforementioned models. The advantage
is that you can get a SubBlock-like
or Fuse-like
interface without having your
generic components registered as a SubBlock
or Fuse
and without any extra overhead. The
disadvantage is that it does nothing else, and functions much like a stripped-down SubBlock
object.
However, this allows you to add and use generic components that could be anything. These components don’t require a base
address, or any other options (otherwise you’d use a SubBlock
) but will still initialize and be kept
track for you.
For example, if I have a class: scan_helper
, which I’ve implemented as something similar to a
SubBlock
, but that doesn’t have a base_address, registers, or anything like that, I can
instantiate it as a component instead:
component(:scan_helper, class_name: ScanHelper)
#=> ScanHelper instance with name :scan_helper
I can then get this item as I would a sub_block
, fuse
, etc.:
components[:scan_helper] #=> ScanHelper instance
self.scan_helper #=> ScanHelper instance
Seems pretty basic, right? Good! That’s what we’re going for! Below is the API that you can do with components.
You can add components in a few different ways:
# Add a component with default class Origen::Component::Default
component(:comp1)
# Add another component. Notice the use component(s).
components(:comp2)
# Add a component of class MyComponent
add_component(:mine, class_name: MyComponent)
# Method add_components also works, as does using a string as the class name
add_components(:also_mine, class_name: 'MyComponent')
# Add a component using block notation
add_components(:block_add) do |comp|
comp.class_name 'MyComponent'
end
# Add another one using block notation
components(:block_add_2) do |c|
c.class_name 'MyComponent'
end
Now, if we call just components
, we’ll get the resulting Hash storing these. So,
components.keys
#=> ["comp1", "comp2", "mine", "also_mine", "block_add", "block_add_2"]
Notice the switch from :comp1
to "comp1"
. This is because the underlying Hash
is with_indifferent_access.
Therefore, you can get components from the Hash using either a String
or a Symbol
,
but when listing, comparing, etc., the keys will be returned as a String
You can add multiple instances at once by passing in the :instances
option. This will auto-append an
appendix number to each item.
For example:
add_component(:multiple, instances: 4)
components.keys
#=> ["multiple0", "multiple1", "multiple2", "multiple3"]
# In addition, this will make an accessor for each one. So, the following are now defined:
multiple0 #=> :multiple0 Component
multiple1 #=> :multiple1 Component
multiple2 #=> :multiple2 Component
multiple3 #=> :multiple3 Component
You can pass other options in as well. Single options will be applied to all instances. A single option being
either not an Array
object, or an Array
object of size 1.
Options that are Arrays
longer than size 1 will be indexed to each instances. For example, if you indicate
four instances of a component, Array index 0 will go to instance 0, Array index index 1 to instance 1, and so on. Providing
an incompatible Array length will result in an Origen::Componentable::Error
.
add_component(:multiple, instances: 4, letter: ['A', 'B', 'C', 'D'], number: 0)
multiple0 #=> :multiple0 Component with parameters: letter: 'A', number: 0
multiple1 #=> :multiple1 Component with parameters: letter: 'B', number: 0
multiple2 #=> :multiple2 Component with parameters: letter: 'C', number: 0
multiple3 #=> :multiple3 Component with parameters: letter: 'D', number: 0
add_component(:more, instances: 3, letter: ['A', 'B', 'C', 'D'], number: 0)
#=> Origen::Componentable::Error
The corner case of wanting a static-sized array given to each instance can be overcome by embedding that Array in
another Array of size 1. For example, the options instance: 2, cores: [:cm0, :cm3, :cm4]
will result in
an Origen::Componentable::Error
but, instance: 2, cores: [[:cm0, :cm3, :cm4]]
will be fine
and results in both instances getting the option cores: [:cm0, :cm3, :cm4]
.
You can copy and move components using these methods:
copy_component(:comp1, :comp1_copy)
# Method copy_components is an alias
components.keys
#=> ["comp1", "comp2", "mine", "also_mine", "block_add", "block_add_2", "comp1_copy"]
move_component(:comp1_copy, :comp1_moved)
# Method move_components is an alias
components.keys
#=> ["comp1", "comp2", "mine", "also_mine", "block_add", "block_add_2", "comp1_moved"]
Note that copying components will actually clone the component. So, you get an independent instance of the object that you copied from.
Listing the components will show you just the names. This is the equivalent of using
components.keys
:
list_components
#=> ["comp1", "comp2", "mine", "also_mine", "block_add", "block_add_2", "comp1_moved"]
You can check if a component has been added using the has_component?
method:
has_component?("comp1") #=> true
has_component?(:comp1) #=> true
has_component?("comp_dne") #=> false
has_component?(:comp_dne) #=> false
# component?(name) is an alias method name
You can get components using either the index notation or by utiliting the accessor given
during the add
process:
components[:comp1] #=> component at name :comp1
comp1 #=> same as calling components[:comp1]
There’s a method available that can query the current classes of the added components.
components_of_class(MyComponent)
#=> ["mine", "also_mine", "block_add"]
# components_instances_of and components_of_type are aliases
Component exposes two commonly used enumerable methods: each
and select
# All of these call the :each method
components.each do |name, component|
# do something
end
each_component do |name, component|
# do something
end
all_components do |name, component|
# do something
end
select_components do |name, component|
# do something
end
You can use other enumerable methods provided by the Hash
class by using
components._enumerable_method_
You can delete components that you’ve previously added. This will return the deleted item, or raise an exception if the name does not exist.
list_components
#=> ["comp1", "comp2", "mine", "also_mine", "block_add", "block_add_2", "comp1_moved"]
delete_component("comp1_moved")
#=> Component at comp1_moved
list_components
#=> ["comp1", "comp2", "mine", "also_mine", "block_add", "block_add_2"]
# Methods delete_components, remove_component, and remove_components, are all aliases
You can also clear the entire component structure and start fresh. Note this this will not return any components.
list_components
#=> ["comp1", "comp2", "mine", "also_mine", "block_add", "comp1_moved"]
delete_all_components
list_components
#=> []
# Methods clear_components and remove_all_components are aliases
Componentable
mixin.
If you add lots of components, or if you add lots of components, subblocks, fuses, etc., it may get messy having each
one try to put its own accessor on your model. If you find yourself not wanting components to place accessors on your
model, you can define a method :disable_componentable_accessors(klass)
and have it return true
if klass
is of type Origen::Component
. In other words:
def disable_componentable_accessors(klass)
if klass == Origen::Component
true
else
false
end
end
true
value will result in all Componentable
objects withholding accessors from your model, including components
. For more details on this, please
see the Componentable Guide.
Component
is an example of a Origen::Componentable
mixin. For information on using this module
in your own classes to get a component-like interface, please see the topic here