5 redditnews.go (Third Iteration)
So far, all the action happens in the main function. As the program grows, structure and modularity become important. What if we want to check several subreddits?
Create a function named Get that takes the name of subreddit, makes the API call, and returns the items from that subreddit.
func Get(reddit string) ([]Item, error) {}
The function Get takes an argument called reddit that is a string. It also returns an []Item slice and an error value (also a string.)
Here’s the code for our Get function:
func Get(reddit string) ([]Item, error) {
url := fmt.Sprintf("http://reddit.com/r/%s.json", reddit)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.New(resp.Status)
}
r := new(Response)
err = json.NewDecoder(resp.Body).Decode(r)
if err != nil {
return nil, err
}
items := make([]Item, len(r.Data1.Children))
for i, child := range r.Data1.Children {
items[i] = child.Data2
}
return items, nil
}
- Our new
Getfunction allows us to call the Reddit API request from anywhere in our program, instead of only from ourmain()function. - When we call the
Getfunction, we will specify which Subreddit to use. - We use
fmt.Sprintfto construct the request URL from the provided redditstring. - Exiting the function, we return a
nilslice and a non-nilerrorvalue, or vice versa. - The response’s
Statusfield is just astring; we use theerrors.Newfunction to convert it to anerrorvalue. - We use the
makefunction to allocate anItemslice big enough to store the response data. - We iterate over the response’s
Childrenslice, assigning each child’sData2element to the corresponding element in the items slice.
Next, we need to implement a formatted string that returns the Article’s Author, Score, URL and Title.
The fmt package knows how to format the built-in types, but it can be told how to format user-defined types, too.
When you pass a value to the fmt.Print functions, it checks to see if it implements the fmt.Stringer interface:
type Stringer interface {
String() string
}
Any type that implements a String() string method is a Stringer, and the fmt package will use that method to format values of that type.
Thus the Stringer interface will allow us to format User-Defined types, such as the “Item” type we’re about to define, to format our Subreddit results.
Here’s a String method for the Item type that returns the Author, Score, URL, Title and a newline:
func (i Item) String() string {
return fmt.Sprintf(
"Author: %s\nScore: %d\nURL: %s\nTitle: %s\n\n",
i.Author,
i.Score,
i.URL,
i.Title)
}
To print the item we just pass it to Println, which uses the provided String method to format the Item.
fmt.Println(item)
Now that we’ve implemented this, change your main() function to reflect this:
for _, item := range items {
fmt.Println(item)
}
|
Discussion
|
Note that we need to add the following import errors to the program that follows.
The complete program so far:
Program redditnews.go
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
)
type Item struct {
Author string `json:"author"`
Score int `json:"score"`
URL string `json:"url"`
Title string `json:"title"`
}
type response struct {
Data1 struct {
Children []struct {
Data2 Item `json:"data"`
} `json:"children"`
} `json:"data"`
}
func Get(reddit string) ([]Item, error) {
url := fmt.Sprintf("http://reddit.com/r/%s.json", reddit)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.New(resp.Status)
}
r := new(response)
err = json.NewDecoder(resp.Body).Decode(r)
if err != nil {
return nil, err
}
items := make([]Item, len(r.Data1.Children))
for i, child := range r.Data1.Children {
items[i] = child.Data2
}
return items, nil
}
func (i Item) String() string {
return fmt.Sprintf(
"Author: %s\nScore: %d\nURL: %s\nTitle: %s\n\n",
i.Author,
i.Score,
i.URL,
i.Title)
}
func main() {
items, err := Get("golang")
if err != nil {
log.Fatal(err)
}
for _, item := range items {
fmt.Println(item)
}
}
You can download this code here.
Now when we run our program we should see a nicely formatted list of links.