Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

rewrite

  • Loading branch information...
commit c57020a761f325087f845ba95195ac2bc80422c8 1 parent ce8fb95
@svenfuchs authored
Showing with 1,330 additions and 854 deletions.
  1. +1 −0  Gemfile
  2. +10 −2 Gemfile.lock
  3. +5 −0 Guardfile
  4. +2 −7 lib/core_ext/{enumerable.rb → enumerable/map_slice.rb}
  5. +1 −2  lib/core_ext/{string.rb → string/wrap.rb}
  6. +5 −16 lib/space.rb
  7. +0 −60 lib/space/action.rb
  8. +0 −27 lib/space/action/builtin.rb
  9. +0 −44 lib/space/action/development.rb
  10. +26 −26 lib/space/app.rb
  11. +48 −0 lib/space/app/command.rb
  12. +45 −0 lib/space/app/command/builtin.rb
  13. +61 −0 lib/space/app/command/development.rb
  14. +47 −0 lib/space/app/handler.rb
  15. +3 −2 lib/space/{action → app}/parser.rb
  16. +8 −8 lib/space/config.rb
  17. +15 −0 lib/space/events.rb
  18. +2 −1  lib/space/helpers.rb
  19. +7 −0 lib/space/models.rb
  20. +0 −44 lib/space/models/bundle.rb
  21. +0 −27 lib/space/models/bundler.rb
  22. +0 −38 lib/space/models/command.rb
  23. +0 −41 lib/space/models/commands.rb
  24. +0 −19 lib/space/models/dependency.rb
  25. +0 −45 lib/space/models/git.rb
  26. +33 −28 lib/space/models/project.rb
  27. +34 −0 lib/space/models/project/bundler.rb
  28. +41 −36 lib/space/models/repo.rb
  29. +43 −0 lib/space/models/repo/bundle.rb
  30. +22 −0 lib/space/models/repo/dependency.rb
  31. +56 −0 lib/space/models/repo/git.rb
  32. +28 −37 lib/space/models/repos.rb
  33. +26 −0 lib/space/models/repos/collection.rb
  34. +18 −0 lib/space/models/repos/scope.rb
  35. +0 −47 lib/space/models/watcher.rb
  36. +16 −20 lib/space/screen.rb
  37. +28 −0 lib/space/screen/dashboard.rb
  38. +14 −0 lib/space/screen/progress.rb
  39. +48 −0 lib/space/screen/view.rb
  40. +56 −0 lib/space/shell.rb
  41. +60 −0 lib/space/shell/command.rb
  42. +46 −0 lib/space/shell/watch.rb
  43. +46 −0 lib/space/shell/watcher.rb
  44. +0 −1  lib/space/templates/{project.erb → header.erb}
  45. +1 −0  lib/space/tmux.rb
  46. +0 −33 lib/space/view.rb
  47. +0 −45 lib/space/watch.rb
  48. +0 −36 spec/action/parser_spec.rb
  49. +8 −0 spec/action_spec.rb
  50. +28 −0 spec/app/handler_spec.rb
  51. +26 −0 spec/app/parser_spec.rb
  52. +57 −0 spec/app_spec.rb
  53. +40 −0 spec/config_spec.rb
  54. +0 −15 spec/models/command_spec.rb
  55. +0 −16 spec/models/commands_spec.rb
  56. +0 −13 spec/models/dependency_spec.rb
  57. +0 −59 spec/models/git_spec.rb
  58. +32 −0 spec/models/project/bundler_spec.rb
  59. +46 −0 spec/models/repo/bundle_spec.rb
  60. +88 −0 spec/models/repo/git_spec.rb
  61. +6 −27 spec/models/repo_spec.rb
  62. +0 −31 spec/models/repos_spec.rb
  63. +46 −0 spec/shell/command_spec.rb
  64. +28 −0 spec/shell_spec.rb
  65. +23 −1 spec/spec_helper.rb
