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:

  • the path to a Puppet file

Returns:

  • the Puppet class name

Raises:

  • if there is not class declaration in the file



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:

  • the path to the Puppet class

Returns:

  • 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:

  • 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:

  • path to a directory containing Puppet class files

Returns:

  • 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:

  • path to a dir containing Puppet manifests

Returns:

  • 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:

  • A Pathname object of the module's manifests dir

Returns:

  • 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 to a Puppet class file

Returns:

  • 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:

  • full Puppet class name

Returns:

  • 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..]))
end

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

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

Parameters:

  • path of the original file

  • path of the new file

  • Sets verbose mode on file operations

  • 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:

  • the original class name

  • the new class name

  • the path to the class file

  • Sets verbose mode on file operations

  • If true, file move file overwrite existing files

Raises:

  • if the class file does not contain the from class declaration



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:

  • Puppet class name

Returns:

  • 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