gem_fresh 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 563e8d2d986b8d974e4fcbfb1fb3c09e9a0cc125
4
+ data.tar.gz: 310303c1f1d099669050b7d619c43681577e2da8
5
+ SHA512:
6
+ metadata.gz: ef9184f6a7ef14df71c550eb9f9a738f33a64074f4b2c655dbb054d32e5a02fdf1a2c3fcfd62b10167a55862384491a42d40a80802cf88e768037005202c8d39
7
+ data.tar.gz: 3aee268d62e854ac6ec8db896933894ee0c2c8efbcbb5150801da95a56dafb2eab8597ff3cb0a66366e8d1ea3de12e23e67a6ee8cae97cba21e8c9a3658af784
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gemfresh.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Anne Geiersbach
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # GemFresh
2
+
3
+ The purpose of GemFresh is to expose useful information about how outdated your application gems are.
4
+
5
+ ## Setup
6
+
7
+ Create a file called GemFresh.rb in the root directory of your application. Fill it out like this:
8
+
9
+ # Any gems that you put in the Gemfile should also be listed here.
10
+ # The rake metrics:outdated_gems task calculates which gems are
11
+ # outdated and then combines that information with the information
12
+ # listed here about a particular gem's reach in the application code.
13
+ #
14
+ GemFresh::Config.configure do |gems|
15
+
16
+ # Updating these gems could require you to make large, system-wide changes
17
+ # to the application code.
18
+ gems.with_system_wide_impact %w(
19
+ resque
20
+ rspec
21
+ ...
22
+ )
23
+
24
+ # Updating these gems could require you to make some changes to small
25
+ # sections of the application.
26
+ gems.with_local_impact %w(
27
+ fog
28
+ tabulous
29
+ ...
30
+ )
31
+
32
+ # When updating these gems, you barely have to touch any code at all.
33
+ gems.with_minimal_impact %w(
34
+ airbrake
35
+ bullet
36
+ ...
37
+ )
38
+
39
+ # We ignore these since we are in complete control of their update cycles.
40
+ gems.that_are_private %w(
41
+ dmc_server_admin
42
+ job_state
43
+ ...
44
+ )
45
+
46
+ end
47
+
48
+ Then create a rake task like this:
49
+
50
+ namespace :metrics do
51
+ desc "display outdated gem version metrics"
52
+ task :outdated_gems do
53
+ GemFresh::Reporter.new.report
54
+ end
55
+ end
56
+
57
+
58
+ ## Usage
59
+
60
+ See information on your outdated gems by running the rake task:
61
+
62
+ rake metrics:outdated_gems
63
+
64
+ This combines information from `bundle outdated` with the information in GemFresh.rb to give a weighted view as to how outdated your third-party Ruby code is and how much it matters.
65
+
66
+ Whenever you add a gem to your Gemfile, add it to GemFresh.rb so that the rake task knows how important the gem is.
67
+
68
+ Gems are assigned points. The more central a gem is, and the more outdated it is, the higher the points. You can think of the points as a "bounty" on the gem, telling you how badly it needs to be updated.
69
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/gemfresh.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'gem_fresh/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gem_fresh"
8
+ spec.version = GemFresh::VERSION
9
+ spec.authors = ["Anne Geiersbach"]
10
+ spec.email = ["annasazi@gmail.com"]
11
+
12
+ spec.summary = %q{Quantifying the freshness of your gems.}
13
+ spec.homepage = "https://github.com/dmcouncil/gem_fresh"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.9"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
@@ -0,0 +1,67 @@
1
+ module GemFresh
2
+ class Calculator
3
+
4
+ # If a gem is behind by a major version, it's worth more points
5
+ # than a minor version.
6
+ POINTS_FOR_MAJOR = 100
7
+ POINTS_FOR_MINOR = 10
8
+ POINTS_FOR_PATCH = 1
9
+
10
+ # Point multipliers based on how central the gem is to the application code.
11
+ MINIMAL_MULTIPLIER = 0.1
12
+ LOCAL_MULTIPLIER = 1
13
+ SYSTEM_MULTIPLIER = 10
14
+ FRAMEWORK_MULTIPLIER = 100
15
+
16
+ def initialize
17
+ @freshness_scores = {}
18
+ end
19
+
20
+ def calculate!
21
+ @gem_freshness_info = GemFresh::Outdated.new.gem_info
22
+ calculate_freshness_scores_for_each_gem
23
+ end
24
+
25
+ def total_score
26
+ @freshness_scores.values.sum.round(1)
27
+ end
28
+
29
+ def all_scores
30
+ @freshness_scores
31
+ end
32
+
33
+ private
34
+
35
+ def calculate_freshness_scores_for_each_gem
36
+ calculate_for_gems(['rails'], FRAMEWORK_MULTIPLIER)
37
+ calculate_for_gems(Config.config.system_wide_gems, SYSTEM_MULTIPLIER)
38
+ calculate_for_gems(Config.config.local_gems, LOCAL_MULTIPLIER)
39
+ calculate_for_gems(Config.config.minimal_gems, MINIMAL_MULTIPLIER)
40
+ end
41
+
42
+ def calculate_for_gems(gems, multiplier)
43
+ gems.each do |gem_name|
44
+ @freshness_scores[gem_name] =
45
+ (calculate_freshness_score_for(gem_name) * multiplier).round(1)
46
+ end
47
+ end
48
+
49
+ def calculate_freshness_score_for(gem_name)
50
+ info = @gem_freshness_info[gem_name]
51
+ return 0 if info.nil? # no outdated info means it's fresh
52
+ major_diff = (info[:available_version].major - info[:current_version].major)
53
+ minor_diff = (info[:available_version].minor - info[:current_version].minor)
54
+ patch_diff = (info[:available_version].patch - info[:current_version].patch)
55
+ score = 0
56
+ if major_diff > 0
57
+ score += major_diff * POINTS_FOR_MAJOR
58
+ elsif minor_diff > 0
59
+ score += minor_diff * POINTS_FOR_MINOR
60
+ elsif patch_diff > 0
61
+ score += patch_diff * POINTS_FOR_PATCH
62
+ end
63
+ score
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,52 @@
1
+ module GemFresh
2
+ class Config
3
+
4
+ attr_reader :system_wide_gems, :local_gems, :minimal_gems, :private_gems
5
+
6
+ def self.configure(&block)
7
+ @@config ||= Config.new
8
+ block.call(@@config)
9
+ end
10
+
11
+ def self.config
12
+ @@config ||= begin
13
+ config = Config.new
14
+ config.with_system_wide_impact([])
15
+ config.with_local_impact([])
16
+ config.with_minimal_impact([])
17
+ config.that_are_private([])
18
+ config
19
+ end
20
+ end
21
+
22
+ def with_system_wide_impact(gems)
23
+ @system_wide_gems = clean_gems(gems)
24
+ end
25
+
26
+ def with_local_impact(gems)
27
+ @local_gems = clean_gems(gems)
28
+ end
29
+
30
+ def with_minimal_impact(gems)
31
+ @minimal_gems = clean_gems(gems)
32
+ end
33
+
34
+ def that_are_private(gems)
35
+ @private_gems = clean_gems(gems)
36
+ end
37
+
38
+ def all_gems
39
+ @system_wide_gems + @local_gems + @minimal_gems + @private_gems
40
+ end
41
+
42
+ private
43
+
44
+ def clean_gems(gems)
45
+ if gems.include?('rails')
46
+ raise "Do not explicitly specify the rails gem in Gemfresh.rb." #TODO: why not?
47
+ end
48
+ gems
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ module GemFresh
2
+ class GemVersion
3
+ attr_reader :major, :minor, :patch
4
+
5
+ # version_string is usually formatted like 7.6.2, but could be
6
+ # 7.6.2.11, 7.6, or even 7.6.2.beta. We just care about the first
7
+ # three numbers
8
+ def initialize(version_string)
9
+ @major, @minor, @patch = version_string.split('.').map(&:to_i)
10
+ @patch ||= 0
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,48 @@
1
+ module GemFresh
2
+ class Missing
3
+
4
+ def initialize
5
+ parse_gemfile
6
+ end
7
+
8
+ def check_for_missing_gems!
9
+ if missing_gems.any?
10
+ message = "The following gems are in your Gemfile but not in your Gemfresh.rb file:\n"
11
+ missing_gems.each do |gem_name|
12
+ message << " #{gem_name}\n"
13
+ end
14
+ raise message
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def missing_gems
21
+ missing_gems = []
22
+ gems_from_gemfresh = gemfresh.all_gems
23
+ gem_names.each do |gem_from_gemfile|
24
+ if not gems_from_gemfresh.include?(gem_from_gemfile)
25
+ missing_gems << gem_from_gemfile
26
+ end
27
+ end
28
+ missing_gems.reject!{|g| g == 'rails'}
29
+ missing_gems
30
+ end
31
+
32
+ def gem_names
33
+ @gem_lines.map do |gem_line|
34
+ gem_line.split.second.gsub(/[^A-Za-z0-9\-_]/, '')
35
+ end
36
+ end
37
+
38
+ #TODO: we shouldn't be so tied to Rails in future
39
+ def parse_gemfile
40
+ gemfile = File.join(Rails.root, 'Gemfile')
41
+ @gem_lines = IO.readlines(gemfile).select{|line| line =~ /\A\s*gem/ }
42
+ end
43
+
44
+ def gemfresh
45
+ GemFresh::Config.config
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,48 @@
1
+ module GemFresh
2
+ class Outdated
3
+
4
+ attr_reader :gem_info
5
+
6
+ def initialize
7
+ figure_out_outdated_gems
8
+ end
9
+
10
+ private
11
+
12
+ def figure_out_outdated_gems
13
+ @gem_info ={}
14
+ raw_gem_info_from_bundler.each do |line|
15
+ #
16
+ # Sample lines from `bundle outdated` look like this:
17
+ #
18
+ # * airbrake (4.1.0 > 3.1.3)
19
+ # * annotate (2.6.5 > 2.6.0.beta2) Gemfile specifies "= 2.6.0.beta2"
20
+ # * bootstrap-multiselect-rails (0.9.5 > 0.0.4)
21
+ # * byebug (3.5.1 > 2.5.0)
22
+ # * enum_field (0.2.0 bff7873 > 0.2.0)
23
+ # or this:
24
+ # * responders (newest 2.1.0, installed 1.1.2) in group "default"
25
+ # * resque-retry (newest 1.4.0, installed 1.3.2) in group "default"
26
+ # * resque-status (newest 0.5.0, installed 0.4.1, requested = 0.4.1) in group "default"
27
+ # * rspec (newest 3.3.0, installed 3.2.0, requested ~> 3.2.0) in groups "development, test"
28
+ #
29
+ words = line.split
30
+ words_that_contain_version_numbers = words.select {|word| word =~ /\d+\.\d+/ }
31
+ version_numbers = words_that_contain_version_numbers.map {|word| word.gsub(/[\(\),]/, '') }
32
+ line =~ /\A\s*\*\s+(\S+)/
33
+ gem_name = $1
34
+ @gem_info[gem_name] = { available_version: GemVersion.new(version_numbers[0]),
35
+ current_version: GemVersion.new(version_numbers[1]) }
36
+ end
37
+ end
38
+
39
+ def raw_gem_info_from_bundler
40
+ output = %x{ bundle outdated }
41
+ just_the_gem_lines = output.split("\n").map(&:strip).select do |line|
42
+ line =~ /\A\s*\*\s+\w/
43
+ end
44
+ just_the_gem_lines
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ require 'gem_fresh'
2
+ require 'rails'
3
+
4
+ module GemFresh
5
+ class Railtie < Rails::Railtie
6
+ railtie_name :gem_fresh
7
+
8
+ puts "Woohoo, a GemFresh Railtie"
9
+ puts "env? #{Rails.env}"
10
+ puts GemFresh::Config.config
11
+
12
+ rake_tasks do
13
+ namespace :gem_fresh do
14
+ desc "outdated"
15
+ task :outdated => :environment do
16
+ GemFresh::Reporter.new.report
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ module GemFresh
2
+ class Reporter
3
+
4
+ def initialize
5
+ Missing.new.check_for_missing_gems!
6
+ @calculator = Calculator.new
7
+ @calculator.calculate!
8
+ end
9
+
10
+ def report
11
+ puts
12
+ puts "A bounty is calculated for each gem based on how outdated the gem"
13
+ puts "is and the impact the gem has on the application code. Foundational"
14
+ puts "gems like RSpec get a higher bounty, simple add-on tools like bullet"
15
+ puts "get a lower bounty. See Gemfresh.rb for details."
16
+ puts
17
+ puts "The total bounty is #{@calculator.total_score} points."
18
+ puts
19
+ puts "These are the outdated gems, sorted by highest bounty:"
20
+ puts
21
+ sorted_scores = @calculator.all_scores.sort_by{|gem_name, score| [-score, gem_name]}
22
+ sorted_scores.each do |gem_name, score|
23
+ name = " #{gem_name.ljust(25)[0..24]} "
24
+ if score >= 1000
25
+ puts "#{name} #{score}"
26
+ elsif score > 0
27
+ puts "#{name} #{score}"
28
+ else
29
+ puts "#{name} up to date"
30
+ end
31
+ end
32
+ puts
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module GemFresh
2
+ VERSION = "0.1.0"
3
+ end
data/lib/gem_fresh.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "gem_fresh/version"
2
+ require "gem_fresh/calculator"
3
+ require "gem_fresh/config"
4
+ require "gem_fresh/gem_version"
5
+ require "gem_fresh/missing"
6
+ require "gem_fresh/outdated"
7
+ require "gem_fresh/railtie" if defined?(Rails)
8
+ require "gem_fresh/reporter"
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gem_fresh
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Anne Geiersbach
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-02-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description:
42
+ email:
43
+ - annasazi@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - .rspec
50
+ - .travis.yml
51
+ - Gemfile
52
+ - LICENSE.txt
53
+ - README.md
54
+ - Rakefile
55
+ - gemfresh.gemspec
56
+ - lib/gem_fresh.rb
57
+ - lib/gem_fresh/calculator.rb
58
+ - lib/gem_fresh/config.rb
59
+ - lib/gem_fresh/gem_version.rb
60
+ - lib/gem_fresh/missing.rb
61
+ - lib/gem_fresh/outdated.rb
62
+ - lib/gem_fresh/railtie.rb
63
+ - lib/gem_fresh/reporter.rb
64
+ - lib/gem_fresh/version.rb
65
+ homepage: https://github.com/dmcouncil/gem_fresh
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.0.14
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Quantifying the freshness of your gems.
89
+ test_files: []