contributors_stats 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE.md +14 -0
  6. data/README.md +43 -0
  7. data/Rakefile +28 -0
  8. data/contributors_stats.gemspec +24 -0
  9. data/lib/contributors_stats.rb +28 -0
  10. data/lib/contributors_stats/base.rb +29 -0
  11. data/lib/contributors_stats/calculator.rb +50 -0
  12. data/lib/contributors_stats/formatter.rb +57 -0
  13. data/lib/contributors_stats/json_helper.rb +49 -0
  14. data/lib/contributors_stats/reader.rb +52 -0
  15. data/lib/contributors_stats/version.rb +4 -0
  16. data/lib/example_data.rb +58 -0
  17. data/lib/plugins/contributors_stats/formatter/html.rb +26 -0
  18. data/lib/plugins/contributors_stats/reader/gh_org.rb +21 -0
  19. data/lib/plugins/contributors_stats/reader/gh_repo.rb +19 -0
  20. data/lib/plugins/contributors_stats/updater/html.rb +36 -0
  21. data/lib/plugins/contributors_stats/user_data/fetch.rb +16 -0
  22. data/lib/plugins/contributors_stats/user_data/simple.rb +23 -0
  23. data/test/contributors_stats/base_test.rb +36 -0
  24. data/test/contributors_stats/calculator_test.rb +70 -0
  25. data/test/contributors_stats/formatter_test.rb +77 -0
  26. data/test/contributors_stats/json_helper_test.rb +76 -0
  27. data/test/contributors_stats/reader_test.rb +59 -0
  28. data/test/contributors_stats_test.rb +13 -0
  29. data/test/fixtures-gh/orgs/railsinstaller/repos.json +269 -0
  30. data/test/fixtures-gh/repos/mpapis/rubygems-bundler/contributors.json +173 -0
  31. data/test/fixtures-gh/repos/railsinstaller/railsinstaller-nix/contributors.json +40 -0
  32. data/test/fixtures-gh/repos/railsinstaller/railsinstaller-windows/contributors.json +116 -0
  33. data/test/fixtures-gh/repos/railsinstaller/website/contributors.json +192 -0
  34. data/test/fixtures-gh/repos/rvm/pluginator/contributors.json +40 -0
  35. data/test/fixtures-gh/users/acco.json +31 -0
  36. data/test/fixtures-gh/users/alexch.json +31 -0
  37. data/test/fixtures-gh/users/biow0lf.json +31 -0
  38. data/test/fixtures-gh/users/drnic.json +31 -0
  39. data/test/fixtures-gh/users/ebertech.json +31 -0
  40. data/test/fixtures-gh/users/edwardchiu38.json +31 -0
  41. data/test/fixtures-gh/users/emachnic.json +31 -0
  42. data/test/fixtures-gh/users/envygeeks.json +31 -0
  43. data/test/fixtures-gh/users/gpxl.json +31 -0
  44. data/test/fixtures-gh/users/jc00ke.json +31 -0
  45. data/test/fixtures-gh/users/joshbuddy.json +31 -0
  46. data/test/fixtures-gh/users/luigidr.json +31 -0
  47. data/test/fixtures-gh/users/luislavena.json +31 -0
  48. data/test/fixtures-gh/users/metaskills.json +31 -0
  49. data/test/fixtures-gh/users/mpapis.json +31 -0
  50. data/test/fixtures-gh/users/parndt.json +31 -0
  51. data/test/fixtures-gh/users/pw.json +31 -0
  52. data/test/fixtures-gh/users/tjouan.json +31 -0
  53. data/test/fixtures-gh/users/veganstraightedge.json +31 -0
  54. data/test/fixtures-gh/users/wayneeseguin.json +31 -0
  55. data/test/fixtures-gh/users/whitequark.json +31 -0
  56. data/test/plugins/formatter/html_test.rb +28 -0
  57. data/test/plugins/reader/gh_org_test.rb +32 -0
  58. data/test/plugins/reader/gh_repo_test.rb +31 -0
  59. data/test/plugins/updater/html_test.rb +55 -0
  60. data/test/plugins/user_data/fetch_test.rb +16 -0
  61. data/test/plugins/user_data/simple_test.rb +18 -0
  62. data/test/test_helper.rb +27 -0
  63. metadata +229 -0
