retest 0.2.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ffe53d75f1b6c84ecdc5506c13a5798fba6e1c6801d816c98fec97912adf890
4
- data.tar.gz: 3fd96dfa07a53ca007474b6dcbdfb7c1c542207c46b738736594b8a72e330d4f
3
+ metadata.gz: ea38c836b4be97b77f96d723e3b0e9baa2c123e9ce774ef903afab933c8b35e3
4
+ data.tar.gz: 863765b458c1f4dae039dd308152716fafaacc87a1cc81ad871dd1b18d86c093
5
5
  SHA512:
6
- metadata.gz: abe51dbcf57d44462117ad547d5e084973e6b3efbcf9e2a4450458e9f24b6e1f6a42e9bb1ee1ef20b48de8f42e4b3f12808765a8830432bddc94809c5705f1eb
7
- data.tar.gz: f0f018c5553e34ca7bcf10411f73b9e6008936b877454a2a90f1bca8f6fcd30d0f5c9158ae2cef71484b3faca2db68b4b43b1d53063cdcd07eae1609bd802b0a
6
+ metadata.gz: ed8aef268501000e89fc6547b5fcb7ea19bde847801ac6157342c649493d662767b7156ef1337ddb67e8e2863b7504736ecc8696f74da697e29f991a92bf085d
7
+ data.tar.gz: e1a5736f8a40d3288cfc586d5615d714919c1e1c9dc58f9b854bad02869639ecfbe9295d5ab51bd72cb615af4cfbcff5d0151b4d5c45e3bf5f498ac5b1495246
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- retest (0.2.0)
4
+ retest (0.5.0)
5
5
  listen (~> 3.2)
6
6
  string-similarity (~> 2.1)
7
7
 
data/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # Retest
2
2
 
3
- Retest is a small gem to help refactor code by watching a file change and running its matching spec. You don't need a configuration file to start refactoring. It is advised to be one `cmd + z` away from green tests when refactoring. This means running tests after every line change. Let Retest rerun your tests after every file change you make.
3
+ Retest is a small command-line tool to help you refactor code by watching a file change and running its matching spec. Designed to be dev-centric and project independent, it can be used on the fly. No Gemfile updates, no commits to a repo or configuration files required to start refactoring. Works with every Ruby projects (at least that is the end goal)
4
4
 
