Module: Freyia::Automations
- Included in:
- Setup
- Defined in:
- lib/freyia/automations.rb,
lib/freyia/automations/directory.rb,
lib/freyia/automations/create_file.rb,
lib/freyia/automations/create_link.rb,
lib/freyia/automations/empty_directory.rb,
lib/freyia/automations/inject_into_file.rb,
lib/freyia/automations/file_manipulation.rb
Defined Under Namespace
Classes: CapturableERB, CreateFile, CreateLink, Directory, EmptyDirectory, InjectIntoFile
Constant Summary collapse
- WARNINGS =
{ unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the " \ "content has already been inserted!", }.freeze
Class Method Summary collapse
-
.escape_globs(path) ⇒ String
Returns a string that has had any glob characters escaped.
Instance Method Summary collapse
-
#append_to_file(path, **config) ⇒ Object
(also: #append_file)
Append text to a file.
-
#apply(path, verbose: true) ⇒ Object
Loads an external file and execute it in the instance binding.
-
#chmod(path, mode, **config) ⇒ Object
Changes the mode of the given file or directory.
-
#comment_lines(path, flag) ⇒ Object
Comment all lines matching a given regex.
-
#copy_file(source, destination = source, **config, &block) ⇒ Object
Copies the file from the relative source to the relative destination.
-
#create_file(destination, data = nil, **config, &block) ⇒ Object
(also: #add_file)
Create a new file relative to the destination root with the given data, which is the return value of a block or a data string.
-
#create_link(destination, source, **config) ⇒ Object
(also: #add_link)
Create a new symbolic link relative to the destination root from the given source.
-
#destination_root ⇒ Object
Returns the root for this freyia class (also aliased as destination root).
-
#destination_root=(root) ⇒ Object
Sets the root for this freyia class.
-
#directory(source, destination = nil, **config) ⇒ Object
Copies recursively the files from source directory to root directory.
-
#empty_directory(destination, **config) ⇒ Object
Creates an empty directory.
-
#find_in_source_paths(file) ⇒ Object
Receives a file or directory and search for it in the source paths.
-
#get(source, destination = nil, **config, &block) ⇒ Object
Gets the content at the given address and places it at the given relative destination.
-
#gsub_file(path, flag, *args, verbose: true) ⇒ Object
Run a regular expression replacement on a file.
-
#gsub_file!(path, flag, *args, verbose: true) ⇒ Object
Run a regular expression replacement on a file, raising an error if the contents of the file are not changed.
-
#in_root ⇒ Object
Goes to the root and execute the given block.
-
#inject_into_class(path, klass, **config) ⇒ Object
Injects text right after the class definition.
-
#inject_into_module(path, module_name, **config) ⇒ Object
Injects text right after the module definition.
-
#insert_into_file(destination, *args, **config, &block) ⇒ Object
(also: #inject_into_file)
Injects the given content into a file.
-
#insert_into_file!(destination, *args, **config, &block) ⇒ Object
(also: #inject_into_file!)
Injects the given content into a file, raising an error if the contents of the file are not changed.
-
#inside(dir = "", verbose: false, &block) ⇒ Object
Do something in the root or on a provided subfolder.
-
#link_file(source, destination = source, **config) ⇒ Object
Links the file from the relative source to the relative destination.
-
#prepend_to_file(path, **config) ⇒ Object
(also: #prepend_file)
Prepend text to a file.
-
#relative_to_original_destination_root(path, remove_dot: true) ⇒ Object
Returns the given path relative to the absolute root (ie, root where the script started).
-
#remove_file(path, verbose: true) ⇒ Object
(also: #remove_dir)
Removes a file at the given location.
-
#run(command, **config) ⇒ Object
Executes a command returning the contents of the command.
-
#template(source, destination = nil, context: nil, type: nil, **config, &block) ⇒ Object
Gets an ERB template at the relative source, executes it and makes a copy at the relative destination.
-
#uncomment_lines(path, flag) ⇒ Object
Uncomment all lines matching a given regex.
Class Method Details
.escape_globs(path) ⇒ String
Returns a string that has had any glob characters escaped.
The glob characters are * ? { } [ ].
20 21 22 |
# File 'lib/freyia/automations.rb', line 20 def self.escape_globs(path) path.to_s.gsub(%r{[*?{}\[\]]}, '\\\\\\&') end |
Instance Method Details
#append_to_file(path, **config) ⇒ Object Also known as: append_file
Append text to a file.
185 186 187 188 |
# File 'lib/freyia/automations/file_manipulation.rb', line 185 def append_to_file(path, *, **config, &) config[:before] = %r{\z} insert_into_file(path, *, **config, &) end |
#apply(path, verbose: true) ⇒ Object
Loads an external file and execute it in the instance binding.
Parameters
- path
The path to the file to execute. Can be a web address or a relative path from the source root.
Examples
apply "http://gist.github.com/103208"
apply "recipes/jquery.rb"
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/freyia/automations.rb', line 135 def apply(path, verbose: true) is_uri = path =~ %r{^https?://} path = find_in_source_paths(path) unless is_uri say_status :apply, path, verbose shell.padding += 1 if verbose contents = if is_uri require "open-uri" URI.open(path, "Accept" => "application/x-freyia-template", &:read) # rubocop:disable Security/Open else File.read(path) end instance_eval(contents, path) shell.padding -= 1 if verbose end |
#chmod(path, mode, **config) ⇒ Object
Changes the mode of the given file or directory.
146 147 148 149 150 151 152 153 |
# File 'lib/freyia/automations/file_manipulation.rb', line 146 def chmod(path, mode, **config) path = File.(path, destination_root) say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true) return if [:pretend] require "fileutils" FileUtils.chmod_R(mode, path) end |
#comment_lines(path, flag) ⇒ Object
Comment all lines matching a given regex. It will leave the space which existed before the beginning of the line in tact and will insert a single space after the comment hash.
293 294 295 296 297 |
# File 'lib/freyia/automations/file_manipulation.rb', line 293 def comment_lines(path, flag) flag = flag.source if flag.respond_to?(:source) gsub_file(path, %r{^(\s*)([^#\n]*#{flag})}, '\1# \2') end |
#copy_file(source, destination = source, **config, &block) ⇒ Object
Copies the file from the relative source to the relative destination. If the destination is not given it's assumed to be equal to the source.
19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/freyia/automations/file_manipulation.rb', line 19 def copy_file(source, destination = source, **config, &block) source = File.(find_in_source_paths(source.to_s)) resulting_destination = create_file destination, nil, **config do content = File.binread(source) content = yield(content) if block content end return unless config[:mode] == :preserve mode = File.stat(source).mode chmod(resulting_destination, mode, **config) end |
#create_file(destination, data = nil, **config, &block) ⇒ Object Also known as: add_file
Create a new file relative to the destination root with the given data, which is the return value of a block or a data string.
22 23 24 |
# File 'lib/freyia/automations/create_file.rb', line 22 def create_file(destination, data = nil, **config, &block) CreateFile.new(self, destination, block || data.to_s, **config).() end |
#create_link(destination, source, **config) ⇒ Object Also known as: add_link
Create a new symbolic link relative to the destination root from the given source.
16 17 18 |
# File 'lib/freyia/automations/create_link.rb', line 16 def create_link(destination, source, **config) CreateLink.new(self, destination, source, **config).() end |
#destination_root ⇒ Object
Returns the root for this freyia class (also aliased as destination root).
26 27 28 |
# File 'lib/freyia/automations.rb', line 26 def destination_root @destination_stack.last end |
#destination_root=(root) ⇒ Object
Sets the root for this freyia class. Relatives path are added to the directory where the script was invoked and expanded.
33 34 35 36 |
# File 'lib/freyia/automations.rb', line 33 def destination_root=(root) @destination_stack ||= [] @destination_stack[0] = File.(root || "") end |
#directory(source, destination = nil, **config) ⇒ Object
Copies recursively the files from source directory to root directory.
If any of the files finishes with .tmpl, it's considered to be a template
and is placed in the destination without the extension .tmpl. If any
empty directory is found, it's copied and all .empty_directory files are
ignored. If any file name is wrapped within % signs, the text within
the % signs will be executed as a method and replaced with the returned
value. Let's suppose a doc directory with the following files:
doc/
components/.empty_directory
README
rdoc.rb.tmpl
%app_name%.rb
When invoked as:
directory "doc"
It will create a doc directory in the destination with the following
files (assuming that the app_name method returns the value "blog"):
doc/
components/
README
rdoc.rb
blog.rb
Encoded path note: Since Freyia internals use Object#respond_to? to check if it can
expand %something%, this something should be a public method in the class calling
#directory.
50 51 52 |
# File 'lib/freyia/automations/directory.rb', line 50 def directory(source, destination = nil, **config, &) Directory.new(self, source, destination, **config, &).() end |
#empty_directory(destination, **config) ⇒ Object
Creates an empty directory.
12 13 14 |
# File 'lib/freyia/automations/empty_directory.rb', line 12 def empty_directory(destination, **config) EmptyDirectory.new(self, destination, **config).() end |
#find_in_source_paths(file) ⇒ Object
Receives a file or directory and search for it in the source paths.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/freyia/automations.rb', line 55 def find_in_source_paths(file) possible_files = [file, file + TEMPLATE_EXTNAME] relative_root = relative_to_original_destination_root(destination_root, remove_dot: false) source_paths.each do |source| possible_files.each do |f| source_file = File.(f, File.join(source, relative_root)) return source_file if File.exist?(source_file) end end = "Could not find #{file.inspect} in any of your source paths. " << if source_paths.empty? "Currently you have no source paths." else "Your current source paths are: \n#{source_paths.join("\n")}" end raise Error, end |
#get(source, destination = nil, **config, &block) ⇒ Object
Gets the content at the given address and places it at the given relative destination. If a block is given instead of destination, the content of the url is yielded and used as location.
get relies on open-uri, so passing application user input would provide
a command injection attack vector.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/freyia/automations/file_manipulation.rb', line 69 def get(source, destination = nil, **config, &block) render = if %r{^https?://}.match?(source) require "open-uri" URI.send(:open, source, config.fetch(:http_headers, {})) do |input| input.binmode.read end else source = File.(find_in_source_paths(source.to_s)) File.open(source) { |input| input.binmode.read } end destination ||= if block_given? block.arity == 1 ? yield(render) : yield else File.basename(source) end create_file destination, render, **config end |
#gsub_file(path, flag, *args, verbose: true) ⇒ Object
Run a regular expression replacement on a file.
263 264 265 266 267 268 |
# File 'lib/freyia/automations/file_manipulation.rb', line 263 def gsub_file(path, flag, *args, verbose: true, &) path = File.(path, destination_root) say_status :gsub, relative_to_original_destination_root(path), verbose actually_gsub_file(path, flag, args, false, &) unless [:pretend] end |
#gsub_file!(path, flag, *args, verbose: true) ⇒ Object
Run a regular expression replacement on a file, raising an error if the contents of the file are not changed.
245 246 247 248 249 250 |
# File 'lib/freyia/automations/file_manipulation.rb', line 245 def gsub_file!(path, flag, *args, verbose: true, &) path = File.(path, destination_root) say_status :gsub, relative_to_original_destination_root(path), verbose actually_gsub_file(path, flag, args, true, &) unless [:pretend] end |
#in_root ⇒ Object
Goes to the root and execute the given block.
119 120 121 |
# File 'lib/freyia/automations.rb', line 119 def in_root(&) inside(@destination_stack.first, &) end |
#inject_into_class(path, klass, **config) ⇒ Object
Injects text right after the class definition.
207 208 209 210 |
# File 'lib/freyia/automations/file_manipulation.rb', line 207 def inject_into_class(path, klass, *, **config, &) config[:after] = %r{class #{klass}\n|class #{klass} .*\n} insert_into_file(path, *, **config, &) end |
#inject_into_module(path, module_name, **config) ⇒ Object
Injects text right after the module definition.
228 229 230 231 |
# File 'lib/freyia/automations/file_manipulation.rb', line 228 def inject_into_module(path, module_name, *, **config, &) config[:after] = %r{module #{module_name}\n|module #{module_name} .*\n} insert_into_file(path, *, **config, &) end |
#insert_into_file(destination, *args, **config, &block) ⇒ Object Also known as: inject_into_file
Injects the given content into a file.
56 57 58 59 60 61 62 |
# File 'lib/freyia/automations/inject_into_file.rb', line 56 def insert_into_file(destination, *args, **config, &block) data = block_given? ? block : args.shift config[:after] = %r{\z} unless config.key?(:before) || config.key?(:after) InjectIntoFile.new(self, destination, data, **config).() end |
#insert_into_file!(destination, *args, **config, &block) ⇒ Object Also known as: inject_into_file!
Injects the given content into a file, raising an error if the contents of the file are not changed.
30 31 32 33 34 35 36 |
# File 'lib/freyia/automations/inject_into_file.rb', line 30 def insert_into_file!(destination, *args, **config, &block) data = block_given? ? block : args.shift config[:after] = %r{\z} unless config.key?(:before) || config.key?(:after) InjectIntoFile.new(self, destination, data, error_on_no_change: true, **config).() end |
#inside(dir = "", verbose: false, &block) ⇒ Object
Do something in the root or on a provided subfolder. If a relative path is given it's referenced from the current root. The full path is yielded to the block you provide. The path is set back to the previous path when the method exits.
Returns the value yielded by the block.
Parameters
- dir
the directory to move to.
- config
give :verbose => true to log and use padding.
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 |
# File 'lib/freyia/automations.rb', line 88 def inside(dir = "", verbose: false, &block) # rubocop:todo Metrics pretend = [:pretend] say_status :inside, dir, verbose shell.padding += 1 if verbose @destination_stack.push File.(dir, destination_root) # If the directory doesn't exist and we're not pretending if !File.exist?(destination_root) && !pretend require "fileutils" FileUtils.mkdir_p(destination_root) end result = nil if pretend # In pretend mode, just yield down to the block result = block.arity == 1 ? yield(destination_root) : yield else require "fileutils" FileUtils.cd(destination_root) do result = block.arity == 1 ? yield(destination_root) : yield end end @destination_stack.pop shell.padding -= 1 if verbose result end |
#link_file(source, destination = source, **config) ⇒ Object
Links the file from the relative source to the relative destination. If the destination is not given it's assumed to be equal to the source.
44 45 46 47 48 |
# File 'lib/freyia/automations/file_manipulation.rb', line 44 def link_file(source, destination = source, **config) source = File.(find_in_source_paths(source.to_s)) create_link destination, source, **config end |
#prepend_to_file(path, **config) ⇒ Object Also known as: prepend_file
Prepend text to a file.
167 168 169 170 |
# File 'lib/freyia/automations/file_manipulation.rb', line 167 def prepend_to_file(path, *, **config, &) config[:after] = %r{\A} insert_into_file(path, *, **config, &) end |
#relative_to_original_destination_root(path, remove_dot: true) ⇒ Object
Returns the given path relative to the absolute root (ie, root where the script started).
41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/freyia/automations.rb', line 41 def relative_to_original_destination_root(path, remove_dot: true) root = @destination_stack[0] if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil, "",].include?(path[root.size..root.size]) path = path.dup path[0...root.size] = "." remove_dot ? (path[2..] || "") : path else path end end |
#remove_file(path, verbose: true) ⇒ Object Also known as: remove_dir
Removes a file at the given location.
307 308 309 310 311 312 313 314 315 |
# File 'lib/freyia/automations/file_manipulation.rb', line 307 def remove_file(path, verbose: true) path = File.(path, destination_root) say_status :remove, relative_to_original_destination_root(path), verbose return unless ![:pretend] && (File.exist?(path) || File.symlink?(path)) require "fileutils" ::FileUtils.rm_rf(path) end |
#run(command, **config) ⇒ Object
Executes a command returning the contents of the command.
Parameters
- command
the command to be executed.
- config
give :verbose => false to not log the status, :capture => true to hide to output. Specify :with to append an executable to command execution.
Example
inside('vendor') do
run('ln -s ~/edge rails')
end
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/freyia/automations.rb', line 166 def run(command, **config) # rubocop:todo Metrics destination = relative_to_original_destination_root(destination_root, remove_dot: false) desc = "#{command} from #{destination.inspect}" if config[:with] desc = "#{File.basename(config[:with].to_s)} #{desc}" command = "#{config[:with]} #{command}" end say_status :run, desc, config.fetch(:verbose, true) return if [:pretend] env_splat = [config[:env]] if config[:env] if config[:capture] require "open3" result, status = Open3.capture2e(*env_splat, command.to_s) success = status.success? else result = system(*env_splat, command.to_s) success = result end abort if !success && config.fetch(:abort_on_failure, self.class.respond_to?(:exit_on_failure?) && self.class.exit_on_failure?) result end |
#template(source, destination = nil, context: nil, type: nil, **config, &block) ⇒ Object
Gets an ERB template at the relative source, executes it and makes a copy
at the relative destination. If the destination is not given it's assumed
to be equal to the source removing .tmpl from the filename.
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/freyia/automations/file_manipulation.rb', line 104 def template(source, destination = nil, context: nil, type: nil, **config, &block) # rubocop:todo Metrics destination ||= source.sub(%r{#{TEMPLATE_EXTNAME}$}o, "") source = File.(find_in_source_paths(source.to_s)) type ||= self.class.template_type create_file destination, nil, **config do if type == :serbea unless @_included_serbea require "serbea" singleton_class.include Serbea::Helpers @_included_serbea = true end variables = if context context.local_variables.to_h { [_1, context.local_variable_get(_1)] } else {} end tmpl = Tilt::SerbeaTemplate.new(source, strip_front_matter: false) { ::File.binread(source) } tmpl.render(self, variables) elsif type == :erb context ||= instance_eval("binding", __FILE__, __LINE__) capturable_erb = CapturableERB.new( ::File.binread(source), trim_mode: "-", eoutvar: "@output_buffer" ) content = capturable_erb.tap do |erb| erb.filename = source end.result(context) content = yield(content) if block content end end end |
#uncomment_lines(path, flag) ⇒ Object
Uncomment all lines matching a given regex. Preserves indentation before the comment hash and removes the hash and any immediate following space.
278 279 280 281 282 |
# File 'lib/freyia/automations/file_manipulation.rb', line 278 def uncomment_lines(path, flag) flag = flag.source if flag.respond_to?(:source) gsub_file(path, %r{^(\s*)#[[:blank:]]?(.*#{flag})}, '\1\2') end |