em-rserve 0.1.0

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/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
+