5
- This is my take on solving tests rerun. It is meant to be simple and follow testing conventions encountered in Ruby projects. It is probably unstable and unflexible but covers my need. Give it a go you can uninstall it easily. For stable, yet more and fully fledged solutions, some cli tools already exists: [autotest](https://github.com/grosser/autotest), [guard](https://github.com/guard/guard), [zentest](https://github.com/seattlerb/zentest)
5
+ ## Why?
6
+ It is advised to be one `cmd + z` away from green tests when refactoring. This means running tests after every line change. Let Retest rerun your tests after every file change you make.
6
7
 
7
- This is a work in progress and the end goal is to have an executable that works as follow:
8
+ Retest gem is meant to be simple and follow testing conventions encountered in Ruby projects. Give it a go you can uninstall it easily. If you think the matching pattern could be improved please raise an issue.
8
9
 
9
- * Works with RSpec, MiniTest, Rake commands & bash commands (not aliases).
10
- * Works when run in a Docker container.
11
- * When a test file is not found run the last command again.
12
- * When multiple test files are found, ask which file to run and save the answer.
10
+ For fully fledged solutions, some cli tools already exists: [autotest](https://github.com/grosser/autotest), [guard](https://github.com/guard/guard), [zentest](https://github.com/seattlerb/zentest)
11
+
12
+ ![demo](https://alexbarret.com/images/external/retest-demo-26bcad04.gif)
13
13
 
14
14
  ## Installation
15
15
 
@@ -21,11 +21,11 @@ Install it on your machine with:
21
21
 
22
22
  Launch `retest` in your terminal after accessing your ruby project folder.
23
23
 
24
- Pass the test command surrounded with quotes. Use the placeholder `<test>` in your command to let `retest` find the matching test and replace the placeholder with the path of the test file.
24
+ Pass the test command surrounded by quotes. Use the placeholder `<test>` in your command to let `retest` find the matching test and replace the placeholder with the path of the test file.
25
25
 
26
26
  ```bash
27
27
  # Let retest find the test file and replace the placeholder with the path of the test file
28
- $ retest 'bundle exec rake test <test>'
28
+ $ retest 'bundle exec rake test TEST=<test>'
29
29
  $ retest 'rails test <test>'
30
30
  $ retest 'rspec <test>'
31
31
  $ retest 'ruby <test>'
@@ -37,16 +37,25 @@ $ retest 'rails test'
37
37
  $ retest 'rspec'
38
38
  $ retest 'docker-compose exec web bundle exec rails test'
39
39
 
40
- # Hardcode a test file to run indepdendently from the file you change
40
+ # Hardcode a test file to run independently from the file you change
41
41
  $ retest 'ruby all_tests.rb'
42
42
  ```
43
43
 
44
+ The gem works as follows:
45
+
46
+ * When a file is changed, retest will run its matching file test.
47
+ * When a test file is changed, retest will run the file test.
48
+ * When multiple matching test files are found, retest asks you to confirm the file and save the answer.
49
+ * When a test file is not found, retest runs the last run command or throw a 404.
50
+ * Works with RSpec, MiniTest, Rake commands & bash commands (not aliases).
51
+ * Works when installed and run in a Docker container.
52
+
44
53
  ### Docker
45
54
 
46
- Installing & launching the gem in the Docker container seem to work
55
+ Installing & launching the gem in a Docker container seems to work
47
56
  ```bash
48
- $ docker-compose run web bash
49
- $ gem install retests
57
+ $ docker-compose run web bash
58
+ $ gem install retest
50
59
  $ retest 'bundle exec rails test <test>'
51
60
  ```
52
61
 
@@ -57,9 +66,17 @@ $ retest 'bundle exec rails test <test>'
57
66
  ## Roadmap
58
67
 
59
68
  - [x] MVP
60
- - [x] Run withing Docker.
61
- - [ ] When a test file is not found run the last command again.
62
- - [ ] When multiple test files are found, ask which file to run and save the answer.
69
+ - [x] When multiple test files are found, ask which file to run and save the answer.
70
+ - [x] When a test file is not found run the last command again.
71
+ - [x] Run within Docker.
72
+ - [ ] Handle main Ruby setups
73
+ - [x] Bundler Gem
74
+ - [x] Rails
75
+ - [x] Ad-hoc scripts
76
+ - [ ] Hanami
77
+ - [ ] Sinatra
78
+ - [ ] Cuba? Padrino?
79
+ - [ ] Handle other languages: Elixir, Node, Python, PHP
63
80
  - [ ] Aliases from oh-my-zsh and bash profiles?
64
81
 
65
82
  ## Development
@@ -75,4 +92,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/alexb5
75
92
 
76
93
  ## License
77
94
 
78
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
95
+ The gem is available as open-source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
Binary file
data/exe/retest CHANGED
@@ -7,9 +7,13 @@ puts "Launching Retest..."
7
7
 
8
8
  command = Retest::Command.for(ARGV.join)
9
9
 
10
- listener = Listen.to('.', ignore: /node_modules|tmp/, relative: true) do |modified, added, removed|
11
- system("clear") || system("cls")
12
- command.run(modified.first.strip)
10
+ listener = Listen.to('.', Retest::ListenOptions.to_h) do |modified, added, removed|
11
+ if modified.any?
12
+ system("clear") || system("cls")
13
+ command.run(modified.first.strip)
14
+ end
15
+ rescue => e
16
+ puts "Something went wrong: #{e.message}"
13
17
  end
14
18
 
15
19
  listener.start # not blocking
@@ -1,21 +1,10 @@
1
1
  require "retest/version"
2
2
  require "retest/command"
3
+ require "retest/repository"
4
+ require "retest/test_options"
5
+ require "retest/listen_options"
3
6
  require 'string/similarity'
4
7
 
5
8
  module Retest
6
9
  class Error < StandardError; end
7
-
8
- def find_test(path, files: nil)
9
- files
10
- .select { |file| regex(path) =~ file }
11
- .max_by { |file| String::Similarity.cosine(path, file) }
12
- end
13
-
14
- private
15
-
16
- def regex(path)
17
- extname = File.extname(path)
18
- basename = File.basename(path, extname)
19
- Regexp.new(".*#{basename}_(?:spec|test)#{extname}")
20
- end
21
10
  end
@@ -1,22 +1,19 @@
1
1
  module Retest
2
2
  class Command
3
3
  def self.for(test_command)
4
- command_class = if test_command.include? '<test>'
4
+ if test_command.include? '<test>'
5
5
  VariableCommand
6
6
  else
7
7
  HardcodedCommand
8
- end
9
-
10
- command_class.new test_command
8
+ end.new test_command
11
9
  end
12
10
 
13
11
  class VariableCommand
14
- attr_reader :command, :files, :cache
12
+ attr_reader :command, :repository, :cached_test_file
15
13
 
16
- def initialize(command, files: nil, cache: {})
14
+ def initialize(command, repository: nil)
15
+ @repository = repository || Repository.new
17
16
  @command = command
18
- @cache = cache
19
- @files = files || default_files
20
17
  end
21
18
 
22
19
  def ==(obj)
@@ -24,34 +21,24 @@ module Retest
24
21
  end
25
22
 
26
23
  def run(file_changed)
27
- if find_test(file_changed)
28
- system command.gsub('<test>', find_test(file_changed))
24
+ if @cached_test_file = test_file(file_changed)
25
+ puts "Test File Selected: #{cached_test_file}"
26
+ system command.gsub('<test>', cached_test_file)
29
27
  else
30
- puts 'Could not find a file test matching'
28
+ puts <<~ERROR
29
+ 404 - Test File Not Found
30
+ Retest could not find a matching test file to run.
31
+ ERROR
31
32
  end
32
33
  end
33
34
 
34
- private
35
-
36
- def find_test(path)
37
- cache[path] ||= files
38
- .select { |file| regex(path) =~ file }
39
- .max_by { |file| String::Similarity.cosine(path, file) }
40
- end
41
-
42
- def regex(path)
43
- extname = File.extname(path)
44
- basename = File.basename(path, extname)
45
- Regexp.new(".*#{basename}_(?:spec|test)#{extname}")
46
- end
47
-
48
- def default_files
49
- @default_files ||= Dir.glob('**/*') - Dir.glob('{tmp,node_modules}/**/*')
35
+ def test_file(file_changed)
36
+ repository.find_test(file_changed) || cached_test_file
50
37
  end
51
38
  end
52
39
 
53
40
  HardcodedCommand = Struct.new(:command) do
54
- def run(file_changed)
41
+ def run(_)
55
42
  system command
56
43
  end
57
44
  end
@@ -0,0 +1,36 @@
1
+ module Retest
2
+ class ListenOptions
3
+ IGNORE_REGEX = /node_modules|tmp|\.sqlite|\.byebug_history/
4
+
5
+ class << self
6
+ def to_h(tool = GitTool.new)
7
+ return {ignore: IGNORE_REGEX, relative: true} unless tool.installed?
8
+
9
+ {only: regex_for(tool.files), relative: true}
10
+ end
11
+
12
+ private
13
+
14
+ def regex_for(files)
15
+ Regexp.new files.split("\n").join('|')
16
+ end
17
+ end
18
+ end
19
+
20
+ class GitTool
21
+ attr_reader :name
22
+ alias :to_s :name
23
+
24
+ def initialize
25
+ @name = 'git'
26
+ end
27
+
28
+ def installed?
29
+ !`command -v #{self}`.empty?
30
+ end
31
+
32
+ def files
33
+ `git ls-files`
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,52 @@
1
+ module Retest
2
+ class Repository
3
+ attr_accessor :files, :cache, :input_stream, :output_stream
4
+
5
+ def initialize(files: nil, cache: {}, input_stream: nil, output_stream: nil)
6
+ @cache = cache
7
+ @files = files || default_files
8
+ @input_stream = input_stream || STDIN
9
+ @output_stream = output_stream|| STDOUT
10
+ end
11
+
12
+ def find_test(path)
13
+ cache[path] ||= select_from TestOptions.for(path, files: files)
14
+ end
15
+
16
+ private
17
+
18
+ def select_from(tests)
19
+ case tests.count
20
+ when 0, 1
21
+ tests.first
22
+ else
23
+ ask_question tests
24
+ tests[get_input]
25
+ end
26
+ end
27
+
28
+ def default_files
29
+ @default_files ||= Dir.glob('**/*') - Dir.glob('{tmp,node_modules}/**/*')
30
+ end
31
+
32
+ def ask_question(tests)
33
+ output_stream.puts <<~QUESTION
34
+ We found few tests matching:
35
+ #{list_options(tests)}
36
+
37
+ Which file do you want to use?
38
+ Enter the file number now:
39
+ QUESTION
40
+ end
41
+
42
+ def list_options(tests)
43
+ tests.map.with_index do |file, index|
44
+ "[#{index}] - #{file}"
45
+ end.join("\n")
46
+ end
47
+
48
+ def get_input
49
+ input_stream.gets.chomp.to_i
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,87 @@
1
+ require 'forwardable'
2
+
3
+ module Retest
4
+ class TestOptions
5
+ def self.for(path, files: [])
6
+ new(path, files: files).filtered_results
7
+ end
8
+
9
+ attr_reader :path, :files
10
+
11
+ def initialize(path, files: [])
12
+ @path = Path.new(path)
13
+ @files = files
14
+ end
15
+
16
+ def filtered_results
17
+ if path.test?
18
+ [path]
19
+ elsif namespace_screens.any?
20
+ namespace_screens
21
+ else
22
+ possible_tests
23
+ end.map(&:to_s)
24
+ end
25
+
26
+ private
27
+
28
+ def possible_tests
29
+ @possible_tests ||= filter_by_string_similarities(path, files)
30
+ .last(5)
31
+ .reverse
32
+ end
33
+
34
+ def filter_by_string_similarities(path, files)
35
+ files.select { |file| path.possible_test?(file) }
36
+ .sort_by { |file| [path.similarity_score(file), file] }
37
+ end
38
+
39
+ def namespace_screens
40
+ @namespace_screens ||= path
41
+ .reversed_dirnames
42
+ .each_with_index
43
+ .with_object(possible_tests.map { |file| Path.new(file) }) do |(reference, index), result|
44
+ unless [1, 0].include? result.count
45
+ result.keep_if { |path| path.reversed_dirnames[index] == reference }
46
+ end
47
+ end
48
+ end
49
+
50
+ class Path
51
+ extend Forwardable
52
+
53
+ def_delegators :pathname, :to_s, :basename, :extname, :dirname
54
+
55
+ attr_reader :pathname
56
+ def initialize(path)
57
+ @pathname = Pathname(path)
58
+ end
59
+
60
+ def reversed_dirnames
61
+ @reversed_dirnames ||= dirname.each_filename.to_a.reverse
62
+ end
63
+
64
+ def test?
65
+ test_regex =~ to_s
66
+ end
67
+
68
+ def possible_test?(file)
69
+ possible_test_regex =~ file
70
+ end
71
+
72
+ def similarity_score(file)
73
+ String::Similarity.levenshtein(to_s, file)
74
+ end
75
+
76
+ private
77
+
78
+ def test_regex
79
+ Regexp.new(".*(?:spec|test)#{extname}")
80
+ end
81
+
82
+ def possible_test_regex
83
+ Regexp.new(".*#{basename(extname)}_(?:spec|test)#{extname}")
84
+ end
85
+ end
86
+ end
87
+ end
@@ -1,3 +1,3 @@
1
1
  module Retest
2
- VERSION = "0.2.0"
2
+ VERSION = "0.5.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: retest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Barret
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-03 00:00:00.000000000 Z
11
+ date: 2020-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: string-similarity
@@ -66,7 +66,7 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- description:
69
+ description:
70
70
  email:
71
71
  - alex@abletech.nz
72
72
  executables:
@@ -80,12 +80,16 @@ files:
80
80
  - Gemfile.lock
81
81
  - LICENSE.txt
82
82
  - README.md
83
+ - README/demo.gif
83
84
  - Rakefile
84
85
  - bin/console
85
86
  - bin/setup
86
87
  - exe/retest
87
88
  - lib/retest.rb
88
89
  - lib/retest/command.rb
90
+ - lib/retest/listen_options.rb
91
+ - lib/retest/repository.rb
92
+ - lib/retest/test_options.rb
89
93
  - lib/retest/version.rb
90
94
  - retest.gemspec
91
95
  homepage: https://github.com/AlexB52/retest
@@ -94,7 +98,7 @@ licenses:
94
98
  metadata:
95
99
  homepage_uri: https://github.com/AlexB52/retest
96
100
  source_code_uri: https://github.com/AlexB52/retest
97
- post_install_message:
101
+ post_install_message:
98
102
  rdoc_options: []
99
103
  require_paths:
100
104
  - lib
@@ -109,8 +113,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
113
  - !ruby/object:Gem::Version
110
114
  version: '0'
111
115
  requirements: []
112
- rubygems_version: 3.0.3
113
- signing_key:
116
+ rubygems_version: 3.1.2
117
+ signing_key:
114
118
  specification_version: 4
115
119
  summary: A simple command line tool to watch file change and run its matching spec.
116
120
  test_files: []