Module: AbideDevUtils::Ppt::ClassUtils

Includes:
Errors::Ppt
Defined in:
lib/abide_dev_utils/ppt/class_utils.rb

Constant Summary collapse

CLASS_NAME_PATTERN =
/\A([a-z][a-z0-9_]*)?(::[a-z][a-z0-9_]*)*\Z/.freeze
CLASS_NAME_CAPTURE_PATTERN =
/\A^class (?<class_name>([a-z][a-z0-9_]*)?(::[a-z][a-z0-9_]*)*).*\Z/.freeze

Class Method Summary collapse

Class Method Details

.class_name_from_declaration(path) ⇒ String

Takes a path to a Puppet file and extracts the class name from the class declaration in the file. This differs from class_name_from_path because we actually read the class file and search the code for a class declaration to get the class name instead of just using the path to construct a valid Puppet class name.

Parameters:

  • path (String)

    the path to a Puppet file

Returns:

  • (String)

    the Puppet class name

Raises:



52
53
54
55
56
57
58
59
# File 'lib/abide_dev_utils/ppt/class_utils.rb', line 52

def self.class_name_from_declaration(path)
  File.readlines(path).each do |line|
    next unless line.match?(/^class /)

    return CLASS_NAME_CAPTURE_PATTERN.match(line)['class_name']
  end
  raise ClassDeclarationNotFoundError, "Path:#{path}"
end

.class_name_from_path(class_path) ⇒ String

Returns the namespaced class name from a file path

Parameters:

  • class_path (String)

    the path to the Puppet class

Returns:

  • (String)

    the namespaced class name



36
37
38
39
40
41
42
43
# File 'lib/abide_dev_utils/ppt/class_utils.rb', line 36

def self.class_name_from_path(class_path)
  parts = class_path.split(File::SEPARATOR).map { |x| x == '' ? File::SEPARATOR : x }
  module_root_idx = parts.find_index('manifests') - 1
  module_root = parts[module_root_idx].split('-')[-1]
  namespaces = parts[(module_root_idx + 2)..-2].join('::') # add 2 to module root idx to skip manifests dir
  class_name = parts[-1].delete_suffix('.pp')
  [module_root, namespaces, class_name].join('::')
end

.find_all_classes_and_paths(puppet_class_dir) ⇒ Array

Returns An array of frozen arrays where each sub-array's index 0 is class_name and index 1 is the full path to the file.

Returns:

  • (Array)

    An array of frozen arrays where each sub-array's index 0 is class_name and index 1 is the full path to the file.



172
173
174
175
176
177
178
179
180
181
# File 'lib/abide_dev_utils/ppt/class_utils.rb', line 172

def self.find_all_classes_and_paths(puppet_class_dir)
  all_cap = []
  Dir.each_child(puppet_class_dir) do |c|
    path = "#{puppet_class_dir}/#{c}"
    next if File.directory?(path) || File.extname(path) != '.pp'

    all_cap << [File.basename(path, '.pp'), path].freeze
  end
  all_cap
end

.find_all_mismatched_class_declarations(class_dir) ⇒ Array

Finds all Puppet classes in the given directory that have class declarations that do not adhere to the autoload path pattern.

Parameters:

  • class_dir (String)

    path to a directory containing Puppet class files

Returns:

  • (Array)

    paths to all Puppet class files with mismatched class names



117
118
119
120
121
122
123
# File 'lib/abide_dev_utils/ppt/class_utils.rb', line 117

def self.find_all_mismatched_class_declarations(class_dir)
  mismatched = []
  Dir[File.join(File.expand_path(class_dir), '*.pp')].each do |class_file|
    mismatched << class_file if mismatched_class_declaration?(class_file)
  end
  mismatched.sort
end

.find_class_namespace(puppet_class_dir) ⇒ String

Given a directory holding Puppet manifests, returns the full namespace for all classes in that directory.

Parameters:

  • puppet_class_dir (String)

    path to a dir containing Puppet manifests

Returns:

  • (String)

    The namespace for all classes in manifests in the dir



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/abide_dev_utils/ppt/class_utils.rb', line 129

def self.find_class_namespace(puppet_class_dir)
  path = Pathname.new(puppet_class_dir)
  mod_root = nil
  ns_parts = []
  found_manifests = false
  path.ascend do |p|
    if found_manifests
      mod_root = find_mod_root(p)
      break
    end
    if File.basename(p) == 'manifests'
      found_manifests = true
      next
    else
      ns_parts << File.basename(p)
    end
  end
  "#{mod_root}::#{ns_parts.reverse.join('::')}::"
