Page MenuHomeMusing Studio

No OneTemporary

diff --git a/category/category.go b/category/category.go
new file mode 100644
index 0000000..f60dc4b
--- /dev/null
+++ b/category/category.go
@@ -0,0 +1,30 @@
+// Package category supports post categories
+package category
+
+import (
+ "errors"
+ "github.com/writeas/slug"
+)
+
+var (
+ ErrNotFound = errors.New("category doesn't exist")
+)
+
+// Category represents a post tag with additional metadata, like a title and slug.
+type Category struct {
+ ID int64 `json:"-"`
+ Hashtag string `json:"hashtag"`
+ Slug string `json:"slug"`
+ Title string `json:"title"`
+}
+
+// NewCategory creates a Category you can insert into the database, based on a hashtag. It automatically breaks up the
+// hashtag by words, based on capitalization, for both the title and a URL-friendly slug.
+func NewCategory(hashtag string) *Category {
+ title := titleFromHashtag(hashtag)
+ return &Category{
+ Hashtag: hashtag,
+ Slug: slug.Make(title),
+ Title: title,
+ }
+}
diff --git a/category/tags.go b/category/tags.go
new file mode 100644
index 0000000..9b99248
--- /dev/null
+++ b/category/tags.go
@@ -0,0 +1,26 @@
+package category
+
+import (
+ "strings"
+ "unicode"
+)
+
+// titleFromHashtag generates an all-lowercase title, with spaces inserted based on initial capitalization -- e.g.
+// "MyWordyTag" becomes "my wordy tag".
+func titleFromHashtag(hashtag string) string {
+ var t strings.Builder
+ var prev rune
+ for i, c := range hashtag {
+ if unicode.IsUpper(c) {
+ if i > 0 && !unicode.IsUpper(prev) {
+ // Insert space if previous rune wasn't also uppercase (e.g. an abbreviation)
+ t.WriteRune(' ')
+ }
+ t.WriteRune(unicode.ToLower(c))
+ } else {
+ t.WriteRune(c)
+ }
+ prev = c
+ }
+ return t.String()
+}
diff --git a/category/tags_test.go b/category/tags_test.go
new file mode 100644
index 0000000..9889cfc
--- /dev/null
+++ b/category/tags_test.go
@@ -0,0 +1,31 @@
+package category
+
+import "testing"
+
+func TestTitleFromHashtag(t *testing.T) {
+ tests := []struct {
+ name string
+ hashtag string
+ expTitle string
+ }{
+ {"proper noun", "Jane", "jane"},
+ {"full name", "JaneDoe", "jane doe"},
+ {"us words", "unitedStates", "united states"},
+ {"usa", "USA", "usa"},
+ {"us monoword", "unitedstates", "unitedstates"},
+ {"100dto", "100DaysToOffload", "100 days to offload"},
+ {"iphone", "iPhone", "iphone"},
+ {"ilike", "iLikeThis", "i like this"},
+ {"abird", "aBird", "a bird"},
+ {"all caps", "URGENT", "urgent"},
+ {"smartphone", "スマートフォン", "スマートフォン"},
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ res := titleFromHashtag(test.hashtag)
+ if res != test.expTitle {
+ t.Fatalf("#%s: got '%s' expected '%s'", test.hashtag, res, test.expTitle)
+ }
+ })
+ }
+}
diff --git a/go.mod b/go.mod
index a8769ef..5170100 100644
--- a/go.mod
+++ b/go.mod
@@ -1,17 +1,19 @@
module github.com/writeas/web-core
go 1.10
require (
github.com/gofrs/uuid v3.3.0+incompatible
github.com/kylemcc/twitter-text-go v0.0.0-20180726194232-7f582f6736ec
github.com/microcosm-cc/bluemonday v1.0.5
+ github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/writeas/go-strip-markdown v2.0.1+incompatible
github.com/writeas/impart v1.1.1
github.com/writeas/openssl-go v1.0.0
github.com/writeas/saturday v1.7.1
+ github.com/writeas/slug v1.2.0
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 // indirect
)
diff --git a/go.sum b/go.sum
index 9780f16..e02806f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,40 +1,44 @@
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylemcc/twitter-text-go v0.0.0-20180726194232-7f582f6736ec h1:ZXWuspqypleMuJy4bzYEqlMhJnGAYpLrWe5p7W3CdvI=
github.com/kylemcc/twitter-text-go v0.0.0-20180726194232-7f582f6736ec/go.mod h1:voECJzdraJmolzPBgL9Z7ANwXf4oMXaTCsIkdiPpR/g=
github.com/microcosm-cc/bluemonday v1.0.5 h1:cF59UCKMmmUgqN1baLvqU/B1ZsMori+duLVTLpgiG3w=
github.com/microcosm-cc/bluemonday v1.0.5/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
+github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
+github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE=
github.com/writeas/impart v1.1.1 h1:RyA9+CqbdbDuz53k+nXCWUY+NlEkdyw6+nWanxSBl5o=
github.com/writeas/impart v1.1.1/go.mod h1:g0MpxdnTOHHrl+Ca/2oMXUHJ0PcRAEWtkCzYCJUXC9Y=
github.com/writeas/openssl-go v1.0.0 h1:YXM1tDXeYOlTyJjoMlYLQH1xOloUimSR1WMF8kjFc5o=
github.com/writeas/openssl-go v1.0.0/go.mod h1:WsKeK5jYl0B5y8ggOmtVjbmb+3rEGqSD25TppjJnETA=
github.com/writeas/saturday v1.7.1 h1:lYo1EH6CYyrFObQoA9RNWHVlpZA5iYL5Opxo7PYAnZE=
github.com/writeas/saturday v1.7.1/go.mod h1:ETE1EK6ogxptJpAgUbcJD0prAtX48bSloie80+tvnzQ=
+github.com/writeas/slug v1.2.0 h1:EMQ+cwLiOcA6EtFwUgyw3Ge18x9uflUnOnR6bp/J+/g=
+github.com/writeas/slug v1.2.0/go.mod h1:RE8shOqQP3YhsfsQe0L3RnuejfQ4Mk+JjY5YJQFubfQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0=
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 h1:POO/ycCATvegFmVuPpQzZFJ+pGZeX22Ufu6fibxDVjU=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=

File Metadata

Mime Type
text/x-diff
Expires
Thu, Dec 4, 10:33 PM (1 d, 10 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3524829

Event Timeline