jsh 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/README.md +77 -0
- data/Rakefile +8 -0
- data/bin/jsh +16 -0
- data/jsh.gemspec +17 -0
- data/lib/jsh.rb +8 -0
- data/lib/jsh/base.rb +24 -0
- data/lib/jsh/commands.rb +20 -0
- data/lib/jsh/context.rb +10 -0
- data/lib/jsh/error_handler.rb +11 -0
- data/lib/jsh/hooks.rb +25 -0
- data/lib/jsh/pair.rb +27 -0
- data/lib/jsh/repl.rb +82 -0
- data/lib/jsh/version.rb +3 -0
- data/sample.rb +17 -0
- data/spec/commands_spec.rb +11 -0
- data/spec/hooks_spec.rb +18 -0
- data/spec/pair_spec.rb +23 -0
- data/spec/repl_spec.rb +49 -0
- data/spec/spec_helper.rb +2 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 23c0a2fde3b8781431b7c70fa0afc29b9cd750ca
|
4
|
+
data.tar.gz: 2d8c98f43f7b7a62ae0b7d6f66b9e04863763cc8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 639a4e8efa80029bed0429bf29c8ad0fa4d5da544f6be94e43294a8bb27663c85d4ab1dc2bc57fe9dd305079e9c6966afabb331a7336f80f42155bcbb633af23
|
7
|
+
data.tar.gz: b9d738b6b2cc74d8f536d11d1032b34af3db1494182609ffaba78d9c7451d5986b6e0860a4e07e93b64b72df86dc5664b885f8252a458a78563812a27644bbd2
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# JSH
|
2
|
+
|
3
|
+
This is an interactive shell for JavaScript which is written in Ruby.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
### Basic
|
8
|
+
|
9
|
+
```shell
|
10
|
+
$ jsh
|
11
|
+
[0] jsh) 1 + 1;
|
12
|
+
2
|
13
|
+
[1] jsh) "hello world".length;
|
14
|
+
11
|
15
|
+
[2] jsh) (function fib(n){ return (n < 2) ? 1 : fib(n - 2) + fib(n - 1); })(10);
|
16
|
+
89
|
17
|
+
```
|
18
|
+
|
19
|
+
### Customize
|
20
|
+
|
21
|
+
You can customize in these way.
|
22
|
+
Then save and execute it.
|
23
|
+
|
24
|
+
#### `hooks`
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require 'jsh'
|
28
|
+
|
29
|
+
JSH::Hooks.register(:before) do
|
30
|
+
puts "before hook!"
|
31
|
+
end
|
32
|
+
|
33
|
+
JSH::Hooks.register(:after) do
|
34
|
+
puts "after hook!"
|
35
|
+
end
|
36
|
+
|
37
|
+
JSH.start
|
38
|
+
```
|
39
|
+
|
40
|
+
#### `commands`
|
41
|
+
|
42
|
+
Save this file as `foo.rb`.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
require 'jsh'
|
46
|
+
|
47
|
+
JSH::Commands.register(:enable_to_say) do |jsh|
|
48
|
+
jsh.context['say'] = lambda{|this, word, times| word * times }
|
49
|
+
end
|
50
|
+
|
51
|
+
JSH.start
|
52
|
+
```
|
53
|
+
|
54
|
+
Then execute it.
|
55
|
+
|
56
|
+
```shell
|
57
|
+
$ ruby foo.rb
|
58
|
+
[0] jsh) enable_to_say
|
59
|
+
[1] jsh) say('Hello', 3)
|
60
|
+
HelloHelloHello
|
61
|
+
```
|
62
|
+
|
63
|
+
### Options
|
64
|
+
|
65
|
+
#### `-r --readline=BOOLEAN`
|
66
|
+
|
67
|
+
readline option should be boolean.
|
68
|
+
default value is `true`.
|
69
|
+
if set `false` to this option, jsh will not use Readline to input.
|
70
|
+
|
71
|
+
#### `--noprompt`
|
72
|
+
|
73
|
+
if use `--noprompt`, jsh will not print prompt.
|
74
|
+
|
75
|
+
## License
|
76
|
+
|
77
|
+
The MIT License
|
data/Rakefile
ADDED
data/bin/jsh
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
4
|
+
|
5
|
+
require 'jsh'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
options, option_parser = {}, OptionParser.new
|
9
|
+
option_parser.version = JSH::VERSION
|
10
|
+
|
11
|
+
option_parser.on('-r', '--readline BOOLEAN', TrueClass){|v| p(v.class);options[:readline] = v }
|
12
|
+
option_parser.on('--noprompt'){|v| options[:noprompt] = v }
|
13
|
+
|
14
|
+
option_parser.parse!(ARGV)
|
15
|
+
|
16
|
+
JSH.start(options)
|
data/jsh.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path("../lib/jsh/version", __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new "jsh", JSH::VERSION do |s|
|
4
|
+
s.description = "Interactive shell for JavaScript which is written in Ruby."
|
5
|
+
s.summary = "Interactive shell for JavaScript"
|
6
|
+
s.author = "namusyaka"
|
7
|
+
s.email = "namusyaka@gmail.com"
|
8
|
+
s.homepage = "https://github.com/namusyaka/jsh"
|
9
|
+
s.files = `git ls-files`.split($/)
|
10
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
11
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.license = "MIT"
|
14
|
+
|
15
|
+
s.add_dependency "therubyracer"
|
16
|
+
s.add_development_dependency "rspec"
|
17
|
+
end
|
data/lib/jsh.rb
ADDED
data/lib/jsh/base.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'jsh/context'
|
2
|
+
require 'jsh/commands'
|
3
|
+
require 'jsh/hooks'
|
4
|
+
require 'jsh/version'
|
5
|
+
|
6
|
+
module JSH
|
7
|
+
class Base
|
8
|
+
def context
|
9
|
+
@context ||= Context.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def output
|
13
|
+
STDOUT
|
14
|
+
end
|
15
|
+
|
16
|
+
def commands
|
17
|
+
@commands ||= Commands.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def hooks
|
21
|
+
@hooks ||= Hooks.new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/jsh/commands.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'jsh/pair'
|
2
|
+
|
3
|
+
module JSH
|
4
|
+
class Commands < Pair
|
5
|
+
DEFAULT_COMMANDS = {}
|
6
|
+
|
7
|
+
alias commands data
|
8
|
+
|
9
|
+
def default_data
|
10
|
+
DEFAULT_COMMANDS
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.register(key, &block)
|
14
|
+
DEFAULT_COMMANDS[key] = block
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
exit_lambda = lambda { throw :halt }
|
20
|
+
%w[quit exit].each{|key| JSH::Commands.register(key.to_sym, &exit_lambda) }
|
data/lib/jsh/context.rb
ADDED
data/lib/jsh/hooks.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'jsh/pair'
|
2
|
+
|
3
|
+
module JSH
|
4
|
+
class Hooks < Pair
|
5
|
+
DEFAULT_HOOKS = {
|
6
|
+
:before => [],
|
7
|
+
:after => []
|
8
|
+
}
|
9
|
+
|
10
|
+
alias hooks data
|
11
|
+
|
12
|
+
def default_data
|
13
|
+
DEFAULT_HOOKS
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register(key, &block)
|
17
|
+
(DEFAULT_HOOKS[key] ||= []) << block
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
JSH::Hooks.register(:before) do
|
23
|
+
puts "JSH ver #{JSH::VERSION}"
|
24
|
+
puts "If you want to stop, exec `exit` or `quit`"
|
25
|
+
end
|
data/lib/jsh/pair.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module JSH
|
2
|
+
class Pair
|
3
|
+
attr_accessor :data
|
4
|
+
|
5
|
+
def initialize(data = nil)
|
6
|
+
@data = data || default_data
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](key)
|
10
|
+
@data[key]
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(key, value)
|
14
|
+
@data[key] = value
|
15
|
+
end
|
16
|
+
|
17
|
+
def all
|
18
|
+
@data
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def default_data
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/jsh/repl.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'jsh/base'
|
2
|
+
require 'readline'
|
3
|
+
|
4
|
+
module JSH
|
5
|
+
class REPL
|
6
|
+
DEFAULT_PROMPT = "[{:number_of_times:}] jsh) "
|
7
|
+
PROMPT_SYNTAX = /\{:(.+?):\}/
|
8
|
+
|
9
|
+
attr_accessor :base, :prompt
|
10
|
+
|
11
|
+
def self.start(options)
|
12
|
+
new(Base.new, options).start
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(base, options = {})
|
16
|
+
@base = base
|
17
|
+
@noprompt = options.fetch(:noprompt, false)
|
18
|
+
@readline = options.fetch(:readline, true)
|
19
|
+
@options = options
|
20
|
+
config[:number_of_times] = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
call_hook(:before)
|
25
|
+
repl
|
26
|
+
call_hook(:after)
|
27
|
+
end
|
28
|
+
|
29
|
+
def repl
|
30
|
+
catch :halt do
|
31
|
+
loop do
|
32
|
+
input = read
|
33
|
+
if command = commands[input.to_sym]
|
34
|
+
command.arity != 0 ? command.call(base) : command.call
|
35
|
+
else
|
36
|
+
output.puts((input.nil? or input.empty?) ? "" : base.context.eval(input))
|
37
|
+
end
|
38
|
+
config[:number_of_times] += 1
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def noprompt?
|
44
|
+
@noprompt
|
45
|
+
end
|
46
|
+
|
47
|
+
def readline?
|
48
|
+
@readline
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def setup_prompt
|
54
|
+
(self.prompt ||= DEFAULT_PROMPT).gsub(PROMPT_SYNTAX){ config[$1.to_sym] || key }
|
55
|
+
end
|
56
|
+
|
57
|
+
def read
|
58
|
+
if readline?
|
59
|
+
Readline.readline(noprompt? ? "" : setup_prompt, true)
|
60
|
+
else
|
61
|
+
print setup_prompt unless noprompt?
|
62
|
+
gets.chomp
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def config
|
67
|
+
@config ||= {}
|
68
|
+
end
|
69
|
+
|
70
|
+
def output
|
71
|
+
@output ||= base.output
|
72
|
+
end
|
73
|
+
|
74
|
+
def commands
|
75
|
+
@commands ||= base.commands
|
76
|
+
end
|
77
|
+
|
78
|
+
def call_hook(key)
|
79
|
+
base.hooks[key].map(&:call)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/jsh/version.rb
ADDED
data/sample.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.expand_path("../lib", __FILE__))
|
3
|
+
require 'jsh'
|
4
|
+
|
5
|
+
JSH::Hooks.register(:before) do
|
6
|
+
puts "before hook!"
|
7
|
+
end
|
8
|
+
|
9
|
+
JSH::Hooks.register(:after) do
|
10
|
+
puts "after hook!"
|
11
|
+
end
|
12
|
+
|
13
|
+
JSH::Commands.register(:enable_to_say) do |jsh|
|
14
|
+
jsh.context['say'] = lambda{|this, word, times| word * times }
|
15
|
+
end
|
16
|
+
|
17
|
+
JSH.start
|
data/spec/hooks_spec.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JSH::Hooks do
|
4
|
+
let(:hooks){ JSH::Hooks.new }
|
5
|
+
describe ".register" do
|
6
|
+
context "for key that is pre-defined" do
|
7
|
+
it "before key" do
|
8
|
+
JSH::Hooks.register(:before){ "hello" }
|
9
|
+
expect(hooks.hooks[:before].last.call).to eq("hello")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "after key" do
|
13
|
+
JSH::Hooks.register(:after){ "hello" }
|
14
|
+
expect(hooks.hooks[:after].last.call).to eq("hello")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/spec/pair_spec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JSH::Pair do
|
4
|
+
let(:pair){ JSH::Pair.new }
|
5
|
+
|
6
|
+
describe "#[]" do
|
7
|
+
before { pair.data[:foo] = :bar }
|
8
|
+
subject { pair[:foo] }
|
9
|
+
it { should eq(:bar) }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#[]=" do
|
13
|
+
before { pair[:bar] = :foo }
|
14
|
+
it { expect(pair[:bar]).to eq(:foo) }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "all" do
|
18
|
+
let(:all){ 10.times{|n| pair["test_#{n}"] = n }}
|
19
|
+
before { pair.data = all }
|
20
|
+
subject { pair.data }
|
21
|
+
it { should eq(all) }
|
22
|
+
end
|
23
|
+
end
|
data/spec/repl_spec.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JSH::REPL do
|
4
|
+
describe ".start" do
|
5
|
+
context "with readline option" do
|
6
|
+
it "should set true to @readline." do
|
7
|
+
repl = JSH::REPL.new(JSH::Base, :readline => true)
|
8
|
+
expect(repl.instance_variable_get(:@readline)).to be_true
|
9
|
+
expect(repl.readline?).to be_true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "without readline option" do
|
14
|
+
it "should set true as default value to @readline." do
|
15
|
+
repl = JSH::REPL.new(JSH::Base)
|
16
|
+
expect(repl.instance_variable_get(:@readline)).to be_true
|
17
|
+
expect(repl.readline?).to be_true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with noprompt option" do
|
22
|
+
it "should set true to @noprompt." do
|
23
|
+
repl = JSH::REPL.new(JSH::Base, :noprompt => true)
|
24
|
+
expect(repl.instance_variable_get(:@noprompt)).to be_true
|
25
|
+
expect(repl.noprompt?).to be_true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "without noprompt option" do
|
30
|
+
it "should set false as default value to @noprompt." do
|
31
|
+
repl = JSH::REPL.new(JSH::Base)
|
32
|
+
expect(repl.instance_variable_get(:@noprompt)).to be_false
|
33
|
+
expect(repl.noprompt?).to be_false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#call_hook" do
|
39
|
+
let(:repl){ JSH::REPL.new(JSH::Base.new) }
|
40
|
+
let(:test){ [] }
|
41
|
+
before do
|
42
|
+
JSH::Hooks.register(:before){ test << :before }
|
43
|
+
JSH::Hooks.register(:after){ test << :after }
|
44
|
+
Readline.stub(:readline).and_return(:quit)
|
45
|
+
repl.start
|
46
|
+
end
|
47
|
+
it { expect(test).to eq([:before, :after]) }
|
48
|
+
end
|
49
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jsh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- namusyaka
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-11-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: therubyracer
|
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
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Interactive shell for JavaScript which is written in Ruby.
|
42
|
+
email: namusyaka@gmail.com
|
43
|
+
executables:
|
44
|
+
- jsh
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- Gemfile
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- bin/jsh
|
52
|
+
- jsh.gemspec
|
53
|
+
- lib/jsh.rb
|
54
|
+
- lib/jsh/base.rb
|
55
|
+
- lib/jsh/commands.rb
|
56
|
+
- lib/jsh/context.rb
|
57
|
+
- lib/jsh/error_handler.rb
|
58
|
+
- lib/jsh/hooks.rb
|
59
|
+
- lib/jsh/pair.rb
|
60
|
+
- lib/jsh/repl.rb
|
61
|
+
- lib/jsh/version.rb
|
62
|
+
- sample.rb
|
63
|
+
- spec/commands_spec.rb
|
64
|
+
- spec/hooks_spec.rb
|
65
|
+
- spec/pair_spec.rb
|
66
|
+
- spec/repl_spec.rb
|
67
|
+
- spec/spec_helper.rb
|
68
|
+
homepage: https://github.com/namusyaka/jsh
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.0.2
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Interactive shell for JavaScript
|
92
|
+
test_files:
|
93
|
+
- spec/commands_spec.rb
|
94
|
+
- spec/hooks_spec.rb
|
95
|
+
- spec/pair_spec.rb
|
96
|
+
- spec/repl_spec.rb
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
has_rdoc:
|