Commit | Line | Data |
4536f655 |
1 | package World4 ; |
2 | |
3 | use strict; |
4 | |
5 | my $attr_spec = |
6 | [ |
7 | { |
8 | 'name' => 'planet', |
9 | 'default' => 'uranus', |
10 | }, |
11 | ]; |
12 | |
13 | sub new { |
14 | |
15 | my( $class ) = shift ; |
16 | my $self = Stem::Class::parse_args( $attr_spec, @_ ) ; |
17 | |
18 | return ( $self ); |
19 | } |
20 | |
21 | # based on who was the receiver of the message |
22 | # we return with the appropriate response |
23 | |
24 | sub hello_cmd { |
25 | |
26 | my( $self ) = @_; |
27 | |
28 | return "Hello world from $self->{'planet'}!\n"; |
29 | } |
30 | |
31 | sub name_cmd { |
32 | |
33 | my ( $self, $msg ) = @_ ; |
34 | |
35 | my $data = $msg->data() ; |
36 | |
37 | return unless $data ; |
38 | |
39 | $self->{'planet'} = ${$data} ; |
40 | |
41 | return ; |
42 | } |
43 | |
44 | |
45 | |
46 | |
47 | =head1 Stem Cookbook - World3 |
48 | |
49 | =head1 NAME |
50 | |
51 | World3 - Mixing class and object B<Stem> cells. |
52 | |
53 | =head1 DESCRIPTION |
54 | |
55 | This is an extension of the B<Stem Cookbook Part 1 & Stem Cookbook Part 2> where |
56 | we talked about the creation of B<Stem> class and object cells. In |
57 | this example, we take the idea of a class cell and an object cell |
58 | and combine them into a single B<Stem> module. We then have |
59 | the ability of creating multiple cells (registered objects) |
60 | with their own private data and at the same time have a |
61 | class cell to manage a global resource. |
62 | |
63 | =head1 CREATING THE CELL |
64 | |
65 | The following lists the requirements for creating a B<Stem> |
66 | object level cell: |
67 | |
68 | =over 4 |
69 | |
70 | =item * |
71 | |
72 | An attribute specification |
73 | |
74 | =item * |
75 | |
76 | An object constructor |
77 | |
78 | =item * |
79 | |
80 | A class registration |
81 | |
82 | =back |
83 | |
84 | =head2 CHANGES FROM PART 1 AND PART 2 |
85 | |
86 | Most of the code from Part 2 and Part 1 remain the same. We keep |
87 | the same attribute specification as well as the same object cell |
88 | constructor (except for a slight modification, see below). |
89 | You remember from Part 1 that we created a class level |
90 | B<Stem> cell from the configuration file, |
91 | |
92 | [ |
93 | class => 'World1', |
94 | name => 'solar_system', |
95 | |
96 | ] |
97 | |
98 | Because we do not have an args field, it means we are creating a |
99 | class cell. In this example, we want a class cell to be created |
100 | as a global resource only if an object cell is created. If the |
101 | class cell is supposed to manage global information for object |
102 | cells there is no need to create one if an object cell does not |
103 | exist. To get this type of behavior, we register the class cell |
104 | from within the B<Stem> module rather than from the configuration |
105 | file, |
106 | |
107 | Stem::Route::register_class( __PACKAGE__, 'solar_system' ); |
108 | |
109 | This line (World3.pm, line 5) effectively registers the class cell |
110 | with the B<Stem> message routing system using the package name |
111 | and a name we wish to register the cell as. |
112 | |
113 | We keep referring to the class cell as a global resource, so in |
114 | this example we create a global resource that the class cell will |
115 | manage, |
116 | |
117 | my @objects = (); |
118 | |
119 | On line 16 in World3.pm we create an array named objects that will |
120 | be used to hold a reference to each of the World3 cell objects that are |
121 | created from the configuration file (Note that this is not a |
122 | requirement for creating this module and is just used as an example. |
123 | It could have just as easily been a simple scalar, a hash, some |
124 | other kind of object, or even nothing!). |
125 | |
126 | In order to populate this array of the objects that are created from |
127 | the configuration file, we simply add them to the array when they |
128 | are created in the object cell constructor, |
129 | |
130 | push @objects, $self; |
131 | |
132 | This simply pushes the reference to the newely created World3 object cell |
133 | onto the objects array. The class cell can now be used to represent the |
134 | World3 object cells as a group. |
135 | |
136 | The next modification exists in the hello_cmd subroutine. We need a way |
137 | to distiguish whether or not a message is being sent to an object cell |
138 | as opposed to a class cell. As you might recall, the perl I<ref> function |
139 | is used to determine if a scalar refers to a reference or a normal |
140 | scalar value. If a subroutine is invoked from an object, the first |
141 | argument of the subroutine will be a reference to the object itself, |
142 | otherwise, it will be the string name of the class from which the subroutine |
143 | belongs. The following code demonstrates a new hello_cmd subroutine |
144 | that makes this distinction and performs accordingly, |
145 | |
146 | sub hello_cmd { |
147 | |
148 | my ($class) = @_; |
149 | |
150 | return "Hello world from $class->{'planet'}\n" if ref $class; |
151 | |
152 | my $response_string = ''; |
153 | foreach my $obj (@objects) { |
154 | |
155 | $response_string .= "Hello world from $obj->{'planet'}\n"; |
156 | } |
157 | |
158 | return $response_string; |
159 | } |
160 | |
161 | As you can see, we return the familiar "Hello world from $class->{'planet'}" |
162 | string, but this time we check to make sure $class is a reference before |
163 | returning the string. If it is not, we know that the hello_cmd was invoked |
164 | from a message that was intended for the class cell. If this is the case, we |
165 | concatenate a "Hello, World ..." string for each of the Hello3 object cells |
166 | that were stored in the objects array and send that string as a response |
167 | message to the sender. |
168 | |
169 | =head1 SEE ALSO |
170 | |
171 | F<Stem Cookbook Part 1> |
172 | |
173 | F<Stem Cookbook Part 2> |
174 | |
175 | F<Hello3 Module> |
176 | |
177 | =cut |
178 | |
179 | 1; |