Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation #124

Merged
merged 14 commits into from
Oct 25, 2023
12 changes: 6 additions & 6 deletions documentation/Connecting two components.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ In reality, having only one component isn't useful at all since components are m
The idea of having two components is that one will send data (through events and services), and the other will listen to the first component in order to receive and treat this data.
To do that, we need to specify each component's contract in order to achieve exactly that.

In the first component's contract, you will need to specify which events and services are respectively produced and provided.
For Events, it's done in `producedComponentEvents`
For Services, it's done in `providedComponentServices`
In the first component's contract, you will need to specify which events and services are respectively produced and provided. \
For Events, it's done in `producedComponentEvents` \
For Services, it's done in `providedComponentServices` \
For Parameters, it's done in `providedComponentParameters`

In the second component's contract, you will need to specify which events and services are respectively consumed and used.
For Events, it's done in `consumedComponentEvents`
For Services, it's done in `usedComponentServices`
In the second component's contract, you will need to specify which events and services are respectively consumed and used. \
For Events, it's done in `consumedComponentEvents` \
For Services, it's done in `usedComponentServices` \
For Parameters, it's done in `usedComponentParameters`

For listening to a component for events, see the last part of [Creating Events](https://github.com/OpenSmock/Molecule/blob/main/documentation/Creating%20Events.md) tutorial.
Expand Down
3 changes: 2 additions & 1 deletion documentation/Creating Events.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ In the case of multiple events, you just need to separate the different Event Tr
This will in return add other methods in the implementations of a Type, namely every event in the added Event Trait(s).
It's here that the event's implementation is made, what you want it to do whenever it's received.
The Event Trait added in `consumedComponentEvents` will in return add a `get[componentName]EventsSubscriber` method which will be used to subscribe to the events emitted from the Trait.
If you're not getting all the methods you should have, you can first click on the Library tab of Pharo, then select Molecule -> Debug and Tools, then click on Define All Components. If you're still not getting what you expected, you will need to verify what is put in your component's contract (no empty instance variable for example). Methods related to the Events as well as `get[componentName]EventsSubscriber` are automatically generated, there is no need to manually type them.

If you're not getting all the methods you should have, you can first click on the **Library** tab of Pharo, then select **Molecule** -> **Debug and Tools**, then click on **Define All Components**. If you're still not getting what you expected, you will need to verify what is put in your component's contract (no empty instance variable for example). Methods related to the Events as well as `get[componentName]EventsSubscriber` are automatically generated, there is no need to manually type them.

For Events in particular, you will need to subscribe to the class specified in `consumedComponentEvents`.
This is done in the `componentActivate` method of a Molecule component (which you will need to create). You then fill out the following template:
Expand Down
2 changes: 1 addition & 1 deletion documentation/Creating Notifiers.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Notifiers are used to trigger events between components.
They're automatically generated based on the Consumed Events part of a component's contract (what Event Trait was put in the `consumedComponentEvents` method), there is no need to manually type it.
If you're not getting this method, you can first click on the Library tab of Pharo, then select Molecule -> Debug and Tools, then click on Define All Components. If you're still not getting what you expected, you will need to verify what is put in your component's contract (no empty instance variable for example).
If you're not getting this method, you can first click on the **Library** tab of Pharo, then select **Molecule** -> **Debug and Tools**, then click on **Define All Components**. If you're still not getting what you expected, you will need to verify what is put in your component's contract (no empty instance variable for example).

To trigger an event, the syntax follows this template:
`self get[componentName]EventsNotifier [eventName]`
Expand Down
4 changes: 2 additions & 2 deletions documentation/Creating Parameters.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Parameters are made of data only used at the initialization of a component.
Parameters are not commonly used, instead opting for Services.
Parameters are made of data only used at the initialization of a component. \
Parameters are not commonly used, instead opting for Services. \
Parameters are created in the `componentInitialize` method, which is executed during the **Initialize** state of a component's life-cycle.

**to complete**
Expand Down
2 changes: 1 addition & 1 deletion documentation/Creating Producers.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Producers are used to specify the sender of the Events created by components.
Producers are created using the following syntax:
`self forEvents: [componentName]Events useProducer: #[instanceName]`
with [instanceName] being the name you chose for starting a component (see the last part of [Create a new Molecule component](https://github.com/OpenSmock/Molecule/blob/main/documentation/Create%20a%20new%20Molecule%20component.md)), `default` if no name was used.
with [instanceName] being the name you chose for starting a component (see the last part of [Create a new Molecule component](https://github.com/OpenSmock/Molecule/blob/main/documentation/Create%20a%20new%20Molecule%20component.md#instantiating-a-component)), `default` if no name was used.
Producers are created in the `componentInitialize` method.

For multiple Producers (multiple named launched components of the same Type), the syntax is
Expand Down
19 changes: 10 additions & 9 deletions documentation/Creating Services.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@ Once you're in these methods, add between the already present curly brackets `{}
In the case of multiple services, you just need to separate the different Service Traits by a dot.
This will in return add other methods in the implementations of a Type, namely every service in the added Service Trait(s).
The Service Trait added in `usedComponentServices` will in return add a `get[componentName]ServicesProvider` method which will be used for getting all the Services' values.
If you're not getting all the methods you should have, you can click on the Library tab of Pharo, then select Molecule -> Debug and Tools, then click on **Define All Components**. If you're still not getting what you expected, you will need to verify what is put in your component's contract (no empty instance variable for example). Methods related to the Services as well as `get[componentName]ServicesProvider` are automatically generated, there is no need to manually type them.

Since services are not directly linked to a variable, you can edit their name independently from the variables (and execute operations on them).
For example, if you want a service to be sent by `ComponentA`, received through `ComponentB` (with or without modifications) then sent to `ComponentC` (so that only `ComponentB` communicates with `ComponentC`), you can create a method in `ComponentAServices` named `name`, assign it a value in `componentActivate`, then get it in `ComponentB `with this line of code (a variable stocks the service named `name`):
If you're not getting all the methods you should have, you can click on the **Library** tab of Pharo, then select **Molecule** -> **Debug and Tools**, then click on **Define All Components**. If you're still not getting what you expected, you will need to verify what is put in your component's contract (no empty instance variable for example). Methods related to the Services as well as `get[componentName]ServicesProvider` are automatically generated, there is no need to manually type them.

Since services are not directly linked to a variable, you can edit their name independently from the variables (and execute operations on them). \
For example, if you want a service to be sent by `ComponentA`, received through `ComponentB` (with or without modifications) then sent to `ComponentC` (so that only `ComponentB` communicates with `ComponentC`), you can create a method in `ComponentAServices` named `name`, assign it a value in `componentActivate`, then get it in `ComponentB `with this line of code (a variable stocks the service named `name`): \
`firstName := self getComponentAServicesProvider name`
Then, `ComponentBServices` needs to have a service named `firstName` (which is an empty method).
`ComponentC` can then call
`name := self getComponentBServicesProvider firstName`
to get this Service's value.
In this example, `ComponentAServices` is written in the `providedComponentServices` method of the `ComponentA` Type Trait and in the `usedComponentServices` of the `ComponentB` Type Trait,
Then, `ComponentBServices` needs to have a service named `firstName` (which is an empty method). \
`ComponentC` can then call \
`name := self getComponentBServicesProvider firstName` \
to get this Service's value. \
In this example, `ComponentAServices` is written in the `providedComponentServices` method of the `ComponentA` Type Trait and in the `usedComponentServices` of the `ComponentB` Type Trait, \
`ComponentBServices` is written in the `providedComponentServices` method of the `ComponentB` Type Trait and in the `usedComponentServices` of the `ComponentC` Type Trait.

The global syntax to get a Service's value is
The global syntax to get a Service's value is \
`self get[componentName]ServicesProvider [serviceName]`

**add img**
9 changes: 5 additions & 4 deletions documentation/Facilitating tests.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
To create quick tests for your components, you can create a package named [yourPackageName]-Examples or [yourPackageName]-Tests, then create an Object subclass named [yourPackageName]Examples, then display the class side and creating methods to have different test cases.
One practical thing that you can do is putting a `<script>` tag at the start of your methods. This will create a small icon next to your method in order to launch it without the use of a Do It in a Playground.

One practical thing that you can do is putting a `<script>` tag at the start of your methods. This will create a small icon next to your method in order to launch it without the use of a Do It in a Playground. \
**img icon**

A script could look like
**insert img (component start without name)**
and another script used to delete active components could be structured like
A script could look like \
**insert img (component start without name)** \
and another script used to delete active components could be structured like \
**insert "cleanUp" (MolComponentManager cleanUp) method img**

You also need to verify the order of components starting since a component that listens to another needs to be started before the latter one. Otherwise, the event will be sent wthout any component listening to it, rendering it meaningless.
Expand Down
6 changes: 3 additions & 3 deletions documentation/Home.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Deprecated version of Molecule (1.1.x) for Pharo 6 and 7 is also available [here
This section briefly presents what is a Molecule component and how it's dynamically managed.

# A Component and his Contract
![Molecule component](https://user-images.githubusercontent.com/49183340/162572734-774a7065-9772-433e-8f0a-9dc538978c92.png)
![Molecule component](https://user-images.githubusercontent.com/49183340/162572734-774a7065-9772-433e-8f0a-9dc538978c92.png) \
Similarly to the LCCM, a component’s business contract is exposed through its component Type. The Type specifies what a component has to offer to other components (namely, provided services and produced events) and what that component requires from other components (namely, used services and consumed events).
![Component application](https://user-images.githubusercontent.com/49183340/162572946-8cd11257-65bb-4ed3-a13a-0fe6dd6f83d1.png)

Expand All @@ -33,7 +33,7 @@ Thus, the main role of the Type is to implement the services that the component
A Molecule component definition is based on Traits. The Type, as well as the services, the events and the parameters are all defined as Traits. A Molecule component is an instance of a standard class which uses Molecule traits.

# Two ways to implement a Component
![Two ways to implement a component](https://user-images.githubusercontent.com/49183340/162573288-4d7fc513-5d98-420e-a309-e98f1e42fc6d.png)
![Two ways to implement a component](https://user-images.githubusercontent.com/49183340/162573288-4d7fc513-5d98-420e-a309-e98f1e42fc6d.png) \
In Molecule, we define the elements of a component's contract (services, events, parameters) as a set of Traits. A component Type aggregates theses traits and is itself defined as a Trait. Molecule provides a dedicated Trait `MolComponentImpl`, which implements cross-cutting behavior shared by all components (e.g., components' life-cycle management). Implementing a component consists in defining a class that uses the `MolComponentImpl` Trait to obtain component shared behavior and uses a Type Trait (`MolComponentType`) implementing the component's business behavior.
![Component application and other](https://user-images.githubusercontent.com/49183340/162573410-9543b74f-af2f-4ad9-a156-aa4759916773.png)

Expand All @@ -44,7 +44,7 @@ The direct benefit of this approach is that it's possible for any existing class
All components are managed by the ComponentManager object. It maintains the list of component instances currently alive in the system. It's currently handled as a singleton. The ComponentManager class implements an API to instantiate and to remove each component, to associate them, to connect events, etc. This API is used to manage each component's life-cycle programmatically.

# Components' life-cycle and states
The activity of a component depends on contextual constraints such as the availability of a resource, the physical state of hardware elements, etc. To manage consumed resources accordingly, the life-cycle of a component has four possible states: Initialized, Activated, Passivated and Removed.
The activity of a component depends on contextual constraints such as the availability of a resource, the physical state of hardware elements, etc. To manage consumed resources accordingly, the life-cycle of a component has four possible states: Initialized, Activated, Passivated and Removed. \
![Components lifecycle and states](https://user-images.githubusercontent.com/49183340/162570154-b39fc041-03f3-40d2-ad3f-30aac027a4b0.png)

After its initialization, a component can switch from an Activated state to a Passivated state and conversely. When the life-cycle of a component is over, then it switches to the Removed state.
Expand Down