Class: RepoManager::BaseAction

Inherits:
Object
  • Object
show all
Defined in:
lib/repo_manager/actions/base_action.rb

Overview

An abstract superclass for basic action functionality

Direct Known Subclasses

AppAction

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args = [], config = {}) ⇒ BaseAction

Returns a new instance of BaseAction.



37
38
39
40
41
42
# File 'lib/repo_manager/actions/base_action.rb', line 37

def initialize(args=[], config={})
  @configuration = config.deep_clone
  @options = @configuration[:options] || {}
  @args = args.dup
  logger.debug "initialize with args: #{@args.inspect}"
end

Instance Attribute Details

#argsObject (readonly)

args as passed on command line



23
24
25
# File 'lib/repo_manager/actions/base_action.rb', line 23

def args
  @args
end

#configurationObject (readonly)

main configuration hash



17
18
19
# File 'lib/repo_manager/actions/base_action.rb', line 17

def configuration
  @configuration
end

#exit_codeObject (readonly)

numeric exit code set from return of process method



32
33
34
# File 'lib/repo_manager/actions/base_action.rb', line 32

def exit_code
  @exit_code
end

#option_parserObject

bin wrapper option parser object



35
36
37
# File 'lib/repo_manager/actions/base_action.rb', line 35

def option_parser
  @option_parser
end

#optionsObject (readonly)

options hash, read from configuration hash



20
21
22
# File 'lib/repo_manager/actions/base_action.rb', line 20

def options
  @options
end

#outputObject

filename to write output



29
30
31
# File 'lib/repo_manager/actions/base_action.rb', line 29

def output
  @output
end

#templateObject

filename to template for rendering



26
27
28
# File 'lib/repo_manager/actions/base_action.rb', line 26

def template
  @template
end

Instance Method Details

#after_executeObject



294
295
296
# File 'lib/repo_manager/actions/base_action.rb', line 294

def after_execute
  logger.debug "callback: after_execute"
end

#asset_optionsObject

asset options separated from assets to make it easier to override assets



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/repo_manager/actions/base_action.rb', line 185

def asset_options
  # include all base action options
  result = options.deep_clone

  # anything left on the command line should be filters as all options have
  # been consumed, for pass through options, filters must be ignored by overwritting them
  filters = args.dup
  filters += result[:filter] if result[:filter]
  result = result.merge(:filter => filters) unless filters.empty?

  # asset type to create
  type = result[:type] || asset_type
  result = result.merge(:type => type)

  # optional key: :assets_folder, absolute path or relative to config file if :base_folder is specified
  result = result.merge(:assets_folder => configuration[:folders][:assets]) if configuration[:folders]

  # optional key: :base_folder is the folder that contains the main config file
  result = result.merge(:base_folder => File.dirname(configuration[:configuration_filename])) if configuration[:configuration_filename]

  result
end

#asset_typeSymbol

Used by asset factory to create assets. Override in app_action.rb or a descendant to set the class to be instantiated by by the AssetManager.

Returns:

  • (Symbol)

    asset type



180
181
182
# File 'lib/repo_manager/actions/base_action.rb', line 180

def asset_type
  :app_asset
end

#assetsObject

TODO: create items/app_item class with at least the ‘name’ accessor

assets: raw configuration handling system for items



171
172
173
174
# File 'lib/repo_manager/actions/base_action.rb', line 171

def assets
  return @assets if @assets
  @assets = AssetManager.new.assets(asset_options)
end

#before_executeObject

callbacks



290
291
292
# File 'lib/repo_manager/actions/base_action.rb', line 290

def before_execute
  logger.debug "callback: before_execute"
end

#executeObject



138
139
140
141
142
143
144
# File 'lib/repo_manager/actions/base_action.rb', line 138

def execute
  before_execute
  parse_options
  @exit_code = process
  after_execute
  @exit_code
end

#help(help_options = {}) ⇒ String

Convert method comments block to help text

Returns:

  • (String)

    suitable for displaying on STDOUT



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/repo_manager/actions/base_action.rb', line 243

