aboutsummaryrefslogtreecommitdiffstats
path: root/root/meta
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2018-11-06 05:55:32 +0000
committers-ol <s-ol@users.noreply.github.com>2018-11-06 05:57:09 +0000
commit5be83254486bef58f85f43dba719b674f0c3bc92 (patch)
tree519608d655497a60e47202504aaed6bd98655717 /root/meta
parentmmm.component fix (diff)
downloadmmm-5be83254486bef58f85f43dba719b674f0c3bc92.tar.gz
mmm-5be83254486bef58f85f43dba719b674f0c3bc92.zip
implement mmmfs on real fs
Diffstat (limited to 'root/meta')
-rw-r--r--root/meta/init.moon36
-rw-r--r--root/meta/mmm.dom/description: text$plain1
-rw-r--r--root/meta/mmm.dom/text$moonscript -> mmm$dom.moon172
-rw-r--r--root/meta/mmm_dom.moon179
-rw-r--r--root/meta/test_component.moon254
-rw-r--r--root/meta/test_mmm.component/description: text$plain1
-rw-r--r--root/meta/test_mmm.component/text$moonscript -> mmm$dom.moon246
-rw-r--r--root/meta/text$moonscript -> fn -> mmm$dom.moon25
-rw-r--r--root/meta/title: text$plain1
-rw-r--r--root/meta/todo.moon42
-rw-r--r--root/meta/todo/description: text$plain1
-rw-r--r--root/meta/todo/text$moonscript -> mmm$component.moon34
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