gjtorikian / biscotto Goto Github PK
View Code? Open in Web Editor NEWUNMAINTAINED. CoffeeScript API documentation tool that uses TomDoc notation.
Home Page: https://gjtorikian.github.io/biscotto/
License: MIT License
UNMAINTAINED. CoffeeScript API documentation tool that uses TomDoc notation.
Home Page: https://gjtorikian.github.io/biscotto/
License: MIT License
I'd love this, as some of my code is in CoffeeScript and some in JavaScript.
Also an article here with the same request: http://www.aaron-gray.com/tomdoc-for-javascript/
I understand that GitHub writes everything in CoffeeScript now, so if this isn't a priority for GitHub, it would be nice to expose a plugin system or the like to be able to add new languages to the biscotto eco-system.
The generated docs reference a branch that isn't there anymore. Re-run biscotto on master. Otherwise the "source" links won't work.
Command:
$ /Users/balupton/Projects/taskgroup/node_modules/biscotto/bin/biscotto -n TaskGroup --title "TaskGroup API Documentation" -r README.md -o docs src - LICENSE.md HISTORY.md
[WARN] Cannot resolve link to
in class Task
[WARN] Cannot resolve link to
in class Task
[WARN] Cannot resolve link to
in class Task
Parsed files: 6
Classes: 2 (0 undocumented)
Mixins: 0
Non-Class files: 0
Methods: 45 (0 undocumented)
Constants: 0 (0 undocumented)
100.00% documented (47 total, 0 with no doc)
3 errors
The problem is that error message is not very helpful, it doesn't tell me what or where I should be looking.
File is from bevry/taskgroup:
# Import
queue = global?.setImmediate or process.nextTick # node 0.8 b/c
events = require('events')
domain = (try require('domain')) ? null
util = require('util')
{EventEmitter} = require('events')
ambi = require('ambi')
csextends = require('csextends')
wait = (delay, fn) -> setTimeout(fn, delay)
# =====================================
# Interface
# Internal: Base class containing common functionality for {Task} and {TaskGroup}.
class Interface extends EventEmitter
# Adds support for the done event while
# ensuring that errors are always handled correctly.
#
# It does this by listening to the `error` and `completed` events,
# and when the emit, we check if there is a `done` listener:
# - if there is, then emit the done event with the original event arguments
# - if there isn't, then output the error to stderr and throw it.
constructor: ->
super
me = @
# Add support for the done event
# If we do have an error, then throw it if there is no existing or done listeners
@on 'error', (args...) ->
err = args[0]
if me.listeners('done').length isnt 0 # has done listener, forward to that
@emit('done', args...)
else if err and me.listeners('error').length is 1 # has error, but no done listener and no event listener, throw err
# this isn't good enough, throw the error
console.error(err.stack or err)
throw err
@on 'completed', (args...) ->
err = args[0]
if me.listeners('done').length isnt 0 # has done listener, forward to that
@emit('done', args...)
else if err and me.listeners('completed').length is 1 # has error, but no done listener and no event listener, throw err
# this isn't good enough, throw the error
console.error(err.stack or err)
throw err
# Chain
@
# Internal: Fire our completion event.
complete: ->
err = throw Error('interface should provide this')
@emit('error', err)
@
# Public: Attaches the listener to the `done` event to be emitted each time.
#
# listener - The {Function} to attach to the `done` event.
whenDone: (listener) ->
# check if we have a listener
if typeof listener is 'function'
@on('done', listener.bind(@))
# Chain
@
# Public: Attaches the listener to the `done` event to be emitted only once, then removed to not fire again.
#
# listener - The {Function} to attach to the `done` event.
onceDone: (listener) ->
# check if we have a listener
if typeof listener is 'function'
@once('done', listener)
# Chain
@
# Public: Alias for {::onceDone}
done: (args...) ->
return @onceDone(args...)
# Public: Get our name with all of our parent names into a {String} or {Array}.
#
# opts - The options
# :format - (default: 'string') A {String} that determines the format that we return, when `string` it will output a string of all our names, when `array` it will return the names as an array
# :seperator - (default: ' ➞ ') A {String} that is used to join our array when returning a joined {String}
#
# Returns either a joined {String} or an {Array} based on the value of the `format` option.
getNames: (opts={}) ->
# Prepare
opts.format ?= 'string'
opts.separator ?= ' ➞ '
# Fetch
names = @config.parent?.getNames(format: 'array') or []
names.push(name) if name = @getName()
# Format
if opts.format isnt 'array'
names = names.join(opts.separator)
# Return
return names
# Public: Get the name of our instance.
#
# If the name was never configured, then return the name in the format of
# `'#{@type} #{Math.random()}'` to output something like `task 0.2123`
#
# Returns the configured name {String}.
getName: ->
return @config.name ?= "#{@type} #{Math.random()}"
# Public: Get the configuration of our instance.
#
# Returns our configuration {Object} directly.
getConfig: -> @config
# =====================================
# Public: Our Task Class.
#
# Available configuration is documented in {::setConfig}.
#
# Available events:
#
# - `started()` - emitted when we start execution
# - `running()` - emitted when the method starts execution
# - `failed(err)` - emitted when execution exited with a failure
# - `passed()` - emitted when execution exited with a success
# - `completed(err, args...)` - emitted when execution exited, `args` are the result arguments from the method
# - `error(err)` - emtited if an unexpected error occurs without ourself
# - `done(err, args...)` - emitted when either execution completes (the `completed` event) or when an unexpected error occurs (the `error` event)
#
# Available internal statuses:
#
# - `null` - execution has not yet started
# - `'started'` - execution has begun
# - `'running'` - execution of our method has begun
# - `'failed'` - execution of our method has failed
# - `'passed'` - execution of our method has succeeded
# - `'destroyed'` - we've been destroyed and can no longer execute
#
# Examples
#
# task = require('taskgroup').Task.create('my synchronous task', function(){
# return 5
# }).done(console.log) // [null, 5]
#
# task = require('taskgroup').Task.create('my asynchronous task', function(complete){
# complete(null, 5)
# }).done(console.log) // [null, 5]
#
# task = require('taskgroup').Task.create('my task that errors', function(){
# var err = new Error('deliberate error')
# return err; // if asynchronous, can also do: complete(err)
# // thrown and uncaught errors are also caught thanks to domains, but that should be avoided
# // as it would put your app in an unknown state
# }).done(console.log) // [Error('deliberator error')]
class Task extends Interface
# Internal: The type of our class for the purpose of duck typing
# which is needed when working with node virtual machines
# as instanceof will not work in those environments.
type: 'task'
# Public: A helper method to check if the passed argument is an instanceof a {Task}.
#
# item - The possible instance of the {Task} that we want to check
#
# Returns a {Boolean} of whether or not the item is a {Task} instance.
@isTask: (item) -> return item?.type is 'task' or item instanceof Task
# Public: A helper method to create a new subclass with our extensions.
#
# extensions - An {Object} of extensions to apply to the new subclass
#
# Returns the new sub {Class}
@subclass: csextends
# Public: Creates a new {Task} instance.
#
# args - The Arguments to forwarded along to the {::constructor}.
#
# Returns the new {Task} instance.
@create: (args...) -> return new @(args...)
# Internal: The first {Error} that has occured.
err: null
# Internal: An {Array} of the result arguments of our method.
# The first item in the array should be the {Error} if it exists.
result: null
# Internal: A {String} containing our current status. See our {Task} description for available values.
status: null
# Internal: An {Array} of the events that we may emit. Events that will be executed can be found in the {Task} description.
events: null
# Internal: The {Domain} that we create to capture errors for our method.
taskDomain: null
# Internal: The configuration for our {Task} instance. See {::setConfig} for available configuration.
config: null
# Public: Initialize our new {Task} instance. Forwards arguments onto {::setConfig}.
constructor: (args...) ->
super
# Prepare
@config ?= {}
@config.run ?= false
@config.onError ?= 'exit'
@config.ambi ?= true
@config.domain ?= true
@events ?= []
@events.push('error', 'started', 'running', 'failed', 'passed', 'completed', 'done', 'destroyed')
# Apply configuration
@setConfig(args)
# Chain
@
# Public: Set the configuration for our instance.
#
# Despite accepting an {Object} of configuration, we can also accept an {Array} of configuration.
# When using an array, a {String} becomes the :name, a {Function} becomes the :method, and an {Object} becomes the :config
#
# config - Our configuration {Object} can contain the following fields:
# :name - (default: null) A {String} for what we would like our name to be, useful for debugging.
# :done - (default: null) A {Function} that we would like passed to {::onceDone} (aliases are :onceDone, and :next)
# :whenDone - (default: null) A {Function} that we would like passed to {::whenDone}
# :on - (default: null) An {Object} of (eventName => listener) that we would like bound via EventEmitter.on.
# :once - (default: null) An {Object} of (eventName => listener) that we would like bound via EventEmitter.once.
# :method - (default: null) The {Function} that we would like to execute within our task.
# :parent - (default: null) A parent {TaskGroup} that we may be attached to.
# :onError - (default: 'exit') A {String} that is either `'exit'` or `'ignore'`, when `'ignore'` duplicate run errors are not reported, useful when combined with the timeout option.
# :args - (default: null) An {Array} of arguments that we would like to forward onto our method when we execute it.
# :timeout - (default: null) A {Number} of millesconds that we would like to wait before timing out the method.
# :ambi - (default: true) A {Boolean} for whether or not to use bevry/ambi to determine if the method is asynchronous or synchronous and execute it appropriately
# :domain - (default: true) A {Boolean} for whether or not to wrap the task execution in a domain to attempt to catch background errors (aka errors that are occuring in other ticks than the initial execution)
setConfig: (opts={}) ->
# Handle arguments
if Array.isArray(opts)
# Prepare configuration
args = opts
opts = {}
# Extract the configuration from the arguments
for arg in args
continue unless arg
switch typeof arg
when 'string'
opts.name = arg
when 'function'
opts.method = arg
when 'object'
for own key,value of arg
opts[key] = value
# Apply the configuration directly to our instance
for own key,value of opts
switch key
when 'on'
for own _key,_value of value
@on(_key, _value) if value
when 'once'
for own _key,_value of value
@once(_key, _value) if value
when 'whenDone'
@whenDone(value) if value
when 'onceDone', 'done', 'next'
@done(value) if value
else
@config[key] = value
# Chain
@
# Public: Have we started execution yet?
#
# Returns a {Boolean} which is `true` if we have commenced execution
hasStarted: ->
return @status isnt null
# Public: Have we finished its execution yet?
#
# Returns a {Boolean} which is `true` if we have finished execution
hasExited: ->
return @status in ['completed', 'destroyed']
# Public: Have we been destroyed?
#
# Returns a {Boolean} which is `true` if we have bene destroyed
isDestroyed: ->
return @status is 'destroyed'
# Public: Have we completed its execution yet?
#
# Returns a {Boolean} which is `true` if we have completed
isComplete: ->
return @status in ['failed', 'passed', 'destroyed']
# Internal: Handles the completion and error conditions for ourself.
#
# Should only ever execute once, if it executes more than once, then we error.
#
# args - The arguments {Array} that will be applied to the {::result} variable. First argument is the {Error} if it exists.
exit: (args...) ->
# Store the first error
@err ?= args[0] if args[0]?
# Complete for the first (and hopefully only) time
if @isComplete() is false
# Apply the result if it exists
if args.length isnt 0
@result = args
# Did we error?
@status = (if @err? then 'failed' else 'passed')
# Notify our listeners of our status
@emit(@status, @err)
# Finish up
@complete()
# Error as we have already completed before
else if @config.onError isnt 'ignore'
err = new Error """
The task [#{@getNames()}] just completed, but it had already completed earlier, this is unexpected. State information is:
#{util.inspect({error:@err, previousResult:@result, currentArguments:args})}
"""
@emit('error', err)
# Chain
@
# Internal: Completetion Emitter. Used to emit the `completed` event and to cleanup our state.
complete: ->
complete = @isComplete()
if complete
# Notify our listeners we have completed
@emit 'completed', (@result or [])...
# Prevent the error from persisting
@err = null
# Should we reset results?
# @results = []
# no, it would break the promise nature of done
# as it would mean that if multiple done listener are added, they would each get different results
# if they wish to reset the results, they should do so manually via resetResults
# Should we reset the status?
# @status = null
# no, it would break the promise nature of done
# as it would mean that once a done is fired, no more can be fired, until run is called again
return complete
# Public: When Done Promise.
# Fires the listener, either on the next tick if we are already done, or if not, each time the `done` event fires.
#
# listener - The {Function} to attach or execute.
whenDone: (listener) ->
if @isComplete()
queue => # avoid zalgo
listener.apply(@, @result or [])
else
super(listener)
@
# Public: Once Done Promise.
# Fires the listener once, either on the next tick if we are already done, or if not, once the `done` event fires.
#
# listener - The {Function} to attach or execute.
onceDone: (listener) ->
if @isComplete()
queue => # avoid zalgo
listener.apply(@, @result or [])
else
super(listener)
@
# Internal: Reset the results.
#
# At this point this method is internal, as it's functionality may change in the future, and it's outside use is not yet confirmed. If you need such an ability, let us know via the issue tracker.
resetResults: ->
@result = []
@
# Public: Destroy the task and prevent it from executing ever again.
destroy: ->
@done =>
# Are we already destroyed?
return if @status is 'destroyed'
# Update our status and notify our listeners
@emit(@status = 'destroyed')
# Clear results
@resetResults()
# item arrays should already be wiped due to done completion
# Remove all isteners
# @TODO should we exit or dispose of the domain?
@removeAllListeners()
# Chain
@
# Internal: Fire the task method with our config arguments and wrapped in a domain.
fire: ->
# Prepare
me = @
args = (@config.args or []).slice()
# Check that we have a method to fire
if me.config.method?.bind? is false
err = new Error """
The task [#{me.getNames()}] failed to run as no method was defined for it.
"""
me.emit('error', err)
return @
# Prepare the task domain if it doesn't already exist
if @config.domain isnt false and @taskDomain? is false and domain?.create?
# Setup the domain
@taskDomain = domain.create()
@taskDomain.on('error', @exit.bind(@))
# Make sure if the method supports a callback, we prevent the domain from entering our task
# RE: https://github.com/bevry/taskgroup/issues/17
complete = (args...) ->
me.taskDomain?.exit()
return me.exit(args...)
else
complete = @exit.bind(@)
# Our fire function that will be wrapped in a domain or executed directly
fireMethod = ->
# Bind the method to ourself
methodToFire = me.config.method.bind(me)
# Execute with ambi if appropriate
if me.config.ambi isnt false
ambi(methodToFire, args...)
# Otherwise execute directly if appropriate
else
methodToFire(args...)
# Setup timeout if appropriate
if me.config.timeout
me.timeout = wait me.config.timeout, ->
if me.isComplete() is false
err = new Error """
The task [#{me.getNames()}] has timed out.
"""
me.exit(err)
# Notify that we are now running
me.emit(me.status = 'running')
# Check for crippled domain and use try...catch instead
if process.browser or process.versions.node.substr(0,3) is '0.8'
# Use the next tick workaround to escape the try...catch scope
# Which would otherwise catch errors inside our code when it shouldn't therefore suppressing errors
completeOriginal = complete
complete = (args...) ->
process.nextTick -> completeOriginal(args...)
return me
# Add the competion callback to the arguments our method will receive
args.push(complete)
# Wrap in a try...catch to at least provide some protection
try
# Fire the method within the domain if desired, otherwise execute directly
if @taskDomain? then @taskDomain.run(fireMethod) else fireMethod()
catch err
me.exit(err)
else
# Add the competion callback to the arguments our method will receive
args.push(complete)
# Fire the method within the domain if desired, otherwise execute directly
if @taskDomain? then @taskDomain.run(fireMethod) else fireMethod()
# Chain
@
# Public: Start the execution of the task.
#
# Will emit an `error` event if the task has already started before.
run: ->
queue =>
# Already completed or even destroyed?
if @hasStarted()
err = new Error """
The task [#{@getNames()}] was just about to start, but it already started earlier, this is unexpected.
"""
@emit('error', err)
# Not yet completed, so lets run!
else
# Apply our new status and notify our listeners
@emit(@status = 'started')
# Fire the task
@fire()
# Chain
@
# =====================================
# Task Group
# Public: Our TaskGroup class.
#
# Available configuration is documented in {::setConfig}.
#
# Available events:
# - `started()` - emitted when we start execution
# - `running()` - emitted when the first item starts execution
# - `failed(err)` - emitted when execution exited with a failure
# - `passed()` - emitted when execution exited with a success
# - `completed(err, results)` - emitted when execution exited, `results` is an {Array} of the result arguments for each item that executed
# - `error(err)` - emtited if an unexpected error occured within ourself
# - `done(err, results)` - emitted when either the execution completes (the `completed` event) or when an unexpected error occurs (the `error` event)
# - `item.*(...)` - bubbled events from an added item
# - `task.*(...)` - bubbled events from an added {Task}
# - `group.*(...)` - bubbled events from an added {TaskGroup}
#
# Available internal statuses:
# - `null` - execution has not yet started
# - `'started'` - execution has begun
# - `'running'` - execution of items has begun
# - `'failed'` - execution has exited with failure status
# - `'passed'` - execution has exited with success status
# - `'destroyed'` - we've been destroyed and can no longer execute
class TaskGroup extends Interface
# Internal: The type of our class for the purpose of duck typing
# which is needed when working with node virtual machines
# as instanceof will not work in those environments.
type: 'taskgroup'
# Public: A helper method to check if the passed argument is an instanceof a {TaskGroup}.
#
# item - The possible instance of the {TaskGroup} that we want to check
#
# Returns a {Boolean} of whether or not the item is a {TaskGroup} instance.
@isTaskGroup: (group) -> return group?.type is 'taskgroup' or group instanceof TaskGroup
# Public: A helper method to create a new subclass with our extensions.
#
# extensions - An {Object} of extensions to apply to the new subclass
#
# Returns the new sub {Class}
@subclass: csextends
# Public: Creates a new {TaskGroup} instance.
#
# args - The Arguments to be forwarded along to the {::constructor}.
#
# Returns the new {TaskGroup} instance.
@create: (args...) -> return new @(args...)
# Internal: An {Array} of the items that are still yet to execute
itemsRemaining: null
# Internal: An {Array} of the items that are currently running
itemsRunning: null
# Internal: An {Array} of the items that have completed
itemsCompleted: null
# Internal: An {Array} of the result Arguments for each completed item when their :includeInResults configuration option is not `false`
results: null
# Internal: An {Error} object if execution has failed at some point
err: null
# Internal: A {String} containing our current status. See our {Task} description for available values.
status: null
# Internal: An {Array} of the events that we may emit. Events that will be executed can be found in the {Task} description.
events: null
# Internal: The configuration for our {Task} instance. See {::setConfig} for available configuration.
config: null
# Public: Initialize our new {TaskGroup} instance. Forwards Arguments onto {::setConfig}.
constructor: (args...) ->
# Init
me = @
super
@config ?= {}
@config.concurrency ?= 1
@config.onError ?= 'exit'
@itemsRemaining ?= []
@itemsRunning ?= []
@itemsCompleted ?= []
@results ?= []
@events ?= []
@events.push('error', 'started', 'running', 'passed', 'failed', 'completed', 'done', 'destroyed')
# Apply configuration
@setConfig(args)
# Give setConfig enough chance to fire
# Changing this to setImmediate breaks a lot of things
# As tasks inside nested taskgroups will fire in any order
queue(@autoRun.bind(@))
# Chain
@
# ---------------------------------
# Configuration
# Public: Set Nested Task Config
setNestedTaskConfig: (config={}) ->
@config.nestedTaskConfig ?= {}
for own key,value of config
@config.nestedTaskConfig[key] = value
@
# Public: Set Nested Config
setNestedConfig: (config={}) ->
@setConfig(config)
@config.nestedConfig ?= {}
for own key,value of config
@config.nestedConfig[key] = value
@
# Public: Set the configuration for our instance.
#
# Despite accepting an {Object} of configuration, we can also accept an {Array} of configuration.
# When using an array, a {String} becomes the :name, a {Function} becomes the :method, and an {Object} becomes the :config
#
# config - Our configuration {Object} can contain the following fields:
# :name - (default: null) A {String} for what we would like our name to be, useful for debugging.
# :done - (default: null) A {Function} that we would like passed to {::onceDone} (aliases are :onceDone, and :next)
# :whenDone - (default: null) A {Function} that we would like passed to {::whenDone}
# :on - (default: null) An {Object} of (eventName => listener) that we would like bound via EventEmitter.on.
# :once - (default: null) An {Object} of (eventName => listener) that we would like bound via EventEmitter.once. # :method - (default: null) A {Function} that we would like to use to created nested groups and tasks using an inline style.
# :parent - (default: null) A parent {TaskGroup} that we may be attached to.
# :onError - (default: 'exit') A {String} that is either `'exit'` or `'ignore'`, when `'ignore'` errors that occur within items will not halt execution and will not be reported in the completion callbacks `err` argument (but will still be in the `results` argument).
# :concurrency - (default: 1) The {Number} of items that we would like to execute at the same time. Use `0` for unlimited. `1` accomplishes serial execution, everything else accomplishes parallel execution.
# :run - (default: true) A {Boolean} for whether or not to the :method (if specified) automatically.
# :nestedConfig - (default: null) An {Object} of nested configuration to be applied to all items of this group.
# :nestedTaskConfig - (default: null) An {Object} of nested configuration to be applied to all {Task}s of this group.
# :tasks - (default: null) An {Array} of tasks to be added as children.
# :groups - (default: null) An {Array} of groups to be added as children.
# :items - (default: null) An {Array} of {Task} and/or {TaskGroup} instances to be added to this group.
setConfig: (opts={}) ->
# Handle arguments
if Array.isArray(opts)
# Prepare configuration
args = opts
opts = {}
# Extract the configuration from the arguments
for arg in args
continue unless arg
switch typeof arg
when 'string'
opts.name = arg
when 'function'
opts.method = arg
when 'object'
for own key,value of arg
opts[key] = value
# Apply the configuration directly to our instance
for own key,value of opts
switch key
when 'on'
for own _key,_value of value
@on(_key, _value) if value
when 'once'
for own _key,_value of value
@once(_key, _value) if value
when 'whenDone'
@whenDone(value) if value
when 'onceDone', 'done', 'next'
@done(value) if value
when 'task', 'tasks'
@addTasks(value) if value
when 'group', 'groups'
@addGroups(value) if value
when 'item', 'items'
@addItems(value) if value
else
@config[key] = value
# Chain
@
# ---------------------------------
# TaskGroup Method
# Internal: Prepare the method and it's configuration, and add it as a task to be executed.
#
# method - The {Function} of our method
# config - An optional {Object} of configuration for the task to be created for our method
addMethod: (method, config={}) ->
method ?= @config.method.bind(@)
method.isTaskGroupMethod = true
config.name ?= 'taskgroup method for '+@getName()
config.args ?= [@addGroup.bind(@), @addTask.bind(@)]
config.includeInResults ?= false
return @addTask(method, config)
# Internal: Autorun ourself under certain conditions.
#
# Those conditions being:
# - if we the :method configuration is defined, and we have no :parent
# - if we the :run configuration is `true`
#
# Used primarily to cause the :method to fire at the appropriate time when using inline style.
autoRun: ->
# Auto run if we are going the inline style and have no parent
if @config.method
# Add the function as our first unamed task with the extra arguments
item = @addMethod()
# If we are the topmost group
if @config.parent? is false
# then run if possible
@config.run ?= true
# Auto run if we are configured to
if @config.run is true
@run()
# Chain
@
# ---------------------------------
# Add Item
# Internal: Add an item to ourself and configure it accordingly
#
# item - A {Task} or {TaskGroup} instance that we would like added to ourself
# args - Additional configuration Arguments to apply to each item
addItem: (item, args...) ->
# Prepare
me = @
# Only add the item if it exists
return null unless item
# Link our item to ourself
item.setConfig({parent: @})
item.setConfig(args...) if args.length isnt 0
item.config.name ?= "#{item.type} #{@getItemsTotal()+1} for #{@getName()}"
# Bubble task events
if Task.isTask(item)
# Nested configuration
item.setConfig(@config.nestedConfig) if @config.nestedConfig?
item.setConfig(@config.nestedTaskConfig) if @config.nestedTaskConfig?
item.events.forEach (event) ->
item.on event, (args...) ->
me.emit("task.#{event}", item, args...)
# Notify our intention
@emit('task.add', item)
# Bubble group events
else if TaskGroup.isTaskGroup(item)
# Nested configuration
item.setNestedConfig(@config.nestedConfig) if @config.nestedConfig?
item.setConfig(nestedTaskConfig: @config.nestedTaskConfig) if @config.nestedTaskConfig?
# Bubble item events
item.events.forEach (event) ->
item.on event, (args...) ->
me.emit("group.#{event}", item, args...)
# Notify our intention
@emit('group.add', item)
# Bubble item events
item.events.forEach (event) ->
item.on event, (args...) ->
me.emit("item.#{event}", item, args...)
###
# Bubble item error event directly
item.on 'error', (args...) ->
me.emit('error', args...)
###
# Notify our intention
@emit('item.add', item)
# Handle item completion and errors once
# we can't just do item.done, or item.once('done'), because we need the item to be the argument, rather than `this`
item.done (args...) ->
me.itemCompletionCallback(item, args...)
# Add the item
@itemsRemaining.push(item)
# We may be running and expecting items, if so, fire
@fire()
# Return the item
return item
# Internal: Add items to ourself and configure them accordingly
#
# items - An {Array} of {Task} and/or {TaskGroup} instances to add to ourself
# args - Optional Arguments to configure each added item
addItems: (items, args...) ->
items = [items] unless Array.isArray(items)
return (@addItem(item, args...) for item in items)
# ---------------------------------
# Add Task
# Public: Create a {Task} instance from some configuration.
#
# If the first argument is already a {Task} instance, then just update it's configuration with the remaning arguments.
#
# args - Arguments to use to configure the {Task} instance
#
# Returns the new {Task} instance
createTask: (args...) ->
# Support receiving an existing task instance
if Task.isTask(args[0])
task = args[0]
task.setConfig(args.slice(1)...)
# Support receiving arguments to create a task instance
else
task = new Task(args...)
# Return the new task
return task
# Public: Add a {Task} with some configuration to ourself, create it if needed.
#
# args - Arguments to configure (and if needed, create) the task
addTask: (args...) ->
task = @addItem @createTask args...
# Chain
@
# Public: Add {Task}s with some configuration to ourself, create it if needed.
#
# items - An {Array} of {Task} items to add to ourself
# args - Optional Arguments to configure each added {Task}
addTasks: (items, args...) ->
items = [items] unless Array.isArray(items)
tasks = (@addTask(item, args...) for item in items)
# Chain
@
# ---------------------------------
# Add Group
# Public: Create a {TaskGroup} instance from some configuration.
#
# If the first argument is already a {TaskGroup} instance, then just update it's configuration with the remaning arguments.
#
# args - Arguments to use to configure the {TaskGroup} instance
#
# Returns the new {TaskGroup} instance
createGroup: (args...) ->
# Support recieving an existing taskgroup instance
if TaskGroup.isTaskGroup(args[0])
taskgroup = args[0]
taskgroup.setConfig(args.slice(1)...)
# Support receiving arugments to create a taskgroup intance
else
taskgroup = new TaskGroup(args...)
# Return the taskgroup instance
return taskgroup
# Public: Add a {TaskGroup} with some configuration to ourself, create it if needed.
#
# args - Arguments to configure (and if needed, create) the {TaskGroup}
addGroup: (args...) ->
group = @addItem @createGroup args...
# Chain
@
# Public: Add {TaskGroup}s with some configuration to ourself, create it if needed.
#
# items - An {Array} of {TaskGroup} items to add to ourself
# args - Optional Arguments to configure each added {TaskGroup}
addGroups: (items, args...) ->
items = [items] unless Array.isArray(items)
groups = (@addGroup(item, args...) for item in items)
# Chain
@
# ---------------------------------
# Status Indicators
# Public: Gets the total number of items
#
# Returns a {Number} of the total items we have
getItemsTotal: ->
running = @itemsRunning.length
remaining = @itemsRemaining.length
completed = @itemsCompleted.length
total = running + remaining + completed
return total
# Public: Gets the names of the items, the total number of items, and their results for the purpose of debugging.
#
# Returns an {Object} containg the hashes:
# :remaining - An {Array} of the names of the remaining items
# :running - An {Array} of the names of the running items
# :completed - An {Array} of the names of the completed items
# :total - A {Number} of the total items we have
# :results - An {Array} of the results of the compelted items
getItemNames: ->
running = @itemsRunning.map (item) -> item.getName()
remaining = @itemsRemaining.map (item) -> item.getName()
completed = @itemsCompleted.map (item) -> item.getName()
results = @results
total = running.length + remaining.length + completed.length
return {
remaining
running
completed
total
results
}
# Public: Gets the total number count of each of our item lists.
#
# Returns an {Object} containg the hashes:
# :remaining - A {Number} of the total remaining items
# :running - A {Number} of the total running items
# :completed - A {Number} of the total completed items
# :total - A {Number} of the total items we have
# :results - A {Number} of the total results we have
getItemTotals: ->
running = @itemsRunning.length
remaining = @itemsRemaining.length
completed = @itemsCompleted.length
results = @results.length
total = running + remaining + completed
return {
remaining
running
completed
total
results
}
# Public: Whether or not we have any running items
#
# Returns a {Boolean} which is `true` if we have any items that are currently running
hasRunning: ->
return @itemsRunning.length isnt 0
# Public: Whether or not we have any items that are yet to execute
#
# Returns a {Boolean} which is `true` if we have any items that are still yet to be executed
hasRemaining: ->
return @itemsRemaining.length isnt 0
# Public: Whether or not we have any items
#
# Returns a {Boolean} which is `true` if we have any running or remaining items
hasItems: ->
return @hasRunning() or @hasRemaining()
# Public: Have we started execution yet?
#
# Returns a {Boolean} which is `true` if we have commenced execution
hasStarted: ->
return @status isnt null
# Public
hasResult: ->
return @err? or @results.length isnt 0
# Public: Have we finished its execution yet?
#
# Returns a {Boolean} which is `true` if we have finished execution
hasExited: ->
return @status in ['completed', 'destroyed']
# Internal: Whether or not we have any available slots to execute more items.
#
# Returns a {Boolean} which is `true` if we have available slots.
hasSlots: ->
return (
@config.concurrency is 0 or
@itemsRunning.length < @config.concurrency
)
# Internal: Whether or not we have errord and want to pause when we have an error.
#
# Returns a {Boolean} which is `true` if we are paused.
shouldPause: ->
return (
@config.onError is 'exit' and
@err?
)
# Internal: Whether or not we are capable of firing more items.
#
# This is determined whether or not we are not paused, and we have remaning items, and we have slots able to execute those remaning items.
#
# Returns a {Boolean} which is `true` if we can fire more items.
shouldFire: ->
return (
@shouldPause() is false and
@hasRemaining() and
@hasSlots()
)
# Public: Whether or not we have no items left
#
# Returns a {Boolean} which is `true` if we have no more running or remaining items
isEmpty: ->
return @hasItems() is false
# Public
isPaused: ->
return (
@shouldPause() and
@hasRunning() is false
)
# Public: Have we completed its execution yet?
#
# Completion of executed is determined of whether or not we have started, and whether or not we are currently paused or have no remaining and running items left
#
# Returns a {Boolean} which is `true` if we have completed execution
isComplete: ->
return (
@hasStarted() and
(
@isPaused() or
@isEmpty()
)
)
# ---------------------------------
# Firers
# Internal: Completetion Emitter. Used to emit the `completed` event and to cleanup our state.
complete: ->
complete = @isComplete()
if complete
# Notity our listners we have completed
@emit('completed', @err, @results)
# Prevent the error from persisting
@err = null
# Cleanup the items that will now go unused
for item in @itemsCompleted
item.destroy()
@itemsCompleted = []
# Should we reset results?
# @results = []
# no, it would break the promise nature of done
# as it would mean that if multiple done handlers are added, they would each get different results
# if they wish to reset the results, they should do so manually via resetResults
# Should we reset the status?
# @status = null
# no, it would break the promise nature of done
# as it would mean that once a done is fired, no more can be fired, until run is called again
return complete
# Public: When Done Promise.
# Fires the listener, either on the next tick if we are already done, or if not, each time the `done` event fires.
#
# listener - The {Function} to attach or execute.
whenDone: (handler) ->
if @isComplete()
queue => # avoid zalgo
handler.call(@, @err, @results)
else
super(handler)
@
# Public: Once Done Promise.
# Fires the listener once, either on the next tick if we are already done, or if not, once the `done` event fires.
#
# listener - The {Function} to attach or execute.
onceDone: (handler) ->
if @isComplete()
queue => # avoid zalgo
handler.call(@, @err, @results)
else
super(handler)
@
# Internal: Reset the results.
#
# At this point this method is internal, as it's functionality may change in the future, and it's outside use is not yet confirmed. If you need such an ability, let us know via the issue tracker.
resetResults: ->
@results = []
@
# Internal: Fire the next items.
#
# Returns either an {Array} items that was fired, or `false` if no items were fired.
fireNextItems: ->
items = []
# Fire the next items
while true
item = @fireNextItem()
if item
items.push(item)
else
break
result =
if items.length isnt 0
items
else
false
return result
# Internal: Fire the next item.
#
# Returns either the item that was fired, or `false` if no item was fired.
fireNextItem: ->
# Prepare
result = false
fire = @shouldFire()
# Can we run the next item?
if fire
# Fire the next item
# Update our status and notify our listeners
@emit(@status = 'running') if @status isnt 'running'
# Get the next item and remove it from the remaining items
item = @itemsRemaining.shift()
@itemsRunning.push(item)
# Run it
item.run()
# Return the item
result = item
# Return
return result
# Internal: What to do when an item completes
itemCompletionCallback: (item, args...) ->
# Update error if it exists
@err ?= args[0] if @config.onError is 'exit' and args[0]
# Mark that one less item is running
index = @itemsRunning.indexOf(item)
if index is -1
# this should never happen, but maybe it could, in which case we definitely want to know about it
@err ?= indexError = new Error("Could not find [#{item.getNames()}] in the running queue")
console.error(indexError.stack or indexError)
else
@itemsRunning = @itemsRunning.slice(0, index).concat(@itemsRunning.slice(index+1))
# Add to the completed queue
@itemsCompleted.push(item)
# Add the result
@results.push(args) if item.config.includeInResults isnt false
# Fire
@fire()
# Chain
@
# Internal: Either execute the reamining items we are not paused, or complete execution by exiting.
fire: ->
# Have we actually started?
if @hasStarted()
# Check if we are complete, if so, exit
if @isComplete()
@exit()
# Otherwise continue firing items if we are wanting to pause
else if @shouldPause() is false
@fireNextItems()
# Chain
@
# Public: Clear remaning items.
clear: ->
# Destroy all the items
for item in @itemsRemaining
item.destroy()
@itemsRemaining = []
# Chain
@
# Public: Destroy all remaining items and remove listeners.
destroy: ->
# Destroy all the items
@clear()
# Once finished, destroy it
@done =>
# Are we already destroyed?
return if @status is 'destroyed'
# Apply our new status and notify our listeners
@emit(@status = 'destroyed')
# Clear results
@resetResults()
# item arrays should already be wiped due to done completion
# Remove listeners
@removeAllListeners()
# Chain
@
# Internal: We now want to exit.
exit: (err) ->
# Update error if set
@err ?= err if err?
# Update the status
@status = (if @err? then 'failed' else 'passed')
# Notify our listeners
@emit(@status, @err)
# Fire the completion callback
@complete()
# Chain
@
# Public: Start the execution.
run: (args...) ->
queue =>
# Apply our new status and notify our intention to run
@emit(@status = 'started')
# Give time for the listeners to complete before continuing
@fire()
# Chain
@
# Export
module.exports = {Task,TaskGroup}
It would be cool if markdown docs were supported. So for example if I'm working on a module with just a index.coffee
with one class in it, then I could generate the doc for that class and link to it from my readme. Or even better, I could actually append it to my readme with gulp-biscotto (& other Gulp plugins).
When including private and internal methods in the rendered docs, there's no visual distinguishment between them. I'd like to have just one set of rendered docs for all my methods, which both core-devs and end-developers can use.
I have a proposal for the structure of a metadata format that I think would be ideal for Biscotto to generate. This format would give us coherent and complete information that would give us a lot of power to generate rich documentation and iterate on our organization system. Please help me riff on this. Hopefully together we can arrive at a really powerful and general metadata system that will fit all our potential needs.
The goal of Biscotto is to produce a precise description of the contents of an npm module. Its output is a JSON "documentation slug" that metadata can be stored in a file called metadata.json
in the root of the module and distributed via npm.
Within a node module, objects are essentially namespaced via the file system due to the use of require
. Node module's depend on each other via the namespace defined by the npm repository and version numbers. Biscotto aims to mirror this structure as closely as possible.
An documentation slug for an npm contains a main
key indicating which file represents the top-level file in the module, along with a files
key pointing to an object containing a file descriptor for every file in the module, keyed by the relative path of each file.
main: 'src/text-buffer.coffee'
files:
'src/buffer-patch.coffee': {} # file descriptor
'src/helpers.coffee': {} # file descriptor
# etc ...
Each file descriptor contains information about all objects defined in the file, along with information about the file's exports.
main: 'src/text-buffer.coffee'
files:
'src/buffer-patch.coffee':
path: 'src/text-buffer.coffee'
objects: {} # object descriptors
exports: {} # export descriptors
The objects
key points to a hash of descriptors every object in the file. By object, we mean "code artifact". An object can be a class, an array, a hash, a function, etc. The objects
hash is organized by the line number on which the object begins. Object descripors contains a startLineNumber
, endLineNumber
, name
, type
. It can also contain other keys containing type-specific metadata.
main: 'src/text-buffer.coffee'
files:
'src/buffer-patch.coffee':
path: 'src/buffer-patch.coffee'
objects:
69:
name: 'TextBuffer'
type: 'class'
startLineNumber: 69
endLineNumber: 1076
doc: "Public: A mutable text container with undo/redo support and..."
classProperties: [70, 71, ...] # line number references
prototypeProperties: [79, 80, ...] # line number references
In the example above, the TextBuffer
class is documented. In addition to the name, type, doc string, and the line number range spanned by the class, it contains additional keys containing lists of references to other objects in the file by their line number.
You may wonder why we reference the properties of the class by line number instead of just including them directly. We are keeping the structure of the file descriptor deliberately shallow to keep references within and between files as simple and language-agnostic as possible. Rather than complex, syntax-dependent paths, we only need a path and a line number to describe a reference. Since all Node.js code entities ultimately live at particular locations in files on the file system, path and line number makes a reasonable common denominator for organizing the file.
In this case, properties of the class can use a parent
key to indicate their logical membership in the class:
# ...
files:
'src/buffer-patch.coffee':
# ...
objects:
69:
name: 'TextBuffer'
type: 'class'
# ...
# ...
131:
name: 'getText'
bindingType: 'prototypeProperty'
startLineNumber: 131
endLineNumber: 139
type: 'function'
parent: 69
paramNames: []
In this case, the getText
method is described as a code object at line 131 that is a function bound to a prototype property inside the code object starting at line 69. Any code interpreting this metadata can follow the references to draw its own conclusions about the structural relationship between these objects. We are free to use fields in the descriptor to provide a rich description of the object. For example, if the function were bound to a local variable, it could have the bindingType
of variable
and still slot cleanly into this organization.
Code entities can also contain references to other files and modules, but before we cover that, let's discuss exports.
In the Node module system, files can export one or more objects. This is expressed by the exports
field. Take the following example file.
# Returns hello
foo = -> 'hello'
exports.foo = foo
# Exporting the answer to life, the universe, and everything.
exports.bar = 42
The documentation would contain descriptors for all the objects in the file, and also an exports
key describing what the file exports.
objects:
1:
name: 'foo'
bindingType: 'variable'
type: 'function'
doc: 'Returns hello'
# ....
6:
name: 'bar'
bindingType: 'exportsProperty'
type: 'number'
doc: 'Exporting the answer to life, the universe, and everything.'
# ...
exports:
foo: 1
bar: 6
In the above example, the exports section contains a hash of exported property names mapping to the line numbers of the actual objects being exported.
There's also a common pattern of exporting a single object from a file by assigning module.exports
:
module.exports =
class Foo
The above file would be described as follows.
objects:
1:
name: 'Foo'
bindingType: 'exports'
type: 'class'
# ....
exports: 1
In this case, instead of a hash of exported property names, we just include a reference directly to the object that's being exported.
In certain cases, we may need to make a reference to an entity defined elsewhere. For example, what if the getText
method were imported from another file, such as a helpers module?
{getText} = require './helpers'
module.exports =
class TextBuffer
getText: getText
In this case, we need to make a reference to the code entity in which getText
is actually defined. The reference supplies a path for a file in the current module and the optional name of an exported symbol.
# ...
files:
'src/buffer-patch.coffee':
# ...
objects:
3:
name: 'TextBuffer'
type: 'class'
# ...
# ...
4:
name: 'getText'
bindingType: 'prototypeProperty'
startLineNumber: 4
endLineNumber: 5
reference:
path: 'src/helpers.coffee'
exportsProperty: 'getText'
'src/helpers.coffee':
# ...
objects:
# ...
56:
name: 'getText'
bindingType: 'exportsProperty'
startLineNumber: 56
endLineNumber: 62
type: 'function'
exports:
getText: 56
If we want to reference a file in a completely different node module we can do that as well. We can also supply an optional top-level exports property or, more rarely, a path to a file within the module.
# Assumes we're references the entire module.exports of the referenced module
reference:
module: '[email protected]'
# Or we can reference a specific exported property of the referenced module
reference:
module: '[email protected]'
exportsProperty: 'getText'
# In rare cases, we can reference a file other than the top-level module
reference:
module: '[email protected]'
path: 'src/helpers.coffee'
exportsProperty: 'getText'
By harvesting metadata from node modules in a manner that mirrors their actual lexical structure, we set ourselves up to build tools that can describe the systems we're building out of these modules really well.
We easily list documentation for all objects in a module, as well as modules it references. We can also automatically link references between modules. We can easily bundle the documentation with the actual source code for modules, and automatically upgrade documentation as our dependencies change.
What's better, we can evolve the infrastructure for harvesting data separately from the code for presenting the documentation.
If I have:
# Public: The exported CSON singleton
class CSON
# Public: Converts an {Object} into a {String} of the desired format
#
# If the format option is not specified, we default to CSON
#
# data - {Object} The data to convert
# opts - {Object} The options (options may also be forwarded onto the parser library)
# :format - {String} The format to use: "cson" (default), "json", "coffeescript", or "javascript"
# :cson - {Boolean} Whether or not the CSON format should be allowed (defaults to `true`)
# :json - {Boolean} Whether or not the JSON format should be allowed (defaults to `true`)
#
# Returns {String} or {Error}
createString: (data, opts) ->
return @action('createString', 'create', 'String', data, opts)
It's being rendered like:
Which is not what we expect or want.
Using the command:
/Users/balupton/Projects/cson/node_modules/biscotto/bin/biscotto -n CSON --title "CSON API Documentation" -r README.md -o docs src - LICENSE.md HISTORY.md
Tried with Biscotto versions 2.3.1, 2.3.0, and 2.2.4 (failed on 2.1) - all produce the same result
Source file is: https://github.com/bevry/cson/blob/6991fc9a7577341ea05ca6260a448d4aa3ea7d23/src/lib/cson.coffee
Rendered doc file is:
https://github.com/bevry/cson/blob/6991fc9a7577341ea05ca6260a448d4aa3ea7d23/docs/classes/CSON.html
The smallest reproducible case:
module.exports =
class Stuff
# YO!
bar: ->
console.log 'yo yo'
### Internal ###
@doSomething: ->
Yields Cannot parse file -> Invalid object key
.
I think #61 (i.e. v2.2.1) might have a problem. When bumping up the biscotto version gulp-biscotto depends on, the tests fail. It seems like biscotto.run done, each
doesn't call each
with class/Module.html
(for a given class called Module
) anymore.
If it helps, the problem can be seen on my biscotto-2.2.1
branch; the first test fails here where it does a dumb check against the files emitted by biscotto. Here's the test failing on TravisCI.
Hi,
let's say I have a Base
class with some methods and lots of classes extending from the Base
class, without methods declaration (since they are inherited). The Base
class should be private, thus only the subclasses should be visible in the documentation with all the inherited methods.
Is that even possible?
# Private
class Base
foo: -> 'bar'
bar: -> 'foo'
# Public
class Foo extends Base
# Public
class Bar extends Base
I would like to Foo
and Bar
to be visible in the documentation with foo
and bar
methods, but not Base
.
Thanks :)
I can't seem to get extra files included.
Right now I've my documentation generated into /docs/code
with an extra file in there called display.html
.
This doesn't work
./app
-
./docs/code/display.html
I've tried without the ./
, the full path, relative to the docs root, etc.
I'm on Windows.
E.g. {EventEmitter:on}
produces: [WARN] Cannot find referenced class EventEmitter:on in class Task (EventEmitter:on)
Looking through Atom and Biscotto I couldn't find any examples of documenting emitted events and felt this was a weakness of Biscotto/TomDoc. Since Biscotto has other extensions I propose adding an Emits keyword:
# Emits loaded whenever something is loaded. The first argument will be an
# {Object} and the second argument will be a {Boolean}
When using prototype method specification using ::
, the methods aren't listed as part of the class, rather put under "prototype"
Example:
Foo::foo()
gets listed under the class, but Foo::bar()
is listed as "method" Foo.prototype.bar()
.
class Foo
foo: () -> 'foo'
Foo::bar = () -> 'bar'
Is this a design choice? I tend to chop up functionality into separate files from time to time, and it would be nice to still generate a single class documentation.
It seems like --listMissing
still causes docs to be generated.
Is there a way to just list the missing docs without generating any actual doc output?
When using biscotto on sources that use functions and not objects or classes, generation fails. This is my test case:
# Public: Generating those docs will fail.
func = (val) ->
module.exports =
func: func
And the error it generates:
undefined:46
'" + ($e($c(this.parent.repo))) + "/" + ($e($c(this.parent.classData.file))) +
^
TypeError: Cannot read property 'file' of undefined
at Object.eval (<anonymous>:46:112)
at Array.partials/method_list (/home/crito/.nvm/v0.10.29/lib/node_modules/biscotto/node_modules/haml-coffee/src/hamlc.coffee:53:26)
at Object.eval (<anonymous>:65:57)
at Array.file (/home/crito/.nvm/v0.10.29/lib/node_modules/biscotto/node_modules/haml-coffee/src/hamlc.coffee:53:26)
at Templater.module.exports.Templater.render (/home/crito/.nvm/v0.10.29/lib/node_modules/biscotto/src/util/templater.coffee:59:12)
at Generator.module.exports.Generator.generateFiles (/home/crito/.nvm/v0.10.29/lib/node_modules/biscotto/src/generator.coffee:199:18)
at Generator.module.exports.Generator.generate (/home/crito/.nvm/v0.10.29/lib/node_modules/biscotto/src/generator.coffee:40:6)
at /home/crito/.nvm/v0.10.29/lib/node_modules/biscotto/src/biscotto.coffee:237:21
at /home/crito/.nvm/v0.10.29/lib/node_modules/biscotto/node_modules/async/lib/async.js:592:17
at done (/home/crito/.nvm/v0.10.29/lib/node_modules/biscotto/node_modules/async/lib/async.js:135:19)
at /home/crito/.nvm/v0.10.29/lib/node_modules/biscotto/node_modules/async/lib/async.js:32:16
at /home/crito/.nvm/v0.10.29/lib/node_modules/biscotto/node_modules/async/lib/async.js:589:21
at /home/crito/.nvm/v0.10.29/lib/node_modules/biscotto/src/biscotto.coffee:317:7
at ChildProcess.exithandler (child_process.js:651:7)
at ChildProcess.emit (events.js:98:17)
at maybeClose (child_process.js:755:16)
at Socket.<anonymous> (child_process.js:968:11)
at Socket.emit (events.js:95:17)
at Pipe.close (net.js:465:12)
If I wrap this function in an object, it works fine. So I wonder if documenting functions will ever be supported or if this is by design.
I'm testing doc generation on Windows. This my file:
require('coffee-script/register');
var biscotto = require('./src/biscotto');
biscotto.run();
I get the following error:
undefined:25
$o.push(" " + $c(this.JST['partials/head'](this)));
^
TypeError: Object has no method 'partials/head'
at Object.eval (<anonymous>:25:44)
at Array.frames (C:\wamp\www\GitHub\biscotto\node_modules\haml-coffee\src\hamlc.coffee:53:26)
at Templater.module.exports.Templater.render (C:\wamp\www\GitHub\biscotto\src\util\templater.coffee:56:12)
at Generator.module.exports.Generator.generateIndex (C:\wamp\www\GitHub\biscotto\src\generator.coffee:68:16)
at Generator.module.exports.Generator.generate (C:\wamp\www\GitHub\biscotto\src\generator.coffee:36:6)
at C:\wamp\www\GitHub\biscotto\src\biscotto.coffee:237:21
at C:\wamp\www\GitHub\biscotto\node_modules\async\lib\async.js:533:17
at C:\wamp\www\GitHub\biscotto\node_modules\async\lib\async.js:119:25
at C:\wamp\www\GitHub\biscotto\node_modules\async\lib\async.js:24:16
at C:\wamp\www\GitHub\biscotto\node_modules\async\lib\async.js:530:21
at C:\wamp\www\GitHub\biscotto\src\biscotto.coffee:331:7
at ChildProcess.exithandler (child_process.js:635:7)
at ChildProcess.EventEmitter.emit (events.js:98:17)
at maybeClose (child_process.js:735:16)
at Socket.<anonymous> (child_process.js:948:11)
at Socket.EventEmitter.emit (events.js:95:17)
at Pipe.close (net.js:466:12)
Changing the references from partials/*
to ./partials/*
doesn't change the result.
It might be something to do with haml-coffee#90 but as defined in the package.json
, my coffee-script
version is 1.6.2
. 1.6.3
doesn't help either. I tried upping the haml-coffee
version from 0.6.0
to 1.14.1
(mentioned in that issue) but still no luck.
cc @nathansobo
It's quite annoying as everytime I regenerate my documentation, even if there is no content changes, the "generated on xxx" output causes changes to occur anyway, which is frustrating for the build process as it means that there is always new changes, even if they aren't actually real changes.
An option to turn the "generated on" output off would be great.
Link from repo's header description — https://atom.github.io/biscotto/ — doesn't seem to open.
Alphabetical order isn't always the most sane way of displaying the methods. Fine for alphabetical in the sidebar, but in the main listing, it should be in the order that they are coded.
So I think something didn't work quite right in the change for 0.0.13, private classes are now appearing on the class listing and if you click on them there files don't exist.
Is it possible to maintain the delegate functionality without listing the private classes in the sidebar?
So currently a method like this:
method: ({option1, option2})
gets rendering into this in the documentation:
method()
Is there a way to get the named arguments through? If not, is there a way to atleast substitute the word options
instead of having nothing?
The following:
# Public: Our Task Class.
#
# Available configuration is documented in {::setConfig}.
#
# Available events:
#
# - `started()` - emitted when we start execution
# - `running()` - emitted when the method starts execution
# - `failed(err)` - emitted when execution exited with a failure
# - `passed()` - emitted when execution exited with a success
# - `completed(err, args...)` - emitted when execution exited, `args` are the result arguments from the method
# - `error(err)` - emtited if an unexpected error occurs without ourself
# - `done(err, args...)` - emitted when either execution completes (the `completed` event) or when an unexpected error occurs (the `error` event)
#
# Available internal statuses:
#
# - `null` - execution has not yet started
# - `'started'` - execution has begun
# - `'running'` - execution of our method has begun
# - `'failed'` - execution of our method has failed
# - `'passed'` - execution of our method has succeeded
# - `'destroyed'` - we've been destroyed and can no longer execute
#
# Examples
#
# task = require('taskgroup').Task.create('my synchronous task', function(){
# return 5
# }).done(console.log) // [null, 5]
#
# task = require('taskgroup').Task.create('my asynchronous task', function(complete){
# complete(null, 5)
# }).done(console.log) // [null, 5]
#
# task = require('taskgroup').Task.create('my task that errors', function(){
# var err = new Error('deliberate error')
# return err; // if asynchronous, can also do: complete(err)
# // thrown and uncaught errors are also caught thanks to domains, but that should be avoided
# // as it would put your app in an unknown state
# }).done(console.log) // [Error('deliberator error')]
Gets outputted as:
https://www.dropbox.com/s/p1khmx4lqvkegww/Screenshot%202015-02-02%2008.04.44.png?dl=0
Lion::walk
in coffeescript is equivalent to Lion.prototype.walk
so it would be cool if {Lion::walk}
in docs linked to the instance method walk
.
Sorry for not creating multiple issues but I don't think each comment would warrant its own.
ctrl+t
(which prevents me from opening new tabs), how about using the same binding as Atom itself (ctrl+shift+p
, or cmd) for the fuzzy finder dialog?ctrl+h
, focusses the location ar for ctrl+l
and wants to save the file with ctrl+s
instead of focusing the search bar. I think you need to call some "prevent default" method on the event where you are parsing bindings - or use completely different bindings.This is on FireFox 28 beta.
Minimum repro:
# Public: Test!
class Test
TESTY_TEST: 1000
Results in:
/usr/local/lib/node_modules/biscotto/src/util/referencer.coffee:217
for (index = _j = 0, _len1 = options.length; _j < _len1; index = ++_
^
TypeError: Cannot read property 'length' of undefined
at Referencer.module.exports.Referencer.resolveDoc (/usr/local/lib/node_modules/biscotto/src/util/referencer.coffee:213:9)
... snip ...
May be a duplicate of #34? But without knowing what code triggered that issue, I can't be sure.
On the other hand, this code:
# Public: Test!
class Test
tESTY_TEST: 1000
parses without crashing.
There are two kind of bugs that happens on documentation when it references external models/projects.
Example 1)The "Model" link on the docs is broken on editor page
sendCommand
gives you two results, and both generates a 404 when trying to open.Perhaps I'm reporting this in error. My apologies if so. Does biscotto support CoffeeScript 1.7+?
I'm getting parse errors when using with CS 1.7 syntax. Unexpected newlines, then this:
...project/node_modules/biscotto/src/util/referencer.coffee:217
for (index = _j = 0, _len1 = options.length; _j < _len1; index = ++_
^
TypeError: Cannot read property 'length' of undefined
at Referencer.module.exports.Referencer.resolveDoc (...project/node_modules/biscotto/src/util/referencer.coffee:213:9)
(filename path trimmed to save space)
Hello,
I'm packaging atom [1] for debian [2].
Your software is there included.
I found some issues in your software:
Regards,
Jörg Frings-Fürst
[1] http://atom.io
[2] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=747824
[3]
// Copyright 20xx-20xx your name
// License: [your license eg. MIT / GPL-2 ....]
Would be cool if this style was supported in param and return comments.
# options - The Hash options used to refine the selection (default: {}):
# :color - The String color to restrict by (optional).
# :weight - The Float weight to restrict by. The weight should
# be specified in grams (optional).
This is a suggestion to attract more user base to this project.
Please Add a section "Projects Using biscotto" in README.md, which holds list of projects using biscotto.
This will help new users to find how useful this project is and who else is using it with confidence 👍
Docs say:
--statsOnly Only returns stats; generates no doc output
> rm -fr doc
> ls doc
ls: doc: No such file or directory
> biscotto --statsOnly src/
[WARN] Cannot resolve link to .script of class Biscotto in class Generator
[WARN] Cannot resolve link to .style of class Biscotto in class Generator
[WARN] Cannot find referenced class #getClassName in class Class (#getClassName)
[WARN] Cannot find referenced class #getClassName in class Class (#getClassName)
[WARN] Cannot find referenced class Mixin#getMixinName in class Mixin (Mixin#getMixinName)
[WARN] Cannot find referenced class Mixin#getMixinName in class Mixin (Mixin#getMixinName)
[WARN] Cannot find referenced class #getLink in class Referencer (#getLink)
[WARN] Cannot find referenced class #getLink in class Referencer (#getLink)
[WARN] Cannot find referenced class #linkTypes in class Referencer (#linkTypes)
Parsed files: 17
Classes: 16 (0 undocumented)
Mixins: 0
Non-Class files: 0
Methods: 131 (5 undocumented)
Constants: 0 (0 undocumented)
96.60% documented (147 total, 5 with no doc)
9 errors
> ls -al doc/
total 0
drwxr-xr-x 4 kevin staff 136 Apr 19 16:22 .
drwxr-xr-x 19 kevin staff 646 Apr 19 16:22 ..
drwxr-xr-x 5 kevin staff 170 Apr 19 16:22 assets
drwxr-xr-x 2 kevin staff 68 Apr 19 16:22 classes
Hi there,
# Set the value of one or multiple fields in the modeled
# document.
#
# {::set} can be called two ways: one-off and
# bulk.
#
#
# Examples
#
# model.set('flavor', 'turkish');
# model.set({flavor: 'turkish', price: 4});
#
#
# Returns true if changes were propagated to the database
I expected to see the examples displayed as pretty-printed code, or to at least be displayed. Am I doing something wrong?
Thanks,
Patrick
How to document Method Overloading ?
For example CODO is having @overload
. This is a common pattern in .js
& .coffee
( same method may determine the signature at run time, based on parameters or order it is received )
# This is a generic Store set method.
#
# @overload set(key, value)
# Sets a value on key
# @param [Symbol] key describe key param
# @param [Object] value describe value param
#
# @overload set(value)
# Sets a value on the default key `:foo`
# @param [Object] value describe value param
# @return [Boolean] true when success
#
set: (args...) ->
Please add the -g option in install section of README.md
current doc:
npm install biscotto
please update with
npm install -g biscotto
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.