watson-ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,493 @@
1
+ module Watson
2
+ # Configuration container class
3
+ # Contains all configuration options and state variables
4
+ # that are accessed throughout watson
5
+ class Config
6
+
7
+ # Include for debug_print
8
+ include Watson
9
+
10
+ # Debug printing for this class
11
+ DEBUG = false
12
+
13
+ # [review] - Combine into single statement (for performance or something?)
14
+ # [todo] - Add config options (rc file) for default max depth and context lines
15
+
16
+ # List of all files/folders to ignore when parsing
17
+ attr_accessor :ignore_list
18
+ # List of directories to parse
19
+ attr_accessor :dir_list
20
+ # List of all files to parse
21
+ attr_accessor :file_list
22
+ # List of tags to look for when parsing
23
+ attr_accessor :tag_list
24
+ # Number of directories to parse recursively
25
+ attr_accessor :parse_depth
26
+ # Number of lines of issue context to grab
27
+ attr_accessor :context_depth
28
+
29
+ # Flag for command line setting of file/dir to parse
30
+ attr_accessor :cl_entry_set
31
+ # Flag for command line setting of file/dir to ignore
32
+ attr_accessor :cl_ignore_set
33
+ # Flag for command line setting of tag to parse for
34
+ attr_accessor :cl_tag_set
35
+
36
+ # Flag for whether less is avaliable to print results
37
+ attr_reader :use_less
38
+ # Flag for where the temp file for printing is located
39
+ attr_reader :tmp_file
40
+
41
+ # Flag for whether remote access is avaliable
42
+ attr_accessor :remote_valid
43
+
44
+ # Flag for whether GitHub access is avaliable
45
+ attr_accessor :github_valid
46
+ # GitHub API key generated from Remote::GitHub setup
47
+ attr_accessor :github_api
48
+ # GitHub repo associated with current directory + watson config
49
+ attr_accessor :github_repo
50
+ # Hash to hold list of all GitHub issues associated with repo
51
+ attr_accessor :github_issues
52
+
53
+
54
+ # Flag for whether Bitbucket access is avaliable
55
+ attr_accessor :bitbucket_valid
56
+ # Bitbucket API key generated from Remote::Bitbucket setup (username for now)
57
+ attr_accessor :bitbucket_api
58
+ # Bitbucket password for access until OAuth is implemented for Bitbucket
59
+ attr_accessor :bitbucket_pw
60
+ # Bitbucket repo associated with current directory + watson config
61
+ attr_accessor :bitbucket_repo
62
+ # Hash to hold list of all Bitbucket issues associated with repo
63
+ attr_accessor :bitbucket_issues
64
+
65
+
66
+ ###########################################################
67
+ # Config initialization method to setup necessary parameters, states, and vars
68
+ def initialize
69
+
70
+ # [review] - Read and store rc FP inside initialize?
71
+ # This way we don't need to keep reopening the FP to use it
72
+ # but then we need a way to reliably close the FP when done
73
+
74
+ # Identify method entry
75
+ debug_print "#{self.class} : #{__method__}\n"
76
+
77
+ # Program config
78
+ @rc_file = ".watsonrc"
79
+ @tmp_file = ".watsonresults"
80
+
81
+ @parse_depth = 0
82
+ @context_depth = 15
83
+
84
+ # State flags
85
+ @cl_entry_set = false
86
+ @cl_tag_set = false
87
+ @cl_ignore_set = false
88
+
89
+ # System flags
90
+ # [todo] - Add option to save output to file also
91
+ @use_less = false
92
+
93
+ # Data containers
94
+ @ignore_list = Array.new()
95
+ @dir_list = Array.new()
96
+ @file_list = Array.new()
97
+ @tag_list = Array.new()
98
+
99
+ # Remote options
100
+ @remote_valid = false
101
+
102
+ @github_valid = false
103
+ @github_api = ""
104
+ @github_repo = ""
105
+ @github_issues = {:open => Hash.new(),
106
+ :closed => Hash.new()
107
+ }
108
+
109
+ # Keep API param (and put username there) for OAuth update later
110
+ @bitbucket_valid = false
111
+ @bitbucket_api = ""
112
+ @bitbucket_pw = ""
113
+ @bitbucket_repo = ""
114
+ @bitbucket_issues = {:open => Hash.new(),
115
+ :closed => Hash.new()
116
+ }
117
+ end
118
+
119
+
120
+ ###########################################################
121
+ # Parse through configuration and obtain remote info if necessary
122
+ def run
123
+
124
+ # Identify method entry
125
+ debug_print "#{ self.class } : #{ __method__ }\n"
126
+
127
+ # check_conf should create if no conf found, exit entirely if can't do either
128
+ exit if check_conf == false
129
+ read_conf
130
+
131
+ unless @github_api.empty? && @github_api.empty?
132
+ Remote::GitHub.get_issues(self)
133
+ end
134
+
135
+ unless @bitbucket_api.empty? && @bitbucket_repo.empty?
136
+ Remote::Bitbucket.get_issues(self)
137
+ end
138
+ end
139
+
140
+
141
+ ###########################################################
142
+ # Check for config file in directory of execution
143
+ # Should have individual .rc for each dir that watson is used in
144
+ # This allows you to keep different preferences for different projects
145
+ # Create conf (with #create_conf) if not found
146
+ def check_conf
147
+
148
+ # Identify method entry
149
+ debug_print "#{ self.class } : #{ __method__ }\n"
150
+
151
+ # Check for .rc
152
+ # If one doesn't exist, create default one with create_conf method
153
+ if !Watson::FS.check_file(@rc_file)
154
+ debug_print "#{ @rc_file } not found\n"
155
+ debug_print "Creating default #{ @rc_file }\n"
156
+
157
+ # Create default .rc and return create_conf (true if created,
158
+ # false if not)
159
+ return create_conf
160
+ else
161
+ debug_print "#{ @rc_file } found\n"
162
+ return true
163
+ end
164
+ end
165
+
166
+
167
+ ###########################################################
168
+ # Watson config creater
169
+ # Copies default config from /assets/defaultConf to the current directory
170
+ def create_conf
171
+ # [review] - Not sure if I should use the open/read/write or Fileutils.cp
172
+
173
+ # Identify method entry
174
+ debug_print "#{ self.class } : #{ __method__ }\n"
175
+
176
+
177
+ # Generate full path since File doesn't care about the LOAD_PATH
178
+ # [review] - gsub uses (.?)+ to grab anything after lib (optional), better regex?
179
+ _full_path = __dir__.gsub(/\/lib(.?)+/, '') + "/" + "assets/defaultConf"
180
+ debug_print "Full path to defaultConf (in gem): #{ _full_path }\n"
181
+
182
+ # Check to make sure we can access the default file
183
+ if !Watson::FS.check_file(_full_path)
184
+ print "Unable to open #{ _full_path }\n"
185
+ print "Cannot create default, exiting...\n"
186
+ return false
187
+ else
188
+ # Open default config file in read mode and read into temp
189
+ _input = File.open(_full_path, 'r')
190
+ _default = _input.read
191
+
192
+ # Open rc file in current directory in write mode and write default
193
+ _output = File.open(@rc_file, 'w')
194
+ _output.write(_default)
195
+
196
+ # Close both default and new rc files
197
+ _input.close
198
+ _output.close
199
+
200
+ debug_print "Successfully wrote defaultConf to current directory\n"
201
+ return true
202
+ end
203
+ end
204
+
205
+
206
+ ###########################################################
207
+ # Read configuration file and populate Config container class
208
+ def read_conf
209
+
210
+ # Identify method entry
211
+ debug_print "#{ self.class } : #{ __method__ }\n"
212
+
213
+
214
+ debug_print "Reading #{ @rc_file }\n"
215
+ if !Watson::FS.check_file(@rc_file)
216
+ print "Unable to open #{@rc_file}, exiting\n"
217
+ return false
218
+ else
219
+ debug_print "Opened #{ @rc_file } for reading\n"
220
+ end
221
+
222
+
223
+ # Check if system has less for output
224
+ @use_less = check_less
225
+
226
+
227
+ # Add all the standard items to ignorelist
228
+ # This gets added regardless of ignore list specified
229
+ # [review] - Keep *.swp in there?
230
+ # [todo] - Add conditional to @rc_file such that if passed by -f we accept it
231
+ # [todo] - Add current file (watson) to avoid accidentally printing app tags
232
+ @ignore_list.push(".")
233
+ @ignore_list.push("..")
234
+ @ignore_list.push("*.swp")
235
+ @ignore_list.push(@rc_file)
236
+ @ignore_list.push(@tmp_file)
237
+
238
+ # Open and read rc
239
+ # [review] - Not sure if explicit file close is required here
240
+ _rc = File.open(@rc_file, 'r').read
241
+
242
+ debug_print "\n\n"
243
+
244
+ # Create temp section var to keep track of what we are populating in config
245
+ _section = ""
246
+
247
+ # Keep index to print what line we are on
248
+ # Could fool around with Enumerable + each_with_index but oh well
249
+ _i = 0;
250
+
251
+ # Fix line endings so we can support Windows/Linux edited rc files
252
+ _rc.gsub!(/\r\n?/, "\n")
253
+ _rc.each_line do | _line |
254
+ debug_print "#{ _i }: #{ _line }" if (_line != "\n")
255
+ _i = _i + 1
256
+
257
+
258
+ # Ignore full line comments or newlines
259
+ if _line.match(/(^#)|(^\n)|(^ )/)
260
+ debug_print "Full line comment or newline found, skipping\n"
261
+ # [review] - More "Ruby" way of going to next line?
262
+ next
263
+ end
264
+
265
+
266
+ # [review] - Use if with match so we can call next on the line reading loop
267
+ # Tried using match(){|_mtch|} as well as do |_mtch| but those don't seem to
268
+ # register the next call to the outer loop, so this way will do for now
269
+
270
+ # Regex on line to find out if we are in a new [section] of
271
+ # config parameters. If so, store it into section var and move
272
+ # to next line
273
+ _mtch = _line.match(/^\[(\w+)\]/)
274
+ if _mtch
275
+ debug_print "Found section #{ _mtch[1] }\n"
276
+ _section = _mtch[1]
277
+ next
278
+ end
279
+
280
+
281
+ case _section
282
+ when "context_depth"
283
+ # No need for regex on context value, command should read this in only as a #
284
+ # Chomp to get rid of any nonsense
285
+ @context_depth = _line.chomp!
286
+
287
+
288
+ when "parse_depth"
289
+ # No need for regex on parse value, command should read this in only as a #
290
+ # Chomp to get rid of any nonsense
291
+ @parse_depth = _line.chomp!
292
+
293
+
294
+ when "dirs"
295
+ # If @dir_list or @file_list wasn't populated by CL args
296
+ # then populate from rc
297
+ # [review] - Populate @dirs/files_list first, then check size instead
298
+ if @cl_entry_set
299
+ debug_print "Directories or files set from command line ignoring rc [dirs]\n"
300
+ next
301
+ end
302
+
303
+ # Regex to grab directory
304
+ # Then substitute trailing / (necessary for later formatting)
305
+ # Then push to @dir_list
306
+ _mtch = _line.match(/^((\w+)?\.?\/?)+/)[0].gsub(/(\/)+$/, "")
307
+ if !_mtch.empty?
308
+ @dir_list.push(_mtch)
309
+ debug_print "#{ _mtch } added to @dir_list\n"
310
+ end
311
+ debug_print "@dir_list --> #{ @dir_list }\n"
312
+
313
+
314
+ when "tags"
315
+ # Same as previous for tags
316
+ # [review] - Populate @tag_list, then check size instead
317
+ if @cl_tag_set
318
+ debug_print "Tags set from command line, ignoring rc [tags]\n"
319
+ next
320
+ end
321
+
322
+ # Same as previous for tags
323
+ # [review] - Need to think about what kind of tags this supports
324
+ # Check compatibility with GitHub + Bitbucket and what makes sense
325
+ # Only supports single word+number tags
326
+ _mtch = _line.match(/^(\S+)/)[0]
327
+ if !_mtch.empty?
328
+ @tag_list.push(_mtch)
329
+ debug_print "#{ _mtch } added to @tag_list\n"
330
+ end
331
+ debug_print "@tag_list --> #{ @tag_list }\n"
332
+
333
+
334
+ when "ignore"
335
+ # Same as previous for ignores
336
+ # [review] - Populate @tag_list, then check size instead
337
+
338
+ if @cl_ignore_set
339
+ debug_print "Ignores set from command line, ignoring rc [ignores]\n"
340
+ next
341
+ end
342
+
343
+ # Same as previous for ignores (regex same as dirs)
344
+ # Don't eliminate trailing / because not sure if dir can have
345
+ # same name as file (Linux it can't, but not sure about Win/Mac)
346
+ # [review] - Can Win/Mac have dir + file with same name in same dir?
347
+ _mtch = _line.match(/^((\w+)?\.?\/?)+/)[0]
348
+ if !_mtch.empty?
349
+ @ignore_list.push(_mtch)
350
+ debug_print "#{ _mtch } added to @ignore_list\n"
351
+ end
352
+ debug_print "@ignore_list --> #{ @ignore_list }\n"
353
+
354
+
355
+ when "github_api"
356
+ # No need for regex on API key, GitHub setup should do this properly
357
+ # Chomp to get rid of any nonsense
358
+ @github_api = _line.chomp!
359
+ debug_print "GitHub API: #{ @github_api }\n"
360
+
361
+
362
+ when "github_repo"
363
+ # Same as above
364
+ @github_repo = _line.chomp!
365
+ debug_print "GitHub Repo: #{ @github_repo }\n"
366
+
367
+
368
+ when "bitbucket_api"
369
+ # Same as GitHub parse above
370
+ @bitbucket_api = _line.chomp!
371
+ debug_print "Bitbucket API: #{ @bitbucket_api }\n"
372
+
373
+
374
+ when "bitbucket_repo"
375
+ # Same as GitHub repo parse above
376
+ @bitbucket_repo = _line.chomp!
377
+ debug_print "Bitbucket Repo: #{ @bitbucket_repo }\n"
378
+
379
+
380
+ else
381
+ debug_print "Unknown tag found #{_section}\n"
382
+ end
383
+
384
+ end
385
+
386
+ return true
387
+ end
388
+
389
+
390
+ ###########################################################
391
+ # Update config file with specified parameters
392
+ # Accepts input parameters that should be updated and writes to file
393
+ # Selective updating to make bookkeeping easier
394
+ def update_conf(*params)
395
+
396
+ # Identify method entry
397
+ debug_print "#{ self.class } : #{ __method__ }\n"
398
+
399
+ # Check if RC exists, if not create one
400
+ if !Watson::FS.check_file(@rc_file)
401
+ print "Unable to open #{ @rc_file }, exiting\n"
402
+ create_conf
403
+ else
404
+ debug_print "Opened #{ @rc_file } for reading\n"
405
+ end
406
+
407
+ # Go through all given params and make sure they are actually config vars
408
+ params.each_with_index do | _param, _i |
409
+ if !self.instance_variable_defined?("@#{ _param }")
410
+ debug_print "#{ _param } does not exist in Config\n"
411
+ debug_print "Check your input(s) to update_conf\n"
412
+ params.slice!(_i)
413
+ end
414
+ end
415
+
416
+
417
+ # Read in currently saved RC and go through it line by line
418
+ # Only update params that were passed to update_conf
419
+ # This allows us to clean up the config file at the same time
420
+
421
+
422
+ # Open and read rc
423
+ # [review] - Not sure if explicit file close is required here
424
+ _rc = File.open(@rc_file, 'r').read
425
+ _update = File.open(@rc_file, 'w')
426
+
427
+
428
+ # Keep index to print what line we are on
429
+ # Could fool around with Enumerable + each_with_index but oh well
430
+ _i = 0;
431
+
432
+ # Keep track of newlines for prettying up the conf
433
+ _nlc = 0
434
+ _section = ""
435
+
436
+ # Fix line endings so we can support Windows/Linux edited rc files
437
+ _rc.gsub!(/\r\n?/, "\n")
438
+ _rc.each_line do | _line |
439
+ # Print line for debug purposes
440
+ debug_print "#{ _i }: #{ _line }"
441
+ _i = _i + 1
442
+
443
+
444
+ # Look for sections and set section var
445
+ _mtch = _line.match(/^\[(\w+)\]/)
446
+ if _mtch
447
+ debug_print "Found section #{ _mtch[1] }\n"
448
+ _section = _mtch[1]
449
+ end
450
+
451
+ # Check for newlines
452
+ # If we already have 2 newlines before any actual content, skip
453
+ # This is just to make the RC file output nicer looking
454
+ if _line == "\n"
455
+ debug_print "Newline found\n"
456
+ _nlc = _nlc + 1
457
+ if _nlc < 3
458
+ debug_print "Less than 3 newlines so far, let it print\n"
459
+ _update.write(_line)
460
+ end
461
+ # If the section we are in doesn't match the params passed to update_conf
462
+ # it is safe to write the line over to the new config
463
+ elsif !params.include?(_section)
464
+ debug_print "Current section NOT a param to update\n"
465
+ debug_print "Writing to new rc\n"
466
+ _update.write(_line)
467
+
468
+ # Reset newline
469
+ _nlc = 0
470
+ end
471
+
472
+ debug_print "line: #{ _line }\n"
473
+ debug_print "nlc: #{ _nlc }\n"
474
+ end
475
+
476
+ # Make sure there is at least 3 newlines between last section before writing new params
477
+ (2 - _nlc).times do
478
+ _update.write("\n")
479
+ end
480
+
481
+ # Now that we have skipped all the things that need to be updated, write them in
482
+ params.each do | _param |
483
+ _update.write("[#{ _param }]\n")
484
+ _update.write("#{ self.instance_variable_get("@#{ _param }") }")
485
+ _update.write("\n\n\n")
486
+ end
487
+
488
+ _update.close
489
+ end
490
+
491
+ end
492
+ end
493
+