{"action":"created","issue":{"url":"https://api.github.com/repos/operable/cog/issues/863","repository_url":"https://api.github.com/repos/operable/cog","labels_url":"https://api.github.com/repos/operable/cog/issues/863/labels{/name}","comments_url":"https://api.github.com/repos/operable/cog/issues/863/comments","events_url":"https://api.github.com/repos/operable/cog/issues/863/events","html_url":"https://github.com/operable/cog/issues/863","id":166191170,"number":863,"title":"Add function call capabilities to command templates","user":{"login":"christophermaier","id":207178,"avatar_url":"https://avatars.githubusercontent.com/u/207178?v=3","gravatar_id":"","url":"https://api.github.com/users/christophermaier","html_url":"https://github.com/christophermaier","followers_url":"https://api.github.com/users/christophermaier/followers","following_url":"https://api.github.com/users/christophermaier/following{/other_user}","gists_url":"https://api.github.com/users/christophermaier/gists{/gist_id}","starred_url":"https://api.github.com/users/christophermaier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/christophermaier/subscriptions","organizations_url":"https://api.github.com/users/christophermaier/orgs","repos_url":"https://api.github.com/users/christophermaier/repos","events_url":"https://api.github.com/users/christophermaier/events{/privacy}","received_events_url":"https://api.github.com/users/christophermaier/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/operable/cog/labels/cog","name":"cog","color":"D0E6FC"},{"url":"https://api.github.com/repos/operable/cog/labels/in-progress","name":"in-progress","color":"5555a5"}],"state":"closed","locked":false,"assignee":{"login":"christophermaier","id":207178,"avatar_url":"https://avatars.githubusercontent.com/u/207178?v=3","gravatar_id":"","url":"https://api.github.com/users/christophermaier","html_url":"https://github.com/christophermaier","followers_url":"https://api.github.com/users/christophermaier/followers","following_url":"https://api.github.com/users/christophermaier/following{/other_user}","gists_url":"https://api.github.com/users/christophermaier/gists{/gist_id}","starred_url":"https://api.github.com/users/christophermaier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/christophermaier/subscriptions","organizations_url":"https://api.github.com/users/christophermaier/orgs","repos_url":"https://api.github.com/users/christophermaier/repos","events_url":"https://api.github.com/users/christophermaier/events{/privacy}","received_events_url":"https://api.github.com/users/christophermaier/received_events","type":"User","site_admin":false},"assignees":[{"login":"christophermaier","id":207178,"avatar_url":"https://avatars.githubusercontent.com/u/207178?v=3","gravatar_id":"","url":"https://api.github.com/users/christophermaier","html_url":"https://github.com/christophermaier","followers_url":"https://api.github.com/users/christophermaier/followers","following_url":"https://api.github.com/users/christophermaier/following{/other_user}","gists_url":"https://api.github.com/users/christophermaier/gists{/gist_id}","starred_url":"https://api.github.com/users/christophermaier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/christophermaier/subscriptions","organizations_url":"https://api.github.com/users/christophermaier/orgs","repos_url":"https://api.github.com/users/christophermaier/repos","events_url":"https://api.github.com/users/christophermaier/events{/privacy}","received_events_url":"https://api.github.com/users/christophermaier/received_events","type":"User","site_admin":false}],"milestone":null,"comments":0,"created_at":"2016-07-18T20:54:40Z","updated_at":"2016-07-19T16:01:13Z","closed_at":"2016-07-19T16:01:13Z","body":"Currently, command templates are limited by a lack of helper functions. We have support for Mustache partials, but those are only arity-1 functions that take the context, and can't be further parameterized. Thus, something as simple as converting a list of names into a delimited string is not possible in a general way (short of providing partials like `join-comma`, `join-space`, etc.)\r\n\r\nWe would like to support helper functions like this in templates. These will be limited to functions written in Elixir and provided by Cog itself; bundles won't be able to add their own functions, and no sort of `eval`-like functionality will exist.\r\n\r\nThe notes below contain details and background on the change. Modifying the executor as detailed below can be a follow-on task.\r\n\r\nThe specific syntax for calling a function should be something distinguishable enough from core Mustache to denote that it is an extension.\r\n\r\n----\r\n\r\nTo support Handlebar-style helper functions\r\n\r\n    {{ helper-fn arg1 arg2 ... }}\r\n\r\nor some kind of alternative syntax\r\n\r\n    {{ table(inputs, ['col1', 'col2', 'col3']) }}\r\n\r\nwe'll need to tweak the FuManchu lexer/parser/generator stack.\r\n\r\nCurrently it treats `{{> partial}}` as a call to an arity-1 function\r\nthat is passed the current context map. We can't use this as is for\r\nsomething like a `table` helper function, because we don't have a way\r\nto pass it arguments (e.g., the columns to extract)\r\n\r\nFuManchu also currently treats `{{ one two three }}` as a single\r\nvariable dereference for a variable named `one two three`. If that\r\nwere to be treated as a call to the function `one` with arguments\r\n`two` and `three`, the parsing logic would have to be able to tease\r\nthat apart.\r\n\r\nA further wrinkle is how (in the above example), the values\r\n`two` and `three` should be interpreted. Handlebars has the notion of\r\n\"literals\", which are [\"numbers, strings, true, false, null and\r\nundefined\"](http://handlebarsjs.com/#literals). If the value is not a\r\nliteral, it's interpreted as a variable to be resolved in the current\r\nscope. We'd need to teach FuManchu about literals, as well as how to\r\nclassify variables within a function-calling scope.\r\n\r\nAnother potential wrinkle stems from the fact that Elixir doesn't have\r\nvararg functions, so for something like a `table` helper (but see\r\nbelow), we'd need to pass the desired columns as an explicit\r\nlist. This means that FuManchu would need to be able to recognize and\r\nprocess lists of literals / variables (we could probably just get by\r\nwith lists for right now; I don't know that dealing with maps,\r\netc. would be necessary).\r\n\r\nIf we add function capabilities, we could subsume the current partials\r\nfunctionality into it; that is, if we detect a function call without\r\nany explicit arguments, we call the function passing the current\r\ncontext as the single argument.\r\n\r\n---\r\n\r\nThe above formulation would allow us to have a helper like \r\n\r\n    {{ join names \", \" }}\r\n\r\nto render a list as a comma-delimited string (this would probably be a\r\ngood motivating use case and initial test instance). However, in order\r\nto be able to create a helper that generates a table, we would need to\r\nchange how the executor dispatches data for template\r\nhandling. Currently, each individual output map generated by a\r\npipeline is rendered by a template; the result of all these renderings\r\nis then concatenated into a final response before being sent back to\r\nthe user. Generating a table would require access to the entire list\r\nof outputs at once.\r\n\r\nI'm not sure if the render-each-output-individually approach is for a\r\nspecific reason, or because we didn't have the ability to do more\r\ncomplicated things inside individual templates, and thus had to wrap\r\nthose more complicated use cases with stand-alone commands that could\r\naccumulate state for us.\r\n\r\nIf we switched to having the executor pass all outputs to a template\r\nfor rendering, we'd be able to generate tables relatively\r\nstraightforwardly inside a template. Incidentally, it would also allow\r\nus to generate more friendly outputs. For instance, `operable:help`\r\ncurrently returns a list of commands, and that's it. It would be nice\r\nto add some explanatory text around that (\"Here are the commands that\r\nare currently enabled...\", \"For details on a particular command, type\r\n`operable:help COMMAND`\", etc). It would also probably allow us to\r\nmore easily take advantage of Slack attachments, HipChat rich\r\nresponses, etc. if we have all data present in one place for templating.\r\n\r\nIncidentally, if we made these changes, we could still have a\r\nstand-alone `table` command. Its template could be something like \r\n\r\n    {{ table rows columns }}\r\n\r\nwhere it is passed a context object of\r\n\r\n    {\r\n      \"rows\": <accumulated outputs>,\r\n      \"columns\": <whatever arguments were passed to the command>\r\n    }\r\n\r\n---\r\n\r\nThis chanage in the executor would be a backward-incompatible change with our current\r\ntemplates, which assume they're operating on a single map. If we\r\ndecided to bump the bundle config version, we could have the executor\r\nuse the old (current) behavior for version 3 bundles, but the new\r\nall-at-once behavior for version 4+ bundles."},"comment":{"url":"https://api.github.com/repos/operable/cog/issues/comments/233680643","html_url":"https://github.com/operable/cog/issues/863#issuecomment-233680643","issue_url":"https://api.github.com/repos/operable/cog/issues/863","id":233680643,"user":{"login":"christophermaier","id":207178,"avatar_url":"https://avatars.githubusercontent.com/u/207178?v=3","gravatar_id":"","url":"https://api.github.com/users/christophermaier","html_url":"https://github.com/christophermaier","followers_url":"https://api.github.com/users/christophermaier/followers","following_url":"https://api.github.com/users/christophermaier/following{/other_user}","gists_url":"https://api.github.com/users/christophermaier/gists{/gist_id}","starred_url":"https://api.github.com/users/christophermaier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/christophermaier/subscriptions","organizations_url":"https://api.github.com/users/christophermaier/orgs","repos_url":"https://api.github.com/users/christophermaier/repos","events_url":"https://api.github.com/users/christophermaier/events{/privacy}","received_events_url":"https://api.github.com/users/christophermaier/received_events","type":"User","site_admin":false},"created_at":"2016-07-19T16:01:13Z","updated_at":"2016-07-19T16:01:13Z","body":"After much discussion, we're closing this. We've determined that Mustache is too limited a templating language, and are going to investigate alternatives in order to achieve our broader goals for template improvements instead of adding to / working around Mustache."}}
{"actor":{"display_login":"christophermaier"}}