em-rserve 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/samples/fibers.rb ADDED
@@ -0,0 +1,32 @@
1
+ $LOAD_PATH << './lib'
2
+ require 'em-rserve'
3
+ require 'em-rserve/pooler'
4
+
5
+ class MyConnection < EM::Rserve::FiberedConnection
6
+ def ready
7
+ p 'ready'
8
+ super
9
+ end
10
+ end
11
+
12
+ pool = EM::Rserve::Pooler.new(3, MyConnection)
13
+
14
+ EM::run do
15
+ pool.fill
16
+
17
+ EM.next_tick do
18
+ EM::Rserve::Pooler::r do |r|
19
+ x = (0 .. 5).map{|v| Math.sin(v)}
20
+ y = x.map{|i| i**2 + 0.4*rand()}
21
+ r[:dat] = {x: x, y: y}
22
+ p r.call('cor(dat$x, dat$y)')
23
+ end
24
+ end
25
+
26
+ EM.add_periodic_timer(1) do
27
+ pool.r do |r|
28
+ r[:dat] = 'hello world'
29
+ p r[:dat]
30
+ end
31
+ end
32
+ end
data/samples/run.rb ADDED
@@ -0,0 +1,66 @@
1
+ #example which shows how various R objects are dumped and translated in Ruby
2
+ $LOAD_PATH << './lib'
3
+
4
+ require 'em-rserve'
5
+ require 'pp'
6
+
7
+ class DevelConnection < EM::Rserve::Connection
8
+ attr_reader :request_queue
9
+
10
+ def receive_message(msg)
11
+ super
12
+ dump_sexp(msg)
13
+ end
14
+
15
+ def dump_sexp(msg)
16
+ raise unless msg.parameters.size == 1
17
+ root = msg.parameters.first
18
+ catch :cannot_translate do
19
+ val = EM::Rserve::R::RtoRuby::Translator.r_to_ruby(root)
20
+ puts "translated"
21
+ puts val.inspect
22
+ end
23
+ node = root.children.first
24
+ pp node
25
+ end
26
+
27
+ def unbind
28
+ super
29
+ p "closing"
30
+ end
31
+
32
+ def ready
33
+ puts "ready"
34
+
35
+ r_eval 'as.Date("2/3/2004", "%m/%d/%Y")'
36
+ return
37
+ r_eval 'ts(1:8)'
38
+ r_eval 'as.formula(y~x1+x2+x3)'
39
+ r_eval 'raw(8)'
40
+ r_eval 'c(NaN, Inf)'
41
+ r_eval 'quote(c(1:3))'
42
+ r_eval 'as.factor(c("a", "a", "b", "c"))'
43
+ r_eval "data.frame(foo=c(1:8), bar=seq(100,800,100))"
44
+ r_eval 'table(c(1,2,3,2,2))'
45
+ r_eval 'list(name="Fred", wife="Mary", no.children=3, child.ages=c(4,7,9))'
46
+ r_eval 'c(1:5)'
47
+ r_eval 'TRUE'
48
+ r_eval 'FALSE'
49
+ r_eval 'NA'
50
+ r_eval "data(Cars93, package='MASS')"
51
+ r_eval "cor(c(1:100), runif(1:100))"
52
+ r_eval "cor(c(1:100), c(1:100))"
53
+ r_eval 'table(c("a", "b", "c", "a", "b"))'
54
+ r_eval "data.frame(foo=c(1:8))"
55
+ r_eval "data.frame(foo=c(1,2,3), bar=c(NA,FALSE,TRUE), row.names=c('foo','bar','baz'))"
56
+ # r_eval 'function(a,b=2){a+b}'
57
+ # r_eval 'ls'
58
+ # r_eval 'print'
59
+ r_eval 't.test(c(1,2,3,1),c(1,6,7,8))'
60
+ end
61
+
62
+ end
63
+
64
+ EM.run do
65
+ DevelConnection.start
66
+ end
data/samples/sql.rb ADDED
@@ -0,0 +1,167 @@
1
+ # this example requires
2
+ # rubygems: sequel and the sqlite3 adapter
3
+ # R: RSQLite package
4
+ # first run this script with 'create' argument to generate a simple table
5
+ require 'sequel'
6
+
7
+ DB = Sequel.sqlite('dat.sqlite')
8
+ items = DB[:items]
9
+
10
+ if ARGV.include?('create')
11
+ DB.create_table :items do
12
+ primary_key :id
13
+ String :name
14
+ Float :price
15
+ Float :sells
16
+ end
17
+
18
+ 100.times do |t|
19
+ price = rand(t)*100
20
+ sells = rand(Math.sqrt(price))
21
+ items.insert(:name => 'abc',
22
+ :price => price,
23
+ :sells => sells)
24
+ end
25
+ end
26
+
27
+ $code = items.filter(:name => 'abc').sql
28
+ p $code
29
+
30
+ DB.disconnect
31
+
32
+ $LOAD_PATH << './lib'
33
+
34
+ require 'em-rserve'
35
+ require 'em-rserve/qap1'
36
+
37
+ class DevelConnection < EM::Rserve::Connection
38
+ attr_reader :request_queue
39
+
40
+ def dump_sexp(msg)
41
+ raise unless msg.parameters.size == 1
42
+ root = msg.parameters.first
43
+ node = root.children.first
44
+ catch :cannot_translate do
45
+ val = EM::Rserve::R::RtoRuby::Translator.r_to_ruby(root)
46
+ p val
47
+ end
48
+ end
49
+
50
+ def ready
51
+ puts "ready"
52
+ r_eval "library(DBI)", true do |q|
53
+ q.errback do |err|
54
+ p "error loading DBI"
55
+ end
56
+ q.callback do |msg|
57
+ p "DBI loaded"
58
+ end
59
+ end
60
+ r_eval "library(RSQLite)", true do |q|
61
+ q.errback do |err|
62
+ p "error loading RSQLite"
63
+ end
64
+ q.callback do |msg|
65
+ p "RSQLite loaded"
66
+ end
67
+ end
68
+ r_eval "driver <- dbDriver('SQLite')", true do |q|
69
+ q.errback do |err|
70
+ p "error driving RSQLite"
71
+ end
72
+ q.callback do |msg|
73
+ p "RSQLite driver"
74
+ end
75
+ end
76
+ code = "con <- dbConnect(driver, \"#{File.expand_path('dat.sqlite')}\")"
77
+ puts code
78
+ r_eval(code, true) do |q|
79
+ q.errback do |err|
80
+ p "error connecting RSQLite"
81
+ end
82
+ q.callback do |msg|
83
+ p "RSQLite connected"
84
+ end
85
+ end
86
+ code = "query <- dbSendQuery(con, statement = \"#{$code}\")"
87
+ puts code
88
+ r_eval(code, true) do |q|
89
+ q.errback do |err|
90
+ p "error querying RSQLite"
91
+ end
92
+ q.callback do |msg|
93
+ p "RSQLite queried"
94
+ end
95
+ end
96
+ r_eval "dat <- fetch(query)", true do |q|
97
+ q.errback do |err|
98
+ p "error fecthing query"
99
+ end
100
+ q.callback do |msg|
101
+ p "query fetched"
102
+ end
103
+ end
104
+ r_eval "sqliteCloseResult(query)", true do |q|
105
+ q.errback do |err|
106
+ p "error closing query"
107
+ end
108
+ q.callback do |msg|
109
+ p "query closed"
110
+ end
111
+ end
112
+ r_eval "sqliteCloseConnection(con)", true do |q|
113
+ q.errback do |err|
114
+ p "error closing connection"
115
+ end
116
+ q.callback do |msg|
117
+ p "connection closed"
118
+ end
119
+ end
120
+ r_eval "sqliteCloseDriver(driver)", true do |q|
121
+ q.errback do |err|
122
+ p "error closing driver"
123
+ end
124
+ q.callback do |msg|
125
+ p "driver closed"
126
+ end
127
+ end
128
+
129
+ # dataframes are mapped to subclasses of Hashes which respond_to :each_struct
130
+ r_eval "dat" do |q|
131
+ q.errback do |err|
132
+ p "error getting dat"
133
+ end
134
+ q.callback do |msg|
135
+ p "got dat"
136
+ root = msg.parameters.first
137
+ node = root.children.first
138
+ catch :cannot_translate do
139
+ val = EM::Rserve::R::RtoRuby::Translator.r_to_ruby(root)
140
+ val.each_struct do |st|
141
+ p st
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ # vectors of doubles with a single item are mapped to a single double
148
+ r_eval "cor(dat$sells, dat$price)" do |q|
149
+ q.errback do |err|
150
+ p "error getting correlation"
151
+ end
152
+ q.callback do |msg|
153
+ p "got correlation"
154
+ dump_sexp(msg)
155
+ end
156
+ end
157
+ r_eval "c(1:10)"
158
+ end
159
+
160
+ end
161
+
162
+ EM.run do
163
+ # EM::Rserve::Connection.start
164
+ DevelConnection.start
165
+ end
166
+
167
+
@@ -0,0 +1,6 @@
1
+ png("<%= @path %>")
2
+ x <- c(1:30)
3
+ y <- x*x
4
+ plot(x, y, type='l', col=color)
5
+ text(5, 200, "y=x^2")
6
+ dev.off()
@@ -0,0 +1,37 @@
1
+
2
+ $LOAD_PATH << './lib'
3
+ require 'sinatra'
4
+ require 'em-rserve'
5
+ require 'em-rserve/pooler'
6
+ require 'fileutils'
7
+
8
+ FileUtils.mkdir_p 'plots'
9
+
10
+ get '/' do
11
+ redirect :plot
12
+ end
13
+
14
+ helpers do
15
+ def pool
16
+ @@pool ||= EM::Rserve::Pooler.new 10
17
+ end
18
+
19
+ def plot(template, system=:erb)
20
+ pool.r do |r|
21
+ @path = File.join(Dir.pwd, "plots", "plot-#{request.object_id}.png")
22
+ r[:color] = 'blue' #sets color variable in R context (used in the template)
23
+ script = send system, template
24
+ r.call(script, true)
25
+ ctype = content_type || 'image/png'
26
+ File.open(@path,'r') do |ret|
27
+ env["async.callback"].call [200, {'Content-Type' => ctype}, ret]
28
+ end
29
+ FileUtils.rm @path
30
+ end
31
+ throw :async
32
+ end
33
+ end
34
+
35
+ get '/plot' do
36
+ plot :plot, :erb
37
+ end
@@ -0,0 +1,45 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ require 'em-rserve/protocol/parser'
3
+
4
+ describe EM::Rserve::Protocol::IDParser do
5
+ before :each do
6
+ @receiver = Class.new do
7
+ attr_accessor :test_block
8
+ def receive_id(id)
9
+ test_block.call(id)
10
+ end
11
+ end.new
12
+
13
+ @parser = EM::Rserve::Protocol::IDParser.new(@receiver)
14
+ end
15
+
16
+ it "should slice stuff in IDs of 4 bytes" do
17
+ idx = 0
18
+ count = 3
19
+ @receiver.test_block = lambda do |id|
20
+ idx += 1
21
+ id.should be_a(EM::Rserve::Protocol::ID)
22
+ id.string.should eql('1234')
23
+ end
24
+ @parser << ("1234"*count)
25
+ idx.should eql(3)
26
+ end
27
+
28
+ it "should not bother about chunked input" do
29
+ idx = 0
30
+ count = 3
31
+ @receiver.test_block = lambda do |id|
32
+ idx += 1
33
+ id.should be_a(EM::Rserve::Protocol::ID)
34
+ id.string.should eql('1234')
35
+ end
36
+ count.times do |t|
37
+ @parser << "1"
38
+ @parser << "23"
39
+ idx.should eql(t)
40
+ @parser << "4"
41
+ end
42
+ idx.should eql(3)
43
+ end
44
+ end
45
+
@@ -0,0 +1,70 @@
1
+
2
+ require File.join(File.dirname(__FILE__), 'spec_helper')
3
+ require 'em-rserve/protocol/parser'
4
+
5
+ describe EM::Rserve::Protocol::MessageParser do
6
+ before :each do
7
+ @receiver = Class.new do
8
+ attr_accessor :h_test_block
9
+ attr_accessor :m_test_block
10
+ def receive_message_header(h)
11
+ h_test_block.call(h)
12
+ end
13
+ def receive_message(m)
14
+ m_test_block.call(m)
15
+ end
16
+ end.new
17
+
18
+ @parser = EM::Rserve::Protocol::MessageParser.new(@receiver)
19
+ end
20
+
21
+ it "should read a header after 16bytes" do
22
+ idx = 0
23
+ @receiver.h_test_block = lambda do |h|
24
+ idx += 1
25
+ h.should be_a(EM::Rserve::QAP1::Header)
26
+ end
27
+ @parser << ("\0" * 16)
28
+ idx.should eql(1)
29
+ end
30
+
31
+ it "should read a header after 16bytes, even if data is chunked" do
32
+ idx = 0
33
+ @receiver.h_test_block = lambda do |h|
34
+ idx += 1
35
+ h.should be_a(EM::Rserve::QAP1::Header)
36
+ end
37
+ 16.times { @parser << "\0" }
38
+ idx.should eql(1)
39
+ end
40
+
41
+
42
+ it "should read a message whose length is specified in the header and data is sliced" do
43
+ hack = Module.new do
44
+ attr_accessor :message_length
45
+ end
46
+ #XXX this is actual, valid data, we should split the slicing and parsing
47
+ message = "0a 0c 00 00 21 08 00 00 00 00 00 00 00 00 f0 3f".split.
48
+ pack('H2'*16)
49
+
50
+ @receiver.h_test_block = lambda do |m|
51
+ m.should be_a(EM::Rserve::QAP1::Header)
52
+ # Here we overwrite the header to not care about header's content in this spec
53
+ m.extend hack
54
+ m.message_length = message.length
55
+ end
56
+
57
+ idx = 0
58
+ @receiver.m_test_block = lambda do |m|
59
+ idx += 1
60
+ m.should be_a(EM::Rserve::QAP1::Message)
61
+ end
62
+
63
+ stream = ("\0" * 16) + message
64
+ stream.split('').each do |char|
65
+ idx.should eql(0)
66
+ @parser << char
67
+ end
68
+ idx.should eql(1)
69
+ end
70
+ end
@@ -0,0 +1,80 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ require 'em-rserve/protocol/parser'
3
+
4
+ describe EM::Rserve::Protocol::Parser, 'initialized' do
5
+ before :each do
6
+ @parser = EM::Rserve::Protocol::Parser.new(:foobar)
7
+ end
8
+
9
+ it "should have an empty buffer" do
10
+ @parser.buffer.should be_empty
11
+ end
12
+
13
+ it "should remember the handler" do
14
+ @parser.handler.should eql(:foobar)
15
+ end
16
+ end
17
+
18
+ describe EM::Rserve::Protocol::Parser, 'replacement' do
19
+ before :each do
20
+ @parser = EM::Rserve::Protocol::Parser.new(:foo)
21
+ @new_parser = EM::Rserve::Protocol::Parser.new(:bar)
22
+ end
23
+
24
+ it "should replace the buffer but not the handler" do
25
+ @parser.buffer << "foobar" #XXX we directly manipulate the buffer here
26
+ @new_parser.replace(@parser)
27
+ @new_parser.handler.should eql(:bar)
28
+ @new_parser.buffer.should eql("foobar")
29
+ end
30
+ end
31
+
32
+ describe EM::Rserve::Protocol::Parser, 'loop guard for topklass' do
33
+ before :each do
34
+ @parser = EM::Rserve::Protocol::Parser.new(:foo)
35
+ end
36
+
37
+ it "should ask for subclassing" do
38
+ lambda {
39
+ @parser << "lulz"
40
+ }.should raise_error(NotImplementedError)
41
+ end
42
+ end
43
+
44
+ describe EM::Rserve::Protocol::Parser, 'loop mechanics for subklasses' do
45
+ before :each do
46
+ m = Module.new do
47
+ attr_accessor :test_proc
48
+ def parse!
49
+ test_proc.call
50
+ end
51
+ end
52
+
53
+ @parser = EM::Rserve::Protocol::Parser.new(:foo)
54
+ @parser.extend m
55
+ end
56
+
57
+ it "should loop until we throw :stop when we call the loop" do
58
+ idx = 0
59
+ limit = 123
60
+ @parser.test_proc = lambda do
61
+ idx += 1
62
+ throw :stop if idx == limit
63
+ end
64
+
65
+ @parser.parse_loop!
66
+ idx.should eql(limit)
67
+ end
68
+
69
+ it "should call the loop when we add data" do
70
+ idx = 0
71
+ limit = 123
72
+ @parser.test_proc = lambda do
73
+ idx += 1
74
+ throw :stop if idx == limit
75
+ end
76
+
77
+ @parser << "for great justice"
78
+ idx.should eql(limit)
79
+ end
80
+ end
@@ -0,0 +1,88 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ require 'em-rserve/r/ruby_to_r/translator'
3
+ require 'em-rserve/r/sexp'
4
+
5
+ describe EM::Rserve::R::RubytoR::Translator do
6
+ include EM::Rserve::R::RubytoR
7
+
8
+ it "should remember the object" do
9
+ tr = Translator.new(:foobar)
10
+ tr.obj.should eql(:foobar)
11
+ end
12
+
13
+ it "should throw :cannot_translate when cannot translate" do
14
+ thrown=true
15
+ catch :cannot_translate do
16
+ Translator.new.translate
17
+ thrown=false
18
+ end
19
+ thrown.should be_true
20
+ end
21
+
22
+ it "should translate simple arrays" do
23
+ thrown=true
24
+ catch :cannot_translate do
25
+ Translator.ruby_to_r([1, 2, 3])
26
+ Translator.ruby_to_r([1, 2.2, 3.2])
27
+ Translator.ruby_to_r([true, false, nil])
28
+ Translator.ruby_to_r(["we", "miss", "you", "_why"])
29
+ thrown=false
30
+ end
31
+ thrown.should be_false
32
+ end
33
+
34
+ it "should translate simple values" do
35
+ thrown=true
36
+ catch :cannot_translate do
37
+ Translator.ruby_to_r(1)
38
+ Translator.ruby_to_r(1.0)
39
+ Translator.ruby_to_r(false)
40
+ Translator.ruby_to_r(nil)
41
+ Translator.ruby_to_r(true)
42
+ Translator.ruby_to_r("hello world")
43
+ thrown=false
44
+ end
45
+ thrown.should be_false
46
+ end
47
+
48
+ it "should translate simple hashes" do
49
+ thrown=true
50
+ catch :cannot_translate do
51
+ Translator.ruby_to_r({'foo' => [1,2,3,4], 'bar' => ['a', 'b', 'c', 'd']})
52
+ thrown=false
53
+ end
54
+ thrown.should be_false
55
+ end
56
+ end
57
+
58
+ describe EM::Rserve::R::RubytoR::Translator::ArrayTranslator do
59
+ include EM::Rserve::R
60
+
61
+ def translator(obj)
62
+ EM::Rserve::R::RubytoR::Translator::ArrayTranslator.new obj
63
+ end
64
+
65
+ def node_class_for(obj)
66
+ translator(obj).array_node_class
67
+ end
68
+
69
+ it "should propose arrays of bools" do
70
+ node_class_for([true, true, false, nil]).should eql(Sexp::Node::ArrayBool)
71
+ end
72
+
73
+ it "should propose arrays of ints" do
74
+ node_class_for([1, 2, 3]).should eql(Sexp::Node::ArrayInt)
75
+ end
76
+
77
+ it "should propose arrays of doubles" do
78
+ node_class_for([1.2, 2.0, Math::PI]).should eql(Sexp::Node::ArrayDouble)
79
+ end
80
+
81
+ it "should propose arrays of strings" do
82
+ node_class_for(["chunky", "bacon"]).should eql(Sexp::Node::ArrayString)
83
+ end
84
+
85
+ it "should propose arrays of doubles for mixes of floats and fixnums" do
86
+ node_class_for([1.5, 3]).should eql(Sexp::Node::ArrayDouble)
87
+ end
88
+ end
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH << './lib'
2
+ require 'rspec'
3
+ require 'em-rserve'
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-rserve
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - crapooze
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-10-10 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Do evented stats with EventMachine and RServe
23
+ email:
24
+ - crapooze@gmail.com
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README
35
+ - Rakefile
36
+ - TODO
37
+ - em-rserve.gemspec
38
+ - lib/em-rserve.rb
39
+ - lib/em-rserve/connection.rb
40
+ - lib/em-rserve/fibered_connection.rb
41
+ - lib/em-rserve/pooler.rb
42
+ - lib/em-rserve/protocol/connector.rb
43
+ - lib/em-rserve/protocol/id.rb
44
+ - lib/em-rserve/protocol/parser.rb
45
+ - lib/em-rserve/protocol/request.rb
46
+ - lib/em-rserve/qap1/constants.rb
47
+ - lib/em-rserve/qap1/header.rb
48
+ - lib/em-rserve/qap1/message.rb
49
+ - lib/em-rserve/qap1/rpack.rb
50
+ - lib/em-rserve/r/r_to_ruby/translator.rb
51
+ - lib/em-rserve/r/ruby_to_r/translator.rb
52
+ - lib/em-rserve/r/sexp.rb
53
+ - lib/em-rserve/version.rb
54
+ - samples/assign.rb
55
+ - samples/detach_attach.rb
56
+ - samples/fibers.rb
57
+ - samples/run.rb
58
+ - samples/sql.rb
59
+ - samples/views/plot.erb
60
+ - samples/webplot.rb
61
+ - specs/id_parser_spec.rb
62
+ - specs/message_parser_spec.rb
63
+ - specs/parser_spec.rb
64
+ - specs/ruby_to_r_translator_spec.rb
65
+ - specs/spec_helper.rb
66
+ has_rdoc: true
67
+ homepage: ""
68
+ licenses: []
69
+
70
+ post_install_message:
71
+ rdoc_options: []
72
+
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ hash: 3
81
+ segments:
82
+ - 0
83
+ version: "0"
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ requirements: []
94
+
95
+ rubyforge_project: em-rserve
96
+ rubygems_version: 1.3.7
97
+ signing_key:
98
+ specification_version: 3
99
+ summary: An EventMachine client for RServe
100
+ test_files: []
101
+