View
1  Gemfile
@@ -7,5 +7,6 @@ gem 'rb-fsevent', git: 'git://github.com/niw/rb-fsevent.git'
group :test do
gem 'rake'
gem 'rspec'
+ gem 'guard-rspec'
gem 'mocha'
end
View
12 Gemfile.lock
@@ -9,7 +9,13 @@ GEM
specs:
ansi (1.4.2)
diff-lcs (1.1.3)
- hashr (0.0.19)
+ ffi (1.0.11)
+ guard (1.0.1)
+ ffi (>= 0.5.0)
+ thor (~> 0.14.6)
+ guard-rspec (0.7.0)
+ guard (>= 0.10.0)
+ hashr (0.0.20)
metaclass (0.0.1)
mocha (0.10.5)
metaclass (~> 0.0.1)
@@ -19,15 +25,17 @@ GEM
rspec-expectations (~> 2.9.0)
rspec-mocks (~> 2.9.0)
rspec-core (2.9.0)
- rspec-expectations (2.9.0)
+ rspec-expectations (2.9.1)
diff-lcs (~> 1.1.3)
rspec-mocks (2.9.0)
+ thor (0.14.6)
PLATFORMS
ruby
DEPENDENCIES
ansi
+ guard-rspec
hashr
mocha
rake
View
5 Guardfile
@@ -0,0 +1,5 @@
+guard 'rspec' do
+ watch(%r{^spec/.+_spec\.rb$})
+ watch(%r{^lib/space/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
+ watch('spec/spec_helper.rb') { "spec" }
+end
View
9 lib/core_ext/enumerable.rb → lib/core_ext/enumerable/map_slice.rb
@@ -1,13 +1,8 @@
module Enumerable
- def map_with_index(&block)
- result = []
- each_with_index { |element, ix| result << yield(element, ix) }
- result
- end
-
def map_slice(num, &block)
result = []
each_slice(num) { |element| result << yield(element) }
result
- end
+ end unless method_defined?(:map_slice)
end
+
View
3  lib/core_ext/string.rb → lib/core_ext/string/wrap.rb
@@ -1,7 +1,6 @@
class String
def wrap(width)
gsub(/(.{1,#{width}})( +|$\n?)|(.{1,#{width}})/, "\\1\\3\n")
- end
+ end unless method_defined?(:wrap)
end
-
View
21 lib/space.rb
@@ -1,26 +1,15 @@
require 'yaml'
require 'hashr'
-require 'core_ext/enumerable'
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 :Screen, 'space/screen'
- autoload :System, 'space/system'
- autoload :View, 'space/view'
+ autoload :Shell, 'space/shell'
autoload :Tmux, 'space/tmux'
- autoload :Watch, 'space/watch'
-
- autoload :Bundler, 'space/models/bundler'
- autoload :Bundle, 'space/models/bundle'
- autoload :Commands, 'space/models/commands'
- autoload :Command, 'space/models/command'
- autoload :Dependency, 'space/models/dependency'
- autoload :Git, 'space/models/git'
- autoload :Project, 'space/models/project'
- autoload :Repos, 'space/models/repos'
- autoload :Repo, 'space/models/repo'
- autoload :Watcher, 'space/models/watcher'
end
+
View
60 lib/space/action.rb
@@ -1,60 +0,0 @@
-module Space
- class Action
- autoload :Parser, 'space/action/parser'
- autoload :Builtin, 'space/action/builtin'
- autoload :Development, 'space/action/development'
-
- include Builtin, Development
-
- class << self
- def run(project, line)
- ::Bundler.with_clean_env do
- new(project, *Parser.new(project.names).parse(line)).run
- end
- end
- end
-
- attr_reader :project, :repos, :command, :args
-
- def initialize(project, repos, command, *args)
- @project = project
- @repos = project.repos.select_by_names(repos) if repos
- @command = normalize(command)
- @args = args
- end
-
- def run
- if respond_to?(command)
- send(command)
- elsif command
- execute(command)
- end
- end
-
- private
-
- def run_scoped(refreshing = false)
- (repos || project.repos.scope).each { |repo| yield repo }
- confirm unless refreshing
- end
-
- def system(cmd)
- ::Bundler.with_clean_env do
- puts cmd
- super
- end
- end
-
- def confirm
- puts "\n--- hit any key to continue ---\n"
- STDIN.getc
- end
-
- def normalize(command)
- command = (command || '').strip
- command = 'unscope' if command == '-'
- command = 'scope' if command.empty?
- command
- end
- end
-end
View
27 lib/space/action/builtin.rb
@@ -1,27 +0,0 @@
-module Space
- class Action
- module Builtin
- def refresh
- project.bundler.reset
- run_scoped(true) do |repo|
- repo.reset
- end
- end
-
- def scope
- project.repos.scope = repos
- end
-
- def unscope
- project.repos.scope = nil
- end
-
- def execute(cmd)
- run_scoped do |repo|
- puts
- repo.execute(cmd)
- end
- end
- end
- end
-end
View
44 lib/space/action/development.rb
@@ -1,44 +0,0 @@
-module Space
- class Action
- module Development
- def local
- run_scoped do |repo|
- system "bundle config --global local.#{repo.name} #{repo.path}"
- end
- project.bundler.reset
- end
-
- def remote
- run_scoped do |repo|
- system "bundle config --delete local.#{repo.name}"
- end
- project.bundler.reset
- end
-
- def install
- run_scoped do |repo|
- repo.execute 'bundle install'
- repo.reset
- end
- end
-
- def update
- run_scoped do |repo|
- repo.execute 'bundle update'
- repo.execute 'git commit -am "bump dependencies"'
- repo.reset
- end
- end
-
- def checkout
- # check if branch exists, git co (-b)
- # check Gemfiles, replace gem "travis-*", :branch => '...' with the new branch
- end
-
- def pull_deps
- # pull all dependencies
- end
- end
- end
-end
-
View
52 lib/space/app.rb
@@ -2,50 +2,50 @@
module Space
class App
+ autoload :Command, 'space/app/command'
+ autoload :Handler, 'space/app/handler'
+ autoload :Parser, 'space/app/parser'
+
include Readline
- attr_reader :name, :config, :project, :screen
+ attr_reader :name, :project, :screen
def initialize(name)
@name = name
- @config = Config.load(name)
- @project = Project.new(name, config)
- @screen = Screen.new(name, config, project, project.repos)
+ @project = Models::Project.new(name)
+ @screen = Screen.new(project)
- project.add_observer(self)
+ project.subscribe(screen)
end
def run
- render
- loop do
- line = readline(prompt, true)
- break if line.nil?
- handle(line) unless line.empty?
- end
- end
-
- def update
- render(prompt: prompt)
+ refresh
+ screen.display(:dashboard)
+ prompt
end
private
- def render(options = {})
- Watcher.ignore do
- screen.clear
- print 'gathering data '
- Commands.preload
- screen.render(options)
+ def refresh
+ screen.display(:progress)
+ Shell::Watcher.ignore do
+ project.refresh
end
end
- def handle(line)
- Action.run(project, line)
- render
+ def prompt
+ loop do
+ line = readline(screen.prompt, true)
+ break if line.nil?
+ handle(line) unless line.empty?
+ end
end
- def prompt
- "#{project.repos.scoped? ? project.repos.scope.map { |r| r.name }.join(', ') : name} > "
+ def handle(line)
+ screen.display(:progress)
+ Handler.new(project).run(line)
+ screen.display(:dashboard)
end
end
end
+
View
48 lib/space/app/command.rb
@@ -0,0 +1,48 @@
+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
+
+ # ::Bundler.with_clean_env do
+ # 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
45 lib/space/app/command/builtin.rb
@@ -0,0 +1,45 @@
+module Space
+ class App
+ class Command
+ class Execute < Command
+ def run
+ Bundler.with_clean_env do
+ Shell::Watcher.ignore do
+ in_scope do |repo|
+ puts
+ repo.execute(*args)
+ end
+ confirm
+ end
+ end
+ end
+ end
+
+ class Refresh < Command
+ def run
+ Bundler.with_clean_env do
+ Shell::Watcher.ignore do
+ project.bundler.refresh
+ in_scope do |repo|
+ repo.refresh
+ end
+ 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
61 lib/space/app/command/development.rb
@@ -0,0 +1,61 @@
+module Space
+ class App
+ class Command
+ class Local < Command
+ def run
+ Shell::Watcher.ignore do
+ repos.each do |repo|
+ system "bundle config --global local.#{repo.name} #{repo.path}"
+ end
+ end
+ confirm
+ project.bundler.refresh
+ end
+ end
+
+ class Remote < Command
+ def run
+ Shell::Watcher.ignore do
+ repos.each do |repo|
+ system "bundle config --delete local.#{repo.name}"
+ end
+ end
+ confirm
+ 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)
+ # # check Gemfiles, replace gem "travis-*", :branch => '...' with the new branch
+ # end
+ # end
+
+ # class PullDeps < Command
+ # def run
+ # # pull all dependencies
+ # end
+ # end
+ end
+ end
+end
View
47 lib/space/app/handler.rb
@@ -0,0 +1,47 @@
+module Space
+ class App
+ class Handler
+ ALIASES = {
+ '' => 'scope',
+ '-' => 'unscope',
+ 'r' => 'refresh'
+ }
+ attr_reader :project
+
+ def initialize(project)
+ @project = project
+ end
+
+ def run(line)
+ scope, type = parse(line)
+ command = command_for(scope, type)
+ command.run
+ end
+
+ private
+
+ def command_for(scope, type)
+ const = const_for(type)
+ args = [project, scope]
+ args << type if const == Command::Execute
+ const.new(*args)
+ end
+
+ def parse(line)
+ Parser.new(project.names).parse(line)
+ end
+
+ def const_for(type)
+ Command.const_get(const_name(type))
+ rescue NameError
+ Command::Execute
+ end
+
+ def const_name(type)
+ type = (type || '').strip
+ type = ALIASES[type] if ALIASES.key?(type)
+ type.capitalize
+ end
+ end
+ end
+end
View
5 lib/space/action/parser.rb → lib/space/app/parser.rb
@@ -1,5 +1,5 @@
module Space
- class Action
+ class App
class Parser
attr_reader :names, :line
@@ -11,7 +11,7 @@ def parse(line)
@line = line
scope = parse_scope
command = parse_command
- [scope, command]
+ [scope || names, command]
end
private
@@ -35,3 +35,4 @@ def resolve(repo)
end
end
end
+
View
16 lib/space/config.rb
@@ -5,16 +5,16 @@ def load(name)
new(YAML.load(File.read(path(name))))
end
- def path(name)
- path = paths(name).detect { |path| File.exists?(path) }
- path || abort("Could not find #{name}.yml at either of ~/.space/#{name}.yml and ./.#{name}.yml")
- end
+ private
- def paths(name)
- %W(~/.space/#{name}.yml ./.#{name}.yml).map do |path|
- File.expand_path(path)
+ def path(name)
+ path = paths(name).detect { |path| File.exists?(path) }
+ path || raise("Could not find #{name}.yml at either of ~/.space/#{name}.yml and ./.space/#{name}.yml")
+ end
+
+ def paths(name)
+ %w(. ~).map { |path| File.expand_path("#{path}/.space/#{name}.yml") }
end
- end
end
define :template_dir => File.expand_path('../templates', __FILE__)
View
15 lib/space/events.rb
@@ -0,0 +1,15 @@
+module Space
+ module Events
+ def observers
+ @observers ||= []
+ end
+
+ def subscribe(observer)
+ observers << observer
+ end
+
+ def notify(event, data)
+ observers.each { |observer| observer.notify(event, data) }
+ end
+ end
+end
View
3  lib/space/helpers.rb
@@ -1,6 +1,6 @@
# encoding: UTF-8
require 'ansi/core'
-require 'core_ext/string'
+require 'core_ext/string/wrap'
module Space
module Helpers
@@ -65,3 +65,4 @@ def i(string, width = 2)
end
end
end
+
View
7 lib/space/models.rb
@@ -0,0 +1,7 @@
+module Space
+ module Models
+ autoload :Project, 'space/models/project'
+ autoload :Repos, 'space/models/repos'
+ autoload :Repo, 'space/models/repo'
+ end
+end
View
44 lib/space/models/bundle.rb
@@ -1,44 +0,0 @@
-require 'observer'
-
-module Space
- class Bundle
- include Watcher, Observable, Commands
-
- COMMANDS = {
- check: 'bundle check',
- list: ->(command) { "bundle list | grep #{command.name}" }
- }
-
- WATCH = [
- 'Gemfile',
- 'Gemfile.lock'
- ]
-
- attr_reader :project
-
- def initialize(project, path)
- @project = project
- super(path)
- end
-
- def name
- project.name
- end
-
- def clean?
- info =~ /dependencies are satisfied/
- end
-
- def info
- result(:check).split("\n").first
- end
-
- def deps
- result(:list).split("\n").map do |dep|
- if matches = dep.strip.match(/^\* (?<name>[\S]+) \(\d+\.\d+\.\d+(?: (?<ref>.+))?\)/)
- Dependency.new(project.repos, matches[:name], matches[:ref])
- end
- end.compact
- end
- end
-end
View
27 lib/space/models/bundler.rb
@@ -1,27 +0,0 @@
-require 'observer'
-
-module Space
- class Bundler
- include Watcher, Observable, Commands
-
- COMMANDS = {
- config: 'bundle config'
- }
-
- WATCH = [
- '.bundle/config'
- ]
-
- def initialize
- 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]
- end
- end
-end
View
38 lib/space/models/command.rb
@@ -1,38 +0,0 @@
-require 'ansi/code'
-
-module Space
- class Command
- attr_reader :context, :command
-
- def initialize(context, command)
- @context = context
- @command = command
- end
-
- def result
- @result ||= strip_ansi(run)
- end
-
- def reset
- @result = nil
- end
-
- private
-
- def run
- ::Bundler.with_clean_env do
- chdir do
- `#{command.is_a?(Proc) ? context.instance_eval(&command) : command}`
- end
- end
- end
-
- def chdir(&block)
- Dir.chdir(context.path, &block)
- end
-
- def strip_ansi(string)
- string.gsub(ANSI::Code::PATTERN, '')
- end
- end
-end
View
41 lib/space/models/commands.rb
@@ -1,41 +0,0 @@
-module Space
- module Commands
- class << self
- def all
- @all ||= []
- end
-
- def preload
- all.map(&:preload)
- end
- end
-
- attr_reader :path
-
- def initialize(path)
- @path = File.expand_path(path)
- Commands.all << self
- end
-
- def result(command)
- commands[command].result
- end
-
- def commands
- @commands ||= Hash[*self.class::COMMANDS.map do |key, cmd|
- [key, Command.new(self, cmd)]
- end.flatten]
- end
-
- def reset
- commands.each { |key, command| command.reset }
- end
-
- def preload
- commands.each do |key, command|
- print '.'
- command.result
- end
- end
- end
-end
View
19 lib/space/models/dependency.rb
@@ -1,19 +0,0 @@
-module Space
- class Dependency
- attr_reader :repos, :name, :ref
-
- def initialize(repos, name, ref)
- @repos = repos
- @name = name
- @ref = ref
- end
-
- def fresh?
- repo.ref == ref
- end
-
- def repo
- repos.find_by_name(name) || raise("cannot find repo #{name}")
- end
- end
-end
View
45 lib/space/models/git.rb
@@ -1,45 +0,0 @@
-require 'observer'
-
-module Space
- class Git
- include Watcher, Observable, Commands
-
- COMMANDS = {
- status: 'git status',
- branch: 'git branch --no-color',
- commit: 'git log -1 HEAD'
- }
-
- WATCH = [
- '.'
- ]
-
- 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 ahead?
- ahead > 0
- end
-
- def ahead
- result(:status) =~ /Your branch is ahead of .* by (\d+) commits?\./ ? $1.to_i : 0
- end
-
- def dirty?
- !clean?
- end
-
- def clean?
- result(:status).include?('nothing to commit (working directory clean)')
- end
- end
-end
View
61 lib/space/models/project.rb
@@ -1,38 +1,43 @@
-require 'observer'
-
module Space
- class Project
- attr_reader :name, :repos, :bundler
+ module Models
+ class Project
+ include Events
- def initialize(name, config)
- @name = name
- @repos = Repos.new(self, config.paths)
- @bundler = Bundler.new
- end
+ autoload :Bundler, 'space/models/project/bundler'
- def names
- @names ||= Tmux.windows || repos.names
- end
+ attr_reader :name, :repos, :bundler, :config
- def local_repos
- bundler.config.keys.map do |key|
- key =~ /^local\.(.+)$/
- $1 if repos.names.include?($1)
- end.compact
- end
+ def initialize(name)
+ @name = name
+ @config = Config.load(name)
+ @repos = Repos.new(self, config.paths)
+ @bundler = Bundler.new(self)
+ end
- def number(name)
- if number = names.index(name)
- number + 1
- else
- names << name
- number(name)
+ def local_repos
+ bundler.config.keys.map do |key|
+ key =~ /^local\.(.+)$/
+ $1 if repos.names.include?($1)
+ end.compact
end
- end
- def add_observer(observer)
- repos.add_observer(observer)
- bundler.add_observer(observer)
+ def names
+ @names ||= Tmux.windows || repos.names
+ end
+
+ def number(name)
+ if number = names.index(name)
+ number + 1
+ else
+ names << name
+ number(name)
+ end
+ end
+
+ def refresh
+ bundler.refresh
+ repos.all.each(&:refresh)
+ end
end
end
end
View
34 lib/space/models/project/bundler.rb
@@ -0,0 +1,34 @@
+require 'core_ext/enumerable/map_slice'
+
+module Space
+ module Models
+ class Project
+ class Bundler
+ include Shell
+
+ commands config: 'bundle config'
+
+ watch '.bundle/config'
+
+ attr_reader :project
+
+ def initialize(project)
+ @project = project
+ 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]
+ end
+
+ def notify(event, data)
+ project.notify(event, data)
+ end
+ end
+ end
+ end
+end
View
77 lib/space/models/repo.rb
@@ -1,49 +1,54 @@
module Space
- class Repo
- attr_reader :project, :path, :git, :bundle
-
- def initialize(project, path)
- @project = project
- @path = File.expand_path(path)
- @git = Git.new(path)
- @bundle = Bundle.new(project, path)
- end
+ module Models
+ class Repo
+ autoload :Bundle, 'space/models/repo/bundle'
+ autoload :Dependency, 'space/models/repo/dependency'
+ autoload :Git, 'space/models/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 name
+ @name ||= File.basename(path)
+ end
- def number
- @number ||= project.number(name)
- end
+ def number
+ @number ||= project.number(name)
+ end
- def ref
- git.commit
- end
+ def ref
+ git.commit
+ end
- def dependencies
- project.repos.select_by_names(bundle.deps.map(&:name))
- end
+ def deps
+ bundle.deps
+ end
- def reset
- git.reset
- bundle.reset
- end
+ def refresh
+ git.refresh
+ bundle.refresh
+ end
- def execute(cmd)
- chdir do
- puts "in #{path}".ansi(:bold, :yellow)
- system(cmd)
+ def execute(cmd)
+ chdir do
+ puts "in #{path}".ansi(:bold, :yellow)
+ system(cmd)
+ end
end
- end
- def chdir(&block)
- Dir.chdir(path, &block)
- end
+ def chdir(&block)
+ Dir.chdir(path, &block)
+ end
- def add_observer(observer)
- git.add_observer(observer)
- bundle.add_observer(observer)
+ def notify(event, data)
+ project.notify(event, data)
+ end
end
end
end
View
43 lib/space/models/repo/bundle.rb
@@ -0,0 +1,43 @@
+require 'observer'
+
+module Space
+ module Models
+ class Repo
+ class Bundle
+ include Shell
+
+ commands check: 'bundle check',
+ list: 'bundle list'
+
+ # watch 'Gemfile',
+ # 'Gemfile.lock'
+
+ attr_reader :repo, :repos
+
+ def initialize(repo, repos)
+ @repo = repo
+ @repos = repos
+ super(repo.path)
+ end
+
+ def clean?
+ info =~ /dependencies are satisfied/
+ end
+
+ def info
+ result(:check).split("\n").first
+ end
+
+ def deps
+ result(:list).scan(/\* ([\w-]+) \(.* ([\d|\w]+)\)/).map do |name, ref|
+ Dependency.new(repos.find_by_name(name), ref) if repos.names.include?(name)
+ end.compact
+ end
+
+ def notify(event, data)
+ repo.notify(event, data)
+ end
+ end
+ end
+ end
+end
View
22 lib/space/models/repo/dependency.rb
@@ -0,0 +1,22 @@
+module Space
+ module Models
+ class Repo
+ class Dependency
+ attr_reader :repo, :ref
+
+ def initialize(repo, ref)
+ @repo = repo
+ @ref = ref
+ end
+
+ def name
+ repo.name
+ end
+
+ def fresh?
+ repo.ref == ref
+ end
+ end
+ end
+ end
+end
View
56 lib/space/models/repo/git.rb
@@ -0,0 +1,56 @@
+require 'observer'
+
+module Space
+ module Models
+ class Repo
+ class Git
+ include Shell
+
+ commands status: 'git status',
+ branch: 'git branch --no-color',
+ commit: 'git log -1 --no-color HEAD'
+
+ watch '.'
+
+ attr_reader :repo
+
+ def initialize(repo)
+ @repo = 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 ahead?
+ ahead > 0
+ end
+
+ def ahead
+ result(:status) =~ /Your branch is ahead of .* by (\d+) commits?\./ ? $1.to_i : 0
+ end
+
+ def dirty?
+ !clean?
+ end
+
+ def clean?
+ result(:status).include?('nothing to commit (working directory clean)')
+ end
+
+ def notify(event, data)
+ repo.notify(event, data)
+ end
+ end
+ end
+ end
+end
View
65 lib/space/models/repos.rb
@@ -1,51 +1,42 @@
module Space
- class Repos
- class Scope < Array
- attr_reader :repos
+ module Models
+ class Repos
+ autoload :Collection, 'space/models/repos/collection'
- def initialize(repos, elements)
- @repos = repos
- super(elements)
- end
+ attr_accessor :project, :paths, :scope
- def self_and_dependencies
- Scope.new(repos, (self + map(&:dependencies)).flatten.uniq)
+ def initialize(project, paths)
+ @project = project
+ @paths = paths
end
- end
-
- attr_accessor :project, :paths, :scope
-
- def initialize(project, paths)
- @project = project
- @paths = paths
- end
- def all
- @all ||= Scope.new(self, paths.map { |path| Repo.new(project, path) })
- end
+ def all
+ @all ||= Collection.new(self, paths.map { |path| Repo.new(project, path) })
+ end
- def scope
- @scope || all
- end
+ def names
+ @names ||= all.map(&:name)
+ end
- def scoped?
- !!@scope
- end
+ def scope
+ @scope || all
+ end
- def names
- all.map(&:name)
- end
+ def scoped?
+ !!@scope
+ end
- def find_by_name(name)
- all.detect { |repo| repo.name == name }
- end
+ def find_by_name(name)
+ all.detect { |repo| repo.name == name } || raise("cannot find repo #{name.inspect}")
+ end
- def select_by_names(names)
- Scope.new(self, all.select { |repo| names.include?(repo.name) })
- end
+ def select_by_names(names)
+ Collection.new(self, all.select { |repo| names.include?(repo.name) })
+ end
- def add_observer(observer)
- all.each { |repo| repo.add_observer(observer) }
+ # def add_observer(observer)
+ # all.each { |repo| repo.add_observer(observer) }
+ # end
end
end
end
View
26 lib/space/models/repos/collection.rb
@@ -0,0 +1,26 @@
+module Space
+ module Models
+ 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/models/repos/scope.rb
@@ -0,0 +1,18 @@
+module Space
+ module Models
+ 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
47 lib/space/models/watcher.rb
@@ -1,47 +0,0 @@
-module Space
- module Watcher
- class << self
- def ignore
- @ignore = true
- yield.tap do
- @ignore= false
- end
- end
-
- def ignore?
- !!@ignore
- end
- end
-
- def initialize(*args)
- super
- watch
- end
-
- private
-
- def watch
- targets.each do |path|
- Watch.new(path, &method(:update)).run
- end
- end
-
- def ignore_updates(*paths, &block)
- Watcher.ignore_updates(*paths, &block)
- end
-
- def update(paths)
- unless Watcher.ignore?
- reset
- changed
- notify_observers
- end
- end
-
- def targets
- Array(self.class::WATCH).map do |path|
- "#{self.path}/#{path}"
- end
- end
- end
-end
View
36 lib/space/screen.rb
@@ -1,35 +1,31 @@
module Space
class Screen
- attr_reader :name, :project, :repos, :view
+ autoload :Progress, 'space/screen/progress'
+ autoload :Dashboard, 'space/screen/dashboard'
+ autoload :View, 'space/screen/view'
- def initialize(name, config, project, repos)
- @name = name
+ attr_reader :project, :current
+
+ def initialize(project)
@project = project
- @repos = repos
- @view = View.new(config.template_dir)
end
- def clear
- print "\e[2J\e[0;0H" # clear screen, move cursor to home
- puts render_project
+ def prompt
+ "#{project.repos.scoped? ? project.repos.scope.map { |r| r.name }.join(', ') : project.name} > "
end
- def render(options = {})
- clear
- repos.scope.self_and_dependencies.each do |repo|
- puts render_repo(repo)
- end
- print options[:prompt].to_s
+ def display(screen)
+ @current = create(screen).tap { |screen| screen.render }
end
- private
+ def notify(event, data)
+ current.notify(event, data)
+ end
- def render_project
- view.render(:project, name: name, project: project)
- end
+ private
- def render_repo(repo)
- view.render(:repo, repos: repos, repo: repo, git: repo.git, bundle: repo.bundle)
+ def create(screen)
+ self.class.const_get(screen.to_s.capitalize).new(project)
end
end
end
View
28 lib/space/screen/dashboard.rb
@@ -0,0 +1,28 @@
+module Space
+ class Screen
+ class Dashboard < View
+ def render(options = {})
+ clear
+ render_header
+ render_repos
+ print options[:prompt].to_s
+ end
+
+ def notify(event, data)
+ render(prompt: prompt)
+ end
+
+ private
+
+ def render_repos
+ project.repos.scope.self_and_deps.each do |repo|
+ render_template(:repo, assigns(repo))
+ end
+ end
+
+ def assigns(repo)
+ { repos: project.repos, repo: repo, git: repo.git, bundle: repo.bundle }
+ end
+ end
+ end
+end
View
14 lib/space/screen/progress.rb
@@ -0,0 +1,14 @@
+module Space
+ class Screen
+ class Progress < View
+ def render
+ clear
+ render_header
+ end
+
+ def notify(event, data)
+ print '.'
+ end
+ end
+ end
+end
View
48 lib/space/screen/view.rb
@@ -0,0 +1,48 @@
+require 'erb'
+
+module Space
+ class Screen
+ class View
+ include Helpers
+
+ attr_reader :project
+
+ def initialize(project)
+ @project = project
+ end
+
+ private
+
+ # TODO duplicate from screen
+ def prompt
+ "#{project.repos.scoped? ? project.repos.scope.map { |r| r.name }.join(', ') : project.name} > "
+ end
+
+ def clear
+ print "\e[2J\e[0;0H" # clear screen, move cursor to home
+ end
+
+ def render_header
+ puts "Project #{project.name}\n\n"
+ end
+
+ def render_template(name, assigns)
+ assigns.each { |name, value| assign(name, value) }
+ print template(name).result(binding)
+ end
+
+ def assign(key, value)
+ instance_variable_set(:"@#{key}", value)
+ (class << self; self; end).send(:attr_reader, key)
+ end
+
+ def templates
+ @templates ||= {}
+ end
+
+ def template(name)
+ templates[name] ||= ERB.new(File.read("#{project.config.template_dir}/#{name}.erb"), nil, '%<>-')
+ end
+ end
+ end
+end
View
56 lib/space/shell.rb
@@ -0,0 +1,56 @@
+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 = nil)
+ paths ? @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, nil)
+ end
+ end
+end
View
60 lib/space/shell/command.rb
@@ -0,0 +1,60 @@
+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
+ notifying do
+ @result = chain.call
+ 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
46 lib/space/shell/watch.rb
@@ -0,0 +1,46 @@
+require 'rb-fsevent'
+
+module Space
+ module Shell
+ class Watch
+ LATENCY = 0
+ NO_DEFER = FALSE
+
+ attr_reader :path, :callback
+
+ def initialize(path, &block)
+ @path = File.expand_path(path)
+ @callback = block
+ self
+ end
+
+ def run
+ Thread.new do
+ watch
+ end
+ end
+
+ private
+
+ def watch
+ fsevent.watch(path, file: file?, latency: LATENCY, no_defer: NO_DEFER) do |paths|
+ fsevent.stop
+ callback.call(paths)
+ # sleep(1)
+ 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
46 lib/space/shell/watcher.rb
@@ -0,0 +1,46 @@
+module Space
+ module Shell
+ module Watcher
+ class << self
+ def ignore
+ @ignore = true
+ yield.tap do
+ @ignore= false
+ end
+ end
+
+ def ignore?
+ !!@ignore
+ end
+ end
+
+ def initialize(*args)
+ watch
+ end
+
+ private
+
+ def watch
+ targets.each do |path|
+ Watch.new(path, &method(:trigger)).run
+ end
+ end
+
+ def ignore_updates(*paths, &block)
+ Watcher.ignore_updates(*paths, &block)
+ end
+
+ def trigger(paths)
+ unless Watcher.ignore?
+ refresh
+ end
+ end
+
+ def targets
+ Array(self.class.watch).map do |path|
+ "#{self.path}/#{path}"
+ end
+ end
+ end
+ end
+end
View
1  lib/space/templates/project.erb → lib/space/templates/header.erb
@@ -1,2 +1 @@
<%= project_title %>
-
View
1  lib/space/tmux.rb
@@ -8,3 +8,4 @@ def windows
end
end
end
+
View
33 lib/space/view.rb
@@ -1,33 +0,0 @@
-require 'erb'
-
-module Space
- class View
- include Helpers
-
- attr_reader :template_dir
-
- def initialize(template_dir)
- @template_dir = template_dir
- end
-
- def render(name, assigns)
- assigns.each { |name, value| assign(name, value) }
- template(name).result(binding)
- end
-
- private
-
- def assign(key, value)
- instance_variable_set(:"@#{key}", value)
- (class << self; self; end).send(:attr_reader, key)
- end
-
- def templates
- @templates ||= {}
- end
-
- def template(name)
- templates[name] ||= ERB.new(File.read("#{template_dir}/#{name}.erb"), nil, '%<>-')
- end
- end
-end
View
45 lib/space/watch.rb
@@ -1,45 +0,0 @@
-require 'rb-fsevent'
-
-module Space
- class Watch
- LATENCY = 0
- NO_DEFER = FALSE
-
- attr_reader :path, :callback
-
- def initialize(path, &block)
- @path = File.expand_path(path)
- @callback = block
- self
- end
-
- def run
- Thread.new do
- watch
- end
- end
-
- private
-
- def watch
- fsevent.watch(path, file: file?, latency: LATENCY, no_defer: NO_DEFER) do |paths|
- fsevent.stop
- callback.call(paths)
- # sleep(1)
- 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
-
View
36 spec/action/parser_spec.rb
@@ -1,36 +0,0 @@
-require 'spec_helper'
-
-describe Action::Parser do
- let(:names) { %w(travis-ci travis-core) }
- let(:parser) { Action::Parser.new(names) }
-
- describe 'parse' do
- it 'returns [["travis-ci"], nil] for "travis-ci"' do
- parser.parse('travis-ci').should == [['travis-ci'], nil]
- end
-
- it 'returns [["travis-ci"], "reload"] for "travis-ci reload"' do
- parser.parse('travis-ci reload').should == [['travis-ci'], 'reload']
- end
-
- it 'returns [["travis-ci", "travis-core"], "reload"] for "travis-ci travis-core reload"' do
- parser.parse('travis-ci travis-core reload').should == [['travis-ci', 'travis-core'], 'reload']
- end
-
- it 'returns [["travis-ci"], nil] for "1"' do
- parser.parse('1').should == [['travis-ci'], nil]
- end
-
- it 'returns [["travis-ci"], "reload"] for "1 reload"' do
- parser.parse('1 reload').should == [['travis-ci'], 'reload']
- end
-
- it 'returns [["travis-ci", "travis-core"], "reload"] for "1 2 reload"' do
- parser.parse('1 2 reload').should == [['travis-ci', 'travis-core'], 'reload']
- end
-
- it 'returns [nil, "reload"] for "reload"' do
- parser.parse('reload').should == [nil, 'reload']
- end
- end
-end
View
8 spec/action_spec.rb
@@ -0,0 +1,8 @@
+#
+# * actions like `bundle update`, `git commit`, `rm file` should
+# display the progress bar and then re-render
+#
+# * actions like `refresh` can work the same way
+#
+# * actions like `scope` can just re-render the dashboard because
+# they don't touch any files
View
28 spec/app/handler_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Space::App::Handler do
+ let(:project) { stub('project') }
+ let(:handler) { Space::App::Handler.new(project) }
+
+ def command_for(command)
+ handler.send(:command_for, nil, command)
+ end
+
+ describe 'command_for' do
+ it 'returns a Command::Refresh instance for an empty command string' do
+ command_for('refresh').should be_a(Space::App::Command::Refresh)
+ end
+
+ it 'returns a Command::Scope instance for an empty command string' do
+ command_for('').should be_a(Space::App::Command::Scope)
+ end
+
+ it 'returns a Command::Unscope instance for "-"' do
+ command_for('-').should be_a(Space::App::Command::Unscope)
+ end
+
+ it 'returns a Command::Execute instance for "ls -al"' do
+ command_for('ls -al').should be_a(Space::App::Command::Execute)
+ end
+ end
+end
View
26 spec/app/parser_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe Space::App::Parser do
+ let(:names) { %w(travis-ci travis-core) }
+ let(:parser) { Space::App::Parser.new(names) }
+
+ describe 'parse' do
+ results = {
+ 'travis-ci' => [['travis-ci'], nil],
+ 'travis-ci travis-core' => [['travis-ci', 'travis-core'], nil],
+ '1' => [['travis-ci'], nil],
+ '1 2' => [['travis-ci', 'travis-core'], nil],
+ 'travis-ci reload' => [['travis-ci'], 'reload'],
+ 'travis-ci travis-core reload' => [['travis-ci', 'travis-core'], 'reload'],
+ '1 reload' => [['travis-ci'], 'reload'],
+ '1 2 reload' => [['travis-ci', 'travis-core'], 'reload'],
+ }
+
+ results.each do |line, result|
+ it "returns #{result.inspect} for #{line.inspect}" do
+ parser.parse(line.dup).should == result
+ end
+ end
+ end
+end
+
View
57 spec/app_spec.rb
@@ -0,0 +1,57 @@
+# encoding: utf-8
+
+require 'spec_helper'
+
+describe Space::App do
+ APP_COMMANDS = {
+ 'bundle config' => "Settings are listed in order of priority. The top value will be used.\n",
+ 'bundle check' => "The Gemfile's dependencies are satisfied",
+ 'bundle list' => " * travis-core (0.0.1 123456)",
+ 'git status' => "nothing to commit (working directory clean)",
+ 'git branch --no-color' => " feature\n* master\n",
+ 'git log -1 --no-color HEAD' => "commit ce8fb952efdd29371ebdc",
+ }
+
+ let(:app) { Space::App.new('travis') }
+ let(:config) { Space::Config.new(base_dir: '~/Development/projects/travis', repositories: %w(travis-ci travis-core)) }
+
+ before :each do
+ Space::Config.stubs(:load).returns(config)
+ stub_commands
+ app.stubs(:prompt)
+ end
+
+ def stub_commands
+ APP_COMMANDS.each do |command, result|
+ Space::Shell::Command.stubs(:execute).with(command).returns(result)
+ end
+ end
+
+ describe 'running the app' do
+ subject { strip_ansi(capture_stdout { app.run }) }
+
+ it 'lists the repository' do
+ should =~ /travis-ci:/
+ end
+
+ it 'displays the current branch' do
+ should =~ /master/
+ end
+
+ it 'displays the current commit hash' do
+ should =~ /ce8fb95/
+ end
+
+ it 'displays the git status' do
+ should =~ /Git: ✔/
+ end
+
+ it 'displays the bundle status' do
+ should =~ /Bundle: ✔/
+ end
+
+ it 'lists the dependencies' do
+ should =~ /• 123456 ⚡ travis-core/
+ end
+ end
+end
View
40 spec/config_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe Space::Config do
+ let(:config) { Space::Config.new }
+
+ describe 'class methods' do
+ describe 'load' do
+ let(:local_filename) { File.expand_path('./.space/travis.yml') }
+ let(:global_filename) { File.expand_path('~/.space/travis.yml') }
+
+ before :each do
+ File.stubs(:exists?).returns(false)
+ File.stubs(:read).with(local_filename).returns('local: true')
+ File.stubs(:read).with(global_filename).returns('global: true')
+ end
+
+ subject { Space::Config.load('travis') }
+
+ it 'loads a local config file if present' do
+ File.stubs(:exists?).with(local_filename).returns(true)
+ subject.local?.should be_true
+ end
+
+ it 'loads globale config file if present' do
+ File.stubs(:exists?).with(global_filename).returns(true)
+ subject.global?.should be_true
+ end
+
+ it 'raises an exception if no config file can be found' do
+ -> { subject }.should raise_error
+ end
+ end
+ end
+
+ describe 'defaults' do
+ it 'template_dir defaults to the expanded path lib/space/templates' do
+ config.template_dir.should == File.expand_path('../../lib/space/templates', __FILE__)
+ end
+ end
+end
View
15 spec/models/command_spec.rb
@@ -1,15 +0,0 @@
-require 'spec_helper'
-
-describe Command do
- describe 'result' do
- it 'changes to the path'
- it 'calls the command'
- it 'removes ansi codes'
- end
-
- describe 'strip_ansi' do
- it 'removes \e[34m'
- it 'removes \e[0m'
- end
-end
-
View
16 spec/models/commands_spec.rb
@@ -1,16 +0,0 @@
-require 'spec_helper'
-
-describe Commands do
- describe 'result' do
- it 'returns the result from the given command'
- end
-
- describe 'commands' do
- it 'returns a hash of commands as defined for this class'
- end
-
- describe 'reset' do
- it 'calls reset on all commands'
- end
-end
-
View
13 spec/models/dependency_spec.rb
@@ -1,13 +0,0 @@
-require 'spec_helper'
-
-describe Dependency do
- describe 'fresh?' do
- it 'returns true if the (locked) dep ref is the actual current repository ref'
- it 'returns false if the (locked) dep ref differs from the actual current repository ref'
- end
-
- describe 'repo' do
- it 'it returns the repository this dependency refers to'
- end
-end
-
View
59 spec/models/git_spec.rb
@@ -1,59 +0,0 @@
-require 'spec_helper'
-
-describe Git do
- let(:git) { Git.new('path/to/somewhere') }
-
- describe 'ahead?' do
- it 'returns true if ahead is greater than 0' do
- git.stubs(:ahead).returns(1)
- git.ahead?.should be_true
- end
-
- it 'returns false if ahead equals 0' do
- git.stubs(:ahead).returns(0)
- git.ahead?.should be_false
- end
- end
-
- describe 'ahead' do
- it "returns 2 if `git status` contains: ahead of '...' by 2 commits." do
- git.stubs(:result).with(:status).returns %(Your branch is ahead of 'origin/master' by 2 commits.\n\nnothing to commit (working directory clean))
- git.ahead.should == 2
- end
-
- it "returns 1 if `git status` contains: ahead of '...' by 1 commit." do
- git.stubs(:result).with(:status).returns %(Your branch is ahead of 'origin/master' by 1 commit.\n\nnothing to commit (working directory clean))
- git.ahead.should == 1
- end
-
- it "returns 0 if `git status` does not contain a line about commits ahead of a remote branch." do
- git.stubs(:result).with(:status).returns %(Your branch is nothing to commit (working directory clean))
- git.ahead.should == 0
- end
- end
-
- describe 'status' do
-
- end
-
- describe 'clean?' do
- it 'returns true if `git status` contains: nothing to commit' do
- git.stubs(:result).with(:status).returns 'nothing to commit (working directory clean)'
- git.clean?.should be_true
- end
-
- it 'returns false if `git status` does not contain: nothing to commit' do
- git.stubs(:result).with(:status).returns 'Changes not staged for commit:'
- git.clean?.should be_false
- end
- end
-
- describe 'branch' do
- it 'returns the current branch name from `git branch --no-color`'
- end
-
- describe 'commit' do
- it 'it returns the commit hash from `git log -1 head`'
- end
-end
-
View
32 spec/models/project/bundler_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe Space::Models::Project::Bundler do
+ BUNDLER_RESULTS = {
+ :config => <<-config
+Settings are listed in order of priority. The top value will be used.
+
+without
+ Set for the current user (/Volumes/Users/sven/.bundle/config): ""
+
+local_override_require_branch
+ Set for the current user (/Volumes/Users/sven/.bundle/config): "false"
+config
+ }
+
+ let(:project) { stub('project') }
+ let(:bundler) { Space::Models::Project::Bundler.new(project) }
+
+ def stub_command(command, result = command)
+ bundler.commands[command].stubs(:result).returns BUNDLER_RESULTS[result].dup
+ end
+
+ describe 'config' do
+ it 'returns the global bundler config as a hash' do
+ stub_command :config
+ bundler.config['local_override_require_branch'].should == 'false'
+ end
+ end
+end
+
+
+
View
46 spec/models/repo/bundle_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe Space::Models::Repo::Bundle do
+ BUNDLE_RESULTS = {
+ check_clean: %(The Gemfile's dependencies are satisfied),
+ check_dirty: %(Your Gemfile's dependencies could not be satisfied),
+ list: %( * travis-ci (0.0.1 123456))
+ }
+
+ let(:repo) { stub('repo', path: '.') }
+ let(:repos) { stub('repos', names: %w(travis-ci), find_by_name: repo) }
+ let(:bundle) { Space::Models::Repo::Bundle.new(repo, repos) }
+
+ def stub_command(command, result = command)
+ bundle.commands[command].stubs(:result).returns BUNDLE_RESULTS[result].dup
+ end
+
+ describe 'clean?' do
+ it 'returns true if info includes "dependencies are satisfied"' do
+ stub_command :check, :check_clean
+ bundle.clean?.should be_true
+ end
+
+ it 'returns false if info does not include "dependencies are satisfied"' do
+ stub_command :check, :check_dirty
+ bundle.clean?.should be_false
+ end
+ end
+
+ describe 'info' do
+ it 'returns the first line from `bundle check`' do
+ stub_command :check, :check_clean
+ bundle.info.should == BUNDLE_RESULTS[:check_clean]
+ end
+ end
+
+ describe 'deps' do
+ it 'returns dependencies listend in `bundle list` that match the app name' do
+ stub_command :list
+ dep = bundle.deps.first
+ [dep.repo, dep.ref].should == [repo, '123456']
+ end
+ end
+end
+
+
View
88 spec/models/repo/git_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe Space::Models::Repo::Git do
+ GIT_RESULTS = {
+ branch: %( feature\n* master\n),
+ commit: %(commit ce8fb952efdd29371ebdc572f8f47e59a9bd5ee5\nAuthor: Sven Fuchs <svenfuchs@artweb-design.de>\nDate: Fri Apr 6 23:17:04 2012 +0200),
+ status_clean: %(nothing to commit (working directory clean)),
+ status_dirty: %(Changes not staged for commit:),
+ ahead_by_2: %(Your branch is ahead of 'origin/master' by 2 commits.\n\nnothing to commit (working directory clean)),
+ ahead_by_1: %(Your branch is ahead of 'origin/master' by 1 commit.\n\nnothing to commit (working directory clean)),
+ ahead_by_0: %(Your branch is nothing to commit (working directory clean))
+ }
+
+ let(:repo) { stub('repo', :path => '.') }
+ let(:git) { Space::Models::Repo::Git.new(repo) }
+
+ def stub_command(command, result)
+ git.commands[command].stubs(:result).returns GIT_RESULTS[result]
+ end
+
+ it 'branch returns the git branch' do
+ stub_command :branch, :branch
+ git.branch.should == 'master'
+ end
+
+ it 'commit returns the git commit' do