gem_fresh 0.1.0

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