trimurti 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. data/trimurti-0.1/docs/classes/Array.html +176 -0
  2. data/trimurti-0.1/docs/classes/Array.src/M000005.html +19 -0
  3. data/trimurti-0.1/docs/classes/Array.src/M000006.html +19 -0
  4. data/trimurti-0.1/docs/classes/Asserter.html +136 -0
  5. data/trimurti-0.1/docs/classes/Brahma.html +360 -0
  6. data/trimurti-0.1/docs/classes/Brahma.src/M000029.html +21 -0
  7. data/trimurti-0.1/docs/classes/Brahma.src/M000030.html +20 -0
  8. data/trimurti-0.1/docs/classes/Brahma.src/M000031.html +36 -0
  9. data/trimurti-0.1/docs/classes/Brahma.src/M000032.html +18 -0
  10. data/trimurti-0.1/docs/classes/Brahma.src/M000033.html +20 -0
  11. data/trimurti-0.1/docs/classes/Brahma.src/M000034.html +21 -0
  12. data/trimurti-0.1/docs/classes/Brahma.src/M000035.html +25 -0
  13. data/trimurti-0.1/docs/classes/Brahma.src/M000036.html +22 -0
  14. data/trimurti-0.1/docs/classes/Brahma.src/M000037.html +22 -0
  15. data/trimurti-0.1/docs/classes/ComponentSpec.html +275 -0
  16. data/trimurti-0.1/docs/classes/ComponentSpec.src/M000001.html +21 -0
  17. data/trimurti-0.1/docs/classes/ComponentSpec.src/M000002.html +18 -0
  18. data/trimurti-0.1/docs/classes/DRb.html +165 -0
  19. data/trimurti-0.1/docs/classes/DRb.src/M000062.html +18 -0
  20. data/trimurti-0.1/docs/classes/DRb.src/M000063.html +18 -0
  21. data/trimurti-0.1/docs/classes/GUID.html +129 -0
  22. data/trimurti-0.1/docs/classes/Linda.html +444 -0
  23. data/trimurti-0.1/docs/classes/Linda.src/M000007.html +16 -0
  24. data/trimurti-0.1/docs/classes/Linda.src/M000008.html +16 -0
  25. data/trimurti-0.1/docs/classes/Linda.src/M000009.html +20 -0
  26. data/trimurti-0.1/docs/classes/Linda.src/M000010.html +16 -0
  27. data/trimurti-0.1/docs/classes/Linda.src/M000011.html +18 -0
  28. data/trimurti-0.1/docs/classes/Linda.src/M000012.html +21 -0
  29. data/trimurti-0.1/docs/classes/Linda.src/M000013.html +18 -0
  30. data/trimurti-0.1/docs/classes/Linda.src/M000014.html +18 -0
  31. data/trimurti-0.1/docs/classes/Linda.src/M000015.html +18 -0
  32. data/trimurti-0.1/docs/classes/Linda.src/M000016.html +18 -0
  33. data/trimurti-0.1/docs/classes/Linda.src/M000017.html +20 -0
  34. data/trimurti-0.1/docs/classes/Linda.src/M000018.html +20 -0
  35. data/trimurti-0.1/docs/classes/Object.html +153 -0
  36. data/trimurti-0.1/docs/classes/Object.src/M000021.html +18 -0
  37. data/trimurti-0.1/docs/classes/Plugins.html +174 -0
  38. data/trimurti-0.1/docs/classes/Plugins.src/M000024.html +32 -0
  39. data/trimurti-0.1/docs/classes/Plugins.src/M000025.html +32 -0
  40. data/trimurti-0.1/docs/classes/Plugins/Spec.html +267 -0
  41. data/trimurti-0.1/docs/classes/Plugins/Spec.src/M000026.html +22 -0
  42. data/trimurti-0.1/docs/classes/Plugins/Spec.src/M000027.html +18 -0
  43. data/trimurti-0.1/docs/classes/Plugins/Spec.src/M000028.html +20 -0
  44. data/trimurti-0.1/docs/classes/QBE_ID.html +147 -0
  45. data/trimurti-0.1/docs/classes/QBE_ID.src/M000059.html +18 -0
  46. data/trimurti-0.1/docs/classes/QueryByExample.html +270 -0
  47. data/trimurti-0.1/docs/classes/QueryByExample.src/M000054.html +17 -0
  48. data/trimurti-0.1/docs/classes/QueryByExample.src/M000055.html +17 -0
  49. data/trimurti-0.1/docs/classes/QueryByExample.src/M000056.html +17 -0
  50. data/trimurti-0.1/docs/classes/QueryByExample.src/M000057.html +17 -0
  51. data/trimurti-0.1/docs/classes/QueryByExample.src/M000058.html +17 -0
  52. data/trimurti-0.1/docs/classes/Shiva.html +159 -0
  53. data/trimurti-0.1/docs/classes/Shiva.src/M000060.html +18 -0
  54. data/trimurti-0.1/docs/classes/Shiva.src/M000061.html +19 -0
  55. data/trimurti-0.1/docs/classes/String.html +176 -0
  56. data/trimurti-0.1/docs/classes/String.src/M000022.html +16 -0
  57. data/trimurti-0.1/docs/classes/String.src/M000023.html +16 -0
  58. data/trimurti-0.1/docs/classes/Symbol.html +176 -0
  59. data/trimurti-0.1/docs/classes/Symbol.src/M000003.html +16 -0
  60. data/trimurti-0.1/docs/classes/Symbol.src/M000004.html +16 -0
  61. data/trimurti-0.1/docs/classes/Trimurti.html +189 -0
  62. data/trimurti-0.1/docs/classes/Trimurti.src/M000019.html +16 -0
  63. data/trimurti-0.1/docs/classes/Trimurti.src/M000020.html +16 -0
  64. data/trimurti-0.1/docs/classes/Vishnu.html +563 -0
  65. data/trimurti-0.1/docs/classes/Vishnu.src/M000038.html +18 -0
  66. data/trimurti-0.1/docs/classes/Vishnu.src/M000039.html +18 -0
  67. data/trimurti-0.1/docs/classes/Vishnu.src/M000040.html +18 -0
  68. data/trimurti-0.1/docs/classes/Vishnu.src/M000041.html +22 -0
  69. data/trimurti-0.1/docs/classes/Vishnu.src/M000042.html +23 -0
  70. data/trimurti-0.1/docs/classes/Vishnu.src/M000043.html +24 -0
  71. data/trimurti-0.1/docs/classes/Vishnu.src/M000044.html +24 -0
  72. data/trimurti-0.1/docs/classes/Vishnu.src/M000045.html +18 -0
  73. data/trimurti-0.1/docs/classes/Vishnu.src/M000046.html +22 -0
  74. data/trimurti-0.1/docs/classes/Vishnu.src/M000047.html +20 -0
  75. data/trimurti-0.1/docs/classes/Vishnu.src/M000048.html +18 -0
  76. data/trimurti-0.1/docs/classes/Vishnu.src/M000049.html +22 -0
  77. data/trimurti-0.1/docs/classes/Vishnu.src/M000050.html +20 -0
  78. data/trimurti-0.1/docs/classes/Vishnu.src/M000051.html +19 -0
  79. data/trimurti-0.1/docs/classes/Vishnu.src/M000052.html +30 -0
  80. data/trimurti-0.1/docs/classes/Vishnu.src/M000053.html +21 -0
  81. data/trimurti-0.1/docs/created.rid +1 -0
  82. data/trimurti-0.1/docs/dot/f_0.dot +14 -0
  83. data/trimurti-0.1/docs/dot/f_0.png +0 -0
  84. data/trimurti-0.1/docs/dot/f_1.dot +14 -0
  85. data/trimurti-0.1/docs/dot/f_1.png +0 -0
  86. data/trimurti-0.1/docs/dot/f_2.dot +14 -0
  87. data/trimurti-0.1/docs/dot/f_2.png +0 -0
  88. data/trimurti-0.1/docs/dot/f_3.dot +39 -0
  89. data/trimurti-0.1/docs/dot/f_3.png +0 -0
  90. data/trimurti-0.1/docs/dot/f_4.dot +39 -0
  91. data/trimurti-0.1/docs/dot/f_4.png +0 -0
  92. data/trimurti-0.1/docs/dot/f_5.dot +69 -0
  93. data/trimurti-0.1/docs/dot/f_5.png +0 -0
  94. data/trimurti-0.1/docs/dot/f_6.dot +149 -0
  95. data/trimurti-0.1/docs/dot/f_6.png +0 -0
  96. data/trimurti-0.1/docs/dot/m_3_0.dot +30 -0
  97. data/trimurti-0.1/docs/dot/m_3_0.png +0 -0
  98. data/trimurti-0.1/docs/dot/m_4_0.dot +39 -0
  99. data/trimurti-0.1/docs/dot/m_4_0.png +0 -0
  100. data/trimurti-0.1/docs/dot/m_5_0.dot +40 -0
  101. data/trimurti-0.1/docs/dot/m_5_0.png +0 -0
  102. data/trimurti-0.1/docs/dot/m_5_1.dot +30 -0
  103. data/trimurti-0.1/docs/dot/m_5_1.png +0 -0
  104. data/trimurti-0.1/docs/dot/m_6_0.dot +40 -0
  105. data/trimurti-0.1/docs/dot/m_6_0.png +0 -0
  106. data/trimurti-0.1/docs/dot/m_6_1.dot +30 -0
  107. data/trimurti-0.1/docs/dot/m_6_1.png +0 -0
  108. data/trimurti-0.1/docs/dot/m_6_2.dot +30 -0
  109. data/trimurti-0.1/docs/dot/m_6_2.png +0 -0
  110. data/trimurti-0.1/docs/files/License_txt.html +132 -0
  111. data/trimurti-0.1/docs/files/ReadMe_Amber_txt.html +491 -0
  112. data/trimurti-0.1/docs/files/ReadMe_txt.html +694 -0
  113. data/trimurti-0.1/docs/files/nist/trimurti/linda_rb.html +129 -0
  114. data/trimurti-0.1/docs/files/nist/trimurti/plugins_rb.html +129 -0
  115. data/trimurti-0.1/docs/files/nist/trimurti/queryByExample_rb.html +128 -0
  116. data/trimurti-0.1/docs/files/nist/trimurti/trimurti_rb.html +151 -0
  117. data/trimurti-0.1/docs/fr_class_index.html +43 -0
  118. data/trimurti-0.1/docs/fr_file_index.html +33 -0
  119. data/trimurti-0.1/docs/fr_method_index.html +89 -0
  120. data/trimurti-0.1/docs/images/overview.jpg +0 -0
  121. data/trimurti-0.1/docs/index.html +24 -0
  122. data/trimurti-0.1/docs/rdoc-style.css +208 -0
  123. data/trimurti-0.1/lib/nist/trimurti/linda.rb +163 -0
  124. data/trimurti-0.1/lib/nist/trimurti/plugins.rb +107 -0
  125. data/trimurti-0.1/lib/nist/trimurti/queryByExample.rb +81 -0
  126. data/trimurti-0.1/lib/nist/trimurti/trimurti.rb +433 -0
  127. data/trimurti-0.1/tests/test_linda.rb +111 -0
  128. data/trimurti-0.1/tests/test_trimurti.rb +247 -0
  129. metadata +241 -0
