k_manager 0.0.24 → 0.0.32

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.
@@ -14,45 +14,109 @@ module KManager
14
14
  default: '.builders',
15
15
  desc: 'The builder folder, defaults to (.builders)'
16
16
 
17
- option :boot_file,
18
- default: 'boot.rb',
19
- aliases: ['-b'],
20
- desc: 'The name of the boot file to create'
21
-
22
17
  option :force,
23
18
  default: false,
24
19
  aliases: ['-f'],
25
20
  desc: 'Force even if guard fails'
26
21
 
22
+ option :template,
23
+ default: false,
24
+ aliases: ['-t'],
25
+ desc: 'Starter template'
26
+
27
+ option :log_level,
28
+ default: nil,
29
+ desc: 'Log level, use debug for more info'
30
+
31
+ option :description,
32
+ default: nil,
33
+ aliases: ['-d'],
34
+ desc: 'Application description'
35
+
36
+ option :user_story,
37
+ default: nil,
38
+ desc: 'Main user story'
39
+
40
+ option :repo_organization,
41
+ default: nil,
42
+ desc: 'Repository organization'
43
+
27
44
  example [
28
45
  ' # Project in current directory - will create a .builders folder and boot file at ./builders/boot.rb',
29
46
  '-b ../config/boot.rb # Project in current directory - will create a .builders folder and boot file at ./config/boot.rb',
47
+ '-t ruby_gem # Use starter template `ruby_gem` to setup the files in .builders/*',
30
48
  'my_project # will watch .xmen folder'
31
49
  ]
32
50
 
33
51
  # rubocop:disable Metrics/ParameterLists
34
- def call(project_folder:, builder_folder:, boot_file:, log_level:, force:, **)
35
- project_folder = absolute_path(project_folder, Dir.pwd)
36
- name = File.basename(project_folder)
37
- builder_folder = absolute_path(builder_folder, project_folder)
38
- boot_file = absolute_path(boot_file, builder_folder)
52
+ def call(project_folder:, builder_folder:, log_level:, force:, template:, **opts)
53
+ project_folder = absolute_path(project_folder, Dir.pwd)
54
+ name = File.basename(project_folder)
55
+ builder_folder = absolute_path(builder_folder, project_folder)
56
+ template_root_folder = File.expand_path('~/dev/kgems/k_templates/definitions/starter')
39
57
 
40
- log_params(name, project_folder, builder_folder, boot_file, force) if log_level == 'debug'
58
+ log_params(name, project_folder, builder_folder, force, log_level, template_root_folder, template, **opts) if log_level == 'debug'
41
59
 
42
- create_project(project_folder, builder_folder, boot_file) if can_create?(force, builder_folder)
60
+ create_project(name, project_folder, builder_folder, template_root_folder, template, **opts) if can_create?(force, builder_folder)
43
61
  end
44
62
  # rubocop:enable Metrics/ParameterLists
45
63
 
46
64
  private
47
65
 
48
- def create_project(project_folder, builder_folder, boot_file)
66
+ # rubocop:disable Metrics/ParameterLists
67
+ def create_project(name, project_folder, builder_folder, template_root_folder, template, **opts)
49
68
  FileUtils.mkdir_p(project_folder)
50
69
  FileUtils.mkdir_p(builder_folder)
51
- File.write(boot_file, '# Boot Sequence')
52
- # Use a boot_file_template if needed
70
+
71
+ # handle_main_start_command
72
+
73
+ setup_builder_from_template(name, builder_folder, template_root_folder, 'default', **opts) unless setup_builder_from_template(name, builder_folder, template_root_folder, template, **opts)
53
74
 
54
75
  log.info 'Project created'
55
76
  end
