Решение на Пета задача от Добромир Иванов

Обратно към всички решения

Към профила на Добромир Иванов

Резултати

  • 4 точки от тестове
  • 0 бонус точки
  • 4 точки общо
  • 18 успешни тест(а)
  • 12 неуспешни тест(а)

Код

require 'digest/sha1'
class RepoObject < Struct.new(:name, :object)
end
class Proxy < BasicObject
attr_reader :message, :result
def initialize(message, success, receiver, result = nil)
@message, @success, @receiver, @result = message, success, receiver, result
end
def success?
@success
end
def error?
not success?
end
def method_missing(name, *args, &block)
@receiver.send(name, *args)
end
def respond_to_missing?(symbol, include_private)
[:head, :get, :add, :remove, :commit, :checkout, :log].insances_methods
end
end
class BranchProxy < BasicObject
def initialize(receiving_repo, receiving_branch)
@receiving_repo, @receiving_branch = receiving_repo, receiving_branch
end
def method_missing(name, *args, &block)
if name == :checkout || name == :remove
@receiving_branch.send(name, *args)
else
@receiving_repo.send(name, *args)
end
end
def respond_to_missing?(symbol, include_private)
[:create, :checkout, :remove, :list].include? symbol
end
end
class Commit
attr_accessor :message, :date, :objects
def initialize(message, objects)
@message = message
@date = Time.new
@objects = objects.dup
end
def hash
Digest::SHA1.hexdigest "#{@date}#{@message}"
end
end
class Branch
attr_reader :name, :commits
def initialize(name, commits, parent_repo)
@name, @commits, @parent_repo = name, commits.dup, parent_repo
@change_count, @head = 0, @commits.last
if @head == nil
@stage = []
else
@stage = @head.objects
end
end
def head
message = "Branch #{@name} does not have any commits yet."
result = false
if @head
message = @head.message
result = true
end
Proxy.new(message, result, self, @head)
end
def get(name)
if @head == nil
message, result = "Object #{name} is not committed.", false
return Proxy.new(message, result, self)
end
wanted_object = @head.objects.detect{|object| object.name == name}
if wanted_object
message, result = "Found object #{name}.", true
Proxy.new(message, result, self, wanted_object.object)
else
message, result = "Object #{name} is not committed.", false
Proxy.new(message, result, self)
end
end
def add(name, object)
@change_count += 1
object_index = index_of_object(name)
if object_index != nil
@stage[object_index] = RepoObject.new(name, object)
else
@stage.push RepoObject.new(name, object)
end
Proxy.new("Added #{name} to stage.", true, @parent_repo, object)
end
def remove(branch_name)
@parent_repo.remove_branch(branch_name)
end
def remove_object(name)
result = @stage.delete_at(index_of_object(name) || @stage.size)
if result
@change_count += 1
message = "Added #{name} for removal."
else
message = "Object #{name} is not committed."
end
Proxy.new(message, !!result, @parent_repo, result)
end
def commit(commit_message)
success = false
if @change_count > 0
message, = "#{commit_message}\n\t#{@change_count} objects changed"
success = true
@head = Commit.new(commit_message, @stage)
@commits.push @head
@stage, @change_count = @head.objects.dup, 0
else
message = "Nothing to commit, working directory clean."
end
Proxy.new(message, success, @parent_repo)
end
def checkout(branch)
@parent_repo.checkout_branch(branch)
end
def checkout_commit(commit_hash)
target_commit = @commits.detect {|commit| commit.hash == commit_hash}
message = "Commit #{commit_hash} does not exist."
if target_commit
@head = target_commit
message = "HEAD is now at #{commit_hash}."
@stage = @head.objects.dup
end
Proxy.new(message, !!target_commit, @parent_repo, target_commit)
end
private
def index_of_object(name)
@stage.find_index {|object| object.name == name}
end
end
class ObjectStore
def ObjectStore.init(&block)
if block
ObjectStore.new.instance_eval(&block)
else
ObjectStore.new
end
end
def initialize
@current_branch = Branch.new("master", [], self)
@branches = [@current_branch]
end
def create(branch_name)
message, result = "Branch #{branch_name} already exists.", false
if not branch_index(branch_name)
message, result = "Created branch #{branch_name}.", true
@branches.push Branch.new(branch_name, @current_branch.commits, self)
end
Proxy.new(message, result, self)
end
def checkout(commit_hash)
@current_branch.checkout_commit(commit_hash)
end
def remove(object_name)
@current_branch.remove_object(object_name)
end
def checkout_branch(branch_name)
target_branch = @branches.at(branch_index(branch_name) || @branches.size)
message, result = "Branch #{branch_name} does not exist.", false
if target_branch
message, result = "Switched to branch #{branch_name}.", true
@current_branch = target_branch
end
Proxy.new(message, result, self)
end
def remove_branch(branch_name)
target_branch = @branches.at(branch_index(branch_name) || @branches.size)
message, success = "Branch #{branch_name} does not exist.", false
if target_branch != nil && target_branch.name != @current_branch.name
message, success = "Removed branch #{branch_name}.", true
@branches.delete_at branch_index(branch_name)
elsif target_branch != nil
message, success = "Cannot remove current branch.", false
end
Proxy.new(message, success, self)
end
def list
message = @branches.map(&:name).sort.map {|name| " " + name + "\n"}.join
pattern = " " + @current_branch.name
message[pattern] = "* " + message[pattern].strip
Proxy.new(message, true, self)
end
def branch
BranchProxy.new(self, @current_branch)
end
def log
if @current_branch.commits.empty?
message = "Branch #{@current_branch.name} does not have any commits yet."
Proxy.new(message, false, @current_branch)
else
format = "Commit %{hash}\nDate: %{date}\n\n\t%{message}"
message = @current_branch.commits.reverse.map do |commit|
date = commit.date.strftime("%a %b %d %H:%M %Y %z")
format % {hash: commit.hash, date: date, message: commit.message}
end
Proxy.new(message.join("\n\n"), true, @current_branch)
end
end
def method_missing(name, *args, &block)
@current_branch.send(name, *args)
end
def respond_to_missing?(symbol, include_private)
[:name, :head, :get, :add, :commit].include? symbol
end
private
def branch_index(name)
@branches.find_index {|branch| branch.name == name}
end
end

