Class: Howzit::Task

Inherits:
Object
  • Object
show all
Defined in:
lib/howzit/task.rb

Overview

Task object

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes, optional: false, default: true) ⇒ Task

Initialize a Task object

Parameters:

  • attributes (Hash)

    the task attributes

  • optional (Boolean) (defaults to: false)

    Task requires confirmation

  • default (Boolean) (defaults to: true)

    Default response for confirmation dialog

Options Hash (attributes):

  • :type (Symbol)

    task type (:block, :run, :include, :copy)

  • :title (String)

    task title

  • :action (String)

    task action

  • :parent (String)

    title of nested (included) topic origin

  • :log_level (String)

    log level for this task (debug, info, warn, error)

  • :source_file (String)

    path to the build note file this task came from



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/howzit/task.rb', line 25

def initialize(attributes, optional: false, default: true)
  @prefix = "{bw}\u{25B7}\u{25B7} {x}"
  # arrow = "{bw}\u{279F}{x}"
  @arguments = attributes[:arguments] || []

  @type = attributes[:type] || :run
  @title = attributes[:title]&.to_s
  @parent = attributes[:parent] || nil

  @action = attributes[:action].render_arguments || nil
  @log_level = attributes[:log_level]
  # Get source_file from parent topic if available, or from attributes
  parent_obj = attributes[:parent]
  @source_file = attributes[:source_file] || parent_obj&.source_file

  @optional = optional
  @default = default
  @last_status = nil
end

Instance Attribute Details

#actionObject (readonly)

Returns the value of attribute action.



8
9
10
# File 'lib/howzit/task.rb', line 8

def action
  @action
end

#argumentsObject (readonly)

Returns the value of attribute arguments.



8
9
10
# File 'lib/howzit/task.rb', line 8

def arguments
  @arguments
end

#defaultObject (readonly)

Returns the value of attribute default.



8
9
10
# File 'lib/howzit/task.rb', line 8

def default
  @default
end

#last_statusObject (readonly)

Returns the value of attribute last_status.



8
9
10
# File 'lib/howzit/task.rb', line 8

def last_status
  @last_status
end

#log_levelObject (readonly)

Returns the value of attribute log_level.



8
9
10
# File 'lib/howzit/task.rb', line 8

def log_level
  @log_level
end

#optionalObject (readonly)

Returns the value of attribute optional.



8
9
10
# File 'lib/howzit/task.rb', line 8

def optional
  @optional
end

#parentObject (readonly)

Returns the value of attribute parent.



8
9
10
# File 'lib/howzit/task.rb', line 8

def parent
  @parent
end

#source_fileObject (readonly)

Returns the value of attribute source_file.



8
9
10
# File 'lib/howzit/task.rb', line 8

def source_file
  @source_file
end

#titleObject (readonly)

Returns the value of attribute title.



8
9
10
# File 'lib/howzit/task.rb', line 8

def title
  @title
end

#typeObject (readonly)

Returns the value of attribute type.



8
9
10
# File 'lib/howzit/task.rb', line 8

def type
  @type
end

Instance Method Details

#apply_log_levelObject

Apply log level for this task



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/howzit/task.rb', line 163

def apply_log_level
  return unless @log_level

  level_map = {
    'debug' => 0,
    'info' => 1,
    'warn' => 2,
    'warning' => 2,
    'error' => 3
  }
  level_value = level_map[@log_level.downcase] || @log_level.to_i
  old_level = Howzit.options[:log_level]
  Howzit.options[:log_level] = level_value
  Howzit.console.log_level = level_value
  ENV['HOWZIT_LOG_LEVEL'] = @log_level.downcase
  old_level
end

#inspectString

Inspect

Returns:



50
51
52
# File 'lib/howzit/task.rb', line 50

