Moose Chef is a library which provides a small DSL to query object-oriented dependencies between FAMIX entities. It is a complete overhaul of the previous Moose Cook API, which provided navigation methods between FAMIX entities and resides in the Famix-Extensions package.
Basically, Moose Chef provides an API with a few methods to ask either a method, a class, a package, or a namespace for its dependencies (for now invocations, inheritances, references, accesses) to other entities at various scope (class, package, namespace). Moose Chef comes as a small set of composable queries, with a well defined meaning, which is easy to browse and understand, and reused across Famix entities through traits.
To start with, a query has a receiver, that is the FAMIX entity for which we want to retrieve dependencies. Currently
FAMIXNamepace support Moose Chef queries. Relative to the receiver, a query has a side: we can query either incoming or outgoing dependencies to/from the receiver. Two different natures of dependencies can be retrieved through Moose Chef queries: it can be the relationship itself (like an invocation, subclass of
FAMIXAssociation), or the opposite object in the relationship (like a method, a class, a package...)
Primitive queries return a set of dependencies related to the receiver. Such dependencies are represented by instances of
FAMIXAssociation: invocations, accesses, inheritances, and references.
The combination of sides and dependencies define the set of primitive queries available for any receiver. Each primitive query is defined by a method in the receiver class.
receiver queryAllIncomingInvocations querySureIncomingInvocations queryIncomingAccesses queryIncomingReferences queryIncomingInheritances
receiver queryAllOutgoingInvocations querySureOutgoingInvocations queryOutgoingAccesses queryOutgoingReferences queryOutgoingInheritances
You can ask for example
aFamixClass queryAllIncomingInvocations aFamixPackage queryAllIncomingInvocations
Notice that a few queries do not make sense for some receivers, like for example asking a FAMIXMethod for inheritances or incoming references - in this cases the query returns a neutral value, that is an empty collection.
Some composite queries are also defined on top
static associations = accesses + references + inheritances sure associations = static + sure invocations all associations = static + invocations
Results from primitive queries may include self loops. That is, a dependency which loops back to its receiver. In some cases, it is not desirable to have such self loops in the results (think for example algorithms which do not like graphs with self loops. A
#withoutSelfLoops sent to a query result can reject them.
aFamixClass queryAllOutgoingInvocations withoutSelfLoops
retrieves all invocations which are not sent to its own class.
Once a primitive query has been sent to a Famix entity, you can transform the query result with more refined queries.
Primitive queries retrieve
FAMIXAssociations. However, since we already know the receiver, we might be interested only by the objects on the other side of the dependency. Chef provides the
#opposites selector to directly retrieve the other side of the association: that is the
#from side of an incoming dependency, and the
#to side of an outgoing dependency.
aMethod queryAllIncomingInvocations opposites
retrieves methods calling aMethod.
aMethod queryOutgoingReferences opposites
retrieves classes referenced by aMethod.
FAMIXAssociation opposite objects
Another interesting operation is to get the dependency information at a higher granularity by changing the scope. For example, one can ask for the dependencies coming from invocations as seen at the package level: that is, querying for invoked or invoking packages.
Three scope operators are available on query results:
atClassScope, atPackageScope, atNamespaceScope
aMethod queryOutgoingReferences atPackageScope
retrieves packages which contain classes referenced by the method.
aPackage queryAllIncomingInvocations atClassScope
retrieves classes which ’call’ the package.
The scope operators exclude self loops by default (this was also the default in Moose Cook). That is, the query result will not contain the receiver scope itself: you will not get something like PackageX -> PackageX in the result (the reason for this is that in general algorithms do not like graphs with self loops).
When querying a high level container (like a package or a class), you might also retrieve which of your components (for examples, which methods) are the sources of dependencies. This is the inverse of the
#opposites query, as the receiver addresses its own side.
aFamixPackage queryAllOugoingDependencies sourceClasses
retrieves classes from the package which are involved in outgoing dependencies. Notice it returns the sources inside the package, not the opposite classes which are depended upon.
The last common refinement is to filter some of the dependencies based on a scope. This is different from scoping in that we don’t change the nature of results (associations or objects), just select or reject some.
aMethod queryOutgoingReferences opposites withinPackage: aPackage
only retrieves referenced classes in the given package.
aPackage queryAllIncomingInvocations atClassScope outOfNamespace: aNamespace
only retrieves invoking classes out of the given namespace.
This also works on primitive queries
aMethod queryOutgoingReferences withinPackage: aPackage
which retrieves FAMIXReferences instead of classes.
There are 2x3 operators for filtering:
#withinClass: #withinPackage: #withinNamespace #outOfClass: #outOfPackage: #outOfNamespace:
For convenience, the following helpers are also defined:
#withinMyClass #withinMyPackage #withinMyNamespace #outOfMyClass #outOfMyPackage #outOfMyNamespace
aClass queryAllOutgoingInvocations atClassScope outOfMyPackage
retrieves invoked classes out of my package.
aMethod queryAllIncomingInvocations opposites withinMyClass
retrieves invoking methods in the same class.
Be aware of the difference between querying a class and querying a package with respect to class extensions. By definition a class queries all its methods regardless of their package. A package will only query methods defined in itself, including class extensions to other packages, but not the one from other packages. This has an impact with queries on invocations and accesses.
Moose Chef was conceived with the intent to maximize reuse, exploit polymorphism so that the implementation is small, easy to browse, understand and debug. It is also quite verbose with long selectors so that reading the query would minimize all interpretation error. As a consequence, Moose Chef is NOT:
If you run into performance issues with Moose Chef, the recommendation is to use Moose Chef as a library for specification and testing.