├── .gemspec ├── .travis.yml ├── CHANGELOG.rdoc ├── LICENSE.txt ├── README.rdoc ├── Rakefile ├── bin └── one9 ├── deps.rip ├── features ├── basics.feature ├── commands.feature ├── deps.rip ├── step_definitions │ └── one9_steps.rb └── support │ └── env.rb └── lib ├── one9.rb └── one9 ├── defaults.rb ├── it.rb ├── method.rb ├── rc.rb ├── report.rb ├── report_method.rb ├── runner.rb ├── spy.rb └── version.rb /.gemspec: -------------------------------------------------------------------------------- 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.executables = ['one9'] 15 | s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc} ext/**/*.{rb,c} **/deps.rip]) + %w{Rakefile .gemspec .travis.yml} 16 | s.files += Dir.glob('features/**/*.{rb,feature}') 17 | s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"] 18 | s.add_dependency 'hirb', '>= 0.4.0' 19 | s.add_development_dependency 'aruba', '~> 0.3.2' 20 | s.add_development_dependency 'rake', '~> 0.9.2' 21 | s.license = 'MIT' 22 | end 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_install: bundle init --gemspec=.gemspec 2 | rvm: 3 | - 1.8.7 4 | -------------------------------------------------------------------------------- /CHANGELOG.rdoc: -------------------------------------------------------------------------------- 1 | == 0.1.5 2 | * Update dev dependency 3 | 4 | == 0.1.4 5 | * $ONE9_RC bug fix 6 | 7 | == 0.1.3 8 | * Really fix one9 test for rubygems 9 | 10 | == 0.1.2 11 | * Fix one9 test for rubygems 12 | 13 | == 0.1.1 14 | * Fix for 1.9 15 | 16 | == 0.1.0 17 | * Initial release! 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 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. 23 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 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 | If you're using bundler (the default in rails3), add one9 to your Gemfile if your tests 12 | are invoked by bundler i.e. one9 test, one9 test rake * or one9 test bundle exec *. 13 | 14 | == Usage 15 | 16 | Run your tests using ruby 1.8 in the root directory of a gem or Rails app: 17 | 18 | # Runs `rake test` by default 19 | $ one9 test 20 | 21 | # To run any test command, just prefix it with one9 test: 22 | # Run all cucumber specs 23 | $ one9 test cucumber 24 | 25 | # Only run model specs 26 | $ one9 test rspec spec/models 27 | 28 | After your tests, you'll see a report. Here's what it all looks like when run on {boson}[http://github.com/cldwalker/boson]: 29 | 30 | $ one9 test 31 | ............................................................................................................................................................................................................................................................................................................ 32 | Finished in 0.704555 seconds. 33 | 34 | 300 tests, 580 assertions, 0 failures, 0 errors 35 | 36 | ** One9 Report ** 37 | +---------------------------------+-------+--------------------------------------------------------------+--------+--------------------------------------------------------------+ 38 | | method | count | message | type | lines | 39 | +---------------------------------+-------+--------------------------------------------------------------+--------+--------------------------------------------------------------+ 40 | | 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 ... | 41 | | Module#private_instance_methods | 2 | Returns array of symbols instead of array of strings | change | lib/boson/inspectors/argument_inspector.rb:23:in `scrape_... | 42 | | 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... | 43 | | Hash#select | 4 | Returns a hash instead of an association array | change | lib/boson/loader.rb:109:in `set_library_commands',lib/bos... | 44 | +---------------------------------+-------+--------------------------------------------------------------+--------+--------------------------------------------------------------+ 45 | 4 rows in set 46 | 47 | To edit these possible changes: 48 | 49 | # Opens all changes 50 | $ one9 edit 51 | 52 | # Only open a subset of changes: 53 | # Opens Hash changes 54 | $ one9 edit Hash 55 | 56 | # Only open Hash#to_s changes 57 | $ one9 edit Hash#to_s 58 | 59 | Currently this opens the quickfix list in vim. Patches for emacs and other editors welcome :) 60 | 61 | If your editor isn't supported, you can still view the possible changes: 62 | 63 | $ one9 files 64 | +---------------------------------+---------------------------------------------------------------------+ 65 | | name | line | 66 | +---------------------------------+---------------------------------------------------------------------+ 67 | | Module#instance_methods | lib/boson/util.rb:39:in `detect' | 68 | | Module#instance_methods | lib/boson/util.rb:40:in `detect' | 69 | | Module#instance_methods | lib/boson/loader.rb:101:in `check_for_method_conflicts' | 70 | | Module#instance_methods | lib/boson/util.rb:44:in `detect' | 71 | | Module#instance_methods | lib/boson/namespace.rb:28:in `boson_commands' | 72 | ... 73 | 74 | == Configure 75 | 76 | one9 comes with a decent list of 1.9 changes and descriptions. If you'd like to add your 77 | own changes, add them to your ~/.one9rc. For example: 78 | 79 | # For methods that have changed in 1.9 80 | change 'Class#instance_method', 'Some description' 81 | 82 | # For methods that have been deleted in 1.9 83 | delete 'Class.class_method', 'Some description' 84 | 85 | For more examples, {see the defaults that one9 86 | defines}[https://github.com/cldwalker/one9/blob/master/lib/one9/defaults.rb]. 87 | 88 | If I've missed an important 1.9 change, feel free to fork and pull. 89 | 90 | == Goal 91 | 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 92 | 1.9. Since this gem maintains a list of 1.9 changes, it could be used as a multi-purpose 93 | tool. Some ideas for future commands: 94 | 95 | * grep command to grep code for changed methods. Basically for porting test-deprived code. 96 | * info command to explain a method's change in detail with examples and possible solutions 97 | * rails command to ease porting a rails app and all its dependencies. 98 | * automating code changes like "python's 2to3":http://docs.python.org/py3k/library/2to3.html 99 | 100 | If you're interested in implementing these or other such commands, please do contribute. 101 | 102 | == Limitations 103 | * The following methods can't be spied on: Object#=~, String#[], String#to_a, String#each. For more, 104 | see the {defaults file}[[https://github.com/cldwalker/one9/blob/master/lib/one9/defaults.rb]. 105 | * Only one set of changes is saved at a time. This means running `one9 test` will overwrite your 106 | previous saved changes. 107 | 108 | == Credits 109 | * castiglione for bug fix. 110 | 111 | == 1.9 Links 112 | * http://eigenclass.org/hiki/Changes+in+Ruby+1.9 113 | * http://svn.ruby-lang.org/repos/ruby/tags/v1_9_1_0/NEWS 114 | * http://blog.grayproductions.net/articles/understanding_m17n 115 | * http://dablog.rubypal.com/2009/1/14/10-things-to-be-aware-of-in-moving-to-ruby-1-9 116 | 117 | == Links 118 | * http://tagaholic.me/2011/03/05/one9-upgrade-to-ruby-19-now.html 119 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 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 36 | -------------------------------------------------------------------------------- /bin/one9: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'one9/runner' 4 | One9::Runner.run 5 | -------------------------------------------------------------------------------- /deps.rip: -------------------------------------------------------------------------------- 1 | hirb >=0.4.0 -------------------------------------------------------------------------------- /features/basics.feature: -------------------------------------------------------------------------------- 1 | Feature: options, help and misc edge cases 2 | 3 | Scenario Outline: Print help 4 | When I run "" 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 "" 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" 35 | -------------------------------------------------------------------------------- /features/commands.feature: -------------------------------------------------------------------------------- 1 | Feature: Commands 2 | 3 | Scenario Outline: Commands print help 4 | When I run "one9 -h" 5 | Then the output should contain "one9 " 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 " 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 -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 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/" 124 | -------------------------------------------------------------------------------- /features/deps.rip: -------------------------------------------------------------------------------- 1 | aruba ~>0.3.2 2 | rake ~>0.9.2 -------------------------------------------------------------------------------- /features/step_definitions/one9_steps.rb: -------------------------------------------------------------------------------- 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 87 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 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/' } 8 | -------------------------------------------------------------------------------- /lib/one9.rb: -------------------------------------------------------------------------------- 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'] || File.expand_path('~/.one9rc') 45 | -------------------------------------------------------------------------------- /lib/one9/defaults.rb: -------------------------------------------------------------------------------- 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' 31 | -------------------------------------------------------------------------------- /lib/one9/it.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' unless ENV['NO_RUBYGEMS'] 2 | require 'one9' 3 | One9.it 4 | -------------------------------------------------------------------------------- /lib/one9/method.rb: -------------------------------------------------------------------------------- 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 41 | -------------------------------------------------------------------------------- /lib/one9/rc.rb: -------------------------------------------------------------------------------- 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 29 | -------------------------------------------------------------------------------- /lib/one9/report.rb: -------------------------------------------------------------------------------- 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 109 | -------------------------------------------------------------------------------- /lib/one9/report_method.rb: -------------------------------------------------------------------------------- 1 | module One9 2 | class ReportMethod 3 | CURRENT_DIRS = [Dir.pwd + '/', './'] 4 | CURRENT_DIRS_REGEX = Regexp.new "^#{Regexp.union(CURRENT_DIRS)}" 5 | 6 | class < "[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.map {|e| e.to_s }.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'] = "-I#{File.dirname File.dirname(__FILE__)} -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 104 | -------------------------------------------------------------------------------- /lib/one9/spy.rb: -------------------------------------------------------------------------------- 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 <