What is the correct way to document changes to an object/function in JSDoc? - jsdoc

Say I have a function that takes one parameter:
/**
* #summary Some function.
*
* #since 1.0.0
*
* etc.
*/
function func( a ) {
// do stuff...
}
And then a few versions later support for a second parameter is added. In PHPDoc you can add additional #since entries to keep a change log specific to that element:
/**
* #summary Some function.
*
* #since 1.0.0
* #since 1.2.0 Parameter b was added, because reasons.
*
* etc.
*/
function func( a, b ) {
// do stuff...
}
That way it is easy for folks to see when a change like this was introduced. Is there an element "change log" standard in JSDoc?
I've checked the page for #since on usejsdoc.org, but there's no mention of anything like this, or of different tag that should be used for it.

Quite some time since your question, but there is now such a tag of the same name: #since
(And FWIW, you can use #version if you want a version to your particular method.)

Related

How does one make jsdoc actually output docs?

I'm trying to get jsdoc (version 3.6.7, using node 16) to turn my documented js code into actual documentation, but no matter what I do it just generates an out directory with an index.html that is primarily empty lines, rather than documentation. I've asked about this over on the issue tracker (after I searched the docs and the web for information on what one might be doing wrong to get jsdoc to generate empty files, but I can't for the life of me find anything useful that addresses that) but since it's been a few days, it feels useful to ask here as well, so that an answer in either place can be cross posted.
Running the jsdoc command does not flag any errors with the input, and completes with a normal zero exit code but generates nothing useful, so hopefully someone here's run into his before and can explain what is necessary in addition to the follow code to actually get jsdoc to generate docs.
An example of code that has no errors according to jsdoc, but also yields no docs whatsoever:
import { Errors } from "../errors.js";
import { Models } from "./models.js";
/**
* Several paragraphs of text that explain this class
*
* #class
* #namespace model
*/
export class Model {
/**
* #ignore
*/
static ALLOW_INCOMPLETE = Symbol();
/**
* Also several paragraphs explaining the use of this function.
*
* #static
* #param {*} data
* #param {*} allowIncomplete (must be Model.ALLOW_INCOMPLETE to do anything)
* #returns {*} a model instance
* #throws {*} one of several errors
*/
static create = function (data = undefined, allowIncomplete) {
return Models.create(
this,
data,
allowIncomplete === Model.ALLOW_INCOMPLETE
);
};
/**
* code comment that explains that if you're reading
* this source, you should not be using the constructor,
* but should use the .create factory function instead.
*
* #ignore
*/
constructor(caller, when) {
if (!caller || typeof when !== "number") {
const { name } = this.__proto__.constructor;
throw Errors.DO_NOT_USE_MODEL_CONSTRUCTOR(name);
}
}
}
Running this with jsdoc test.js yields an out dir with an index.html and test.js.html file, the first containing some thirty newlines of "no docs here" with boilerplate wrapper HTML code, and the second containing the original source code with nothing else useful either.
What else does one need to do to get jsdoc to actually generate documentation here?
I have fixed it by not using export infront of classes, instead exporting them at the end of the file. like this:
import { Errors } from "../errors.js";
import { Models } from "./models.js";
/**
* Several paragraphs of text that explain this class
*
* #class
* #namespace model
*/
class Model {
/**
* #ignore
*/
static ALLOW_INCOMPLETE = Symbol();
/**
* Also several paragraphs explaining the use of this function.
*
* #static
* #param {*} data
* #param {*} allowIncomplete (must be Model.ALLOW_INCOMPLETE to do anything)
* #returns {*} a model instance
* #throws {*} one of several errors
*/
static create = function (data = undefined, allowIncomplete) {
return Models.create(
this,
data,
allowIncomplete === Model.ALLOW_INCOMPLETE
);
};
/**
* code comment that explains that if you're reading
* this source, you should not be using the constructor,
* but should use the .create factory function instead.
*
* #ignore
*/
constructor(caller, when) {
if (!caller || typeof when !== "number") {
const { name } = this.__proto__.constructor;
throw Errors.DO_NOT_USE_MODEL_CONSTRUCTOR(name);
}
}
}
export {Model}
Turns out this was posted too early: taking the time to start at the official documentation for classes over on https://jsdoc.app/tags-class.html and running that example through jsdoc works perfectly fine, and subsequently building out that example to match the actual file's code yields working documentation just fine, too.
And in this specific case, there were several problems:
adding #namespace paired with #class was the main problem. Neither were necessary, but the #namespace entry changes how jsdoc parses the rest of a file's documentation, where if methods are to show up, they must use a #name property that includes that namespace. As that was not the case here, nothing ended up showing in the documentation.
having an #ignore on the constructor function, rather than using the #hideconstructor property on the class meant that even with #namespace removed, no documentation got written. JSdoc treats the class docs heading and the constructor as the same thing, so #ignoreing the constructor is treated the same as ignoring the entire class.
Fixing both mistakes, and removing the unnecessary #class at the top, gives perfectly fine documentation:
import { Errors } from "../errors.js";
import { Models } from "./models.js";
/**
* Several paragraphs of text that explain this class
*
* #hideconstructor
*/
export class Model {
/**
* #ignore
*/
static ALLOW_INCOMPLETE = Symbol();
/**
* Also several paragraphs explaining the use of this function.
*
* #static
* #param {*} data
* #param {*} allowIncomplete (must be Model.ALLOW_INCOMPLETE to do anything)
* #returns {*} a model instance
* #throws {*} one of several errors
*/
static create = function (data = undefined, allowIncomplete) {
return Models.create(
this,
data,
allowIncomplete === Model.ALLOW_INCOMPLETE
);
};
/**
* code comment that explains that if you're reading
* this source, you should not be using the constructor,
* but should use the .create factory function instead.
*/
constructor(caller, when) {
if (!caller || typeof when !== "number") {
const { name } = this.__proto__.constructor;
throw Errors.DO_NOT_USE_MODEL_CONSTRUCTOR(name);
}
}
}

Doxygen assign multiple functions to class

I am trying to document C interface of my library with doxygen. All functions are grouped within a virtual classes and the assigned with operator \memberof. Since there are a lot of functions, I would like to assign multiple functions with group operator #{.
/**
* \class Base
*/
/**
* \class Derived
* \extends Base
*/
/**
* \memberof Base
* #{
*/
/**
* \brief Function A
*/
void Base_A();
/**
* \brief Function B
*/
void Base_B();
/** #} */
This creates page for derived class as following:
For some reason, there are two sections for inherited members - public inherited and related. In my opinion, both functions should be in the same category.
Adding \memberof command to every single function seems to solve the problem.
/**
* \class Base
*/
/**
* \class Derived
* \extends Base
*/
/**
* \brief Function A
* \memberof Base
*/
void Base_A();
/**
* \brief Function B
* \memberof Base
*/
void Base_B();
Can someone confirm this being a bug?
Can someone provide a working group assignment of multiple functions to a class? I have hundreds of functions and adding so many commands is not viable.
I hesitate to call this a bug. The \{ \} commands are intended to be used for grouping together with grouping commands like \ingroup, \defgroup, \addtogroup, \weakgroup and in this case it is used together with \memberof.
It sees the \memberof command and 'assigns' the next function to 'Base' conform:
This command makes a function a member of a class in a similar way as \relates does, only with this command the
function is represented as a real member of the class. This can be useful when the programming language does
not support the concept of member functions natively (e.g. C).
It looks like it ignores the \{ and\}.

In an extbase extension, how to access the persistence layer from a scheduler task?

What sounds a bit academic in the title is actually quite straightforward: I have set up a TYPO3 6.1 extbase extension that I've equipped with a scheduler task. The task is supposed to import a CSV file and save it into the extension's database fields.
But how do I tell the scheduler task to use the extension's model etc. and save the received data into the persistence layer?
I've seen this answer to a similar question: Execute repository functions in scheduler task and I think it points the right way, but I think need a full example to start understanding how the dependency injection works.
First you have to consider the aspect of performance:
If you want to insert a big amount of data, you should not use the Extbase persistence for such a task. Because if you do so, it will generate an object for each row you want to insert and persist it immediately. This is quite slow and has a big memory footprint.
If you don't have much data or you split the jobs (e.g. perform 100 import jobs per scheduler run), then use the Extbase persistence.
You can have both in CommandController context, and since CommandControllers are straight-forward to set up, you should go for them instead of an own Scheduler task.
Using Extbase persistence
In the CommandController, inject your repository:
/**
* myRepository
*
* #var \Venor\Myext\Domain\Repository\MyRepository
* #inject
*/
protected $myRepository
Then iterate through the rows you want to import (foreach) and create a new object for every row and add it to your repository:
$myObject = $this->objectManager->get('Vendor\Myext\Domain\Model\MyModel');
$myObject->setProperty('foo');
$myObject->setOtherProperty('bar');
$this->myRepository->add($myObject);
To actually save the objects to the database, you need to persist them. So you also inject the persistenceManager:
/**
* #var \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager
* #inject
*/
protected $persistenceManager;
And use it:
$this->persistenceManager->persistAll();
You should not do that for every single object (for performance reasons); but for memory usage reasons you should not wait until after thousands of objects to persist, either. So you just insert an iterator to your foreach loop and persist every 20th, 40th, or whatever loop.
Please don't forget that the Scheduler works in Backend context, so the TypoScript must be available by module.tx_yourext. If you want to share the same settings/storagePid with the frontend part of your app, use
module.tx_yourext.persistence < plugin.tx_yourext.persistence
[...]
The TypoScript needs to be present in the root page of your website for backend modules/CommandControllers to use them. I suggest you add the stuff to myext/Configuration/TypoScript/setup.txt and add the static template of your extension to the root page.
Using DataHandler
The TYPO3 DataHandler (formerly TCEmain) is the engine the TYPO3 backend uses for inserting and modifying database records. It is very powerful.
Instead of an object, inside your loop you create an array containing all the data. The first array index is the table, the next level is the affected record, where NEW means that a new record is created. Then you can just set every field of a table with the desired value
$data = array();
$data['be_users']['NEW'] = array(
'pid' => 0,
'username' => $staffMember['IDPerson'],
'password' => md5(GeneralUtility::generateRandomBytes(40)), // random password
'usergroup' => '1,2',
'email' => $staffMember['Email'],
'realName' => $staffMember['Nachname'] . ' ' . $staffMember['Vorname'],
'lang' => 'de',
);
Now you can make an Instance of DataHandler and persist the changes:
/** #var $tce t3lib_TCEmain */
$tce = GeneralUtility::makeInstance('TYPO3\CMS\Core\DataHandling\DataHandler');
$tce->bypassAccessCheckForRecords = TRUE;
$tce->start($data, array());
$tce->admin = TRUE;
$tce->process_datamap();
$newRecordsUidArray = $tce->substNEWwithIDs['NEW'];
Please note the line $tce->admin = TRUE. This suggests to DataHandler that an admin is performing the action. This is convenient because you don't have to set allowed exclude fields for the Scheduler user and can also insert records to PID 0. But it is a possible security flaw, so carefully consider its usage.
Records inserted/updated by DataHandler logged correctly, can be reverted etc.. You can find some examples (such as adding pictures, resolving MM relations) here. In this case all DataHandler related functions were moved to an external repository class that is injected to the CommandController as described above (it's just named in Extbase convention).
A good overview of DataHandler functions can be found here.
In Addition to lorenz's answer: Beginner's Guide to set up a Command Controller Scheduler task:
My example is an import task. Change the Name part "Import" to your needs.
Create a new file EXT:Classes/Controller/ImportCommandController.php
<?php
namespace NAMESPACE\Myextension\Controller;
/***************************************************************
* Copyright notice
*
* (c) 2014
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
/**
*
*
* #package Myextension
* #license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3 or later
*
*/
class ImportCommandController extends \TYPO3\CMS\Extbase\Mvc\Controller\CommandController {
/**
* itemRepository
*
* #var \NAMESPACE\Myextension\Domain\Repository\ItemRepository
* #inject
*/
protected $itemRepository;
/**
* #var \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager
* #inject
*/
protected $persistenceManager;
/**
*
* #param \integer $storagePid
* #param \string $url
*
* #return bool
*/
// very nice: parameters will be fields in the scheduler!
public function importCommand($storagePid = 0,$url = NULL) {
$source = utf8_encode(utf8_encode(file_get_contents($url)));
// set storage page ourselves
// not sure if really necessary
$querySettings = $this->itemRepository->createQuery()->getQuerySettings();
$querySettings->setRespectStoragePage(FALSE);
$this->itemRepository->setDefaultQuerySettings($querySettings);
// do your stuff here
$source = 'foo';
$rendered = 'bar';
// then store it
// this seems to be only necessary if we don't have an existing item yet
// but as we don't know that here, we have to do it
$item = $this->objectManager->get('STUBR\Therapiestellen\Domain\Model\Item');
// find all existing items
$all = $this->itemRepository->findAll();
// if we have an item already, take the first (and only one)
if(count($all) > 0){
$item = $all->getFirst();
}
// set / update properties
$item->setSource($source);
$item->setRendered($r);
$item->setPid($storagePid);
// either update or add it
if(count($all) > 0){
$this->itemRepository->update($item);
}
else {
$this->itemRepository->add($item);
}
// persist it
$this->persistenceManager->persistAll();
}
}
?>
In EXT:ext_localconf.php, add the command controller:
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['extbase']['commandControllers'][] = 'NAMESPACE\\Myextension\\Controller\\ImportCommandController';
Configure in Scheduler:
That's basically it!

How to Document following function in JSDoc JS-Toolkit

*How to Document following function in JSDoc JS-Toolkit *
I want to document try and help method in this main function
but i did not figure it out how to do that.
/** Sample doc
* #class
* #constructor
* #name Sample
*/
var main=function(){
this.value="";
/** help function
* #param {String} Name
*/
this.help=function(name){
console.log('help me'+name);
}
/** help function
* #param {String} Name
*/
this.try=function(name){
console.log('try me'+name);
}
}
I just struggled with this for several hours. I tried:
#member
#augments
#method
#this
From the examples and tutorials I found, member functions and variables should appear in the output simply by having /** description/* comments above them, but I found that was not the case. Like you, I'm using standard JavaScript constructors, where this should be able to be inferred automatically due to the #constructor being in place. Maybe there's some wrinkle I'm not seeing.
In the end, I found two tags that worked for me, #name and#memberof. They both allow you to specify the object that the property is a member of. Using #name in this way is undocumented (at least, I didn't see it anywhere), but very straightforward. You'll also need to use #function.
Here's an example with the #name tag:
/** help function
* #name Sample.try
* #function
* #param {String} Name
*/
this.try=function(name){
console.log('try me'+name);
};
And here's an example with the #memberof tag:
/** help function
* #memberof Sample
* #function
* #param {String} Name
*/
this.try=function(name){
console.log('try me'+name);
};
As you can see the output is almost the same. The only difference that I see is that #memberof includes this. in the method name. For that reason I've settled on using #name.
The remaining issue is that the functions are per-instance, not <static>.
Hope this helps!

ScalaDocs tags in InitelliJ?

When I type out a method with parameters in IntelliJ then try to add doc tags to it, when I type
/**
I would expect that the #param and #return tags are filled out for me. How can I get that to automatically happen when I type the above
e.g.
/**
*
* #param field1
* #return String
*/
def testMethod(field1:String): String = "hi"
but instead when I do that it does this:
/**
*/
def testMethod(field1:String): String = "hi"
It is working in IntelliJ idea v11.1.2 and scala pluggin v0.5.800
Just type /** and then press "return" on top of a function. It generates the following code:
/**
*
* #param field1
* #return
*/
def testMethod(field1:String): String = "hi"
You can also try to update your scala pluggin, this feature is young.
Edit: There is an open ticket in JetBrains for the doc, but "it seems to be fixed"
http://youtrack.jetbrains.com/issue/SCL-2433#tab=Comments
Must have been a problem with my existing settings somewhere, I cleared out my settings on OSX deleting the ~/Library/Preferences/IntelliJ* folder and starting it back up, reconfiguring my IntelliJ setup. It's now working with scaladoc tags now.