k_builder 0.0.61 → 0.0.69

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ /Users/davidcruwys/dev/slides-addons/highlight_css
@@ -0,0 +1,7 @@
1
+ <html>
2
+ <head><title>404 Not Found</title></head>
3
+ <body bgcolor="white">
4
+ <center><h1>404 Not Found</h1></center>
5
+ <hr><center>nginx</center>
6
+ </body>
7
+ </html>
@@ -1,21 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KBuilder
4
+ # TODO: Why is this called BaseBuilder, why not just builder
5
+ # TODO: Is this really the builder pattern, it could be the class used by a director
6
+ # but it is not really storing information for builder purposes.
7
+ #
4
8
  # Base builder defines builder methods, build method and configuration
5
9
  #
6
10
  # Convention: Setter methods (are Fluent) and use the prefix set_
7
11
  # Getter methods (are NOT fluent) and return the stored value
8
12
  # Setter methods (are NOT fluent) can be created as needed
9
13
  # these methods would not be prefixed with the set_
14
+
15
+ # process_any_content(content: 'abc')
16
+ # process_any_content(content_file: 'abc.txt')
17
+ # process_any_content(template: 'abc {{name}}', name: 'sean')
18
+ # process_any_content(template_file: 'abc.txt', name: 'sean')
19
+
20
+ # process_any_content(content_gist: 'https://gist.github.com/klueless-io/8d4b6d199dbe4a5d40807a47fff8ed1c')
21
+ # process_any_content(template_gist: 'https://gist.github.com/klueless-io/8d4b6d199dbe4a5d40807a47fff8ed1c', name: 'sean')
22
+
10
23
  class BaseBuilder
11
24
  include KLog::Logging
12
25
 
13
26
  attr_reader :configuration
14
27
 
15
28
  attr_accessor :target_folders
16
-
17
29
  attr_accessor :template_folders
18
30
 
31
+ attr_accessor :last_output_file
32
+ attr_accessor :last_output_folder
33
+ # attr_accessor :last_template
34
+ attr_accessor :last_template_file
35
+
19
36
  # Factory method that provides a builder for a specified structure
20
37
  # runs through a configuration block and then builds the final structure
21
38
  #
@@ -59,6 +76,7 @@ module KBuilder
59
76
  }
60
77
  end
61
78
 
79
+ # rubocop:disable Metrics/AbcSize
62
80
  def debug
63
81
  log.subheading 'kbuilder'
64
82
 
@@ -69,8 +87,16 @@ module KBuilder
69
87
  log.info ''
70
88
 
71
89
  template_folders.debug(title: 'template folders (search order)')
90
+
91
+ log.info ''
92
+ log.kv 'last output file' , last_output_file
93
+ log.kv 'last output folder' , last_output_folder
94
+ # log.kv 'last template' , last_template
95
+ log.kv 'last template file' , last_template_file
96
+
72
97
  ''
73
98
  end
99
+ # rubocop:enable Metrics/AbcSize
74
100
 
75
101
  # ----------------------------------------------------------------------
76
102
  # Fluent interface
@@ -95,31 +121,108 @@ module KBuilder
95
121
  # Extra options will be used as data for templates, e.g
96
122
  # @option opts [String] :to Recipient email
97
123
  # @option opts [String] :body The email's body
124
+ def add_file_action(file, **opts)
125
+ {
126
+ action: :add_file,
127
+ played: false,
128
+ file: file,
129
+ opts: opts
130
+ }
131
+ end
132
+
133
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
98
134
  def add_file(file, **opts)
99
135
  # move to command
100
- full_file = opts.key?(:folder_key) ? target_file(file, folder: opts[:folder_key]) : target_file(file)
136
+ full_file = target_file(file, **opts) # opts.key?(:folder_key) || opts.key?(:folder) ? target_file(file, folder: opts[:folder], folder_key: opts[:folder_key]) : target_file(file)
101
137
 
102
138
  # Need logging options that can log these internal details
103
- FileUtils.mkdir_p(File.dirname(full_file))
139
+ mkdir_p(File.dirname(full_file))
104
140
 
