grudge 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.txt +2 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +108 -0
- data/Rakefile +18 -0
- data/TODO.markdown +27 -0
- data/bin/grudge +55 -0
- data/config.ru +13 -0
- data/config/grudge.yml +31 -0
- data/grudge.gemspec +64 -0
- data/lib/boot.rb +8 -0
- data/lib/grudge.rb +73 -0
- data/lib/grudge/base.rb +16 -0
- data/lib/grudge/config.rb +23 -0
- data/lib/grudge/database.rb +15 -0
- data/lib/grudge/extensions.rb +18 -0
- data/lib/grudge/helpers.rb +28 -0
- data/lib/grudge/repository.rb +47 -0
- data/lib/models/commit.rb +55 -0
- data/lib/models/vote.rb +16 -0
- data/lib/views/application.haml +26 -0
- data/lib/views/application.sass +110 -0
- data/lib/views/index.haml +28 -0
- data/lib/views/not_found.haml +1 -0
- data/lib/views/show.haml +25 -0
- data/test/commit_test.rb +226 -0
- data/test/config_test.rb +18 -0
- data/test/grudge_test.rb +193 -0
- data/test/model_factory.rb +30 -0
- data/test/repository_test.rb +118 -0
- data/test/test_helper.rb +46 -0
- data/test/vote_test.rb +65 -0
- metadata +165 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Grudge
|
4
|
+
module Config
|
5
|
+
class ConfigFile
|
6
|
+
def initialize(config_file, env=:development)
|
7
|
+
@env_config = OpenStruct.new(YAML.load(config_file)[env.to_s])
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(meth, *args, &blk)
|
11
|
+
@env_config.send(meth)
|
12
|
+
end
|
13
|
+
end # ConfigFile
|
14
|
+
|
15
|
+
def self.fire_me_up(env)
|
16
|
+
@config = ConfigFile.new(File.new('config/grudge.yml'), env)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.method_missing(meth, *args, &blk)
|
20
|
+
@config.send(meth)
|
21
|
+
end
|
22
|
+
end # Config
|
23
|
+
end # Grudge
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'data_mapper'
|
2
|
+
require 'data_objects'
|
3
|
+
require 'dm-validations'
|
4
|
+
|
5
|
+
module Grudge
|
6
|
+
module Database
|
7
|
+
def self.fire_me_up
|
8
|
+
puts "Connecting to database and migrating"
|
9
|
+
db_config = Grudge::Config.database
|
10
|
+
db_config = db_config.symbolize_keys if db_config.kind_of?(Hash)
|
11
|
+
DataMapper.setup(:default, db_config)
|
12
|
+
DataMapper.auto_upgrade!
|
13
|
+
end
|
14
|
+
end # Database
|
15
|
+
end # Grudge
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Grudge
|
2
|
+
module Sinatra
|
3
|
+
module Extensions
|
4
|
+
def catch_all_css
|
5
|
+
get('/stylesheets/*.css') {sass params["splat"].first.to_sym}
|
6
|
+
end
|
7
|
+
|
8
|
+
# When you don't want to do anything special, but load
|
9
|
+
def get_obvious(name)
|
10
|
+
get "/#{name}" do
|
11
|
+
title = name.to_s
|
12
|
+
haml name.to_sym
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end # Extensions
|
17
|
+
end # Sinatra
|
18
|
+
end # Grudge
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Grudge
|
2
|
+
module Sinatra
|
3
|
+
module Helpers
|
4
|
+
def set_title(msg) @title = msg; end
|
5
|
+
def title; "Grudge - #{@title}"; end
|
6
|
+
|
7
|
+
def anchor(name, url, options={})
|
8
|
+
defaults = {:href => url}
|
9
|
+
options_str = hash_to_attributes(defaults.merge(options))
|
10
|
+
%Q[<a #{options_str}>#{name}</a>]
|
11
|
+
end
|
12
|
+
|
13
|
+
def stylesheet_include(name, options={})
|
14
|
+
defaults = {:href => "/stylesheets/#{name}.css", :media => "screen",
|
15
|
+
:rel => "stylesheet", :type => "text/css"}
|
16
|
+
options_str = hash_to_attributes(defaults.merge(options))
|
17
|
+
%Q[<link #{options_str}/>]
|
18
|
+
end
|
19
|
+
|
20
|
+
def repository_name; Grudge::Repository.origin_uri.to_s; end
|
21
|
+
|
22
|
+
private
|
23
|
+
def hash_to_attributes(options)
|
24
|
+
options.map {|k,v| "#{k}=\"#{v}\""}.join(' ')
|
25
|
+
end
|
26
|
+
end # Helpers
|
27
|
+
end # Sinatra
|
28
|
+
end # Grudge
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'git'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Grudge
|
5
|
+
module Repository
|
6
|
+
|
7
|
+
def self.origin_uri
|
8
|
+
URI.parse(Grudge::Config.repo["origin"])
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.master_path; Grudge::Config.repo["master"]; end
|
12
|
+
|
13
|
+
def self.open
|
14
|
+
@repo ||= repo(:open, master_path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.open_or_clone
|
18
|
+
return open if File.exists?(master_path)
|
19
|
+
repo(:clone, origin_uri, master_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.pull
|
23
|
+
open.pull('origin', 'origin/master', 'grudge origin pull')
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.object(sha)
|
27
|
+
open.object(sha)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.latest_commits_since(since=nil, &block)
|
31
|
+
log_opts = {}
|
32
|
+
log_opts[:between] = [since, ''] if since
|
33
|
+
pull
|
34
|
+
open.lib.log_commits(log_opts).map do |commit|
|
35
|
+
yield object(commit) if block_given?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
# ensuring that we always append the logger to the options
|
41
|
+
def self.repo(command, *args)
|
42
|
+
@logger ||= Logger.new(Grudge::Config.log)
|
43
|
+
args << {:log => @logger}
|
44
|
+
Git.send(command, *args)
|
45
|
+
end
|
46
|
+
end # Repository
|
47
|
+
end # Grudge
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class Commit
|
2
|
+
include DataMapper::Resource
|
3
|
+
property :id, Serial
|
4
|
+
property :sha, String, :length => 40, :nullable => false,
|
5
|
+
:unique_index => true
|
6
|
+
property :committed_at, DateTime, :nullable => false
|
7
|
+
property :score, Integer, :default => 0
|
8
|
+
property :created_at, DateTime
|
9
|
+
property :updated_at, DateTime
|
10
|
+
|
11
|
+
has n, :votes
|
12
|
+
|
13
|
+
def awesome!; vote!(Vote.awesome); end
|
14
|
+
def awesome_score; vote_score(:score.gt); end
|
15
|
+
def awesome?; score > 0; end
|
16
|
+
|
17
|
+
def suck!; vote!(Vote.suck); end
|
18
|
+
def suck_score; vote_score(:score.lt); end
|
19
|
+
def suck?; score < 0; end
|
20
|
+
|
21
|
+
# Helpers
|
22
|
+
|
23
|
+
def self.last; first(:order => [:committed_at.desc]); end
|
24
|
+
def self.recent; first(20, :order => [:committed_at.desc]); end
|
25
|
+
def self.popular; first(20, :order => [:score.desc]); end
|
26
|
+
def self.unpopular; first(20, :order => [:score.asc]); end
|
27
|
+
def self.search(sha)
|
28
|
+
object = Grudge::Repository.object(sha)
|
29
|
+
first(:sha => object.sha) if object
|
30
|
+
end
|
31
|
+
|
32
|
+
# Repository delegation methods
|
33
|
+
|
34
|
+
def author_name; repository_object.author.name; end
|
35
|
+
def message; repository_object.message; end
|
36
|
+
|
37
|
+
def self.download!
|
38
|
+
last_commit = last
|
39
|
+
last_sha = last_commit ? last_commit.sha : nil
|
40
|
+
Grudge::Repository.latest_commits_since(last_sha) do |repo_commit|
|
41
|
+
Commit.create!(:sha => repo_commit.sha,
|
42
|
+
:committed_at => repo_commit.committer_date)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def vote!(vote)
|
48
|
+
votes << vote
|
49
|
+
update_attributes({:score => @score + vote.score})
|
50
|
+
end
|
51
|
+
|
52
|
+
def vote_score(condition); votes.sum(:score, condition => 0); end
|
53
|
+
|
54
|
+
def repository_object; @object ||= Grudge::Repository.object(sha); end
|
55
|
+
end
|
data/lib/models/vote.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class Vote
|
2
|
+
include DataMapper::Resource
|
3
|
+
property :id, Serial
|
4
|
+
property :commit_id, Integer, :nullable => false
|
5
|
+
property :score, Integer, :nullable => false
|
6
|
+
property :created_at, DateTime
|
7
|
+
|
8
|
+
belongs_to :commit
|
9
|
+
|
10
|
+
def self.awesome; self.new(:score => 1); end
|
11
|
+
def self.suck; self.new(:score => -1); end
|
12
|
+
|
13
|
+
def awesome?; score > 0; end
|
14
|
+
def suck?; score < 0; end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
%html
|
2
|
+
%head
|
3
|
+
%title= title
|
4
|
+
= stylesheet_include :application
|
5
|
+
%body
|
6
|
+
#header
|
7
|
+
%span Grudge
|
8
|
+
%span.slogan commit some fun
|
9
|
+
|
10
|
+
#nav
|
11
|
+
= anchor('Recent', "/")
|
12
|
+
= anchor('Popular', "/popular")
|
13
|
+
= anchor('Unpopular', "/unpopular")
|
14
|
+
%span#searchbox
|
15
|
+
Find a Commit
|
16
|
+
%form.box{:method => "post", :action => '/search'}
|
17
|
+
%input{:type => 'text', :name => "query", :maxlength => 30, :size => 40}
|
18
|
+
|
19
|
+
%p
|
20
|
+
Repository:
|
21
|
+
= repository_name
|
22
|
+
|
23
|
+
#content= yield
|
24
|
+
#footer.clear
|
25
|
+
© 2008 - Thumble Monks -
|
26
|
+
= Grudge::VERSION
|
@@ -0,0 +1,110 @@
|
|
1
|
+
!serif = times
|
2
|
+
!sans = Arial
|
3
|
+
|
4
|
+
!light_text_color = #999
|
5
|
+
!medium_text_color = #666
|
6
|
+
|
7
|
+
!highlight_color = #ffc
|
8
|
+
|
9
|
+
!link_color = #24b
|
10
|
+
|
11
|
+
body
|
12
|
+
:background-color white
|
13
|
+
:width 900px
|
14
|
+
:margin 0 auto
|
15
|
+
|
16
|
+
.clear
|
17
|
+
:clear both
|
18
|
+
|
19
|
+
a, a:link, a:visited
|
20
|
+
:color = !link_color
|
21
|
+
|
22
|
+
#header
|
23
|
+
:font-family = !serif
|
24
|
+
:font-size 180%
|
25
|
+
:margin-top 1ex
|
26
|
+
:padding-bottom .5ex
|
27
|
+
:border-bottom 1px solid #999
|
28
|
+
.slogan
|
29
|
+
:font-family = !sans
|
30
|
+
:color green
|
31
|
+
:font-size 60%
|
32
|
+
|
33
|
+
#footer
|
34
|
+
:font-family = !sans
|
35
|
+
:font-size 70%
|
36
|
+
:padding-top 1ex
|
37
|
+
|
38
|
+
#nav
|
39
|
+
:padding-top 1ex
|
40
|
+
:font-family = !sans
|
41
|
+
:font-size 85%
|
42
|
+
#searchbox
|
43
|
+
:margin-left 5em
|
44
|
+
form
|
45
|
+
:display inline
|
46
|
+
|
47
|
+
#content
|
48
|
+
:font-family = !sans
|
49
|
+
|
50
|
+
h2
|
51
|
+
:margin 1ex 0ex
|
52
|
+
:font-weight normal
|
53
|
+
:font-family = !serif
|
54
|
+
:color = !medium_text_color
|
55
|
+
|
56
|
+
h3
|
57
|
+
:margin 0
|
58
|
+
:font-weight normal
|
59
|
+
:font-family = !serif
|
60
|
+
:color = !medium_text_color
|
61
|
+
|
62
|
+
ul
|
63
|
+
:list-style-type none
|
64
|
+
:padding 0
|
65
|
+
li
|
66
|
+
:margin 0 0 1em 0
|
67
|
+
:padding 0
|
68
|
+
&.commit
|
69
|
+
.awesome
|
70
|
+
:color green
|
71
|
+
.suck
|
72
|
+
:color #e24
|
73
|
+
.vote
|
74
|
+
:float left
|
75
|
+
:text-align right
|
76
|
+
:width 3em
|
77
|
+
:padding 0 1ex .5ex 1ex
|
78
|
+
:font-size 175%
|
79
|
+
:color #555
|
80
|
+
.info
|
81
|
+
:font-size 90%
|
82
|
+
:float left
|
83
|
+
:width 50%
|
84
|
+
:border-left 1px dotted #ddd
|
85
|
+
:padding 0 0 0 1em
|
86
|
+
.date
|
87
|
+
:color #69c
|
88
|
+
:font-size 90%
|
89
|
+
:width 15em
|
90
|
+
:float left
|
91
|
+
.author
|
92
|
+
:float left
|
93
|
+
:font-size 90%
|
94
|
+
.sha
|
95
|
+
:color = !light_text_color
|
96
|
+
:font-size 90%
|
97
|
+
a
|
98
|
+
:color = !light_text_color
|
99
|
+
.nav
|
100
|
+
:float left
|
101
|
+
:border-left 1px dotted #ddd
|
102
|
+
:padding 0 0 0 1em
|
103
|
+
:width 25%
|
104
|
+
:font-size 70%
|
105
|
+
.message
|
106
|
+
:margin-top .5ex
|
107
|
+
:background-color #f8f8f8
|
108
|
+
:font-family = !serif
|
109
|
+
pre
|
110
|
+
:padding 1ex
|
@@ -0,0 +1,28 @@
|
|
1
|
+
%h2 Commits
|
2
|
+
%ul
|
3
|
+
- @commits.each do |commit|
|
4
|
+
%li.commit
|
5
|
+
.vote
|
6
|
+
.net
|
7
|
+
- if commit.awesome?
|
8
|
+
%span.awesome= commit.score
|
9
|
+
- elsif commit.suck?
|
10
|
+
%span.suck= commit.score
|
11
|
+
- else
|
12
|
+
= commit.score
|
13
|
+
.info
|
14
|
+
.date= commit.committed_at.strftime("%b %m, %Y %X")
|
15
|
+
.author= commit.author_name
|
16
|
+
.sha.clear= anchor(commit.sha, "/#{commit.sha}")
|
17
|
+
.nav
|
18
|
+
%div
|
19
|
+
Score:
|
20
|
+
%span.awesome= commit.awesome_score
|
21
|
+
\/
|
22
|
+
%span.suck= commit.suck_score
|
23
|
+
Vote:
|
24
|
+
= anchor('Awesome', "/#{commit.sha}/awesome")
|
25
|
+
= anchor('Suck', "/#{commit.sha}/suck")
|
26
|
+
.message.clear
|
27
|
+
%pre= commit.message
|
28
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
I don't even know you anymore
|
data/lib/views/show.haml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
%ul
|
2
|
+
%li.commit
|
3
|
+
.vote
|
4
|
+
.net
|
5
|
+
- if @commit.awesome?
|
6
|
+
%span.awesome= @commit.score
|
7
|
+
- elsif @commit.suck?
|
8
|
+
%span.suck= @commit.score
|
9
|
+
- else
|
10
|
+
= @commit.score
|
11
|
+
.info
|
12
|
+
.date= @commit.committed_at.strftime("%b %m, %Y %X")
|
13
|
+
.author= @commit.author_name
|
14
|
+
.sha.clear= @commit.sha
|
15
|
+
.nav
|
16
|
+
%div
|
17
|
+
Score:
|
18
|
+
%span.awesome= @commit.awesome_score
|
19
|
+
\/
|
20
|
+
%span.suck= @commit.suck_score
|
21
|
+
Vote:
|
22
|
+
= anchor('Awesome', "/#{@commit.sha}/awesome")
|
23
|
+
= anchor('Suck', "/#{@commit.sha}/suck")
|
24
|
+
.message.clear
|
25
|
+
%pre= @commit.message
|
data/test/commit_test.rb
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class CommitTest < Test::Unit::TestCase
|
4
|
+
context "required attributes" do
|
5
|
+
setup do
|
6
|
+
@commit = Commit.new
|
7
|
+
@commit.valid?
|
8
|
+
@errors = @commit.errors
|
9
|
+
end
|
10
|
+
|
11
|
+
should "include sha" do
|
12
|
+
assert_equal ["Sha must not be blank"], @errors[:sha]
|
13
|
+
end
|
14
|
+
|
15
|
+
should "include committed at" do
|
16
|
+
assert_equal ["Committed at must not be blank"], @errors[:committed_at]
|
17
|
+
end
|
18
|
+
end # required attributes
|
19
|
+
|
20
|
+
context "unique attributes" do
|
21
|
+
setup do
|
22
|
+
@commit = Factory(:commit)
|
23
|
+
end
|
24
|
+
|
25
|
+
should "include unique sha" do
|
26
|
+
begin
|
27
|
+
Factory(:commit, :sha => @commit.sha)
|
28
|
+
flunk("Should hav raised a unique exception for :sha")
|
29
|
+
rescue Exception => e
|
30
|
+
assert_match %r[not unique], e.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end # unique attributes
|
34
|
+
|
35
|
+
context "default values" do
|
36
|
+
setup { @commit = Commit.new }
|
37
|
+
|
38
|
+
should "be zero for score" do
|
39
|
+
assert_equal 0, @commit.score
|
40
|
+
end
|
41
|
+
end # default values
|
42
|
+
|
43
|
+
context "associations" do
|
44
|
+
setup { @commit = Factory(:commit) }
|
45
|
+
should "have n votes" do
|
46
|
+
assert_kind_of DataMapper::Associations::OneToMany::Proxy, @commit.votes
|
47
|
+
assert_equal (1.0/0), Commit.relationships[:votes].options[:max]
|
48
|
+
end
|
49
|
+
end # associations
|
50
|
+
|
51
|
+
context "scoring" do
|
52
|
+
context "awesome!" do
|
53
|
+
setup do
|
54
|
+
@commit = Factory(:commit)
|
55
|
+
@commit.awesome!
|
56
|
+
end
|
57
|
+
|
58
|
+
should_change "Vote.count", :by => 1
|
59
|
+
|
60
|
+
should "add a new positive vote" do
|
61
|
+
assert @commit.votes.all?(&:awesome?)
|
62
|
+
end
|
63
|
+
|
64
|
+
should "be accessible seperate from net score" do
|
65
|
+
assert_equal 1, @commit.awesome_score
|
66
|
+
end
|
67
|
+
|
68
|
+
should "update net score" do
|
69
|
+
assert_equal 1, @commit.score
|
70
|
+
end
|
71
|
+
|
72
|
+
should "be awesome?" do
|
73
|
+
assert @commit.awesome?
|
74
|
+
end
|
75
|
+
|
76
|
+
should "not suck?" do
|
77
|
+
deny @commit.suck?
|
78
|
+
end
|
79
|
+
end # awesome!
|
80
|
+
|
81
|
+
context "suck!" do
|
82
|
+
setup do
|
83
|
+
@commit = Factory(:commit)
|
84
|
+
@commit.suck!
|
85
|
+
end
|
86
|
+
|
87
|
+
should_change "Vote.count", :by => 1
|
88
|
+
|
89
|
+
should "add a new negative vote" do
|
90
|
+
assert @commit.votes.all?(&:suck?)
|
91
|
+
end
|
92
|
+
|
93
|
+
should "be accessible seperate from net score" do
|
94
|
+
assert_equal -1, @commit.suck_score
|
95
|
+
end
|
96
|
+
|
97
|
+
should "update net score" do
|
98
|
+
assert_equal -1, @commit.score
|
99
|
+
end
|
100
|
+
|
101
|
+
should "not be awesome?" do
|
102
|
+
deny @commit.awesome?
|
103
|
+
end
|
104
|
+
|
105
|
+
should "suck?" do
|
106
|
+
assert @commit.suck?
|
107
|
+
end
|
108
|
+
end # suck!
|
109
|
+
|
110
|
+
context "with equal mix" do
|
111
|
+
setup do
|
112
|
+
@commit = Factory(:commit)
|
113
|
+
@commit.awesome!
|
114
|
+
@commit.suck!
|
115
|
+
end
|
116
|
+
|
117
|
+
should_change "Vote.count", :by => 2
|
118
|
+
|
119
|
+
should "be neither awesome or sucky" do
|
120
|
+
deny @commit.awesome?
|
121
|
+
deny @commit.suck?
|
122
|
+
end
|
123
|
+
|
124
|
+
should "have zero score" do
|
125
|
+
assert_equal 0, @commit.score
|
126
|
+
end
|
127
|
+
end # with equal mix
|
128
|
+
end # scoring
|
129
|
+
|
130
|
+
context "helpers" do
|
131
|
+
setup do
|
132
|
+
@awesome = Factory(:commit, :committed_at => (Time.now - 7200))
|
133
|
+
@awesome.awesome!
|
134
|
+
|
135
|
+
@suck = Factory(:commit, :committed_at => (Time.now - 3600))
|
136
|
+
@suck.suck!
|
137
|
+
|
138
|
+
@neutral = Factory(:commit, :committed_at => (Time.now))
|
139
|
+
end
|
140
|
+
|
141
|
+
should "return most recently committed commits" do
|
142
|
+
assert_equal [@neutral, @suck, @awesome], Commit.recent
|
143
|
+
end
|
144
|
+
|
145
|
+
should "return most popular commits" do
|
146
|
+
assert_equal [@awesome, @neutral, @suck], Commit.popular
|
147
|
+
end
|
148
|
+
|
149
|
+
should "return most unpopular commits" do
|
150
|
+
assert_equal [@suck, @neutral, @awesome], Commit.unpopular
|
151
|
+
end
|
152
|
+
|
153
|
+
should "return last committed record" do
|
154
|
+
assert_equal @neutral, Commit.last
|
155
|
+
end
|
156
|
+
end # helpers
|
157
|
+
|
158
|
+
context "search" do
|
159
|
+
context "for existing object" do
|
160
|
+
setup do
|
161
|
+
@commit = Factory(:commit)
|
162
|
+
@search_key = 'abc'
|
163
|
+
Grudge::Repository.expects(:object).with(@search_key).
|
164
|
+
returns(O(:sha => @commit.sha))
|
165
|
+
end
|
166
|
+
|
167
|
+
should "find commit based on partial id" do
|
168
|
+
assert_equal @commit, Commit.search(@search_key)
|
169
|
+
end
|
170
|
+
end # for existing object
|
171
|
+
|
172
|
+
context "for nonexistant object" do
|
173
|
+
setup do
|
174
|
+
@search_key = 'abc'
|
175
|
+
Grudge::Repository.expects(:object).with(@search_key).returns(nil)
|
176
|
+
end
|
177
|
+
|
178
|
+
should "find nothing if sha does not match anything" do
|
179
|
+
assert_nil Commit.search(@search_key)
|
180
|
+
end
|
181
|
+
end # for nonexistant object
|
182
|
+
end # search
|
183
|
+
|
184
|
+
context "unstored values" do
|
185
|
+
setup do
|
186
|
+
@commit = Factory(:commit)
|
187
|
+
Grudge::Repository.expects(:object).with(@commit.sha).
|
188
|
+
returns(repository_commit)
|
189
|
+
end
|
190
|
+
|
191
|
+
should "delegate author name" do
|
192
|
+
assert_equal "Barney", @commit.author_name
|
193
|
+
end
|
194
|
+
|
195
|
+
should "delegate commit message" do
|
196
|
+
assert_equal "This is the shiznit!", @commit.message
|
197
|
+
end
|
198
|
+
end # unstored values
|
199
|
+
|
200
|
+
context "download" do
|
201
|
+
setup do
|
202
|
+
@repo_commits = (1..10).map {repository_commit}
|
203
|
+
end
|
204
|
+
|
205
|
+
context "when there is one existing commit" do
|
206
|
+
setup do
|
207
|
+
last_commit = Factory(:commit)
|
208
|
+
Grudge::Repository.stubs(:latest_commits_since).with(last_commit.sha).
|
209
|
+
multiple_yields(*@repo_commits)
|
210
|
+
Commit.download!
|
211
|
+
end
|
212
|
+
|
213
|
+
should_change "Commit.count", :by => 11
|
214
|
+
end # when there is one existing commit
|
215
|
+
|
216
|
+
context "when there are no existing commits" do
|
217
|
+
setup do
|
218
|
+
Grudge::Repository.expects(:latest_commits_since).with(nil).
|
219
|
+
multiple_yields(*@repo_commits)
|
220
|
+
Commit.download!
|
221
|
+
end
|
222
|
+
|
223
|
+
should_change "Commit.count", :by => 10
|
224
|
+
end # when there are no existing commits
|
225
|
+
end # download
|
226
|
+
end
|