freyia 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +62 -0
- data/Rakefile +12 -0
- data/lib/freyia/automations/create_file.rb +107 -0
- data/lib/freyia/automations/create_link.rb +63 -0
- data/lib/freyia/automations/directory.rb +114 -0
- data/lib/freyia/automations/empty_directory.rb +146 -0
- data/lib/freyia/automations/file_manipulation.rb +415 -0
- data/lib/freyia/automations/inject_into_file.rb +168 -0
- data/lib/freyia/automations.rb +191 -0
- data/lib/freyia/line_editor/basic.rb +39 -0
- data/lib/freyia/line_editor/readline.rb +90 -0
- data/lib/freyia/line_editor.rb +19 -0
- data/lib/freyia/shell/basic.rb +400 -0
- data/lib/freyia/shell/color.rb +117 -0
- data/lib/freyia/shell/column_printer.rb +32 -0
- data/lib/freyia/shell/lcs_diff.rb +50 -0
- data/lib/freyia/shell/table_printer.rb +120 -0
- data/lib/freyia/shell/terminal.rb +43 -0
- data/lib/freyia/shell/wrapped_printer.rb +39 -0
- data/lib/freyia/shell.rb +30 -0
- data/lib/freyia/version.rb +5 -0
- data/lib/freyia.rb +56 -0
- metadata +68 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "automations/create_file"
|
|
4
|
+
require_relative "automations/create_link"
|
|
5
|
+
require_relative "automations/directory"
|
|
6
|
+
require_relative "automations/empty_directory"
|
|
7
|
+
require_relative "automations/file_manipulation"
|
|
8
|
+
require_relative "automations/inject_into_file"
|
|
9
|
+
|
|
10
|
+
module Freyia
|
|
11
|
+
module Automations
|
|
12
|
+
# Wraps an action object and call it accordingly to the freyia class behavior.
|
|
13
|
+
#
|
|
14
|
+
def action(instance) #:nodoc:
|
|
15
|
+
instance.invoke!
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Returns the root for this freyia class (also aliased as destination root).
|
|
19
|
+
#
|
|
20
|
+
def destination_root
|
|
21
|
+
@destination_stack.last
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Sets the root for this freyia class. Relatives path are added to the
|
|
25
|
+
# directory where the script was invoked and expanded.
|
|
26
|
+
#
|
|
27
|
+
def destination_root=(root)
|
|
28
|
+
@destination_stack ||= []
|
|
29
|
+
@destination_stack[0] = File.expand_path(root || "")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns the given path relative to the absolute root (ie, root where
|
|
33
|
+
# the script started).
|
|
34
|
+
#
|
|
35
|
+
def relative_to_original_destination_root(path, remove_dot: true)
|
|
36
|
+
root = @destination_stack[0]
|
|
37
|
+
if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil,
|
|
38
|
+
"",].include?(path[root.size..root.size])
|
|
39
|
+
path = path.dup
|
|
40
|
+
path[0...root.size] = "."
|
|
41
|
+
remove_dot ? (path[2..] || "") : path
|
|
42
|
+
else
|
|
43
|
+
path
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Receives a file or directory and search for it in the source paths.
|
|
48
|
+
#
|
|
49
|
+
def find_in_source_paths(file)
|
|
50
|
+
possible_files = [file, file + TEMPLATE_EXTNAME]
|
|
51
|
+
relative_root = relative_to_original_destination_root(destination_root, remove_dot: false)
|
|
52
|
+
|
|
53
|
+
source_paths.each do |source|
|
|
54
|
+
possible_files.each do |f|
|
|
55
|
+
source_file = File.expand_path(f, File.join(source, relative_root))
|
|
56
|
+
return source_file if File.exist?(source_file)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
message = "Could not find #{file.inspect} in any of your source paths. "
|
|
61
|
+
|
|
62
|
+
message << if source_paths.empty?
|
|
63
|
+
"Currently you have no source paths."
|
|
64
|
+
else
|
|
65
|
+
"Your current source paths are: \n#{source_paths.join("\n")}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
raise Error, message
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Do something in the root or on a provided subfolder. If a relative path
|
|
72
|
+
# is given it's referenced from the current root. The full path is yielded
|
|
73
|
+
# to the block you provide. The path is set back to the previous path when
|
|
74
|
+
# the method exits.
|
|
75
|
+
#
|
|
76
|
+
# Returns the value yielded by the block.
|
|
77
|
+
#
|
|
78
|
+
# ==== Parameters
|
|
79
|
+
# dir<String>:: the directory to move to.
|
|
80
|
+
# config<Hash>:: give :verbose => true to log and use padding.
|
|
81
|
+
#
|
|
82
|
+
def inside(dir = "", config = {}, &block) # rubocop:todo Metrics
|
|
83
|
+
verbose = config.fetch(:verbose, false)
|
|
84
|
+
pretend = options[:pretend]
|
|
85
|
+
|
|
86
|
+
say_status :inside, dir, verbose
|
|
87
|
+
shell.padding += 1 if verbose
|
|
88
|
+
@destination_stack.push File.expand_path(dir, destination_root)
|
|
89
|
+
|
|
90
|
+
# If the directory doesn't exist and we're not pretending
|
|
91
|
+
if !File.exist?(destination_root) && !pretend
|
|
92
|
+
require "fileutils"
|
|
93
|
+
FileUtils.mkdir_p(destination_root)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
result = nil
|
|
97
|
+
if pretend
|
|
98
|
+
# In pretend mode, just yield down to the block
|
|
99
|
+
result = block.arity == 1 ? yield(destination_root) : yield
|
|
100
|
+
else
|
|
101
|
+
require "fileutils"
|
|
102
|
+
FileUtils.cd(destination_root) do
|
|
103
|
+
result = block.arity == 1 ? yield(destination_root) : yield
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
@destination_stack.pop
|
|
108
|
+
shell.padding -= 1 if verbose
|
|
109
|
+
result
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Goes to the root and execute the given block.
|
|
113
|
+
#
|
|
114
|
+
def in_root(&)
|
|
115
|
+
inside(@destination_stack.first, &)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Loads an external file and execute it in the instance binding.
|
|
119
|
+
#
|
|
120
|
+
# ==== Parameters
|
|
121
|
+
# path<String>:: The path to the file to execute. Can be a web address or
|
|
122
|
+
# a relative path from the source root.
|
|
123
|
+
#
|
|
124
|
+
# ==== Examples
|
|
125
|
+
#
|
|
126
|
+
# apply "http://gist.github.com/103208"
|
|
127
|
+
#
|
|
128
|
+
# apply "recipes/jquery.rb"
|
|
129
|
+
#
|
|
130
|
+
def apply(path, config = {})
|
|
131
|
+
verbose = config.fetch(:verbose, true)
|
|
132
|
+
is_uri = path =~ %r{^https?://}
|
|
133
|
+
path = find_in_source_paths(path) unless is_uri
|
|
134
|
+
|
|
135
|
+
say_status :apply, path, verbose
|
|
136
|
+
shell.padding += 1 if verbose
|
|
137
|
+
|
|
138
|
+
contents = if is_uri
|
|
139
|
+
require "open-uri"
|
|
140
|
+
URI.open(path, "Accept" => "application/x-freyia-template", &:read) # rubocop:disable Security/Open
|
|
141
|
+
else
|
|
142
|
+
File.read(path)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
instance_eval(contents, path)
|
|
146
|
+
shell.padding -= 1 if verbose
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Executes a command returning the contents of the command.
|
|
150
|
+
#
|
|
151
|
+
# ==== Parameters
|
|
152
|
+
# command<String>:: the command to be executed.
|
|
153
|
+
# config<Hash>:: give :verbose => false to not log the status, :capture => true to hide to
|
|
154
|
+
# output. Specify :with to append an executable to command execution.
|
|
155
|
+
#
|
|
156
|
+
# ==== Example
|
|
157
|
+
#
|
|
158
|
+
# inside('vendor') do
|
|
159
|
+
# run('ln -s ~/edge rails')
|
|
160
|
+
# end
|
|
161
|
+
#
|
|
162
|
+
def run(command, config = {}) # rubocop:todo Metrics
|
|
163
|
+
destination = relative_to_original_destination_root(destination_root, remove_dot: false)
|
|
164
|
+
desc = "#{command} from #{destination.inspect}"
|
|
165
|
+
|
|
166
|
+
if config[:with]
|
|
167
|
+
desc = "#{File.basename(config[:with].to_s)} #{desc}"
|
|
168
|
+
command = "#{config[:with]} #{command}"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
say_status :run, desc, config.fetch(:verbose, true)
|
|
172
|
+
|
|
173
|
+
return if options[:pretend]
|
|
174
|
+
|
|
175
|
+
env_splat = [config[:env]] if config[:env]
|
|
176
|
+
|
|
177
|
+
if config[:capture]
|
|
178
|
+
require "open3"
|
|
179
|
+
result, status = Open3.capture2e(*env_splat, command.to_s)
|
|
180
|
+
success = status.success?
|
|
181
|
+
else
|
|
182
|
+
result = system(*env_splat, command.to_s)
|
|
183
|
+
success = result
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
abort if !success && config.fetch(:abort_on_failure, self.class.exit_on_failure?)
|
|
187
|
+
|
|
188
|
+
result
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Freyia
|
|
4
|
+
module LineEditor
|
|
5
|
+
class Basic
|
|
6
|
+
attr_reader :prompt, :options
|
|
7
|
+
|
|
8
|
+
def self.available?
|
|
9
|
+
true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(prompt, options)
|
|
13
|
+
@prompt = prompt
|
|
14
|
+
@options = options
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def readline
|
|
18
|
+
$stdout.print(prompt)
|
|
19
|
+
get_input
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def get_input # rubocop:disable Naming/AccessorMethodName
|
|
25
|
+
if echo?
|
|
26
|
+
$stdin.gets
|
|
27
|
+
else
|
|
28
|
+
# Lazy-load io/console since it is gem-ified as of 2.3
|
|
29
|
+
require "io/console"
|
|
30
|
+
$stdin.noecho(&:gets)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def echo?
|
|
35
|
+
options.fetch(:echo, true)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Freyia
|
|
4
|
+
module LineEditor
|
|
5
|
+
class Readline < Basic
|
|
6
|
+
def self.available?
|
|
7
|
+
begin
|
|
8
|
+
require "readline"
|
|
9
|
+
rescue LoadError # rubocop:disable Lint/SuppressedException
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
Object.const_defined?(:Readline)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def readline
|
|
16
|
+
if echo?
|
|
17
|
+
::Readline.completion_append_character = nil
|
|
18
|
+
# rb-readline does not allow Readline.completion_proc= to receive nil.
|
|
19
|
+
if (complete = completion_proc)
|
|
20
|
+
::Readline.completion_proc = complete
|
|
21
|
+
end
|
|
22
|
+
::Readline.readline(prompt, add_to_history?)
|
|
23
|
+
else
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def add_to_history?
|
|
31
|
+
options.fetch(:add_to_history, true)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def completion_proc
|
|
35
|
+
if use_path_completion?
|
|
36
|
+
proc { |text| PathCompletion.new(text).matches }
|
|
37
|
+
elsif completion_options.any?
|
|
38
|
+
proc do |text|
|
|
39
|
+
completion_options.select { |option| option.start_with?(text) }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def completion_options
|
|
45
|
+
options.fetch(:limited_to, [])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def use_path_completion?
|
|
49
|
+
options.fetch(:path, false)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class PathCompletion
|
|
53
|
+
attr_reader :text
|
|
54
|
+
private :text
|
|
55
|
+
|
|
56
|
+
def initialize(text)
|
|
57
|
+
@text = text
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def matches
|
|
61
|
+
relative_matches
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def relative_matches
|
|
67
|
+
absolute_matches.map { |path| path.sub(base_path, "") }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def absolute_matches
|
|
71
|
+
Dir[glob_pattern].map do |path|
|
|
72
|
+
if File.directory?(path)
|
|
73
|
+
"#{path}/"
|
|
74
|
+
else
|
|
75
|
+
path
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def glob_pattern
|
|
81
|
+
"#{base_path}#{text}*"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def base_path
|
|
85
|
+
"#{Dir.pwd}/"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "line_editor/basic"
|
|
4
|
+
require_relative "line_editor/readline"
|
|
5
|
+
|
|
6
|
+
module Freyia
|
|
7
|
+
module LineEditor
|
|
8
|
+
def self.readline(prompt, options = {})
|
|
9
|
+
best_available.new(prompt, options).readline
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.best_available
|
|
13
|
+
[
|
|
14
|
+
Freyia::LineEditor::Readline,
|
|
15
|
+
Freyia::LineEditor::Basic,
|
|
16
|
+
].detect(&:available?)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|