Лог от изпълнението

.F.FF...FFFF.F.......F...F..FF

Failures:

  1) ObjectStore can commit objects
     Failure/Error: expect(repo.commit("So cool!")).to be_success("So cool!\n\t2 objects changed", repo.head.result)
       expected #<ObjectStore:0x007f6c988ce1a8 @current_branch=#<Branch:0x007f6c988ce0e0 @parent_repo=#<ObjectStore:0x007f6c988ce1a8 ...>, @commits=[#<Commit:0x007f6c988cd960 @message="So cool!", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>], @name="master", @head=#<Commit:0x007f6c988cd960 @message="So cool!", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>, @change_count=0, @stage=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>, @branches=[#<Branch:0x007f6c988ce0e0 @parent_repo=#<ObjectStore:0x007f6c988ce1a8 ...>, @commits=[#<Commit:0x007f6c988cd960 @message="So cool!", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>], @name="master", @head=#<Commit:0x007f6c988cd960 @message="So cool!", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>, @change_count=0, @stage=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>]> to be success "So cool!\n\t2 objects changed" and #<Commit:0x007f6c988cd960 @message="So cool!", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>
     # /tmp/d20160111-5693-242n79/spec.rb:30:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  2) ObjectStore can remove objects
     Failure/Error: expect(repo.remove("object1")).to be_success("Added object1 for removal.", "content1")
       expected #<ObjectStore:0x007f6c988b9578 @current_branch=#<Branch:0x007f6c988ba860 @parent_repo=#<ObjectStore:0x007f6c988b9578 ...>, @commits=[#<Commit:0x007f6c988b3178 @message="So cool!", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>], @name="master", @head=#<Commit:0x007f6c988b3178 @message="So cool!", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>, @change_count=1, @stage=[#<struct RepoObject name="object2", object="content2">]>, @branches=[#<Branch:0x007f6c988ba860 @parent_repo=#<ObjectStore:0x007f6c988b9578 ...>, @commits=[#<Commit:0x007f6c988b3178 @message="So cool!", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>], @name="master", @head=#<Commit:0x007f6c988b3178 @message="So cool!", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>, @change_count=1, @stage=[#<struct RepoObject name="object2", object="content2">]>]> to be success "Added object1 for removal." and "content1"
     # /tmp/d20160111-5693-242n79/spec.rb:43:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  3) ObjectStore can commit changes which include only removed objects
     Failure/Error: expect(repo.commit("Removed object2")).to be_success("Removed object2\n\t1 objects changed", repo.head.result)
       expected #<ObjectStore:0x007f6c98898f80 @current_branch=#<Branch:0x007f6c9889a1f0 @parent_repo=#<ObjectStore:0x007f6c98898f80 ...>, @commits=[#<Commit:0x007f6c98896f28 @message="So cool!", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>, #<Commit:0x007f6c98896bb8 @message="Removed object2", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">]>], @name="master", @head=#<Commit:0x007f6c98896bb8 @message="Removed object2", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">]>, @change_count=0, @stage=[#<struct RepoObject name="object1", object="content1">]>, @branches=[#<Branch:0x007f6c9889a1f0 @parent_repo=#<ObjectStore:0x007f6c98898f80 ...>, @commits=[#<Commit:0x007f6c98896f28 @message="So cool!", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">, #<struct RepoObject name="object2", object="content2">]>, #<Commit:0x007f6c98896bb8 @message="Removed object2", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">]>], @name="master", @head=#<Commit:0x007f6c98896bb8 @message="Removed object2", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">]>, @change_count=0, @stage=[#<struct RepoObject name="object1", object="content1">]>]> to be success "Removed object2\n\t1 objects changed" and #<Commit:0x007f6c98896bb8 @message="Removed object2", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="object1", object="content1">]>
     # /tmp/d20160111-5693-242n79/spec.rb:53:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  4) ObjectStore can show log of changes for a single commit
     Failure/Error: commit_hash = Digest::SHA1.hexdigest("#{commit.date.strftime("%a %b %d %H:%M %Y %z")}#{commit.message}")
     NoMethodError:
       undefined method `date' for nil:NilClass
     # /tmp/d20160111-5693-242n79/spec.rb:82:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  5) ObjectStore can show log of changes for a single commit
     Failure/Error: commit_hash = Digest::SHA1.hexdigest("#{commit.date.strftime("%a %b %d %H:%M %Y %z")}#{commit.message}")
     NoMethodError:
       undefined method `date' for nil:NilClass
     # /tmp/d20160111-5693-242n79/spec.rb:91:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  6) ObjectStore can show log of changes for multiple commits
     Failure/Error: commit1_hash = Digest::SHA1.hexdigest("#{commit1.date.strftime(time_format)}#{commit1.message}")
     NoMethodError:
       undefined method `date' for nil:NilClass
     # /tmp/d20160111-5693-242n79/spec.rb:105:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  7) ObjectStore shows the log for the current branch only
     Failure/Error: commit1_hash = Digest::SHA1.hexdigest("#{commit1.date.strftime(time_format)}#{commit1.message}")
     NoMethodError:
       undefined method `date' for nil:NilClass
     # /tmp/d20160111-5693-242n79/spec.rb:126:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  8) ObjectStore can list branches
     Failure/Error: expect(repo.branch.list).to be_success("  develop\n  feature\n* master")
       expected #<ObjectStore:0x007f6c98827038 @current_branch=#<Branch:0x007f6c98826fc0 @parent_repo=#<ObjectStore:0x007f6c98827038 ...>, @commits=[], @name="master", @head=nil, @change_count=0, @stage=[]>, @branches=[#<Branch:0x007f6c98826fc0 @parent_repo=#<ObjectStore:0x007f6c98827038 ...>, @commits=[], @name="master", @head=nil, @change_count=0, @stage=[]>, #<Branch:0x007f6c98826de0 @parent_repo=#<ObjectStore:0x007f6c98827038 ...>, @commits=[], @name="develop", @head=nil, @change_count=0, @stage=[]>, #<Branch:0x007f6c98826bb0 @parent_repo=#<ObjectStore:0x007f6c98827038 ...>, @commits=[], @name="feature", @head=nil, @change_count=0, @stage=[]>]> to be success "  develop\n  feature\n* master"
     # /tmp/d20160111-5693-242n79/spec.rb:140:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  9) ObjectStore can be initialized with block
     Failure/Error: expect(repo.head).to be_success("Second commit", $second_commit)
     NoMethodError:
       undefined method `head' for nil:NilClass
     # /tmp/d20160111-5693-242n79/spec.rb:195:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  10) ObjectStore can checkout commits
     Failure/Error: expect(repo.checkout(first_commit.hash)).to be_success("HEAD is now at #{first_commit.hash}.", first_commit)
       expected #<ObjectStore:0x007f6c9833a200 @current_branch=#<Branch:0x007f6c9833a070 @parent_repo=#<ObjectStore:0x007f6c9833a200 ...>, @commits=[#<Commit:0x007f6c98339440 @message="First commit", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="number", object=42>]>, #<Commit:0x007f6c983388d8 @message="Second commit", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="number", object=21>]>], @name="master", @head=#<Commit:0x007f6c983388d8 @message="Second commit", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="number", object=21>]>, @change_count=0, @stage=[#<struct RepoObject name="number", object=21>]>, @branches=[#<Branch:0x007f6c9833a070 @parent_repo=#<ObjectStore:0x007f6c9833a200 ...>, @commits=[#<Commit:0x007f6c98339440 @message="First commit", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="number", object=42>]>, #<Commit:0x007f6c983388d8 @message="Second commit", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="number", object=21>]>], @name="master", @head=#<Commit:0x007f6c983388d8 @message="Second commit", @date=2016-01-11 11:54:22 +0200, @objects=[#<struct RepoObject name="number", object=21>]>, @change_count=0, @stage=[#<struct RepoObject name="number", object=21>]>]> to be success "HEAD is now at 2115300952265339016." and nil
     # /tmp/d20160111-5693-242n79/spec.rb:223:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  11) ObjectStore can show the objects in a repo after overwriting an object
     Failure/Error: expect(first_commit.objects).to match_array(["content1"])
     NoMethodError:
       undefined method `objects' for nil:NilClass
     # /tmp/d20160111-5693-242n79/spec.rb:243:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  12) ObjectStore can show the objects of a repo after removing an object
     Failure/Error: expect(first_commit.objects).to match_array(["content1", "content2", "content3"])
     NoMethodError:
       undefined method `objects' for nil:NilClass
     # /tmp/d20160111-5693-242n79/spec.rb:259:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.02837 seconds
30 examples, 12 failures

Failed examples:

rspec /tmp/d20160111-5693-242n79/spec.rb:26 # ObjectStore can commit objects
rspec /tmp/d20160111-5693-242n79/spec.rb:38 # ObjectStore can remove objects
rspec /tmp/d20160111-5693-242n79/spec.rb:46 # ObjectStore can commit changes which include only removed objects
rspec /tmp/d20160111-5693-242n79/spec.rb:77 # ObjectStore can show log of changes for a single commit
rspec /tmp/d20160111-5693-242n79/spec.rb:86 # ObjectStore can show log of changes for a single commit
rspec /tmp/d20160111-5693-242n79/spec.rb:95 # ObjectStore can show log of changes for multiple commits
rspec /tmp/d20160111-5693-242n79/spec.rb:111 # ObjectStore shows the log for the current branch only
rspec /tmp/d20160111-5693-242n79/spec.rb:136 # ObjectStore can list branches
rspec /tmp/d20160111-5693-242n79/spec.rb:187 # ObjectStore can be initialized with block
rspec /tmp/d20160111-5693-242n79/spec.rb:217 # ObjectStore can checkout commits
rspec /tmp/d20160111-5693-242n79/spec.rb:239 # ObjectStore can show the objects in a repo after overwriting an object
rspec /tmp/d20160111-5693-242n79/spec.rb:253 # ObjectStore can show the objects of a repo after removing an object

История (3 версии и 0 коментара)

Добромир обнови решението на 22.11.2015 10:18 (преди над 8 години)

+require 'digest/sha1'
+
+class RepoObject < Struct.new(:name, :object)
+end
+
+class Proxy < BasicObject
+ attr_reader :message, :result
+
+ def initialize(message, success, receiver, result = nil)
+ @message, @success, @receiver, @result = message, success, receiver, result
+ end
+
+ def success?
+ @success
+ end
+
+ def error?
+ not success?
+ end
+
+ def method_missing(name, *args, &block)
+ @receiver.send(name, *args)
+ end
+end
+
+class Commit
+ attr_accessor :message, :date, :objects
+
+ def initialize(message, objects)
+ @message = message
+ @date = Time.new.strftime("%a %b %d %H:%M %Y %z")
+ @objects = objects.dup
+ end
+
+ def hash
+ Digest::SHA1.hexdigest "#{@date}#{@message}"
+ end
+end
+
+class Branch
+ attr_reader :name
+
+ def initialize(name, commits, parent_repo)
+ @name, @commits, @parent_repo = name, commits, parent_repo
+ @stage, @change_count = @commits.map(&:objects).flatten, 0
+ @head = @commits.last
+ end
+
+ def head
+ message = "Branch #{@name} does not have any commits yet."
+ result = false
+ if @head
+ message = @head.message
+ result = true
+ end
+ Proxy.new(message, result, self, @head)
+ end
+
+ def get(name)
+ recent_changes = changes_upto_commit(@head.hash).reverse
+ wanted_object = recent_changes.detect{|object| object.name == name}
+ message, result = "Object #{name} is not committed.", false
+ if wanted_object
+ message, result = "Found object #{name}.", true
+ end
+ Proxy.new(message, result, self, wanted_object.object)
+ end
+
+ def add(name, object)
+ @change_count += 1
+ @stage.push RepoObject.new(name, object)
+ Proxy.new("Added #{name} to stage.", true, @parent_repo, object)
+ end
+
+ def remove(name)
+ result = remove_from_stage(name)
+ if result
+ @change_count += 1
+ message = "Added #{name} for removal."
+ else
+ message = "Object #{name} is not committed."
+ end
+ Proxy.new(message, !!result, @parent_repo, result)
+ end
+
+ def commit(message)
+ success = false
+ if @change_count > 0
+ message, success = "#{message}\n\t#{@change_count} objects changed", true
+ @head = Commit.new(message, @stage)
+ @commits.push @head
+ @stage, @change_count = changes_upto_commit(@head.hash), 0
+ else
+ message = "Nothing to commit, working directory clean."
+ end
+ Proxy.new(message, success, @parent_repo)
+ end
+
+ def checkout(commit_hash)
+ target_commit = @commit.detect {|commit| commit.hash == hash}
+ message = "Commit #{hash} does not exist."
+ if target_commit
+ @head = target_commit
+ message = "HEAD is now at #{hash}."
+ @stage = changes_upto_commit(target_commit.hash)
+ end
+ Proxy.new(message, !!target_commit, @parent_repo, target_commit)
+ end
+
+ private
+
+ def changes_upto_commit(commit_hash)
+ commit_index = @commits.find_index {|commit| commit.hash == commit_hash}
+ @commits[0..commit_index].map(&:objects).flatten
+ end
+
+ def index_of_object(name)
+ @stage.reverse.find_index {|object| object.name == name}
+ end
+
+ def remove_from_stage(name)
+ result = @stage.reverse!.delete_at(index_of_bject(name) || @stage.size)
+ @stage.reverse!
+ result
+ end
+end
+
+class ObjectStore
+ def ObjectStore.init
+ if block_given?
+ ObjectStore.new.instance_eval(yield)
+ else
+ ObjectStore.new
+ end
+ end
+
+ def initialize
+ @current_branch = Branch.new("master", [], self)
+ @branches = [@current_branch]
+ end
+
+ def create(branch_name)
+ message, result = "Branch #{name} already exists.", false
+ if branch_index(name)
+ message, result = "Created branch #{name}.", true
+ end
+ Proxy.new(message, result, self)
+ end
+
+ def checkout(branch_name)
+ target_branch = @branches.at(branch_index(branch_name) || @branches.size)
+ message, result = "Branch #{name} does not exist.", false
+ if target_branch
+ message, result = "Switched to branch #{name}.", true
+ @current_branch = target_branch
+ end
+ Proxy.new(message, result, self)
+ end
+
+ def remove(branch_name)
+ target_branch = @branches.at(branch_index(branch_name) || @branches.size)
+ message, success = "Branch #{name} does not exist.", false
+ if target_branch != nil && target_branch != current_branch
+ message, success = "Removed branch #{name}.", true
+ elsif target_branch != nil
+ message, success = "Cannot remove current branch.", false
+ end
+ Proxy.new(message, success, self)
+ end
+
+ def list
+ message = @branches.map(&:name).sort.map {|name| " " + name + "\n"}.join
+ pattern = " " + @current_branch.name
+ message[pattern] = "* " + message[pattern].strip
+ Proxy.new(message, true, self)
+ end
+
+ def branch
+ self
+ end
+
+ def method_missing(name, *args, &block)
+ @current_branch.send(name, *args)
+ end
+
+ private
+
+ def branch_index(name)
+ @branches.find_index {|branch| branch.name == name}
+ end
+end

Добромир обнови решението на 22.11.2015 16:51 (преди над 8 години)

require 'digest/sha1'
class RepoObject < Struct.new(:name, :object)
end
class Proxy < BasicObject
attr_reader :message, :result
+ RESPONDS_TO = []
def initialize(message, success, receiver, result = nil)
@message, @success, @receiver, @result = message, success, receiver, result
end
def success?
@success
end
def error?
not success?
end
def method_missing(name, *args, &block)
@receiver.send(name, *args)
end
+
+ def respond_to_missing?(symbol, include_private)
+ [:head, :get, :add, :remove, :commit, :checkout, :log].insances_methods
+ end
end
+class BranchProxy < BasicObject
+ def initialize(receiving_repo, receiving_branch)
+ @receiving_repo, @receiving_branch = receiving_repo, receiving_branch
+ end
+
+ def method_missing(name, *args, &block)
+ if name == :checkout || name == :remove
+ @receiving_branch.send(name, *args)
+ else
+ @receiving_repo.send(name, *args)
+ end
+ end
+
+ def respond_to_missing?(symbol, include_private)
+ [:create, :checkout, :remove, :list].include? symbol
+ end
+end
+
class Commit
attr_accessor :message, :date, :objects
def initialize(message, objects)
@message = message
- @date = Time.new.strftime("%a %b %d %H:%M %Y %z")
+ @date = Time.new#.strftime("%a %b %d %H:%M %Y %z")
@objects = objects.dup
end
def hash
Digest::SHA1.hexdigest "#{@date}#{@message}"
end
end
class Branch
- attr_reader :name
+ attr_reader :name, :commits
def initialize(name, commits, parent_repo)
- @name, @commits, @parent_repo = name, commits, parent_repo
- @stage, @change_count = @commits.map(&:objects).flatten, 0
- @head = @commits.last
+ @name, @commits, @parent_repo = name, commits.dup, parent_repo
+ @change_count, @head = 0, @commits.last
+ if @head == nil
+ @stage = []
+ else
+ @stage = @head.objects
+ end
end
def head
message = "Branch #{@name} does not have any commits yet."
result = false
if @head
message = @head.message
result = true
end
Proxy.new(message, result, self, @head)
end
def get(name)
- recent_changes = changes_upto_commit(@head.hash).reverse
- wanted_object = recent_changes.detect{|object| object.name == name}
- message, result = "Object #{name} is not committed.", false
+ if @head == nil
+ message, result = "Object #{name} is not committed.", false
+ return Proxy.new(message, result, self)
+ end
+
+ wanted_object = @head.objects.detect{|object| object.name == name}
if wanted_object
message, result = "Found object #{name}.", true
+ Proxy.new(message, result, self, wanted_object.object)
+ else
+ message, result = "Object #{name} is not committed.", false
+ Proxy.new(message, result, self)
end
- Proxy.new(message, result, self, wanted_object.object)
end
def add(name, object)
@change_count += 1
- @stage.push RepoObject.new(name, object)
+ object_index = index_of_object(name)
+ if object_index != nil
+ @stage[object_index] = RepoObject.new(name, object)
+ else
+ @stage.push RepoObject.new(name, object)
+ end
Proxy.new("Added #{name} to stage.", true, @parent_repo, object)
end
- def remove(name)
- result = remove_from_stage(name)
+ def remove(branch_name)
+ @parent_repo.remove_branch(branch_name)
+ end
+
+ def remove_object(name)
+ result = @stage.delete_at(index_of_object(name) || @stage.size)
if result
@change_count += 1
message = "Added #{name} for removal."
else
message = "Object #{name} is not committed."
end
Proxy.new(message, !!result, @parent_repo, result)
end
- def commit(message)
+ def commit(commit_message)
success = false
if @change_count > 0
- message, success = "#{message}\n\t#{@change_count} objects changed", true
- @head = Commit.new(message, @stage)
+ message, = "#{commit_message}\n\t#{@change_count} objects changed"
+ success = true
+ @head = Commit.new(commit_message, @stage)
@commits.push @head
- @stage, @change_count = changes_upto_commit(@head.hash), 0
+ @stage, @change_count = @head.objects.dup, 0
else
message = "Nothing to commit, working directory clean."
end
Proxy.new(message, success, @parent_repo)
end
- def checkout(commit_hash)
- target_commit = @commit.detect {|commit| commit.hash == hash}
- message = "Commit #{hash} does not exist."
+ def checkout(branch)
+ @parent_repo.checkout_branch(branch)
+ end
+
+ def checkout_commit(commit_hash)
+ target_commit = @commits.detect {|commit| commit.hash == commit_hash}
+ message = "Commit #{commit_hash} does not exist."
if target_commit
@head = target_commit
- message = "HEAD is now at #{hash}."
- @stage = changes_upto_commit(target_commit.hash)
+ message = "HEAD is now at #{commit_hash}."
+ @stage = @head.objects.dup
end
Proxy.new(message, !!target_commit, @parent_repo, target_commit)
end
private
-
- def changes_upto_commit(commit_hash)
- commit_index = @commits.find_index {|commit| commit.hash == commit_hash}
- @commits[0..commit_index].map(&:objects).flatten
- end
-
def index_of_object(name)
- @stage.reverse.find_index {|object| object.name == name}
+ @stage.find_index {|object| object.name == name}
end
-
- def remove_from_stage(name)
- result = @stage.reverse!.delete_at(index_of_bject(name) || @stage.size)
- @stage.reverse!
- result
- end
end
class ObjectStore
def ObjectStore.init
if block_given?
ObjectStore.new.instance_eval(yield)
else
ObjectStore.new
end
end
def initialize
@current_branch = Branch.new("master", [], self)
@branches = [@current_branch]
end
def create(branch_name)
- message, result = "Branch #{name} already exists.", false
- if branch_index(name)
- message, result = "Created branch #{name}.", true
+ message, result = "Branch #{branch_name} already exists.", false
+ if not branch_index(branch_name)
+ message, result = "Created branch #{branch_name}.", true
+ @branches.push Branch.new(branch_name, @current_branch.commits, self)
end
Proxy.new(message, result, self)
end
- def checkout(branch_name)
+ def checkout(commit_hash)
+ @current_branch.checkout_commit(commit_hash)
+ end
+
+ def remove(object_name)
+ @current_branch.remove_object(object_name)
+ end
+
+ def checkout_branch(branch_name)
target_branch = @branches.at(branch_index(branch_name) || @branches.size)
- message, result = "Branch #{name} does not exist.", false
+ message, result = "Branch #{branch_name} does not exist.", false
if target_branch
- message, result = "Switched to branch #{name}.", true
+ message, result = "Switched to branch #{branch_name}.", true
@current_branch = target_branch
end
Proxy.new(message, result, self)
end
- def remove(branch_name)
+ def remove_branch(branch_name)
target_branch = @branches.at(branch_index(branch_name) || @branches.size)
- message, success = "Branch #{name} does not exist.", false
- if target_branch != nil && target_branch != current_branch
- message, success = "Removed branch #{name}.", true
+ message, success = "Branch #{branch_name} does not exist.", false
+ if target_branch != nil && target_branch.name != @current_branch.name
+ message, success = "Removed branch #{branch_name}.", true
+ @branches.delete_at branch_index(branch_name)
elsif target_branch != nil
message, success = "Cannot remove current branch.", false
end
Proxy.new(message, success, self)
end
def list
message = @branches.map(&:name).sort.map {|name| " " + name + "\n"}.join
pattern = " " + @current_branch.name
message[pattern] = "* " + message[pattern].strip
Proxy.new(message, true, self)
end
def branch
- self
+ BranchProxy.new(self, @current_branch)
end
def method_missing(name, *args, &block)
@current_branch.send(name, *args)
+ end
+
+ def respond_to_missing?(symbol, include_private)
+ [:name, :head, :get, :add, :commit].include? symbol
end
private
def branch_index(name)
@branches.find_index {|branch| branch.name == name}
end
end

Добромир обнови решението на 22.11.2015 17:36 (преди над 8 години)

require 'digest/sha1'
class RepoObject < Struct.new(:name, :object)
end
class Proxy < BasicObject
attr_reader :message, :result
- RESPONDS_TO = []
def initialize(message, success, receiver, result = nil)
@message, @success, @receiver, @result = message, success, receiver, result
end
def success?
@success
end
def error?
not success?
end
def method_missing(name, *args, &block)
@receiver.send(name, *args)
end
def respond_to_missing?(symbol, include_private)
[:head, :get, :add, :remove, :commit, :checkout, :log].insances_methods
end
end
class BranchProxy < BasicObject
def initialize(receiving_repo, receiving_branch)
@receiving_repo, @receiving_branch = receiving_repo, receiving_branch
end
def method_missing(name, *args, &block)
if name == :checkout || name == :remove
@receiving_branch.send(name, *args)
else
@receiving_repo.send(name, *args)
end
end
def respond_to_missing?(symbol, include_private)
[:create, :checkout, :remove, :list].include? symbol
end
end
class Commit
attr_accessor :message, :date, :objects
def initialize(message, objects)
@message = message
- @date = Time.new#.strftime("%a %b %d %H:%M %Y %z")
+ @date = Time.new
@objects = objects.dup
end
def hash
Digest::SHA1.hexdigest "#{@date}#{@message}"
end
end
class Branch
attr_reader :name, :commits
def initialize(name, commits, parent_repo)
@name, @commits, @parent_repo = name, commits.dup, parent_repo
@change_count, @head = 0, @commits.last
if @head == nil
@stage = []
else
@stage = @head.objects
end
end
def head
message = "Branch #{@name} does not have any commits yet."
result = false
if @head
message = @head.message
result = true
end
Proxy.new(message, result, self, @head)
end
def get(name)
if @head == nil
message, result = "Object #{name} is not committed.", false
return Proxy.new(message, result, self)
end
wanted_object = @head.objects.detect{|object| object.name == name}
if wanted_object
message, result = "Found object #{name}.", true
Proxy.new(message, result, self, wanted_object.object)
else
message, result = "Object #{name} is not committed.", false
Proxy.new(message, result, self)
end
end
def add(name, object)
@change_count += 1
object_index = index_of_object(name)
if object_index != nil
@stage[object_index] = RepoObject.new(name, object)
else
@stage.push RepoObject.new(name, object)
end
Proxy.new("Added #{name} to stage.", true, @parent_repo, object)
end
def remove(branch_name)
@parent_repo.remove_branch(branch_name)
end
def remove_object(name)
result = @stage.delete_at(index_of_object(name) || @stage.size)
if result
@change_count += 1
message = "Added #{name} for removal."
else
message = "Object #{name} is not committed."
end
Proxy.new(message, !!result, @parent_repo, result)
end
def commit(commit_message)
success = false
if @change_count > 0
message, = "#{commit_message}\n\t#{@change_count} objects changed"
success = true
@head = Commit.new(commit_message, @stage)
@commits.push @head
@stage, @change_count = @head.objects.dup, 0
else
message = "Nothing to commit, working directory clean."
end
Proxy.new(message, success, @parent_repo)
end
def checkout(branch)
@parent_repo.checkout_branch(branch)
end
def checkout_commit(commit_hash)
target_commit = @commits.detect {|commit| commit.hash == commit_hash}
message = "Commit #{commit_hash} does not exist."
if target_commit
@head = target_commit
message = "HEAD is now at #{commit_hash}."
@stage = @head.objects.dup
end
Proxy.new(message, !!target_commit, @parent_repo, target_commit)
end
private
def index_of_object(name)
@stage.find_index {|object| object.name == name}
end
end
class ObjectStore
- def ObjectStore.init
- if block_given?
- ObjectStore.new.instance_eval(yield)
+ def ObjectStore.init(&block)
+ if block
+ ObjectStore.new.instance_eval(&block)
else
ObjectStore.new
end
end
def initialize
@current_branch = Branch.new("master", [], self)
@branches = [@current_branch]
end
def create(branch_name)
message, result = "Branch #{branch_name} already exists.", false
if not branch_index(branch_name)
message, result = "Created branch #{branch_name}.", true
@branches.push Branch.new(branch_name, @current_branch.commits, self)
end
Proxy.new(message, result, self)
end
def checkout(commit_hash)
@current_branch.checkout_commit(commit_hash)
end
def remove(object_name)
@current_branch.remove_object(object_name)
end
def checkout_branch(branch_name)
target_branch = @branches.at(branch_index(branch_name) || @branches.size)
message, result = "Branch #{branch_name} does not exist.", false
if target_branch
message, result = "Switched to branch #{branch_name}.", true
@current_branch = target_branch
end
Proxy.new(message, result, self)
end
def remove_branch(branch_name)
target_branch = @branches.at(branch_index(branch_name) || @branches.size)
message, success = "Branch #{branch_name} does not exist.", false
if target_branch != nil && target_branch.name != @current_branch.name
message, success = "Removed branch #{branch_name}.", true
@branches.delete_at branch_index(branch_name)
elsif target_branch != nil
message, success = "Cannot remove current branch.", false
end
Proxy.new(message, success, self)
end
def list
message = @branches.map(&:name).sort.map {|name| " " + name + "\n"}.join
pattern = " " + @current_branch.name
message[pattern] = "* " + message[pattern].strip
Proxy.new(message, true, self)
end
def branch
BranchProxy.new(self, @current_branch)
+ end
+
+ def log
+ if @current_branch.commits.empty?
+ message = "Branch #{@current_branch.name} does not have any commits yet."
+ Proxy.new(message, false, @current_branch)
+ else
+ format = "Commit %{hash}\nDate: %{date}\n\n\t%{message}"
+ message = @current_branch.commits.reverse.map do |commit|
+ date = commit.date.strftime("%a %b %d %H:%M %Y %z")
+ format % {hash: commit.hash, date: date, message: commit.message}
+ end
+ Proxy.new(message.join("\n\n"), true, @current_branch)
+ end
end
def method_missing(name, *args, &block)
@current_branch.send(name, *args)
end
def respond_to_missing?(symbol, include_private)
[:name, :head, :get, :add, :commit].include? symbol
end
private
def branch_index(name)
@branches.find_index {|branch| branch.name == name}
end
end