sr 1.0.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
+ 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: []