The #param tag allow the documentation of properties, e.g.
/**
* #param {Object} userInfo Information about the user.
* #param {String} userInfo.name The name of the user.
* #param {String} userInfo.email The email of the user.
*/
How would I document the properties of the #this tag?
/**
* #this {Object}
* #param {String} this.name The name of the user.
* #param {String} this.email The email of the user.
*/
I'm wondering if anyone working on the project knows. (The docs are still being created...)
To document instance members, use #name Class#member:
/**
* Construct a new component
*
* #class Component
* #classdesc A generic component
*
* #param {Object} options - Options to initialize the component with
* #param {String} options.name - This component's name, sets {#link Component#name}
* #param {Boolean} options.visible - Whether this component is visible, sets {#link Component#visible}
*/
function Component(options) {
/**
* Whether this component is visible or not
*
* #name Component#visible
* #type Boolean
* #default false
*/
this.visible = options.visible;
/**
* This component's name
*
* #name Component#name
* #type String
* #default "Component"
* #readonly
*/
Object.defineProperty(this, 'name', {
value: options.name || 'Component',
writable: false
});
}
This results in a Members section in the documentation that lists each member, its type, default value, and whether it's read only.
The output as generated by jsdoc#3.3.0-alpha3 looks like this:
See also:
JSDoc namepaths
#instance tag
#readonly tag
#type tag
Use the #property tag to describe the attribute of an object.
#param is used to define the parameters of a method or constructor.
#this is used to define which object this refers to.
So here's an example using JSDOC 3.
/**
* #class Person
* #classdesc A person object that only takes in names.
* #property {String} this.name - The name of the Person.
* #param {String} name - The name that will be supplied to this.name.
* #this Person
*/
var Person = function( name ){
this.name = name;
};
JSDOC 3: https://github.com/jsdoc3/jsdoc
More information: http://usejsdoc.org/index.html
More info: http://code.google.com/p/jsdoc-toolkit/wiki/TagParam
Within the constructor of a class, jsdoc will realize by itself that the documented properties belong to class intances. So this should be enough:
/**
* #classdesc My little class.
*
* #class
* #memberof module:MyModule
* #param {*} myParam Constructor parameter.
*/
function MyLittleClass(myParam) {
/**
* Instance property.
* #type {string}
*/
this.myProp = 'foo';
}
If it is not clear for jsdoc that the function is the class constructor, you can use #this to define what this refers to:
/**
* #classdesc My little class.
*
* #class
* #memberof module:MyModule
* #name MyLittleClass
* #param {*} myParam Constructor parameter.
*/
// Somewhere else, the constructor is defined:
/**
* #this module:MyModule.MyLittleClass
*/
function(myParam) {
/**
* Instance property.
* #type {string}
*/
this.myProp = 'foo';
}
Related
i overrided the twig error pages and i've got an extension to get some settings for layout but i can't access this extension in the error pages
Here is my extension
/**
* Class ThemeExtension.
*/
class ThemeExtension extends AbstractExtension
{
use SettingManagerTrait;
/**
* #var TokenStorageInterface
*/
private $tokenStorage;
/**
* #var AuthorizationCheckerInterface
*/
private $authorizationChecker;
/**
* #var RequestStack
*/
private $stack;
/**
* #var array
*/
private $available_colors;
/**
* ThemeExtension constructor.
*
* #param TokenStorageInterface $tokenStorage
* #param AuthorizationCheckerInterface $authorizationChecker
* #param RequestStack $stack
* #param array $available_colors
*/
public function __construct(TokenStorageInterface $tokenStorage, AuthorizationCheckerInterface $authorizationChecker, RequestStack $stack, array $available_colors)
{
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $authorizationChecker;
$this->stack = $stack;
$this->available_colors = $available_colors;
}
/**
* #return array
*/
public function getFunctions(): array
{
return [
new TwigFunction('theme_name', [$this, 'getThemeName']),
];
}
/**
* #return string
*
* #throws NonUniqueResultException
*/
public function getThemeName(): string
{
// stuff
}
}
I would like to know how to use this in error pages if somebody knows. Thank you for any help or idea. Maybe it's not possible
I have a controller method which I am using to "collect" variables to be assigned to template. I have overridden controller's render() method to merge "collected" and render parameters and assign them to template.
Example:
class Controller extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
private $jsVars = [];
protected function addJsVar($name, $value)
{
$this->jsVars[$name] = $value;
}
public function render($view, array $parameters = [], Response $response = null)
{
return parent::render($view, array_merge($parameters, ['jsVars' => $this->jsVars], $response);
}
public function indexAction()
{
// collect variables for template
$this->addJsVar('foo', 'bar');
return $this->render('#App/index.html.twig', ['foo2' => 'bar2']);
}
}
I just upgraded to Symfony 3.4 which complains that since Symfony4 I am not allowed to override render() method as it will be final.
How could I make it work seamlessly, i.e without defining a new method?
I know about Twig globals but these dont help me
I could use a service to collection variables and inject that service to Twig but that seems odd
Are there events I could listen, e.g TwigPreRender or smth?
You can render a controller from inside Twig like that:
{{ render(controller('App\\Controller\\YourController::yourAction', { 'args': 'hi' })) }}
Documentation here
Seems that there is no easy way.
Basically there are 2 options:
create your own template engine by extending current Symfony\Bundle\TwigBundle\TwigEngine
decorate current templating engine service templating.engine.mytwig
I chose the latter.
Few explanations:
I created service templating.engine.mytwig which decorates current engine templating.engine.twig. Class will get current ´TwigEngine` as input and I'll delegate most of the stuff to it
The class also needs to be twig extension by implementing \Twig_ExtensionInterface (or extending \Twig_Extension was sufficient for me). Also service needs to have tag twig.extension. Otherwise you'll end up having errors such as "Cannot find private service 'assetic' etc"
setParameter/getParameter are for collecting and returning parameters
Then I added shortcut methods to my Controller - setJsVar
Twig template requires also handling of those variables, preferably somewhere in the layout level. But that is not included here
One could you this solution to collect arbitrary template parameters, e.g if you want to assign from another method or whatever
It would be good idea to clear collected parameters after render
Was that all worth it? I dont know :) Cannot understand why Symfony team chose to make Controller::render final in the first place. But anyway here it is:
TwigEnging class:
namespace My\CommonBundle\Component\Templating\MyTwigEngine;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Bundle\TwigBundle\TwigEngine;
use Symfony\Component\HttpFoundation\Response;
class MyTwigEngine extends \Twig_Extension implements EngineInterface
{
/**
* #var TwigEngine $twig Original Twig Engine object
*/
private $twig;
/**
* #var array $parameters Collected parameters to be passed to template
*/
private $parameters = [];
/**
* MyTwigEngine constructor.
*
* #param TwigEngine $twig
*/
public function __construct(TwigEngine $twig)
{
$this->twig = $twig;
}
/**
* "Collects" parameter to be passed to template.
*
* #param string $key
* #param mixed $value
*
* #return static
*/
public function setParameter($key, $value)
{
$this->parameters[$key] = $value;
return $this;
}
/**
* Returns "collected" parameter
*
* #param string $key
* #return mixed
*/
public function getParameter($key, $default = null)
{
$val = $this->parameters[$key] ?? $default;
return $val;
}
/**
* #param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
* #param array $parameters
*
* #return string
* #throws \Twig\Error\Error
*/
public function render($name, array $parameters = array())
{
return $this->twig->render($name, $this->getTemplateParameters($parameters));
}
/**
* #param string $view
* #param array $parameters
* #param Response|null $response
*
* #return Response
* #throws \Twig\Error\Error
*/
public function renderResponse($view, array $parameters = array(), Response $response = null)
{
return $this->twig->renderResponse($view, $this->getTemplateParameters($parameters), $response);
}
/**
* #param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
*
* #return bool
*/
public function exists($name)
{
return $this->twig->exists($name);
}
/**
* #param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
*
* #return bool
*/
public function supports($name)
{
return $this->twig->supports($name);
}
/**
* #param $name
* #param array $parameters
*
* #throws \Twig\Error\Error
*/
public function stream($name, array $parameters = array())
{
$this->twig->stream($name, $this->getTemplateParameters($parameters));
}
/**
* Returns template parameters, with merged jsVars, if there are any
* #param array $parameters
* #return array
*/
protected function getTemplateParameters(array $parameters = [])
{
$parameters = array_merge($this->parameters, $parameters);
return $parameters;
}
}
Decorator service (services.yml):
services:
templating.engine.mytwig:
decorates: templating.engine.twig
class: My\CommonBundle\Component\Templating\MyTwigEngine
# pass the old service as an argument
arguments: [ '#templating.engine.mytwig.inner' ]
# private, because you probably won't be needing to access "mytwig" directly
public: false
tags:
- { name: twig.extension }
Base controller alteration:
namespace My\CommonBundle\Controller;
use My\CommonBundle\Component\Templating\MyTwigEngine;
abstract class Controller extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
/**
* Allows to set javascript variable from action
*
* It also allows to pass arrays and objects - these are later json encoded
*
* #param string $name Variable name
* #param mixed $value - string|int|object|array
*
* #return static
*/
protected function setJsVar($name, $value)
{
/** #var MyTwigEngine $templating */
$templating = $this->getTemplating();
if (!$templating instanceof MyTwigEngine) {
throw new \RuntimeException(sprintf(
'Method %s is implemented only by %s', __METHOD__, MyTwigEngine::class
));
}
$jsvars = $templating->getParameter('jsVars', []);
$jsvars[$name] = $value;
$templating->setParameter('jsVars', $jsvars);
return $this;
}
/**
* Returns templating service
* #return null|object|\Twig\Environment
*/
private function getTemplating()
{
if ($this->container->has('templating')) {
$templating = $this->container->get('templating');
} elseif ($this->container->has('twig')) {
$templating = $this->container->get('twig');
} else {
$templating = null;
}
return $templating;
}
}
I've been working on a Node.js project and only just noticed that Visual Studio Code provides information about base EventEmitter objects. So I imagine it should be possible to provide JSDoc for custom ones too.
I've already attempted following the JSDoc http://usejsdoc.org/tags-event.html documentation but doesn't seem to pick up.
I don't know if this is effecting it but I'm using the ES6 class where the events get processed in a function outside it but it's inside the same script.
This is the test code.
// voice
if (voice) {
try {
/**
* Voice event.
*
* #event TelegramBot#voice
* #type {object}
* #property {object} chat - [object Chat]
* #property {number} date - Date when content was sent.
* #property {object} from - [object User]
* #property {number} message_id - Message id.
* #property {string} caption - Caption added to message. Value is undefined if none is added.
* #property {object} voice - [object Voice]
*/
context.emit('voice', chat, date, from, message_id, caption, voice)
} catch (error) {
context.emit('error', error)
}
}
I found this question while attempting to do the same, so I kept searching and I found a possible solution, by overriding the addListener methods, and documenting them.
I successfully made it work for both NodeJS and browser JS.
Result in VSCode
Typedef
First, create the typedef with your events names :
/**
* #typedef {["someEvent" | "someOtherEvent", ...any[]]} eventsDef
*/
I am using the spread syntax since I don't know how many arguments the user will pass to the method.
Implementation
For NodeJS:
The class needs to override the on and addListener methods, document the arguments, and simply call the parent's own method :
class Test extends EventEmitter {
/**
* #param {eventsDef} args
*/
addListener(...args) {
super.addListener(...args);
}
/**
* #param {eventsDef} args
*/
on(...args) {
super.on(...args);
}
fireEvent() {
this.emit("someEvent", "someValue");
}
}
Then you can use this class like so :
const t = new Test();
t.on("someEvent", (val) => console.log(val));
t.fireEvent(); // Outputs "someValue"
For the browser:
The class needs to extends EventTarget and override the addEventListener method.
Here is the snippet :
class Test extends EventTarget {
/**
* #param {["someEvent" | "someOtherEvent", ...any[]]} args
*/
addEventListener(...args) {
super.addEventListener(...args);
}
fireEvent() {
this.dispatchEvent(new CustomEvent("someEvent", {detail: "someValue"}))
}
}
const t = new Test();
t.addEventListener("someEvent", val => console.log(val.detail));
document.querySelector("button").addEventListener("click", () => {
t.fireEvent();
})
<button>Fire event</button>
I'm trying to develop a project in Groovy and I've been looking through my code and trying to find areas which I could replace with something more idiomatically Groovy until I find a solution for another issue I've been having.
I've started taking a more in-depth look into the use of AST transformation annotations - they've helped significantly cut down on the amount of code I have to write in some places. However, I'm having an issue using the groovy.transform.builder.Builder annotation with one of my immutable value classes. The source for this annotation is hosted here.
The issue is that the annotation seems to make the builder set the values of the buildee directly rather than storing a copy of the values and passing them to the buildee's constructor. This results in a ReadOnlyPropertyException when you try to use it with immutable classes.
There are four possible Builder strategies you can select with this annotation, and of them I've tried DefaultStrategy, ExternalStrategy and InitializerStrategy. However, all of these have caused problems.
ExternalStrategy looks like the most promising of the four, and you can find an SSCCE based on it detailing the problem here.
The source code from the example is also included below:
import groovy.transform.Immutable
import groovy.transform.builder.Builder as GBuilder
import groovy.transform.builder.ExternalStrategy
/*
* Uncommenting the below causes a failure:
* 'groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: value for class: Value'
*/
//#Immutable
class Value {
#GBuilder(forClass = Value, prefix = 'set', builderStrategy = ExternalStrategy)
static class Builder { }
int value
String toString() { "Value($value)" }
}
def builder = new Value.Builder()
println builder.setValue(1).build()
There also seems to be a relevant JIRA discussion on the matter here.
Edit
I've tried using CFrick's answer below, using InitializerStrategy rather than ExternalStrategy.
Everything now compiles, but I get the following errors at run-time when I try to execute my tests:
java.lang.IllegalAccessError: tried to access class com.github.tagc.semver.version.BaseVersion from class com.github.tagc.semver.version.BaseVersion$com.github.tagc.semver.version.BaseVersionInitializer
at java.lang.Class.getDeclaringClass(Class.java:1227)
at java.beans.MethodRef.set(MethodRef.java:46)
at java.beans.MethodDescriptor.setMethod(MethodDescriptor.java:117)
at java.beans.MethodDescriptor.<init>(MethodDescriptor.java:72)
at java.beans.MethodDescriptor.<init>(MethodDescriptor.java:56)
at java.beans.Introspector.getTargetMethodInfo(Introspector.java:1163)
at java.beans.Introspector.getBeanInfo(Introspector.java:426)
at java.beans.Introspector.getBeanInfo(Introspector.java:173)
at com.github.tagc.semver.version.VersionFactory.createBaseVersion(VersionFactory.groovy:34)
at com.github.tagc.semver.test.util.TestSetup.<clinit>(TestSetup.groovy:77)
at java.lang.Class.forName(Class.java:344)
at com.github.tagc.semver.version.SnapshotDecoratorSpec.#decoratedVersion should be considered equal to patch-bumped #releaseVersion snapshot(SnapshotDecoratorSpec.groovy:24)
Followed thereafter by a series of exceptions like the following:
java.lang.NoClassDefFoundError: Could not initialize class com.github.tagc.semver.test.util.TestSetup
at java.lang.Class.forName(Class.java:344)
at com.github.tagc.semver.version.SnapshotDecoratorSpec.#decoratedVersion should be considered equal to minor-bumped #releaseVersion snapshot(SnapshotDecoratorSpec.groovy:36)
What I have right now is a BaseVersion class like the following:
/**
* A concrete, base implementation of {#link com.github.tagc.semver.version.Version Version}.
*
* #author davidfallah
* #since v0.1.0
*/
#Immutable
#Builder(prefix = 'set', builderStrategy = InitializerStrategy)
#PackageScope
final class BaseVersion implements Version {
// ...
/**
* The major category of this version.
*/
int major = 0
/**
* The minor category of this version.
*/
int minor = 0
/**
* The patch category of this version.
*/
int patch = 0
/**
* Whether this version is a release or snapshot version.
*/
boolean release = false
// ...
}
A factory to produce instances of these:
/**
* A factory for producing base and decorated {#code Version} objects.
*
* #author davidfallah
* #since v0.5.0
*/
class VersionFactory {
// ...
/**
* Returns an instance of {#link com.github.tagc.semver.version.BaseVersion BaseVersion} constructed
* with the given parameters.
*
* #param major the major category value of the version instance
* #param minor the minor category value of the version instance
* #param patch the patch category value of the version instance
* #param release the release setting of the version instance
* #return an instance of {#code BaseVersion}
*/
static BaseVersion createBaseVersion(int major, int minor, int patch, boolean release) {
return new BaseVersion(major, minor, patch, release)
}
/**
* Returns an instance of {#link com.github.tagc.semver.version.BaseVersion BaseVersion} constructed
* with the given parameters.
*
* #param m a map of parameter names and their corresponding values corresponding to the
* construction parameters of {#code BaseVersion}.
*
* #return an instance of {#code BaseVersion}
*/
static BaseVersion createBaseVersion(Map m) {
return new BaseVersion(m)
}
/**
* Returns an instance of {#link com.github.tagc.semver.version.BaseVersion BaseVersion} constructed
* with the given parameters.
*
* #param l a list of parameter values corresponding to the construction parameters of {#code BaseVersion}.
*
* #return an instance of {#code BaseVersion}
*/
static BaseVersion createBaseVersion(List l) {
return new BaseVersion(l)
}
/**
* Returns a builder for {#link com.github.tagc.semver.version.BaseVersion BaseVersion} to specify
* the construction parameters for the {#code BaseVersion} incrementally.
*
* #return an instance of {#code BaseVersion.Builder}
*/
static Object createBaseVersionBuilder() {
return BaseVersion.builder()
}
// ...
}
A test specification class for Version objects:
/**
* Test specification for {#link com.github.tagc.semver.version.Version Version}.
*
* #author davidfallah
* #since 0.1.0
*/
#Unroll
class VersionSpec extends Specification {
static exampleVersions = [
VersionFactory.createBaseVersion(major:1, minor:2, patch:3),
VersionFactory.createBaseVersion(major:0, minor:0, patch:0),
VersionFactory.createBaseVersion(major:5, minor:4, patch:3),
VersionFactory.createBaseVersion(major:1, minor:16, patch:2),
VersionFactory.createBaseVersion(major:4, minor:5, patch:8),
]
// ...
}
And other classes that try to create instances of BaseVersion that are failing, such as TestSetup.
Your code there fails, because chosen strategy there basically does:
def v = new Value().with{ setValue(1); return it }
and this can not be done on #Immutable objects.
According to the docs, there is only InitializerStrategy, that can explicitly cope with #Immutable.
You can use the InitializerStrategy in conjunction with #Canonical and #Immutable. If your #Builder annotation doesn’t have explicit includes or excludes annotation attributes but your #Canonical annotation does, the ones from #Canonical will be re-used for #Builder.
E.g.
import groovy.transform.*
import groovy.transform.builder.*
#Immutable
#ToString
#Builder(prefix='set', builderStrategy=InitializerStrategy)
class Value {
int value
}
def builder = Value.createInitializer().setValue(1)
assert new Value(builder).toString()=='Value(1)'
Depending on what you are up to, this is rahter ugly syntax and you might be better off just using the Map-based c'tors. Even without e.g. #TypeChecked a new Value(vlaue: 666) will generate an error and leaving params (for a class with multiple properties) will leave them null.
How can I set the default value in field.
In my document I need to set default value false for field emailnotify
In mogodb th default value should be zero.
Check my document
namespace xxx\xxxBundle\Document;
use FOS\UserBundle\Document\User as BaseUser;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class User extends BaseUser
{
/**
* #MongoDB\Id(strategy="auto")
*/
protected $id;
/**
* #MongoDB\Boolean
*/
protected $emailnotify;
/**
* Sets the emailnotify.
*
* #param boolean $emailnotify
*
* #return User
*/
public function setEmailnotify($emailnotify)
{
$this->emailnotify = (Boolean) $emailnotify;
return $this;
}
/**
* #return boolean
*/
public function isEmailnotify()
{
return $this->emailnotify;
}
}
I have found that setting the default value in the constructor works
public function __construct() {
$this->emailnotify = false;
}
Of course just setting the class variable to false will work for most parts if you use Doctrine to fetch the Document again afterwards, but the property will not be persisted to MongoDB like with the above.