contributions 0.1.1
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/.gitignore +20 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +162 -0
- data/Rakefile +16 -0
- data/TODO.markdown +6 -0
- data/contributions.gemspec +22 -0
- data/lib/contributions/contributions.rb +153 -0
- data/lib/contributions/git.rb +66 -0
- data/lib/contributions/github_api.rb +62 -0
- data/lib/contributions/repository_list.rb +56 -0
- data/lib/contributions/string_utils.rb +133 -0
- data/lib/contributions/version.rb +3 -0
- data/lib/contributions.rb +9 -0
- data/test/contributions_integration_test.rb +78 -0
- data/test/contributions_test.rb +112 -0
- data/test/git_test.rb +28 -0
- data/test/github_api_test.rb +41 -0
- data/test/repository_list_test.rb +73 -0
- data/test/string_utils_test.rb +62 -0
- data/test/teststrap.rb +2 -0
- metadata +108 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Charlie Tanksley
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# Contributions
|
2
|
+
|
3
|
+
Get the detailed information about all your OSS contributions in one
|
4
|
+
place.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'contributions'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install contributions
|
19
|
+
|
20
|
+
## Fair Warning:
|
21
|
+
|
22
|
+
Right now, Contributions doesn't know what to do if you have more than
|
23
|
+
100 public repositories. I'll fix that later. Consider yourself
|
24
|
+
warned.
|
25
|
+
|
26
|
+
## Requirements
|
27
|
+
|
28
|
+
[](http://travis-ci.org/charlietanksley/contributions)
|
29
|
+
|
30
|
+
Contributions is known to work on:
|
31
|
+
|
32
|
+
* MRI 1.9.2
|
33
|
+
* MRI 1.9.3
|
34
|
+
|
35
|
+
At present it does not work on:
|
36
|
+
|
37
|
+
* Rubinius 1.2.4
|
38
|
+
|
39
|
+
I don't know about any other Rubys.
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
### Finding contributions
|
44
|
+
|
45
|
+
The main idea behind contributions is to make it easy to add your open
|
46
|
+
source contributions to a resume or website.
|
47
|
+
|
48
|
+
A Contributions object takes a hash as an argument. The only required
|
49
|
+
key is `:username`. This is the user's github username. From here you
|
50
|
+
have a few options. You can manually update the list of repositories
|
51
|
+
(see below). When you are ready you can use the
|
52
|
+
`.contributions_as_hash` method to return the user's contributions.
|
53
|
+
|
54
|
+
Finding all a user's contributions is very computationally intensive: we
|
55
|
+
actually create a clone (in a temporary directory :)) of every forked
|
56
|
+
repository and look for contributions via a `git log --author=username`
|
57
|
+
command. The first time you call this method it may take a while to
|
58
|
+
return the hash (especially if you have forks of really big projects).
|
59
|
+
Since you might want to access this hash multiple times, the result is
|
60
|
+
cached. If you want or need to update this for some reason, use the
|
61
|
+
`.reload_contributions` method and then `.contributions_as_hash` to get
|
62
|
+
the new results.
|
63
|
+
|
64
|
+
### But which projects?
|
65
|
+
|
66
|
+
By default, contributions assumes that a user has contributed to every
|
67
|
+
forked OSS project in his or her github account. When you generate your
|
68
|
+
list of contributions, contributions will look for contributions in
|
69
|
+
every forked repository in your account. (Note: it will look not for
|
70
|
+
additions you have made locally, but for additions that have been merged
|
71
|
+
into the forked project; it looks for commits in that project for which
|
72
|
+
you are either an author or committer.)
|
73
|
+
|
74
|
+
Some people have lots of forks that they don't contribute to. In that
|
75
|
+
case, you can pass an array of projects to ignore to contributions:
|
76
|
+
|
77
|
+
Contributions::Contributions.new(:username => 'u', :remove => ['homebrew'])
|
78
|
+
|
79
|
+
If you have contributed to projects that you have not forked (perhaps
|
80
|
+
you keep a tidy github account :)), you can add those in by passing an
|
81
|
+
array of projects to contributions:
|
82
|
+
|
83
|
+
Contributions::Contributions.new(:username => 'u', :add => ['rubinius/rubinius'])
|
84
|
+
|
85
|
+
Notice that you must pass both the repository name and the username in
|
86
|
+
this case.
|
87
|
+
|
88
|
+
Finally, you might want to only get your contributions to a certain set
|
89
|
+
of repositories, ignoring any others (e.g., any others your forked).
|
90
|
+
|
91
|
+
Contributions::Contributions.new(:username => 'u', :only => ['rubinius/rubinius'])
|
92
|
+
|
93
|
+
The envisioned use case for this command is when you have lots of forked
|
94
|
+
repositories, perhaps even ones you have contributed to, but only care
|
95
|
+
to get your contributions for one.
|
96
|
+
|
97
|
+
### Adding or subtracting contributions
|
98
|
+
|
99
|
+
Before you determine the contributions for a user, you might need to
|
100
|
+
alter the list of repositories you are looking at. You can find out
|
101
|
+
which repositories are currently being considered with the
|
102
|
+
`repositories` method:
|
103
|
+
|
104
|
+
c = Contributions::Contributions.new(:username => 'u')
|
105
|
+
c.repositories
|
106
|
+
# ['rubinius/rubinius', 'mxcl/homebrew']
|
107
|
+
|
108
|
+
You can find out just the project names with the `project_names` method
|
109
|
+
|
110
|
+
c = Contributions::Contributions.new(:username => 'u')
|
111
|
+
c.project_names
|
112
|
+
# ['rubinius', 'homebrew']
|
113
|
+
|
114
|
+
You can subtract a repository using the `remove` method. The `remove`
|
115
|
+
method takes either a string (in the case of a single repository) or an
|
116
|
+
array of strings as an argument. The repositories should be specified
|
117
|
+
in the username/repository pattern.
|
118
|
+
|
119
|
+
c = Contributions::Contributions.new(:username => 'u')
|
120
|
+
c.repositories
|
121
|
+
# ['sinatra/sinatra', 'rubinius/rubinius', 'mxcl/homebrew']
|
122
|
+
c.remove('sinatra/sinatra')
|
123
|
+
# ['rubinius/rubinius', 'mxcl/homebrew']
|
124
|
+
c.remove(['rubinius/rubinius', 'mxcl/homebrew'])
|
125
|
+
# []
|
126
|
+
|
127
|
+
You can add a repository using the `add` command. It takes arguments
|
128
|
+
just like `remove`.
|
129
|
+
|
130
|
+
c = Contributions::Contributions.new(:username => 'u')
|
131
|
+
c.repositories
|
132
|
+
# []
|
133
|
+
c.add('sinatra/sinatra')
|
134
|
+
# ['sinatra/sinatra']
|
135
|
+
c.add(['rubinius/rubinius', 'mxcl/homebrew'])
|
136
|
+
# ['sinatra/sinatra', 'rubinius/rubinius', 'mxcl/homebrew']
|
137
|
+
|
138
|
+
### Determining your contributions
|
139
|
+
|
140
|
+
c = Contributions::Contributions.new(:username => 'u')
|
141
|
+
# => # does not determine the contributions
|
142
|
+
c.contributions_as_hash
|
143
|
+
# => {:project1 => [...], :project2 => [...] ... }
|
144
|
+
|
145
|
+
### Getting the information out
|
146
|
+
|
147
|
+
You access commits via `contributions_as_hash`:
|
148
|
+
|
149
|
+
c = Contributions::Contributions.new(:username => 'u')
|
150
|
+
c.repositories
|
151
|
+
# ['sinatra/sinatra', 'rubinius/rubinius']
|
152
|
+
c.contributions_as_hash
|
153
|
+
# => {:'sinatra/sinatra' => ["commit data", "commit data"], :'rubinius/rubinius' => ["commit data"]}
|
154
|
+
|
155
|
+
|
156
|
+
## Contributing
|
157
|
+
|
158
|
+
1. Fork it
|
159
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
160
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
161
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
162
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/testtask'
|
6
|
+
|
7
|
+
desc "Run all our tests"
|
8
|
+
task :test do
|
9
|
+
Rake::TestTask.new do |t|
|
10
|
+
t.libs << "test"
|
11
|
+
t.pattern = "test/**/*_test.rb"
|
12
|
+
t.verbose = false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
task :default => :test
|
data/TODO.markdown
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/contributions/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Charlie Tanksley"]
|
6
|
+
gem.email = ["charlie.tanksley@gmail.com"]
|
7
|
+
gem.description = %q{Gather your contributions to OSS projects (hosted on github!).}
|
8
|
+
gem.summary = %q{A gem to find all your contributions to OSS projects on github and make that information easy to access.}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "contributions"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Contributions::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency('json')
|
19
|
+
|
20
|
+
gem.add_development_dependency('riot')
|
21
|
+
gem.add_development_dependency('rake')
|
22
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Contributions
|
4
|
+
class Contributions
|
5
|
+
|
6
|
+
# opts - a Hash with, at the very least, a username. Optional
|
7
|
+
# arguments include :remove (to ignore some repository),
|
8
|
+
# :add (to add), and :only (to focus).
|
9
|
+
def initialize(opts={})
|
10
|
+
@username = opts.delete(:username)
|
11
|
+
setup_repositories(opts)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: Add a repository (or array of repositories).
|
15
|
+
#
|
16
|
+
# repos - a 'username/repository' String or Array of such strings.
|
17
|
+
#
|
18
|
+
# Returns the updated array of repositories.
|
19
|
+
def add(repos)
|
20
|
+
@repositories.add(repos)
|
21
|
+
|
22
|
+
repositories
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: Return a user's OSS contributions as a hash. If the hash
|
26
|
+
# hasn't already been determined, the contributions are all looked
|
27
|
+
# up and stashed in an ivar: @contributions. If @contributions
|
28
|
+
# already exists, it is returned without the costly lookup being
|
29
|
+
# performed.
|
30
|
+
#
|
31
|
+
# Returns a Hash.
|
32
|
+
def contributions_as_hash
|
33
|
+
load_contributions unless @contributions
|
34
|
+
@contributions
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Determine a user's contributions and load the
|
38
|
+
# @contributions ivar.
|
39
|
+
#
|
40
|
+
# Returns a Hash.
|
41
|
+
def load_contributions
|
42
|
+
@contributions = Hash.new
|
43
|
+
repositories.each do |f|
|
44
|
+
conts = get_contributions(f)
|
45
|
+
@contributions[f] = conts unless conts.empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
@contributions
|
49
|
+
end
|
50
|
+
|
51
|
+
# Public: Replace the user's forked repositories with the specified
|
52
|
+
# repositories.
|
53
|
+
#
|
54
|
+
# repos - a 'username/repository_name' string, or an array of such
|
55
|
+
# strings.
|
56
|
+
#
|
57
|
+
# Returns the updated array of repositories.
|
58
|
+
def only(repos)
|
59
|
+
@repositories.only repos
|
60
|
+
|
61
|
+
repositories
|
62
|
+
end
|
63
|
+
|
64
|
+
# Public: Provide the names for all the forked projects.
|
65
|
+
#
|
66
|
+
# Example:
|
67
|
+
#
|
68
|
+
# user.repositories
|
69
|
+
# # => ['r/r', 's/s']
|
70
|
+
# user.project_names
|
71
|
+
# # => ['r', 's']
|
72
|
+
#
|
73
|
+
# Returns an Array.
|
74
|
+
def project_names
|
75
|
+
repositories.map { |s| s.match(/[^\/]*$/)[0] }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Public: Remove a repository (or array of repositories).
|
79
|
+
#
|
80
|
+
# repos - a 'username/repository' String or Array of such strings.
|
81
|
+
#
|
82
|
+
# Returns the updated array of repositories.
|
83
|
+
def remove(repos)
|
84
|
+
@repositories.remove(repos)
|
85
|
+
|
86
|
+
repositories
|
87
|
+
end
|
88
|
+
|
89
|
+
# Public: Accessor method for the @repositories ivar.
|
90
|
+
#
|
91
|
+
# array - an array of 'username/repository_name' strings.
|
92
|
+
#
|
93
|
+
# Returns nothing.
|
94
|
+
def repositories=(array)
|
95
|
+
@repositories = RepositoryList.new(array)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Public: Accessor method for the @repositories ivar.
|
99
|
+
#
|
100
|
+
# Returns an Array of 'username/repository_name' strings.
|
101
|
+
def repositories
|
102
|
+
@repositories.list
|
103
|
+
end
|
104
|
+
|
105
|
+
# Internal: attr_accessor for @contributions. This method really only
|
106
|
+
# exists for testing.
|
107
|
+
#
|
108
|
+
# hash - a hash.
|
109
|
+
#
|
110
|
+
# Returns a Hash.
|
111
|
+
def contributions=(hash)
|
112
|
+
@contributions = hash
|
113
|
+
end
|
114
|
+
|
115
|
+
# Internal: attr_reader for @contributions. This method really only
|
116
|
+
# exists for testing.
|
117
|
+
#
|
118
|
+
# Returns a Hash.
|
119
|
+
def contributions
|
120
|
+
@contributions
|
121
|
+
end
|
122
|
+
|
123
|
+
# Internal: Generate an array of forked repositories for the user.
|
124
|
+
# This array is set as the @repositories variable.
|
125
|
+
#
|
126
|
+
# opts - an array with, possible, keys for :only, :remove, and :add.
|
127
|
+
#
|
128
|
+
# Returns an Array of repositories.
|
129
|
+
def setup_repositories(opts)
|
130
|
+
@repositories = RepositoryList.new(GithubAPI.forks(@username))
|
131
|
+
update(opts)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Internal: Get the user's contributions to the repository.
|
135
|
+
#
|
136
|
+
# Returns a Hash.
|
137
|
+
def get_contributions(repository)
|
138
|
+
Git.contributions GithubAPI.name(@username), repository
|
139
|
+
end
|
140
|
+
|
141
|
+
# Internal: Combine the user's explicit preferences with an array of
|
142
|
+
# forks.
|
143
|
+
#
|
144
|
+
# Returns an Array.
|
145
|
+
def update(opts)
|
146
|
+
opts.each_pair do |k,v|
|
147
|
+
@repositories.send(k.to_sym, v)
|
148
|
+
end
|
149
|
+
|
150
|
+
repositories
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'contributions/string_utils'
|
4
|
+
|
5
|
+
module Contributions
|
6
|
+
class Git
|
7
|
+
|
8
|
+
ENDING = " CONTRIBUTIONS_ENDING "
|
9
|
+
KEYS = [:sha, :date, :subject, :body]
|
10
|
+
SEPARATOR = " CONTRIBUTIONS_SEPARATOR "
|
11
|
+
|
12
|
+
# Public: Get all the contributions in a repository by a user
|
13
|
+
# (contributions for which the user is the *author*).
|
14
|
+
#
|
15
|
+
# user - a user's name (the name that shows up as the committer or
|
16
|
+
# author---e.g., 'John Smith'
|
17
|
+
# repository - a 'username/repository_name' string.
|
18
|
+
#
|
19
|
+
# Returns an Array of Hashes with keys for :sha, :date, :subject, :body
|
20
|
+
def self.contributions(user, repository)
|
21
|
+
log = self.clone(repository) { self.read_log(user) }
|
22
|
+
StringUtils.string_to_hash(log, KEYS, SEPARATOR, ENDING)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: Clone a repository and run the block passed inside the
|
26
|
+
# newly cloned repository.
|
27
|
+
#
|
28
|
+
# repository - a 'username/repository_name' string.
|
29
|
+
# block - a block to be executed inside the newly cloned
|
30
|
+
# directory.
|
31
|
+
#
|
32
|
+
# Returns the return value of the block.
|
33
|
+
def self.clone(repository, &block)
|
34
|
+
value = ''
|
35
|
+
repo_name = repository.match(/([^\/]*$)/)[1]
|
36
|
+
Dir.mktmpdir(repo_name) do |dir|
|
37
|
+
cloned_repo_name = dir + '/' + repo_name
|
38
|
+
system "git clone -q https://github.com/#{repository} #{cloned_repo_name}"
|
39
|
+
Dir.chdir(cloned_repo_name) do
|
40
|
+
value = yield
|
41
|
+
end
|
42
|
+
|
43
|
+
FileUtils.rm_rf(cloned_repo_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
value
|
47
|
+
end
|
48
|
+
|
49
|
+
# Internal: The command to read the git log.
|
50
|
+
#
|
51
|
+
# user - the user's name.
|
52
|
+
#
|
53
|
+
# Returns nothing.
|
54
|
+
def self.read_log(user)
|
55
|
+
# We want a string returned (for parsing); so use read over
|
56
|
+
# readlines.
|
57
|
+
IO.popen("git log --author='#{user}' --format='#{self.log_format}' --no-color") { |io| io.read }
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.log_format
|
61
|
+
["%h", "%ci", "%s", "%b"].join(SEPARATOR) << ENDING
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Contributions
|
5
|
+
class GithubAPI
|
6
|
+
|
7
|
+
# Public: Get just the user's repositories that are forks.
|
8
|
+
#
|
9
|
+
# Returns an Array.
|
10
|
+
def self.forks(username)
|
11
|
+
forks = self.repos(username).select { |r| r["fork"] == true }
|
12
|
+
repo_names = forks.map { |r| r["owner"]["login"] + '/' + r["name"] }
|
13
|
+
repo_names.map { |r| self.parent(r) }
|
14
|
+
end
|
15
|
+
|
16
|
+
# Public: Get the name of the user.
|
17
|
+
#
|
18
|
+
# username - github username.
|
19
|
+
#
|
20
|
+
# Returns a String.
|
21
|
+
def self.name(username)
|
22
|
+
self.user(username)["name"]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: Get the name of the forked repository.
|
26
|
+
#
|
27
|
+
# repository - a 'username/repository_name' string.
|
28
|
+
#
|
29
|
+
# Returns a String.
|
30
|
+
def self.parent(repository)
|
31
|
+
username, repo_name = repository.split('/')
|
32
|
+
repo_info = self.repository(repository)
|
33
|
+
repo_info["parent"]["owner"]["login"] + '/' + repo_name
|
34
|
+
end
|
35
|
+
|
36
|
+
# Public: Get all the user's repositories.
|
37
|
+
#
|
38
|
+
# Returns an Array.
|
39
|
+
def self.repos(username)
|
40
|
+
JSON.parse(open("https://api.github.com/users/#{username}/repos?per_page=100") { |f| f.read } )
|
41
|
+
end
|
42
|
+
|
43
|
+
# Internal: Get the user info (all of it) from github.
|
44
|
+
#
|
45
|
+
# username - github username.
|
46
|
+
#
|
47
|
+
# Returns a Hash.
|
48
|
+
def self.user(username)
|
49
|
+
JSON.parse(open("https://api.github.com/users/#{username}") { |f| f.read } )
|
50
|
+
end
|
51
|
+
|
52
|
+
# Internal: Get the repository info (all of it) from github.
|
53
|
+
#
|
54
|
+
# repository - a 'username/repository_name' string.
|
55
|
+
#
|
56
|
+
# Returns a Hash.
|
57
|
+
def self.repository(repository)
|
58
|
+
JSON.parse(open("https://api.github.com/repos/#{repository}") { |f| f.read } )
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Contributions
|
2
|
+
class RepositoryList
|
3
|
+
|
4
|
+
attr_reader :list
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@list = [args].flatten
|
8
|
+
end
|
9
|
+
|
10
|
+
# Public: Add a string or array of strings to the repository list.
|
11
|
+
#
|
12
|
+
# repos - a string or an array of strings (each of which is a
|
13
|
+
# 'username/repo')
|
14
|
+
#
|
15
|
+
# Returns a RepositoryList
|
16
|
+
def add(repos)
|
17
|
+
@list.push(repos).flatten!
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Turn repositories into key value pairs.
|
22
|
+
#
|
23
|
+
# Returns an Array of Hashes {:username, :repository}
|
24
|
+
def key_value_pairs
|
25
|
+
results = []
|
26
|
+
@list.each do |e|
|
27
|
+
p = e.split('/')
|
28
|
+
results.push Hash[:username => p[0], :repository => p[1]]
|
29
|
+
end
|
30
|
+
|
31
|
+
results
|
32
|
+
end
|
33
|
+
|
34
|
+
# Public: Replace list of repositories with the list provided.
|
35
|
+
#
|
36
|
+
# repos - a string or an array of strings (each of which is a
|
37
|
+
# 'username/repo')
|
38
|
+
#
|
39
|
+
# Returns a RepositoryList
|
40
|
+
def only(repos)
|
41
|
+
@list = [repos].flatten
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: Remove a string or array of strings from the repository
|
46
|
+
# list.
|
47
|
+
# repos - a string or an array of strings (each of which is a
|
48
|
+
# 'username/repo')
|
49
|
+
#
|
50
|
+
# Returns a RepositoryList
|
51
|
+
def remove(repos)
|
52
|
+
@list.delete_if { |e| [repos].flatten.include? e }
|
53
|
+
self
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Contributions
|
2
|
+
class StringUtils
|
3
|
+
|
4
|
+
# Public: Read a long string of commit data and turn it into a
|
5
|
+
# hash.
|
6
|
+
#
|
7
|
+
# string - a string of commit data.
|
8
|
+
# separator - the separator between values in an entry.
|
9
|
+
# ending - the separator between entries.
|
10
|
+
# keys - an Array of keys for the final hash.
|
11
|
+
#
|
12
|
+
# Returns a Hash.
|
13
|
+
def self.string_to_hash(string, keys, separator, *ending)
|
14
|
+
s = string.dup
|
15
|
+
s_as_array = ending.empty? ? [s] : self.split!(s, ending[0])
|
16
|
+
|
17
|
+
s_as_array.map! { |e| self.split!(e, separator) }
|
18
|
+
self.remove_empty(s_as_array)
|
19
|
+
|
20
|
+
s_as_array.map! do |e|
|
21
|
+
e.map! { |line| line.strip }
|
22
|
+
self.zip_to_hash(keys, e)
|
23
|
+
end
|
24
|
+
|
25
|
+
s_as_array.map do |e|
|
26
|
+
self.short_dates(e)
|
27
|
+
end
|
28
|
+
|
29
|
+
# s_as_array.map do |e|
|
30
|
+
# self.zip_to_hash(keys, e)
|
31
|
+
# end
|
32
|
+
|
33
|
+
|
34
|
+
# self.split!(s, ending).each do |e|
|
35
|
+
# self.remove_empty(self.split!(e, separator))
|
36
|
+
# end
|
37
|
+
|
38
|
+
# Now we need the zip move, then to a hash, then replace nils with
|
39
|
+
# ''
|
40
|
+
|
41
|
+
# s = string.dup
|
42
|
+
# array = string.split()
|
43
|
+
# small_arrays = array.map { |e| e.split(separator).map { |l| l.strip } }
|
44
|
+
# small_arrays.delete_if { |a| a[0] == "" and a[1] == nil }
|
45
|
+
# small_arrays.map! { |a| [:sha, :date, :subject, :body].zip a }
|
46
|
+
# small_arrays
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
# results = []
|
52
|
+
# s = string.dup
|
53
|
+
# s.gsub!(/(\d{4}-\d{2}-\d{2})/) { |f| " BREAK " + f }
|
54
|
+
# s = s.split(" BREAK ")
|
55
|
+
# s.delete_if { |l| l.empty? }
|
56
|
+
# s.each do |line|
|
57
|
+
# m = /(?<date>\d{4}-\d{2}-\d{2})/.match line
|
58
|
+
# results.push m["date"]
|
59
|
+
# end
|
60
|
+
|
61
|
+
# results
|
62
|
+
end
|
63
|
+
|
64
|
+
# Public: Split the string on the give separator.
|
65
|
+
#
|
66
|
+
# separator - the character(s) on which to split the string.
|
67
|
+
#
|
68
|
+
# Returns a modified version of the string.
|
69
|
+
def self.split!(string, separator)
|
70
|
+
string.split(separator)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Internal: Remove any empty arrays after a split.
|
74
|
+
#
|
75
|
+
# array - an array of Strings.
|
76
|
+
#
|
77
|
+
# Returns an Array of Strings (modified)
|
78
|
+
def self.remove_empty(array)
|
79
|
+
array.delete_if { |a| self.practically_empty?(a[0]) && a[1].nil? }
|
80
|
+
end
|
81
|
+
|
82
|
+
# Internal: Determine whether a string has any content.
|
83
|
+
#
|
84
|
+
# Examples:
|
85
|
+
#
|
86
|
+
# StringUtils.practically_empty?('')
|
87
|
+
# # => true
|
88
|
+
# StringUtils.practically_empty?("\n\n")
|
89
|
+
# # => true
|
90
|
+
# StringUtils.practically_empty?("a\n")
|
91
|
+
# # => false
|
92
|
+
#
|
93
|
+
# Returns a Boolean.
|
94
|
+
def self.practically_empty?(arg)
|
95
|
+
if arg.empty?
|
96
|
+
return true
|
97
|
+
elsif !arg.match /\w/
|
98
|
+
return true
|
99
|
+
else
|
100
|
+
return false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Internal: Convert a pair of arrays into a hash with the first as
|
105
|
+
# keys.
|
106
|
+
#
|
107
|
+
# keys - an Array of keys.
|
108
|
+
# values - an Array of values.
|
109
|
+
#
|
110
|
+
# Returns a Hash.
|
111
|
+
def self.zip_to_hash(keys, values)
|
112
|
+
value = Hash.new
|
113
|
+
zipped = keys.zip values
|
114
|
+
zipped.each do |pair|
|
115
|
+
value[pair.first] = pair.last || ''
|
116
|
+
end
|
117
|
+
|
118
|
+
value
|
119
|
+
end
|
120
|
+
|
121
|
+
# Internal: Convert date format to a simpler one.
|
122
|
+
#
|
123
|
+
# hash - a hash with a :date key
|
124
|
+
#
|
125
|
+
# Returns a Hash.
|
126
|
+
def self.short_dates(hash)
|
127
|
+
old_date = hash[:date]
|
128
|
+
hash[:date] = old_date.match(/(\d{4}-\d{2}-\d{2})/)[1]
|
129
|
+
|
130
|
+
hash
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'teststrap'
|
2
|
+
require 'contributions'
|
3
|
+
|
4
|
+
context "Contributions::Contributions" do
|
5
|
+
hookup { RR.reset }
|
6
|
+
helper(:full) { Contributions::Contributions.new(:username => 'charlietanksley') }
|
7
|
+
|
8
|
+
context ".new finds out about all forks" do
|
9
|
+
setup { full }
|
10
|
+
asserts(:repositories).includes "msanders/snipmate.vim"
|
11
|
+
asserts(:repositories).includes "thumblemonks/riot"
|
12
|
+
end
|
13
|
+
|
14
|
+
context ".new with an :only modifies the forks" do
|
15
|
+
setup { Contributions::Contributions.new(:username => 'charlietanksley', :only => 'thumblemonks/riot') }
|
16
|
+
asserts(:repositories).equals ['thumblemonks/riot']
|
17
|
+
end
|
18
|
+
|
19
|
+
context ".new with a :remove subtracts a fork" do
|
20
|
+
setup { Contributions::Contributions.new(:username => 'charlietanksley', :remove => 'thumblemonks/riot') }
|
21
|
+
denies(:repositories).includes 'thumblemonks/riot'
|
22
|
+
asserts("we have subtracted 1") { full.repositories.count - topic.repositories.count == 1 }.equals true
|
23
|
+
end
|
24
|
+
|
25
|
+
context ".new will :remove with an array as an argument" do
|
26
|
+
setup { Contributions::Contributions.new(:username => 'charlietanksley', :remove => ['thumblemonks/riot', 'rubinius/rubinius']) }
|
27
|
+
denies(:repositories).includes 'thumblemonks/riot'
|
28
|
+
denies(:repositories).includes 'rubinius/rubinius'
|
29
|
+
end
|
30
|
+
|
31
|
+
context ".new with an :add adds a fork" do
|
32
|
+
setup { Contributions::Contributions.new(:username => 'charlietanksley', :add => 'thumblemonks/chicago') }
|
33
|
+
asserts(:repositories).includes 'thumblemonks/chicago'
|
34
|
+
asserts("we have added 1") { topic.repositories.count - full.repositories.count == 1 }.equals true
|
35
|
+
end
|
36
|
+
|
37
|
+
context ".new will :add with an array as an argument" do
|
38
|
+
setup { Contributions::Contributions.new(:username => 'charlietanksley', :add => ['t/r', 'r/r']) }
|
39
|
+
asserts(:repositories).includes 't/r'
|
40
|
+
asserts(:repositories).includes 'r/r'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "a full run of Contributions::Contributions" do
|
45
|
+
setup { Contributions::Contributions.new(:username => 'charlietanksley',
|
46
|
+
:only => ['thumblemonks/riot', 'msanders/snipmate.vim', 'davetron5000/methadone']) }
|
47
|
+
|
48
|
+
context "starts with a list of repositories" do
|
49
|
+
asserts(:repositories).equals ['thumblemonks/riot', 'msanders/snipmate.vim', 'davetron5000/methadone']
|
50
|
+
end
|
51
|
+
|
52
|
+
context "does not start with any contributions" do
|
53
|
+
asserts(:contributions).nil
|
54
|
+
end
|
55
|
+
|
56
|
+
context "grabs the contributions when asked" do
|
57
|
+
|
58
|
+
context "with the right keys" do
|
59
|
+
setup { topic.contributions_as_hash.keys }
|
60
|
+
asserts_topic.includes 'thumblemonks/riot'
|
61
|
+
asserts_topic.includes 'davetron5000/methadone'
|
62
|
+
end
|
63
|
+
|
64
|
+
context "and saves them off" do
|
65
|
+
hookup { topic.contributions_as_hash }
|
66
|
+
setup { topic.contributions.keys }
|
67
|
+
asserts_topic.includes 'thumblemonks/riot'
|
68
|
+
asserts_topic.includes 'davetron5000/methadone'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when the list is updated" do
|
73
|
+
hookup { topic.remove ['thumblemonks/riot', 'davetron5000/methadone'] }
|
74
|
+
hookup { topic.load_contributions }
|
75
|
+
asserts { topic.contributions_as_hash.keys }.equals []
|
76
|
+
asserts(:contributions_as_hash).equals Hash[]
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'teststrap'
|
2
|
+
require 'contributions'
|
3
|
+
|
4
|
+
context "Contributions public api with mocked .new" do
|
5
|
+
hookup do
|
6
|
+
stub(Contributions::GithubAPI).forks(anything) { ['r/r', 's/s'] }
|
7
|
+
end
|
8
|
+
|
9
|
+
setup { Contributions::Contributions.new(:username => 'charlietanksley') }
|
10
|
+
|
11
|
+
context ".new" do
|
12
|
+
context "assignments" do
|
13
|
+
asserts_topic.assigns :username
|
14
|
+
asserts_topic.assigns :repositories
|
15
|
+
end
|
16
|
+
|
17
|
+
asserts(:repositories).equals ['r/r', 's/s']
|
18
|
+
end
|
19
|
+
|
20
|
+
context ".add will add repositories" do
|
21
|
+
context "if given a string" do
|
22
|
+
setup { topic.add 'added/added' }
|
23
|
+
asserts_topic.includes 'added/added'
|
24
|
+
end
|
25
|
+
|
26
|
+
context "if given an array" do
|
27
|
+
setup { topic.add ['added/first', 'added/second'] }
|
28
|
+
asserts_topic.includes 'added/first'
|
29
|
+
asserts_topic.includes 'added/second'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context ".contributions_as_hash" do
|
34
|
+
context "does not reload hash if it already exists" do
|
35
|
+
hookup do
|
36
|
+
topic.contributions = Hash[:k => 'v']
|
37
|
+
dont_allow(topic).load_contributions
|
38
|
+
end
|
39
|
+
|
40
|
+
asserts(:contributions_as_hash).equals Hash[:k => 'v']
|
41
|
+
end
|
42
|
+
|
43
|
+
context "calls #load_contributions if there aren't any contributions yet" do
|
44
|
+
hookup { mock(topic).load_contributions { topic.contributions = Hash[:k => 'v'] } }
|
45
|
+
asserts(:contributions_as_hash).equals Hash[:k => 'v']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context ".only will trade out repositories" do
|
50
|
+
context "if given a string" do
|
51
|
+
setup { topic.only 'added/added' }
|
52
|
+
asserts_topic.includes 'added/added'
|
53
|
+
denies_topic.includes 'r/r'
|
54
|
+
denies_topic.includes 's/s'
|
55
|
+
end
|
56
|
+
|
57
|
+
context "if given an array" do
|
58
|
+
setup { topic.only ['added/first', 'added/second'] }
|
59
|
+
asserts_topic.includes 'added/first'
|
60
|
+
asserts_topic.includes 'added/second'
|
61
|
+
denies_topic.includes 'r/r'
|
62
|
+
denies_topic.includes 's/s'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context ".project_names returns an array of project names" do
|
67
|
+
asserts(:project_names).equals { ['r', 's'] }
|
68
|
+
end
|
69
|
+
|
70
|
+
context ".load_contributions" do
|
71
|
+
context "actually calls out to get the contributions for each fork" do
|
72
|
+
hookup do
|
73
|
+
mock(Contributions::Git).contributions('Charlie Tanksley', 'r/r') { Hash[:sha => 1] }
|
74
|
+
mock(Contributions::Git).contributions('Charlie Tanksley', 's/s') { Hash[:sha => 2] }
|
75
|
+
end
|
76
|
+
|
77
|
+
denies(:load_contributions).nil
|
78
|
+
end
|
79
|
+
|
80
|
+
context "returns a hash with repository names as keys" do
|
81
|
+
hookup do
|
82
|
+
stub(Contributions::Git).contributions('Charlie Tanksley', 'r/r') { Hash[:sha => 1] }
|
83
|
+
stub(Contributions::Git).contributions('Charlie Tanksley', 's/s') { Hash[:sha => 2] }
|
84
|
+
end
|
85
|
+
|
86
|
+
asserts(:load_contributions).equals Hash['r/r' => Hash[:sha => 1], 's/s' => Hash[:sha => 2]]
|
87
|
+
end
|
88
|
+
|
89
|
+
context "stashes the results in an instance variable" do
|
90
|
+
hookup do
|
91
|
+
stub(Contributions::Git).contributions('Charlie Tanksley', 'r/r') { Hash[:sha => 1] }
|
92
|
+
stub(Contributions::Git).contributions('Charlie Tanksley', 's/s') { Hash[:sha => 2] }
|
93
|
+
topic.load_contributions
|
94
|
+
end
|
95
|
+
|
96
|
+
asserts(:contributions).equals Hash['r/r' => Hash[:sha => 1], 's/s' => Hash[:sha => 2]]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context ".remove will drop repositories" do
|
101
|
+
context "if given a string" do
|
102
|
+
setup { topic.remove 'r/r' }
|
103
|
+
denies_topic.includes 'added/added'
|
104
|
+
end
|
105
|
+
|
106
|
+
context "if given an array" do
|
107
|
+
setup { topic.remove ['r/r', 's/s'] }
|
108
|
+
denies_topic.includes 'r/r'
|
109
|
+
denies_topic.includes 's/s'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/test/git_test.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'teststrap'
|
2
|
+
require 'contributions/git'
|
3
|
+
|
4
|
+
context "Contributions::Git" do
|
5
|
+
context ".contributions" do
|
6
|
+
context 'determines all the user\'s contributions' do
|
7
|
+
setup { Contributions::Git.contributions('Charlie Tanksley', 'thumblemonks/riot') }
|
8
|
+
|
9
|
+
asserts_topic.size 6
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context ".clone creates a clone and runs the block you pass it" do
|
14
|
+
helper(:command) { "git log --author='Charlie Tanksley' --format='%h %ci %s %b' --no-color" }
|
15
|
+
setup do
|
16
|
+
Contributions::Git.clone('thumblemonks/riot') { IO.popen(command) { |io| io.readlines } }
|
17
|
+
end
|
18
|
+
|
19
|
+
asserts_topic.size 6
|
20
|
+
end
|
21
|
+
|
22
|
+
context ".log_format" do
|
23
|
+
setup { Contributions::Git.log_format }
|
24
|
+
|
25
|
+
asserts_topic.equals "%h CONTRIBUTIONS_SEPARATOR %ci CONTRIBUTIONS_SEPARATOR %s CONTRIBUTIONS_SEPARATOR %b CONTRIBUTIONS_ENDING "
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'teststrap'
|
2
|
+
require 'contributions/github_api'
|
3
|
+
|
4
|
+
context "Contributions::GithubAPI" do
|
5
|
+
context ".forks" do
|
6
|
+
setup { Contributions::GithubAPI.forks('charlietanksley') }
|
7
|
+
|
8
|
+
context "gets a list of all the forks" do
|
9
|
+
setup { topic.count }
|
10
|
+
denies_topic.equals 0
|
11
|
+
end
|
12
|
+
|
13
|
+
context "gets the original names, not the name of the fork" do
|
14
|
+
denies_topic.includes 'charlietanksley/riot'
|
15
|
+
asserts_topic.includes 'thumblemonks/riot'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context ".repos gets all the repositories when there are less than 100" do
|
20
|
+
setup { Contributions::GithubAPI.repos('rubinius').count }
|
21
|
+
|
22
|
+
asserts_topic.equals JSON.parse(open("https://api.github.com/users/rubinius") { |f| f.read })["public_repos"]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Pending
|
26
|
+
context ".repos gets all the repositories when there are tons" do
|
27
|
+
# setup { Contributions::GithubAPI.repos('vim-scripts').count }
|
28
|
+
|
29
|
+
# asserts_topic.equals JSON.parse(open("https://api.github.com/users/vim-scripts") { |f| f.read })["public_repos"]
|
30
|
+
end
|
31
|
+
|
32
|
+
context ".name returns the name of the user" do
|
33
|
+
setup { Contributions::GithubAPI.name('charlietanksley') }
|
34
|
+
asserts_topic.equals 'Charlie Tanksley'
|
35
|
+
end
|
36
|
+
|
37
|
+
context ".parent returns the name of the forked repository" do
|
38
|
+
setup { Contributions::GithubAPI.parent('charlietanksley/riot') }
|
39
|
+
asserts_topic.equals 'thumblemonks/riot'
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'teststrap'
|
2
|
+
require 'contributions/repository_list'
|
3
|
+
|
4
|
+
context "RepositoryList" do
|
5
|
+
context "#add" do
|
6
|
+
context "with a blank canvas" do
|
7
|
+
setup { Contributions::RepositoryList.new }
|
8
|
+
|
9
|
+
context "adds a single string" do
|
10
|
+
hookup { topic.add('rubinius/rubinius') }
|
11
|
+
asserts(:list).equals ['rubinius/rubinius']
|
12
|
+
end
|
13
|
+
|
14
|
+
context "adds an array of strings" do
|
15
|
+
hookup { topic.add ['rubinius/rubinius', 'vim-scripts/test.vim'] }
|
16
|
+
asserts(:list).equals ['rubinius/rubinius', 'vim-scripts/test.vim']
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "with a starting array" do
|
21
|
+
setup { Contributions::RepositoryList.new(['s/s']) }
|
22
|
+
|
23
|
+
context "adds a single string" do
|
24
|
+
hookup { topic.add 'rubinius/rubinius' }
|
25
|
+
asserts(:list).equals ['s/s', 'rubinius/rubinius']
|
26
|
+
end
|
27
|
+
|
28
|
+
context "adds an array of strings" do
|
29
|
+
hookup { topic.add ['rubinius/rubinius', 'vim-scripts/test.vim'] }
|
30
|
+
asserts(:list).equals ['s/s', 'rubinius/rubinius', 'vim-scripts/test.vim']
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "#remove" do
|
36
|
+
setup { Contributions::RepositoryList.new(['s/s', 's/ss', 'o/other']) }
|
37
|
+
|
38
|
+
context "selectively removes a single repo" do
|
39
|
+
hookup { topic.remove 's/s' }
|
40
|
+
asserts(:list).equals ['s/ss', 'o/other']
|
41
|
+
end
|
42
|
+
|
43
|
+
context "selectively removes an array of repos" do
|
44
|
+
hookup { topic.remove ['s/s', 's/ss'] }
|
45
|
+
asserts(:list).equals ['o/other']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "#only completely replaces the list" do
|
50
|
+
setup { Contributions::RepositoryList.new(['s/s', 's/ss', 'o/other']) }
|
51
|
+
|
52
|
+
context "when given a single repo" do
|
53
|
+
hookup { topic.only 'a/a' }
|
54
|
+
asserts(:list).equals ['a/a']
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when given an array" do
|
58
|
+
hookup { topic.only ['a/a', 'b/b'] }
|
59
|
+
asserts(:list).equals ['a/a', 'b/b']
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "#key_value_pairs splits into key value pairs" do
|
64
|
+
setup { Contributions::RepositoryList.new(['s/s', 's/ss', 'o/other']) }
|
65
|
+
asserts(:key_value_pairs).equals [{:username => 's', :repository => 's'},
|
66
|
+
{:username => 's', :repository => 'ss'},
|
67
|
+
{:username => 'o', :repository => 'other'}]
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'teststrap'
|
2
|
+
require 'contributions/string_utils'
|
3
|
+
|
4
|
+
context "StringUtils" do
|
5
|
+
helper(:string) { "1b90c5e CONTRIBUTIONS_SEPARATOR 2011-09-13 07:49:43 -0500 CONTRIBUTIONS_SEPARATOR remove mention of deprecated exists from README CONTRIBUTIONS_SEPARATOR CONTRIBUTIONS_ENDING \n1620141 CONTRIBUTIONS_SEPARATOR 2011-12-16 19:26:58 -0500 CONTRIBUTIONS_SEPARATOR adjust input boxes to be same height as buttons CONTRIBUTIONS_SEPARATOR There was this weird thing happening where the inline submit button on a\nform would be like twice as tall as the text input area (notably on the\nuser_search form, but in a few other places it looked weird). This\nmakes all those a standard height (the same as the default Twitter\nbutton height). CONTRIBUTIONS_ENDING \n\n" }
|
6
|
+
helper(:ending) { " CONTRIBUTIONS_ENDING " }
|
7
|
+
helper(:separator) { " CONTRIBUTIONS_SEPARATOR " }
|
8
|
+
|
9
|
+
context ".split!" do
|
10
|
+
context "splits the string in place" do
|
11
|
+
setup { Contributions::StringUtils.split!(string, ending) }
|
12
|
+
|
13
|
+
asserts_topic.size 3
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context ".remove_empty throws out any 'blank' arrays" do
|
18
|
+
setup { Contributions::StringUtils.remove_empty([['s'], ['', 's'], [''], ["\n\n"]]) }
|
19
|
+
asserts_topic.size 2
|
20
|
+
end
|
21
|
+
|
22
|
+
context ".zip_to_hash" do
|
23
|
+
context "creates a hash with the first array as the key" do
|
24
|
+
setup { Contributions::StringUtils.zip_to_hash([:k, :v, :e], ['key', 'value', 'empty']) }
|
25
|
+
asserts_topic.equals Hash[:k => 'key', :v => 'value', :e => 'empty']
|
26
|
+
end
|
27
|
+
|
28
|
+
context "creates a hash with the first array as the key and '' for any empty values" do
|
29
|
+
setup { Contributions::StringUtils.zip_to_hash([:k, :v, :e], ['key']) }
|
30
|
+
asserts_topic.equals Hash[:k => 'key', :v => '', :e => '']
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context ".string_to_hash turns a hash of commit info into a useful hash" do
|
35
|
+
setup do
|
36
|
+
one = "1b90c5e CONTRIBUTIONS_SEPARATOR 2011-09-13 07:49:43 -0500 CONTRIBUTIONS_SEPARATOR remove mention of deprecated exists from README CONTRIBUTIONS_SEPARATOR CONTRIBUTIONS_ENDING \n"
|
37
|
+
two = "7201941 CONTRIBUTIONS_SEPARATOR 2011-09-13 07:48:53 -0500 CONTRIBUTIONS_SEPARATOR add deprecation warning to exists macro CONTRIBUTIONS_SEPARATOR CONTRIBUTIONS_ENDING \n"
|
38
|
+
three = "1620141 CONTRIBUTIONS_SEPARATOR 2011-12-16 19:26:58 -0500 CONTRIBUTIONS_SEPARATOR adjust input boxes to be same height as buttons CONTRIBUTIONS_SEPARATOR There was this weird thing happening where the inline submit button on a\nform would be like twice as tall as the text input area (notably on the\nuser_search form, but in a few other places it looked weird). This\nmakes all those a standard height (the same as the default Twitter\nbutton height). CONTRIBUTIONS_ENDING \n\n"
|
39
|
+
Contributions::StringUtils.string_to_hash([one, two, three].join, [:sha, :date, :subject, :body], separator, ending)
|
40
|
+
end
|
41
|
+
|
42
|
+
asserts_topic.equals [{:sha => "1b90c5e",
|
43
|
+
:date => "2011-09-13",
|
44
|
+
:subject => "remove mention of deprecated exists from README",
|
45
|
+
:body => ''},
|
46
|
+
{:sha=>"7201941",
|
47
|
+
:date=>"2011-09-13",
|
48
|
+
:subject=>"add deprecation warning to exists macro",
|
49
|
+
:body=>""},
|
50
|
+
{:sha=>"1620141",
|
51
|
+
:date=>"2011-12-16",
|
52
|
+
:subject=>"adjust input boxes to be same height as buttons",
|
53
|
+
:body=>"There was this weird thing happening where the inline submit button on a\nform would be like twice as tall as the text input area (notably on the\nuser_search form, but in a few other places it looked weird). This\nmakes all those a standard height (the same as the default Twitter\nbutton height)."}]
|
54
|
+
end
|
55
|
+
|
56
|
+
context ".short_dates gives you cleaner dates" do
|
57
|
+
setup { Contributions::StringUtils.short_dates(Hash[:sha=>"7201941", :date=>"2011-09-13 07:48:53 -0500", :subject=>"add deprecation warning to exists macro", :body=>""]) }
|
58
|
+
|
59
|
+
asserts_topic.equals Hash[:sha=>"7201941", :date=>"2011-09-13", :subject=>"add deprecation warning to exists macro", :body=>""]
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
data/test/teststrap.rb
ADDED
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: contributions
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Charlie Tanksley
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json
|
16
|
+
requirement: &70340630508160 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70340630508160
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: riot
|
27
|
+
requirement: &70340630505820 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70340630505820
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rake
|
38
|
+
requirement: &70340630514680 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70340630514680
|
47
|
+
description: Gather your contributions to OSS projects (hosted on github!).
|
48
|
+
email:
|
49
|
+
- charlie.tanksley@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- .travis.yml
|
56
|
+
- Gemfile
|
57
|
+
- LICENSE
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- TODO.markdown
|
61
|
+
- contributions.gemspec
|
62
|
+
- lib/contributions.rb
|
63
|
+
- lib/contributions/contributions.rb
|
64
|
+
- lib/contributions/git.rb
|
65
|
+
- lib/contributions/github_api.rb
|
66
|
+
- lib/contributions/repository_list.rb
|
67
|
+
- lib/contributions/string_utils.rb
|
68
|
+
- lib/contributions/version.rb
|
69
|
+
- test/contributions_integration_test.rb
|
70
|
+
- test/contributions_test.rb
|
71
|
+
- test/git_test.rb
|
72
|
+
- test/github_api_test.rb
|
73
|
+
- test/repository_list_test.rb
|
74
|
+
- test/string_utils_test.rb
|
75
|
+
- test/teststrap.rb
|
76
|
+
homepage: ''
|
77
|
+
licenses: []
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.8.11
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: A gem to find all your contributions to OSS projects on github and make that
|
100
|
+
information easy to access.
|
101
|
+
test_files:
|
102
|
+
- test/contributions_integration_test.rb
|
103
|
+
- test/contributions_test.rb
|
104
|
+
- test/git_test.rb
|
105
|
+
- test/github_api_test.rb
|
106
|
+
- test/repository_list_test.rb
|
107
|
+
- test/string_utils_test.rb
|
108
|
+
- test/teststrap.rb
|