diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2018-11-06 05:55:32 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2018-11-06 05:57:09 +0000 |
| commit | 5be83254486bef58f85f43dba719b674f0c3bc92 (patch) | |
| tree | 519608d655497a60e47202504aaed6bd98655717 /root/meta | |
| parent | mmm.component fix (diff) | |
| download | mmm-5be83254486bef58f85f43dba719b674f0c3bc92.tar.gz mmm-5be83254486bef58f85f43dba719b674f0c3bc92.zip | |
implement mmmfs on real fs
Diffstat (limited to 'root/meta')
| -rw-r--r-- | root/meta/init.moon | 36 | ||||
| -rw-r--r-- | root/meta/mmm.dom/description: text$plain | 1 | ||||
| -rw-r--r-- | root/meta/mmm.dom/text$moonscript -> mmm$dom.moon | 172 | ||||
| -rw-r--r-- | root/meta/mmm_dom.moon | 179 | ||||
| -rw-r--r-- | root/meta/test_component.moon | 254 | ||||
| -rw-r--r-- | root/meta/test_mmm.component/description: text$plain | 1 | ||||
| -rw-r--r-- | root/meta/test_mmm.component/text$moonscript -> mmm$dom.moon | 246 | ||||
| -rw-r--r-- | root/meta/text$moonscript -> fn -> mmm$dom.moon | 25 | ||||
| -rw-r--r-- | root/meta/title: text$plain | 1 | ||||
| -rw-r--r-- | root/meta/todo.moon | 42 | ||||
| -rw-r--r-- | root/meta/todo/description: text$plain | 1 | ||||
| -rw-r--r-- | root/meta/todo/text$moonscript -> mmm$component.moon | 34 |
12 files changed, 481 insertions, 511 deletions
diff --git a/root/meta/init.moon b/root/meta/init.moon deleted file mode 100644 index 4c2fc15..0000000 --- a/root/meta/init.moon +++ /dev/null @@ -1,36 +0,0 @@ -import div, h3, p, br, ul, li, b, a from require 'mmm.dom' -import define_fileders from require 'mmm.mmmfs' -Fileder = define_fileders ... -require = relative ... - -Fileder { - 'name: alpha': 'meta', - 'title: text/plain': 'about mmm', - 'fn -> mmm/dom': (path) => div { - style: { 'max-width': '700px' }, - h3 @gett 'title: text/plain', style: { 'margin-bottom': '-.5em' }, - p "mmm is a collection of Lua/Moonscript modules for web development.", - "All modules are 'polymorphic' - they can run in the ", (b 'browser'), - ", using the native browser API for creating and interacting with DOM content, as well as on the ", - (b 'server'), ", where they operate on and produce equivalent HTML strings." - p "As the two implementations of each module are designed to be compatible, - mmm facilitates code and content sharing between server and client - and enables serverside rendering and rehydration." - ul for child in *@children - name = child\gett 'name: alpha' - desc = child\gett 'description: mmm/dom' - li { - a name, { - href: child.path, - onclick: (e) => - e\preventDefault! - BROWSER\navigate child.path - }, - ': ', desc - } - } - - require '.mmm_dom' - require '.todo' - require '.test_component' -} diff --git a/root/meta/mmm.dom/description: text$plain b/root/meta/mmm.dom/description: text$plain new file mode 100644 index 0000000..5f4f965 --- /dev/null +++ b/root/meta/mmm.dom/description: text$plain @@ -0,0 +1 @@ +a lightweight DSL for creating HTML documents diff --git a/root/meta/mmm.dom/text$moonscript -> mmm$dom.moon b/root/meta/mmm.dom/text$moonscript -> mmm$dom.moon new file mode 100644 index 0000000..07dab49 --- /dev/null +++ b/root/meta/mmm.dom/text$moonscript -> mmm$dom.moon @@ -0,0 +1,172 @@ +import article, h1, h2, p, a, span, div, pre, code from require 'mmm.dom' + +mmmdom = -> code 'mmm.dom' + +source = do + build = (lang) -> if MODE == 'SERVER' + (str) -> code (str\match '^ *(..-) *$'), class: "hljs lang-#{lang}" + else + (str) -> + result = window.hljs\highlight lang, (str\match '^ *(..-) *$'), true + with code class: 'hljs' + .innerHTML = result.value + + mklua, mkmoon = (build 'lua'), build 'moonscript' + + (moon, lua, demo=true) -> + the_code = pre (mkmoon moon), (mklua lua), class: 'dual-code' + + return the_code unless demo + + example = assert load lua + div the_code, div example!, class: 'example' + +article { + h1 mmmdom! + p mmmdom!, " is a lightweight DSL for creating HTML documents in Lua and Moonscript." + + p do + fengari = a "fengari.io", href: '//fengari.io' + + "The same API is supported both on the server / in native Lua, where it outputs an HTML string, + as well as on the client (using ", fengari, "), where it dynamically creates DOM Nodes that can + be further manipulated using Lua or JavaScript. The API behaves exactly the same way in both modes + so that you can write code once that works both for rendering offline/serverside and in the browser. + This enables you to build websites and applications with dynamic content as well as perfect static + views for clients with JS disabled and SEO." + + h2 "API" + p "Begin by requiring ", mmmdom!, ". The module returns a 'magic table' that allows you to instantiate + HTML elements of any type. Compare the usage in Lua and Moonscript:" + + source [[ +import div, h1, p, a from require 'mmm.dom' + +-- or just use dom.div etc. if you want to keep your +-- namespace clean, they are cached automatically + +dom = require 'mmm.dom' + ]], [[ +local dom = require 'mmm.dom' + +local div, h1, p, a = dom.div, dom.h1, dom.p, dom.a + +-- or just use dom.div etc. if you want to keep your +-- namespace clean, they are cached automatically + ]], false + + p "Each of these constructor functions can now be used to instantiate elements of the specified type. + In the simplest case, you may want to a simple element with no attributes or children, for example a ", + (code '<br />'), " line break tag:" + + source [[ +import br from require 'mmm.dom' + +br! + ]], [[ +local dom = require 'mmm.dom' + +return dom.br() + ]], false + + p "You can pass any number of children as arguments when you create an element. They will be joined without spaces:" + + source [[ +import h3, i, code from require 'mmm.dom' + +h3 "this is a ", +(i 'headline'), +" with some ", +code 'rich text' +]], [[ +local d = require 'mmm.dom' + +return d.h3( +"this is a ", +d.i 'headline', +" with some ", +d.code 'rich text' +) + ]] + + p "As you can see, ", mmmdom!, " can be used quite comfortably both in Lua and Moonscript." + p "As you build bigger structures, you may find that constructing HTML trees via argument lists can get a bit + confusing (especially with Moonscript and indentation rules). + Because of this ", mmmdom!, " also supports passing a table with children in the integer keys 1, 2, ... + This is particularily useful when you make use of Lua's shorthand for calling functions with single arguments:" + + source [[ +import article, h3, span, div, i, p from require 'mmm.dom' + +article { +h3 "This is a headline with ", i "cursive text" +p { + "The content goes ", + i "here." +} +p "more paragraphs can be added as well, of course." +} + ]], [[ +local dom = require 'mmm.dom' +local article, h3, span, div, i, p = dom.article, dom.h3, dom.span, dom.div, dom.i, dom.p + +return article{ +h3{ "This is a headline with ", i "cursive text" }, +p{ + "The content goes ", + i"here." +}, +p"more paragraphs can be added as well, of course." +} + ]] + + p "Of course ", mmmdom!, " also allows you to set attributes on elements." + p "When you are using a constructor using the single-table approach, + all string keys set on the table are considered attributes and set on the element:" + + source [[ +import div from require 'mmm.dom' + +div { +id: 'my_div', +class: 'shadow', +"This is div matches the CSS selector `div#my_div.shadow`" +} + ]], [[ +local div = require('mmm.dom').div + +return div{ +id = 'my_div', +class = 'shadow', +"This is div matches the CSS selector `div#my_div.shadow`" +} + ]] + + p "When you are passing multiple arguments, you can attach the attributes in a table + as the last argument. This works very well with Moonscript syntax." + + p "In general attribute values need to be strings or numbers to be rendered correctly both + on client on server. The only exception is the ", (code 'style'), " attribute, which, + if it is passed as a table, will be expanded into a valid CSS string:" + + source [[ +import div from require 'mmm.dom' + +div "red div with white text", style: { +background: 'red', +color: '#ffffff', +} + ]], [[ +local div = require('mmm.dom').div + +return div( +"red div with white text", +{ + style = { + background = 'red', + color = '#ffffff', + } +} +) + ]] +} diff --git a/root/meta/mmm_dom.moon b/root/meta/mmm_dom.moon deleted file mode 100644 index 35d60c0..0000000 --- a/root/meta/mmm_dom.moon +++ /dev/null @@ -1,179 +0,0 @@ -import define_fileders from require 'mmm.mmmfs' -Fileder = define_fileders ... - -import article, h1, h2, p, a, span, div, pre, code from require 'mmm.dom' - -mmmdom = -> code 'mmm.dom' - -source = do - build = (lang) -> if MODE == 'SERVER' - (str) -> code (str\match '^ *(..-) *$'), class: "hljs lang-#{lang}" - else - (str) -> - result = window.hljs\highlight lang, (str\match '^ *(..-) *$'), true - with code class: 'hljs' - .innerHTML = result.value - - mklua, mkmoon = (build 'lua'), build 'moonscript' - - (moon, lua, demo=true) -> - the_code = pre (mkmoon moon), (mklua lua), class: 'dual-code' - - return the_code unless demo - - example = assert load lua - div the_code, div example!, class: 'example' - -Fileder { - 'name: alpha': 'mmm.dom' - 'description: mmm/dom': 'a lightweight DSL for creating HTML documents', - 'mmm/dom': article { - h1 mmmdom! - p mmmdom!, " is a lightweight DSL for creating HTML documents in Lua and Moonscript." - - p do - fengari = a "fengari.io", href: '//fengari.io' - - "The same API is supported both on the server / in native Lua, where it outputs an HTML string, - as well as on the client (using ", fengari, "), where it dynamically creates DOM Nodes that can - be further manipulated using Lua or JavaScript. The API behaves exactly the same way in both modes - so that you can write code once that works both for rendering offline/serverside and in the browser. - This enables you to build websites and applications with dynamic content as well as perfect static - views for clients with JS disabled and SEO." - - h2 "API" - p "Begin by requiring ", mmmdom!, ". The module returns a 'magic table' that allows you to instantiate - HTML elements of any type. Compare the usage in Lua and Moonscript:" - - source [[ -import div, h1, p, a from require 'mmm.dom' - --- or just use dom.div etc. if you want to keep your --- namespace clean, they are cached automatically - -dom = require 'mmm.dom' - ]], [[ -local dom = require 'mmm.dom' - -local div, h1, p, a = dom.div, dom.h1, dom.p, dom.a - --- or just use dom.div etc. if you want to keep your --- namespace clean, they are cached automatically - ]], false - - p "Each of these constructor functions can now be used to instantiate elements of the specified type. - In the simplest case, you may want to a simple element with no attributes or children, for example a ", - (code '<br />'), " line break tag:" - - source [[ -import br from require 'mmm.dom' - -br! - ]], [[ -local dom = require 'mmm.dom' - -return dom.br() - ]], false - - p "You can pass any number of children as arguments when you create an element. They will be joined without spaces:" - - source [[ -import h3, i, code from require 'mmm.dom' - -h3 "this is a ", - (i 'headline'), - " with some ", - code 'rich text' - ]], [[ -local d = require 'mmm.dom' - -return d.h3( - "this is a ", - d.i 'headline', - " with some ", - d.code 'rich text' -) - ]] - - p "As you can see, ", mmmdom!, " can be used quite comfortably both in Lua and Moonscript." - p "As you build bigger structures, you may find that constructing HTML trees via argument lists can get a bit - confusing (especially with Moonscript and indentation rules). - Because of this ", mmmdom!, " also supports passing a table with children in the integer keys 1, 2, ... - This is particularily useful when you make use of Lua's shorthand for calling functions with single arguments:" - - source [[ -import article, h3, span, div, i, p from require 'mmm.dom' - -article { - h3 "This is a headline with ", i "cursive text" - p { - "The content goes ", - i "here." - } - p "more paragraphs can be added as well, of course." -} - ]], [[ -local dom = require 'mmm.dom' -local article, h3, span, div, i, p = dom.article, dom.h3, dom.span, dom.div, dom.i, dom.p - -return article{ - h3{ "This is a headline with ", i "cursive text" }, - p{ - "The content goes ", - i"here." - }, - p"more paragraphs can be added as well, of course." -} - ]] - - p "Of course ", mmmdom!, " also allows you to set attributes on elements." - p "When you are using a constructor using the single-table approach, - all string keys set on the table are considered attributes and set on the element:" - - source [[ -import div from require 'mmm.dom' - -div { - id: 'my_div', - class: 'shadow', - "This is div matches the CSS selector `div#my_div.shadow`" -} - ]], [[ -local div = require('mmm.dom').div - -return div{ - id = 'my_div', - class = 'shadow', - "This is div matches the CSS selector `div#my_div.shadow`" -} - ]] - - p "When you are passing multiple arguments, you can attach the attributes in a table - as the last argument. This works very well with Moonscript syntax." - - p "In general attribute values need to be strings or numbers to be rendered correctly both - on client on server. The only exception is the ", (code 'style'), " attribute, which, - if it is passed as a table, will be expanded into a valid CSS string:" - - source [[ -import div from require 'mmm.dom' - -div "red div with white text", style: { - background: 'red', - color: '#ffffff', -} - ]], [[ -local div = require('mmm.dom').div - -return div( - "red div with white text", - { - style = { - background = 'red', - color = '#ffffff', - } - } -) - ]] - } -} diff --git a/root/meta/test_component.moon b/root/meta/test_component.moon deleted file mode 100644 index d5fb6f1..0000000 --- a/root/meta/test_component.moon +++ /dev/null @@ -1,254 +0,0 @@ -import define_fileders from require 'mmm.mmmfs' -Fileder = define_fileders ... - -Fileder { - 'name: alpha': 'test_mmm.component' - 'description: text/plain': 'Tests for mmm.component' - 'fn -> mmm/dom': => - import article, div, h1, ul, li, pre from require 'mmm.dom' - - _content = {} - append = (stuff) -> table.insert _content, stuff - - last = nil - test_group = (name) -> - if last - append div (h1 last.name), ul last - - last = { :name } - (name, test) -> - ok, err = pcall test - table.insert last, li if ok - "passed '#{name}'" - else - "failed '#{name}'", pre err - - expect = (expected, note, ...) -> - ok, msg = pcall ... - if ok or not msg\find expected - error note - - run_test = test_group 'component.moon' - - local ReactiveVar, ReactiveElement - run_test "exports ReactiveVar, ReactiveElement", -> - import ReactiveVar, ReactiveElement from require 'mmm.component' - assert ReactiveVar, "ReactiveVar not exported" - assert ReactiveElement, "ReactiveElement not exported" - - run_test "exports tohtml helper", -> - import tohtml from require 'mmm.component' - assert 'function' == (type tohtml), "tohtml not exported" - - -- @TODO: get_or_create - - run_test "exports text helper", -> - import text from require 'mmm.component' - assert 'function' == (type text), "text not exported" - - node = text 'a test string' - - data = if MODE == 'CLIENT' - assert (js.instanceof node, js.global.Node), "expected text to generate a Node" - node.data - else - node - - assert data == 'a test string', "expected text to store the string" - - run_test "text joins multiple arguments", -> - import text from require 'mmm.component' - - node = text 'a', 'test', 'string' - - data = if MODE == 'CLIENT' then node.data else node - assert data == 'a test string', "expected text to join arguments with spaces" - - run_test "exports elements table", -> - import elements from require 'mmm.component' - - assert (type elements.div!) == 'table', "expected to build element with elements.div!" - assert (type elements.madeup!) == 'table', "expected to build element with elements.madeup!" - - run_test = test_group 'ReactiveVar' - - run_test "stores a value", -> - reactive = ReactiveVar 'test' - assert 'test' == reactive\get!, "expected x to be 'test'" - - run_test "provides #map shorthand", -> - local done - - original = ReactiveVar 1 - mapped = original\map (a) -> a + 10 - - assert mapped\get! == 11, "expected mapped to be 11" - return if MODE == 'SERVER' - - original\set 4 - assert mapped\get! == 14, "expected mapped to update" - - mapped\subscribe coroutine.wrap (next, last) -> - assert next == 26, "expected next to be 26" - assert last == 14, "expected last to be 14" - done = true - - original\set 16 - assert done, "expected to reach the end" - - if MODE == 'CLIENT' - run_test "propagates updates", -> - local done - - reactive = ReactiveVar 'test' - reactive\subscribe coroutine.wrap (next) -> - assert next == 'toast', "expected next to be 'toast'" - assert coroutine.yield! == 'cheese', "expected next to be 'cheese'" - done = true - - reactive\set 'toast' - assert 'toast' == reactive\get!, "expected #get to return 'toast'" - reactive\set 'cheese' - assert done, "expected to reach the end" - - run_test "passes old value as well", -> - local done - - reactive = ReactiveVar 1 - reactive\subscribe coroutine.wrap (next, last) -> - assert last == 1, "expected last:1 to be 1" - next, last = coroutine.yield! - assert last == 2, "expected last:2 to be 2" - done = true - - reactive\set 2 - reactive\set 3 - assert done, "expected to reach the end" - - run_test "provides #transform shorthand", -> - local done - - reactive = ReactiveVar 1 - reactive\subscribe coroutine.wrap (next, last) -> - assert last == 1, "expected last:1 to be 1" - next, last = coroutine.yield! - assert last == 2, "expected last:2 to be 2" - done = true - - add_one = (a) -> a + 1 - reactive\transform add_one - reactive\transform add_one - assert done, "expected to reach the end" - - run_test "#subscribe returns function to unsubscribe", -> - calls = 0 - - reactive = ReactiveVar 1 - unsub = reactive\subscribe coroutine.wrap () -> - calls += 1 - coroutine.yield! - calls += 1 - coroutine.yield! - calls += 1 - - assert 'function' == (type unsub), "expected to receive a function" - - reactive\set 2 - reactive\set 3 - assert calls == 2, "wat" - - unsub! - reactive\set 4 - assert calls == 2, "expected to stop receiving updates" - - run_test "tracks multiple subscriptions at once", -> - reactive = ReactiveVar 'test' - unsub = reactive\subscribe coroutine.wrap (next) -> - assert next == 'toast', "expected next to be toast" - next = coroutine.yield! - assert next == 'cheese', "expected next to be cheese" - coroutine.yield! - error "expected not to get here" - - reactive\set 'toast' - - local done - reactive\subscribe coroutine.wrap (next) -> - assert next == 'cheese', "expected next to be cheese" - next = coroutine.yield! - assert next == 'test', "expected next to be test" - done = true - - reactive\set 'cheese' - unsub! - reactive\set 'test' - assert done, "expected to reach the end" - - run_test = test_group 'ReactiveElement' - - run_test "creates a HTML element", -> - elem = ReactiveElement 'span' - assert elem.node and elem.node.localName == 'span', "expected Node to be a <span>" - - -- @TODO: can take over a DOM Node - - run_test "sets attributes from a table arg", -> - elem = ReactiveElement 'span', class: 'never' - assert elem.node.className == 'never', "expected class to be 'never'" - - run_test "appends Nodes from arguments", -> - e_div, e_pre = div!, pre! - elem = ReactiveElement 'span', e_div, e_pre - assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem" - assert elem.node.lastElementChild == e_pre, "expected pre to be the last child of elem" - - run_test "can append ReactiveElements and text", -> - e_div = ReactiveElement 'div' - elem = ReactiveElement 'div', e_div, 'testtext' - assert elem.node.firstElementChild == e_div.node, "expected div to be the first child of elem" - assert elem.node.lastChild.data == 'testtext', "expected last child of elem to be 'testtext'" - - run_test "accepts attributes after children", -> - e_div = div! - elem = ReactiveElement 'div', e_div, class: 'test' - assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem" - assert elem.node.className == 'test', "expected class to be 'test'" - - run_test "allows mixing attributes and children in a single table", -> - e_div, e_pre = div!, pre! - elem = ReactiveElement 'div', { class: 'test', e_div, e_pre } - assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem" - assert elem.node.lastElementChild == e_pre, "expected pre to be the last child of elem" - assert elem.node.className == 'test', "expected class to be 'test'" - - run_test "can unwrap and track attributes from ReactiveVars", -> - klass = ReactiveVar 'test' - elem = ReactiveElement 'div', class: klass - assert elem.node.className == 'test', "expected class to be 'test'" - klass\set 'toast' - assert elem.node.className == 'toast', "expected class to be 'toast'" - - run_test "can unwrap and track children from ReactiveVars", -> - child = ReactiveVar h1 'test' - elem = ReactiveElement 'div', child, pre 'fixed' - assert elem.node.firstElementChild.localName == 'h1', "expected first child to be h1" - assert elem.node.childElementCount == 2, "expected node to have two children" - child\set div 'toast' - assert elem.node.firstElementChild.localName == 'div', "expected first child to be div" - assert elem.node.childElementCount == 2, "expected node to have two children" - - run_test "warns when appending a string from a ReactiveVar", -> - import text from require 'mmm.component' - - str = ReactiveVar 'test' - elem = ReactiveElement 'div', str - expect 'cannot replace string node', 'expected error', str\set, 'string too' - elem\destroy! - - elem = ReactiveElement 'div', str\map text - str\set 'this is text' - - test_group! - - article _content -} diff --git a/root/meta/test_mmm.component/description: text$plain b/root/meta/test_mmm.component/description: text$plain new file mode 100644 index 0000000..691fea5 --- /dev/null +++ b/root/meta/test_mmm.component/description: text$plain @@ -0,0 +1 @@ +Tests for mmm.component diff --git a/root/meta/test_mmm.component/text$moonscript -> mmm$dom.moon b/root/meta/test_mmm.component/text$moonscript -> mmm$dom.moon new file mode 100644 index 0000000..547b030 --- /dev/null +++ b/root/meta/test_mmm.component/text$moonscript -> mmm$dom.moon @@ -0,0 +1,246 @@ +import article, div, h1, ul, li, pre from require 'mmm.dom' + +_content = {} +append = (stuff) -> table.insert _content, stuff + +last = nil +test_group = (name) -> + if last + append div (h1 last.name), ul last + + last = { :name } + (name, test) -> + ok, err = pcall test + table.insert last, li if ok + "passed '#{name}'" + else + "failed '#{name}'", pre err + +expect = (expected, note, ...) -> + ok, msg = pcall ... + if ok or not msg\find expected + error note + +run_test = test_group 'component.moon' + +local ReactiveVar, ReactiveElement +run_test "exports ReactiveVar, ReactiveElement", -> + import ReactiveVar, ReactiveElement from require 'mmm.component' + assert ReactiveVar, "ReactiveVar not exported" + assert ReactiveElement, "ReactiveElement not exported" + +run_test "exports tohtml helper", -> + import tohtml from require 'mmm.component' + assert 'function' == (type tohtml), "tohtml not exported" + +-- @TODO: get_or_create + +run_test "exports text helper", -> + import text from require 'mmm.component' + assert 'function' == (type text), "text not exported" + + node = text 'a test string' + + data = if MODE == 'CLIENT' + assert (js.instanceof node, js.global.Node), "expected text to generate a Node" + node.data + else + node + + assert data == 'a test string', "expected text to store the string" + +run_test "text joins multiple arguments", -> + import text from require 'mmm.component' + + node = text 'a', 'test', 'string' + + data = if MODE == 'CLIENT' then node.data else node + assert data == 'a test string', "expected text to join arguments with spaces" + +run_test "exports elements table", -> + import elements from require 'mmm.component' + + assert (type elements.div!) == 'table', "expected to build element with elements.div!" + assert (type elements.madeup!) == 'table', "expected to build element with elements.madeup!" + +run_test = test_group 'ReactiveVar' + +run_test "stores a value", -> + reactive = ReactiveVar 'test' + assert 'test' == reactive\get!, "expected x to be 'test'" + +run_test "provides #map shorthand", -> + local done + + original = ReactiveVar 1 + mapped = original\map (a) -> a + 10 + + assert mapped\get! == 11, "expected mapped to be 11" + return if MODE == 'SERVER' + + original\set 4 + assert mapped\get! == 14, "expected mapped to update" + + mapped\subscribe coroutine.wrap (next, last) -> + assert next == 26, "expected next to be 26" + assert last == 14, "expected last to be 14" + done = true + + original\set 16 + assert done, "expected to reach the end" + +if MODE == 'CLIENT' + run_test "propagates updates", -> + local done + + reactive = ReactiveVar 'test' + reactive\subscribe coroutine.wrap (next) -> + assert next == 'toast', "expected next to be 'toast'" + assert coroutine.yield! == 'cheese', "expected next to be 'cheese'" + done = true + + reactive\set 'toast' + assert 'toast' == reactive\get!, "expected #get to return 'toast'" + reactive\set 'cheese' + assert done, "expected to reach the end" + + run_test "passes old value as well", -> + local done + + reactive = ReactiveVar 1 + reactive\subscribe coroutine.wrap (next, last) -> + assert last == 1, "expected last:1 to be 1" + next, last = coroutine.yield! + assert last == 2, "expected last:2 to be 2" + done = true + + reactive\set 2 + reactive\set 3 + assert done, "expected to reach the end" + + run_test "provides #transform shorthand", -> + local done + + reactive = ReactiveVar 1 + reactive\subscribe coroutine.wrap (next, last) -> + assert last == 1, "expected last:1 to be 1" + next, last = coroutine.yield! + assert last == 2, "expected last:2 to be 2" + done = true + + add_one = (a) -> a + 1 + reactive\transform add_one + reactive\transform add_one + assert done, "expected to reach the end" + + run_test "#subscribe returns function to unsubscribe", -> + calls = 0 + + reactive = ReactiveVar 1 + unsub = reactive\subscribe coroutine.wrap () -> + calls += 1 + coroutine.yield! + calls += 1 + coroutine.yield! + calls += 1 + + assert 'function' == (type unsub), "expected to receive a function" + + reactive\set 2 + reactive\set 3 + assert calls == 2, "wat" + + unsub! + reactive\set 4 + assert calls == 2, "expected to stop receiving updates" + + run_test "tracks multiple subscriptions at once", -> + reactive = ReactiveVar 'test' + unsub = reactive\subscribe coroutine.wrap (next) -> + assert next == 'toast', "expected next to be toast" + next = coroutine.yield! + assert next == 'cheese', "expected next to be cheese" + coroutine.yield! + error "expected not to get here" + + reactive\set 'toast' + + local done + reactive\subscribe coroutine.wrap (next) -> + assert next == 'cheese', "expected next to be cheese" + next = coroutine.yield! + assert next == 'test', "expected next to be test" + done = true + + reactive\set 'cheese' + unsub! + reactive\set 'test' + assert done, "expected to reach the end" + +run_test = test_group 'ReactiveElement' + +run_test "creates a HTML element", -> + elem = ReactiveElement 'span' + assert elem.node and elem.node.localName == 'span', "expected Node to be a <span>" + +-- @TODO: can take over a DOM Node + +run_test "sets attributes from a table arg", -> + elem = ReactiveElement 'span', class: 'never' + assert elem.node.className == 'never', "expected class to be 'never'" + +run_test "appends Nodes from arguments", -> + e_div, e_pre = div!, pre! + elem = ReactiveElement 'span', e_div, e_pre + assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem" + assert elem.node.lastElementChild == e_pre, "expected pre to be the last child of elem" + +run_test "can append ReactiveElements and text", -> + e_div = ReactiveElement 'div' + elem = ReactiveElement 'div', e_div, 'testtext' + assert elem.node.firstElementChild == e_div.node, "expected div to be the first child of elem" + assert elem.node.lastChild.data == 'testtext', "expected last child of elem to be 'testtext'" + +run_test "accepts attributes after children", -> + e_div = div! + elem = ReactiveElement 'div', e_div, class: 'test' + assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem" + assert elem.node.className == 'test', "expected class to be 'test'" + +run_test "allows mixing attributes and children in a single table", -> + e_div, e_pre = div!, pre! + elem = ReactiveElement 'div', { class: 'test', e_div, e_pre } + assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem" + assert elem.node.lastElementChild == e_pre, "expected pre to be the last child of elem" + assert elem.node.className == 'test', "expected class to be 'test'" + +run_test "can unwrap and track attributes from ReactiveVars", -> + klass = ReactiveVar 'test' + elem = ReactiveElement 'div', class: klass + assert elem.node.className == 'test', "expected class to be 'test'" + klass\set 'toast' + assert elem.node.className == 'toast', "expected class to be 'toast'" + +run_test "can unwrap and track children from ReactiveVars", -> + child = ReactiveVar h1 'test' + elem = ReactiveElement 'div', child, pre 'fixed' + assert elem.node.firstElementChild.localName == 'h1', "expected first child to be h1" + assert elem.node.childElementCount == 2, "expected node to have two children" + child\set div 'toast' + assert elem.node.firstElementChild.localName == 'div', "expected first child to be div" + assert elem.node.childElementCount == 2, "expected node to have two children" + +run_test "warns when appending a string from a ReactiveVar", -> + import text from require 'mmm.component' + + str = ReactiveVar 'test' + elem = ReactiveElement 'div', str + expect 'cannot replace string node', 'expected error', str\set, 'string too' + elem\destroy! + + elem = ReactiveElement 'div', str\map text + str\set 'this is text' + +test_group! + +article _content diff --git a/root/meta/text$moonscript -> fn -> mmm$dom.moon b/root/meta/text$moonscript -> fn -> mmm$dom.moon new file mode 100644 index 0000000..bff47be --- /dev/null +++ b/root/meta/text$moonscript -> fn -> mmm$dom.moon @@ -0,0 +1,25 @@ +import div, h3, p, br, ul, li, b, a from require 'mmm.dom' + +=> div { + style: { 'max-width': '700px' }, + h3 @gett 'title: text/plain', style: { 'margin-bottom': '-.5em' }, + p "mmm is a collection of Lua/Moonscript modules for web development.", + "All modules are 'polymorphic' - they can run in the ", (b 'browser'), + ", using the native browser API for creating and interacting with DOM content, as well as on the ", + (b 'server'), ", where they operate on and produce equivalent HTML strings." + p "As the two implementations of each module are designed to be compatible, + mmm facilitates code and content sharing between server and client + and enables serverside rendering and rehydration." + ul for child in *@children + name = child\gett 'name: alpha' + desc = child\gett 'description: mmm/dom' + li { + a name, { + href: child.path, + onclick: (e) => + e\preventDefault! + BROWSER\navigate child.path + }, + ': ', desc + } + } diff --git a/root/meta/title: text$plain b/root/meta/title: text$plain new file mode 100644 index 0000000..ac2dbb1 --- /dev/null +++ b/root/meta/title: text$plain @@ -0,0 +1 @@ +about mmm diff --git a/root/meta/todo.moon b/root/meta/todo.moon deleted file mode 100644 index a568a74..0000000 --- a/root/meta/todo.moon +++ /dev/null @@ -1,42 +0,0 @@ -import define_fileders from require 'mmm.mmmfs' -Fileder = define_fileders ... - -Fileder { - 'name: alpha': 'todo' - 'description: text/plain': 'TodoMVC using mmm.component' - 'fn -> mmm/component': => - import ReactiveVar, text, elements from require 'mmm.component' - import article, div, form, span, h3, a, input from elements - - parent = div! - todoItem = (desc, done) -> - -- convert into reactive data sources - desc, done = (ReactiveVar desc), ReactiveVar done - with me = div style: - margin: '8px' - padding: '8px' - background: '#eeeeee' - \append h3 (desc\map text), style: 'margin: 0;' - \append span done\map (done) -> text if done then 'done' else 'not done yet' - \append input type: 'checkbox', checked: done, onchange: (e) => done\set e.target.checked - \append a (text 'delete'), href: '#', onclick: (e) => parent\remove me - - parent\append todoItem 'write a Component System', true - parent\append todoItem 'eat Lasagna', true - parent\append todoItem 'do other things' - - desc = ReactiveVar 'start' - form = with form { - action: '' - style: - margin: '2px' - onsubmit: (e) => - e\preventDefault! - parent\append todoItem desc\get! - desc\set '' - } - \append input type: 'text', value: desc, onchange: (e) => desc\set e.target.value - \append input type: 'submit', value: 'add' - - article parent, form -} diff --git a/root/meta/todo/description: text$plain b/root/meta/todo/description: text$plain new file mode 100644 index 0000000..baaf6fc --- /dev/null +++ b/root/meta/todo/description: text$plain @@ -0,0 +1 @@ +TodoMVC using mmm.component diff --git a/root/meta/todo/text$moonscript -> mmm$component.moon b/root/meta/todo/text$moonscript -> mmm$component.moon new file mode 100644 index 0000000..dc4e5e1 --- /dev/null +++ b/root/meta/todo/text$moonscript -> mmm$component.moon @@ -0,0 +1,34 @@ +import ReactiveVar, text, elements from require 'mmm.component' +import article, div, form, span, h3, a, input from elements + +parent = div! +todoItem = (desc, done) -> + -- convert into reactive data sources + desc, done = (ReactiveVar desc), ReactiveVar done + with me = div style: + margin: '8px' + padding: '8px' + background: '#eeeeee' + \append h3 (desc\map text), style: 'margin: 0;' + \append span done\map (done) -> text if done then 'done' else 'not done yet' + \append input type: 'checkbox', checked: done, onchange: (e) => done\set e.target.checked + \append a (text 'delete'), href: '#', onclick: (e) => parent\remove me + +parent\append todoItem 'write a Component System', true +parent\append todoItem 'eat Lasagna', true +parent\append todoItem 'do other things' + +desc = ReactiveVar 'start' +form = with form { + action: '' + style: + margin: '2px' + onsubmit: (e) => + e\preventDefault! + parent\append todoItem desc\get! + desc\set '' + } + \append input type: 'text', value: desc, onchange: (e) => desc\set e.target.value + \append input type: 'submit', value: 'add' + +article parent, form |