105
141
  content = process_any_content(**opts)
106
142
 
107
- File.write(full_file, content)
143
+ file_write(full_file, content, on_exist: opts[:on_exist])
108
144
 
109
145
  # Prettier needs to work with the original file name
110
- run_prettier file if opts.key?(:pretty)
146
+ run_prettier file if opts.key?(:pretty)
147
+ # TODO: Add test
148
+ run_cop(full_file, fix_safe: true) if opts.key?(:cop) || opts.key?(:ruby_cop)
149
+
111
150
  # Need support for rubocop -a
151
+ open_file(last_output_file) if opts.key?(:open)
152
+ open_file(last_template_file) if opts.key?(:open_template)
153
+ browse_file(last_output_file) if opts.key?(:browse)
154
+ pause(opts[:pause]) if opts[:pause]
112
155
 
113
156
  self
114
157
  end
158
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
159
+
160
+ def play_actions(actions)
161
+ actions.reject { |action| action[:played] }.each do |action|
162
+ play_action(action)
163
+ end
164
+ end
165
+
166
+ def play_action(action)
167
+ case action[:action]
168
+ when :add_file
169
+ add_file(action[:file], action[:opts])
170
+ when :delete_file
171
+ delete_file(action[:file], action[:opts])
172
+ when :vscode
173
+ vscode(action[:file_parts], action[:opts])
174
+ when :browse
175
+ browse(action[:file_parts], action[:opts])
176
+ when :set_current_folder
177
+ set_current_folder(action[:folder_key])
178
+ when :run_command
179
+ run_command(action[:command])
180
+ else
181
+ log.error "Unknown action: #{action[:action]}"
182
+ end
183
+
184
+ action[:played] = true
185
+ end
186
+
115
187
  alias touch add_file # it is expected that you would not supply any options, just a file name
116
188
 
189
+ def delete_file_action(file, **opts)
190
+ {
191
+ action: :delete_file,
192
+ played: false,
193
+ file: file,
194
+ opts: opts
195
+ }
196
+ end
197
+
198
+ def delete_file(file, **opts)
199
+ full_file = target_file(file, **opts) # = opts.key?(:folder_key) ? target_file(file, folder: opts[:folder_key]) : target_file(file)
200
+
201
+ File.delete(full_file) if File.exist?(full_file)
202
+
203
+ self
204
+ end
205
+
206
+ def file_exist?(file, **opts)
207
+ # full_file = opts.key?(:folder_key) ? target_file(file, folder_key: opts[:folder_key]) : target_file(file)
208
+ full_file = target_file(file, **opts)
209
+
210
+ File.exist?(full_file)
211
+ end
212
+
213
+ # ToDo
214
+ # def delete_folder(file)
215
+ # FileUtils.remove_dir(path_to_directory) if File.directory?(path_to_directory)
216
+
217
+ # self
218
+ # end
219
+
117
220
  def make_folder(folder_key = nil, sub_path: nil)
118
221
  folder_key = current_folder_key if folder_key.nil?
119
222
  folder = target_folder(folder_key)
120
223
  folder = File.join(folder, sub_path) unless sub_path.nil?
121
224
 
122
- FileUtils.mkdir_p(folder)
225
+ mkdir_p(folder)
123
226
 
124
227
  self
125
228
  end
@@ -140,6 +243,8 @@ module KBuilder
140
243
 
141
244
  begin
142
245
  IO.popen('pbcopy', 'w') { |f| f << content }
246
+
247
+ open_file(last_template_file) if opts.key?(:open_template)
143
248
  rescue Errno::ENOENT => e
144
249
  if e.message == 'No such file or directory - pbcopy'
145
250
  # May want to use this GEM in the future
@@ -152,15 +257,84 @@ module KBuilder
152
257
  end
153
258
  alias clipboard_copy add_clipboard
154
259
 
