space 0.0.3 → 0.0.4
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.
- data/Gemfile +2 -0
- data/Gemfile.lock +17 -2
- data/Guardfile +5 -0
- data/README.md +4 -0
- data/Rakefile +1 -1
- data/bin/space +2 -1
- data/lib/core_ext/{enumerable.rb → enumerable/map_slice.rb} +2 -7
- data/lib/core_ext/{string.rb → string/wrap.rb} +1 -2
- data/lib/space.rb +6 -16
- data/lib/space/app.rb +36 -48
- data/lib/space/app/command.rb +45 -0
- data/lib/space/app/command/builtin.rb +43 -0
- data/lib/space/app/command/development.rb +60 -0
- data/lib/space/app/handler.rb +47 -0
- data/lib/space/app/logger.rb +21 -0
- data/lib/space/{action → app}/parser.rb +3 -2
- data/lib/space/config.rb +14 -6
- data/lib/space/events.rb +34 -0
- data/lib/space/events/buffer.rb +18 -0
- data/lib/space/events/event.rb +14 -0
- data/lib/space/helpers.rb +30 -15
- data/lib/space/models.rb +7 -0
- data/lib/space/models/project.rb +48 -0
- data/lib/space/models/project/bundler.rb +30 -0
- data/lib/space/models/repo.rb +46 -29
- data/lib/space/models/repo/bundle.rb +39 -0
- data/lib/space/models/repo/dependency.rb +22 -0
- data/lib/space/models/repo/git.rb +52 -0
- data/lib/space/models/repos.rb +31 -26
- data/lib/space/models/repos/collection.rb +26 -0
- data/lib/space/models/repos/scope.rb +18 -0
- data/lib/space/screen.rb +19 -15
- data/lib/space/screen/dashboard.rb +28 -0
- data/lib/space/screen/progress.rb +15 -0
- data/lib/space/screen/view.rb +43 -0
- data/lib/space/shell.rb +56 -0
- data/lib/space/shell/command.rb +63 -0
- data/lib/space/shell/watch.rb +50 -0
- data/lib/space/shell/watcher.rb +40 -0
- data/lib/space/templates/{project.erb → header.erb} +0 -1
- data/lib/space/templates/{repository.erb → repo.erb} +1 -1
- data/lib/space/tmux.rb +11 -0
- data/lib/space/version.rb +1 -1
- metadata +33 -15
- data/lib/space/action.rb +0 -84
- data/lib/space/models/bundle.rb +0 -44
- data/lib/space/models/command.rb +0 -28
- data/lib/space/models/commands.rb +0 -23
- data/lib/space/models/dependency.rb +0 -18
- data/lib/space/models/git.rb +0 -23
- data/lib/space/view.rb +0 -25
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'observer'
|
2
|
+
|
3
|
+
module Space
|
4
|
+
module Models
|
5
|
+
class Repo
|
6
|
+
class Git
|
7
|
+
include Events, Shell
|
8
|
+
|
9
|
+
commands status: 'git status',
|
10
|
+
branch: 'git branch --no-color',
|
11
|
+
commit: 'git log -1 --no-color HEAD'
|
12
|
+
|
13
|
+
watch '.'
|
14
|
+
|
15
|
+
attr_reader :repo
|
16
|
+
|
17
|
+
def initialize(repo)
|
18
|
+
@repo = repo
|
19
|
+
super(repo.path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def branch
|
23
|
+
result(:branch) =~ /^\* (.+)/ && $1.strip
|
24
|
+
end
|
25
|
+
|
26
|
+
def commit
|
27
|
+
result(:commit) =~ /^commit (\S{7})/ && $1
|
28
|
+
end
|
29
|
+
|
30
|
+
def status
|
31
|
+
dirty? ? :dirty : (ahead? ? :ahead : :clean)
|
32
|
+
end
|
33
|
+
|
34
|
+
def ahead?
|
35
|
+
ahead > 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def ahead
|
39
|
+
result(:status) =~ /Your branch is ahead of .* by (\d+) commits?\./ ? $1.to_i : 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def dirty?
|
43
|
+
!clean?
|
44
|
+
end
|
45
|
+
|
46
|
+
def clean?
|
47
|
+
result(:status).include?('nothing to commit (working directory clean)')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/space/models/repos.rb
CHANGED
@@ -1,45 +1,50 @@
|
|
1
1
|
module Space
|
2
|
-
|
3
|
-
class
|
2
|
+
module Models
|
3
|
+
class Repos
|
4
|
+
include Events
|
5
|
+
|
6
|
+
autoload :Collection, 'space/models/repos/collection'
|
7
|
+
|
8
|
+
attr_accessor :project, :paths, :scope
|
9
|
+
|
10
|
+
def initialize(project, paths)
|
11
|
+
@project = project
|
12
|
+
@paths = paths
|
13
|
+
end
|
14
|
+
|
4
15
|
def all
|
5
|
-
@all ||=
|
16
|
+
@all ||= Collection.new(self, paths.map { |path| Repo.new(project, path) })
|
6
17
|
end
|
7
18
|
|
8
19
|
def names
|
9
20
|
@names ||= all.map(&:name)
|
10
21
|
end
|
11
22
|
|
12
|
-
def
|
13
|
-
|
23
|
+
def scope=(scope)
|
24
|
+
@scope = scope
|
25
|
+
notify(:update, nil)
|
14
26
|
end
|
15
27
|
|
16
|
-
def
|
17
|
-
all
|
28
|
+
def scope
|
29
|
+
@scope || all
|
18
30
|
end
|
19
31
|
|
20
|
-
def
|
21
|
-
|
32
|
+
def scoped?
|
33
|
+
!!@scope
|
22
34
|
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def names
|
26
|
-
map(&:name)
|
27
|
-
end
|
28
|
-
|
29
|
-
def scoped?
|
30
|
-
size != self.class.all.size
|
31
|
-
end
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
def find_by_name(name)
|
37
|
+
all.detect { |repo| repo.name == name } || raise("cannot find repo #{name.inspect}")
|
38
|
+
end
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
+
def select_by_names(names)
|
41
|
+
Collection.new(self, all.select { |repo| names.include?(repo.name) })
|
42
|
+
end
|
40
43
|
|
41
|
-
|
42
|
-
|
44
|
+
def subscribe(*args)
|
45
|
+
super
|
46
|
+
all.each { |repo| repo.subscribe(*args) }
|
47
|
+
end
|
43
48
|
end
|
44
49
|
end
|
45
50
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Space
|
2
|
+
module Models
|
3
|
+
class Repos
|
4
|
+
class Collection < Array
|
5
|
+
attr_reader :repos
|
6
|
+
|
7
|
+
def initialize(repos, elements)
|
8
|
+
@repos = repos
|
9
|
+
super(elements)
|
10
|
+
end
|
11
|
+
|
12
|
+
def names
|
13
|
+
map(&:name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self_and_deps
|
17
|
+
Collection.new(repos, (self + deps).uniq)
|
18
|
+
end
|
19
|
+
|
20
|
+
def deps
|
21
|
+
map(&:deps).flatten.map(&:repo).compact
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Space
|
2
|
+
module Models
|
3
|
+
class Repos
|
4
|
+
class Collection < Array
|
5
|
+
attr_reader :repos
|
6
|
+
|
7
|
+
def initialize(repos, elements)
|
8
|
+
@repos = repos
|
9
|
+
super(elements)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self_and_dependencies
|
13
|
+
Collection.new(repos, (self + map(&:dependencies)).flatten.uniq)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/space/screen.rb
CHANGED
@@ -1,28 +1,32 @@
|
|
1
1
|
module Space
|
2
2
|
class Screen
|
3
|
-
|
3
|
+
autoload :Progress, 'space/screen/progress'
|
4
|
+
autoload :Dashboard, 'space/screen/dashboard'
|
5
|
+
autoload :View, 'space/screen/view'
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
+
attr_reader :project, :view
|
8
|
+
|
9
|
+
def initialize(project)
|
10
|
+
@project = project
|
11
|
+
end
|
12
|
+
|
13
|
+
def display(name)
|
14
|
+
@view = create(name)
|
15
|
+
render
|
7
16
|
end
|
8
17
|
|
9
18
|
def render
|
10
|
-
|
11
|
-
puts render_project
|
12
|
-
repos = app.repos.scoped? ? app.repos.self_and_dependencies : app.repos
|
13
|
-
repos.each do |repo|
|
14
|
-
puts render_repo(repo)
|
15
|
-
end
|
19
|
+
view.render
|
16
20
|
end
|
17
21
|
|
18
|
-
|
22
|
+
def notify(event)
|
23
|
+
view.notify(event)
|
24
|
+
end
|
19
25
|
|
20
|
-
|
21
|
-
View.new.render(:project, :app => app, :bundle => app.bundle)
|
22
|
-
end
|
26
|
+
private
|
23
27
|
|
24
|
-
def
|
25
|
-
|
28
|
+
def create(screen)
|
29
|
+
self.class.const_get(screen.to_s.capitalize).new(project)
|
26
30
|
end
|
27
31
|
end
|
28
32
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Space
|
2
|
+
class Screen
|
3
|
+
class Dashboard < View
|
4
|
+
def render
|
5
|
+
App.logger.debug('RENDER dashboard')
|
6
|
+
clear
|
7
|
+
render_header
|
8
|
+
render_repos
|
9
|
+
end
|
10
|
+
|
11
|
+
def notify(event)
|
12
|
+
render
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def render_repos
|
18
|
+
project.repos.scope.self_and_deps.each do |repo|
|
19
|
+
render_template(:repo, assigns(repo))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def assigns(repo)
|
24
|
+
{ repos: project.repos, repo: repo, git: repo.git, bundle: repo.bundle }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Space
|
4
|
+
class Screen
|
5
|
+
class View
|
6
|
+
include Helpers
|
7
|
+
|
8
|
+
attr_reader :project
|
9
|
+
|
10
|
+
def initialize(project)
|
11
|
+
@project = project
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def clear
|
17
|
+
print "\e[2J\e[0;0H" # clear screen, move cursor to home
|
18
|
+
end
|
19
|
+
|
20
|
+
def render_header
|
21
|
+
puts "Project #{project.name}\n\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
def render_template(name, assigns)
|
25
|
+
assigns.each { |name, value| assign(name, value) }
|
26
|
+
print template(name).result(binding)
|
27
|
+
end
|
28
|
+
|
29
|
+
def assign(key, value)
|
30
|
+
instance_variable_set(:"@#{key}", value)
|
31
|
+
(class << self; self; end).send(:attr_reader, key)
|
32
|
+
end
|
33
|
+
|
34
|
+
def templates
|
35
|
+
@templates ||= {}
|
36
|
+
end
|
37
|
+
|
38
|
+
def template(name)
|
39
|
+
templates[name] ||= ERB.new(File.read("#{project.config.template_dir}/#{name}.erb"), nil, '%<>-')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/space/shell.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module Space
|
2
|
+
module Shell
|
3
|
+
autoload :Command, 'space/shell/command'
|
4
|
+
autoload :Watch, 'space/shell/watch'
|
5
|
+
autoload :Watcher, 'space/shell/watcher'
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def commands(commands = nil)
|
9
|
+
commands ? @commands = commands : @commands
|
10
|
+
end
|
11
|
+
|
12
|
+
def watch(*paths)
|
13
|
+
paths.empty? ? (@paths || []) : (@paths = paths)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
include Watcher
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def included(base)
|
21
|
+
base.extend(ClassMethods)
|
22
|
+
end
|
23
|
+
|
24
|
+
def all
|
25
|
+
@all ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
def refresh
|
29
|
+
all.map(&:refresh)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :path
|
34
|
+
|
35
|
+
def initialize(path = '.')
|
36
|
+
@path = File.expand_path(path)
|
37
|
+
Shell.all << self
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
def result(command)
|
42
|
+
commands[command].result
|
43
|
+
end
|
44
|
+
|
45
|
+
def commands
|
46
|
+
@commands ||= self.class.commands.inject({}) do |commands, (key, command)|
|
47
|
+
commands.merge(key => Command.new(self, command))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def refresh
|
52
|
+
commands.each { |key, command| command.run }
|
53
|
+
# notify(:refresh, self.class.name)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'ansi/code'
|
2
|
+
|
3
|
+
module Space
|
4
|
+
module Shell
|
5
|
+
class Command
|
6
|
+
class << self
|
7
|
+
def execute(command)
|
8
|
+
`#{command}`
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :context, :command, :result
|
13
|
+
|
14
|
+
def initialize(context, command)
|
15
|
+
@context = context
|
16
|
+
@command = command
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
Watcher.suspend do
|
21
|
+
App.logger.debug "RUNNING #{command} [#{context.path}]"
|
22
|
+
notifying do
|
23
|
+
@result = chain.call
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def notifying(&block)
|
31
|
+
old = result
|
32
|
+
yield.tap do |new|
|
33
|
+
notify unless old == new
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def notify
|
38
|
+
context.notify(command, result)
|
39
|
+
end
|
40
|
+
|
41
|
+
def chain
|
42
|
+
runner = -> { clean(self.class.execute(command)) }
|
43
|
+
filters.reverse.inject(runner) { |runner, filter| -> { filter.call(&runner) } }
|
44
|
+
end
|
45
|
+
|
46
|
+
def filters
|
47
|
+
[method(:chdir), ::Bundler.method(:with_clean_env) ]
|
48
|
+
end
|
49
|
+
|
50
|
+
def chdir(&block)
|
51
|
+
Dir.chdir(context.path) { |path| block.call }
|
52
|
+
end
|
53
|
+
|
54
|
+
def clean(string)
|
55
|
+
strip_ansi(string.chomp)
|
56
|
+
end
|
57
|
+
|
58
|
+
def strip_ansi(string)
|
59
|
+
string.gsub(ANSI::Code::PATTERN, '')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rb-fsevent'
|
2
|
+
|
3
|
+
module Space
|
4
|
+
module Shell
|
5
|
+
class Watch
|
6
|
+
LATENCY = 0
|
7
|
+
NO_DEFER = FALSE
|
8
|
+
|
9
|
+
attr_reader :path, :callback, :mutex
|
10
|
+
|
11
|
+
def initialize(path, &block)
|
12
|
+
@path = File.expand_path(path)
|
13
|
+
@callback = block
|
14
|
+
@mutex = Mutex.new
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
Thread.new do
|
20
|
+
App.logger.debug("WATCHING #{path}")
|
21
|
+
watch
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def watch
|
28
|
+
fsevent.watch(path, file: file?, latency: LATENCY, no_defer: NO_DEFER) do |paths|
|
29
|
+
App.logger.debug("WATCH triggered: #{paths.inspect}")
|
30
|
+
fsevent.stop
|
31
|
+
mutex.synchronize do
|
32
|
+
callback.call(paths)
|
33
|
+
end
|
34
|
+
fsevent.run
|
35
|
+
end
|
36
|
+
fsevent.run
|
37
|
+
rescue Exception => e
|
38
|
+
puts e.message, e.backtrace
|
39
|
+
end
|
40
|
+
|
41
|
+
def file?
|
42
|
+
File.file?(path)
|
43
|
+
end
|
44
|
+
|
45
|
+
def fsevent
|
46
|
+
@fsevent ||= FSEvent.new
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|