77
+ # rubocop:enable Metrics/ParameterLists
78
+
79
+ def setup_builder_from_template(name, builder_folder, template_root_folder, template, **opts)
80
+ return false unless template
81
+
82
+ # /Users/davidcruwys/dev/kgems/k_templates/definitions/starter/ruby_gem/.starter.json
83
+ template_folder = File.join(template_root_folder, template)
84
+ starter_config = template_starter_config(template_folder)
85
+
86
+ return false unless starter_config
87
+
88
+ builder = get_builder(builder_folder, template_folder)
89
+
90
+ return false if starter_config['files'].nil? || starter_config['files'].empty?
91
+
92
+ starter_config['files']&.each do |relative_file|
93
+ builder.add_file(relative_file, template_file: relative_file, **{ name: name }.merge(opts))
94
+ end
95
+ end
96
+
97
+ def template_starter_config(template_folder)
98
+ starter_file = File.join(template_folder, '.starter.json')
99
+
100
+ return nil unless File.exist?(starter_file)
101
+
102
+ JSON.parse(File.read(starter_file))
103
+ end
104
+
105
+ # rubocop:disable Metrics/AbcSize
106
+ def get_builder(builder_folder, template_folder)
107
+ Handlebars::Helpers.configure do |config|
108
+ config.helper_config_file = File.join(Gem.loaded_specs['handlebars-helpers'].full_gem_path, '.handlebars_helpers.json')
109
+ config.string_formatter_config_file = File.join(Gem.loaded_specs['handlebars-helpers'].full_gem_path, '.handlebars_string_formatters.json')
110
+ end
111
+
112
+ KConfig.configure(:starter_template) do |config|
113
+ config.target_folders.add(:builder , builder_folder)
114
+ config.template_folders.add(:template , template_folder)
115
+ end
116
+
117
+ KBuilder::BaseBuilder.init(KConfig.configuration(:starter_template))
118
+ end
119
+ # rubocop:enable Metrics/AbcSize
56
120
 
57
121
  def can_create?(force, builder_folder)
58
122
  return true if force
@@ -63,14 +127,22 @@ module KManager
63
127
  false
64
128
  end
65
129
 
66
- def log_params(name, project_folder, builder_folder, boot_file, force)
130
+ # rubocop:disable Metrics/ParameterLists, Metrics/AbcSize
131
+ def log_params(name, project_folder, builder_folder, force, log_level, template_root_folder, template, **opts)
67
132
  log.section_heading('Create new project')
68
- log.kv 'name' , name
69
- log.kv 'project_folder' , project_folder
70
- log.kv 'builder_folder' , builder_folder
71
- log.kv 'boot_file' , boot_file
72
- log.kv 'force' , force
133
+ log.kv 'name' , name
134
+ log.kv 'project_folder' , project_folder
135
+ log.kv 'builder_folder' , builder_folder
136
+ log.kv 'force' , force
137
+ log.kv 'log_level' , log_level
138
+ log.kv 'template_root_folder' , template_root_folder
139
+ log.kv 'template' , template
140
+
141
+ opts.each do |key, value|
142
+ log.kv key, value
143
+ end
73
144
  end
145
+ # rubocop:enable Metrics/ParameterLists, Metrics/AbcSize
74
146
  end
75
147
  end
76
148
  end
@@ -27,16 +27,32 @@ module KManager
27
27
 
28
28
  log_params(builder_folder, boot_file) if log_level == 'debug'
29
29
 
30
- # puts builder_folder
31
- # puts boot_file
30
+ # If you do a System Exit (control+c) you can go into a reboot sequence based on options
31
+ # if the option is not set then system will exit gracefully
32
+ while keep_watching(builder_folder, boot_file); end
33
+ end
34
+
35
+ private
32
36
 
37
+ # rubocop:disable Metrics/AbcSize
38
+ def keep_watching(builder_folder, boot_file)
33
39
  Dir.chdir(builder_folder) do
34
40
  watcher = KManager::Watcher.new(builder_folder, boot_file)
35
41
  watcher.start
36
42
  end
37
- end
43
+ false
44
+ rescue Interrupt, SystemExit
45
+ if KManager.opts.reboot_on_kill == true || KManager.opts.reboot_on_kill == 1
46
+ puts "\nRebooting #{KManager.opts.app_name}..."
47
+ sleep KManager.opts.reboot_sleep unless KManager.opts.reboot_sleep.zero?
38
48
 
39
- private
49
+ return true
50
+ end
51
+
52
+ puts "\nExiting..."
53
+ false
54
+ end
55
+ # rubocop:enable Metrics/AbcSize
40
56
 
41
57
  def log_params(builder_folder, boot_file)
