<commit_before>RFC 0102: XP Class Runner
========================================================================
$Id$

@authors: friebe
@created: Mon Dec 18 11:57:41 CET 2006
@status: draft
@category: util.cmd

Scope of Change
---------------
An runner script (similar to the unittest runner) will be created to run
classes as scripts.


Rationale
---------
Simplify command line utilities development.


Functionality
-------------

Overview
~~~~~~~~
Instead of writing a complete script we now declare a class as follows:

<?php
  class DumpNews extends Command {
    var
      $id   = 0,
      $conn = NULL;

    #[@arg(position= 0)]
    function setNewsId($id) {
      $this->id= $id;
    }

    #[@inject(type= 'rdbms.DBConnection', name= 'news')]  
    function setConnection(&$conn) {
      $this->conn= &$conn;
    }
  
    function run() {
      $this->out->writeLine(xp::stringOf($conn->select(
        '* from news where news_id= %d', 
        $this->id
      )));
    }    
  }
?>

We can run this class by using the cli-runner:
<pre>
  $ xpcli -c ~/.xp/news/ de.thekid.blog.DumpNews 1
</pre>


Rules
~~~~~
<pre>
* The class must extend the util.cmd.Command class

* Arguments and resources are injected by means of annotated methods

* We use an output stream to write data

* The class can be reused in different environments because it is not 
  dependant on anything console-specific.
</pre>


The util.cmd.Command class
~~~~~~~~~~~~~~~~~~~~~~~~~~
<?php
  class Command extends Object {
    var
      $out = NULL,
      $err = NULL;
    
    /**
     * Run method
     *
     * @model   abstract
     * @access  public
     */
    function run() { }
  }
?>
The out and err members are created by the runner and are 
PrintOutputStream objects. For the console runner, they write data
to STDOUT and STDERR.


The @arg annotation
~~~~~~~~~~~~~~~~~~~
This annotation marks methods as parameter acceptors.

<pre>
* Methods that wish to accept a command line argument must have exactly 
  one argument.

* This argument may have a default value.

* If the method does not have a default value, the parameter passing
  stops hard at this point.

* Otherwise, the method is invoked without arguments, so that the
  default value will be passed.
</pre>

How to accept arguments:

1) Positional argument
<?php
  #[@arg(position= 0)]
  function setFirstArgument($v) { }
?>
  This will take the first command line argument


2) Named argument
<?php
  #[@arg(name= 'verbosity')]
  function setVerbosity($v) { }
?>
  This will take the value passed as --verbosity=<value> or -v <value>


3) Auto-named argument
<?php
  #[@arg]
  function setClassname($v) { }
?>
  This will take the value passed as --classname=<value> or -c <value>

  The argument name is calculated by using a lowercase version of the 
  method name and stripping a leading "set".

<pre>
Note for cases 2) and 3): The short argument's name is calculated by using 
the first character of the long argument's name. It can be overwritten to
resolve ambiguous names by adding a short= '[CHARACTER]' to the @arg
annotation.
</pre>


Security considerations
-----------------------
n/a


Speed impact
------------
n/a


Dependencies
------------
- RFC #0088
  I/O Streams API


Related documents
-----------------
- http://experiments.xp-framework.net/?people,friebe,cli<commit_msg>- Add details on the @inject annotation<commit_after>RFC 0102: XP Class Runner
========================================================================
$Id$

@authors: friebe
@created: Mon Dec 18 11:57:41 CET 2006
@status: draft
@category: util.cmd

Scope of Change
---------------
An runner script (similar to the unittest runner) will be created to run
classes as scripts.


Rationale
---------
Simplify command line utilities development.


Functionality
-------------

Overview
~~~~~~~~
Instead of writing a complete script we now declare a class as follows:

