yap-shell 0.1.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|