one9 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'rubygems' unless Object.const_defined?(:Gem)
3
+ require File.dirname(__FILE__) + "/lib/one9/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "one9"
7
+ s.version = One9::VERSION
8
+ s.authors = ["Gabriel Horner"]
9
+ s.email = "gabriel.horner@gmail.com"
10
+ s.homepage = "http://github.com/cldwalker/one9"
11
+ s.summary = "commandline tool to convert your 1.8 code to ruby 1.9.2"
12
+ s.description = "one9 is a commandline tool to help convert your ruby 1.8.7 code to 1.9.2. It works by spying on your tests and detecting 1.9 changes. Once your test suite finishes, one9 prints a report listing the exact locations of methods that have changed in 1.9. To make the transition even easier, one9 can open this list in an editor. So what's your excuse for not upgrading to 1.9.2? ;)"
13
+ s.required_rubygems_version = ">= 1.3.6"
14
+ s.rubyforge_project = 'tagaholic'
15
+ s.executables = ['one9']
16
+ s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc} ext/**/*.{rb,c} **/deps.rip]) + %w{Rakefile .gemspec}
17
+ s.files += Dir.glob('features/**/*.{rb,feature}')
18
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
19
+ s.add_dependency 'hirb', '>= 0.4.0'
20
+ s.add_development_dependency 'aruba', '~> 0.3.2'
21
+ s.license = 'MIT'
22
+ end
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,2 @@
1
+ == 0.1.0
2
+ * Initial release!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT LICENSE
2
+
3
+ Copyright (c) 2011 Gabriel Horner
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.rdoc ADDED
@@ -0,0 +1,105 @@
1
+ == Description
2
+ one9 is a commandline tool to help convert your ruby 1.8.7 code to 1.9.2. It works by spying on
3
+ your tests and detecting 1.9 changes. Once your test suite finishes, one9 prints a report listing
4
+ the exact locations of methods that have changed in 1.9. To make the transition even easier, one9
5
+ can open this list in an editor. So what's your excuse for not upgrading to 1.9.2? ;)
6
+
7
+ == Install
8
+
9
+ $ gem install one9
10
+
11
+ == Usage
12
+
13
+ Run your tests using ruby 1.8 in the root directory of a gem or Rails app:
14
+
15
+ # Runs `rake test` by default
16
+ $ one9 test
17
+
18
+ # To run any test command, just prefix it with one9 test:
19
+ # Run all cucumber specs
20
+ $ one9 test cucumber
21
+
22
+ # Only run model specs
23
+ $ one9 test rspec spec/models
24
+
25
+ After your tests, you'll see a report. Here's what it looks like when run on {boson}[http://github.com/cldwalker/boson]:
26
+
27
+ $ one9 test
28
+ ............................................................................................................................................................................................................................................................................................................
29
+ Finished in 0.704555 seconds.
30
+
31
+ 300 tests, 580 assertions, 0 failures, 0 errors
32
+
33
+ ** One9 Report **
34
+ +---------------------------------+-------+--------------------------------------------------------------+--------+--------------------------------------------------------------+
35
+ | method | count | message | type | lines |
36
+ +---------------------------------+-------+--------------------------------------------------------------+--------+--------------------------------------------------------------+
37
+ | Module#instance_methods | 5 | Returns array of symbols instead of array of strings | change | lib/boson/util.rb:39:in `detect',lib/boson/util.rb:40:in ... |
38
+ | Module#private_instance_methods | 2 | Returns array of symbols instead of array of strings | change | lib/boson/inspectors/argument_inspector.rb:23:in `scrape_... |
39
+ | Hash#to_s | 4 | An alias of #inspect instead of a spaceless join of the e... | change | lib/boson/loader.rb:87:in `initialize_library_module',lib... |
40
+ | Hash#select | 4 | Returns a hash instead of an association array | change | lib/boson/loader.rb:109:in `set_library_commands',lib/bos... |
41
+ +---------------------------------+-------+--------------------------------------------------------------+--------+--------------------------------------------------------------+
42
+ 4 rows in set
43
+
44
+ To edit these possible changes:
45
+
46
+ # Opens all changes
47
+ $ one9 edit
48
+
49
+ # Only open a subset of changes:
50
+ # Opens Hash changes
51
+ $ one9 edit Hash
52
+
53
+ # Only open Hash#to_s changes
54
+ $ one9 edit Hash#to_s
55
+
56
+ Currently this opens the quickfix list in vim. Patches for emacs and other editors welcome :)
57
+
58
+ If your editor isn't supported, you can still view the possible changes:
59
+
60
+ $ one9 files
61
+ +---------------------------------+---------------------------------------------------------------------+
62
+ | name | line |
63
+ +---------------------------------+---------------------------------------------------------------------+
64
+ | Module#instance_methods | lib/boson/util.rb:39:in `detect' |
65
+ | Module#instance_methods | lib/boson/util.rb:40:in `detect' |
66
+ | Module#instance_methods | lib/boson/loader.rb:101:in `check_for_method_conflicts' |
67
+ | Module#instance_methods | lib/boson/util.rb:44:in `detect' |
68
+ | Module#instance_methods | lib/boson/namespace.rb:28:in `boson_commands' |
69
+ ...
70
+
71
+ == Configure
72
+
73
+ one9 comes with a decent list of 1.9 changes and descriptions. However, if you'd like to add your
74
+ own changes, you can add them to your ~/.one9rc. For example:
75
+
76
+ # For methods that have changed in 1.9
77
+ change 'Class#instance_method', 'Some description'
78
+
79
+ # For methods that have been deleted in 1.9
80
+ delete 'Class.class_method', 'Some description'
81
+
82
+ For more examples, {see the defaults that one9
83
+ defines}[https://github.com/cldwalker/one9/blob/master/lib/one9/defaults.rb].
84
+
85
+ If I've missed an important 1.9 change, feel free to fork and pull.
86
+
87
+ == Goal
88
+ This gem aims to get the ruby community to seriously use 1.9.2 and to port as many of the 1.8 gems to
89
+ 1.9. Since this gem maintains a list of 1.9 changes, it could be used as a multi-purpose
90
+ tool. Some ideas for future commands:
91
+
92
+ * grep command to grep code for changed methods. Basically for porting test-deprived code.
93
+ * info command to explain a method's change in detail with examples and possible solutions
94
+ * rails command to port a rails app and all its gem dependencies.
95
+
96
+ If you're interested in implementing these or other such commands, please do contribute.
97
+
98
+ == Limitations
99
+ The following methods can't be spied on: Object#=~, String#[], String#to_a, String#each. For more,
100
+ see the {defaults file}[[https://github.com/cldwalker/one9/blob/master/lib/one9/defaults.rb].
101
+
102
+ == 1.9 Links
103
+ * http://eigenclass.org/hiki/Changes+in+Ruby+1.9
104
+ * http://svn.ruby-lang.org/repos/ruby/tags/v1_9_1_0/NEWS
105
+ * http://blog.grayproductions.net/articles/understanding_m17n
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ require 'rake'
2
+ require 'fileutils'
3
+
4
+ def gemspec
5
+ @gemspec ||= eval(File.read('.gemspec'), binding, '.gemspec')
6
+ end
7
+
8
+ desc "Build the gem"
9
+ task :gem=>:gemspec do
10
+ sh "gem build .gemspec"
11
+ FileUtils.mkdir_p 'pkg'
12
+ FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", 'pkg'
13
+ end
14
+
15
+ desc "Install the gem locally"
16
+ task :install => :gem do
17
+ sh %{gem install pkg/#{gemspec.name}-#{gemspec.version}}
18
+ end
19
+
20
+ desc "Generate the gemspec"
21
+ task :generate do
22
+ puts gemspec.to_ruby
23
+ end
24
+
25
+ desc "Validate the gemspec"
26
+ task :gemspec do
27
+ gemspec.validate
28
+ end
29
+
30
+ desc 'Run tests'
31
+ task :test do |t|
32
+ sh 'RUBYLIB=$PWD/lib:$RUBYLIB PATH=bin:$PATH cucumber'
33
+ end
34
+
35
+ task :default => :test
data/bin/one9 ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'one9/runner'
4
+ One9::Runner.run
data/deps.rip ADDED
@@ -0,0 +1 @@
1
+ hirb >=0.4.0
@@ -0,0 +1,34 @@
1
+ Feature: options, help and misc edge cases
2
+
3
+ Scenario Outline: Print help
4
+ When I run "<command>"
5
+ Then the output should contain "one9 [OPTIONS] COMMAND"
6
+
7
+ Examples:
8
+ | command |
9
+ | one9 |
10
+ | one9 -h |
11
+ | one9 --help |
12
+
13
+ Scenario Outline: Print version
14
+ When I run "<command>"
15
+ Then the output contains the current version
16
+
17
+ Examples:
18
+ | command |
19
+ | one9 -v |
20
+ | one9 --version |
21
+
22
+ Scenario: Print error for invalid option
23
+ When I run "one9 -z"
24
+ Then the stderr should contain "one9: invalid option `-z'"
25
+
26
+ Scenario: Print error for invalid command
27
+ When I run "one9 blah"
28
+ Then the output should match /^one9: Invalid command `blah'/
29
+ And the exit status should be 1
30
+
31
+ Scenario: Print error for unexpected error
32
+ When I run an invalid command
33
+ Then the stderr should contain "one9 error:"
34
+ And the stderr should contain "moooo"
@@ -0,0 +1,123 @@
1
+ Feature: Commands
2
+
3
+ Scenario Outline: Commands print help
4
+ When I run "one9 <command> -h"
5
+ Then the output should contain "one9 <usage>"
6
+
7
+ Examples:
8
+ | command | usage |
9
+ | test | test [COMMAND='rake test'] |
10
+ | list | list [QUERY] [-a\|--all] |
11
+ | edit | edit [QUERY] |
12
+ | changes | changes [QUERY] |
13
+ | lines | lines [QUERY] [-a\|--all] |
14
+ | quickfix | quickfix [QUERY] [-a\|--all] |
15
+
16
+ Scenario Outline: Commands print error for no report
17
+ Given I have no report
18
+ When I run "one9 <command>"
19
+ Then the stderr should contain "one9 has no report. `one9 test` your project first."
20
+ And the exit status should be 1
21
+
22
+ Examples:
23
+ | command |
24
+ | list |
25
+ | edit |
26
+ | lines |
27
+ | quickfix |
28
+
29
+ Scenario Outline: Commands with -a option print all changes
30
+ Given I have a report
31
+ When I run "one9 <command> -a"
32
+ Then the output should contain "Module#public_m"
33
+
34
+ Examples:
35
+ | command |
36
+ | list |
37
+ | quickfix |
38
+ | lines |
39
+
40
+ Scenario Outline: Commands with queries return correct results
41
+ Given I have a report
42
+ When I run "one9 <command> Hash"
43
+ Then the output should contain "Hash"
44
+ And the output should not contain "Module"
45
+
46
+ Examples:
47
+ | command |
48
+ | list |
49
+ | quickfix |
50
+ | lines |
51
+ | changes |
52
+
53
+ Scenario: edit command with unsupported editor
54
+ Given I have a report
55
+ And I have the editor "nano"
56
+ When I run "one9 edit"
57
+ Then the output should contain "No support for nano yet. Patches welcome :)"
58
+
59
+ Scenario: edit command with supported editor
60
+ Given I have a report
61
+ And I have the editor "vim"
62
+ When I run "one9 edit" which hangs
63
+ Then the output should contain ""
64
+
65
+ Scenario: test command with arguments
66
+ When I run "one9 test ruby -e 'puts'"
67
+ Then the output should contain "** One9 Report **"
68
+ And the output should not contain multiple reports
69
+
70
+ Scenario: test command with no arguments
71
+ Given a file named "Rakefile" with:
72
+ | task(:test) { sh %[ruby -e 'puts "OK"'] } |
73
+ When I run "one9 test"
74
+ Then the output should contain "** One9 Report **"
75
+ And the output should not contain multiple reports
76
+
77
+ Scenario: test command prints error when saving fails
78
+ Given I am unable to save my test
79
+ When I run "one9 test ruby -e 'puts'"
80
+ Then the stderr should contain "one9: Error while saving report"
81
+
82
+ Scenario: test command with invalid Rakefile prints errors
83
+ Given an empty file named "Rakefile"
84
+ When I run "one9 test"
85
+ Then the stderr should contain "** one9: Error occurred while testing **"
86
+
87
+ Scenario: changes command with rc file
88
+ Given I have a rc file
89
+ When I run "one9 changes"
90
+ Then the output contains all default methods
91
+ And the output should contain "Module#stub"
92
+
93
+ Scenario: changes command with no rc file
94
+ Given I have no rc file
95
+ When I run "one9 changes"
96
+ Then the output contains all default methods
97
+
98
+ Scenario: list command with valid data
99
+ Given I have a report
100
+ When I run "one9 list"
101
+ Then the output should contain "** One9 Report **"
102
+ And the output should contain "Hash#select"
103
+ And the output should contain "4 rows in set"
104
+
105
+ Scenario: list command with invalid data
106
+ Given I have an invalid report
107
+ When I run "one9 list"
108
+ Then the stderr should contain "one9 error: marshal"
109
+
110
+ Scenario: list command with no data
111
+ Given I have a report with no data
112
+ When I run "one9 list"
113
+ Then the output should contain "No 1.9 changes found"
114
+
115
+ Scenario: list command in a Rails project
116
+ Given I'm in a Rails environment
117
+ And I have a Rails report
118
+ When I run "one9 list"
119
+ Then the output should contain "** One9 Report **"
120
+ And the output should contain "app/models"
121
+ And the output should contain "config/"
122
+ And the output should contain "lib/"
123
+ And the output should not contain "vendor/"
@@ -0,0 +1,86 @@
1
+ After do
2
+ FileUtils.rm_rf One9.dir
3
+ end
4
+
5
+ Given /^I have no rc file$/ do
6
+ FileUtils.rm_f One9.rc
7
+ end
8
+
9
+ Given /^I have a rc file$/ do
10
+ FileUtils.mkdir_p One9.dir
11
+ File.open(One9.rc, 'w') {|f| f.write 'change "Module#stub", "stuuub"' }
12
+ end
13
+
14
+ Given /^I have no report$/ do
15
+ FileUtils.rm_f One9::Report.marshal_file
16
+ end
17
+
18
+ Given /^I have a report$/ do
19
+ FileUtils.mkdir_p One9.dir
20
+ stacks = {
21
+ "Module#instance_methods" => [['./lib/blah.rb:11']],
22
+ "Module#private_instance_methods" => [['./lib/blah.rb:21']],
23
+ "Hash#to_s" => [['./lib/blah.rb:31']],
24
+ "Hash#select" => [['./lib/blah.rb:41']],
25
+ "Module#public_methods" => [['./test/blah.rb:51']]
26
+ }
27
+ File.open(One9::Report.marshal_file, 'wb') {|f| f.write Marshal.dump([{}, stacks]) }
28
+ end
29
+
30
+ Given /^I have an invalid report$/ do
31
+ FileUtils.mkdir_p One9.dir
32
+ File.open(One9::Report.marshal_file, 'w') {|f| f.write '' }
33
+ end
34
+
35
+ Given /^I have a report with no data$/ do
36
+ FileUtils.mkdir_p One9.dir
37
+ File.open(One9::Report.marshal_file, 'wb') {|f| f.write Marshal.dump([{}, {}]) }
38
+ end
39
+
40
+ Given "I have a Rails report" do
41
+ FileUtils.mkdir_p One9.dir
42
+ stacks = {
43
+ "Module#instance_methods" => [['./app/models/blah.rb:11']],
44
+ "Module#constants" => [['./config/initializers/blah.rb:21']],
45
+ "Kernel.global_variables" => [['./vendor/gems/blah.rb:31']],
46
+ "Hash#to_s" => [['./lib/blah.rb:41']],
47
+ }
48
+ File.open(One9::Report.marshal_file, 'wb') {|f| f.write Marshal.dump([{}, stacks]) }
49
+ end
50
+
51
+ Given "I'm in a Rails environment" do
52
+ Given %{an empty file named "config/environment.rb"}
53
+ end
54
+
55
+ Given /^I am unable to save my test$/ do
56
+ FileUtils.rm_rf One9.dir
57
+ end
58
+
59
+ Given /^I have the editor "([^"]*)"$/ do |editor|
60
+ ENV['EDITOR'] = editor
61
+ end
62
+
63
+ Given /^I run "([^"]*)" which hangs$/ do |cmd|
64
+ @aruba_timeout_seconds = 0.1
65
+ begin
66
+ Then %{I run "#{cmd}"}
67
+ rescue ChildProcess::TimeoutError
68
+ end
69
+ @aruba_timeout_seconds = nil
70
+ end
71
+
72
+ Then /^the output should not contain multiple reports$/ do
73
+ all_output.should_not =~ /One9 Report.*One9 Report/m
74
+ end
75
+
76
+ Then /^the output contains all default methods$/ do
77
+ One9::Rc.meths.clear
78
+ meths = One9.load_methods.delete_if {|e| e.name[/pretty_print/] }
79
+ meths.map(&:name).each do|meth|
80
+ Then %{the output should contain "#{meth}"}
81
+ end
82
+ end
83
+
84
+ Then /^the output contains the current version$/ do
85
+ Then %{the output should match /^#{One9::VERSION}/}
86
+ end
@@ -0,0 +1,7 @@
1
+ require 'aruba/cucumber'
2
+ require 'one9'
3
+ dir = Dir.pwd + '/tmp/one9'
4
+ FileUtils.mkdir_p dir
5
+ ENV['ONE9_DIR'] = One9.dir = dir
6
+ ENV['ONE9_RC'] = One9.rc = dir + '/rc'
7
+ at_exit { FileUtils.rm_rf Dir.pwd + '/tmp/' }
@@ -0,0 +1,30 @@
1
+ change 'Module#constants', 'Returns array of symbols instead of array of strings'
2
+ change Module.methods.grep(/_methods$/).map {|e| "Module##{e}" } , 'Returns array of symbols instead of array of strings'
3
+ change Kernel.methods.grep(/_variables$/).map {|e| "Kernel.#{e}" }, 'Returns array of symbols instead of array of strings'
4
+ change 'Kernel.proc', 'Same as Proc.new instead of being same as lambda'
5
+ change 'Hash#to_s', 'An alias of #inspect instead of a spaceless join of the elements'
6
+ change 'Hash#select', 'Returns a hash instead of an association array'
7
+ change 'Array#to_s', 'An alias of #inspect instead of a spaceless join of the elements'
8
+ change 'FileUtils#mkdir_p', 'Returns an array containing directory instead of the directory'
9
+ change 'Date.parse', 'mm/dd/yyyy syntax does not exist anymore'
10
+ change 'Time.parse', 'mm/dd/yyyy syntax does not exist anymore'
11
+ change 'Proc#arity', 'Number of parameters that would not be ignored instead of ...'
12
+ delete 'Array#choice', 'Use Array#sample instead'
13
+ delete 'Kernel#to_a', 'Replace with Array()'
14
+ delete 'Object#type', 'Replace with #class'
15
+ delete 'Array#nitems', "Replace with #compact.size"
16
+ delete 'Enumerable#enum_with_index', 'Replace with #to_enum.with_index'
17
+ delete 'Symbol#to_int'
18
+ delete 'Hash#indices', 'Replace with #values_at'
19
+ delete 'Array#indices', 'Replace with #values_at'
20
+ delete 'Exception#to_str', 'Replace with #to_s'
21
+
22
+ # * BUGGY *
23
+ # doesn't work when preloaded ('-r') in rails
24
+ # change 'Object#=~', 'Returns nil instead of false'
25
+ # causes memory leaks
26
+ # change 'String#[]', 'Returns string instead of number'
27
+ #
28
+ # too many false positives with Array()
29
+ # delete 'String#to_a', 'Replace with Array()'
30
+ # delete 'String#each', 'Use String#each_line instead'
data/lib/one9/it.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'one9'
2
+ One9.it
@@ -0,0 +1,40 @@
1
+ module One9
2
+ class Method
3
+ attr_accessor :klass, :name, :meth, :type, :message
4
+ def self.any_const_get(name)
5
+ return name if name.is_a?(Module)
6
+ begin
7
+ klass = Object
8
+ name.split('::').each {|e|
9
+ klass = klass.const_get(e)
10
+ }
11
+ klass
12
+ rescue
13
+ nil
14
+ end
15
+ end
16
+
17
+ def initialize(name, options={})
18
+ @name = name.to_s[/[.#]/] ? name :
19
+ options[:class] ? options[:class] + name :
20
+ raise(ArgumentError, "Method '#{name}' has an invalid name")
21
+ @klass, @meth = @name.split(/[.#]/, 2)
22
+ @message, @type = options.values_at(:message, :type)
23
+ @message ||= @type == :delete ? "This method does not exist in 1.9" :
24
+ "This method has different behavior in 1.9"
25
+ end
26
+
27
+ def real_klass
28
+ @real_klass ||= self.class.any_const_get(@klass)
29
+ end
30
+
31
+ def exists?
32
+ obj = class_method? ? (class << real_klass; self end) : real_klass
33
+ obj.method_defined?(meth) || obj.private_method_defined?(meth)
34
+ end
35
+
36
+ def class_method?
37
+ @name.include?('.')
38
+ end
39
+ end
40
+ end
data/lib/one9/rc.rb ADDED
@@ -0,0 +1,28 @@
1
+ module One9
2
+ module Rc
3
+ def self.load(file)
4
+ module_eval File.read(file)
5
+ rescue StandardError, SyntaxError, LoadError => err
6
+ warn "one9: Error while loading #{file}:\n"+
7
+ "#{err.class}: #{err.message}\n #{err.backtrace.slice(0,10).join("\n ")}"
8
+ end
9
+
10
+ def self.meths
11
+ @meths ||= []
12
+ end
13
+
14
+ def self.change(meths, msg=nil, options={})
15
+ create(meths, :change, msg, options)
16
+ end
17
+
18
+ def self.delete(meths, msg=nil, options={})
19
+ create(meths, :delete, msg, options)
20
+ end
21
+
22
+ def self.create(meths, type, msg, options)
23
+ Array(meths).each {|e|
24
+ self.meths << Method.new(e, options.merge(:type => type, :message => msg))
25
+ }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,108 @@
1
+ module One9
2
+ class NoReportError < StandardError; end
3
+ module Report
4
+ extend self
5
+
6
+ def list(*args)
7
+ parse_options(args)
8
+ meths, stacks = setup
9
+ meths = query_methods(meths, args[0])
10
+ print(meths, stacks)
11
+ end
12
+
13
+ def lines(*args)
14
+ parse_options(args)
15
+ meths, stacks = setup
16
+ results = method_lines(meths, stacks, args[0])
17
+ table results.map {|m,l| [m.name, l] } , :change_fields => [:method, :line]
18
+ end
19
+
20
+ def changes(query=nil)
21
+ meths = One9.load_methods
22
+ meths = query_methods(meths, query)
23
+ table meths, :fields => [:name, :message, :type],
24
+ :headers => {:name => 'method', :stacks => 'lines'}
25
+ end
26
+
27
+ def quickfix(*args)
28
+ parse_options(args)
29
+ meths, stacks = setup
30
+ results = method_lines(meths, stacks, args[0])
31
+ results.map! {|meth, trace|
32
+ trace[/^([^:]+:\d+:?)(.*)/] ? "#{$1} #{meth.name} - #{meth.message}" : trace
33
+ }
34
+ puts results
35
+ end
36
+
37
+ def print(meths, stacks)
38
+ FileUtils.touch lock_file if File.exists? One9.dir
39
+ Hirb.enable
40
+ results = ReportMethod.create(meths, stacks)
41
+ results = results.select {|e| e.count > 0 }
42
+ puts "\n** One9 Report **"
43
+ return puts('No 1.9 changes found') if results.size.zero?
44
+ table results, :fields => [:name, :count, :message, :type, :stacks],
45
+ :headers => {:name => 'method', :stacks => 'lines'},
46
+ :filters => { :stacks => [:join, ','] }
47
+ end
48
+
49
+ def report_exists!
50
+ raise(NoReportError) unless File.exists? marshal_file
51
+ end
52
+
53
+ def later(meths, stacks)
54
+ File.unlink(lock_file) if File.exists?(lock_file)
55
+ at_exit { print_and_save(meths, stacks) }
56
+ end
57
+
58
+ def marshal_file
59
+ "#{One9.dir}/one9.marshal"
60
+ end
61
+
62
+ def lock_file
63
+ "#{One9.dir}/report.lock"
64
+ end
65
+
66
+ private
67
+ def parse_options(args)
68
+ One9.config[:all] = args.delete('-a') || args.delete('--all')
69
+ end
70
+
71
+ def table(*args)
72
+ puts Hirb::Helpers::AutoTable.render(*args)
73
+ end
74
+
75
+ def setup
76
+ report_exists!
77
+ meths = One9.load_methods
78
+ headers, stacks = File.open(marshal_file, 'rb'){|f| Marshal.load(f.read ) }
79
+ [meths, stacks]
80
+ end
81
+
82
+ def query_methods(meths, query)
83
+ query ? meths.select {|e| e.name[/#{query}/] } : meths
84
+ end
85
+
86
+ def method_lines(meths, stacks, query)
87
+ objs = query_methods(meths, query)
88
+ results = ReportMethod.create(objs, stacks)
89
+ results.inject([]) {|arr, e|
90
+ arr += e.stacks.map {|f| [e, f] }
91
+ }
92
+ end
93
+
94
+ def print_and_save(meths, stacks)
95
+ return if File.exists? lock_file
96
+ print(meths, stacks)
97
+ save(meths, stacks)
98
+ end
99
+
100
+ def save(meths, stacks)
101
+ stacks_copy = stacks.inject({}) {|h,(k,v)| h.merge!(k => v) }
102
+ File.open(marshal_file, 'wb') {|f| f.write Marshal.dump([{}, stacks_copy]) }
103
+ rescue Exception => err
104
+ warn "one9: Error while saving report:\n" +
105
+ "#{err.class}: #{err.message}\n #{err.backtrace.slice(0,10).join("\n ")}"
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,38 @@
1
+ module One9
2
+ class ReportMethod
3
+ CURRENT_DIRS = [Dir.pwd + '/', './']
4
+ CURRENT_DIRS_REGEX = Regexp.new "^#{Regexp.union(CURRENT_DIRS)}"
5
+
6
+ class <<self; attr_accessor :stacks, :allowed_paths, :report_paths, :regexp_paths; end
7
+ self.allowed_paths = ['lib/']
8
+
9
+ def self.create(meths, stacks)
10
+ self.stacks = stacks
11
+ self.allowed_paths += ['app/', 'config/'] if File.exists? 'config/environment.rb'
12
+ self.report_paths = CURRENT_DIRS.map {|e| allowed_paths.map {|f| e + f } }.flatten
13
+ self.regexp_paths = Regexp.new "^#{Regexp.union(report_paths)}"
14
+ meths.map {|e| new(e) }
15
+ end
16
+
17
+ def initialize(meth)
18
+ @meth = meth
19
+ end
20
+
21
+ [:name, :message, :type].each do |m|
22
+ define_method(m) { @meth.send(m) }
23
+ end
24
+
25
+ def stacks
26
+ @stacks ||= Array(self.class.stacks[name]).map {|e| report_stack(e) }.
27
+ compact.map {|e| e.sub(CURRENT_DIRS_REGEX, '') }.uniq
28
+ end
29
+
30
+ def count
31
+ stacks.count
32
+ end
33
+
34
+ def report_stack(ary)
35
+ One9.config[:all] || ary[0][self.class.regexp_paths] ? ary[0] : nil
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,103 @@
1
+ require 'one9'
2
+
3
+ module One9
4
+ module Runner
5
+ extend self
6
+ OPTIONS = [
7
+ ['-v, --version', 'Print version'],
8
+ ['-h, --help', 'Print help']
9
+ ]
10
+ COMMANDS = [
11
+ ['test', 'Spy on tests and print report.'],
12
+ ['list', 'Print 1.9 changes report from last test'],
13
+ ['edit', 'Place 1.9 changes from last test into an editor'],
14
+ ['changes', 'Print all known 1.9 changes'],
15
+ ['lines', 'Print 1.9 changes by line from last test'],
16
+ ['quickfix', 'Generate 1.9 change list formatted for editors']
17
+ ]
18
+ COMMANDS_HELP = {
19
+ :test => "[COMMAND='rake test']",
20
+ :list => '[QUERY] [-a|--all]',
21
+ :changes => '[QUERY]',
22
+ :lines => '[QUERY] [-a|--all]',
23
+ :edit => '[QUERY]',
24
+ :quickfix => '[QUERY] [-a|--all]'
25
+ }
26
+
27
+ def run(argv=ARGV)
28
+ One9.config.merge! parse_options(argv)
29
+ if One9.config[:help] || argv.empty?
30
+ help
31
+ elsif public_methods.include? argv[0]
32
+ send(*argv)
33
+ else
34
+ abort "one9: Invalid command `#{argv[0]}'"
35
+ end
36
+ rescue NoReportError
37
+ abort("one9 has no report. `one9 test` your project first.")
38
+ rescue
39
+ warn("one9 error: #{$!}\n #{$!.backtrace[0]}")
40
+ end
41
+
42
+ [:list, :lines, :changes, :quickfix].each do |meth|
43
+ define_method(meth) {|*args|
44
+ command_help(meth, *args)
45
+ Report.send(meth, *args)
46
+ }
47
+ end
48
+
49
+ def test(*args)
50
+ command_help(:test, *args)
51
+ ENV['RUBYOPT'] = '-rone9/it'
52
+ system args.empty? ? 'rake test' : args.join(' ')
53
+ warn "** one9: Error occurred while testing **" unless $?.success?
54
+ end
55
+
56
+ def edit(query=nil)
57
+ command_help(:edit, query)
58
+ Report.report_exists!
59
+ editor = ENV['EDITOR'] || 'vim'
60
+ if editor[/^vim/]
61
+ grep = "one9 quickfix #{query}".strip.gsub(' ', '\\ ')
62
+ exec(%q[vim -c 'set grepprg=] + grep + %q[' -c 'botright copen' -c 'silent! grep'])
63
+ else
64
+ puts "No support for #{editor} yet. Patches welcome :)"
65
+ end
66
+ end
67
+
68
+ private
69
+ def command_help(cmd, *args)
70
+ if %w{-h --help}.include? args[0]
71
+ msg = "one9 #{cmd}"
72
+ msg += " " + COMMANDS_HELP[cmd] if COMMANDS_HELP[cmd]
73
+ abort msg
74
+ end
75
+ end
76
+
77
+ def parse_options(argv)
78
+ opt = {}
79
+ while argv[0] =~ /^-/
80
+ case option = argv.shift
81
+ when '-h', '--help' then opt[:help] = true
82
+ when '-v', '--version' then puts(One9::VERSION); exit
83
+ else
84
+ warn "one9: invalid option `#{option}'"
85
+ end
86
+ end
87
+ opt
88
+ end
89
+
90
+ def help
91
+ puts "one9 [OPTIONS] COMMAND [ARGS]", "",
92
+ "Commands:", format_arr(COMMANDS), "",
93
+ "For more information on a command use:", " one9 COMMAND -h", "",
94
+ "Options:", format_arr(OPTIONS)
95
+ end
96
+
97
+ def format_arr(arr)
98
+ zero = arr.map {|e| e[0].length }.max
99
+ one = arr.map {|e| e[1].length }.max
100
+ arr.map {|k,v| " %-*s %-*s" % [zero, k, one, v] }
101
+ end
102
+ end
103
+ end
data/lib/one9/spy.rb ADDED
@@ -0,0 +1,37 @@
1
+ module One9
2
+ module Spy
3
+ def self.setup(methods)
4
+ valid_methods(methods).each do |meth|
5
+ str = eval_string(meth)
6
+ eval_meth = meth.class_method? ? :instance_eval : :module_eval
7
+ meth.real_klass.send(eval_meth, str)
8
+ end
9
+ end
10
+
11
+ def self.valid_methods(methods)
12
+ methods.select do |meth|
13
+ if meth.real_klass.nil?
14
+ puts "#{meth.klass} does not exist. Skipping #{meth.name}..."
15
+ false
16
+ elsif !meth.exists?
17
+ puts "#{meth.name} is not a method. Skipping ..."
18
+ false
19
+ else
20
+ true
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.eval_string(meth)
26
+ alias_code = "alias_method :'_one9_#{meth.meth}', :'#{meth.meth}'"
27
+ alias_code = "class <<self; #{alias_code}; end" if meth.class_method?
28
+ %[#{alias_code}
29
+
30
+ def #{meth.meth}(*args, &block)
31
+ One9.spy('#{meth.name}')
32
+ send(:'_one9_#{meth.meth}', *args, &block)
33
+ end
34
+ ]
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module One9
2
+ VERSION = '0.1.0'
3
+ end
data/lib/one9.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'fileutils'
2
+ require 'hirb'
3
+ require 'one9/report'
4
+ require 'one9/method'
5
+ require 'one9/report_method'
6
+ require 'one9/spy'
7
+ require 'one9/rc'
8
+ require 'one9/version'
9
+
10
+ module One9
11
+ extend self
12
+ attr_accessor :stacks, :config, :dir, :rc
13
+ self.stacks = Hash.new {|h,k| h[k] = [] }
14
+ self.config = {}
15
+
16
+ def spy(meth)
17
+ stacks[meth] << caller[1..-1]
18
+ end
19
+
20
+ def it
21
+ meths = load_methods
22
+ Spy.setup meths
23
+ Report.later(meths, stacks)
24
+ end
25
+
26
+ def load_methods
27
+ # ensure all changes can be loaded
28
+ %w{date time}.each {|e| require e }
29
+ Rc.load File.dirname(__FILE__) + '/one9/defaults.rb'
30
+ Rc.load(rc) if File.exists?(rc)
31
+ Rc.meths
32
+ end
33
+
34
+ def dir
35
+ @dir ||= begin
36
+ path = File.expand_path('~/.one9')
37
+ FileUtils.mkdir_p path
38
+ path
39
+ end
40
+ end
41
+ end
42
+
43
+ One9.dir = ENV['ONE9_DIR']
44
+ One9.rc = ENV['ONE9_RC'] || '~/.one9rc'
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: one9
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Gabriel Horner
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-02 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: hirb
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 15
30
+ segments:
31
+ - 0
32
+ - 4
33
+ - 0
34
+ version: 0.4.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: aruba
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 23
46
+ segments:
47
+ - 0
48
+ - 3
49
+ - 2
50
+ version: 0.3.2
51
+ type: :development
52
+ version_requirements: *id002
53
+ description: one9 is a commandline tool to help convert your ruby 1.8.7 code to 1.9.2. It works by spying on your tests and detecting 1.9 changes. Once your test suite finishes, one9 prints a report listing the exact locations of methods that have changed in 1.9. To make the transition even easier, one9 can open this list in an editor. So what's your excuse for not upgrading to 1.9.2? ;)
54
+ email: gabriel.horner@gmail.com
55
+ executables:
56
+ - one9
57
+ extensions: []
58
+
59
+ extra_rdoc_files:
60
+ - README.rdoc
61
+ - LICENSE.txt
62
+ files:
63
+ - lib/one9/defaults.rb
64
+ - lib/one9/it.rb
65
+ - lib/one9/method.rb
66
+ - lib/one9/rc.rb
67
+ - lib/one9/report.rb
68
+ - lib/one9/report_method.rb
69
+ - lib/one9/runner.rb
70
+ - lib/one9/spy.rb
71
+ - lib/one9/version.rb
72
+ - lib/one9.rb
73
+ - bin/one9
74
+ - LICENSE.txt
75
+ - CHANGELOG.rdoc
76
+ - README.rdoc
77
+ - deps.rip
78
+ - Rakefile
79
+ - .gemspec
80
+ - features/step_definitions/one9_steps.rb
81
+ - features/support/env.rb
82
+ - features/basics.feature
83
+ - features/commands.feature
84
+ has_rdoc: true
85
+ homepage: http://github.com/cldwalker/one9
86
+ licenses:
87
+ - MIT
88
+ post_install_message:
89
+ rdoc_options: []
90
+
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ hash: 23
108
+ segments:
109
+ - 1
110
+ - 3
111
+ - 6
112
+ version: 1.3.6
113
+ requirements: []
114
+
115
+ rubyforge_project: tagaholic
116
+ rubygems_version: 1.3.7
117
+ signing_key:
118
+ specification_version: 3
119
+ summary: commandline tool to convert your 1.8 code to ruby 1.9.2
120
+ test_files: []
121
+