def inspect
  %(<#Howzit::Task @type=:#{@type} @title="#{@title}" @action="#{@action}" @arguments=#{@arguments} @block?=#{@action.split(/\n/).count > 1}>)
end

#restore_log_level(old_level) ⇒ Object

Restore log level after task execution



184
185
186
187
188
189
190
# File 'lib/howzit/task.rb', line 184

def restore_log_level(old_level)
  return unless @log_level

  Howzit.options[:log_level] = old_level
  Howzit.console.log_level = old_level
  ENV.delete('HOWZIT_LOG_LEVEL')
end

#runObject

Execute the task



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/howzit/task.rb', line 282

def run
  output = []
  tasks = 1
  res = if @type == :block
          run_block
        else
          case @type
          when :include
            output, tasks = run_include
          when :run
            run_run
          when :copy
            run_copy
          when :open
            Util.os_open(@action)
            @last_status = 0
            true
          end
        end

  [output, tasks, res]
end

#run_blockObject

Execute a block type



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/howzit/task.rb', line 66

def run_block
  Howzit.console.info "#{@prefix}{bg}Running block {bw}#{@title}{x}".c if Howzit.options[:log_level] < 2
  block = @action
  # Apply variable substitution to block content at execution time
  # (variables from previous run blocks are now available)
  block = block.render_arguments if block && !block.empty?
  script = Tempfile.new('howzit_script')
  comm_file = ScriptComm.setup
  old_log_level = apply_log_level

  # Get execution directory from source_file
  # Only change directory in stack mode, and only if source_file is from a parent directory (not a template)
  exec_dir = nil
  if @source_file && Howzit.options[:stack]
    expanded_source = File.expand_path(@source_file)
    source_dir = File.dirname(expanded_source)

    # Check if this is a template file - don't change directory for templates
    is_template = false
    if Howzit.config.respond_to?(:template_folder) && Howzit.config.template_folder
      template_folder = File.expand_path(Howzit.config.template_folder)
      is_template = expanded_source.start_with?(template_folder)
    end

    # Only change directory if not a template
    exec_dir = source_dir unless is_template
  end
  original_dir = Dir.pwd

  begin
    # Change to source file directory if available and different from current
    # Only change if the directory exists and is actually different
    if exec_dir && Dir.exist?(exec_dir)
      expanded_exec_dir = File.expand_path(exec_dir)
      expanded_original = File.expand_path(original_dir)
      Dir.chdir(expanded_exec_dir) if expanded_exec_dir != expanded_original
    end

    # Ensure support directory exists and install helpers
    ScriptSupport.ensure_support_dir
    ENV['HOWZIT_SUPPORT_DIR'] = ScriptSupport.support_dir

    # Inject helper script loading
    modified_block, interpreter = ScriptSupport.inject_helper(block)

    script.write(modified_block)
    script.close
    File.chmod(0o755, script.path)

    # Use appropriate interpreter command
    cmd = ScriptSupport.execution_command_for(script.path, interpreter)
    # If interpreter is nil, execute directly (will respect hashbang)
    res = if interpreter.nil?
            system(script.path)
          else
            system(cmd)
          end
  ensure
    # Restore original directory
    if exec_dir && Dir.exist?(exec_dir)
      expanded_exec_dir = File.expand_path(exec_dir)
      expanded_original = File.expand_path(original_dir)
      Dir.chdir(expanded_original) if expanded_exec_dir != expanded_original
    end
    restore_log_level(old_log_level) if old_log_level
    script.close
    script.unlink
    # Process script communication
    ScriptComm.apply(comm_file) if comm_file
  end

  update_last_status(res ? 0 : 1)
  res
end

#run_copyObject

Execute a copy task



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/howzit/task.rb', line 260

def run_copy
  # If a title was explicitly provided (different from action), always use it
  # Otherwise, use action (or respect show_all_code if no title)
  display_title = if @title && !@title.empty? && @title != @action
                    # Title was explicitly provided, use it
                    @title
                  elsif Howzit.options[:show_all_code]
                    # No explicit title, show code if requested
                    @action
                  else
                    # No explicit title, use title if available (might be same as action), otherwise action
                    @title && !@title.empty? ? @title : @action
                  end
  Howzit.console.info("#{@prefix}{bg}Copied {bw}#{display_title}{bg} to clipboard{x}".c)
  Util.os_copy(@action)
  @last_status = 0
  true
end

#run_includeArray

Execute an include task

Returns:

  • (Array)
    [Array

    output, [Integer] number of tasks executed]



146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/howzit/task.rb', line 146

def run_include
  output = []
  action = @action

  matches = Howzit.buildnote.find_topic(action)
  raise "Topic not found: #{action}" if matches.empty?

  Howzit.console.info("#{@prefix}{by}Running tasks from {bw}#{matches[0].title}{x}".c)
  output.concat(matches[0].run(nested: true))
  Howzit.console.info("{by}End include: #{matches[0].tasks.count} tasks{x}".c)
  @last_status = nil
  [output, matches[0].tasks.count]
end

#run_runObject

Execute a run task



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/howzit/task.rb', line 195

def run_run
  # If a title was explicitly provided (different from action), always use it
  # Otherwise, use action (or respect show_all_code if no title)
  display_title = if @title && !@title.empty? && @title != @action
                    # Title was explicitly provided, use it
                    @title
                  elsif Howzit.options[:show_all_code]
                    # No explicit title, show code if requested
                    @action
                  else
                    # No explicit title, use title if available (might be same as action), otherwise action
                    @title && !@title.empty? ? @title : @action
                  end
  Howzit.console.info("#{@prefix}{bg}Running {bw}#{display_title}{x}".c)
  ENV['HOWZIT_SCRIPTS'] = File.expand_path('~/.config/howzit/scripts')
  comm_file = ScriptComm.setup
  old_log_level = apply_log_level

  # Get execution directory from source_file
  # Only change directory in stack mode, and only if source_file is from a parent directory (not a template)
  exec_dir = nil
  if @source_file && Howzit.options[:stack]
    expanded_source = File.expand_path(@source_file)
    source_dir = File.dirname(expanded_source)

    # Check if this is a template file - don't change directory for templates
    is_template = false
    if Howzit.config.respond_to?(:template_folder) && Howzit.config.template_folder
      template_folder = File.expand_path(Howzit.config.template_folder)
      is_template = expanded_source.start_with?(template_folder)
    end

    # Only change directory if not a template
    exec_dir = source_dir unless is_template
  end
  original_dir = Dir.pwd

  begin
    # Change to source file directory if available and different from current
    # Only change if the directory exists and is actually different
    if exec_dir && Dir.exist?(exec_dir)
      expanded_exec_dir = File.expand_path(exec_dir)
      expanded_original = File.expand_path(original_dir)
      Dir.chdir(expanded_exec_dir) if expanded_exec_dir != expanded_original
    end

    res = system(@action)
  ensure
    # Restore original directory
    if exec_dir && Dir.exist?(exec_dir)
      expanded_exec_dir = File.expand_path(exec_dir)
      expanded_original = File.expand_path(original_dir)
      Dir.chdir(expanded_original) if expanded_exec_dir != expanded_original
    end
    restore_log_level(old_log_level) if old_log_level
    # Process script communication
    ScriptComm.apply(comm_file) if comm_file
  end
  update_last_status(res ? 0 : 1)
  res
end

#to_listString

Output terminal-formatted list item

Returns:

  • (String)

    List representation of the object.



319
320
321
322
323
324
325
326
327
# File 'lib/howzit/task.rb', line 319

def to_list
  # Highlight variables in title if parent topic has the method
  display_title = if @parent.respond_to?(:highlight_variables)
                    @parent.highlight_variables(@title.preserve_escapes)
                  else
                    @title.preserve_escapes
                  end
  "    * #{@type}: #{display_title}"
end

#to_sString

Output string representation

Returns:

  • (String)

    string representation of the object.



59
60
61
# File 'lib/howzit/task.rb', line 59

def to_s
  @title
end

#update_last_status(default = nil) ⇒ Object



305
306
307
308
309
310
311
312
# File 'lib/howzit/task.rb', line 305

def update_last_status(default = nil)
  status = if defined?($CHILD_STATUS) && $CHILD_STATUS
             $CHILD_STATUS.exitstatus
           else
             default
           end
  @last_status = status
end