instant 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.
- data/Gemfile +19 -0
- data/Manifest +12 -0
- data/Procfile +1 -0
- data/README.md +38 -0
- data/Rakefile +14 -0
- data/instant.gemspec +29 -0
- data/lib/instant.rb +5 -0
- data/lib/instant/context.rb +93 -0
- data/lib/instant/processor.rb +63 -0
- data/lib/instant/runner.rb +36 -0
- data/lib/instant/sinatra/app.rb +30 -0
- data/lib/instant/version.rb +3 -0
- metadata +69 -0
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
gem "ruby2ruby", "~> 1.3.1"
|
4
|
+
gem "ruby_parser", "~> 2.3.1"
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem "rspec", "~> 2.9.0"
|
8
|
+
gem "rake", "~> 1.1"
|
9
|
+
gem "echoe"
|
10
|
+
gem "autotest"
|
11
|
+
gem "pry"
|
12
|
+
end
|
13
|
+
|
14
|
+
group :production do
|
15
|
+
gem "sinatra", "~> 1.3.2"
|
16
|
+
gem "sinatra-synchrony", "~> 0.1.1"
|
17
|
+
gem "thin", "~> 1.3.1"
|
18
|
+
gem "foreman"
|
19
|
+
end
|
data/Manifest
ADDED
data/Procfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
server: bundle exec rackup -p $PORT
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Instant
|
2
|
+
|
3
|
+
An experiment on real time visualize development tool, inspired by
|
4
|
+
[Bret Victor's Inventing on Principle](http://vimeo.com/36579366)
|
5
|
+
talk and [@ermau C# Implementation](https://github.com/ermau/Instant).
|
6
|
+
|
7
|
+
## Install as gem
|
8
|
+
|
9
|
+
gem install instant
|
10
|
+
|
11
|
+
require 'instant'
|
12
|
+
|
13
|
+
runner = Instant::Runner.new
|
14
|
+
runner.run "def hello(a); a = 10 + a; end; hello(20)"
|
15
|
+
|
16
|
+
## Run a demo
|
17
|
+
|
18
|
+

|
19
|
+
|
20
|
+
You can run it live at [instant-ruby.herokuapp.com](http://instant-ruby.herokuapp.com).
|
21
|
+
|
22
|
+
## Run locally
|
23
|
+
|
24
|
+
bundle install
|
25
|
+
foreman start
|
26
|
+
|
27
|
+
then open your browser at http://127.0.0.1:5000/
|
28
|
+
|
29
|
+
## Credits
|
30
|
+
|
31
|
+
- [Bret Victor's Inventing on Principle](http://vimeo.com/36579366) - this talk is just eyes opening!
|
32
|
+
- [Instant (C#)](https://github.com/ermau/Instant) @ermau's C# implementation motivate me to create this Ruby port
|
33
|
+
- [ACE](http://ace.ajax.org/) - Really cool AJAX based IDE
|
34
|
+
- [RubyParser](https://github.com/seattlerb/ruby_parser) and [Ruby2Ruby](https://github.com/seattlerb/ruby2ruby) - created by Seattle.rb, they make this app possible
|
35
|
+
|
36
|
+
## Contact
|
37
|
+
|
38
|
+
[@siuying](http://twitter.com/siuying)
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('instant', '0.0.1') do |p|
|
6
|
+
p.description = "Generate a unique token with Active Record."
|
7
|
+
p.url = "http://github.com/ryanb/uniquify"
|
8
|
+
p.author = "Ryan Bates"
|
9
|
+
p.email = "ryan@railscasts.com"
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
|
+
p.development_dependencies = []
|
12
|
+
end
|
13
|
+
|
14
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/instant.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "instant"
|
5
|
+
s.version = "0.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Ryan Bates"]
|
9
|
+
s.date = "2012-04-19"
|
10
|
+
s.description = "Generate a unique token with Active Record."
|
11
|
+
s.email = "ryan@railscasts.com"
|
12
|
+
s.extra_rdoc_files = ["README.md", "lib/instant.rb", "lib/instant/context.rb", "lib/instant/processor.rb", "lib/instant/runner.rb", "lib/instant/sinatra/app.rb", "lib/instant/version.rb"]
|
13
|
+
s.files = ["Gemfile", "Procfile", "README.md", "Rakefile", "instant.gemspec", "lib/instant.rb", "lib/instant/context.rb", "lib/instant/processor.rb", "lib/instant/runner.rb", "lib/instant/sinatra/app.rb", "lib/instant/version.rb", "Manifest"]
|
14
|
+
s.homepage = "http://github.com/ryanb/uniquify"
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Instant", "--main", "README.md"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = "instant"
|
18
|
+
s.rubygems_version = "1.8.15"
|
19
|
+
s.summary = "Generate a unique token with Active Record."
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
s.specification_version = 3
|
23
|
+
|
24
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
25
|
+
else
|
26
|
+
end
|
27
|
+
else
|
28
|
+
end
|
29
|
+
end
|
data/lib/instant.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module Instant
|
5
|
+
class LoopTooDeepError < StandardError; end
|
6
|
+
|
7
|
+
class LogCollector
|
8
|
+
def initialize(keys=[])
|
9
|
+
@logs = {}
|
10
|
+
keys.each {|k| @logs[k] = [] } if keys
|
11
|
+
end
|
12
|
+
|
13
|
+
def append(key, value)
|
14
|
+
@logs[key] ||= []
|
15
|
+
|
16
|
+
if @logs[key].length + 1 > 10
|
17
|
+
raise ::Instant::LoopTooDeepError.new("Loop too much")
|
18
|
+
end
|
19
|
+
|
20
|
+
@logs[key] << value
|
21
|
+
end
|
22
|
+
|
23
|
+
def fill_empty
|
24
|
+
max_length = @logs.values.collect{|v| v.length}.max
|
25
|
+
fill_keys = @logs.keys.select {|k| @logs[k].length < max_length }
|
26
|
+
fill_keys.each do |key|
|
27
|
+
@logs[key] << ""
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
@logs.collect do |key, values|
|
33
|
+
value = values.collect {|v| v.to_s.center(5) }.join("|")
|
34
|
+
"#{key.to_s.ljust(8)} = #{value} "
|
35
|
+
end.join("\n")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Context
|
40
|
+
def initialize
|
41
|
+
@stringio = StringIO.new
|
42
|
+
@assigns = Set.new
|
43
|
+
@loop_counter = 0
|
44
|
+
@logger = Logger.new(@stringio)
|
45
|
+
@logger.formatter = proc { |severity, datetime, progname, msg| "#{msg}\n" }
|
46
|
+
|
47
|
+
@log_collectors = []
|
48
|
+
@loop_counter = 0
|
49
|
+
end
|
50
|
+
|
51
|
+
def log_assign(name, value)
|
52
|
+
if @log_collectors.size > 0
|
53
|
+
@log_collectors.last.append name, value
|
54
|
+
else
|
55
|
+
@assigns << name
|
56
|
+
@logger.info "#{name.to_s.ljust(8)} = #{value.to_s.center(5)}"
|
57
|
+
end
|
58
|
+
return value
|
59
|
+
end
|
60
|
+
|
61
|
+
def loop_begin
|
62
|
+
@log_collectors.push LogCollector.new(@assigns)
|
63
|
+
end
|
64
|
+
|
65
|
+
def loop_inside_begin
|
66
|
+
end
|
67
|
+
|
68
|
+
def loop_inside_end
|
69
|
+
@log_collectors.last.fill_empty
|
70
|
+
@loop_counter = @loop_counter + 1
|
71
|
+
|
72
|
+
if @loop_counter > 1000
|
73
|
+
raise ::Instant::LoopTooDeepError.new("Loop too much")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def loop_end
|
78
|
+
collector = @log_collectors.pop
|
79
|
+
@logger.info collector.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
def close
|
83
|
+
while collector = @log_collectors.pop
|
84
|
+
@logger.info collector.to_s
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s
|
89
|
+
@stringio.string
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'ruby_parser'
|
2
|
+
require 'ruby2ruby'
|
3
|
+
|
4
|
+
module Instant
|
5
|
+
class Processor
|
6
|
+
def initialize
|
7
|
+
@parser = RubyParser.new
|
8
|
+
@generator = Ruby2Ruby.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def process(source)
|
12
|
+
sexp = @parser.process(source)
|
13
|
+
sexp = process_sexp(sexp)
|
14
|
+
@generator.process(sexp)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def process_sexp(sexp)
|
19
|
+
sexp.inject(s()) do |s, node|
|
20
|
+
case node
|
21
|
+
when Sexp
|
22
|
+
name = node[0]
|
23
|
+
case name
|
24
|
+
when :lasgn
|
25
|
+
s << log_lasgn(node)
|
26
|
+
when :while
|
27
|
+
s << s(:call, nil, :loop_begin, s(:arglist))
|
28
|
+
s << log_while(node)
|
29
|
+
s << s(:call, nil, :loop_end, s(:arglist))
|
30
|
+
else
|
31
|
+
s << process_sexp(node)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
s << node
|
35
|
+
end
|
36
|
+
s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# watch for :lasgn
|
41
|
+
def log_lasgn(node)
|
42
|
+
name = node[1]
|
43
|
+
expr = node[2]
|
44
|
+
s(:lasgn,
|
45
|
+
name,
|
46
|
+
s(:call, nil, :log_assign, s(:arglist, s(:lit, name), process_sexp(expr))))
|
47
|
+
end
|
48
|
+
|
49
|
+
# watch for :while
|
50
|
+
def log_while(node)
|
51
|
+
s(:while,
|
52
|
+
process_sexp(node[1]),
|
53
|
+
s(:block,
|
54
|
+
s(:call, nil, :loop_inside_begin, s(:arglist)),
|
55
|
+
process_sexp(node[2]),
|
56
|
+
s(:call, nil, :loop_inside_end, s(:arglist))),
|
57
|
+
true)
|
58
|
+
end
|
59
|
+
|
60
|
+
# watch for :masgn
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Instant
|
4
|
+
class Runner
|
5
|
+
def initialize(processor = Processor.new)
|
6
|
+
@processor = processor
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(source)
|
10
|
+
begin
|
11
|
+
@processed = @processor.process(source)
|
12
|
+
context = Context.new
|
13
|
+
|
14
|
+
begin
|
15
|
+
return_value = context.instance_eval(@processed)
|
16
|
+
ensure
|
17
|
+
context.close
|
18
|
+
end
|
19
|
+
{:status => :ok, :result => context.to_s, :return_value => return_value}
|
20
|
+
rescue SyntaxError => e
|
21
|
+
{:status => :error, :cause => :syntax_error, :message => format_error(e), :result => context.to_s }
|
22
|
+
rescue Racc::ParseError => e
|
23
|
+
{:status => :error, :cause => :parse_error, :message => format_error(e), :result => context.to_s }
|
24
|
+
rescue Instant::LoopTooDeepError => e
|
25
|
+
{:status => :error, :cause => :loop_too_deep, :message => "Loop too deep", :result => context.to_s }
|
26
|
+
rescue StandardError => e
|
27
|
+
{:status => :error, :cause => :unknown, :message => format_error(e), :result => context.to_s }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def format_error(e)
|
33
|
+
"#{e.message}<br><br>#{ e.backtrace[0..10].join('<br>')}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'sinatra/synchrony'
|
3
|
+
|
4
|
+
module Instant
|
5
|
+
module Sinatra
|
6
|
+
class App < ::Sinatra::Base
|
7
|
+
register ::Sinatra::Synchrony
|
8
|
+
|
9
|
+
set :public_folder, File.expand_path(File.join(File.dirname(__FILE__), "../../../public"))
|
10
|
+
set :views, File.expand_path(File.join(File.dirname(__FILE__), "../../../views"))
|
11
|
+
|
12
|
+
get '/' do
|
13
|
+
erb :index
|
14
|
+
end
|
15
|
+
|
16
|
+
post '/compile' do
|
17
|
+
content_type :json
|
18
|
+
source = params[:source]
|
19
|
+
|
20
|
+
puts "source:" + source
|
21
|
+
if source && source.strip != ""
|
22
|
+
result = Instant::Runner.new.run(source.strip)
|
23
|
+
result.to_json
|
24
|
+
else
|
25
|
+
{:status => :ok, :result => ""}.to_json
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: instant
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ryan Bates
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-19 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Generate a unique token with Active Record.
|
15
|
+
email: ryan@railscasts.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files:
|
19
|
+
- README.md
|
20
|
+
- lib/instant.rb
|
21
|
+
- lib/instant/context.rb
|
22
|
+
- lib/instant/processor.rb
|
23
|
+
- lib/instant/runner.rb
|
24
|
+
- lib/instant/sinatra/app.rb
|
25
|
+
- lib/instant/version.rb
|
26
|
+
files:
|
27
|
+
- Gemfile
|
28
|
+
- Procfile
|
29
|
+
- README.md
|
30
|
+
- Rakefile
|
31
|
+
- instant.gemspec
|
32
|
+
- lib/instant.rb
|
33
|
+
- lib/instant/context.rb
|
34
|
+
- lib/instant/processor.rb
|
35
|
+
- lib/instant/runner.rb
|
36
|
+
- lib/instant/sinatra/app.rb
|
37
|
+
- lib/instant/version.rb
|
38
|
+
- Manifest
|
39
|
+
homepage: http://github.com/ryanb/uniquify
|
40
|
+
licenses: []
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --line-numbers
|
44
|
+
- --inline-source
|
45
|
+
- --title
|
46
|
+
- Instant
|
47
|
+
- --main
|
48
|
+
- README.md
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.2'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project: instant
|
65
|
+
rubygems_version: 1.8.15
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Generate a unique token with Active Record.
|
69
|
+
test_files: []
|