gom.parseGomfile   F
last analyzed

Complexity

Conditions 17

Size

Total Lines 63
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 50
nop 1
dl 0
loc 63
rs 1.8
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like gom.parseGomfile often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
package gom
2
3
// This is copied + slightly adapted from gom's `gomfile.go` file.
4
//
5
// gom's license is MIT-style.
6
7
import (
8
	"bufio"
9
	"fmt"
10
	"io"
11
	"os"
12
	"regexp"
13
	"strings"
14
)
15
16
var qx = `'[^']*'|"[^"]*"`
17
var kx = `:[a-z][a-z0-9_]*`
18
var ax = `(?:\s*` + kx + `\s*|,\s*` + kx + `\s*)`
19
var reGroup = regexp.MustCompile(`\s*group\s+((?:` + kx + `\s*|,\s*` + kx + `\s*)*)\s*do\s*$`)
20
var reEnd = regexp.MustCompile(`\s*end\s*$`)
21
var reGom = regexp.MustCompile(`^\s*gom\s+(` + qx + `)\s*((?:,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*))*)$`)
22
var reOptions = regexp.MustCompile(`(,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*)\s*)`)
23
24
func unquote(name string) string {
25
	name = strings.TrimSpace(name)
26
	if len(name) > 2 {
27
		if (name[0] == '\'' && name[len(name)-1] == '\'') || (name[0] == '"' && name[len(name)-1] == '"') {
28
			return name[1 : len(name)-1]
29
		}
30
	}
31
	return name
32
}
33
34
func parseOptions(line string, options map[string]interface{}) {
35
	ss := reOptions.FindAllStringSubmatch(line, -1)
36
	reA := regexp.MustCompile(ax)
37
	for _, s := range ss {
38
		kvs := strings.SplitN(strings.TrimSpace(s[0])[1:], "=>", 2)
39
		kvs[0], kvs[1] = strings.TrimSpace(kvs[0]), strings.TrimSpace(kvs[1])
40
		if kvs[1][0] == '[' {
41
			as := reA.FindAllStringSubmatch(kvs[1][1:len(kvs[1])-1], -1)
42
			a := []string{}
43
			for i := range as {
44
				it := strings.TrimSpace(as[i][0])
45
				if strings.HasPrefix(it, ",") {
46
					it = strings.TrimSpace(it[1:])
47
				}
48
				if strings.HasPrefix(it, ":") {
49
					it = strings.TrimSpace(it[1:])
50
				}
51
				a = append(a, it)
52
			}
53
			options[kvs[0][1:]] = a
54
		} else {
55
			options[kvs[0][1:]] = unquote(kvs[1])
56
		}
57
	}
58
}
59
60
// Gom represents configuration from Gom.
61
type Gom struct {
62
	name    string
63
	options map[string]interface{}
64
}
65
66
func parseGomfile(filename string) ([]Gom, error) {
67
	f, err := os.Open(filename + ".lock")
68
	if err != nil {
69
		f, err = os.Open(filename)
70
		if err != nil {
71
			return nil, err
72
		}
73
	}
74
	br := bufio.NewReader(f)
75
76
	goms := make([]Gom, 0)
77
78
	n := 0
79
	skip := 0
80
	valid := true
81
	var envs []string
82
	for {
83
		n++
84
		lb, _, err := br.ReadLine()
85
		if err != nil {
86
			if err == io.EOF {
87
				return goms, nil
88
			}
89
			return nil, err
90
		}
91
		line := strings.TrimSpace(string(lb))
92
		if line == "" || strings.HasPrefix(line, "#") {
93
			continue
94
		}
95
96
		name := ""
97
		options := make(map[string]interface{})
98
		var items []string
99
		if reGroup.MatchString(line) {
100
			envs = strings.Split(reGroup.FindStringSubmatch(line)[1], ",")
101
			for i := range envs {
102
				envs[i] = strings.TrimSpace(envs[i])[1:]
103
			}
104
			valid = true
105
			continue
106
		} else if reEnd.MatchString(line) {
107
			if !valid {
108
				skip--
109
				if skip < 0 {
110
					return nil, fmt.Errorf("Syntax Error at line %d", n)
111
				}
112
			}
113
			valid = false
114
			envs = nil
115
			continue
116
		} else if skip > 0 {
117
			continue
118
		} else if reGom.MatchString(line) {
119
			items = reGom.FindStringSubmatch(line)[1:]
120
			name = unquote(items[0])
121
			parseOptions(items[1], options)
122
		} else {
123
			return nil, fmt.Errorf("Syntax Error at line %d", n)
124
		}
125
		if envs != nil {
126
			options["group"] = envs
127
		}
128
		goms = append(goms, Gom{name, options})
129
	}
130
}
131