Something promised is something due. Only two days have passed since my last post, Ruby Inox Part4: Property. Two days human time span is like a decade of internet time span. Some bits stayed the same and some changed. Notable changes were made to the module Actions and the module Properties, stepping closer to the final goal.
In this article its all about Components!
Motivation
We are slowly, but steady advancing toward a rich OO toolkit written in ruby. The most important building block for such a toolkit is the father object for the rest of the hierarchy which I will call Component. It should possess the following criteria:
- should have a parent
- should be able to have children
- should have default actions
- should be persistent
1) Parent
All the user created components have a parent instance. If no parent is given than the very first existing object named Inox is the parent of the component. This is to ensure that every Component is tracked at any time.
The actual hierarchy of f.ex a button component should be as follow
- Inox
- Hardware
- OS
- Ruby
- Application
- Window
- Button
Note that for practical reasons at the time of this writing the following objects, and only existing objects, are singleton instances: Application
The Hardware object should contain all the useful methods for querying or using hardware. The OS part should contain the methods for communicating with the OS. Ruby represents the interpreter and so on. As Singleton Components their instance can be accessed at anytime.
At any time a component has only one parent. If that parent is disposed all of its children gets disposed to. So if a user created Application is closed, its children are also closed. But if a component is attached to Ruby and used in Application. And Application get closed, the Component is still available.
At the ruby level we could start one Application as background worker and a second Application as GUI.
Application is used later to describe a general Application class which is a subclass of Component; and doesn’t need to be tight to a GUI.
Up until now we have a parent attribute and an action: parent_changed, which get called with the old parent as argument when a new parent is set.
2) Children
A component should also be able to contain children, but it’s not requirement, it’s more a possible feature of a Component. So the specific interface for handling children is defined as includable module.
The module Container defines the methods add_child, remove_child, has an attribute children and includes the ruby’s Enumerable module. It also defines two actions: child_added and child_removed
Object is extended with container? method so that every object can be queried.
3) Default Actions
So far the default actions are self-explanatory: create, dispose, parent_changed. Every Component can be created. Conforms to actions; and it can be assigned a block of code to execute with the on_create method, it can be assigned a Proc/Method with simple assignment create=, it can be executed by calling create and at last there is a ‘create!’ (create bang) method which is called implicitly by create and is meant for subclasses to override.
More on actions: Ruby Part 3.1: Actions
4) Persistency
Marshall comes and he rescues us with the following small limitation: it cannot serialize the actions. No Proc, no Method and no block.
I can imagine a solution for serializing the action/Method between two components that could be sufficient to get started with:
# adding dumping and loading of method class Method attr_accessor :target, :method_sym def _dump(_) raise "expect method from obj.methdo(sym)" if @target = nil r = Marshal.dump(@target) r << ':' << @method_sym.to_s r end def self._load(s) obj = Marshal.load(s) sym = s.split(':').last.to_sym obj.method(sym) end end # overriding Object.method class Object def method(sym) m = super(sym) m.target = self m.name = sym m end end # Test class Foo attr_accessor :test def initialize(t) @test = t end end class B def test_method puts "Hello World" end end b = B.new foo = Foo.new( b.method(:test_method) ) c = Marshal.dump(foo) d = Marshal.load(c) d.test.call => "Hello World"
There are still some complication at sight, but we are way to early to address this issues here. Just remember that a Component should be able to get stored and loaded, no matter what this feature costs!
Source Code: Component
class ::Object def self.container?; false; end def container?; self.class.container?; end end class Component include Actions include Properties attr_accessor :parent actions :create, :dispose, :parent_changed def initialize(the_parent = nil, *args, &block) self.parent = the_parent create(*args, &block) end def parent=(obj) assert2(obj).nil?.or.kind_of?(Component).end old_parent = @parent @parent = obj parent_changed(old_parent) end def dispose! children.each { |c| c.dispose } if container? parent.remove_child(self) unless parent.nil? or not parent.container? end end module Container def self.included(base) with base do include Enumerable actions :child_added, :child_removed def self.container?; true; end include InstanceMethods end end module InstanceMethods def children; @children ||= []; end def add_child(obj) assert2(obj).nil?.or.kind_of?(Component).end children << obj obj.parent = self child_added(obj) end alias << add_child def remove_child(obj) assert2(obj).not.nil?.and.kind_of?(Component).end res = children.delete(obj) unless res.nil? res.parent = nil child_removed(res) end end end end
To be continued
This show will go on! The next article will get to the really hot part. The first true visual component class in ruby.
Ruby Inox on GitHub
This file is part of the Ruby Inox project. Inox is a work in progress and this file might not be up to date. The status and latest files are available on GitHub
Related posts:
- Ruby Inox Part 6: Widget We are steering toward a graphical user interface. In this...
- Ruby Inox Part 7: Native Originally this article formed one article with Ruby Inox Part...
- Ruby Inox Part 3.1: Actions Here is a short update for the Ruby Inox Part3:...
- Ruby Inox Part 3: Actions Here is the pursuit of a series of related articles...
- Ruby Inox Part 4.1: Property In Ruby Inox Part 4: Property I described I would...
- Keyword With From other programming language I know a interesting feature; the...



ShareThis