sr 1.0.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 +7 -0
- data/README.md +71 -0
- data/UNLICENSE.md +24 -0
- data/bin/sr +18 -0
- data/lib/sr.rb +128 -0
- data/lib/sr/core_patches.rb +51 -0
- data/lib/sr/version.rb +6 -0
- data/sr.gemspec +25 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 061f3869f4efe07024e5a09389787a7b587a27e2
|
4
|
+
data.tar.gz: 36eebe8d7ee9d6db3d6ac9201edc0e802cae62f6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d2b46015eaecf94a3754b5c1a3c60aa64c54657bd552a73ec8f9e7cb13a270536141df2fc10e19e124b8d66fe299d51273b79ea91b79385e310f142b161b657d
|
7
|
+
data.tar.gz: aaa9b5446e9a5f352560f94a6217fb2c18b9d7d39e7f3d9d5ee8befcc6283f69f5411ce02fef041ffddaaec0f4b97b19b7b2363693ec8cbee2d32611ac613433
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# sr
|
2
|
+
|
3
|
+
`sr` is a simple, tiny, hackable REPL for Ruby.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
Simply call `sr` to use `sr` as a Ruby REPL like `irb` or `pry`:
|
8
|
+
|
9
|
+
```sh
|
10
|
+
$ sr
|
11
|
+
```
|
12
|
+
|
13
|
+
`sr` can also be used to include a REPL in your own code (for debugging
|
14
|
+
purposes, for example) by requiring 'sr' and calling `SR.repl`:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
require 'sr'
|
18
|
+
|
19
|
+
class Test
|
20
|
+
def initialize
|
21
|
+
SR.repl
|
22
|
+
end
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
You may also pass any object to the `SR.repl` method to have `sr` begin in the
|
27
|
+
context of that object instead of the default (the top level binding):
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
class Test
|
31
|
+
def initialize
|
32
|
+
SR.repl(self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
Within the REPL itself, you can manipulate the current context via the methods
|
38
|
+
`SR.bind`, `SR.unbind`, and `SR.reset_binding`:
|
39
|
+
|
40
|
+
```
|
41
|
+
$ sr
|
42
|
+
== main:
|
43
|
+
>> SR.bind Object.new
|
44
|
+
-> #<Binding:0x81338ed0>
|
45
|
+
== #<Object:0x81338f34>:
|
46
|
+
>> SR.unbind
|
47
|
+
-> #<Binding:0x8124dc78>
|
48
|
+
== main:
|
49
|
+
>>
|
50
|
+
```
|
51
|
+
|
52
|
+
`SR.bind` sets the current context to the given object and pushes the previous
|
53
|
+
context onto the context stack; `SR.unbind` retrieves the last object from the
|
54
|
+
stack and sets the context to it. `SR.reset_binding` may be used without
|
55
|
+
arguments at any time to completely clear the context stack and reset the
|
56
|
+
binding to the default (usually the top level binding).
|
57
|
+
|
58
|
+
When given an argument, however, `SR.reset_binding` clears the context stack
|
59
|
+
and resets the default binding to the _given object_.
|
60
|
+
|
61
|
+
## Configuration
|
62
|
+
|
63
|
+
`sr` looks for its configuration in the 'sr/config.rb' file within
|
64
|
+
**$XDG_CONFIG_DIRS** and **$XDG_CONFIG_HOME**. By default, these files would
|
65
|
+
be '/etc/xdg/sr/config.rb' for system-wide configuration and
|
66
|
+
'~/.config/sr/config.rb' for user-specific configuration. These files are
|
67
|
+
simply Ruby files which are loaded into `sr` via `require`.
|
68
|
+
|
69
|
+
## License
|
70
|
+
|
71
|
+
`sr` is free and unencumbered software released into the public domain.
|
data/UNLICENSE.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
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 NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, please refer to <http://unlicense.org/>
|
data/bin/sr
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$0.start_with?('/') ? require('sr') : require_relative('../lib/sr')
|
5
|
+
|
6
|
+
if ARGV.size > 0
|
7
|
+
valid = %w(-h --help).any? { |a| ARGV.first == a }
|
8
|
+
(valid ? $stdout : $stderr).puts <<-END
|
9
|
+
sr #{SR::VERSION}
|
10
|
+
|
11
|
+
sr is a simple, tiny, hackable REPL for Ruby.
|
12
|
+
|
13
|
+
usage: #{$0} [-h|--help]
|
14
|
+
END
|
15
|
+
exit valid ? 0 : 1
|
16
|
+
end
|
17
|
+
|
18
|
+
SR.repl
|
data/lib/sr.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
require_relative 'sr/core_patches'
|
2
|
+
require_relative 'sr/version'
|
3
|
+
require 'readline'
|
4
|
+
require 'sxdg/as_xdg'
|
5
|
+
|
6
|
+
# `sr` is a simple, tiny, hackable REPL for Ruby.
|
7
|
+
module SR
|
8
|
+
class << self
|
9
|
+
# @return [Binding] the currently bound SR context
|
10
|
+
attr_reader :context
|
11
|
+
|
12
|
+
# @return [Hash{Class=>#call}] the hash of exception handlers
|
13
|
+
attr_reader :handlers
|
14
|
+
|
15
|
+
# @return [Hash{Symbol=>#call,#to_s}] the hash of prompts to display; valid
|
16
|
+
# keys are `:error`, `:input`, `:more`, and `:return`; valid values must
|
17
|
+
# respond to `#to_s`
|
18
|
+
attr_reader :prompt
|
19
|
+
end
|
20
|
+
|
21
|
+
# The array of known configuration files for SR which should be loaded when
|
22
|
+
# the read-eval-print loop is started.
|
23
|
+
CONFIGURATION_FILES = (XDG_CONFIG_DIRS + [XDG_CONFIG_HOME]).map! do |dir|
|
24
|
+
(File.expand_path(dir) + '/sr/config.rb').freeze
|
25
|
+
end.select! { |file| File.exist?(file) }.freeze
|
26
|
+
|
27
|
+
# The regular expression used to determine whether or not a syntax error is
|
28
|
+
# a valid continuation which should prompt for more user input.
|
29
|
+
CONTINUATION_REGEXP =
|
30
|
+
/unterminated (?:regexp|string)|unexpected end-of-input|tIDENTIFIER/
|
31
|
+
|
32
|
+
@prompt = {
|
33
|
+
error: proc { |e| "!! #{e.class}: #{e.message}" },
|
34
|
+
input: proc { "== #{@context.receiver}:\n>> " },
|
35
|
+
more: '.. ',
|
36
|
+
return: proc { |v| "-> #{v.inspect}" },
|
37
|
+
}
|
38
|
+
|
39
|
+
@handlers = {
|
40
|
+
SyntaxError => proc do |e, i|
|
41
|
+
e.message =~ CONTINUATION_REGEXP ? read_eval_print(i) : report(e)
|
42
|
+
end,
|
43
|
+
Interrupt => proc do |_, _, m|
|
44
|
+
m ? (puts ; read_eval_print) : @enabled = false
|
45
|
+
end,
|
46
|
+
SystemExit => proc { @enabled = false },
|
47
|
+
Exception => proc { |e| report(e) },
|
48
|
+
}
|
49
|
+
|
50
|
+
@context = @ORIGINAL_CONTEXT = TOPLEVEL_BINDING
|
51
|
+
|
52
|
+
@stack = []
|
53
|
+
|
54
|
+
# @return [Array<Binding>] a copy of the SR context stack
|
55
|
+
def self.stack
|
56
|
+
@stack.dup
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param object [#__binding__] the object to bind the SR context to
|
60
|
+
# @return [Binding] the new SR context
|
61
|
+
def self.bind(object)
|
62
|
+
@stack.push(@context)
|
63
|
+
@context = object.__binding__
|
64
|
+
end
|
65
|
+
class << self ; alias_method :context=, :bind ; end
|
66
|
+
|
67
|
+
# @return [Binding] the previous binding in the SR context stack or the
|
68
|
+
# default context if no previous binding exists
|
69
|
+
def self.unbind
|
70
|
+
@context = @stack.empty? ? @ORIGINAL_CONTEXT : @stack.pop
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param context [#__binding__] the context to use as the new default
|
74
|
+
# context; defaults to the top level binding if no context given
|
75
|
+
# @return [Binding] the reset binding context
|
76
|
+
def self.reset_binding(context = nil)
|
77
|
+
@stack.clear
|
78
|
+
context = context.__binding__ unless context.nil?
|
79
|
+
@context = @ORIGINAL_CONTEXT = context ? context : TOPLEVEL_BINDING
|
80
|
+
end
|
81
|
+
|
82
|
+
# @param key [Symbol] the key of the @prompt hash to call or fetch
|
83
|
+
# @param args [Array<Object>] the arguments to be passed to the value of the
|
84
|
+
# given key if the value is a proc
|
85
|
+
# @return [String] the prompt to display
|
86
|
+
private_class_method def self.call_prompt(key, *args)
|
87
|
+
prompt = @prompt[key]
|
88
|
+
prompt.respond_to?(:call) ? prompt.call(*args) : (prompt || '?? ')
|
89
|
+
end
|
90
|
+
|
91
|
+
# @param ex [Exception] the exception to report
|
92
|
+
# @return [void]
|
93
|
+
private_class_method def self.report(ex)
|
94
|
+
@context.local_variable_set(:_, ex)
|
95
|
+
$stderr.puts call_prompt(:error, ex) if @prompt[:error]
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [Array<Boolean>] an array representing whether or not each
|
99
|
+
# configuration file was loaded by `require`
|
100
|
+
private_class_method def self.load_configuration
|
101
|
+
CONFIGURATION_FILES.map(&method(:require))
|
102
|
+
end
|
103
|
+
|
104
|
+
# @param more [String] the previous input to be evaluated along with the
|
105
|
+
# given input
|
106
|
+
# @return [Object] the return value of the given input
|
107
|
+
private_class_method def self.read_eval_print(more = nil)
|
108
|
+
input = "#{Readline.readline(call_prompt(more ? :more : :input), true)}\n"
|
109
|
+
input = "#{more}#{input}" if more
|
110
|
+
value = @context.eval("_ = #{input}")
|
111
|
+
puts call_prompt(:return, value) if @prompt[:return]
|
112
|
+
rescue *@handlers.keys => ex
|
113
|
+
@handlers.find { |(c,_)| ex.is_a?(c) }[1].call(ex, input, more)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Loads the configuration for SR, resets the default binding to the given
|
117
|
+
# `context`, and starts the read-eval-print loop.
|
118
|
+
#
|
119
|
+
# @param context [#__binding__] the object providing the default context for
|
120
|
+
# `sr`; defaults to the top level binding if no context given
|
121
|
+
# @return [void]
|
122
|
+
def self.repl(context = nil)
|
123
|
+
load_configuration
|
124
|
+
reset_binding(context) unless context.nil?
|
125
|
+
@enabled = true
|
126
|
+
read_eval_print while @enabled
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BasicObject
|
4
|
+
# Used by `sr` to allow the same general access to `BasicObject` instances
|
5
|
+
# that `Object#__binding__` permits for all other objects.
|
6
|
+
#
|
7
|
+
# @return [Binding] an unclean `Binding` instance representing the current
|
8
|
+
# binding of this object
|
9
|
+
def __binding__
|
10
|
+
(class << self ; self end).class_eval <<-END
|
11
|
+
def __fallback_binding__
|
12
|
+
::Kernel.binding
|
13
|
+
end
|
14
|
+
END
|
15
|
+
self.__fallback_binding__
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [String] a simple inspection string
|
19
|
+
def inspect
|
20
|
+
'#<BasicObject>'
|
21
|
+
end
|
22
|
+
alias_method :to_s, :inspect
|
23
|
+
end
|
24
|
+
|
25
|
+
class Object
|
26
|
+
# Used extensively by `sr` -- this method is essentially what allows
|
27
|
+
# interactive code to reach into the state of objects at runtime.
|
28
|
+
#
|
29
|
+
# @return [Binding] a clean `Binding` instance representing the current
|
30
|
+
# binding of this object
|
31
|
+
def __binding__
|
32
|
+
is_a?(Module) ? class_eval('binding') : binding
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Kernel
|
37
|
+
# @return [Binding] the top level binding
|
38
|
+
def main
|
39
|
+
TOPLEVEL_BINDING
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class << TOPLEVEL_BINDING
|
44
|
+
# @note this method exists entirely for aesthetic purposes (when the default
|
45
|
+
# input prompt calls `@context.receiver` to get a name for the current
|
46
|
+
# binding/context)
|
47
|
+
# @return [String] a simple inspection string
|
48
|
+
def to_s
|
49
|
+
'main'
|
50
|
+
end
|
51
|
+
end
|
data/lib/sr/version.rb
ADDED
data/sr.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'sr/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'sr'
|
10
|
+
spec.version = SR::VERSION
|
11
|
+
spec.summary = 'sr is a simple, tiny, hackable REPL for Ruby.'
|
12
|
+
spec.description = spec.summary
|
13
|
+
|
14
|
+
spec.authors = ['Robin Owen']
|
15
|
+
spec.email = ['vthrenody@gmail.com']
|
16
|
+
spec.homepage = 'https://github.com/vthrenody/sr'
|
17
|
+
|
18
|
+
spec.files = %w(sr.gemspec) + Dir['*.md', 'lib/**/*.rb']
|
19
|
+
spec.executables = 'sr'
|
20
|
+
spec.require_path = 'lib'
|
21
|
+
spec.license = 'Unlicense'
|
22
|
+
|
23
|
+
spec.add_runtime_dependency 'sxdg', '~> 1.0'
|
24
|
+
spec.add_development_dependency 'sgem', '~> 1.0'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robin Owen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sxdg
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sgem
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
description: sr is a simple, tiny, hackable REPL for Ruby.
|
42
|
+
email:
|
43
|
+
- vthrenody@gmail.com
|
44
|
+
executables:
|
45
|
+
- sr
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- README.md
|
50
|
+
- UNLICENSE.md
|
51
|
+
- bin/sr
|
52
|
+
- lib/sr.rb
|
53
|
+
- lib/sr/core_patches.rb
|
54
|
+
- lib/sr/version.rb
|
55
|
+
- sr.gemspec
|
56
|
+
homepage: https://github.com/vthrenody/sr
|
57
|
+
licenses:
|
58
|
+
- Unlicense
|
59
|
+
metadata: {}
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 2.6.8
|
77
|
+
signing_key:
|
78
|
+
specification_version: 4
|
79
|
+
summary: sr is a simple, tiny, hackable REPL for Ruby.
|
80
|
+
test_files: []
|