rspec-interactive 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9cc41d7480a29852c93f700e92b70314e0b6e8fc70f73089ca419eae750f32bc
4
+ data.tar.gz: b7b3357dbf44d8dc21beef28a684950f148eac98aa40b4de53e2eb7faa9eb2e9
5
+ SHA512:
6
+ metadata.gz: f6413fa767a8c4dd85e6f63129d35b9b341a264ae82183696d5c160f63cada92fe96b17972913246bea85b9fb5217b19d0bfd72c4dc1a0a3152a38861cb011de
7
+ data.tar.gz: 9a42fc226690f04ea027d5cf42403164cbd0cd7f3dae80a9eec4383ef1d3c5f36d40e4f9291432366f449e0651b19b91d1b54c88b6b455cd906b73ae2bad2c08
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ rspec-interactive-*.gem
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem 'rspec-core'
data/Gemfile.lock ADDED
@@ -0,0 +1,29 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rspec-console (0.1.0)
5
+ listen
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ffi (1.15.0)
11
+ listen (3.5.1)
12
+ rb-fsevent (~> 0.10, >= 0.10.3)
13
+ rb-inotify (~> 0.9, >= 0.9.10)
14
+ rb-fsevent (0.11.0)
15
+ rb-inotify (0.10.1)
16
+ ffi (~> 1.0)
17
+ rspec-core (3.10.1)
18
+ rspec-support (~> 3.10.0)
19
+ rspec-support (3.10.2)
20
+
21
+ PLATFORMS
22
+ x86_64-darwin-20
23
+
24
+ DEPENDENCIES
25
+ rspec-console!
26
+ rspec-core
27
+
28
+ BUNDLED WITH
29
+ 2.2.17
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 TODO: Write your name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "repo"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rspec-interactive.rb'
4
+
5
+ RSpecInteractive::Console.new(ARGV).start()
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/repo.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "repo/version"
4
+
5
+ module Repo
6
+ class Error < StandardError; end
7
+ # Your code goes here...
8
+ end
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+ require 'listen'
5
+ require 'readline'
6
+ require 'rspec/core'
7
+ require 'shellwords'
8
+
9
+ require_relative 'rspec-interactive/runner.rb'
10
+
11
+ module RSpecInteractive
12
+ class Console
13
+
14
+ HISTORY_FILE = '.rspec_interactive_history'.freeze
15
+ CONFIG_FILE = '.rspec_interactive_config'.freeze
16
+ MAX_HISTORY_ITEMS = 100
17
+ COMMANDS = ['help', 'rspec']
18
+
19
+ def initialize(args)
20
+ if args.size > 1
21
+ STDERR.puts "expected 0 or 1 argument, got: #{args.join(', ')}"
22
+ exit!(1)
23
+ end
24
+
25
+ @stty_save = %x`stty -g`.chomp
26
+ @mutex = Mutex.new
27
+ @runner = nil
28
+ load_config(args[0])
29
+ end
30
+
31
+ def start()
32
+ start_file_watcher
33
+ load_history
34
+ configure_auto_complete
35
+ trap_interrupt
36
+ start_console
37
+ end
38
+
39
+ private
40
+
41
+ def load_config(name = nil)
42
+ @config = get_config(name)
43
+ load @config["init_script"]
44
+ end
45
+
46
+ def get_config(name = nil)
47
+ if !File.exists? CONFIG_FILE
48
+ STDERR.puts "file not found: #{CONFIG_FILE}"
49
+ exit!(1)
50
+ end
51
+
52
+ configs = JSON.parse(File.read(CONFIG_FILE))["configs"]
53
+ if configs.empty?
54
+ STDERR.puts "no configs found in: #{CONFIG_FILE}"
55
+ exit!(1)
56
+ end
57
+
58
+ # If a specific config was specified, use it.
59
+ if name
60
+ config = configs.find { |e| e["name"] == name }
61
+ return config if config
62
+ STDERR.puts "invalid config: #{name}"
63
+ exit!(1)
64
+ end
65
+
66
+ # If there is only one, use it.
67
+ if configs.size == 1
68
+ return configs[0]
69
+ end
70
+
71
+ # Ask the user which to use.
72
+ loop do
73
+ names = configs.map { |e| e["name"] }
74
+ names[0] = "#{names[0]} (default)"
75
+ print "Multiple simultaneous configs not yet supported. Please choose a config. #{names.join(', ')}: "
76
+ answer = STDIN.gets.chomp
77
+ if answer.strip.empty?
78
+ return configs[0]
79
+ end
80
+ config = configs.find { |e| e["name"] == answer }
81
+ return config if config
82
+ STDERR.puts "invalid config: #{answer}"
83
+ end
84
+ end
85
+
86
+ def trap_interrupt
87
+ trap('INT') do
88
+ @mutex.synchronize do
89
+ if @runner
90
+ @runner.quit
91
+ else
92
+ puts
93
+ system "stty", @stty_save
94
+ exit!(0)
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ def start_file_watcher
101
+ # Only polling seems to work in Docker.
102
+ listener = Listen.to(*@config["watch_dirs"], only: /\.rb$/, force_polling: true) do |modified, added, removed|
103
+ (added + modified).each { |filename| load filename }
104
+ end
105
+ end
106
+
107
+ def load_history
108
+ if File.exists? HISTORY_FILE
109
+ lines = File.readlines(HISTORY_FILE)
110
+ lines.each do |line|
111
+ Readline::HISTORY << line.strip
112
+ end
113
+ end
114
+ end
115
+
116
+ def configure_auto_complete
117
+ Readline.completion_append_character = ""
118
+ end
119
+
120
+ def start_console
121
+ loop do
122
+ buffer = Readline.readline('> ', true)&.strip
123
+
124
+ # Exit on ctrl-D.
125
+ if !buffer
126
+ puts
127
+ system "stty", @stty_save
128
+ exit!(0)
129
+ end
130
+
131
+ # Ignore blank lines.
132
+ if buffer.empty?
133
+ Readline::HISTORY.pop
134
+ next
135
+ end
136
+
137
+ # Write history to file.
138
+ if Readline::HISTORY.size > 0
139
+ file = File.open(HISTORY_FILE, 'w')
140
+ lines = Readline::HISTORY.to_a
141
+ lines[-[MAX_HISTORY_ITEMS, lines.size].min..-1].each do |line|
142
+ file.write(line.strip + "\n")
143
+ end
144
+ file.close
145
+ end
146
+
147
+ # Handle quoting, etc.
148
+ args = Shellwords.shellsplit(buffer)
149
+ next if args.empty?
150
+
151
+ command = args[0].strip
152
+ if COMMANDS.include?(command)
153
+ send command.to_sym, args[1..-1]
154
+ else
155
+ STDERR.puts "command not found: #{args[0]}"
156
+ end
157
+ end
158
+ end
159
+
160
+ def help(args)
161
+ if !args.empty?
162
+ STDERR.puts "invalid argument(s): #{args}"
163
+ return
164
+ end
165
+
166
+ print "commands:\n\n"
167
+ print "help - print this message\n"
168
+ print "rspec - execute the specified spec file(s), wildcards allowed\n"
169
+ end
170
+
171
+ def rspec(args)
172
+ if args.empty?
173
+ STDERR.puts "you must specify one or more spec files"
174
+ return
175
+ end
176
+
177
+ # Allow wildcards.
178
+ filenames = args.flat_map { |filename| Dir.glob(filename) }
179
+
180
+ # Store formatters, if any, set by the init script. They will be cleared by RSpec below.
181
+ formatters = RSpec.configuration.formatters || []
182
+
183
+ # Initialize the runner. Also accessed by the signal handler above.
184
+ # RSpecInteractive::Runner sets RSpec.world.wants_to_quit to false. The signal
185
+ # handler sets it to true.
186
+ @mutex.synchronize { @runner = RSpecInteractive::Runner.new(filenames) }
187
+
188
+ # Run the specs.
189
+ @runner.run
190
+
191
+ # Clear the runner.
192
+ @mutex.synchronize { @runner = nil }
193
+
194
+ # Clear data from previous run.
195
+ RSpec.clear_examples
196
+
197
+ # Formatters get cleared by clear_examples. I don't understand why but the actual run
198
+ # also modifies the list of formatters. Reset them to whatever the init script set.
199
+ if !RSpec.configuration.formatters.empty?
200
+ raise "internal error. expected formatters to be cleared."
201
+ end
202
+ formatters.each { |f| RSpec.configuration.add_formatter(f) }
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,46 @@
1
+ require 'rspec/core'
2
+
3
+ module RSpecInteractive
4
+ class Runner
5
+ def initialize(args)
6
+ RSpec.world.wants_to_quit = false
7
+ @options = RSpec::Core::ConfigurationOptions.new(args)
8
+ end
9
+
10
+ def run()
11
+ begin
12
+ @options.configure(RSpec.configuration)
13
+ return if RSpec.world.wants_to_quit
14
+
15
+ RSpec.configuration.load_spec_files
16
+ ensure
17
+ RSpec.world.announce_filters
18
+ end
19
+
20
+ return RSpec.configuration.reporter.exit_early(RSpec.configuration.failure_exit_code) if RSpec.world.wants_to_quit
21
+
22
+ example_groups = RSpec.world.ordered_example_groups
23
+ examples_count = RSpec.world.example_count(example_groups)
24
+
25
+ success = RSpec.configuration.reporter.report(examples_count) do |reporter|
26
+ RSpec.configuration.with_suite_hooks do
27
+ if examples_count == 0 && RSpec.configuration.fail_if_no_examples
28
+ return RSpec.configuration.failure_exit_code
29
+ end
30
+
31
+ result = example_groups.map do |example_group|
32
+ example_group.run(reporter)
33
+ end
34
+
35
+ result.all?
36
+ end
37
+ end
38
+
39
+ success && !RSpec.world.non_example_failure ? 0 : RSpec.configuration.failure_exit_code
40
+ end
41
+
42
+ def quit
43
+ RSpec.world.wants_to_quit = true
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpecInteractive
4
+ VERSION = "0.1.0"
5
+ end
Binary file
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/rspec-interactive/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "rspec-interactive"
7
+ spec.version = RSpecInteractive::VERSION
8
+ spec.authors = ["Nick Dower"]
9
+ spec.email = ["nicholasdower@gmail.com"]
10
+
11
+ spec.summary = "An interactive console for running specs."
12
+ spec.description = "An interactive console for running specs."
13
+ spec.homepage = "https://github.com/nicholasdower/rspec-interactive"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/nicholasdower/rspec-interactive"
19
+ spec.metadata["changelog_uri"] = "https://github.com/nicholasdower/rspec-interactive"
20
+
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
23
+ end
24
+ spec.bindir = 'bin'
25
+ spec.executables << 'rspec-interactive'
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_dependency "listen"
29
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-interactive
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nick Dower
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-05-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: listen
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: An interactive console for running specs.
28
+ email:
29
+ - nicholasdower@gmail.com
30
+ executables:
31
+ - rspec-interactive
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - Gemfile
37
+ - Gemfile.lock
38
+ - LICENSE.txt
39
+ - Rakefile
40
+ - bin/console
41
+ - bin/rspec-interactive
42
+ - bin/setup
43
+ - lib/repo.rb
44
+ - lib/rspec-interactive.rb
45
+ - lib/rspec-interactive/runner.rb
46
+ - lib/rspec-interactive/version.rb
47
+ - rspec-console-0.1.0.gem
48
+ - rspec-interactive.gemspec
49
+ homepage: https://github.com/nicholasdower/rspec-interactive
50
+ licenses:
51
+ - MIT
52
+ metadata:
53
+ homepage_uri: https://github.com/nicholasdower/rspec-interactive
54
+ source_code_uri: https://github.com/nicholasdower/rspec-interactive
55
+ changelog_uri: https://github.com/nicholasdower/rspec-interactive
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 2.4.0
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubygems_version: 3.2.3
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: An interactive console for running specs.
75
+ test_files: []