42
58
  log.section_heading('Watch project')
@@ -5,6 +5,8 @@ module KManager
5
5
  #
6
6
  # TODO: Write Tests
7
7
  class Manager
8
+ include KLog::Logging
9
+
8
10
  attr_accessor :active_uri
9
11
 
10
12
  # NOTE: rename current_resource to active_resource, focused_resource?
@@ -28,7 +30,29 @@ module KManager
28
30
  yield(@current_resource)
29
31
  end
30
32
 
31
- Options = Struct.new(:sleep, :exception_style)
33
+ # Usage
34
+ #
35
+ # KManager.opts.sleep = 10
36
+ # KManager.opts.reboot_on_kill = 0
37
+ # KManager.opts.exception_style = :short
38
+ # KManager.opts.show.time_taken = true
39
+ # KManager.opts.show.finished = true
40
+ # KManager.opts.show.finished_message = 'FINISHED :)'
41
+
42
+ Options = Struct.new(
43
+ :app_name,
44
+ :exception_style,
45
+ :reboot_on_kill,
46
+ :reboot_sleep,
47
+ :sleep,
48
+ :show
49
+ )
50
+
51
+ Show = Struct.new(
52
+ :time_taken,
53
+ :finished,
54
+ :finished_message
55
+ )
32
56
 
33
57
  # @param [Integer] sleep Seconds to sleep after running, 0 = no sleep
34
58
  # @param [Symbol] exception_style Format for exception messages caught by watcher.
@@ -36,13 +60,31 @@ module KManager
36
60
  # :short - message and short backtrace
37
61
  # :long - message and long backtrace
38
62
  def opts
39
- @opts ||= Options.new(0, :message)
63
+ @opts ||= Options.new('', :message, false, 1, 0, Show.new(true, true, 'FINISHED :)'))
40
64
  end
41
65
 
42
66
  def areas
43
67
  @areas ||= []
44
68
  end
45
69
 
70
+ # List of resources for an area.
71
+ #
72
+ # if area is nil, then return resources for the area matching the current_resource
73
+ def area_resources(area: nil)
74
+ area = resolve_area(area)
75
+
76
+ log.error 'Could not resolve area' if area.nil?
77
+
78
+ area&.resources
79
+ end
80
+
81
+ # List of documents for an area.
82
+ #
83
+ # if area is nil, for the area matching the current_resource
84
+ def area_documents(area: nil)
85
+ area_resources(area: area)&.flat_map(&:documents)
86
+ end
87
+
46
88
  def add_area(name, namespace: nil)
47
89
  area = find_area(name)
48
90
 
@@ -54,16 +96,7 @@ module KManager
54
96
  end
55
97
 
56
98
  def find_document(tag, area: nil)
57
- area = resolve_area(area)
58
-
59
- log.error 'Could not resolve area' if area.nil?
60
-
61
- log.line
62
- log.error(tag)
63
- log.line
64
-
65
- documents = area.resources.flat_map(&:documents)
66
- documents.find { |d| d.tag == tag }
99
+ area_documents(area: area)&.find { |d| d.tag == tag }
67
100
  end
68
101
 
69
102
  def fire_actions(*actions)
@@ -79,14 +112,14 @@ module KManager
79
112
 
80
113
  def resolve_area(area)
81
114
  if area.nil?
82
- return KManager.current_resource.area if KManager.current_resource
115
+ return current_resource.area if current_resource
83
116
 
84
- return KManager.areas.first
117
+ return areas.first
85
118
  end
86
119
 
87
120
  return area if area.is_a?(Area)
88
121
 
89
- find_area(name)
122
+ find_area(area)
90
123
  end
91
124
 
92
125
  # Return a list of resources for a URI.
@@ -104,7 +137,19 @@ module KManager
104
137
  @active_uri = nil
105
138
  end
106
139
 
140
+ def boot
141
+ KManager.fire_actions(:load_content, :register_document, :preload_document, :load_document)
142
+ end
143
+
144
+ def reboot
145
+ puts 'Fire reboot'
146
+ KManager.opts.reboot_on_kill = 1
147
+ raise SystemExit
148
+ end
149
+
107
150
  def debug(*sections)
