Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

thread/event based rewrite

  • Loading branch information...
commit f1b53715e0586d63e8793e241537f577b5ba02ae 1 parent bf14d19
@svenfuchs authored
Showing with 803 additions and 624 deletions.
  1. +6 −0 bin/space
  2. +5 −0 lib/core_ext/string/demodulize.rb
  3. +4 −4 lib/space.rb
  4. +43 −0 lib/space/action.rb
  5. +43 −0 lib/space/action/builtin.rb
  6. +56 −0 lib/space/action/development.rb
  7. +17 −9 lib/space/{app → action}/handler.rb
  8. +4 −7 lib/space/{app → action}/parser.rb
  9. +15 −30 lib/space/app.rb
  10. +0 −45 lib/space/app/command.rb
  11. +0 −43 lib/space/app/command/builtin.rb
  12. +0 −60 lib/space/app/command/development.rb
  13. +0 −21 lib/space/app/logger.rb
  14. +31 −21 lib/space/events.rb
  15. +0 −18 lib/space/events/buffer.rb
  16. +0 −14 lib/space/events/event.rb
  17. +30 −0 lib/space/events/sources.rb
  18. +23 −0 lib/space/events/subscription.rb
  19. +34 −0 lib/space/logger.rb
  20. +7 −0 lib/space/model.rb
  21. +4 −10 lib/space/{models → model}/project.rb
  22. +7 −8 lib/space/{models → model}/project/bundler.rb
  23. +30 −0 lib/space/model/project/bundler/config.rb
  24. +38 −0 lib/space/model/repo.rb
  25. +6 −7 lib/space/{models → model}/repo/bundle.rb
  26. +1 −1  lib/space/{models → model}/repo/dependency.rb
  27. +14 −16 lib/space/{models → model}/repo/git.rb
  28. +18 −9 lib/space/{models → model}/repos.rb
  29. +26 −0 lib/space/model/repos/collection.rb
  30. +18 −0 lib/space/model/repos/scope.rb
  31. +0 −7 lib/space/models.rb
  32. +0 −57 lib/space/models/repo.rb
  33. +28 −11 lib/space/screen.rb
  34. +21 −5 lib/space/screen/dashboard.rb
  35. +16 −5 lib/space/screen/progress.rb
  36. +2 −6 lib/space/screen/view.rb
  37. +0 −56 lib/space/shell.rb
  38. +0 −63 lib/space/shell/command.rb
  39. +0 −50 lib/space/shell/watch.rb
  40. +0 −40 lib/space/shell/watcher.rb
  41. +51 −0 lib/space/source.rb
  42. +71 −0 lib/space/source/command.rb
  43. +64 −0 lib/space/source/watch.rb
  44. +32 −0 lib/space/source/watcher.rb
  45. +19 −0 play/fsevent.rb
  46. +8 −0 play/run.rb
  47. +10 −0 play/thread.rb
  48. 0  {lib/space/models → }/repos/collection.rb
  49. 0  {lib/space/models → }/repos/scope.rb
  50. +1 −1  spec/app_spec.rb
