Skip to content

Commit

Permalink
chore: refactor blog (#391)
Browse files Browse the repository at this point in the history
Signed-off-by: Manfred Touron <[email protected]>

Signed-off-by: Manfred Touron <[email protected]>
  • Loading branch information
moul authored Nov 17, 2022
1 parent 60144eb commit 32a040d
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 68 deletions.
78 changes: 43 additions & 35 deletions examples/gno.land/p/demo/blog/blog.gno
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
)

type Blog struct {
Title string
Prefix string // i.e. r/gnoland/blog:
Posts avl.MutTree // slug -> Post
}
Expand All @@ -25,11 +26,13 @@ func (b Blog) Render(path string) string {

switch {
case isHome:
output := breadcrumb([]string{b.Title})

if b.Posts.Size() == 0 {
return "No posts."
output += "No posts."
return output
}

output := ""
b.Posts.Iterate("", "", func(n *avl.Tree) bool {
post := n.Value().(*Post)
output += post.RenderListItem()
Expand All @@ -46,26 +49,52 @@ func (b Blog) Render(path string) string {
if !found {
return "404"
}
typedPost := post.(*Post)
return typedPost.RenderPage()
p := post.(*Post)

output := breadcrumb([]string{
ufmt.Sprintf("[%s](%s)", b.Title, b.Prefix),
"p",
p.Title,
})

// output += ufmt.Sprintf("## [%s](%s)\n", p.Title, p.URL())
output += p.Body + "\n\n"
output += p.RenderTagList() + "\n\n"
output += formatAuthorAndDate(p.Author, p.CreatedAt) + "\n"
output += "\n"

// comments
p.Comments.IterateReverse("", "", func(n *avl.Tree) bool {
comment := n.Value().(*Comment)
output += comment.RenderListItem()
return false
})

return output

case isViewTag:
tagSlug := parts[1]

output := ""
output := breadcrumb([]string{
ufmt.Sprintf("[%s](%s)", b.Title, b.Prefix),
"t",
tagSlug,
})

nb := 0
b.Posts.Iterate("", "", func(n *avl.Tree) bool {
post := n.Value().(*Post)
if !post.HasTag(tagSlug) {
return false
}
output += post.RenderListItem()
nb++
return false
})
if output != "" {
return output
if nb == 0 {
output += "No posts."
}

return "No posts."
return output
}

return "404"
Expand Down Expand Up @@ -181,35 +210,14 @@ func (p *Post) RenderListItem() string {
return "error: no such post\n"
}
output := ""
output += ufmt.Sprintf("## [%s](%s)\n", p.Title, p.URL())
output += p.Summary() + "\n\n"
output += p.RenderTagList() + "\n\n"
output += formatAuthorAndDate(p.Author, p.CreatedAt) + "\n"
output += ufmt.Sprintf("## [&#9656; %s](%s)\n", p.Title, p.URL())
// output += p.Summary() + "\n\n"
// output += p.RenderTagList() + "\n\n"
// output += formatAuthorAndDate(p.Author, p.CreatedAt) + "\n"
output += "\n"
return output
}

func (p *Post) RenderPage() string {
if p == nil {
return "error: no such post\n"
}
output := ""
output += ufmt.Sprintf("## [%s](%s)\n", p.Title, p.URL())
output += p.Body + "\n\n"
output += p.RenderTagList() + "\n\n"
output += formatAuthorAndDate(p.Author, p.CreatedAt) + "\n"
output += "\n"

// comments
p.Comments.IterateReverse("", "", func(n *avl.Tree) bool {
comment := n.Value().(*Comment)
output += comment.RenderListItem()
return false
})

return output
}

func (p *Post) RenderTagList() string {
if p == nil {
return "error: no such post\n"
Expand All @@ -220,7 +228,7 @@ func (p *Post) RenderTagList() string {
output += " "
}
tagURL := p.Blog.Prefix + "t/" + tag
output += ufmt.Sprintf("[%s](%s)", tag, tagURL)
output += ufmt.Sprintf("[#%s](%s)", tag, tagURL)
}
return output
}
Expand Down
7 changes: 7 additions & 0 deletions examples/gno.land/p/demo/blog/util.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package blog

import "strings"

func breadcrumb(parts []string) string {
return "# " + strings.Join(parts, " / ") + "\n\n"
}
5 changes: 2 additions & 3 deletions examples/gno.land/r/gnoland/blog/gnoblog.gno
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
)

var b = &blog.Blog{
Title: "Gnoland's Blog",
Prefix: "/r/gnoland/blog:",
}

Expand All @@ -20,7 +21,5 @@ func AddComment(postSlug, comment string) {
}

func Render(path string) string {
output := "# Gnoland's Blog\n\n"
output += b.Render(path)
return output
return b.Render(path)
}
42 changes: 12 additions & 30 deletions examples/gno.land/r/gnoland/blog/gnoblog_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,9 @@ No posts.
expected := `
# Gnoland's Blog
## [title1](/r/gnoland/blog:p/slug1)
body1
## [&#9656; title1](/r/gnoland/blog:p/slug1)
[tag1](/r/gnoland/blog:t/tag1) [tag2](/r/gnoland/blog:t/tag2)
by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 2009-02-13 11:31pm UTC
## [title2](/r/gnoland/blog:p/slug2)
body2
[tag1](/r/gnoland/blog:t/tag1) [tag3](/r/gnoland/blog:t/tag3)
by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 2009-02-13 11:31pm UTC
## [&#9656; title2](/r/gnoland/blog:p/slug2)
`
assertMDEquals(t, got, expected)
}
Expand All @@ -51,12 +41,11 @@ by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 2009-02-13 11:31pm UTC
{
got := Render("p/slug2")
expected := `
# Gnoland's Blog
# [Gnoland's Blog](/r/gnoland/blog:) / p / title2
## [title2](/r/gnoland/blog:p/slug2)
body2
[tag1](/r/gnoland/blog:t/tag1) [tag3](/r/gnoland/blog:t/tag3)
[#tag1](/r/gnoland/blog:t/tag1) [#tag3](/r/gnoland/blog:t/tag3)
by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 2009-02-13 11:31pm UTC
`
Expand All @@ -66,19 +55,14 @@ by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 2009-02-13 11:31pm UTC
// list by tags.
{
got := Render("t/invalid")
expected := "# Gnoland's Blog\n\nNo posts."
expected := "# [Gnoland's Blog](/r/gnoland/blog:) / t / invalid\n\nNo posts."
assertMDEquals(t, got, expected)

got = Render("t/tag2")
expected = `
# Gnoland's Blog
## [title1](/r/gnoland/blog:p/slug1)
body1
[tag1](/r/gnoland/blog:t/tag1) [tag2](/r/gnoland/blog:t/tag2)
# [Gnoland's Blog](/r/gnoland/blog:) / t / tag2
by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 2009-02-13 11:31pm UTC
## [&#9656; title1](/r/gnoland/blog:p/slug1)
`
assertMDEquals(t, got, expected)
}
Expand All @@ -92,12 +76,11 @@ by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 2009-02-13 11:31pm UTC
AddComment("slug1", "comment5")
got := Render("p/slug2")
expected := `
# Gnoland's Blog
# [Gnoland's Blog](/r/gnoland/blog:) / p / title2
## [title2](/r/gnoland/blog:p/slug2)
body2
[tag1](/r/gnoland/blog:t/tag1) [tag3](/r/gnoland/blog:t/tag3)
[#tag1](/r/gnoland/blog:t/tag1) [#tag3](/r/gnoland/blog:t/tag3)
by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 2009-02-13 11:31pm UTC
Expand All @@ -115,12 +98,11 @@ comment2
ModEditPost("slug2", "title2++", "body2++", "tag1,tag4")
got := Render("p/slug2")
expected := `
# Gnoland's Blog
# [Gnoland's Blog](/r/gnoland/blog:) / p / title2++
## [title2++](/r/gnoland/blog:p/slug2)
body2++
[tag1](/r/gnoland/blog:t/tag1) [tag4](/r/gnoland/blog:t/tag4)
[#tag1](/r/gnoland/blog:t/tag1) [#tag4](/r/gnoland/blog:t/tag4)
by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 2009-02-13 11:31pm UTC
Expand Down Expand Up @@ -150,7 +132,7 @@ comment2
}
for _, notFoundPath := range notFoundPaths {
got := Render(notFoundPath)
expected := "# Gnoland's Blog\n\n404"
expected := "404"
if got != expected {
t.Errorf("path %q: expected %q, got %q.", notFoundPath, expected, got)
}
Expand Down
2 changes: 2 additions & 0 deletions gnoland/genesis/genesis_txs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@
{"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","Simple echo example with coins","This is a simple test realm contract that demonstrates how to use the banker.\n\nSee [gno.land/r/demo/banktest/banktest.gno](/r/banktest/banktest.gno) to see the original contract code.\n\nThis article will go through each line to explain how it works.\n\n```go\npackage banktest\n```\n\nThis package is locally named \"banktest\" (could be anything).\n\n```go\nimport (\n\t\"std\"\n)\n```\n\nThe \"std\" package is defined by the gno code in stdlibs/std/. \u003c/br\u003e\nSelf explanatory; and you'll see more usage from std later.\n\n```go\ntype activity struct {\n\tcaller std.Address\n\tsent std.Coins\n\treturned std.Coins\n\ttime std.Time\n}\n\nfunc (act *activity) String() string {\n\treturn act.caller.String() + \" \" +\n\t\tact.sent.String() + \" sent, \" +\n\t\tact.returned.String() + \" returned, at \" +\n\t\tstd.FormatTimestamp(act.time, \"2006-01-02 3:04pm MST\")\n}\n\nvar latest [10]*activity\n```\n\nThis is just maintaining a list of recent activity to this contract.\nNotice that the \"latest\" variable is defined \"globally\" within\nthe context of the realm with path \"gno.land/r/demo/banktest\".\n\nThis means that calls to functions defined within this package\nare encapsulated within this \"data realm\", where the data is \nmutated based on transactions that can potentially cross many\nrealm and non-realm packge boundaries (in the call stack).\n\n```go\n// Deposit will take the coins (to the realm's pkgaddr) or return them to user.\nfunc Deposit(returnDenom string, returnAmount int64) string {\n\tstd.AssertOriginCall()\n\tcaller := std.GetOrigCaller()\n\tsend := std.Coins{{returnDenom, returnAmount}}\n```\n\nThis is the beginning of the definition of the contract function named\n\"Deposit\". `std.AssertOriginCall() asserts that this function was called by a\ngno transactional Message. The caller is the user who signed off on this\ntransactional message. Send is the amount of deposit sent along with this\nmessage.\n\n```go\n\t// record activity\n\tact := \u0026activity{\n\t\tcaller: caller,\n\t\tsent: std.GetOrigSend(),\n\t\treturned: send,\n\t\ttime: std.GetTimestamp(),\n\t}\n\tfor i := len(latest) - 2; i \u003e= 0; i-- {\n\t\tlatest[i+1] = latest[i] // shift by +1.\n\t}\n\tlatest[0] = act\n```\n\nUpdating the \"latest\" array for viewing at gno.land/r/demo/banktest: (w/ trailing colon).\n\n```go\n\t// return if any.\n\tif returnAmount \u003e 0 {\n```\n\nIf the user requested the return of coins...\n\n```go\n\t\tbanker := std.GetBanker(std.BankerTypeOrigSend)\n```\n\nuse a std.Banker instance to return any deposited coins to the original sender.\n\n```go\n\t\tpkgaddr := std.GetOrigPkgAddr()\n\t\t// TODO: use std.Coins constructors, this isn't generally safe.\n\t\tbanker.SendCoins(pkgaddr, caller, send)\n\t\treturn \"returned!\"\n```\n\nNotice that each realm package has an associated Cosmos address.\n\n\nFinally, the results are rendered via an ABCI query call when you visit [/r/banktest:](/r/banktest:).\n\n```go\nfunc Render(path string) string {\n\t// get realm coins.\n\tbanker := std.GetBanker(std.BankerTypeReadonly)\n\tcoins := banker.GetCoins(std.GetOrigPkgAddr())\n\n\t// render\n\tres := \"\"\n\tres += \"## recent activity\\n\"\n\tres += \"\\n\"\n\tfor _, act := range latest {\n\t\tif act == nil {\n\t\t\tbreak\n\t\t}\n\t\tres += \" * \" + act.String() + \"\\n\"\n\t}\n\tres += \"\\n\"\n\tres += \"## total deposits\\n\"\n\tres += coins.String()\n\treturn res\n}\n```\n\nYou can call this contract yourself, by vistiing [/r/banktest](/r/banktest) and the [quickstart guide](/r/boards:testboard/4).\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A+FhNtsXHjLfSJk1lB8FbiL4mGPjc50Kt81J7EKDnJ2y"},"signature":"iZX/llZlNTdZMLv1goCTgK2bWqzT8enlTq56wMTCpVxJGA0BTvuEM5Nnt9vrnlG6Taqj2GuTrmEnJBkDFTmt9g=="}],"memo":""}
{"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","TASK: Describe in your words","Describe in an essay (250+ words), on your favorite medium, why you are interested in gno.land and gnolang.\n\nReply here with a URL link to your written piece as a comment, for rewards.\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":"4HBNtrta8HdeHj4JTN56PBTRK8GOe31NMRRXDiyYtjozuyRdWfOGEsGjGgHWcoBUJq6DepBgD4FetdqfhZ6TNQ=="}],"memo":""}
{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","Getting Started","This is a demo of Gno smart contract programming. This document was\nconstructed by Gno onto a smart contract hosted on the data Realm\nname [\"gno.land/r/demo/boards\"](https://gno.land/r/demo/boards/)\n([github](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo/boards)).\n\n\n\n## Build `gnokey`, create your account, and interact with Gno.\n\nNOTE: Where you see `--remote %%REMOTE%%` here, that flag can be replaced\nwith `--remote localhost:26657` for local testnets.\n\n### Build `gnokey`.\n\n```bash\ngit clone [email protected]:gnolang/gno.git\ncd ./gno\nmake\n```\n\n### Generate a seed/mnemonic code.\n\n```bash\n./build/gnokey generate\n```\n\nNOTE: You can generate 24 words with any good bip39 generator.\n\n### Create a new account using your mnemonic.\n\n```bash\n./build/gnokey add KEYNAME --recover\n```\n\nNOTE: `KEYNAME` is your key identifier, and should be changed.\n\n### Verify that you can see your account locally.\n\n```bash\n./build/gnokey list\n```\n\n## Interact with the blockchain:\n\n### Get your current balance, account number, and sequence number.\n\n```bash\n./build/gnokey query auth/accounts/ACCOUNT_ADDR --remote %%REMOTE%%\n```\n\nNOTE: you can retrieve your `ACCOUNT_ADDR` with `./build/gnokey list`.\n\n### Acquire testnet tokens using the official faucet.\n\nGo to https://gno.land/faucet\n\n### Create a board with a smart contract call.\n\nNOTE: `BOARDNAME` will be the slug of the board, and should be changed.\n\n```bash\n./build/gnokey maketx call KEYNAME --pkgpath \"gno.land/r/demo/boards\" --func \"CreateBoard\" --args \"BOARDNAME\" --gas-fee \"1000000ugnot\" --gas-wanted \"2000000\" --broadcast true --chainid %%CHAINID%% --remote %%REMOTE%%\n```\n\nInteractive documentation: https://gno.land/r/demo/boards?help\u0026__func=CreateBoard\n\nNext, query for the permanent board ID by querying (you need this to create a new post):\n\n```bash\n./build/gnokey query \"vm/qeval\" --data \"gno.land/r/demo/boards\nGetBoardIDFromName(\\\"BOARDNAME\\\")\" --remote %%REMOTE%%\n```\n\n### Create a post of a board with a smart contract call.\n\nNOTE: If a board was created successfully, your SEQUENCE_NUMBER would have increased.\n\n```bash\n./build/gnokey maketx call KEYNAME --pkgpath \"gno.land/r/demo/boards\" --func \"CreateThread\" --args BOARD_ID --args \"Hello gno.land\" --args\\#file \"./examples/gno.land/r/demo/boards/example_post.md\" --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast true --chainid %%CHAINID%% --remote %%REMOTE%%\n```\n\nInteractive documentation: https://gno.land/r/demo/boards?help\u0026__func=CreateThread\n\n### Create a comment to a post.\n\n```bash\n./build/gnokey maketx call KEYNAME --pkgpath \"gno.land/r/demo/boards\" --func \"CreateReply\" --args \"BOARD_ID\" --args \"1\" --args \"1\" --args \"Nice to meet you too.\" --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast true --chainid %%CHAINID%% --remote %%REMOTE%%\n```\n\nInteractive documentation: https://gno.land/r/demo/boards?help\u0026__func=CreateReply\n\n```bash\n./build/gnokey query \"vm/qrender\" --data \"gno.land/r/demo/boards\nBOARDNAME/1\" --remote %%REMOTE%%\n```\n\n### Render page with optional path expression.\n\nThe contents of `https://gno.land/r/demo/boards:` and `https://gno.land/r/demo/boards:gnolang` are rendered by calling\nthe `Render(path string)` function like so:\n\n```bash\n./build/gnokey query \"vm/qrender\" --data \"gno.land/r/demo/boards\ngnolang\"\n```\n\n## Starting a local `gnoland` node:\n\n### Add test account.\n\n```bash\n./build/gnokey add test1 --recover\n```\n\nUse this mneonic:\n\u003e source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast\n\n### Start `gnoland` node.\n\n```bash\n./build/gnoland\n```\n\nNOTE: This can be reset with `make reset`\n\n### Publish the \"gno.land/p/demo/avl\" package.\n\n```bash\n./build/gnokey maketx addpkg test1 --pkgpath \"gno.land/p/demo/avl\" --pkgdir \"examples/gno.land/p/demo/avl\" --deposit 100000000ugnot --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast true --chainid %%CHAINID%% --remote localhost:26657\n```\n\n### Publish the \"gno.land/r/demo/boards\" realm package.\n\n```bash\n./build/gnokey maketx addpkg test1 --pkgpath \"gno.land/r/demo/boards\" --pkgdir \"examples/gno.land/r/demo/boards\" --deposit 100000000ugnot --gas-fee 1000000ugnot --gas-wanted 300000000 --broadcast true --chainid %%CHAINID%% --remote localhost:26657\n```\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"sHjOGXZEi9wt2FSXFHmkDDoVQyepvFHKRDDU0zgedHYnCYPx5/YndyihsDD5Y2Z7/RgNYBh4JlJwDMGFNStzBQ=="}],"memo":""}
{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/gnoland/blog","func":"ModAddPost","args":["post1","First post","Lorem Ipsum", "tag1,tag2"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"sHjOGXZEi9wt2FSXFHmkDDoVQyepvFHKRDDU0zgedHYnCYPx5/YndyihsDD5Y2Z7/RgNYBh4JlJwDMGFNStzBQ=="}],"memo":""}
{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/gnoland/blog","func":"ModAddPost","args":["post2","Second post","Lorem Ipsum", "tag1,tag3"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"sHjOGXZEi9wt2FSXFHmkDDoVQyepvFHKRDDU0zgedHYnCYPx5/YndyihsDD5Y2Z7/RgNYBh4JlJwDMGFNStzBQ=="}],"memo":""}

0 comments on commit 32a040d

Please sign in to comment.