Attempting to Learn Go - Ghost to Hugo 4
Tags: go, ghost, hugo
Published: 2022-10-01 20:58:42

---
title: Attempting to Learn Go - Ghost to Hugo 4
published: true
date: 2022-10-01 20:54:29 UTC
tags: go, ghost, hugo
canonical_url: https://shindakun.dev/posts/attempting-to-learn-go-ghost-to-hugo-4/
cover_image: https://res.cloudinary.com/practicaldev/image/fetch/s--8gMQgnUg--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev.shindakun.net/img/attemptinggo_w.png
---

## Intro

Here we are [once again](https://dev.to/shindakun/attempting-to-learn-go-ghost-to-hugo-3-31p4)! If you missed the last couple of posts I suggest reading them first to get a feeling for how our little prototype has evolved. This time around we’re going to be doing some refactoring to clean up a bit. The majority of what we’ll be doing is adding methods to our “database” struct.

### The Method

What is a method? Well, you can think of it as a function that is attached to a struct. Or as [A Tour of Go](https://go.dev/tour/methods/1) puts it

> A method is a function with a special _receiver_ argument.

In our code we have a struct `GhostDatabase`. We can add methods with the following signature where the receiver is our struct.

```go
func (gd *GhostDatabase) getLength() int
```

We could then call the method with the following

```go
var db GhostDatabase

...load JSON code here...

length := db.getLength()
```

OK so what’s our complete method look like? In the case of most of the methods we’ll be putting in place is pretty easy to reason about.

```go
func (gd *GhostDatabase) getLength() int {
	return len(gd.Db[0].Data.Posts)
}
```

### Many Methods

Now lets quickly knock out some useful methods. For the most part they do what they say on the tin, so I won’t go into detail on each one.

```go
func (gd *GhostDatabase) getMobiledoc(i int) string {
	return gd.Db[0].Data.Posts[i].Mobiledoc
}

func (gd *GhostDatabase) getPostId(i int) string {
	return gd.Db[0].Data.Posts[i].ID
}

func (gd *GhostDatabase) getPostTitle(i int) string {
	return gd.Db[0].Data.Posts[i].Title
}

func (gd *GhostDatabase) getPostSlug(i int) string {
	return gd.Db[0].Data.Posts[i].Slug
}

func (gd *GhostDatabase) getPostStatus(i int) string {
	return gd.Db[0].Data.Posts[i].Status
}

func (gd *GhostDatabase) getPostCreatedAt(i int) time.Time {
	return gd.Db[0].Data.Posts[i].CreatedAt
}

func (gd *GhostDatabase) getPostUpdatedAt(i int) time.Time {
	return gd.Db[0].Data.Posts[i].UpdatedAt
}

func (gd *GhostDatabase) getPostPublishedAt(i int) time.Time {
	return gd.Db[0].Data.Posts[i].PublishedAt
}

func (gd *GhostDatabase) getPostFeatureImage(i int) string {
	return gd.Db[0].Data.Posts[i].FeatureImage
}
```

### Tags

One thing that we haven’t looked into so far is retrieving the tags associated with a post. Since we’re working on adding methods now is a great time to work that out. Let’s talk through what we need to do.

First we need to loop through the “database” of post tags. From here we check to see if the `PostID` field matches with the post ID, `pid`, we pass into the function. We then assign the tag ID to a variable, `tagId`.

Next we’ll loop our way though the `Tags` “database” and check to see if the current tag id matches with `tagId`. If there is a match we append the tag name to a slice `r`. Once we’re done we return `r`.

Let’s see what that looks like in practice.

```go
func (gd *GhostDatabase) getTags(pid string) []string {
	var r []string
	for i := 0; i < len(gd.Db[0].Data.PostsTags); i++ {
		if gd.Db[0].Data.PostsTags[i].PostID == pid {
			tagId := gd.Db[0].Data.PostsTags[i].TagID
			for j := 0; j < len(gd.Db[0].Data.Tags); j++ {
				if gd.Db[0].Data.Tags[j].ID == tagId {
					r = append(r, gd.Db[0].Data.Tags[j].Name)
				}
			}
		}
	}
	return r
}
```

Not to shabby.

### Next Time

That’s all of our methods for now. With them out of the way I believe we have all the parts need to actually start writing our Markdown files!

***

| Enjoy this post? |
| --- |
| [How about buying me a coffee?](https://ko-fi.com/shindakun) |

{% github shindakun/atlg %}

***

### Code Listing

```go
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type GhostDatabase struct {
	Db []struct {
		Meta struct {
			ExportedOn int64 `json:"exported_on"`
			Version string `json:"version"`
		} `json:"meta"`
		Data struct {
			Posts []struct {
				ID string `json:"id"`
				UUID string `json:"uuid"`
				Title string `json:"title"`
				Slug string `json:"slug"`
				Mobiledoc string `json:"mobiledoc"`
				HTML string `json:"html"`
				CommentID string `json:"comment_id"`
				Plaintext string `json:"plaintext"`
				FeatureImage string `json:"feature_image"`
				Featured int `json:"featured"`
				Type string `json:"type"`
				Status string `json:"status"`
				Locale interface{} `json:"locale"`
				Visibility string `json:"visibility"`
				EmailRecipientFilter string `json:"email_recipient_filter"`
				AuthorID string `json:"author_id"`
				CreatedAt time.Time `json:"created_at"`
				UpdatedAt time.Time `json:"updated_at"`
				PublishedAt time.Time `json:"published_at"`
				CustomExcerpt interface{} `json:"custom_excerpt"`
				CodeinjectionHead interface{} `json:"codeinjection_head"`
				CodeinjectionFoot interface{} `json:"codeinjection_foot"`
				CustomTemplate interface{} `json:"custom_template"`
				CanonicalURL interface{} `json:"canonical_url"`
			} `json:"posts"`
			PostsAuthors []struct {
				ID string `json:"id"`
				PostID string `json:"post_id"`
				AuthorID string `json:"author_id"`
				SortOrder int `json:"sort_order"`
			} `json:"posts_authors"`
			PostsMeta []interface{} `json:"posts_meta"`
			PostsTags []struct {
				ID string `json:"id"`
				PostID string `json:"post_id"`
				TagID string `json:"tag_id"`
				SortOrder int `json:"sort_order"`
			} `json:"posts_tags"`
			Roles []struct {
				ID string `json:"id"`
				Name string `json:"name"`
				Description string `json:"description"`
				CreatedAt time.Time `json:"created_at"`
				UpdatedAt time.Time `json:"updated_at"`
			} `json:"roles"`
			RolesUsers []struct {
				ID string `json:"id"`
				RoleID string `json:"role_id"`
				UserID string `json:"user_id"`
			} `json:"roles_users"`
			Settings []struct {
				ID string `json:"id"`
				Group string `json:"group"`
				Key string `json:"key"`
				Value string `json:"value"`
				Type string `json:"type"`
				Flags interface{} `json:"flags"`
				CreatedAt time.Time `json:"created_at"`
				UpdatedAt time.Time `json:"updated_at"`
			} `json:"settings"`
			Tags []struct {
				ID string `json:"id"`
				Name string `json:"name"`
				Slug string `json:"slug"`
				Description interface{} `json:"description"`
				FeatureImage interface{} `json:"feature_image"`
				ParentID interface{} `json:"parent_id"`
				Visibility string `json:"visibility"`
				OgImage interface{} `json:"og_image"`
				OgTitle interface{} `json:"og_title"`
				OgDescription interface{} `json:"og_description"`
				TwitterImage interface{} `json:"twitter_image"`
				TwitterTitle interface{} `json:"twitter_title"`
				TwitterDescription interface{} `json:"twitter_description"`
				MetaTitle interface{} `json:"meta_title"`
				MetaDescription interface{} `json:"meta_description"`
				CodeinjectionHead interface{} `json:"codeinjection_head"`
				CodeinjectionFoot interface{} `json:"codeinjection_foot"`
				CanonicalURL interface{} `json:"canonical_url"`
				AccentColor interface{} `json:"accent_color"`
				CreatedAt time.Time `json:"created_at"`
				UpdatedAt time.Time `json:"updated_at"`
			} `json:"tags"`
			Users []struct {
				ID string `json:"id"`
				Name string `json:"name"`
				Slug string `json:"slug"`
				Password string `json:"password"`
				Email string `json:"email"`
				ProfileImage string `json:"profile_image"`
				CoverImage interface{} `json:"cover_image"`
				Bio interface{} `json:"bio"`
				Website interface{} `json:"website"`
				Location interface{} `json:"location"`
				Facebook interface{} `json:"facebook"`
				Twitter interface{} `json:"twitter"`
				Accessibility string `json:"accessibility"`
				Status string `json:"status"`
				Locale interface{} `json:"locale"`
				Visibility string `json:"visibility"`
				MetaTitle interface{} `json:"meta_title"`
				MetaDescription interface{} `json:"meta_description"`
				Tour interface{} `json:"tour"`
				LastSeen time.Time `json:"last_seen"`
				CreatedAt time.Time `json:"created_at"`
				UpdatedAt time.Time `json:"updated_at"`
			} `json:"users"`
		} `json:"data"`
	} `json:"db"`
}

type Mobiledoc struct {
	Version string `json:"version"`
	Markups []interface{} `json:"markups"`
	Atoms []interface{} `json:"atoms"`
	Cards [][]interface{} `json:"cards"`
	Sections [][]interface{} `json:"sections"`
	GhostVersion string `json:"ghostVersion"`
}

func (gd *GhostDatabase) getLength() int {
	return len(gd.Db[0].Data.Posts)
}

func (gd *GhostDatabase) getMobiledoc(i int) string {
	return gd.Db[0].Data.Posts[i].Mobiledoc
}

func (gd *GhostDatabase) getPostId(i int) string {
	return gd.Db[0].Data.Posts[i].ID
}

func (gd *GhostDatabase) getPostTitle(i int) string {
	return gd.Db[0].Data.Posts[i].Title
}

func (gd *GhostDatabase) getPostSlug(i int) string {
	return gd.Db[0].Data.Posts[i].Slug
}

func (gd *GhostDatabase) getPostStatus(i int) string {
	return gd.Db[0].Data.Posts[i].Status
}

func (gd *GhostDatabase) getPostCreatedAt(i int) time.Time {
	return gd.Db[0].Data.Posts[i].CreatedAt
}

func (gd *GhostDatabase) getPostUpdatedAt(i int) time.Time {
	return gd.Db[0].Data.Posts[i].UpdatedAt
}

func (gd *GhostDatabase) getPostPublishedAt(i int) time.Time {
	return gd.Db[0].Data.Posts[i].PublishedAt
}

func (gd *GhostDatabase) getPostFeatureImage(i int) string {
	return gd.Db[0].Data.Posts[i].FeatureImage
}

func (gd *GhostDatabase) getTags(pid string) []string {
	var r []string
	for i := 0; i < len(gd.Db[0].Data.PostsTags); i++ {
		if gd.Db[0].Data.PostsTags[i].PostID == pid {
			tagId := gd.Db[0].Data.PostsTags[i].TagID
			for j := 0; j < len(gd.Db[0].Data.Tags); j++ {
				if gd.Db[0].Data.Tags[j].ID == tagId {
					r = append(r, gd.Db[0].Data.Tags[j].Name)
				}
			}
		}
	}
	return r
}

func main() {
	fmt.Println("ghost2hugo")

	file, err := os.Open("shindakun-dot-net.ghost.2022-03-18-22-02-58.json")
	if err != nil {
		fmt.Println(err)
	}

	defer file.Close()

	b, err := io.ReadAll(file)
	if err != nil {
		fmt.Println(err)
	}

	var db GhostDatabase

	err = json.Unmarshal(b, &db)
	if err != nil {
		fmt.Println(err)
	}

	for i := 0; i < db.getLength(); i++ {
		fmt.Println(db.getPostTitle(i))
		fmt.Println(db.getPostSlug(i))
		fmt.Println(db.getPostStatus(i))
		fmt.Println(db.getPostCreatedAt(i))
		fmt.Println(db.getPostUpdatedAt(i))
		fmt.Println(db.getPostPublishedAt(i))
		fmt.Println(db.getPostFeatureImage(i))

		id := db.getPostId(i)
		tags := db.getTags(id)
		fmt.Println(tags)

		c := strings.ReplaceAll(db.getMobiledoc(i), "`", "%'")
		cc := "`" + c + "`"

		ucn, err := strconv.Unquote(cc)
		if err != nil {
			fmt.Println(err, ucn)
		}

		ucn = strings.ReplaceAll(ucn, "%'", "`")

		var md Mobiledoc

		err = json.Unmarshal([]byte(ucn), &md)
		if err != nil {
			fmt.Println(err)
		}

		if reflect.ValueOf(md.Cards).Len() > 0 {
			card := md.Cards[0][1]
			bbb := card.(map[string]interface{})

			fmt.Println(bbb["markdown"])
		}

		fmt.Println("---")
	}
}
```