151
+ log.structure(KUtil.data.to_hash(opts))
152
+
108
153
  areas.each do |area|
109
154
  area.debug(*sections)
110
155
  end
@@ -5,6 +5,10 @@ module KManager
5
5
  # Generate dashboard information on the console
6
6
  #
7
7
  # TODO: ConsoleDashboard and HtmlConsole
8
+ # dashboard = KManager::Overview::Dashboard.new(KManager.manager)
9
+ # dashboard.areas
10
+ # dashboard.resources
11
+ # dashboard.documents
8
12
  class Dashboard
9
13
  include KLog::Logging
10
14
 
@@ -45,7 +49,7 @@ module KManager
45
49
  { namespace: { display_method: ->(row) { row.namespace } } },
46
50
  { status: { display_method: ->(row) { row.status } } },
47
51
  { content_type: { display_method: ->(row) { row.content_type } } },
48
- { content: { display_method: ->(row) { row.content } } },
52
+ { content: { display_method: ->(row) { format_content(row.content, row.content_type) }, width: 50 } },
49
53
  { document_count: { display_method: ->(row) { blank_zero(row.document_count) } } },
50
54
  { valid: { display_method: ->(row) { row.valid } } },
51
55
  { error_count: { display_method: ->(row) { blank_zero(row.errors.length) } } },
@@ -83,6 +87,7 @@ module KManager
83
87
  # # { path: { display_method: -> (row) { row.resource_path } } },
84
88
  # { exist: { display_method: -> (row) { row.resource_exist } } },
85
89
  { document_id: { display_method: ->(row) { blank_zero(row.document_id) } } },
90
+ { state: { display_method: ->(row) { document_state(row.document_state) } } },
86
91
  { data: { display_method: ->(row) { row.document_data } } },
87
92
  { error_count: { display_method: ->(row) { blank_zero(row.document_errors.length) } } },
88
93
  { key: { display_method: ->(row) { row.document_key } } },
@@ -119,12 +124,35 @@ module KManager
119
124
  log.structure(data, **opts)
120
125
  end
121
126
 
