Idiomatic Go

master
Michael Dvorkin 10 years ago
parent 27ebd8dfa4
commit 51b6250fcf
  1. 10
      cmd/mop.go
  2. 16
      column_editor.go
  3. 36
      layout.go
  4. 14
      line_editor.go
  5. 7
      markup.go
  6. 10
      sorter.go
  7. 19
      yahoo_quotes.go

@ -10,7 +10,7 @@ import (
`time` `time`
) )
const help = `Mop v0.1.0 -- Copyright (c) 2013 Michael Dvorkin. All Rights Reserved. const help = `Mop v0.2.0 -- Copyright (c) 2013-15 Michael Dvorkin. All Rights Reserved.
NO WARRANTIES OF ANY KIND WHATSOEVER. SEE THE LICENSE FILE FOR DETAILS. NO WARRANTIES OF ANY KIND WHATSOEVER. SEE THE LICENSE FILE FOR DETAILS.
<u>Command</u> <u>Description </u> <u>Command</u> <u>Description </u>
@ -46,8 +46,8 @@ func mainLoop(screen *mop.Screen, profile *mop.Profile) {
} }
}() }()
market := new(mop.Market).Initialize() market := mop.NewMarket()
quotes := new(mop.Quotes).Initialize(market, profile) quotes := mop.NewQuotes(market, profile)
screen.Draw(market, quotes) screen.Draw(market, quotes)
loop: loop:
@ -60,10 +60,10 @@ loop:
if event.Key == termbox.KeyEsc || event.Ch == 'q' || event.Ch == 'Q' { if event.Key == termbox.KeyEsc || event.Ch == 'q' || event.Ch == 'Q' {
break loop break loop
} else if event.Ch == '+' || event.Ch == '-' { } else if event.Ch == '+' || event.Ch == '-' {
lineEditor = new(mop.LineEditor).Initialize(screen, quotes) lineEditor = mop.NewLineEditor(screen, quotes)
lineEditor.Prompt(event.Ch) lineEditor.Prompt(event.Ch)
} else if event.Ch == 'o' || event.Ch == 'O' { } else if event.Ch == 'o' || event.Ch == 'O' {
columnEditor = new(mop.ColumnEditor).Initialize(screen, quotes) columnEditor = mop.NewColumnEditor(screen, quotes)
} else if event.Ch == 'g' || event.Ch == 'G' { } else if event.Ch == 'g' || event.Ch == 'G' {
if profile.Regroup() == nil { if profile.Regroup() == nil {
screen.Draw(quotes) screen.Draw(quotes)

@ -16,13 +16,15 @@ type ColumnEditor struct {
profile *Profile // Pointer to Profile where we save newly selected sort order. profile *Profile // Pointer to Profile where we save newly selected sort order.
} }
// Initialize sets internal variables and highlights current column name // Returns new initialized ColumnEditor struct. As part of initialization it
// (as stored in Profile). // highlights current column name (as stored in Profile).
func (editor *ColumnEditor) Initialize(screen *Screen, quotes *Quotes) *ColumnEditor { func NewColumnEditor(screen *Screen, quotes *Quotes) *ColumnEditor {
editor.screen = screen editor := &ColumnEditor{
editor.quotes = quotes screen: screen,
editor.layout = screen.layout quotes: quotes,
editor.profile = quotes.profile layout: screen.layout,
profile: quotes.profile,
}
editor.selectCurrentColumn() editor.selectCurrentColumn()

@ -33,9 +33,9 @@ type Layout struct {
quotesTemplate *template.Template // Pointer to template to format the list of stock quotes. quotesTemplate *template.Template // Pointer to template to format the list of stock quotes.
} }
// Initialize assigns the default values that stay unchanged for the life of // Creates the layout and assigns the default values that stay unchanged.
// allocated Layout struct. func NewLayout() *Layout {
func (layout *Layout) Initialize() *Layout { layout := &Layout{}
layout.columns = []Column{ layout.columns = []Column{
{ -7, `Ticker`, `Ticker`, nil }, { -7, `Ticker`, `Ticker`, nil },
{ 10, `LastTrade`, `Last`, currency }, { 10, `LastTrade`, `Last`, currency },
@ -67,8 +67,9 @@ func (layout *Layout) Market(market *Market) string {
return err // then simply return the error string. return err // then simply return the error string.
} }
highlight(market.Dow, market.Sp500, market.Nasdaq, market.London, market.Frankfurt, highlight(market.Dow, market.Sp500, market.Nasdaq,
market.Paris, market.Tokyo, market.HongKong, market.Shanghai) market.Tokyo, market.HongKong, market.London, market.Frankfurt,
market.Yield, market.Oil, market.Euro, market.Gold)
buffer := new(bytes.Buffer) buffer := new(bytes.Buffer)
layout.marketTemplate.Execute(buffer, market) layout.marketTemplate.Execute(buffer, market)
@ -152,7 +153,7 @@ func (layout *Layout) prettify(quotes *Quotes) []Stock {
profile := quotes.profile profile := quotes.profile
if layout.sorter == nil { // Initialize sorter on first invocation. if layout.sorter == nil { // Initialize sorter on first invocation.
layout.sorter = new(Sorter).Initialize(profile) layout.sorter = NewSorter(profile)
} }
layout.sorter.SortByCurrentColumn(pretty) layout.sorter.SortByCurrentColumn(pretty)
// //
@ -183,9 +184,9 @@ func (layout *Layout) pad(str string, width int) string {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func buildMarketTemplate() *template.Template { func buildMarketTemplate() *template.Template {
markup := `<yellow>{{.Dow.name}}</> {{.Dow.change}} ({{.Dow.percent}}) at {{.Dow.latest}} <yellow>{{.Sp500.name}}</> {{.Sp500.change}} ({{.Sp500.percent}}) at {{.Sp500.latest}} <yellow>{{.Nasdaq.name}}</> {{.Nasdaq.change}} ({{.Nasdaq.percent}}) at {{.Nasdaq.latest}} markup := `<yellow>Dow</> {{.Dow.change}} ({{.Dow.percent}}) at {{.Dow.latest}} <yellow>S&P 500</> {{.Sp500.change}} ({{.Sp500.percent}}) at {{.Sp500.latest}} <yellow>NASDAQ</> {{.Nasdaq.change}} ({{.Nasdaq.percent}}) at {{.Nasdaq.latest}}
<yellow>{{.London.name}}</> {{.London.change}} ({{.London.percent}}) at {{.London.latest}} <yellow>{{.Frankfurt.name}}</> {{.Frankfurt.change}} ({{.Frankfurt.percent}}) at {{.Frankfurt.latest}} <yellow>{{.Paris.name}}</> {{.Paris.change}} ({{.Paris.percent}}) at {{.Paris.latest}} {{if .IsClosed}}<right>U.S. markets closed</right>{{end}} <yellow>Tokyo</> {{.Tokyo.change}} ({{.Tokyo.percent}}) at {{.Tokyo.latest}} <yellow>HK</> {{.HongKong.change}} ({{.HongKong.percent}}) at {{.HongKong.latest}} <yellow>London</> {{.London.change}} ({{.London.percent}}) at {{.London.latest}} <yellow>Frankfurt</> {{.Frankfurt.change}} ({{.Frankfurt.percent}}) at {{.Frankfurt.latest}} {{if .IsClosed}}<right>U.S. markets closed</right>{{end}}
<yellow>{{.Tokyo.name}}</> {{.Tokyo.change}} ({{.Tokyo.percent}}) at {{.Tokyo.latest}} <yellow>{{.HongKong.name}}</> {{.HongKong.change}} ({{.HongKong.percent}}) at {{.HongKong.latest}} <yellow>{{.Shanghai.name}}</> {{.Shanghai.change}} ({{.Shanghai.percent}}) at {{.Shanghai.latest}}` <yellow>10-Year Yield</> {{.Yield.latest}}% ({{.Yield.change}}) <yellow>Euro</> ${{.Euro.latest}} ({{.Euro.change}}%) <yellow>Yen</> ¥{{.Yen.latest}} ({{.Yen.change}}%) <yellow>Oil</> ${{.Oil.latest}} ({{.Oil.change}}%) <yellow>Gold</> ${{.Gold.latest}} ({{.Gold.change}}%)`
return template.Must(template.New(`market`).Parse(markup)) return template.Must(template.New(`market`).Parse(markup))
} }
@ -267,7 +268,8 @@ func last(str string) string {
if len(str) >= 6 && str[0:6] == `N/A - ` { if len(str) >= 6 && str[0:6] == `N/A - ` {
return str[6:] return str[6:]
} }
return str
return percent(str)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -282,11 +284,23 @@ func currency(str string) string {
return `$` + str return `$` + str
} }
// Returns percent value truncated at 2 decimal points.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func percent(str string) string { func percent(str string) string {
if str == `N/A` { if str == `N/A` {
return `-` return `-`
} }
return str + `%` split := strings.Split(str, ".")
if len(split) == 2 {
digits := len(split[1])
if digits > 2 {
digits = 2
}
str = split[0] + "." + split[1][0:digits]
}
if str[len(str)-1] != '%' {
str += `%`
}
return str
} }

@ -24,13 +24,13 @@ type LineEditor struct {
regex *regexp.Regexp // Regex to split comma-delimited input string. regex *regexp.Regexp // Regex to split comma-delimited input string.
} }
// Initialize sets internal pointers and compiles the regular expression. // Returns new initialized LineEditor struct.
func (editor *LineEditor) Initialize(screen *Screen, quotes *Quotes) *LineEditor { func NewLineEditor(screen *Screen, quotes *Quotes) *LineEditor {
editor.screen = screen return &LineEditor{
editor.quotes = quotes screen: screen,
editor.regex = regexp.MustCompile(`[,\s]+`) quotes: quotes,
regex: regexp.MustCompile(`[,\s]+`),
return editor }
} }
// Prompt displays a prompt in response to '+' or '-' commands. Unknown commands // Prompt displays a prompt in response to '+' or '-' commands. Unknown commands

@ -31,9 +31,10 @@ type Markup struct {
regex *regexp.Regexp // Regex to identify the supported tag names. regex *regexp.Regexp // Regex to identify the supported tag names.
} }
// Initialize creates tags to Termbox translation hash and sets default // Creates markup to define tag to Termbox translation rules and store default
// colors and string alignment. // colors and column alignments.
func (markup *Markup) Initialize() *Markup { func NewMarkup() *Markup {
markup := &Markup{}
markup.Foreground = termbox.ColorDefault markup.Foreground = termbox.ColorDefault
markup.Background = termbox.ColorDefault markup.Background = termbox.ColorDefault
markup.RightAligned = false markup.RightAligned = false

@ -85,11 +85,11 @@ func (list byDividendDesc) Less(i, j int) bool { return list.sortable[j].Divi
func (list byYieldDesc) Less(i, j int) bool { return list.sortable[j].Yield < list.sortable[i].Yield } func (list byYieldDesc) Less(i, j int) bool { return list.sortable[j].Yield < list.sortable[i].Yield }
func (list byMarketCapDesc) Less(i, j int) bool { return m(list.sortable[j].MarketCap) < m(list.sortable[i].MarketCap) } func (list byMarketCapDesc) Less(i, j int) bool { return m(list.sortable[j].MarketCap) < m(list.sortable[i].MarketCap) }
// Initialize simply saves the pointer to Profile for later use. // Returns new Sorter struct.
func (sorter *Sorter) Initialize(profile *Profile) *Sorter { func NewSorter(profile *Profile) *Sorter {
sorter.profile = profile return &Sorter{
profile: profile,
return sorter }
} }
// SortByCurrentColumn builds a list of sort interface based on current sort // SortByCurrentColumn builds a list of sort interface based on current sort

@ -15,10 +15,6 @@ import (
// See http://www.gummy-stuff.org/Yahoo-stocks.htm // See http://www.gummy-stuff.org/Yahoo-stocks.htm
// //
// Also http://query.yahooapis.com/v1/public/yql
// ?q=select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in(%22ALU%22,%22AAPL%22)
// &env=http%3A%2F%2Fstockstables.org%2Falltables.env&format=json'
// const quotesURL = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=sl1c6k2oghjkva2r2rdyj3j1` // const quotesURL = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=sl1c6k2oghjkva2r2rdyj3j1`
// c2: realtime change vs c1: change // c2: realtime change vs c1: change
// k2: realtime change vs p2: change // k2: realtime change vs p2: change
@ -57,14 +53,13 @@ type Quotes struct {
errors string // Error string if any. errors string // Error string if any.
} }
// Initialize sets the initial values for the Quotes struct. It returns "self" // Sets the initial values and returns new Quotes struct.
// so that the next function call could be chained. func NewQuotes(market *Market, profile *Profile) *Quotes {
func (quotes *Quotes) Initialize(market *Market, profile *Profile) *Quotes { return &Quotes{
quotes.market = market market: market,
quotes.profile = profile profile: profile,
quotes.errors = `` errors: ``,
}
return quotes
} }
// Fetch the latest stock quotes and parse raw fetched data into array of // Fetch the latest stock quotes and parse raw fetched data into array of

Loading…
Cancel
Save