contributors_stats 1.0.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.
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