<?php
  class DumpNews extends Command {
    var
      $id   = 0,
      $conn = NULL;

    #[@arg(position= 0)]
    function setNewsId($id) {
      $this->id= $id;
    }

    #[@inject(type= 'rdbms.DBConnection', name= 'news')]  
    function setConnection(&$conn) {
      $this->conn= &$conn;
    }
  
    function run() {
      $this->out->writeLine(xp::stringOf($conn->select(
        '* from news where news_id= %d', 
        $this->id
      )));
    }    
  }
?>

We can run this class by using the cli-runner:
<pre>
  $ xpcli -c ~/.xp/news/ de.thekid.blog.DumpNews 1
</pre>


Rules
~~~~~
<pre>
* The class must extend the util.cmd.Command class

* Arguments and resources are injected by means of annotated methods

* We use an output stream to write data

* The class can be reused in different environments because it is not 
  dependant on anything console-specific.
</pre>


The util.cmd.Command class
~~~~~~~~~~~~~~~~~~~~~~~~~~
<?php
  class Command extends Object {
    var
      $out = NULL,
      $err = NULL;
    
    /**
     * Run method
     *
     * @model   abstract
     * @access  public
     */
    function run() { }
  }
?>
The out and err members are created by the runner and are 
PrintOutputStream objects. For the console runner, they write data
to STDOUT and STDERR.


The @arg annotation
~~~~~~~~~~~~~~~~~~~
This annotation marks methods as parameter acceptors.

<pre>
* Methods that wish to accept a command line argument must have exactly 
  one argument.

* This argument may have a default value.

* If the method does not have a default value, the parameter passing
  stops hard at this point.

* Otherwise, the method is invoked without arguments, so that the
  default value will be passed.
</pre>

How to accept arguments:

1) Positional argument
<?php
  #[@arg(position= 0)]
  function setFirstArgument($v) { }
?>
  This will take the first command line argument


2) Named argument
<?php
  #[@arg(name= 'verbosity')]
  function setVerbosity($v) { }
?>
  This will take the value passed as --verbosity=<value> or -v <value>


3) Auto-named argument
<?php
  #[@arg]
  function setClassname($v) { }
?>
  This will take the value passed as --classname=<value> or -c <value>

  The argument name is calculated by using a lowercase version of the 
  method name and stripping a leading "set".

<pre>
Note for cases 2) and 3): The short argument's name is calculated by using 
the first character of the long argument's name. It can be overwritten to
resolve ambiguous names by adding a short= '[CHARACTER]' to the @arg
annotation.
</pre>


The @inject annotation
~~~~~~~~~~~~~~~~~~~~~~
This annotation marks methods as resource injectors.

<pre>
* Resources are one of the following: database connections, logger 
  categories, properties.

* Resources are configured by property objects:
  - The property manager's lookup path is set to "etc/" per default
    and may be set via xpcli -c <path>
  - The connection manager is configured by database.ini
  - The logger is configured by log.ini
</pre>

1) Injecting database connections
<?php
  #[@inject(type= 'rdbms.DBConnection', name= 'news')]  
  function setConnection(&$conn) { }
?>
  This method will receive a DBConnection instance (via 
  ConnectionManager::getInstance()->getByHost('news', 0))


2) Injecting logger categories
<?php
  #[@inject(type= 'util.log.LogCategory', name= 'default')]  
  function setTrace(&$cat) { }
?>
  This method will receive a LogCategory instance (via 
  Logger::getInstance()->getCategory('default'))


3) Injecting properties
<?php
  #[@inject(type= 'util.Properties', name= 'app')]  
  function setApplicationConfig(&$conf) { }
?>
  This method will receive a LogCategory instance (via 
  PropertyManager::getInstance()->getProperties('app'))


Security considerations
-----------------------
n/a


Speed impact
------------
n/a


Dependencies
------------
- RFC #0088
  I/O Streams API


Related documents
-----------------
- http://experiments.xp-framework.net/?people,friebe,cli
{'fraction_non_alphanumeric': 0.15313723014770864, 'fraction_numerical': 0.007827294533518495, 'mean_word_length': 3.108921161825726, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 58, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3344634', 'n_tokens_mistral': 2419, 'n_tokens_neox': 2266, 'n_words': 916}