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 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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SR
4
+ # Semantic version of `sr`.
5
+ VERSION = '1.0.0'
6
+ end
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: []