127
+ def format_content(content, type)
128
+ formatted = content
129
+ case type
130
+ when :ruby
131
+ formatted = formatted&.sub(/\A# frozen_string_literal: true/, '')&.strip
132
+ end
133
+ formatted
134
+ end
135
+
122
136
  def blank_zero(value)
123
137
  return nil if value.nil? || (value.is_a?(Integer) && value.zero?)
124
138
 
125
139
  value
126
140
  end
127
141
 
142
+ # Valid states are:
143
+ # :new
144
+ # :evaluated
145
+ # :initialized
146
+ # :children_evaluated
147
+ # :actioned
148
+ def document_state(state)
149
+ return 'unknown' if state.nil?
150
+ return 'loaded (partially)' if state == :evaluated # probably has unmet dependency
151
+ return 'loaded' if state == :children_evaluated
152
+
153
+ state.to_s
154
+ end
155
+
128
156
  def lpad(size, value)
129
157
  value.to_s.rjust(size)
130
158
  end
@@ -62,9 +62,12 @@ module KManager
62
62
  attribute :resource_relative_path , Types::Strict::String.optional.default(nil)
63
63
  attribute :resource_exist? , Types::Strict::Bool
64
64
  attribute :document_id , Types::Strict::Integer
65
+ attribute :document_state , Types::Strict::String | Types::Strict::Symbol
65
66
  attribute :document_data , Types::Strict::Any.optional.default # Hash.optional.default(nil) | Types::Strict::Array.of(Types::Strict::Hash).optional.default(nil)
66
67
  attribute :document_key , Types::Strict::String | Types::Strict::Symbol
67
- attribute :document_namespace , Types::Strict::String | Types::Strict::Array.of(Types::Strict::String).optional.default(nil)
68
+ attribute :document_namespace , Types::Strict::String | Types::Strict::Symbol |
69
+ Types::Strict::Array.of(Types::Strict::String).optional.default(nil) |
70
+ Types::Strict::Array.of(Types::Strict::Symbol).optional.default(nil)
68
71
  attribute :document_tag , Types::Strict::String | Types::Strict::Symbol
69
72
  attribute :document_type , Types::Strict::String | Types::Strict::Symbol
70
73
  # TODO: Write code to populate this with the resource line number
@@ -35,6 +35,7 @@ module KManager
35
35
  **area.attribute_values('area_'),
36
36
  **resource.attribute_values('resource_'),
37
37
  document_id: document.object_id,
38
+ document_state: document.block_state,
38
39
  document_data: document.data,
39
40
  document_key: document.key,
40
41
  document_namespace: document.namespace,
@@ -25,7 +25,7 @@ module KManager
25
25
  include KLog::Logging
26
26
  include KDoc::Guarded
27
27
 
28
- ACTIONS = %i[load_content register_document load_document].freeze
28
+ ACTIONS = %i[load_content register_document preload_document load_document].freeze
29
29
 
30
30
  class << self
31
31
  def valid_action?(action)
@@ -42,6 +42,8 @@ module KManager
42
42
  # - :content_loaded
43
43
  # - :documents_registering
44
44
  # - :documents_registered
45
+ # - :documents_preloading
46
+ # - :documents_preloaded
45
47
  # - :documents_loading
46
48
  # - :documents_loaded
47
49
  attr_reader :status
@@ -102,9 +104,9 @@ module KManager
102
104
  end
103
105
 
104
106
  # TODO: Is this really needed?
105
- def document
106
- @document ||= documents&.first
107
- end
107
+ # def document
108
+ # @document ||= documents&.first
109
+ # end
108
110
 
109
111
  def activated?
110
112
  # log.section_heading("Am I activated?")
@@ -120,7 +122,8 @@ module KManager
120
122
  # @param [Symbol] action what action is to be fired
121
123
  # - :load_content for loading text content
122
124
  # - :register_document for registering 1 or more documents (name and namespace) against the resource
123
- # - :load_document for parsing the content into a document
125
+ # - :preload_document for parsing the content into a document
126
+ # - :load_document for finalizing the document load with met dependencies and action execution if applicable
124
127
  # rubocop:disable Metrics/CyclomaticComplexity
125
128
  def fire_action(action)
126
129
  # TODO: Write test for valid
@@ -131,20 +134,25 @@ module KManager
131
134
  load_content_action if alive?
132
135
  when :register_document
133
136
  register_document_action if content_loaded?
137
+ when :preload_document
138
+ preload_document_action if documents_registered?
134
139
  when :load_document
135
- load_document_action if documents_registered?
140
+ load_document_action if documents_preloaded?
136
141
  else
137
142
  log.warn "Action: '#{action}' is invalid for status: '#{status}'"
138
143
  end
139
144
  end
140
145
  # rubocop:enable Metrics/CyclomaticComplexity
141
146
 
147
+ # I don't think this is needed, it is never really used
142
148
  def fire_next_action
143
149
  if alive?
144
150
  fire_action(:load_content)
145
151
  elsif content_loaded?
146
152
  fire_action(:register_document)
147
153
  elsif documents_registered?
154
+ fire_action(:preload_document)
155
+ elsif documents_preloaded?
148
156
  fire_action(:load_document)
149
157
  end
150
158
  end
@@ -162,20 +170,30 @@ module KManager
162
170
  end
163
171
 
164
172
  def register_document
165
- # log.warn 'you need to implement register_document'
166
173
  KManager::Resources::ResourceDocumentFactory.create_documents(self)
167
174
  end
168
175
 
176
+ # rubocop:disable Lint/RescueException
177
+ def preload_document
178
+ documents.each(&:execute_block)
179
+ rescue Exception => e
180
+ guard(e.message)
181
+ debug
182
+ log.exception(e, style: KManager.opts.exception_style)
183
+ # log.exception(e, style: :short)
184
+ end
185
+ # rubocop:enable Lint/RescueException
186
+
169
187
  # rubocop:disable Lint/RescueException
170
188
  def load_document
171
- # log.warn 'you need to implement register_document'
172
189
  documents.each do |document|
173
190
  document.execute_block(run_actions: activated?)
174
191
  end
175
192
  rescue Exception => e
176
193
  guard(e.message)
177
194
  debug
178
- log.exception(e, style: :short)
195
+ log.exception(e, style: KManager.opts.exception_style)
196
+ # log.exception(e, style: :short)
179
197
  end
180
198
  # rubocop:enable Lint/RescueException
181
199
 
@@ -239,6 +257,10 @@ module KManager
239
257
  @status == :documents_registered
240
258
  end
241
259
 
260
+ def documents_preloaded?
261
+ @status == :documents_preloaded
262
+ end
263
+
242
264
  def documents_loaded?
243
265
  @status == :documents_loaded
244
266
  end
@@ -317,14 +339,18 @@ module KManager
317
339
  def register_document_action
318
340
  @status = :documents_registering
319
341
  register_document
320
- # document_factory.create_documents
321
342
  @status = :documents_registered
322
343
  end
323
344
 
345
+ def preload_document_action
346
+ @status = :documents_preloading
347
+ preload_document
348
+ @status = :documents_preloaded
349
+ end
350
+
324
351
  def load_document_action
325
352
  @status = :documents_loading
326
353
  load_document
327
- # document_factory.parse_content
328
354
  @status = :documents_loaded
329
355
  end
330
356
  end
@@ -89,6 +89,8 @@ module KManager
89
89
  # if documents.length > 0
90
90
  # resource.resource_type = KDsl::Resources::Resource::TYPE_RUBY_DSL
91
91
  # end
92
+ rescue Interrupt, SystemExit
93
+ raise
92
94
  rescue Exception => e
93
95
  # Report the error but still add the document so that you can see
94
96
  # it in the ResourceDocument list, it will be marked as Error
@@ -93,8 +93,13 @@ module KManager
93
93
  replace_resource = resource.recreate(resource)
94
94
  replace_resource.fire_action(:load_content)
95
95
  replace_resource.fire_action(:register_document)
96
+ replace_resource.fire_action(:preload_document)
96
97
  replace_resource.fire_action(:load_document)
97
98
  resource_set.replace(replace_resource)
99
+
100
+ # This is a bit of a hack, but it works for now
101
+ # TODO: I don't think is actually working.
102
+ attach_dependencies
98
103
  end
99
104
 
100
105
  def delete_resource(resource_uri)
@@ -146,6 +151,7 @@ module KManager
146
151
 
147
152
  load_content if actions.include?(:load_content)
148
153
  register_documents if actions.include?(:register_document)
154
+ preload_documents if actions.include?(:preload_document)
149
155
  load_documents if actions.include?(:load_document)
150
156
  end
151
157
 
@@ -167,18 +173,45 @@ module KManager
167
173
  end
168
174
  end
169
175
 
176
+ def preload_documents
177
+ # first pass will attempt to load every document, if a document has dependencies
178
+ # it will be loaded in a second pass after dependencies are available
179
+ resources.each do |resource|
180
+ resource.fire_action(:preload_document)
181
+ end
182
+
183
+ attach_dependencies
184
+ end
185
+
170
186
  def load_documents
187
+ # second pass will finalize any documents that were partially load due to dependencies
171
188
  resources.each do |resource|
172
189
  resource.fire_action(:load_document)
173
190
  end
174
191
  end
175
192
 
193
+ def find_document(tag)
194
+ resources.flat_map(&:documents).find { |d| d.tag == tag }
195
+ end
196
+
176
197
  def debug
177
198
  resources.each(&:debug)
199
+ nil
178
200
  end
179
201
 
180
202
  private
181
203
 
204
+ def attach_dependencies
205
+ documents_with_unmet_dependencies = resources.flat_map(&:documents).reject(&:dependencies_met?)
206
+
207
+ documents_with_unmet_dependencies.each do |document|
208
+ document.depend_on_tags.each do |tag|
209
+ dependant_document = find_document(tag)
210
+ document.resolve_dependency(dependant_document) if dependant_document
211
+ end
212
+ end
213
+ end
214
+
182
215
  def parse_uri(uri)
183
216
  return uri if uri.is_a?(URI)
184
217
  return URI.parse(uri) if uri =~ URI::ABS_URI # https://stackoverflow.com/questions/1805761/how-to-check-if-a-url-is-valid
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KManager
4
- VERSION = '0.0.24'
4
+ VERSION = '0.0.32'
5
5
  end