155
- def vscode(*file_parts, folder: current_folder_key)
260
+ def vscode_action(*file_parts, folder_key: current_folder_key, file: nil)
261
+ {
262
+ action: :vscode,
263
+ played: false,
264
+ file_parts: file_parts,
265
+ opts: { folder_key: folder_key, file: file }
266
+ }
267
+ end
268
+
269
+ def vscode(*file_parts, folder_key: current_folder_key, file: nil)
156
270
  # move to command
157
- file = target_file(*file_parts, folder: folder)
271
+ file = target_file(*file_parts, folder_key: folder_key) if file.nil?
158
272
 
159
273
  rc "code #{file}"
160
274
 
161
275
  self
162
276
  end
163
277
 
278
+ def browse_action(*file_parts, folder_key: current_folder_key, file: nil)
279
+ {
280
+ action: :browse,
281
+ played: false,
282
+ file_parts: file_parts,
283
+ opts: { folder_key: folder_key, file: file }
284
+ }
285
+ end
286
+
287
+ def browse(*file_parts, folder_key: current_folder_key, file: nil)
288
+ # move to command
289
+ file = target_file(*file_parts, folder_key: folder_key) if file.nil?
290
+
291
+ rc "open -a \"Google Chrome\" #{file}"
292
+
293
+ self
294
+ end
295
+
296
+ def open
297
+ open_file(last_output_file)
298
+
299
+ self
300
+ end
301
+ alias o open
302
+
303
+ def open_template
304
+ open_file(last_template_file)
305
+
306
+ self
307
+ end
308
+ alias ot open_template
309
+
310
+ def open_file(file)
311
+ if file.nil?
312
+ log.warn('open_file will not open when file is nil')
313
+ return self
314
+ end
315
+
316
+ vscode(file: file)
317
+
318
+ self
319
+ end
320
+
321
+ def browse_file(file)
322
+ if file.nil?
323
+ log.warn('browse_file will not browse when file is nil')
324
+ return self
325
+ end
326
+
327
+ browse(file: file)
328
+
329
+ self
330
+ end
331
+
332
+ def pause(seconds = 1)
333
+ sleep(seconds)
334
+
335
+ self
336
+ end
337
+
164
338
  # ----------------------------------------------------------------------
165
339
  # Attributes: Think getter/setter
166
340
  #
@@ -174,6 +348,14 @@ module KBuilder
174
348
  # Target folders and files
175
349
  # ----------------------------------------------------------------------
176
350
 
351
+ def set_current_folder_action(folder_key)
352
+ {
353
+ action: :set_current_folder,
354
+ played: false,
355
+ folder_key: folder_key
356
+ }
357
+ end
358
+
177
359
  def set_current_folder(folder_key)
178
360
  target_folders.current = folder_key
179
361
 
@@ -216,12 +398,18 @@ module KBuilder
216
398
  # target_file('/abc.txt')
217
399
  # target_file('/xyz/abc.txt')
218
400
  # target_file('/xyz', 'abc.txt')
219
- def target_file(*file_parts, folder: current_folder_key)
401
+ def target_file(*file_parts, folder_key: current_folder_key, folder: nil, **)
402
+ # TODO: Mismatch (sometimes called folder, sometimes called folder_key:)
403
+ if folder
404
+ log.error("Change folder: to folder_key: for #{folder} - #{file_parts}")
405
+ return
406
+ end
407
+
220
408
  # Absolute path
221
409
  return File.join(*file_parts) if Pathname.new(file_parts.first).absolute?
222
410
 
223
- # Relative to :folder
224
- File.join(target_folder(folder), *file_parts)
411
+ # Relative to :folder_key
412
+ File.join(target_folder(folder_key), *file_parts)
225
413
  end
226
414
 
227
415
  # Template folder & Files
@@ -242,7 +430,8 @@ module KBuilder
242
430
  # Gets a template_file relative to the template folder, looks first in
243
431
  # local template folder and if not found, looks in global template folder
244
432
  def find_template_file(file_parts)
245
- template_folders.find_file(file_parts)
433
+ self.last_template_file = template_folders.find_file(file_parts)
434
+ last_template_file
246
435
  end
247
436
 
