pessimize 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ log
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ before_install: gem install bundler
3
+ rvm:
4
+ - 2.0.0
5
+ - 1.9.3
6
+ - 1.9.2
7
+ - jruby-1.7.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pessimize.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jon Cairns
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,57 @@
1
+ # Pessimize
2
+
3
+ [![Build Status](https://travis-ci.org/joonty/pessimize.png?branch=master)](https://travis-ci.org/joonty/pessimize)
4
+
5
+ ### Who is this for?
6
+ Anyone who works with a Gemfile, i.e. a project that uses [bundler][1].
7
+
8
+ ### What does it do?
9
+ Pessimize adds version numbers with the pessimistic constraint operator (`~>`, a.k.a. "spermy" operator) to all gems in your `Gemfile`.
10
+
11
+ ### Why?
12
+ You should be using `~> x.x.x` to limit the version numbers of your gems, otherwise `bundle update` could potentially break your application. Read the section on "why bundle update can be dangerous" for a more detailed description, or take a look at the [rubygems explanation][1].
13
+
14
+ ### But why a gem?
15
+
16
+ *I.e. why not just do it by hand?*
17
+
18
+ When you start building an application that uses bundler, you aren't yet sure which versions of gems will work together, so you probably won't specify the precise versions. You would normally only do this when the application and it's dependencies are in a fairly stable state. However, if you have more than a few gems, it's very tedious to collect the version numbers for each gem and add the pessimistic constraint operator to each line in the `Gemfile`.
19
+
20
+ Pessimize works out the version of each gem from `Gemfile.lock` then generates a new `Gemfile` with the added version constraints. But don't worry: it backs up the existing Gemfile before running, so you can get the original back if it goes wrong. (If it does go wrong, please submit an issue.)
21
+
22
+ ## Installation
23
+
24
+ You don't need to add it to your Gemfile - it's best kept as a system-wide gem. All you need to do is install it from the command line:
25
+
26
+ $ gem install pessimize
27
+
28
+ This installs the command line tool `pessimize`.
29
+
30
+ ## Usage
31
+
32
+ Change to a directory that contains a `Gemfile` and execute:
33
+
34
+ $ pessimize
35
+
36
+ This backs up the existing `Gemfile` and creates a new one with everything neatly organised and versioned.
37
+
38
+ And that's it!
39
+
40
+ ## Why `bundle update` can be dangerous
41
+
42
+ If you add gems to your Gemfile without specifying a version, bundler will attempt to get the latest stable version for that gem. When you first run `bundle install`, bundler will get and install the latest versions, then create a `Gemfile.lock` which specifies the versions used.
43
+
44
+ This is fine until someone runs `bundle update`. In this case, bundler will try to update each gem to the maximum possible version. If no constraints have been applied, that means that **major** versions can potentially be incremented. Gems have interdependencies with other gems, and if those gems haven't specified the version constraints then breakages could occur.
45
+
46
+ The pessimistic constraint operator will only allow the final number of the version string to increase. You can use this to only allow patch level upgrades, or minor (if you're feeling dangerous). This means that when you run `bundle update`, there's a limit on how far gems will update.
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create new Pull Request
55
+
56
+ [1]: http://gembundler.com
57
+ [2]: http://docs.rubygems.org/read/chapter/16#page74
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/pessimize ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pessimize'
3
+ require 'pessimize/shell'
4
+
5
+ Pessimize::Shell.new.run
@@ -0,0 +1,17 @@
1
+ module Pessimize
2
+ class Declaration
3
+ attr_reader :name, :args
4
+
5
+ def initialize(name, *args)
6
+ @name = name
7
+ @args = args
8
+ end
9
+
10
+ def to_code
11
+ s = ""
12
+ s << "#{name} "
13
+ s << args.map(&:inspect).join(", ")
14
+ s
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ module Pessimize
2
+ class DSL
3
+ def initialize(collector)
4
+ @collector = collector
5
+ @current_group = nil
6
+ end
7
+
8
+ def parse(definition)
9
+ instance_eval definition
10
+ end
11
+
12
+ def method_missing(name, *args)
13
+ collector.add_declaration(name.to_s, *args)
14
+ end
15
+
16
+ protected
17
+ attr_reader :collector
18
+ attr_accessor :current_group
19
+
20
+ def gem(*args)
21
+ if current_group
22
+ collector.add_grouped_gem(current_group, *args)
23
+ else
24
+ collector.add_gem(*args)
25
+ end
26
+ end
27
+
28
+ def group(name)
29
+ if block_given?
30
+ self.current_group = name
31
+ yield
32
+ self.current_group = nil
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ module Pessimize
2
+ class FileManager
3
+ def gemfile
4
+ 'Gemfile'
5
+ end
6
+
7
+ def gemfile_lock
8
+ 'Gemfile.lock'
9
+ end
10
+
11
+ def gemfile?
12
+ File.exists? gemfile
13
+ end
14
+
15
+ def gemfile_contents
16
+ File.read gemfile
17
+ end
18
+
19
+ def gemfile_lock?
20
+ File.exists? gemfile_lock
21
+ end
22
+
23
+ def backup_gemfile!
24
+ backup_file! gemfile
25
+ end
26
+
27
+ def backup_gemfile_lock!
28
+ backup_file! gemfile_lock
29
+ end
30
+
31
+ private
32
+ def backup_file!(file)
33
+ cmd = "cp #{file} #{file}.backup"
34
+ puts cmd
35
+ system cmd
36
+ $?.exitstatus == 0
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ module Pessimize
2
+ class Gem
3
+ attr_reader :name, :version, :options
4
+ attr_writer :version
5
+
6
+ def initialize(*args)
7
+ @name = args.shift
8
+ while arg = args.shift
9
+ if arg.is_a? Hash
10
+ @options = arg
11
+ else
12
+ @version = arg
13
+ end
14
+ end
15
+ end
16
+
17
+ def to_code
18
+ s = ""
19
+ s << %Q{gem "#{name}"}
20
+ s << %Q{, "#{version}"} if version
21
+ s << %Q{, #{options.inspect}} if options
22
+ s
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'gem'
2
+ require_relative 'declaration'
3
+
4
+ module Pessimize
5
+ class GemCollection
6
+ attr_reader :gems, :declarations
7
+
8
+ def initialize
9
+ @gems = Hash.new do |hash, missing|
10
+ hash[missing] = []
11
+ end
12
+ @declarations = []
13
+ end
14
+
15
+ def add_gem(*args)
16
+ add_grouped_gem(:global, *args)
17
+ end
18
+
19
+ def add_grouped_gem(group, *args)
20
+ self.gems[group] << Gem.new(*args)
21
+ end
22
+
23
+ def add_declaration(name, *args)
24
+ self.declarations << Declaration.new(name, *args)
25
+ end
26
+
27
+ def all
28
+ gems.values.flatten
29
+ end
30
+
31
+ protected
32
+ attr_writer :gems, :declarations
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ module Pessimize
2
+ class GemfileLockVersionParser
3
+ attr_reader :versions
4
+
5
+ def initialize
6
+ self.versions = {}
7
+ self.parse_enabled = false
8
+ end
9
+
10
+ def call(gemfile_lock_file)
11
+ gemfile_lock_file.each_line do |line|
12
+ if line.start_with? 'GEM'
13
+ self.parse_enabled = true
14
+ end
15
+ if parse_enabled
16
+ parse_line(line)
17
+ end
18
+ end
19
+ end
20
+
21
+ protected
22
+ attr_writer :versions
23
+ attr_accessor :parse_enabled
24
+
25
+ def parse_line(line)
26
+ if line =~ /^\s{4}[a-z0-9]/i
27
+ line.strip!
28
+ matches = /([^(]+)\([^0-9]*([^)]+)/.match(line)
29
+ if matches
30
+ self.versions[matches[1].strip] = matches[2]
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,69 @@
1
+ require 'pessimize/dsl'
2
+ require 'pessimize/gem_collection'
3
+ require 'pessimize/gemfile_lock_version_parser'
4
+
5
+ module Pessimize
6
+ class Pessimizer
7
+ def initialize(file_manager)
8
+ self.file_manager = file_manager
9
+ self.collection = GemCollection.new
10
+ self.dsl = DSL.new collection
11
+ self.lock_parser = GemfileLockVersionParser.new
12
+ end
13
+
14
+ def run
15
+ collect_gems_and_versions
16
+ update_gem_versions
17
+ write_new_gemfile
18
+ end
19
+
20
+ protected
21
+ attr_accessor :collection, :dsl, :lock_parser, :file_manager
22
+
23
+ def sep(num = 1)
24
+ "\n" * num
25
+ end
26
+
27
+ def collect_gems_and_versions
28
+ dsl.parse file_manager.gemfile_contents
29
+ puts "Collected #{collection.all.length} gems from #{file_manager.gemfile}"
30
+ lock_parser.call File.open(file_manager.gemfile_lock)
31
+ end
32
+
33
+ def update_gem_versions
34
+ puts "Updating gem versions with pessimistic operator (~>)"
35
+ collection.all.each do |gem|
36
+ if lock_parser.versions.has_key? gem.name
37
+ gem.version = "~> #{lock_parser.versions[gem.name]}"
38
+ end
39
+ end
40
+ end
41
+
42
+ def write_new_gemfile
43
+ File.delete(file_manager.gemfile)
44
+ File.open(file_manager.gemfile, 'w') do |f|
45
+ collection.declarations.each do |dec|
46
+ f.write(dec.to_code)
47
+ end
48
+ f.write sep(2)
49
+ gem_groups = collection.gems
50
+ global_gems = gem_groups[:global]
51
+ gem_groups.delete :global
52
+ gem_groups.each do |group, gems|
53
+ f.write("group :#{group} do#{sep}")
54
+ gems.each do |gem|
55
+ f.write(" " + gem.to_code + sep)
56
+ end
57
+ f.write("end" + sep(2))
58
+ end
59
+ if global_gems
60
+ global_gems.each do |gem|
61
+ f.write(gem.to_code + sep)
62
+ end
63
+ end
64
+ end
65
+ puts "Written new Gemfile"
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,49 @@
1
+ require 'pessimize/file_manager'
2
+ require 'pessimize/pessimizer'
3
+
4
+ module Pessimize
5
+ class Shell
6
+ def initialize
7
+ self.file_manager = FileManager.new
8
+ end
9
+
10
+ def run
11
+ verify_files
12
+ Pessimizer.new(file_manager).run
13
+ end
14
+
15
+ protected
16
+ attr_accessor :file_manager
17
+
18
+ def sep(num = 1)
19
+ "\n" * num
20
+ end
21
+
22
+ def verify_files
23
+ file_manager.gemfile? or exit_with 1, <<-ERR.strip
24
+ error: no Gemfile exists in the current directory, exiting
25
+ ERR
26
+
27
+ file_manager.gemfile_lock? or exit_with 2, <<-ERR.strip
28
+ error: no Gemfile.lock exists in the current directory, exiting
29
+ Please run `bundle install` before running pessimize
30
+ ERR
31
+
32
+ puts "Backing up Gemfile and Gemfile.lock"
33
+
34
+ file_manager.backup_gemfile! or exit_with 3, <<-ERR.strip
35
+ error: failed to backup existing Gemfile, exiting
36
+ ERR
37
+
38
+ file_manager.backup_gemfile_lock! or exit_with 4, <<-ERR.strip
39
+ error: failed to backup existing Gemfile.lock, exiting
40
+ ERR
41
+ puts ""
42
+ end
43
+
44
+ def exit_with(status, message)
45
+ $stderr.write message
46
+ exit status
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module Pessimize
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,11 @@
1
+ module Pessimize
2
+ class VersionMapper
3
+ def call(gems, versions)
4
+ gems.each do |gem|
5
+ if versions.has_key? gem.name
6
+ gem.version = "~> #{versions[gem.name]}"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
data/lib/pessimize.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "pessimize/version"
2
+
3
+ module Pessimize; end
data/pessimize.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pessimize/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "pessimize"
8
+ gem.version = Pessimize::VERSION
9
+ gem.authors = ["Jon Cairns"]
10
+ gem.email = ["jon@joncairns.com"]
11
+ gem.description = %q{Add the pessimistic constraint operator to all gems in your Gemfile, restricting the maximum update version.
12
+
13
+ This is for people who work with projects that use bundler, such as rails projects. The pessimistic constraint operator (~>) allows you to specify the maximum version that a gem can be updated, and reduces potential breakages when running `bundle update`. Pessimize automatically retrieves the current versions of your gems, then adds them to your Gemfile (so you don't have to do it by hand).}
14
+ gem.summary = %q{Add the pessimistic constraint operator to all gems in your Gemfile, restricting the maximum update version.}
15
+ gem.homepage = "https://github.com/joonty/pessimize"
16
+
17
+ gem.add_development_dependency 'rspec', '~> 2.13.0'
18
+ gem.add_development_dependency 'rake', '~> 10.0.3'
19
+
20
+ gem.files = `git ls-files`.split($/)
21
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
22
+ gem.test_files = gem.files.grep(%r{^spec/})
23
+ gem.require_paths = ["lib"]
24
+ end
@@ -0,0 +1,26 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pessimize (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.4)
10
+ rake (10.0.4)
11
+ rspec (2.13.0)
12
+ rspec-core (~> 2.13.0)
13
+ rspec-expectations (~> 2.13.0)
14
+ rspec-mocks (~> 2.13.0)
15
+ rspec-core (2.13.1)
16
+ rspec-expectations (2.13.0)
17
+ diff-lcs (>= 1.1.3, < 2.0)
18
+ rspec-mocks (2.13.1)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ pessimize!
25
+ rake (~> 10.0.3)
26
+ rspec (~> 2.13.0)