end

.find_mod_root(pathname) ⇒ String

Given a Pathname object of the 'manifests' directory in a Puppet module, determines the module namespace root. Does this by consulting metadata.json, if it exists, or by using the parent directory name.

Parameters:

  • pathname (Pathname)

    A Pathname object of the module's manifests dir

Returns:

  • (String)

    The module's namespace root



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/abide_dev_utils/ppt/class_utils.rb', line 154

def self.find_mod_root(pathname)
   = nil
  pathname.entries.each do |e|
     = "#{pathname}/metadata.json" if File.basename(e) == 'metadata.json'
  end
  if .nil?
    File.basename(p)
  else
    File.open() do |f|
      file = JSON.parse(f.read)
      File.basename(p) unless file.key?('name')
      file['name'].split('-')[-1]
    end
  end
end

.mismatched_class_declaration?(path) ⇒ Boolean

Determines if a Puppet class name is mismatched by constructing a class name from a path to a Puppet file and extracting the class name from the class declaration inside the file. This is useful to determine if a Puppet class file breaks the autoload path pattern.

Parameters:

  • path (String)

    path to a Puppet class file

Returns:

  • (Boolean)

    if the actual class name and path-constructed class name match



109
110
111
# File 'lib/abide_dev_utils/ppt/class_utils.rb', line 109

def self.mismatched_class_declaration?(path)
  class_name_from_path(path) != class_name_from_declaration(path)
end

.path_from_class_name(class_name) ⇒ String

Takes a full Puppet class name and returns the path of the class file. This command must be run from the root module directory if validate_path is true.

Parameters:

  • class_name (String)

    full Puppet class name

Returns:

  • (String)

    path to class file



27
28
29
30
31
# File 'lib/abide_dev_utils/ppt/class_utils.rb', line 27

def self.path_from_class_name(class_name)
  parts = class_name.split('::')
  parts[-1] = "#{parts[-1]}.pp"
  File.expand_path(File.join('manifests', parts[1..-1]))
end

.rename_class_file(from_path, to_path, **kwargs) ⇒ Object

Renames a file by file move. Ensures destination path exists before moving.

Parameters:

  • from_path (String)

    path of the original file

  • to_path (String)

    path of the new file

  • verbose (Boolean)

    Sets verbose mode on file operations

  • force (Boolean)

    If true, file move file overwrite existing files



66
67
68
69
70
71
# File 'lib/abide_dev_utils/ppt/class_utils.rb', line 66

def self.rename_class_file(from_path, to_path, **kwargs)
  verbose = kwargs.fetch(:verbose, false)
  force = kwargs.fetch(:force, false)
  FileUtils.mkdir_p(File.dirname(to_path), verbose: verbose)
  FileUtils.mv(from_path, to_path, verbose: verbose, force: force)
end

.rename_puppet_class_declaration(from, to, file_path, **kwargs) ⇒ Object

Renames a Puppet class in the class declaration of the given file

Parameters:

  • from (String)

    the original class name

  • to (String)

    the new class name

  • file_path (String)

    the path to the class file

  • verbose (Boolean)

    Sets verbose mode on file operations

  • force (Boolean)

    If true, file move file overwrite existing files

Raises:



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/abide_dev_utils/ppt/class_utils.rb', line 80

def self.rename_puppet_class_declaration(from, to, file_path, **kwargs)
  verbose = kwargs.fetch(:verbose, false)
  force = kwargs.fetch(:force, false)
  temp_file = Tempfile.new
  renamed = false
  begin
    File.readlines(file_path).each do |line|
      if line.match?(/^class #{from}.*/)
        line.gsub!(/^class #{from}/, "class #{to}")
        renamed = true
      end
      temp_file.puts line
    end
    raise ClassDeclarationNotFoundError, "File:#{file_path},Declaration:class #{from}" unless renamed

    temp_file.close
    FileUtils.mv(temp_file.path, file_path, verbose: verbose, force: force)
  ensure
    temp_file.close
    temp_file.unlink
  end
end

.valid_class_name?(name) ⇒ Boolean

Validates a Puppet class name

Parameters:

  • name (String)

    Puppet class name

Returns:

  • (Boolean)

    Is the name a valid Puppet class name



18
19
20
# File 'lib/abide_dev_utils/ppt/class_utils.rb', line 18

def self.valid_class_name?(name)
  name.match?(CLASS_NAME_PATTERN)
end