jsh 0.0.1

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.
@@ -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
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ desc "Run all specs."
4
+ RSpec::Core::RakeTask.new(:rspec) do |spec|
5
+ spec.pattern = 'spec/*_spec.rb'
6
+ spec.rspec_opts = %w(--format p --color)
7
+ end
8
+ task :default => :rspec
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)
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'jsh/base'
2
+ require 'jsh/repl'
3
+
4
+ module JSH
5
+ def self.start(options = {})
6
+ REPL.start(options)
7
+ end
8
+ end
@@ -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
@@ -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) }
@@ -0,0 +1,10 @@
1
+ require 'v8'
2
+ require 'jsh/error_handler'
3
+
4
+ module JSH
5
+ class Context < V8::Context
6
+ def eval(*)
7
+ super rescue ErrorHandler.new($!)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ module JSH
2
+ class ErrorHandler
3
+ def initialize(error)
4
+ @error = error
5
+ end
6
+
7
+ def to_s
8
+ @error.message
9
+ end
10
+ end
11
+ end
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module JSH
2
+ VERSION = '0.0.1'
3
+ end
@@ -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
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe JSH::Commands do
4
+ let(:commands){ JSH::Commands.new }
5
+ describe ".register" do
6
+ it "before key" do
7
+ JSH::Commands.register(:hey){ "hey" }
8
+ expect(commands.commands[:hey].call).to eq("hey")
9
+ end
10
+ end
11
+ end
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,2 @@
1
+ $:.unshift(File.expand_path("../../lib", __FILE__))
2
+ require 'jsh'
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: