k_builder 0.0.61 → 0.0.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  ''