instant 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![](http://f.cl.ly/items/0m2o252A3n1C032R2s0X/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7%202012-04-19%20%E4%B8%8A%E5%8D%8812.23.58.png)
|
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: []
|