View
6 bin/space
@@ -5,4 +5,10 @@ require 'bundler/setup'
require 'space'
abort 'Need to specify a project name' if ARGV.empty?
+
+paths = Dir[File.expand_path('lib/**/*.rb')]
+paths.each do |path|
+ require path
+end
+
Space::App.new(ARGV[0]).run
View
5 lib/core_ext/string/demodulize.rb
@@ -0,0 +1,5 @@
+class String
+ def demodulize
+ split('::').last
+ end unless method_defined?(:demodulize)
+end
View
8 lib/space.rb
@@ -2,14 +2,14 @@
require 'hashr'
module Space
- # autoload :Action, 'space/action'
+ autoload :Action, 'space/action'
autoload :App, 'space/app'
autoload :Config, 'space/config'
autoload :Events, 'space/events'
autoload :Helpers, 'space/helpers'
- autoload :Models, 'space/models'
+ autoload :Logger, 'space/logger'
+ autoload :Model, 'space/model'
autoload :Screen, 'space/screen'
- autoload :Shell, 'space/shell'
+ autoload :Source, 'space/source'
autoload :Tmux, 'space/tmux'
end
-
View
43 lib/space/action.rb
@@ -0,0 +1,43 @@
+module Space
+ class Action
+ autoload :Handler, 'space/action/handler'
+ autoload :Parser, 'space/action/parser'
+
+ autoload :Execute, 'space/action/builtin'
+ autoload :Refresh, 'space/action/builtin'
+ autoload :Scope, 'space/action/builtin'
+ autoload :Unscope, 'space/action/builtin'
+
+ autoload :Local, 'space/action/development'
+ autoload :Remote, 'space/action/development'
+
+ attr_reader :project, :scope, :args
+
+ def initialize(project, scope, *args)
+ @project = project
+ @scope = scope
+ @args = args
+ log "ACTION #{self.class.name.demodulize} (#{scope.map(&:name).inspect})"
+ end
+
+ def run
+ raise 'not implemented'
+ end
+
+ private
+
+ def in_scope
+ scope.each { |repo| yield repo }
+ end
+
+ def system(cmd)
+ puts cmd
+ super
+ end
+
+ def confirm
+ puts "--- hit any key to continue ---\n"
+ STDIN.getc
+ end
+ end
+end
View
43 lib/space/action/builtin.rb
@@ -0,0 +1,43 @@
+require 'ansi/core'
+
+module Space
+ class Action
+ class Execute < Action
+ def run
+ Events.sources.registered do
+ in_scope do |repo|
+ Dir.chdir(repo.path) do
+ puts "in #{repo.path}\n".ansi(:bold, :green)
+ system(*args)
+ puts
+ end
+ end
+ confirm
+ end
+ end
+ end
+
+ class Refresh < Action
+ def run
+ project.bundler.refresh
+ in_scope do |repo|
+ repo.refresh
+ end
+ end
+ end
+
+ class Scope < Action
+ def run
+ project.repos.scope = scope
+ Events.trigger(:finish)
+ end
+ end
+
+ class Unscope < Action
+ def run
+ project.repos.scope = nil
+ Events.trigger(:finish)
+ end
+ end
+ end
+end
View
56 lib/space/action/development.rb
@@ -0,0 +1,56 @@
+module Space
+ class Action
+ class Local < Action
+ def run
+ Events.sources.registered do
+ scope.each do |repo|
+ system "bundle config --global local.#{repo.name} #{repo.path}"
+ end
+ sleep 0.2 # not perfect, but fsevent is too slow to trigger
+ end
+ end
+ end
+
+ class Remote < Action
+ def run
+ Events.sources.registered do
+ scope.each do |repo|
+ system "bundle config --delete local.#{repo.name}"
+ end
+ sleep 0.2
+ end
+ end
+ end
+
+ # class Install < Action
+ # def run
+ # in_scope do |repo|
+ # repo.execute 'bundle install'
+ # repo.refresh
+ # end
+ # end
+ # end
+
+ # class Update < Action
+ # def run
+ # in_scope do |repo|
+ # repo.execute 'bundle update'
+ # repo.execute 'git commit -am "bump dependencies"'
+ # repo.refresh
+ # end
+ # end
+ # end
+
+ # class Checkout < Action
+ # def run
+ # # check if branch exists, git co (-b)
+ # end
+ # end
+
+ # class PullDeps < Action
+ # def run
+ # # pull all dependencies
+ # end
+ # end
+ end
+end
View
26 lib/space/app/handler.rb → lib/space/action/handler.rb
@@ -1,5 +1,7 @@
+require 'core_ext/string/demodulize'
+
module Space
- class App
+ class Action
class Handler
ALIASES = {
'' => 'scope',
@@ -14,27 +16,29 @@ def initialize(project)
def run(line)
scope, type = parse(line)
- command = command_for(scope, type)
- command.run
+ action = action_for(scope, type)
+ action.run
+ Events.flush
end
private
- def command_for(scope, type)
+ def action_for(scope, type)
const = const_for(type)
- args = [project, scope]
- args << type if const == Command::Execute
+ repos = repos_for(scope)
+ args = [project, repos]
+ args << type if const == Action::Execute
const.new(*args)
end
def parse(line)
- Parser.new(project.names).parse(line)
+ Parser.new(project.repos.names).parse(line)
end
def const_for(type)
- Command.const_get(const_name(type))
+ Action.const_get(const_name(type))
rescue NameError
- Command::Execute
+ Action::Execute
end
def const_name(type)
@@ -42,6 +46,10 @@ def const_name(type)
type = ALIASES[type] if ALIASES.key?(type)
type.capitalize
end
+
+ def repos_for(scope)
+ scope ? project.repos.select_by_names_or_numbers(scope) : project.repos.scope.self_and_deps
+ end
end
end
end
View
11 lib/space/app/parser.rb → lib/space/action/parser.rb
@@ -1,5 +1,5 @@
module Space
- class App
+ class Action
class Parser
attr_reader :names, :line
@@ -11,7 +11,7 @@ def parse(line)
@line = line
scope = parse_scope
command = parse_command
- [scope || names, command]
+ [scope, command]
end
private
@@ -19,7 +19,7 @@ def parse(line)
def parse_scope
scope = []
pattern = /^(#{names.join('|')}|\d+)\s*/
- line.gsub!(pattern) { |repo| scope << resolve(repo.strip); '' } while line =~ pattern
+ line.gsub!(pattern) { |repo| scope << repo.strip; '' } while line =~ pattern
line.strip!
scope unless scope.empty?
end
@@ -28,11 +28,8 @@ def parse_command
line.strip
line unless line.empty?
end
-
- def resolve(repo)
- repo =~ /^\d+$/ ? names[repo.to_i - 1] : repo
- end
end
end
end
+
View
45 lib/space/app.rb
@@ -1,55 +1,41 @@
+require 'space/logger'
require 'readline'
+require 'thread'
module Space
class App
- autoload :Command, 'space/app/command'
- autoload :Handler, 'space/app/handler'
- autoload :Logger, 'space/app/logger'
- autoload :Parser, 'space/app/parser'
-
- class << self
- def logger
- @logger ||= Logger.new
- end
- end
-
- include Readline
-
attr_reader :name, :project, :screen
def initialize(name)
@name = name
- @project = Models::Project.new(name)
+ @project = Model::Project.new(name)
@screen = Screen.new(project)
-
- project.subscribe(screen)
end
def run
- refresh
- screen.display(:dashboard)
+ log 'foo'
+ screen.display
+ project.refresh
cli_loop
+ # Thread.new(&method(:cli_loop))
+ # sleep
+ puts
end
private
- def refresh
- screen.display(:progress)
- project.refresh
- end
-
def cli_loop
loop do
- line = readline(prompt, true)
- break if line.nil?
- handle(line) unless line.empty?
+ print "\e[3;0H"
+ line = Readline.readline(prompt, true) || break
+ handle(line)
end
+ rescue Exception => e
+ log e.message, e.backtrace
end
def handle(line)
- screen.display(:progress)
- Handler.new(project).run(line)
- screen.display(:dashboard)
+ Action::Handler.new(project).run(line) unless line.empty?
end
def prompt
@@ -57,4 +43,3 @@ def prompt
end
end
end
-
View
45 lib/space/app/command.rb
@@ -1,45 +0,0 @@
-module Space
- class App
- class Command
- autoload :Execute, 'space/app/command/builtin'
- autoload :Refresh, 'space/app/command/builtin'
- autoload :Scope, 'space/app/command/builtin'
- autoload :Unscope, 'space/app/command/builtin'
-
- autoload :Local, 'space/app/command/development'
- autoload :Remote, 'space/app/command/development'
-
- attr_reader :project, :scope, :args
-
- def initialize(project, scope, *args)
- @project = project
- @scope = scope
- @args = args
- end
-
- def run
- raise 'not implemented'
- end
-
- private
-
- def repos
- @repos ||= project.repos.select_by_names(scope)
- end
-
- def in_scope
- repos.each { |repo| yield repo }
- end
-
- def system(cmd)
- puts cmd
- super
- end
-
- def confirm
- puts "\n--- hit any key to continue ---\n"
- STDIN.getc
- end
- end
- end
-end
View
43 lib/space/app/command/builtin.rb
@@ -1,43 +0,0 @@
-module Space
- class App
- class Command
- class Execute < Command
- def run
- Bundler.with_clean_env do
- in_scope do |repo|
- repo.buffering do
- puts
- repo.execute(*args)
- end
- end
- confirm
- end
- end
- end
-
- class Refresh < Command
- def run
- Bundler.with_clean_env do
- project.bundler.refresh
- in_scope do |repo|
- repo.refresh
- end
- end
- end
- end
-
- class Scope < Command
- def run
- project.repos.scope = repos
- end
- end
-
- class Unscope < Command
- def run
- project.repos.scope = nil
- end
- end
- end
- end
-end
-
View
60 lib/space/app/command/development.rb
@@ -1,60 +0,0 @@
-module Space
- class App
- class Command
- class Local < Command
- def run
- Shell::Watcher.suspend do
- repos.each do |repo|
- system "bundle config --global local.#{repo.name} #{repo.path}"
- end
- confirm
- end
- project.bundler.refresh
- end
- end
-
- class Remote < Command
- def run
- Shell::Watcher.suspend do
- repos.each do |repo|
- system "bundle config --delete local.#{repo.name}"
- end
- confirm
- end
- project.bundler.refresh
- end
- end
-
- # class Install < Command
- # def run
- # in_scope do |repo|
- # repo.execute 'bundle install'
- # repo.refresh
- # end
- # end
- # end
-
- # class Update < Command
- # def run
- # in_scope do |repo|
- # repo.execute 'bundle update'
- # repo.execute 'git commit -am "bump dependencies"'
- # repo.refresh
- # end
- # end
- # end
-
- # class Checkout < Command
- # def run
- # # check if branch exists, git co (-b)
- # end
- # end
-
- # class PullDeps < Command
- # def run
- # # pull all dependencies
- # end
- # end
- end
- end
-end
View
21 lib/space/app/logger.rb
@@ -1,21 +0,0 @@
-require 'logger'
-require 'fileutils'
-
-module Space
- class App
- class Logger < ::Logger
- def initialize
- truncate
- super('/tmp/space.log')
- end
-
- def truncate
- File.open(filename, 'w+') { |f| f.write('-' * 80 + "\n") }
- end
-
- def filename
- '/tmp/space.log'
- end
- end
- end
-end
View
52 lib/space/events.rb
@@ -1,34 +1,44 @@
module Space
module Events
- autoload :Buffer, 'space/events/buffer'
- autoload :Event, 'space/events/event'
+ autoload :Subscription, 'space/events/subscription'
+ autoload :Sources, 'space/events/sources'
- attr_reader :buffer
+ class << self
+ def sources
+ @sources ||= Sources.new(self)
+ end
- def buffering
- @buffer = Buffer.new
- yield.tap do
- buffer, @buffer = @buffer, nil
- buffer.flush
+ def subscriptions
+ @subscriptions ||= []
end
- end
- def buffering?
- !!@buffer
- end
+ def events
+ @events ||= []
+ end
- def observers
- @observers ||= []
- end
+ def subscribe(observer, *types)
+ subscriptions << Subscription.new(observer, types)
+ end
- def subscribe(observer)
- observers << observer
+ def trigger(event)
+ notify(event)
+ end
+
+ def flush
+ event = events.first
+ events.clear
+ notify(event, false) if event
+ end
+
+ def notify(event)
+ subscriptions.each do |subscription|
+ subscription.notify(event)
+ end
+ end
end
- def notify(*args)
- event = args.first.is_a?(Event) ? args.first : Event.new(self, *args)
- App.logger.debug "Event on #{self.class.name.split('::').last}: #{event.source.class.name.split('::').last} #{event.event.inspect}"
- buffering? ? buffer.push(event) : observers.each { |observer| observer.notify(event) }
+ def trigger(*args)
+ Events.trigger(*args)
end
end
end
View
18 lib/space/events/buffer.rb
@@ -1,18 +0,0 @@
-module Space
- module Events
- class Buffer < Array
- def push(event)
- # if any? { |e| e.source == event.source }
- # App.logger.debug("REJECT event #{event.event.inspect} on #{event.source.class.name.split("\\n").last}")
- # else
- super
- # end
- end
-
- def flush
- each { |event| event.source.notify(*event) }
- clear
- end
- end
- end
-end
View
14 lib/space/events/event.rb
@@ -1,14 +0,0 @@
-module Space
- module Events
- class Event
- attr_reader :source, :event, :data
-
- def initialize(source, event, data = {})
- @source = source
- @event = event
- @data = data
- end
- end
- end
-end
-
View
30 lib/space/events/sources.rb
@@ -0,0 +1,30 @@
+module Space
+ module Events
+ class Sources
+ attr_reader :events, :sources
+
+ def initialize(events)
+ @events = events
+ @sources = []
+ end
+
+ def registered
+ register(Thread.current.object_id)
+ yield.tap do
+ unregister(Thread.current.object_id)
+ end
+ end
+
+ def register(source)
+ events.trigger(:start) if sources.empty?
+ sources << source
+ end
+
+ def unregister(source)
+ sources.delete(source)
+ events.trigger(:finish) if sources.empty?
+ end
+ end
+ end
+end
+
View
23 lib/space/events/subscription.rb
@@ -0,0 +1,23 @@
+module Space
+ module Events
+ class Subscription
+ attr_reader :observer, :types
+
+ def initialize(observer, types)
+ @observer = observer
+ @types = types
+ end
+
+ def notify(event)
+ observer.notify(event) if matches?(event)
+ end
+
+ private
+
+ def matches?(event)
+ # log [observer.class, types, event].inspect
+ types.empty? || types.include?(event)
+ end
+ end
+ end
+end
View
34 lib/space/logger.rb
@@ -0,0 +1,34 @@
+require 'logger'
+require 'fileutils'
+
+module Kernel
+ def log(*msgs)
+ $logger.log(*msgs)
+ end
+end
+
+module Space
+ class Logger < ::Logger
+ def initialize(path)
+ truncate
+ super
+ self.formatter = ->(severity, datetime, progname, msg) { "#{msg}\n" }
+ end
+
+ def log(*msgs)
+ msgs.each do |msg|
+ info msg.is_a?(Array) ? msg.join("\n") : msg
+ end
+ end
+
+ def truncate
+ File.open(filename, 'w+') { |f| f.write('-' * 80 + "\n") }
+ end
+
+ def filename
+ '/tmp/space.log'
+ end
+ end
+end
+
+$logger = Space::Logger.new('/tmp/space.log')
View
7 lib/space/model.rb
@@ -0,0 +1,7 @@
+module Space
+ module Model
+ autoload :Project, 'space/model/project'
+ autoload :Repos, 'space/model/repos'
+ autoload :Repo, 'space/model/repo'
+ end
+end
View
14 lib/space/models/project.rb → lib/space/model/project.rb
@@ -1,9 +1,9 @@
module Space
- module Models
+ module Model
class Project
- include Events
+ autoload :Bundler, 'space/model/project/bundler'
- autoload :Bundler, 'space/models/project/bundler'
+ include Events
attr_reader :name, :repos, :bundler, :config
@@ -35,13 +35,7 @@ def number(name)
end
def refresh
- bundler.refresh
- repos.all.each(&:refresh)
- end
-
- def subscribe(*args)
- super
- [bundler, repos].each { |object| object.subscribe(self) }
+ [bundler, repos].each(&:refresh)
end
end
end
View
15 lib/space/models/project/bundler.rb → lib/space/model/project/bundler.rb
@@ -1,10 +1,12 @@
require 'core_ext/enumerable/map_slice'
module Space
- module Models
+ module Model
class Project
class Bundler
- include Events, Shell
+ autoload :Config, 'space/model/project/bundler/config'
+
+ include Source
commands config: 'bundle config'
@@ -14,17 +16,14 @@ class Bundler
def initialize(project)
@project = project
- super()
+ super('.')
end
def config
- lines = result(:config).split("\n")[2..-1] || []
- values = lines.map_slice(3) do |name, value, _|
- [name, value =~ /: "(.*)"/ && $1]
- end
- Hash[*values.compact.flatten]
+ Config.new(result(:config)).to_hash
end
end
end
end
end
+
View
30 lib/space/model/project/bundler/config.rb
@@ -0,0 +1,30 @@
+module Space
+ module Model
+ class Project
+ class Bundler
+ class Config
+ attr_reader :config
+
+ def initialize(config)
+ @config = config
+ end
+
+ def lines
+ config.split("\n")[2..-1] || []
+ end
+
+ def data
+ lines.map_slice(3) do |name, value, _|
+ [name, value =~ /: "(.*)"/ && $1]
+ end.compact.flatten
+ end
+
+ def to_hash
+ Hash[*data]
+ end
+ end
+ end
+ end
+ end
+end
+
View
38 lib/space/model/repo.rb
@@ -0,0 +1,38 @@
+module Space
+ module Model
+ class Repo
+ autoload :Bundle, 'space/model/repo/bundle'
+ autoload :Dependency, 'space/model/repo/dependency'
+ autoload :Git, 'space/model/repo/git'
+
+ attr_reader :project, :path, :git, :bundle
+
+ def initialize(project, path)
+ @project = project
+ @path = File.expand_path(path)
+ @git = Git.new(self)
+ @bundle = Bundle.new(self, project.repos)
+ end
+
+ def name
+ @name ||= File.basename(path)
+ end
+
+ def number
+ @number ||= project.number(name)
+ end
+
+ def ref
+ git.commit
+ end
+
+ def deps
+ bundle.deps
+ end
+
+ def refresh
+ [git, bundle].each(&:refresh)
+ end
+ end
+ end
+end
View
13 lib/space/models/repo/bundle.rb → lib/space/model/repo/bundle.rb
@@ -1,18 +1,16 @@
-require 'observer'
-
module Space
- module Models
+ module Model
class Repo
class Bundle
- include Events, Shell
+ include Source
- commands check: 'bundle check',
+ commands check: 'bundle check --no-lock',
list: 'bundle list'
watch 'Gemfile',
'Gemfile.lock'
- attr_reader :repo, :repos
+ attr_reader :repos
def initialize(repo, repos)
@repo = repo
@@ -25,7 +23,7 @@ def clean?
end
def info
- result(:check).split("\n").first
+ result(:check).split("\n").first || ''
end
def deps
@@ -37,3 +35,4 @@ def deps
end
end
end
+
View
2  lib/space/models/repo/dependency.rb → lib/space/model/repo/dependency.rb
@@ -1,5 +1,5 @@
module Space
- module Models
+ module Model
class Repo
class Dependency
attr_reader :repo, :ref
View
30 lib/space/models/repo/git.rb → lib/space/model/repo/git.rb
@@ -1,10 +1,8 @@
-require 'observer'
-
module Space
- module Models
+ module Model
class Repo
class Git
- include Events, Shell
+ include Source
commands status: 'git status',
branch: 'git branch --no-color',
@@ -19,32 +17,32 @@ def initialize(repo)
super(repo.path)
end
- def branch
- result(:branch) =~ /^\* (.+)/ && $1.strip
- end
-
- def commit
- result(:commit) =~ /^commit (\S{7})/ && $1
- end
-
def status
dirty? ? :dirty : (ahead? ? :ahead : :clean)
end
+ def dirty?
+ !clean?
+ end
+
def ahead?
ahead > 0
end
+ def clean?
+ result(:status) =~ /nothing to commit (working directory clean)/
+ end
+
def ahead
result(:status) =~ /Your branch is ahead of .* by (\d+) commits?\./ ? $1.to_i : 0
end
- def dirty?
- !clean?
+ def branch
+ result(:branch) =~ /^\* (.+)/ && $1.strip
end
- def clean?
- result(:status).include?('nothing to commit (working directory clean)')
+ def commit
+ result(:commit) =~ /^commit (\S{7})/ && $1
end
end
end
View
27 lib/space/models/repos.rb → lib/space/model/repos.rb
@@ -1,9 +1,7 @@
module Space
- module Models
+ module Model
class Repos
- include Events
-
- autoload :Collection, 'space/models/repos/collection'
+ autoload :Collection, 'space/model/repos/collection'
attr_accessor :project, :paths, :scope
@@ -21,8 +19,8 @@ def names
end
def scope=(scope)
+ log "SCOPE: #{scope ? scope.map(&:name) : '-'}"
@scope = scope
- notify(:update, nil)
end
def scope
@@ -33,17 +31,28 @@ def scoped?
!!@scope
end
+ def find_by_name_or_number(repo)
+ repo =~ /^\d+$/ ? find_by_number(repo.to_i) : find_by_name(repo)
+ end
+
def find_by_name(name)
- all.detect { |repo| repo.name == name } || raise("cannot find repo #{name.inspect}")
+ all.detect { |repo| repo.name == name } || raise("cannot find repo by name #{name.inspect}")
+ end
+
+ def find_by_number(number)
+ all.detect { |repo| repo.number == number } || raise("cannot find repo by number #{number.inspect}")
+ end
+
+ def select_by_names_or_numbers(repos)
+ Collection.new(self, repos.map { |repo| find_by_name_or_number(repo) })
end
def select_by_names(names)
Collection.new(self, all.select { |repo| names.include?(repo.name) })
end
- def subscribe(*args)
- super
- all.each { |repo| repo.subscribe(*args) }
+ def refresh
+ all.each(&:refresh)
end
end
end
View
26 lib/space/model/repos/collection.rb
@@ -0,0 +1,26 @@
+module Space
+ module Model
+ class Repos
+ class Collection < Array
+ attr_reader :repos
+
+ def initialize(repos, elements)
+ @repos = repos
+ super(elements)
+ end
+
+ def names
+ map(&:name)
+ end
+
+ def self_and_deps
+ Collection.new(repos, (self + deps).uniq)
+ end
+
+ def deps
+ map(&:deps).flatten.map(&:repo).compact
+ end
+ end
+ end
+ end
+end
View
18 lib/space/model/repos/scope.rb
@@ -0,0 +1,18 @@
+module Space
+ module Model
+ class Repos
+ class Collection < Array
+ attr_reader :repos
+
+ def initialize(repos, elements)
+ @repos = repos
+ super(elements)
+ end
+
+ def self_and_dependencies
+ Collection.new(repos, (self + map(&:dependencies)).flatten.uniq)
+ end
+ end
+ end
+ end
+end
View
7 lib/space/models.rb
@@ -1,7 +0,0 @@
-module Space
- module Models
- autoload :Project, 'space/models/project'
- autoload :Repos, 'space/models/repos'
- autoload :Repo, 'space/models/repo'
- end
-end
View
57 lib/space/models/repo.rb
@@ -1,57 +0,0 @@
-module Space
- module Models
- class Repo
- autoload :Bundle, 'space/models/repo/bundle'
- autoload :Dependency, 'space/models/repo/dependency'
- autoload :Git, 'space/models/repo/git'
-
- include Events
-
- attr_reader :project, :path, :git, :bundle
-
- def initialize(project, path)
- @project = project
- @path = File.expand_path(path)
- @git = Git.new(self)
- @bundle = Bundle.new(self, project.repos)
- end
-
- def name
- @name ||= File.basename(path)
- end
-
- def number
- @number ||= project.number(name)
- end
-
- def ref
- git.commit
- end
-
- def deps
- bundle.deps
- end
-
- def refresh
- git.refresh
- bundle.refresh
- end
-
- def execute(cmd)
- chdir do
- puts "in #{path}".ansi(:bold, :yellow)
- system(cmd)
- end
- end
-
- def chdir(&block)
- Dir.chdir(path, &block)
- end
-
- def subscribe(*args)
- super
- [git, bundle].each { |object| object.subscribe(self) }
- end
- end
- end
-end
View
39 lib/space/screen.rb
@@ -8,25 +8,42 @@ class Screen
def initialize(project)
@project = project
+ render_header
end
- def display(name)
- @view = create(name)
- render
+ def display
+ @views = [Progress.new(project), Dashboard.new(project)]
end
- def render
- view.render
- end
+ # def render
+ # view.render
+ # move prompt.size + 1, 3
+ # end
- def notify(event)
- view.notify(event)
- end
+ # def notify(event)
+ # view.notify(event)
+ # end
private
- def create(screen)
- self.class.const_get(screen.to_s.capitalize).new(project)
+ def render_header
+ print "\e[2J" # clear entire screen
+ move 0, 0
+ puts "Project #{project.name}\n\n"
+ puts prompt
+ end
+
+ def move(x, y)
+ print "\e[#{y};#{x}H"
end
+
+ def prompt
+ "#{project.repos.scoped? ? project.repos.scope.map { |r| r.name }.join(', ') : project.name} > "
+ end
+
+ # def create(screen)
+ # self.class.const_get(screen.to_s.capitalize).new(project)
+ # end
end
end
+
View
26 lib/space/screen/dashboard.rb
@@ -1,11 +1,9 @@
module Space
class Screen
class Dashboard < View
- def render
- App.logger.debug('RENDER dashboard')
- clear
- render_header
- render_repos
+ def initialize(*)
+ Events.subscribe(self, :finish)
+ super
end
def notify(event)
@@ -14,6 +12,24 @@ def notify(event)
private
+ def render
+ App.log 'RENDER dashboard'
+ clear
+ render_repos
+ move prompt.size + 1, 3
+ print "\e[0K"
+ end
+
+ def clear
+ move 0, 4
+ print "\e[J" # clear from cursor down
+ move 0, 5
+ end
+
+ def prompt
+ "#{project.repos.scoped? ? project.repos.scope.map { |r| r.name }.join(', ') : project.name} > "
+ end
+
def render_repos
project.repos.scope.self_and_deps.each do |repo|
render_template(:repo, assigns(repo))
View
21 lib/space/screen/progress.rb
@@ -1,15 +1,26 @@
module Space
class Screen
class Progress < View
- def render
- App.logger.debug('RENDER dashboard')
- clear
- render_header
+ def initialize(*)
+ super
+ Events.subscribe(self, :start, :update)
end
def notify(event)
- print '.'
+ case event
+ when :start
+ clear
+ when :update
+ print '.'
+ end
end
+
+ private
+
+ def clear
+ move 0, 5
+ print "\e[0J" # clear from cursor down
+ end
end
end
end
View
8 lib/space/screen/view.rb
@@ -13,12 +13,8 @@ def initialize(project)
private
- def clear
- print "\e[2J\e[0;0H" # clear screen, move cursor to home
- end
-
- def render_header
- puts "Project #{project.name}\n\n"
+ def move(x, y)
+ print "\e[#{y};#{x}H"
end
def render_template(name, assigns)
View
56 lib/space/shell.rb
@@ -1,56 +0,0 @@
-module Space
- module Shell
- autoload :Command, 'space/shell/command'
- autoload :Watch, 'space/shell/watch'
- autoload :Watcher, 'space/shell/watcher'
-
- module ClassMethods
- def commands(commands = nil)
- commands ? @commands = commands : @commands
- end
-
- def watch(*paths)
- paths.empty? ? (@paths || []) : (@paths = paths)
- end
- end
-
- include Watcher
-
- class << self
- def included(base)
- base.extend(ClassMethods)
- end
-
- def all
- @all ||= []
- end
-
- def refresh
- all.map(&:refresh)
- end
- end
-
- attr_reader :path
-
- def initialize(path = '.')
- @path = File.expand_path(path)
- Shell.all << self
- super
- end
-
- def result(command)
- commands[command].result
- end
-
- def commands
- @commands ||= self.class.commands.inject({}) do |commands, (key, command)|
- commands.merge(key => Command.new(self, command))
- end
- end
-
- def refresh
- commands.each { |key, command| command.run }
- # notify(:refresh, self.class.name)
- end
- end
-end
View
63 lib/space/shell/command.rb
@@ -1,63 +0,0 @@
-require 'ansi/code'
-
-module Space
- module Shell
- class Command
- class << self
- def execute(command)
- `#{command}`
- end
- end
-
- attr_reader :context, :command, :result
-
- def initialize(context, command)
- @context = context
- @command = command
- end
-
- def run
- Watcher.suspend do
- App.logger.debug "RUNNING #{command} [#{context.path}]"
- notifying do
- @result = chain.call
- end
- end
- end
-
- private
-
- def notifying(&block)
- old = result
- yield.tap do |new|
- notify unless old == new
- end
- end
-
- def notify
- context.notify(command, result)
- end
-
- def chain
- runner = -> { clean(self.class.execute(command)) }
- filters.reverse.inject(runner) { |runner, filter| -> { filter.call(&runner) } }
- end
-
- def filters
- [method(:chdir), ::Bundler.method(:with_clean_env) ]
- end
-
- def chdir(&block)
- Dir.chdir(context.path) { |path| block.call }
- end
-
- def clean(string)
- strip_ansi(string.chomp)
- end
-
- def strip_ansi(string)
- string.gsub(ANSI::Code::PATTERN, '')
- end
- end
- end
-end
View
50 lib/space/shell/watch.rb
@@ -1,50 +0,0 @@
-require 'rb-fsevent'
-
-module Space
- module Shell
- class Watch
- LATENCY = 0
- NO_DEFER = FALSE
-
- attr_reader :path, :callback, :mutex
-
- def initialize(path, &block)
- @path = File.expand_path(path)
- @callback = block
- @mutex = Mutex.new
- self
- end
-
- def run
- Thread.new do
- App.logger.debug("WATCHING #{path}")
- watch
- end
- end
-
- private
-
- def watch
- fsevent.watch(path, file: file?, latency: LATENCY, no_defer: NO_DEFER) do |paths|
- App.logger.debug("WATCH triggered: #{paths.inspect}")
- fsevent.stop
- mutex.synchronize do
- callback.call(paths)
- end
- fsevent.run
- end
- fsevent.run
- rescue Exception => e
- puts e.message, e.backtrace
- end
-
- def file?
- File.file?(path)
- end
-
- def fsevent
- @fsevent ||= FSEvent.new
- end
- end
- end
-end
View
40 lib/space/shell/watcher.rb
@@ -1,40 +0,0 @@
-module Space
- module Shell
- module Watcher
- class << self
- def suspend
- @suspended = true
- yield.tap do
- @suspended = false
- end
- end
-
- def suspended?
- !!@suspended
- end
- end
-
- def initialize(*args)
- watch
- end
-
- private
-
- def watch
- targets.each do |path|
- Watch.new(path, &method(:trigger)).run
- end
- end
-
- def trigger(paths)
- refresh unless Watcher.suspended?
- end
-
- def targets
- self.class.watch.map do |path|
- path[0, 1] == '~' ? path : "#{self.path}/#{path}"
- end
- end
- end
- end
-end
View
51 lib/space/source.rb
@@ -0,0 +1,51 @@
+module Space
+ module Source
+ autoload :Command, 'space/source/command'
+ autoload :Watch, 'space/source/watch'
+ autoload :Watcher, 'space/source/watcher'
+
+ module ClassMethods
+ def commands(commands = nil)
+ commands ? @commands = commands : @commands
+ end
+
+ def watch(*paths)
+ paths.empty? ? (@paths || []) : (@paths = paths)
+ end
+ end
+
+ include Events, Watcher
+
+ class << self
+ def included(base)
+ base.extend(ClassMethods)
+ end
+ end
+
+ attr_reader :results
+
+ def initialize(path)
+ @results = {}
+ super
+ end
+
+ def commands
+ @commands ||= self.class.commands.inject({}) do |commands, (key, command)|
+ commands.merge(key => Command.new(self, key, command))
+ end
+ end
+
+ def result(key)
+ results[key] || ''
+ end
+
+ def refresh
+ commands.each { |key, command| command.refresh }
+ end
+
+ def update(key, result)
+ results[key] = result
+ trigger(:update)
+ end
+ end
+end
View
71 lib/space/source/command.rb
@@ -0,0 +1,71 @@
+require 'ansi/code'
+
+module Space
+ module Source
+ class Command
+ class << self
+ def execute(command)
+ `#{command}`
+ end
+ end
+
+ attr_accessor :source, :key, :command
+
+ def initialize(source, key, command)
+ @source = source
+ @key = key
+ @command = command
+ end
+
+ def refresh
+ Thread.new(&method(:run))
+ end
+
+ private
+
+ def run
+ chain.call
+ rescue Exception => e
+ log e.message, e.backtrace
+ end
+
+ def chain
+ @chain ||= filters.reverse.inject(method(:execute)) do |chain, method|
+ -> { method.call(&chain) }
+ end
+ end
+
+ def execute
+ log "#{File.basename(source.path)} $ #{command}"
+ self.class.execute(command)
+ end
+
+ def filters
+ [
+ Events.sources.method(:registered),
+ method(:update),
+ Thread.method(:exclusive),
+ method(:clean),
+ method(:chdir),
+ ::Bundler.method(:with_clean_env)
+ ]
+ end
+
+ def update
+ source.update(key, yield)
+ end
+
+ def clean
+ strip_ansi(yield.chomp)
+ end
+
+ def chdir(&block)
+ Dir.chdir(source.path) { |path| block.call }
+ end
+
+ def strip_ansi(string)
+ string.gsub(ANSI::Code::PATTERN, '')
+ end
+ end
+ end
+end
View
64 lib/space/source/watch.rb
@@ -0,0 +1,64 @@
+require 'rb-fsevent'
+
+module Space
+ module Source
+ class Watch
+ LATENCY = 0.1
+ NO_DEFER = FALSE
+
+ attr_reader :path, :callback, :suspended, :fsevent
+
+ def initialize(path, &block)
+ @path = File.expand_path(path)
+ @callback = block
+ # @suspended = []
+ @fsevent = FSEvent.new
+ end
+
+ def run
+ @thread = Thread.new do
+ log "WATCHING #{path}"
+ watch
+ end
+ self
+ end
+
+ # def suspended?
+ # !suspended.empty?
+ # end
+
+ # def suspend
+ # suspended << true
+ # end
+
+ # def unsuspend
+ # suspended.pop
+ # end
+
+ private
+
+ def watch
+ fsevent.watch(path, file: file?, latency: LATENCY, no_defer: NO_DEFER) do |paths|
+ unless git_dir?(paths)
+ log "=> WATCH triggered: #{paths.inspect}"
+ fsevent.stop
+ callback.call(paths)
+ fsevent.run
+ end
+ end
+ fsevent.run
+ rescue Exception => e
+ puts e.message, e.backtrace
+ end
+
+ def file?
+ File.file?(path)
+ end
+
+ def git_dir?(paths)
+ paths.size == 1 && File.basename(paths.first) == '.git'
+ end
+ end
+ end
+end
+
View
32 lib/space/source/watcher.rb
@@ -0,0 +1,32 @@
+module Space
+ module Source
+ module Watcher
+ attr_reader :path
+
+ def initialize(path)
+ @path = path
+ start
+ end
+
+ def watched_paths
+ @watched_paths ||= self.class.watch.map do |path|
+ path[0, 1] == '~' ? path : "#{self.path}/#{path}"
+ end
+ end
+
+ private
+
+ def start
+ watchers.map(&:run)
+ end
+
+ def watchers
+ @watchers ||= watched_paths.map do |path|
+ Watch.new(path) do |paths|
+ refresh
+ end
+ end
+ end
+ end
+ end
+end
View
19 play/fsevent.rb
@@ -0,0 +1,19 @@
+require 'rubygems'
+require 'rb-fsevent'
+
+path = File.expand_path('.')
+
+Thread.new do
+ begin
+ fsevent = FSEvent.new
+ fsevent.watch(path, latency: 0.1, file: File.file?(path)) do |paths|
+ p '-------------'
+ p paths
+ end
+ fsevent.run
+ rescue => e
+ puts e.message
+ end
+end
+
+sleep
View
8 play/run.rb
@@ -0,0 +1,8 @@
+$: << 'lib'
+require 'space'
+
+app = Space::App.new('travis')
+app.run
+# @repo = Space::Model::Repo.new(project, '.')
+# @repo.git.refresh
+
View
10 play/thread.rb
@@ -0,0 +1,10 @@
+1.upto(100) do
+ Thread.new do
+ loop do
+ p "KEKSE"
+ sleep 1
+ end
+ end
+end
+
+sleep(100)
View
0  lib/space/models/repos/collection.rb → repos/collection.rb
File renamed without changes
View
0  lib/space/models/repos/scope.rb → repos/scope.rb
File renamed without changes
View
2  spec/app_spec.rb
@@ -23,7 +23,7 @@
def stub_commands
APP_COMMANDS.each do |command, result|
- Space::Shell::Command.stubs(:execute).with(command).returns(result)
+ Space::Source::Command.stubs(:execute).with(command).returns(result)
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.