@@ -0,0 +1,26 @@
1
+ class ContributorsStats::Formatter
2
+
3
+ # generate html for a contributor, by default it will create a link to profile with contributor avatar
4
+ class Html
5
+ # default template building link to profile with contributor avatar
6
+ DEFAULT_TEMPLATE = %q{%Q{<a href="#{data['html_url']}" title="#{login} - #{data['contributions']}"><img src="#{data['avatar_url']}" alt="#{login} - #{data['contributions']}"/></a>}}
7
+
8
+ # access to the template used to generate content
9
+ attr_accessor :template
10
+
11
+ # create the generator object
12
+ # @option template [String] the template to use, if not given default will be used
13
+ def initialize(options = {})
14
+ @template = options[:template] || DEFAULT_TEMPLATE
15
+ end
16
+
17
+ # format user data using template
18
+ # @param login [String] user name
19
+ # @param data [Hash] user data
20
+ # @return [String]
21
+ def format(login, data)
22
+ eval(template)
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ require 'contributors_stats/json_helper'
2
+
3
+ class ContributorsStats::Reader
4
+
5
+ # Plugin to load contributions from Github organization
6
+ class GhOrg
7
+ extend ContributorsStats::JsonHelper
8
+
9
+ # load contributions for Github organization
10
+ # param name [String] name of the organization to load
11
+ # return [Array] loaded conributors data
12
+ def self.load(name)
13
+ load_json(url_builder("orgs/#{name}/repos")).map{ |repo|
14
+ data = load_json(repo['contributors_url'])
15
+ yield(data, "#{name}/#{repo['name']}") if block_given?
16
+ data
17
+ }.inject(&:+)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ require 'contributors_stats/json_helper'
2
+
3
+ class ContributorsStats::Reader
4
+
5
+ # Plugin to load contributions from Github repository
6
+ class GhRepo
7
+ extend ContributorsStats::JsonHelper
8
+
9
+ # load contributions for Github repository
10
+ # param name [String] name of the repository to load
11
+ # return [Array] loaded conributors data
12
+ def self.load(name)
13
+ data = load_json(url_builder("repos/#{name}/contributors"))
14
+ yield(data, name) if block_given?
15
+ data
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,36 @@
1
+ module ContributorsStats::Updater
2
+
3
+ # Update a target file with content
4
+ class Html
5
+ # Default search pattern
6
+ DEFAULT_SEARCH = /<span class="contributors">.*?<\/span>/m
7
+ # Default replace template
8
+ DEFAULT_REPLACE = %q{%Q{<span class="contributors">\n#{replace_content.join("\n")}\n</span>}}
9
+
10
+ attr_accessor :search, :replace
11
+ attr_reader :file_content, :replace_content
12
+
13
+ # check if given file is supported by this updater
14
+ def self.handles?(file)
15
+ %w{ .html .html.erb }.any?{|ext| file.end_with?(ext) }
16
+ end
17
+
18
+ # perform the content update
19
+ def self.update(file_content, replace_content, options = {})
20
+ new(file_content, replace_content, options).to_s
21
+ end
22
+
23
+ # set initial parameters, set default options
24
+ def initialize(file_content, replace_content, options = {})
25
+ @search = options[:search] || DEFAULT_SEARCH
26
+ @replace = options[:replace] || DEFAULT_REPLACE
27
+ @file_content = file_content
28
+ @replace_content = replace_content
29
+ end
30
+
31
+ # perform the replace operation
32
+ def to_s
33
+ file_content.sub( search, eval(replace) )
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ require 'contributors_stats/json_helper'
2
+
3
+ module ContributorsStats::UserData
4
+
5
+ # fetch user data from github
6
+ class Fetch
7
+ extend ContributorsStats::JsonHelper
8
+
9
+ # fetch user data from github
10
+ def self.load(login, data, contributions)
11
+ user_data = load_json(data['url'])
12
+ user_data['contributions'] = contributions
13
+ user_data
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ module ContributorsStats::UserData
2
+
3
+ # fill in user data using minimal possible set of data
4
+ class Simple
5
+
6
+ # fill in user data using minimal possible set of data
7
+ def self.load(login, data, contributions)
8
+ {
9
+ 'avatar_url' => data['avatar_url'],
10
+ 'name' => login,
11
+ 'url' => data['url'],
12
+ 'html_url' => profile_url(login),
13
+ 'contributions' => contributions
14
+ }
15
+ end
16
+ # build github profile url for the given user name
17
+ # @param username [String]
18
+ # @return [String] github profile url
19
+ def self.profile_url(username)
20
+ "https://github.com/#{username}"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+ require 'contributors_stats/base'
3
+
4
+ describe ContributorsStats::Base do
5
+ before do
6
+ @tester = ContributorsStats::Base
7
+ end
8
+
9
+ it "logs messages" do
10
+ tester = @tester.new
11
+ tester.logger = ContributorsStats::ArrayLoggerExample.new
12
+ tester.send(:log, 'something')
13
+ tester.send(:log, 'nothing')
14
+ tester.logger.data.must_equal(['something', 'nothing'])
15
+ end
16
+
17
+ it "loads plugins" do
18
+ plugins = @tester.new.send(:plugins)
19
+ plugins.must_be_kind_of(Pluginator::Group)
20
+ plugins.types.size.must_be(:>=, 4)
21
+ plugins.first_class('formatter', 'html').wont_be_nil
22
+ end
23
+
24
+ it "filters options" do
25
+ example_input = {
26
+ :gh_org => "railsisntaller",
27
+ :nothing => "name",
28
+ }
29
+ example_output = {
30
+ :gh_org => "railsisntaller"
31
+ }
32
+ plugins = @tester.new(example_input).send(:filter_options, 'reader')
33
+ plugins.must_equal(example_output)
34
+ end
35
+
36
+ end
@@ -0,0 +1,70 @@
1
+ require 'test_helper'
2
+ require 'contributors_stats/calculator'
3
+
4
+ describe ContributorsStats::Calculator do
5
+ before do
6
+ @tester = ContributorsStats::Calculator
7
+ end
8
+
9
+ it "initializes user_data_type" do
10
+ tester = @tester.new(:user_data => :fetch)
11
+ tester.user_data_type.must_equal(:fetch)
12
+ end
13
+
14
+ it "calculates contributions" do
15
+ tester = @tester.new
16
+ tester.send(:contributions, [{"contributions" => 2}, {"contributions" => 3}]).must_equal(5)
17
+ end
18
+
19
+ it "calculates user_data" do
20
+ data = [
21
+ {"contributions" => 2, "url" => "users/mpapis.json", "avatar_url" => "mpapis-avatar.jpg"},
22
+ {"contributions" => 4},
23
+ ]
24
+ expected = {
25
+ "avatar_url"=>"mpapis-avatar.jpg",
26
+ "name"=>"mpapis",
27
+ "url"=>"users/mpapis.json",
28
+ "html_url"=>"https://github.com/mpapis",
29
+ "contributions"=>6
30
+ }
31
+ tester = @tester.new
32
+ tester.send(:configure_path, File.expand_path("../../fixtures-gh", __FILE__), ".json")
33
+ result = tester.send(:user_data, "mpapis", data)
34
+ result.must_equal(expected)
35
+ end
36
+
37
+ it "calculates data" do
38
+ raw_data = [
39
+ {"login" => "mpapis", "contributions" => 2, "url" => "users/mpapis.json", "avatar_url" => "mpapis-avatar.jpg"},
40
+ {"login" => "mpapis", "contributions" => 5},
41
+ ]
42
+ expected = [["mpapis", {
43
+ "avatar_url"=>"mpapis-avatar.jpg",
44
+ "name"=>"mpapis",
45
+ "url"=>"users/mpapis.json",
46
+ "html_url"=>"https://github.com/mpapis",
47
+ "contributions"=>7
48
+ } ]]
49
+ tester = @tester.new
50
+ tester.logger = ContributorsStats::ArrayLoggerExample.new
51
+ tester.send(:configure_path, File.expand_path("../../fixtures-gh", __FILE__), ".json")
52
+ tester.instance_variable_set(:@raw_data, raw_data)
53
+ tester.send(:calculated_data)
54
+ tester.data.must_equal(expected)
55
+ end
56
+
57
+ it "invokes formatter" do
58
+ data = [["mpapis", {
59
+ "avatar_url"=>"mpapis-avatar.jpg",
60
+ "name"=>"mpapis",
61
+ "url"=>"users/mpapis.json",
62
+ "html_url"=>"https://github.com/mpapis",
63
+ "contributions"=>7
64
+ }]]
65
+ tester = @tester.new
66
+ tester.instance_variable_set(:@data, data)
67
+ tester.format.kind_of?(ContributorsStats::Formatter).must_equal(true)
68
+ end
69
+
70
+ end
@@ -0,0 +1,77 @@
1
+ require 'test_helper'
2
+ require 'contributors_stats/formatter'
3
+
4
+ def with_temp_file(name: 'contributors_stats', extension: "")
5
+ file = Tempfile.new([name, extension])
6
+ begin
7
+ yield file if block_given?
8
+ ensure
9
+ file.close
10
+ file.unlink
11
+ end
12
+ end
13
+
14
+ describe ContributorsStats::Formatter do
15
+ before do
16
+ @tester = ContributorsStats::Formatter
17
+ @example_content = [
18
+ "<a href=\"https://github.com/mpapis\" title=\"mpapis - 7\"><img src=\"mpapis-avatar.jpg\" alt=\"mpapis - 7\"/></a>"
19
+ ]
20
+ end
21
+
22
+ it "loads plugins" do
23
+ plugins = @tester.allocate.send(:plugins)
24
+ plugins.must_be_kind_of(Pluginator::Group)
25
+ plugins.types.size.must_be(:>=, 4)
26
+ plugins.first_class('updater', 'html').wont_be_nil
27
+ end
28
+
29
+ it "invokes formatter" do
30
+ data = [["mpapis", {
31
+ "avatar_url"=>"mpapis-avatar.jpg",
32
+ "name"=>"mpapis",
33
+ "url"=>"users/mpapis.json",
34
+ "html_url"=>"https://github.com/mpapis",
35
+ "contributions"=>7
36
+ }]]
37
+ tester = @tester.new(data, :html)
38
+ tester.content.must_equal(@example_content)
39
+ end
40
+
41
+ it "saves to file" do
42
+ with_temp_file do |file|
43
+ tester = @tester.allocate
44
+ tester.instance_variable_set(:@content, @example_content)
45
+ tester.save(file.path)
46
+ file.readlines.must_equal(@example_content)
47
+ end
48
+ end
49
+
50
+ it "updates files - internal" do
51
+ with_temp_file do |file|
52
+ tester = @tester.allocate
53
+ File.open(file.path, "w") {|f| f.write("anything") }
54
+ tester.send(:update_file, file.path) do |text|
55
+ text.gsub(/a/, "b")
56
+ end
57
+ File.read(file.path).must_equal("bnything")
58
+ end
59
+ end
60
+
61
+ it "updates files - with plugins" do
62
+ content_start = %q{<body><span class="contributors">}
63
+ content_end = %q{</span></body>}
64
+ with_temp_file(extension: ".html") do |file|
65
+ tester = @tester.allocate
66
+ File.open(file.path, "w") {|f| f.write("#{content_start}#{content_end}\n") }
67
+ tester.instance_variable_set(:@content, @example_content)
68
+ tester.update(file.path)
69
+ File.read(file.path).must_equal(<<-EXPECTED)
70
+ #{content_start}
71
+ #{@example_content * "\n"}
72
+ #{content_end}
73
+ EXPECTED
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1,76 @@
1
+ require 'test_helper'
2
+ require 'contributors_stats/json_helper'
3
+
4
+ class ContributorsStats::JsonHelperTester
5
+ include ContributorsStats::JsonHelper
6
+ end
7
+
8
+ describe ContributorsStats::JsonHelper do
9
+ before do
10
+ @tester = ContributorsStats::JsonHelperTester.new
11
+ end
12
+
13
+ it "sets default prefix" do
14
+ @tester.path_prefix.must_equal("https://api.github.com/")
15
+ end
16
+
17
+ it "uses given prefix and ads /" do
18
+ @tester.send(:configure_path, "test_me", ".json")
19
+ @tester.path_prefix.must_equal("test_me/")
20
+ end
21
+
22
+ it "sets default suffix" do
23
+ @tester.path_suffix.must_equal("")
24
+ end
25
+
26
+ it "uses given suffix and ads ." do
27
+ @tester.send(:configure_path, "test_me", "json")
28
+ @tester.path_suffix.must_equal(".json")
29
+ end
30
+
31
+ it "builds url" do
32
+ @tester.url_builder("anything").must_equal("https://api.github.com/anything")
33
+ end
34
+
35
+ it "builds custom url" do
36
+ @tester.send(:configure_path, "test_me", "json")
37
+ @tester.url_builder("anything").must_equal("test_me/anything.json")
38
+ end
39
+
40
+ describe ".load_json" do
41
+ it "loads json from full path" do
42
+ example = File.expand_path("../../fixtures-gh/users/mpapis.json", __FILE__)
43
+ result = @tester.load_json(example)
44
+ result["id"].must_equal(48054)
45
+ result["gravatar_id"].must_equal("3ec52ed58eb92026d86e62c39bdb7589")
46
+ end
47
+
48
+ it "loads json from partial path" do
49
+ @tester.send(:configure_path, File.expand_path("../../fixtures-gh", __FILE__), "json")
50
+ result = @tester.load_json("users/mpapis.json")
51
+ result["id"].must_equal(48054)
52
+ result["gravatar_id"].must_equal("3ec52ed58eb92026d86e62c39bdb7589")
53
+ end
54
+
55
+ it "loads json from minimal path" do
56
+ @tester.send(:configure_path, File.expand_path("../../fixtures-gh", __FILE__), "json")
57
+ result = @tester.load_json("users/mpapis")
58
+ result["id"].must_equal(48054)
59
+ result["gravatar_id"].must_equal("3ec52ed58eb92026d86e62c39bdb7589")
60
+ end
61
+
62
+ it "does not loads json for missing URL" do
63
+ lambda {
64
+ @tester.load_json("https://api.github.com/mpapis/nonexisting/")
65
+ }.must_raise(OpenURI::HTTPError)
66
+ end
67
+
68
+ it "does not loads json for broken path" do
69
+ @tester.send(:configure_path, File.expand_path("../../fixtures-gh", __FILE__), "json")
70
+ lambda {
71
+ @tester.load_json("nonexisting")
72
+ }.must_raise(Errno::ENOENT)
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,59 @@
1
+ require 'test_helper'
2
+ require 'contributors_stats/reader'
3
+
4
+ describe ContributorsStats::Reader do
5
+ before do
6
+ @tester = ContributorsStats::Reader
7
+ end
8
+
9
+ it "loads data using single plugin single source" do
10
+ tester = @tester.new
11
+ tester.logger = ContributorsStats::ArrayLoggerExample.new
12
+ tester.send(:configure_path, File.expand_path("../../fixtures-gh", __FILE__), ".json")
13
+ tester.load(:gh_repo, "railsinstaller/website")
14
+ tester.raw_data.size.must_equal(10)
15
+ end
16
+
17
+ it "produces logs" do
18
+ tester = @tester.new
19
+ tester.logger = ContributorsStats::ArrayLoggerExample.new
20
+ tester.send(:configure_path, File.expand_path("../../fixtures-gh", __FILE__), ".json")
21
+ tester.load(:gh_org, "railsinstaller")
22
+ tester.logger.data.size.must_equal(3)
23
+ end
24
+
25
+ it "loads data using single plugin multiple sources" do
26
+ tester = @tester.new
27
+ tester.logger = ContributorsStats::ArrayLoggerExample.new
28
+ tester.send(:configure_path, File.expand_path("../../fixtures-gh", __FILE__), ".json")
29
+ tester.load(:gh_repo, "railsinstaller/website")
30
+ tester.load(:gh_repo, "railsinstaller/railsinstaller-nix")
31
+ tester.raw_data.size.must_equal(12)
32
+ end
33
+
34
+ it "loads data using single plugin array of sources (single) - privately" do
35
+ tester = @tester.new
36
+ tester.logger = ContributorsStats::ArrayLoggerExample.new
37
+ tester.send(:configure_path, File.expand_path("../../fixtures-gh", __FILE__), ".json")
38
+ tester.send(:parse_readers, gh_repo: "railsinstaller/website")
39
+ tester.raw_data.size.must_equal(10)
40
+ end
41
+
42
+ it "loads data using single plugin array of sources (multiple) - privately" do
43
+ tester = @tester.new
44
+ tester.logger = ContributorsStats::ArrayLoggerExample.new
45
+ tester.send(:configure_path, File.expand_path("../../fixtures-gh", __FILE__), ".json")
46
+ tester.send(:parse_readers, gh_repo: ["railsinstaller/website", "railsinstaller/railsinstaller-nix"])
47
+ tester.raw_data.size.must_equal(12)
48
+ end
49
+
50
+ it "loads data using single plugin array of sources" do
51
+ tester = @tester.new(
52
+ :configure_path => [File.expand_path("../../fixtures-gh", __FILE__), ".json"],
53
+ :logger => ContributorsStats::ArrayLoggerExample.new,
54
+ :gh_repo => "railsinstaller/railsinstaller-nix"
55
+ )
56
+ tester.raw_data.size.must_equal(2)
57
+ end
58
+
59
+ end