248
437
  # Building content from templates
@@ -268,15 +457,6 @@ module KBuilder
268
457
 
269
458
  return "content not found: #{opts[:content_file]}" if cf.nil?
270
459
 
271
- # cf = opts[:content_file]
272
-
273
- # unless File.exist?(cf)
274
- # cf_from_template_folders = find_template_file(cf)
275
- # return "Content not found: #{File.expand_path(cf)}" unless File.exist?(cf_from_template_folders)
276
-
277
- # cf = cf_from_template_folders
278
- # end
279
-
280
460
  File.read(cf)
281
461
  end
282
462
 
@@ -320,13 +500,11 @@ module KBuilder
320
500
  Handlebars::Helpers::Template.render(template_content, opts) unless template_content.nil?
321
501
  end
322
502
 
323
- def run_cop(file, auto_safe: false, auto_all: false)
324
- cli = RuboCop::CLI.new
325
- opts = ['--format', 'simple', file]
326
- opts.prepend('-a') if auto_safe
327
- opts.prepend('-A') if auto_all
503
+ def run_cop(file, **opts)
504
+ command = Commands::RuboCopCommand.new(file, builder: self, **opts)
505
+ command.execute
328
506
 
329
- cli.run(opts)
507
+ self
330
508
  end
331
509
 
332
510
  # Need to handle absolute files, see
@@ -342,7 +520,7 @@ module KBuilder
342
520
  # Deep path create if needed
343
521
  tf = target_folder
344
522
 
345
- FileUtils.mkdir_p(tf)
523
+ mkdir_p(tf)
346
524
 
347
525
  build_command = "cd #{tf} && #{command}"
348
526
 
@@ -353,5 +531,54 @@ module KBuilder
353
531
  system(build_command)
354
532
  end
355
533
  alias rc run_command
534
+
535
+ def run_command_action(command)
536
+ {
537
+ action: :run_command,
538
+ played: false,
539
+ command: command
540
+ }
541
+ end
542
+
543
+ def file_write(file, content, on_exist: :skip)
544
+ self.last_output_file = file # if file not found, we still want to record this as the last_output_file
545
+
546
+ not_found = !File.exist?(file)
547
+
548
+ if not_found
549
+ File.write(file, content)
550
+ return
551
+ end
552
+
553
+ return if %i[skip ignore].include?(on_exist)
554
+
555
+ if %i[overwrite write].include?(on_exist)
556
+ File.write(file, content)
557
+ return
558
+ end
559
+
560
+ return unless on_exist == :compare
561
+
562
+ vscompare(file, content)
563
+ end
564
+
565
+ def vscompare(file, content)
566
+ # need to use some sort of caching folder for this
567
+ ext = File.extname(file)
568
+ fn = File.basename(file, ext)
569
+ temp_file = Tempfile.new([fn, ext])
570
+
571
+ temp_file.write(content)
572
+ temp_file.close
573
+
574
+ return if File.read(file) == content
575
+
576
+ system("code -d #{file} #{temp_file.path}")
577
+ sleep 2
578
+ end
579
+
580
+ def mkdir_p(folder)
581
+ @last_output_folder = FileUtils.mkdir_p(folder).first
582
+ end
356
583
  end