def help(help_options={})
  comment_starting_with = help_options[:comment_starting_with] || ""
  located_in_file = help_options[:located_in_file] || __FILE__
  text = File.read(located_in_file)

  result = text.match(/(^\s*#\s*#{comment_starting_with}.*)^\s*class .* AppAction/m)
  result = $1
  result = result.gsub(/ @example/, '')
  result = result.gsub(/ @return \[Number\]/, ' Exit code:')
  result = result.gsub(/ @return .*/, '')
  result = result.gsub(/ @see .*$/, '')

  # strip the leading whitespace, the '#' and space
  result = result.gsub(/^\s*# ?/, '')

  # strip surrounding whitespace
  result.strip
end

#itemsArray

items to be rendered, defaults to assets, override to suit

Returns:

  • (Array)

    of items to be rendered



211
212
213
# File 'lib/repo_manager/actions/base_action.rb', line 211

def items
  assets
end

#overwrite_output?Boolean

Returns true if output doesn’t exist or it is OK to overwrite.

Returns:

  • (Boolean)

    true if output doesn’t exist or it is OK to overwrite



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/repo_manager/actions/base_action.rb', line 263

def overwrite_output?
  return true unless File.exists?(output)

  if options[:force]
    logger.debug "overwriting output with --force option"
    return true
  end

  unless STDOUT.isatty
    logger.debug "TTY not detected, skipping overwrite prompt"
    return false
  end

  result = false
  print "File '#{output}' exists. Would you like overwrite? [y/n]: "
  case gets.strip
    when 'Y', 'y', 'yes'
      logger.debug "user answered yes to overwrite prompt"
      result = true
    else
      logger.debug "user answered no to overwrite prompt"
  end

  result
end

#parse_options(parser_configuration = {}) {|option_parser| ... } ⇒ OptionParser

Parse generic action options for all decendant actions

Yields:

Returns:

  • (OptionParser)

    for use by decendant actions



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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
# File 'lib/repo_manager/actions/base_action.rb', line 47

def parse_options(parser_configuration = {})
  raise_on_invalid_option = parser_configuration.has_key?(:raise_on_invalid_option) ? parser_configuration[:raise_on_invalid_option] : true
  parse_base_options = parser_configuration.has_key?(:parse_base_options) ? parser_configuration[:parse_base_options] : true
  logger.debug "parsing args: #{@args.inspect}, raise_on_invalid_option: #{raise_on_invalid_option}, parse_base_options: #{parse_base_options}"

  @option_parser ||= OptionParser.new

  option_parser.banner = help + "\n\nOptions:"

  if parse_base_options
    option_parser.on("--template [NAME]", "Use a template to render output. (default=default.slim)") do |t|
      options[:template] = t.nil? ? "default.slim" : t
      @template = options[:template]
    end

    option_parser.on("--output FILENAME", "Render output directly to a file") do |f|
      options[:output] = f
      @output = options[:output]
    end

    option_parser.on("--force", "Overwrite file output without prompting") do |f|
      options[:force] = f
    end

    option_parser.on("-r", "--repos a1,a2,a3", "--asset a1,a2,a3", "--filter a1,a2,a3", Array, "List of regex asset name filters") do |list|
      options[:filter] = list
    end

    # NOTE: OptionParser will add short options, there is no way to stop '-m' from being the same as '--match'
    option_parser.on("--match [MODE]", "Asset filter match mode.  MODE=ALL (default), FIRST, EXACT, or ONE (fails if more than 1 match)") do |m|
      options[:match] = m || "ALL"
      options[:match].upcase!
      unless ["ALL", "FIRST", "EXACT", "ONE"].include?(options[:match])
        puts "invalid match mode option: #{options[:match]}"
        exit 1
      end
    end
  end

  # allow decendants to add options
  yield option_parser if block_given?

  # reprocess args for known options, see binary wrapper for first pass
  # (first pass doesn't know about action specific options), find all
  # action options that may come after the action/subcommand (options
  # before subcommand have already been processed) and its args
  logger.debug "args before reprocessing: #{@args.inspect}"
  begin
    option_parser.order!(@args)
  rescue OptionParser::InvalidOption => e
    if raise_on_invalid_option
      puts "option error: #{e}"
      puts option_parser
      exit 1
    else
      # parse and consume until we hit an unknown option (not arg), put it back so it
      # can be shifted into the new array
      e.recover(@args)
    end
  end
  logger.debug "args before unknown collection: #{@args.inspect}"

  unknown_args = []
  while unknown_arg = @args.shift
    logger.debug "unknown_arg: #{unknown_arg.inspect}"
    unknown_args << unknown_arg
    begin
      # consume options and stop at an arg
      option_parser.order!(@args)
    rescue OptionParser::InvalidOption => e
      if raise_on_invalid_option
        puts "option error: #{e}"
        puts option_parser
        exit 1
      else
        # parse and consume until we hit an unknown option (not arg), put it back so it
        # can be shifted into the new array
        e.recover(@args)
      end
    end
  end
  logger.debug "args after unknown collection: #{@args.inspect}"

  @args = unknown_args.dup
  logger.debug "args after reprocessing: #{@args.inspect}"
  logger.debug "configuration after reprocessing: #{@configuration.inspect}"
  logger.debug "options after reprocessing: #{@options.inspect}"

  option_parser
end

#processObject

handle “assets to items” transformations, if any, and write to output



147
148
149
# File 'lib/repo_manager/actions/base_action.rb', line 147

def process
  write_to_output(render)
end

#render(view_options = configuration) ⇒ String

Render items result to a string

Returns:

  • (String)

    suitable for displaying on STDOUT or writing to a file



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/repo_manager/actions/base_action.rb', line 218

def render(view_options=configuration)
  logger.debug "rendering"
  result = ""
  if template
    logger.debug "rendering with template : #{template}"
    view = AppView.new(items, view_options)
    view.template = template
    result = view.render
  else
    items.each_with_index do |item, index|
      result += "\n" unless index == 0
      result += item.name.green + ":\n"
      if item.respond_to?(:attributes)
        attributes = item.attributes.deep_clone
        result += attributes.recursively_stringify_keys!.to_conf.gsub(/\s+$/, '') # strip trailing whitespace from YAML
        result += "\n"
      end
    end
  end
  result
end

#write_to_output(content) ⇒ Object

TODO: add exception handler and pass return values



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/repo_manager/actions/base_action.rb', line 152

def write_to_output(content)
  if output
    logger.debug "write_to_output called with output : #{output}"
    if overwrite_output?
      logger.debug "writing output to : #{output}"
      File.open(output, 'wb') {|f| f.write(content) }
    else
      logger.info "existing file not overwritten.  To overwrite automatically, use the '--force' option."
    end
  else
    logger.debug "writing to STDOUT"
    print content
  end
  return 0
end