@@ -0,0 +1,107 @@
1
+ =begin rdoc
2
+ This file includes code to find plugins, load them, and
3
+ help integrate them into an application.
4
+
5
+ For an overview and an example of use, see ReadMe.txt[link:files/ReadMe_txt.html].
6
+
7
+ Author: A. Griesser
8
+ =end
9
+
10
+ require 'rbtree'
11
+
12
+ # An Array of Plugins::Spec instances.
13
+ # The use of this global is optional:
14
+ # see the documentation for file plugins.rb
15
+ # for alternatives.
16
+ $PLUGINS = Array.new
17
+
18
+ module Plugins
19
+
20
+ # Provides information an application can use to:
21
+ # * Initialize the plugin
22
+ # * Add the plugin to a menu
23
+ # * Describe the pugin in an "About..." dialog
24
+ # Intended to be passed to the application through the $PLUGINS global.
25
+ class Spec
26
+ # Identifies the plugin
27
+ attr_accessor :name
28
+ # Who wrote the plugin
29
+ attr_accessor :author
30
+ # A String describing the plugin. Can be to be nil.
31
+ attr_accessor :description
32
+ # A Hash with:
33
+ # * keys are menu labels
34
+ # * values depend on the application in which the plugin is to be embedded
35
+ attr_accessor :actions
36
+ # A proc the application can use to initialize the plugin. can be nil.
37
+ attr_accessor :init_proc
38
+ # Name and Author are required Strings. Description can be nil.
39
+ # Actions must be a Hash, but it can be empty.
40
+ def initialize(name, author, description=nil, actions=RBTree.new, init_proc=nil)
41
+ self.name=name
42
+ self.author=author
43
+ self.description=description
44
+ self.actions=actions
45
+ self.init_proc=init_proc
46
+ end
47
+ # Calls the init_proc. Any arguments provided are passed on to the init_proc.
48
+ # Plugin developers need to know what arguments the appliction will use.
49
+ def init(*args)
50
+ init_proc.call(*args) if init_proc
51
+ end
52
+ # Returns a String representation of the plugin, suitable for use in an "About..." dialog.
53
+ def to_s(str=String.new)
54
+ str << name << ' plugin written by ' << author << '.'
55
+ str << "\n" << description if description
56
+ str
57
+ end
58
+ end # Spec
59
+
60
+ # Looks in all the $LOAD_PATH search roots for files matching pluginGlob.
61
+ # PluginGlob can be a String or an Array of Strings.
62
+ # Returns a possibly empty Array of paths local to one of the search roots.
63
+ # Suffix defines extensions to remove: see File.basename
64
+ def find_on_load_path(pluginGlob, suffix='.*')
65
+ answer = []
66
+ case pluginGlob
67
+ when String
68
+ local_path = File.dirname(pluginGlob)
69
+ answer = $LOAD_PATH.collect {|root|
70
+ sought = File.join(root, pluginGlob)
71
+ globMatches = Dir.glob(sought)
72
+ globMatches.collect {|f| File.join(local_path, File.basename(f, suffix)) }
73
+ }
74
+ when Array
75
+ answer = pluginGlob.collect {|relPath| plugin_dirs(relPath) }
76
+ else
77
+ raise "Incomprehensible argument to plugins (should be String or Array, was #{pluginGlob.class.name})"
78
+ end
79
+ answer.flatten.uniq.sort
80
+ end
81
+
82
+ # Loads (via 'require') all the plugins described by pluginGlob,
83
+ # so long as the plugin is in accessable from the $LOAD_PATH.
84
+ # Returns the Array of local paths to plugins (with extensions removed).
85
+ # PluginGlob can be a String or an Array of Strings.
86
+ # Takes an optional one-argument block, which is executed
87
+ # after every load.
88
+ def require_plugins(pluginGlob, io=STDOUT)
89
+ parts = find_on_load_path(pluginGlob)
90
+ io << "\n"
91
+ if parts.empty?
92
+ io << "\nDid not find any plugins matching #{pluginGlob.inspect}"
93
+ io.flush if io.respond_to?(:flush)
94
+ return parts
95
+ end
96
+ parts.each {|part|
97
+ io << "\nRequiring plugin: '#{part}'"
98
+ io.flush if io.respond_to?(:flush)
99
+ require part
100
+ next unless block_given?
101
+ yield(self, part)
102
+ }
103
+ return parts
104
+ end
105
+
106
+
107
+ end # Plugins
@@ -0,0 +1,81 @@
1
+
2
+ # For an overview and an example of use, see ReadMe.txt[link:files/ReadMe_txt.html].
3
+ #
4
+ # Author: A. Griesser
5
+
6
+ require 'nist/common/rubyToolsDay'
7
+ require 'nist/common/guid'
8
+
9
+ # This module lets you find objects with specified attributes. To perform a query:
10
+ # 1. Instantate an 'example' object of the class you are looking for
11
+ # 2. Set (to the query values) whatever attributes you are querying on.
12
+ # 3. Call query, passing the example as the argument.
13
+ # If you only want a single result, you can instad call retrieve instead.
14
+ #
15
+ # UNDER CONSTRUCTION.
16
+ #
17
+ # Room for improvement:
18
+ # * Queries are not aware of the inheritance hierarchy.
19
+ # * Performance not tested: may not scale well (depends on Rinda implementation).
20
+ # * None of these methods are thread safe.
21
+ module QueryByExample
22
+ include Vishnu
23
+
24
+
25
+ # attribute_values(shouldSort=false, answer=Array.new, ignoredVars=nil, onlyVars=nil)
26
+
27
+ # Creates and stores into the repository a record corresponding to the argument
28
+ def create(thing)
29
+ end
30
+
31
+ # Returns an Array of repository records that match 'thing'.
32
+ # (empty if not matches). If deRef is true, instead returns the
33
+ # array of objects described by the records.
34
+ def retrieve(thing, deRef=true)
35
+ end
36
+
37
+ # Returns the registered object with the specified qbeID
38
+ # (nil if there is no such object).
39
+ def find(qbeID)
40
+ end
41
+
42
+ # Update the repository record corresponding to the argument.
43
+ # Returns a Boolean, true if successful. If there is no
44
+ # corresponding record, returns the result of evaluating
45
+ # an optional block. The optional block may add the argument
46
+ # to the repository, may raise an exception, or just return
47
+ # something. If the optional block is not present, returns false.
48
+ def update(thing)
49
+ end
50
+
51
+ # Remove from the repository the record corresponding to 'thing'
52
+ # Returns the record. If deRef is true, instead returns the object
53
+ # described by the record. Silently returns nil if there is no record
54
+ # corresponding to 'thing'.
55
+ def delete(thing, deRef=true)
56
+ end
57
+
58
+ end # QueryByExample
59
+
60
+
61
+ class Object
62
+ # Literals resolve to themselves. All objects are literals,
63
+ # unless they are instances of the class used to represent ids.
64
+ # The ID classes should override this method by including QBE_ID.
65
+ def resolve_within(container)
66
+ self
67
+ end
68
+ end
69
+
70
+ # Mix this into classes that can act like ids.
71
+ module QBE_ID
72
+ # Since this is an id rather than a literal,
73
+ # look up (in the container) the thing to which this correspondes.
74
+ def resolve_within(container)
75
+ container.find(self)
76
+ end
77
+ end
78
+
79
+ class GUID
80
+ include QBE_ID
81
+ end # GIUD
@@ -0,0 +1,433 @@
1
+
2
+ # For an overview and an example of use, see ReadMe.txt[link:files/ReadMe_txt.html].
3
+ #
4
+ # Author: A. Griesser
5
+
6
+ require 'nist/common/log'
7
+ require 'nist/trimurti/plugins'
8
+ require 'nist/trimurti/linda'
9
+ require 'yaml'
10
+ require 'test/unit/assertions'
11
+
12
+
13
+ class Asserter
14
+ include Test::Unit::Assertions
15
+ end
16
+ ASSERTER = Asserter.new
17
+
18
+ class Symbol
19
+ # Receiver specifies API
20
+ def to_service_spec; [nil, nil, self]; end
21
+ # Receiver specifies name
22
+ def to_component_spec; [self.to_s, nil]; end
23
+ end
24
+
25
+ class String
26
+ # Receiver specifies name
27
+ def to_service_spec; [self, nil, nil]; end
28
+ # Receiver specifies name
29
+ def to_component_spec; [self, nil]; end
30
+ end
31
+
32
+ class Array
33
+ # Must be [name, host, symbol]
34
+ def to_service_spec
35
+ ASSERTER.assert_equal(3, size, 'Service spec must be in form: [name, host, symbol] (see Brahma#service)')
36
+ self
37
+ end
38
+ # Must be [name, host]
39
+ def to_component_spec
40
+ ASSERTER.assert_equal(2, size, 'Component spec must be in form: [name, host] (see Brahma#component)')
41
+ self
42
+ end
43
+ end
44
+
45
+
46
+ # ######################################################################
47
+
48
+ # Vishnu preserves registries of components and services.
49
+ module Vishnu
50
+
51
+ attr_accessor :linda
52
+
53
+ def initialize(is_local=true)
54
+ self.linda = Linda.new(is_local)
55
+ end
56
+
57
+ # Returns a String, the name of the computer the method executes on.
58
+ def gethostname
59
+ linda.gethostname
60
+ end
61
+
62
+ # Register 'thing' of specified type under specified name
63
+ def register(type, name, thing, *more)
64
+ linda.put(type, name, thing, *more)
65
+ end
66
+
67
+ # Register a component. 'provides' is an Array of Symbols specifying service APIs provided.
68
+ # Needs is a Hash with:
69
+ # * Key is a Symbol, the setter for a service the componnent requires
70
+ # * Value responds to to_service_spec (it specifies some other component)
71
+ # Component definition Tuples look like this:
72
+ # [:component, name, component, hostname, provides, needs, life_cycle]
73
+ def register_component(name, component, provides, needs={}, life_cycle={})
74
+ ASSERTER.assert_kind_of(Array, provides, "The 'provides' argument to Linda#register_component must be an Array")
75
+ ASSERTER.assert_kind_of(Hash, needs, "The 'needs' argument to Linda#register_component must be a Hash")
76
+ ASSERTER.assert_kind_of(Hash, life_cycle, "The 'life_cycle' argument to Linda#register_component must be a Hash")
77
+ component = _create(life_cycle) unless component
78
+ register(:component, name, component, gethostname, provides, needs, life_cycle)
79
+ end
80
+
81
+ # Register a service provider.
82
+ # Provides is an Array of Symbols, each naming a service offered by the provider.
83
+ # Service tuples look like this:
84
+ # [:service, name, provider, hostname, symbol]
85
+ def register_service(name, provider, provides)
86
+ provides.each {|symbol|
87
+ # For alternative implementation, see task :rinda_register_simplified in Ambrose
88
+ # This is not compatable: tuple fields are different.
89
+ register(:service, name, provider, gethostname, symbol)
90
+ }
91
+ # Ok to terminate (server state has been updated)
92
+ end
93
+
94
+ # Appends registered components to the argument, which can be a String or IO.
95
+ # Returns the argument.
96
+ def show_components(io=STDOUT)
97
+ tuples = component_tuples
98
+ return io << "\nNo components are registered. " unless tuples
99
+ io << "\nAll registered components (Name, host, provides, needs, life_cycle):"
100
+ tuples.each {|tuple|
101
+ io << "\n\t#{tuple[1].inspect}\t#{tuple[3].inspect}\t#{tuple[4].inspect}\t#{tuple[5].inspect}\t#{tuple[6].inspect}"
102
+ }
103
+ io << "\n"
104
+ end
105
+
106
+ # Appends registered services to the argument, which can be a String or IO.
107
+ # Returns the argument.
108
+ def show_services(io=STDOUT)
109
+ tuples = service_tuples
110
+ return io << "\nNo services are registered. " unless tuples
111
+ io << "\nAll registered services (name, host, provides):"
112
+ tuples.each {|tuple|
113
+ io << "\n\t#{tuple[1].inspect}\t#{tuple[3].inspect}\t#{tuple[4].inspect}"
114
+ }
115
+ io << "\n"
116
+ end
117
+
118
+ # Finds Tuples describing components.
119
+ # Arguments can be:
120
+ # * nil if you don't care what the value should be
121
+ # * A Regexp if you want to select a matching pattern.
122
+ # This returns a (possibly empty) Array of matching Tuples
123
+ def component_tuples(name=nil, host=nil)
124
+ linda.rd_all(:component, name, nil, host, nil, nil, nil)
125
+ end
126
+
127
+ # Find a Tuple describing a component.
128
+ # arguments can be:
129
+ # * nil if you don't care what the value is
130
+ # * A Regexp if you want to select a matching pattern.
131
+ # This returns:
132
+ # * a Tuple describing a matching component (the local host is prefered)
133
+ # * nil if there is no matching tuple
134
+ def component_tuple(name=nil, host=nil, symbol=nil)
135
+ h = host || gethostname
136
+ tuples = component_tuples(name, h)
137
+ tuples = component_tuples(name, nil) if tuples.empty? && !host
138
+ return nil unless tuples
139
+ tuples[0]
140
+ end
141
+
142
+ # Find a specific component.
143
+ # arguments can be:
144
+ # * nil if you don't care what the value is
145
+ # * A Regexp if you want to select a matching pattern.
146
+ # This returns:
147
+ # * a component with a matching Tuple (the local host is prefered)
148
+ # * nil if there is no matching tuple
149
+ def component(name=nil, host=nil)
150
+ tuple = component_tuple(name, host)
151
+ return nil unless tuple
152
+ tuple[2]
153
+ end
154
+
155
+ # Finds Tuples describing services.
156
+ # Arguments can be:
157
+ # * nil if you don't care what the value should be
158
+ # * A Regexp if you want to select a matching pattern.
159
+ # This returns a (possibly empty) Array of matching Tuples
160
+ def service_tuples(name=nil, host=nil, symbol=nil )
161
+ linda.rd_all(:service, name, nil, host, symbol)
162
+ end
163
+
164
+ # Find a Tuple describing a service.
165
+ # arguments can be:
166
+ # * nil if you don't care what the value is
167
+ # * A Regexp if you want to select a matching pattern.
168
+ # This returns:
169
+ # * a Tuple describing a matching provider (the local host is prefered)
170
+ # * nil if there is no matching tuple
171
+ def service_tuple(name=nil, host=nil, symbol=nil)
172
+ h = host || gethostname
173
+ tuples = service_tuples(name, h, symbol )
174
+ tuples = service_tuples(name, nil, symbol ) if tuples.empty? && !host
175
+ return nil unless tuples
176
+ tuples[0]
177
+ end
178
+
179
+ # Find a specific service.
180
+ # arguments can be:
181
+ # * nil if you don't care what the value is
182
+ # * A Regexp if you want to select a matching pattern.
183
+ # This returns:
184
+ # * a provider with a matching Tuple (the local host is prefered)
185
+ # * nil if there is no matching tuple
186
+ # Usually only the symbol is necessary: the other arguments are
187
+ # present for consistency with other methods (and those rare
188
+ # times when you really do need to specify them).
189
+ def service(name=nil, host=nil, symbol=nil)
190
+ tuple = service_tuple(name, host, symbol)
191
+ return nil unless tuple
192
+ tuple[2]
193
+ end
194
+
195
+
196
+ # Execute the code String (which may contain a 'component' local),
197
+ # returning the result of this evaluation. Returns nil if the
198
+ # code string is nil.
199
+ def apply(code, component)
200
+ return nil unless code
201
+ eval(code)
202
+ end
203
+
204
+ # For each component, apply the life_cycle[selector] code.
205
+ def life_cycle(selector)
206
+ tuples = component_tuples
207
+ problems = Array.new
208
+ tuples.each {|tuple|
209
+ component = tuple[2]
210
+ life_cyc = tuple[6]
211
+ begin
212
+ apply(life_cyc[selector], component)
213
+ rescue
214
+ problems << $!
215
+ end
216
+ }
217
+ return if problems.empty?
218
+ raise "Problems connecting components: #{problems.join(', ')}"
219
+ end
220
+
221
+
222
+ # Gets the :create code from the life_cycle_hash, and executes it.
223
+ # Fails if unable to create component.
224
+ def _create(life_cycle_hash)
225
+ fail 'Can not create component: no life_cycle Hash' unless life_cycle_hash
226
+ instantiation_code = life_cycle_hash[:create]
227
+ fail 'Can not create component: life_cycle Hash is missing :create.' unless instantiation_code
228
+ apply(instantiation_code, nil)
229
+ end
230
+
231
+ end # Vishnu
232
+
233
+ # ######################################################################
234
+
235
+ # Brahma creates applications from components and services.
236
+ module Brahma
237
+ include Vishnu
238
+
239
+ # Register the services of specified component.
240
+ # Each service shares the name of its component.
241
+ def register_component_services(componentTuple)
242
+ name = componentTuple[1]
243
+ component = componentTuple[2]
244
+ provided = componentTuple[4]
245
+ register_service(name, component, provided)
246
+ end
247
+
248
+ # Register the services of every previously registered component
249
+ def register_services
250
+ component_tuples.each {|tuple|
251
+ register_component_services(tuple)
252
+ }
253
+ end
254
+
255
+ # For each component, get the services it needs, and set them.
256
+ def connect_services
257
+ tuples = component_tuples
258
+ problems = Array.new
259
+ tuples.each {|tuple|
260
+ name = tuple[1]
261
+ component = tuple[2]
262
+ needs = tuple[5]
263
+ needs.each {|setter, svc_api|
264
+ provider = service(*svc_api.to_service_spec)
265
+ unless provider
266
+ problems << "Component <#{name}> needs, but couldn't find, service #{svc_api.inspect}"
267
+ next
268
+ end
269
+ err = _connect(component, name, setter, provider)
270
+ problems << err if err
271
+ }
272
+ }
273
+ return if problems.empty?
274
+ joint = "\n"
275
+ fail "Problem(s) (#{problems.size.to_s}) connecting components: \n#{problems.join(joint)}"
276
+ end
277
+
278
+ # For each component, evaluate the life_cycle[:start] code.
279
+ def start_components
280
+ life_cycle(:start)
281
+ end
282
+
283
+ # Assembles the application from registered components by:
284
+ # * Registering all the services provided by the components
285
+ # * Connecting service consumers to service providers.
286
+ # * Starting the components
287
+ def assemble
288
+ register_services
289
+ connect_services
290
+ start_components
291
+ end
292
+
293
+ # Returns nil if successful, an error String if a problem occured.
294
+ def _connect(component, name, setter, provider)
295
+ component.send(setter, provider )
296
+ return nil
297
+ rescue
298
+ return "Error providing service to <#{name}> using <#{setter}>: #{$!}"
299
+ end
300
+
301
+ # Registers components described in a YAML file.
302
+ # Does not register the services contained in the components, and
303
+ # does not hook together the service providers and consumers.
304
+ # Source can be
305
+ # * A String naming a file containing a YAML serialization (if isFilename=true)
306
+ # * A String containing a YAML serialization (if isFilename=false)
307
+ # * An IO streaming over a YAML serialization (if isFilename=false)
308
+ # In any case, the YAML serialization must contain an Array of ComponentSpec instances.
309
+ def from_yaml(source, isFilename=true)
310
+ source = File.open(source) if isFilename
311
+ specs = YAML.load(source)
312
+ errors = specs.collect {|spec| create_registered_component(spec) }
313
+ errors.flatten!
314
+ errors.compact!
315
+ return if errors.empty?
316
+ sep = "\n"
317
+ raise "Error(s) creating or registering components: #{errors.join(sep)}"
318
+ end
319
+
320
+
321
+
322
+ # Creates component:
323
+ # * Uses 'require' to load any necessary source
324
+ # * Executes life_cycle[:create] code
325
+ # * Sets compSpec.component
326
+ def create_component(componentSpec)
327
+ componentSpec.require_source
328
+ answer = _create(componentSpec.life_cycle)
329
+ raise "Attempt to instantiate component resulted in nil: #{componentSpec.inspect}" unless answer
330
+ componentSpec.component= answer
331
+ answer
332
+ end
333
+
334
+ # Creates and registers component:
335
+ # * Uses 'require' to load any necessary source
336
+ # * Executes life_cycle[:create] code
337
+ # * Sets compSpec.component
338
+ # * Registers the component
339
+ # Returns nil or an error String.
340
+ def create_registered_component(compSpec)
341
+ create_component(compSpec)
342
+ register_component(compSpec.name, compSpec.component, compSpec.provides, compSpec.needs, compSpec.life_cycle )
343
+ return nil
344
+ rescue
345
+ return "Error creating component #{compSpec.inspect}: #{$!}"
346
+ end
347
+
348
+ end # Brahma
349
+
350
+ # ######################################################################
351
+
352
+ # Shiva shuts down and destroys (in an orderly fashion) applications
353
+ # built from components and services.
354
+ module Shiva
355
+
356
+ def stop_components
357
+ life_cycle(:stop)
358
+ end
359
+
360
+ def destroy_components(stop=true)
361
+ stop_components if stop
362
+ life_cycle(:destroy)
363
+ end
364
+
365
+ end # Shiva
366
+
367
+ # ######################################################################
368
+
369
+ # A class that provides all the functionality of Vishnu, Brahma, and Shiva,
370
+ # for use when mixins are not required. The behaviors are available on both instance
371
+ # side, and the class side (through a lazy-initialized default_instance).
372
+ class Trimurti
373
+ include Plugins
374
+ include Brahma
375
+ include Shiva
376
+
377
+ @@default_instance=nil
378
+ def self.default_instance; @@default_instance || @@default_instance=self.new; end
379
+ def self.default_instance=(inst); @@default_instance=inst; end
380
+
381
+ forward_reflect_except(:default_instance, Trimurti)
382
+
383
+ end # Trimurti
384
+
385
+ # ######################################################################
386
+
387
+
388
+ # Describes a Component that can be managed by Vishnu/Brahma.
389
+ # Can be unmarshalled by Yaml to register a Component.
390
+ class ComponentSpec
391
+
392
+ # A String identifying the instance
393
+ attr_accessor :name
394
+
395
+ # Not marshalled
396
+ attr_accessor :component
397
+
398
+ # An Array of file names that are loaded by 'require' before instantiation
399
+ attr_accessor :required_source
400
+
401
+ # A Hash that specifies how to manage the component's life cycle. Only the
402
+ # :create key is required.
403
+ # * Keys are Symbols that specify life cycle events. The following keys are valid:
404
+ # * :create - Instantiate the component
405
+ # * :start - Activate all services
406
+ # * :stop - Turn off all services
407
+ # * :destroy - Discard references to other components
408
+ # * Values are Strings that specify behaviors that is to occur at life cyle events.
409
+ attr_accessor :life_cycle
410
+
411
+ # An Array of Symbols, each naming a collection of methods provided by the instance
412
+ attr_accessor :provides
413
+
414
+ # A Hash that describes how to provided necessary services to the instance:
415
+ # * Key is a name of a method (on the component) that takes one argument (the service provider)
416
+ # * Value is either:
417
+ # * a Symbol that identifies the required API
418
+ # * an Array containing the 3 arguments to Brahman#service
419
+ attr_accessor :needs
420
+
421
+ # Load (via 'require') all the source code needed by the component.
422
+ def require_source
423
+ required_source.each {|src|
424
+ print "\nComponentSpec #{name} is requiring #{src} "; STDOUT.flush
425
+ require src
426
+ }
427
+ end
428
+
429
+ def to_yaml_properties
430
+ ['@name', '@required_source', '@provides', '@needs', '@life_cycle']
431
+ end
432
+
433
+ end # ComponentSpec