357
584
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBuilder
4
+ module Commands
5
+ # Base command for single responsibility actions that can be fired
6
+ # from methods in the builder.
7
+ #
8
+ # Uses the command pattern
9
+ class BaseCommand
10
+ include KLog::Logging
11
+
12
+ attr_accessor :builder
13
+ attr_accessor :valid
14
+
15
+ def initialize(**opts)
16
+ @builder = opts[:builder]
17
+ @valid = true
18
+ end
19
+
20
+ def guard(message)
21
+ # THIS SHOULD ONLY LOG IF DEBUGGING IS TURNED ON
22
+ log.error(message)
23
+ @valid = false
24
+ end
25
+
26
+ def valid?
27
+ @valid
28
+ end
29
+
30
+ def debug(title: nil)
31
+ log.section_heading(title) if title
32
+ debug_values if respond_to?(:debug_values)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBuilder
4
+ module Commands
5
+ # Run CodeSyntaxHighlighter against source code and produce a styled HTML representation
6
+ #
7
+ # Alternatives to Highlighter-js could be carbon-now and ray.so
8
+ class CodeSyntaxHighlighterCommand < BaseCommand
9
+ attr_reader :source_code
10
+ attr_reader :formatted_code
11
+
12
+ def initialize(source_code, **opts)
13
+ super(**opts)
14
+
15
+ self.source_code = source_code
16
+ end
17
+
18
+ def execute
19
+ return unless valid?
20
+
21
+ run
22
+ end
23
+
24
+ private
25
+
26
+ def source_code=(value)
27
+ @source_code = value
28
+
29
+ guard('Source code is required for formatting') if value.nil? || value.empty?
30
+ end
31
+
32
+ def run
33
+ # @formatted_code = ExecJS.eval("'red yellow blue'.split(' ')")
34
+
35
+ # # highlight_source = 'lib/k_builder/assets/a.js'
36
+ # highlight_source = 'lib/k_builder/assets/highlight.min.js'
37
+
38
+ # log.error ExecJS.runtime.name
39
+
40
+ # a = File.read(highlight_source)
41
+ # # context = ExecJS.compile(a)
42
+ # context = ExecJS.compile(highlight_source)
43
+ # context.call("html = hljs.highlightAuto('<h1>Hello World!</h1>').value")
44
+
45
+ # get_js_asset('highlight')
46
+ # get_js_asset('ruby')
47
+ end
48
+
49
+ # https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.0.1/highlight.min.js
50
+ # https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.0.1/languages/ruby.min.js
51
+
52
+ def get_js_asset(name)
53
+ url = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.0.1/#{name}.min.js"
54
+ target_folder = 'lib/k_builder/assets'
55
+ file = "#{name}.min.js"
56
+
57
+ get_asset(url, target_folder, file)
58
+ end
59
+
60
+ def get_asset(url, target_folder, file)
61
+ local_asset_file = File.join(target_folder, file)
62
+
63
+ return if File.exist?(local_asset_file)
64
+
65
+ content = Net::HTTP.get(URI.parse(url))
66
+
67
+ File.write(local_asset_file, content)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBuilder
4
+ module Commands
5
+ # Run RuboCop against a file
6
+ class RuboCopCommand < BaseCommand
7
+ attr_reader :file_pattern
8
+ attr_reader :fix_safe
9
+ attr_reader :fix_unsafe
10
+ attr_reader :rubo_config_file
11
+ attr_reader :show_console
12
+
13
+ attr_reader :cop_options
14
+ attr_reader :cop_opt_values
15
+
16
+ # Initialize RuboCop command
17
+ #
18
+ # @param [String] file_pattern File name or file pattern
19
+ # @param [Hash] **opts The options
20
+ # @option opts [Boolean] :fix_safe RuboCop -a option will fix simple and safe issues
21
+ # @option opts [Boolean] :fix_unsafe RuboCop -A option will fix simple but potentially unsafe issues
22
+ # @option opts [Boolean] :show_console This will show in console, or if false set --out ~/last_cop.txt so that console is redirected to file
23
+ # @option opts [String] :rubo_config_file YAML file with RuboCop configuration options
24
+ #
25
+ # @example Cop for single file with auto fix turned on for simple issues
26
+ #
27
+ # RubCopCommand.new('abc.rb', fix_safe: true)
28
+ #
29
+ # @example Cop for all spec files to auto simple and unsafe issues
30
+ #
31
+ # RubCopCommand.new('spec/**/*.rb', fix_unsafe: true)
32
+ def initialize(file_pattern, **opts)
33
+ super(**opts)
34
+
35
+ @valid = true
36
+
37
+ self.file_pattern = file_pattern
38
+
39
+ self.fix_safe = opts[:fix_safe]
40
+ self.fix_unsafe = opts[:fix_unsafe]
41
+ self.show_console = opts[:show_console]
42
+ self.rubo_config_file = opts[:rubo_config_file]
43
+ end
44
+
45
+ def execute
46
+ return unless valid?
47
+
48
+ cop_run
49
+ end
50
+
51
+ def cli_options
52
+ cli_options = []
53
+ # quite is the same as simple, except you will see nothing if no offenses
54
+ cli_options << '--format' << 'quiet' # 'simple'
55
+ cli_options << '-a' if fix_safe
56
+ cli_options << '-A' if fix_unsafe
57
+ cli_options << '--config' << rubo_config_file if rubo_config_file
58
+ cli_options << '--out' << File.expand_path('~/last_cop.txt') unless show_console
59
+ cli_options << file_pattern
60
+ cli_options
61
+ end
62
+
63
+ def debug_values
64
+ log.kv 'rubocop target', file_pattern
65
+ log.kv '-a', 'automatic fix for safe issues' if fix_safe
66
+ log.kv '-A', 'automatic fix for potentially unsafe issues' if fix_unsafe
67
+ log.kv '-config', rubo_config_file if rubo_config_file
68
+ end
69
+
70
+ private
71
+
72
+ def file_pattern=(value)
73
+ @file_pattern = value
74
+
75
+ if value.nil? || value.empty?
76
+ guard 'file_pattern is required'
77
+ elsif Pathname.glob(value).length.zero?
78
+ guard 'file_pattern does not reference an existing file'
79
+ end
80
+ end
81
+
82
+ def fix_safe=(value)
83
+ @fix_safe = value || false
84
+ end
85
+
86
+ def fix_unsafe=(value)
87
+ @fix_unsafe = value || false
88
+ end
89
+
90
+ def show_console=(value)
91
+ @show_console = value || false
92
+ end
93
+
94
+ def rubo_config_file=(value)
95
+ @rubo_config_file = value
96
+
97
+ return if value.nil? || value.empty?
98
+
99
+ guard("Unknown RuboCop config file: #{value}") unless File.exist?(value)
100
+ end
101
+
102
+ def cop_run
103
+ cli = RuboCop::CLI.new
104
+
105
+ # log.section_heading('CLI OPTIONS')
106
+ # log.block cli_options
107
+
108
+ cli.run(cli_options)
109
+ end
110
+ end
111
+ end
112
+ end
@@ -5,18 +5,24 @@ module KBuilder
5
5
  # Configuration for webpack5/builder
