@cryptotaxi247 / kubo / commits / c364b4c34

refactor(fsrepo): extract configCompoenent

The struct was getting unmanageable. extracted the config component to reduce complexity. The datastore will be written as another component.

Brian Tiger Chow committed Jan 14, 2015 at 06:00 UTC c364b4c34c37c5a30042b4fca58c313421eefc55
2 files changed +189 -87
repo/fsrepo/config_component.go new
+145
@@ -0,0 +1,145 @@
1 +package fsrepo
2 +
3 +import (
4 + common "github.com/jbenet/go-ipfs/repo/common"
5 + config "github.com/jbenet/go-ipfs/repo/config"
6 + util "github.com/jbenet/go-ipfs/util"
7 +)
8 +
9 +var _ component = &configComponent{}
10 +
11 +// configComponent abstracts the config component of the FSRepo.
12 +// NB: create with makeConfigComponent function.
13 +type configComponent struct {
14 + path string // required at instantiation
15 + config *config.Config // assigned on Open()
16 +}
17 +
18 +// makeConfigComponent instantiates a valid configComponent.
19 +func makeConfigComponent(path string) configComponent {
20 + return configComponent{path: path}
21 +}
22 +
23 +// fsrepoConfigInit initializes the FSRepo's configComponent.
24 +func initConfigComponent(path string, conf *config.Config) error {
25 + if configComponentIsInitialized(path) {
26 + return nil
27 + }
28 + configFilename, err := config.Filename(path)
29 + if err != nil {
30 + return err
31 + }
32 + // initialization is the one time when it's okay to write to the config
33 + // without reading the config from disk and merging any user-provided keys
34 + // that may exist.
35 + if err := writeConfigFile(configFilename, conf); err != nil {
36 + return err
37 + }
38 + return nil
39 +}
40 +
41 +// Open returns an error if the config file is not present.
42 +func (c *configComponent) Open() error {
43 + configFilename, err := config.Filename(c.path)
44 + if err != nil {
45 + return err
46 + }
47 + conf, err := load(configFilename)
48 + if err != nil {
49 + return err
50 + }
51 + c.config = conf
52 + return nil
53 +}
54 +
55 +// Close satisfies the fsrepoComponent interface.
56 +func (c *configComponent) Close() error {
57 + return nil // config doesn't need to be closed.
58 +}
59 +
60 +func (c *configComponent) Config() *config.Config {
61 + return c.config
62 +}
63 +
64 +// SetConfig updates the config file.
65 +func (c *configComponent) SetConfig(updated *config.Config) error {
66 + return c.setConfigUnsynced(updated)
67 +}
68 +
69 +// GetConfigKey retrieves only the value of a particular key.
70 +func (c *configComponent) GetConfigKey(key string) (interface{}, error) {
71 + filename, err := config.Filename(c.path)
72 + if err != nil {
73 + return nil, err
74 + }
75 + var cfg map[string]interface{}
76 + if err := readConfigFile(filename, &cfg); err != nil {
77 + return nil, err
78 + }
79 + return common.MapGetKV(cfg, key)
80 +}
81 +
82 +// SetConfigKey writes the value of a particular key.
83 +func (c *configComponent) SetConfigKey(key string, value interface{}) error {
84 + filename, err := config.Filename(c.path)
85 + if err != nil {
86 + return err
87 + }
88 + var mapconf map[string]interface{}
89 + if err := readConfigFile(filename, &mapconf); err != nil {
90 + return err
91 + }
92 + if err := common.MapSetKV(mapconf, key, value); err != nil {
93 + return err
94 + }
95 + if err := writeConfigFile(filename, mapconf); err != nil {
96 + return err
97 + }
98 + // in order to get the updated values, read updated config from the
99 + // file-system.
100 + conf, err := config.FromMap(mapconf)
101 + if err != nil {
102 + return err
103 + }
104 + return c.setConfigUnsynced(conf) // TODO roll this into this method
105 +}
106 +
107 +// configComponentIsInitialized returns true if the repo is initialized at
108 +// provided |path|.
109 +func configComponentIsInitialized(path string) bool {
110 + configFilename, err := config.Filename(path)
111 + if err != nil {
112 + return false
113 + }
114 + if !util.FileExists(configFilename) {
115 + return false
116 + }
117 + return true
118 +}
119 +
120 +// setConfigUnsynced is for private use.
121 +func (r *configComponent) setConfigUnsynced(updated *config.Config) error {
122 + configFilename, err := config.Filename(r.path)
123 + if err != nil {
124 + return err
125 + }
126 + // to avoid clobbering user-provided keys, must read the config from disk
127 + // as a map, write the updated struct values to the map and write the map
128 + // to disk.
129 + var mapconf map[string]interface{}
130 + if err := readConfigFile(configFilename, &mapconf); err != nil {
131 + return err
132 + }
133 + m, err := config.ToMap(updated)
134 + if err != nil {
135 + return err
136 + }
137 + for k, v := range m {
138 + mapconf[k] = v
139 + }
140 + if err := writeConfigFile(configFilename, mapconf); err != nil {
141 + return err
142 + }
143 + *r.config = *updated // copy so caller cannot modify this private config
144 + return nil
145 +}
repo/fsrepo/fsrepo.go
+44 -87
@@ -10,11 +10,9 @@ import (
10 "sync"
11
12 repo "github.com/jbenet/go-ipfs/repo"
13 - common "github.com/jbenet/go-ipfs/repo/common"
13 config "github.com/jbenet/go-ipfs/repo/config"
14 lockfile "github.com/jbenet/go-ipfs/repo/fsrepo/lock"
15 opener "github.com/jbenet/go-ipfs/repo/fsrepo/opener"
17 - util "github.com/jbenet/go-ipfs/util"
16 debugerror "github.com/jbenet/go-ipfs/util/debugerror"
17 )
18
@@ -51,15 +49,21 @@ type FSRepo struct {
49 // config is loaded when FSRepo is opened and kept up to date when the
50 // FSRepo is modified.
51 // TODO test
54 - config *config.Config
52 + configComponent configComponent
53 +}
54 +
55 +type component interface {
56 + Open() error
57 + io.Closer
58 }
59
60 // At returns a handle to an FSRepo at the provided |path|.
61 func At(repoPath string) *FSRepo {
62 // This method must not have side-effects.
63 return &FSRepo{
61 - path: path.Clean(repoPath),
62 - state: unopened, // explicitly set for clarity
64 + path: path.Clean(repoPath),
65 + configComponent: makeConfigComponent(repoPath),
66 + state: unopened, // explicitly set for clarity
67 }
68 }
69
@@ -88,16 +92,10 @@ func Init(path string, conf *config.Config) error {
92 if isInitializedUnsynced(path) {
93 return nil
94 }
91 - configFilename, err := config.Filename(path)
92 - if err != nil {
93 - return err
94 - }
95 - // initialization is the one time when it's okay to write to the config
96 - // without reading the config from disk and merging any user-provided keys
97 - // that may exist.
98 - if err := writeConfigFile(configFilename, conf); err != nil {
95 + if err := initConfigComponent(path, conf); err != nil {
96 return err
97 }
98 +
99 return nil
100 }
101
@@ -151,15 +149,11 @@ func (r *FSRepo) Open() error {
149 return err
150 }
151
154 - configFilename, err := config.Filename(r.path)
155 - if err != nil {
156 - return err
157 - }
158 - conf, err := load(configFilename)
159 - if err != nil {
160 - return err
152 + for _, opener := range r.components() {
153 + if err := opener.Open(); err != nil {
154 + return err
155 + }
156 }
162 - r.config = conf
157
158 // datastore
159 dspath, err := config.DataStorePath("")
@@ -189,6 +183,12 @@ func (r *FSRepo) Close() error {
183 if r.state != opened {
184 return debugerror.Errorf("repo is %s", r.state)
185 }
186 +
187 + for _, closer := range r.components() {
188 + if err := closer.Close(); err != nil {
189 + return err
190 + }
191 + }
192 return transitionToClosed(r)
193 }
194
@@ -209,7 +209,7 @@ func (r *FSRepo) Config() *config.Config {
209 if r.state != opened {
210 panic(fmt.Sprintln("repo is", r.state))
211 }
212 - return r.config
212 + return r.configComponent.Config()
213 }
214
215 // SetConfig updates the FSRepo's config.
@@ -219,7 +219,7 @@ func (r *FSRepo) SetConfig(updated *config.Config) error {
219 packageLock.Lock()
220 defer packageLock.Unlock()
221
222 - return r.setConfigUnsynced(updated)
222 + return r.configComponent.SetConfig(updated)
223 }
224
225 // GetConfigKey retrieves only the value of a particular key.
@@ -230,15 +230,7 @@ func (r *FSRepo) GetConfigKey(key string) (interface{}, error) {
230 if r.state != opened {
231 return nil, debugerror.Errorf("repo is %s", r.state)
232 }
233 - filename, err := config.Filename(r.path)
234 - if err != nil {
235 - return nil, err
236 - }
237 - var cfg map[string]interface{}
238 - if err := readConfigFile(filename, &cfg); err != nil {
239 - return nil, err
240 - }
241 - return common.MapGetKV(cfg, key)
233 + return r.configComponent.GetConfigKey(key)
234 }
235
236 // SetConfigKey writes the value of a particular key.
@@ -249,25 +241,7 @@ func (r *FSRepo) SetConfigKey(key string, value interface{}) error {
241 if r.state != opened {
242 return debugerror.Errorf("repo is %s", r.state)
243 }
252 - filename, err := config.Filename(r.path)
253 - if err != nil {
254 - return err
255 - }
256 - var mapconf map[string]interface{}
257 - if err := readConfigFile(filename, &mapconf); err != nil {
258 - return err
259 - }
260 - if err := common.MapSetKV(mapconf, key, value); err != nil {
261 - return err
262 - }
263 - if err := writeConfigFile(filename, mapconf); err != nil {
264 - return err
265 - }
266 - conf, err := config.FromMap(mapconf)
267 - if err != nil {
268 - return err
269 - }
270 - return r.setConfigUnsynced(conf)
244 + return r.configComponent.SetConfigKey(key, value)
245 }
246
247 var _ io.Closer = &FSRepo{}
@@ -279,7 +253,19 @@ func IsInitialized(path string) bool {
253 // Init or Remove the repo while this call is in progress.
254 packageLock.Lock()
255 defer packageLock.Unlock()
282 - return isInitializedUnsynced(path)
256 +
257 + // componentInitCheckers are functions that indicate whether the component
258 + // is isInitialized
259 + var componentInitCheckers = []func(path string) bool{
260 + configComponentIsInitialized,
261 + // TODO add datastore component initialization checker
262 + }
263 + for _, isInitialized := range componentInitCheckers {
264 + if !isInitialized(path) {
265 + return false
266 + }
267 + }
268 + return true
269 }
270
271 // private methods below this point. NB: packageLock must held by caller.
@@ -287,14 +273,7 @@ func IsInitialized(path string) bool {
273 // isInitializedUnsynced reports whether the repo is initialized. Caller must
274 // hold openerCounter lock.
275 func isInitializedUnsynced(path string) bool {
290 - configFilename, err := config.Filename(path)
291 - if err != nil {
292 - return false
293 - }
294 - if !util.FileExists(configFilename) {
295 - return false
296 - }
297 - return true
276 + return configComponentIsInitialized(path)
277 }
278
279 // initCheckDir ensures the directory exists and is writable
@@ -346,32 +325,10 @@ func transitionToClosed(r *FSRepo) error {
325 return nil
326 }
327
349 -// setConfigUnsynced is for private use. Callers must hold the packageLock.
350 -func (r *FSRepo) setConfigUnsynced(updated *config.Config) error {
351 - if r.state != opened {
352 - return fmt.Errorf("repo is", r.state)
353 - }
354 - configFilename, err := config.Filename(r.path)
355 - if err != nil {
356 - return err
357 - }
358 - // to avoid clobbering user-provided keys, must read the config from disk
359 - // as a map, write the updated struct values to the map and write the map
360 - // to disk.
361 - var mapconf map[string]interface{}
362 - if err := readConfigFile(configFilename, &mapconf); err != nil {
363 - return err
328 +// components returns the FSRepo's constituent components
329 +func (r *FSRepo) components() []component {
330 + return []component{
331 + &r.configComponent,
332 + // TODO add datastore
333 }
365 - m, err := config.ToMap(updated)
366 - if err != nil {
367 - return err
368 - }
369 - for k, v := range m {
370 - mapconf[k] = v
371 - }
372 - if err := writeConfigFile(configFilename, mapconf); err != nil {
373 - return err
374 - }
375 - *r.config = *updated // copy so caller cannot modify this private config
376 - return nil
334 }