yap-shell 0.1.1 → 0.3.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 +4 -4
- data/.ruby-version +1 -0
- data/Gemfile +5 -0
- data/WISHLIST.md +14 -0
- data/addons/history/Gemfile +2 -0
- data/addons/history/history.rb +101 -0
- data/addons/history/lib/history/buffer.rb +204 -0
- data/addons/history/lib/history/events.rb +13 -0
- data/addons/keyboard_macros/keyboard_macros.rb +295 -0
- data/addons/prompt/Gemfile +1 -0
- data/addons/prompt/right_prompt.rb +17 -0
- data/addons/prompt_updates/prompt_updates.rb +28 -0
- data/addons/tab_completion/Gemfile +0 -0
- data/addons/tab_completion/lib/tab_completion/completer.rb +62 -0
- data/addons/tab_completion/lib/tab_completion/custom_completion.rb +33 -0
- data/addons/tab_completion/lib/tab_completion/dsl_methods.rb +7 -0
- data/addons/tab_completion/lib/tab_completion/file_completion.rb +75 -0
- data/addons/tab_completion/tab_completion.rb +157 -0
- data/bin/yap +13 -4
- data/lib/tasks/addons.rake +51 -0
- data/lib/yap.rb +4 -55
- data/lib/yap/shell.rb +51 -10
- data/lib/yap/shell/builtins.rb +2 -2
- data/lib/yap/shell/builtins/alias.rb +2 -2
- data/lib/yap/shell/builtins/cd.rb +9 -11
- data/lib/yap/shell/builtins/env.rb +11 -0
- data/lib/yap/shell/commands.rb +29 -18
- data/lib/yap/shell/evaluation.rb +185 -68
- data/lib/yap/shell/evaluation/shell_expansions.rb +85 -0
- data/lib/yap/shell/event_emitter.rb +18 -0
- data/lib/yap/shell/execution/builtin_command_execution.rb +1 -1
- data/lib/yap/shell/execution/command_execution.rb +3 -3
- data/lib/yap/shell/execution/context.rb +32 -9
- data/lib/yap/shell/execution/file_system_command_execution.rb +12 -7
- data/lib/yap/shell/execution/ruby_command_execution.rb +6 -6
- data/lib/yap/shell/execution/shell_command_execution.rb +17 -2
- data/lib/yap/shell/prompt.rb +21 -0
- data/lib/yap/shell/repl.rb +179 -18
- data/lib/yap/shell/version.rb +1 -1
- data/lib/yap/world.rb +149 -15
- data/lib/yap/world/addons.rb +135 -0
- data/rcfiles/.yaprc +240 -10
- data/test.rb +206 -0
- data/update-rawline.sh +6 -0
- data/yap-shell.gemspec +11 -3
- metadata +101 -10
- data/addons/history.rb +0 -171
@@ -0,0 +1 @@
|
|
1
|
+
gem "chronic", "~> 0.10.2"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class RightPrompt < Addon
|
2
|
+
def self.load_addon
|
3
|
+
@instance ||= new
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize_world(world)
|
7
|
+
@world = world
|
8
|
+
|
9
|
+
# @world.subscribe(:refresh_right_prompt) do |event|
|
10
|
+
# @world.right_prompt_text = Time.now.strftime("%H:%M:%S")
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# @world.events.recur(
|
14
|
+
# name: "refresh_right_prompt", source: self, interval_in_ms: 1_000
|
15
|
+
# )
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class PromptUpdates < Addon
|
2
|
+
attr_reader :world
|
3
|
+
|
4
|
+
def initialize_world(world)
|
5
|
+
current_branch = determine_branch
|
6
|
+
|
7
|
+
@world = world
|
8
|
+
@world.events.recur(
|
9
|
+
name: 'prompt_updates',
|
10
|
+
source: self,
|
11
|
+
interval_in_ms: 100
|
12
|
+
) do
|
13
|
+
new_branch = determine_branch
|
14
|
+
if current_branch != new_branch
|
15
|
+
current_branch = new_branch
|
16
|
+
@world.refresh_prompt
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def determine_branch
|
25
|
+
`git branch`.scan(/\*\s*(.*)$/).flatten.first
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
File without changes
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class TabCompletion
|
2
|
+
|
3
|
+
class Completer
|
4
|
+
def initialize(char:, line:, completion:, completion_found:, completion_not_found:, done:)
|
5
|
+
@completion_char = char
|
6
|
+
@line = line
|
7
|
+
@completion_proc = completion
|
8
|
+
@completion_found_proc = completion_found
|
9
|
+
@completion_not_found_proc = completion_not_found
|
10
|
+
@done_proc = done
|
11
|
+
|
12
|
+
@completion_matches = HistoryBuffer.new(0) do |h|
|
13
|
+
h.duplicates = false
|
14
|
+
h.cycle = true
|
15
|
+
end
|
16
|
+
@completion_matches.empty
|
17
|
+
|
18
|
+
@first_time = true
|
19
|
+
@word_start = @line.word[:start]
|
20
|
+
end
|
21
|
+
|
22
|
+
def read_bytes(bytes)
|
23
|
+
return unless bytes.any?
|
24
|
+
|
25
|
+
if bytes.map(&:ord) != @completion_char
|
26
|
+
@done_proc.call(bytes)
|
27
|
+
elsif @first_time
|
28
|
+
matches = @completion_proc.call(sub_word) unless !@completion_proc || @completion_proc == []
|
29
|
+
matches = matches.to_a.compact.sort.reverse
|
30
|
+
|
31
|
+
if matches.any?
|
32
|
+
@completion_matches.resize(matches.length)
|
33
|
+
matches.each { |w| @completion_matches << w }
|
34
|
+
|
35
|
+
# Get first match
|
36
|
+
@completion_matches.back
|
37
|
+
match = @completion_matches.get
|
38
|
+
|
39
|
+
# completion matches is a history implementation and its in reverse order from what
|
40
|
+
# a user would expect
|
41
|
+
@completion_found_proc.call(completion: match, possible_completions: @completion_matches.reverse)
|
42
|
+
else
|
43
|
+
@completion_not_found_proc.call
|
44
|
+
@done_proc.call
|
45
|
+
end
|
46
|
+
@first_time = false
|
47
|
+
else
|
48
|
+
@completion_matches.back
|
49
|
+
match = @completion_matches.get
|
50
|
+
|
51
|
+
@completion_found_proc.call(completion: match, possible_completions: @completion_matches.reverse)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def sub_word
|
58
|
+
@line.text[@line.word[:start]..@line.position-1] || ""
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class TabCompletion
|
2
|
+
class CustomCompletion
|
3
|
+
PRIORITY = 2
|
4
|
+
|
5
|
+
attr_reader :name, :pattern, :priority
|
6
|
+
|
7
|
+
def initialize(world:, name:nil, pattern:nil, priority:PRIORITY, &blk)
|
8
|
+
@world = world
|
9
|
+
@name = name
|
10
|
+
@pattern = pattern
|
11
|
+
@priority = priority
|
12
|
+
@blk = blk
|
13
|
+
end
|
14
|
+
|
15
|
+
def new(world:)
|
16
|
+
@world = world
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def completions_for(word, line)
|
21
|
+
# TODO
|
22
|
+
return []
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def match_rgx
|
28
|
+
return // if pattern.nil?
|
29
|
+
return pattern if pattern.is_a?(Regexp)
|
30
|
+
/^#{Regexp.escape(pattern.to_s)}\s/
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
class TabCompletion
|
2
|
+
class FileCompletion
|
3
|
+
class << self
|
4
|
+
attr_accessor :priority
|
5
|
+
end
|
6
|
+
self.priority = 1
|
7
|
+
|
8
|
+
attr_reader :world
|
9
|
+
|
10
|
+
def initialize(world:, word_break_characters:, path:nil)
|
11
|
+
@world = world
|
12
|
+
@word_break_characters = word_break_characters
|
13
|
+
path ||= @world.env["PATH"]
|
14
|
+
@paths = path.split(":")
|
15
|
+
end
|
16
|
+
|
17
|
+
def completions_for(word, line)
|
18
|
+
completions = []
|
19
|
+
if looking_for_command?
|
20
|
+
completions.concat command_completion_matches_for(word, line)
|
21
|
+
end
|
22
|
+
completions.concat filename_completion_matches_for(word, line)
|
23
|
+
completions
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def looking_for_command?
|
29
|
+
# TODO
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def command_completion_matches_for(word, line)
|
34
|
+
matches = @paths.inject([]) do |matches, path|
|
35
|
+
glob = "#{path}*"
|
36
|
+
arr = Dir[glob].select { |path| File.executable?(path) && File.file?(path) }
|
37
|
+
arr.each { |path| matches << path }
|
38
|
+
matches
|
39
|
+
end
|
40
|
+
|
41
|
+
matches.map { |path| File.basename(path) }.sort.uniq.map do |command|
|
42
|
+
CompletionResult.new(type: :command, text:command)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def filename_completion_matches_for(word, line)
|
47
|
+
glob = "#{word}*"
|
48
|
+
glob.gsub!("~", world.env["HOME"])
|
49
|
+
Dir.glob(glob, File::FNM_CASEFOLD).map do |path|
|
50
|
+
text = path.gsub(filtered_work_break_characters_rgx, '\\\\\1')
|
51
|
+
descriptive_text = File.basename(text)
|
52
|
+
if File.directory?(path)
|
53
|
+
CompletionResult.new(type: :directory, text: text, descriptive_text: descriptive_text)
|
54
|
+
elsif File.symlink?(path)
|
55
|
+
CompletionResult.new(type: :symlink, text: text, descriptive_text: descriptive_text)
|
56
|
+
elsif File.file?(path) && File.executable?(path)
|
57
|
+
CompletionResult.new(type: :command, text: text, descriptive_text: descriptive_text)
|
58
|
+
else
|
59
|
+
CompletionResult.new(type: :file, text: text, descriptive_text: descriptive_text)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Remove file separator and the back-slash from word break characters when determining
|
65
|
+
# the pre-word-context
|
66
|
+
def filtered_word_break_characters
|
67
|
+
@word_break_characters.sub(File::Separator, "").sub('\\', '')
|
68
|
+
end
|
69
|
+
|
70
|
+
def filtered_work_break_characters_rgx
|
71
|
+
/([#{Regexp.escape(filtered_word_break_characters)}])/
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'term/ansicolor'
|
2
|
+
|
3
|
+
class TabCompletion < Addon
|
4
|
+
require 'tab_completion/completer'
|
5
|
+
require 'tab_completion/dsl_methods'
|
6
|
+
require 'tab_completion/custom_completion'
|
7
|
+
require 'tab_completion/file_completion'
|
8
|
+
|
9
|
+
class CompletionResult
|
10
|
+
attr_accessor :text, :type, :descriptive_text
|
11
|
+
|
12
|
+
def initialize(text:, type:, descriptive_text: nil)
|
13
|
+
@descriptive_text = descriptive_text || text
|
14
|
+
@text = text
|
15
|
+
@type = type
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
other.is_a?(self.class) && @text == other.text && @type == other.type
|
20
|
+
end
|
21
|
+
|
22
|
+
def <=>(other)
|
23
|
+
@text <=> other.text
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
@text
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
COMPLETIONS = [ FileCompletion ]
|
32
|
+
|
33
|
+
Color = Term::ANSIColor
|
34
|
+
|
35
|
+
DISPLAY_PROCS = Hash.new{ |h,k| h[k] = ->(text){ text } }.merge(
|
36
|
+
directory: -> (text){ text + "/" }
|
37
|
+
)
|
38
|
+
|
39
|
+
STYLE_PROCS = Hash.new{ |h,k| h[k] = ->(text){ text } }.merge(
|
40
|
+
directory: -> (text){ Color.bold(Color.red(text)) },
|
41
|
+
command: -> (text){ Color.bold(Color.green(text)) },
|
42
|
+
symlink: -> (text){ Color.bold(Color.cyan(text)) },
|
43
|
+
selected: -> (text){ Color.negative(text) }
|
44
|
+
)
|
45
|
+
|
46
|
+
DECORATION_PROCS = Hash.new{ |h,k| h[k] = ->(text){ text } }.merge(
|
47
|
+
directory: -> (text){ text + "/" },
|
48
|
+
command: -> (text){ text + "@" }
|
49
|
+
)
|
50
|
+
|
51
|
+
attr_reader :editor, :world
|
52
|
+
|
53
|
+
def initialize_world(world)
|
54
|
+
@world = world
|
55
|
+
@world.extend TabCompletion::DslMethods
|
56
|
+
@editor = @world.editor
|
57
|
+
@editor.completion_proc = -> (word, line){
|
58
|
+
complete(word, line)
|
59
|
+
}
|
60
|
+
@editor.bind(:tab){ @editor.complete }
|
61
|
+
@completions = COMPLETIONS.dup
|
62
|
+
|
63
|
+
@style_procs = STYLE_PROCS.dup
|
64
|
+
@decoration_procs = DECORATION_PROCS.dup
|
65
|
+
@display_procs = DISPLAY_PROCS.dup
|
66
|
+
|
67
|
+
editor.on_word_complete do |event|
|
68
|
+
sub_word = event[:payload][:sub_word]
|
69
|
+
word = event[:payload][:word]
|
70
|
+
actual_completion = event[:payload][:completion]
|
71
|
+
possible_completions = event[:payload][:possible_completions]
|
72
|
+
|
73
|
+
semi_formatted_possibilities = possible_completions.map.with_index do |completion, i|
|
74
|
+
if completion == actual_completion
|
75
|
+
style_text_for_selected_match(completion) + "\e[0m"
|
76
|
+
else
|
77
|
+
style_text_for_nonselected_match(completion) + "\e[0m"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
max_width = @editor.terminal_width
|
82
|
+
max_item_width = semi_formatted_possibilities.map(&:length).max + 2
|
83
|
+
most_per_line = max_width / max_item_width
|
84
|
+
padding_at_the_end = max_width % max_item_width
|
85
|
+
|
86
|
+
formatted_possibilities = semi_formatted_possibilities.map.with_index do |completion, i|
|
87
|
+
spaces_to_pad = max_item_width - completion.length
|
88
|
+
completion + (" " * spaces_to_pad)
|
89
|
+
end
|
90
|
+
|
91
|
+
editor.content_box.children = formatted_possibilities.map do |str|
|
92
|
+
TerminalLayout::Box.new(content: str, style: { display: :float, float: :left, height: 1, width: max_item_width })
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
editor.on_word_complete_no_match do |event|
|
97
|
+
sub_word = event[:payload][:sub_word]
|
98
|
+
word = event[:payload][:word]
|
99
|
+
editor.content_box.children = []
|
100
|
+
# editor.content_box.content = "Failed to find a match to complete #{sub_word} portion of #{word}"
|
101
|
+
end
|
102
|
+
|
103
|
+
editor.on_word_complete_done do |event|
|
104
|
+
# TODO: add a better way to clear content
|
105
|
+
editor.content_box.children = []
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_completion(name, pattern, &blk)
|
110
|
+
raise ArgumentError, "Must supply block!" unless block_given?
|
111
|
+
# @completions.push CustomCompletion.new(name:name, pattern:pattern, world:world, &blk)
|
112
|
+
end
|
113
|
+
|
114
|
+
def set_decoration(type, &blk)
|
115
|
+
raise ArgumentError, "Must supply block!" unless block_given?
|
116
|
+
@style_procs[type] = blk
|
117
|
+
end
|
118
|
+
|
119
|
+
def complete(word, line)
|
120
|
+
matches = @completions.sort_by(&:priority).reverse.map do |completion|
|
121
|
+
if completion.respond_to?(:call)
|
122
|
+
completion.call
|
123
|
+
else
|
124
|
+
completions = completion.new(
|
125
|
+
world: @world,
|
126
|
+
word_break_characters: editor.word_break_characters
|
127
|
+
).completions_for(word, line)
|
128
|
+
completions.each do |completion|
|
129
|
+
completion.text = display_text_for_match(completion)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end.flatten
|
133
|
+
|
134
|
+
matches
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def display_text_for_match(match)
|
140
|
+
ANSIString.new @display_procs[match.type].call(match.text.dup)
|
141
|
+
end
|
142
|
+
|
143
|
+
def style_text_for_selected_match(match)
|
144
|
+
styled_text = @style_procs[match.type].call(match.descriptive_text.dup).to_s
|
145
|
+
styled_text = @decoration_procs[match.type].call(styled_text).to_s
|
146
|
+
uncolored_text = Color.uncolored(styled_text)
|
147
|
+
ANSIString.new @style_procs[:selected].call(uncolored_text)
|
148
|
+
end
|
149
|
+
|
150
|
+
def style_text_for_nonselected_match(match)
|
151
|
+
str = @decoration_procs[match.type].call(
|
152
|
+
@style_procs[match.type].call(match.descriptive_text.dup)
|
153
|
+
)
|
154
|
+
ANSIString.new str
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
data/bin/yap
CHANGED
@@ -1,20 +1,29 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup
|
5
|
+
require 'pry'
|
6
|
+
|
3
7
|
file = __FILE__
|
4
8
|
if File.symlink?(file)
|
5
9
|
file = File.readlink(file)
|
6
10
|
end
|
7
11
|
|
12
|
+
$z = File.open("/tmp/z.log", "w+")
|
13
|
+
$z.sync = true
|
14
|
+
|
8
15
|
trap "SIGTSTP", "IGNORE"
|
9
16
|
trap "SIGINT", "IGNORE"
|
10
17
|
trap "SIGTTIN", "IGNORE"
|
11
18
|
trap "SIGTTOU", "IGNORE"
|
12
19
|
|
13
|
-
ENV["PATH"] = "/Applications/Postgres.app/Contents/MacOS/bin:/usr/local/share/npm/bin/:/usr/local/heroku/bin:/Users/zdennis/.bin:/Users/zdennis/.rvm/gems/ruby-2.1.5/bin:/Users/zdennis/.rvm/gems/ruby-2.1.5@global/bin:/Users/zdennis/.rvm/rubies/ruby-2.1.5/bin:/usr/local/bin:/usr/local/sbin:/Users/zdennis/bin:/bin:/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/CrossPack-AVR/bin:/private/tmp/.tidbits/bin:/Users/zdennis/source/playground/AdobeAir/AdobeAIRSDK/bin:/Users/zdennis/.rvm/bin:/Users/zdennis/Downloads/adt-bundle-mac-x86_64-20130219/sdk/tools/:/Users/zdennis/.rvm/bin"
|
14
|
-
ENV["GEM_HOME"] = "/Users/zdennis/.rvm/gems/ruby-2.1.5:/Users/zdennis/.rvm/gems/ruby-2.1.5@global"
|
15
|
-
ENV["GEM_PATH"] = "/Users/zdennis/.rvm/gems/ruby-2.1.5"
|
20
|
+
#ENV["PATH"] = "/Applications/Postgres.app/Contents/MacOS/bin:/usr/local/share/npm/bin/:/usr/local/heroku/bin:/Users/zdennis/.bin:/Users/zdennis/.rvm/gems/ruby-2.1.5/bin:/Users/zdennis/.rvm/gems/ruby-2.1.5@global/bin:/Users/zdennis/.rvm/rubies/ruby-2.1.5/bin:/usr/local/bin:/usr/local/sbin:/Users/zdennis/bin:/bin:/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/CrossPack-AVR/bin:/private/tmp/.tidbits/bin:/Users/zdennis/source/playground/AdobeAir/AdobeAIRSDK/bin:/Users/zdennis/.rvm/bin:/Users/zdennis/Downloads/adt-bundle-mac-x86_64-20130219/sdk/tools/:/Users/zdennis/.rvm/bin"
|
16
21
|
|
17
|
-
$LOAD_PATH
|
22
|
+
$LOAD_PATH.unshift File.dirname(file) + '/../lib'
|
23
|
+
# $LOAD_PATH.unshift File.dirname(file) + '/../../yap-shell-parser/lib'
|
18
24
|
|
19
25
|
require "yap"
|
26
|
+
|
27
|
+
$stdout.sync = true
|
28
|
+
|
20
29
|
Yap.run_shell
|
@@ -0,0 +1,51 @@
|
|
1
|
+
namespace :addons do
|
2
|
+
namespace :update do
|
3
|
+
desc "Update the gemspec based on add-on specific dependnecies"
|
4
|
+
task :gemspec do
|
5
|
+
require 'bundler'
|
6
|
+
runtime_deps = []
|
7
|
+
development_deps = []
|
8
|
+
root_dir = File.dirname(__FILE__) + "/../.."
|
9
|
+
gemfiles = Dir[root_dir + "/addons/**/**/Gemfile"]
|
10
|
+
gemfiles.each do |gemfile|
|
11
|
+
bd = Bundler::Definition.build(gemfile, nil, nil)
|
12
|
+
runtime_deps.push *bd.dependencies.select{ |dep| dep.type == :runtime }
|
13
|
+
development_deps.push *bd.dependencies.select{ |dep| dep.type == :development }
|
14
|
+
end
|
15
|
+
|
16
|
+
runtime_h = Hash.new{ |h,name| h[name] = Gem::Dependency.new(name) }
|
17
|
+
runtime_deps.each { |dep| runtime_h[dep.name] = runtime_h[dep.name].merge(dep) }
|
18
|
+
|
19
|
+
dev_h = Hash.new{ |h,name| h[name] = Gem::Dependency.new(name) }
|
20
|
+
development_deps.each { |dep| dev_h[dep.name] = dev_h[dep.name].merge(dep) }
|
21
|
+
|
22
|
+
deps_str = ""
|
23
|
+
deps_str << runtime_h.map do |name, dep|
|
24
|
+
if dep.requirement.none?
|
25
|
+
%| spec.add_dependency "#{dep.name}"|
|
26
|
+
else
|
27
|
+
%| spec.add_dependency "#{dep.name}", "#{dep.requirement.as_list.first}"|
|
28
|
+
end
|
29
|
+
end.join("\n")
|
30
|
+
|
31
|
+
deps_str << dev_h.map do |name, dep|
|
32
|
+
if dep.requirement.none?
|
33
|
+
%| spec.add_development_dependency "#{dep.name}"|
|
34
|
+
else
|
35
|
+
%| spec.add_development_dependency "#{dep.name}", "#{dep.requirement.as_list.first}"|
|
36
|
+
end
|
37
|
+
end.join("\n")
|
38
|
+
|
39
|
+
gemspec = Dir[root_dir + "/*.gemspec"].first || raise("No gemspec found in directory: #{root_dir}")
|
40
|
+
gemspec = File.expand_path(gemspec)
|
41
|
+
contents = File.read(gemspec)
|
42
|
+
new_contents = contents.sub(/(\#--BEGIN_ADDON_GEM_DEPENDENCIES--\#)\s*.*(^.*\#--END_ADDON_GEM_DEPENDENCIES--\#)/mx) do
|
43
|
+
"#{$1}\n#{deps_str}\n#{$2}"
|
44
|
+
end
|
45
|
+
|
46
|
+
File.write(gemspec, new_contents)
|
47
|
+
puts "Updated #{gemspec}"
|
48
|
+
puts new_contents
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|