6
6
  class << self
7
7
  attr_writer :configuration
8
- end
9
8
 
10
- def self.configuration
11
- @configuration ||= Configuration.new
12
- end
9
+ def configuration(name = :default)
10
+ @configuration ||= Hash.new do |h, key|
11
+ h[key] = KBuilder::Configuration.new
12
+ end
13
+ @configuration[name]
14
+ end
13
15
 
14
- def self.reset
15
- @configuration = Configuration.new
16
- end
16
+ def reset(name = :default)
17
+ @configuration ||= Hash.new do |h, key|
18
+ h[key] = KBuilder::Configuration.new
19
+ end
20
+ @configuration[name] = KBuilder::Configuration.new
21
+ end
17
22
 
18
- def self.configure
19
- yield(configuration)
23
+ def configure(name = :default)
24
+ yield(configuration(name))
25
+ end
20
26
  end
21
27
 
22
28
  # Does this class need to move out into k_types?
@@ -48,12 +54,16 @@ module KBuilder
48
54
  @template_folders = orig.template_folders.clone
49
55
  end
50
56
 
51
- def debug
52
- log.subheading 'kbuilder base configuration'
57
+ def debug(heading: 'kbuilder base configuration')
58
+ log.section_heading 'kbuilder base configuration' if heading
59
+
60
+ # TODO: Add name to configuration object
61
+ # Don't have support for name on the configuration object yet
62
+ # log.kv 'config name', name
53
63
 
54
64
  target_folders.debug(title: 'target_folders')
55
65
 
56
- log.info ''
66
+ # log.info ''
57
67
 
58
68
  template_folders.debug(title: 'template folders (search order)')
59
69
  ''