grudge 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|