rsugar 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c80b764190509a7f1474d4ea707ef88c6a42e25c
4
+ data.tar.gz: 0a4ba36be557a03e99620f18c8d732e69f6f09f6
5
+ SHA512:
6
+ metadata.gz: 6b243bf9489ad07af200c8292e7edd2c626d6a41788afedd26b15284c6cba705bff7236b5ebd7ed213c2c3437e09ff329b268a8f1a251eb642298fd8f9aa2866
7
+ data.tar.gz: 8a6b51393b74d00143de2a1ef848017ca8ff8362d2b5f3c4e4e3e16c01859808610e2f63fc9a6d9db5ca5f65c6bce00d76d7d0627b35efe0ab40dcfda0db0713
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rsugar.gemspec
4
+ gemspec
5
+
6
+ gem 'rserve-client'
7
+
8
+ group :development do
9
+ gem "rspec"
10
+ gem "pry"
11
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Justin Wiley
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # Rsugar
2
+
3
+ RSugar allows you to execute R language commands from Ruby. It wraps rserve_client gem, providing some syntactic sugar and a few helper methods.
4
+
5
+ ## Installation
6
+
7
+ Install rserve:
8
+
9
+ http://www.rforge.net/Rserve/doc.html
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'rsugar'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install rsugar
22
+
23
+ ## Usage
24
+
25
+ RSugar allows you to execute R language code on a seperate RServe. It is essentially a wrapper that provides some nice sugar and block style execution for the rserve_client gem.
26
+
27
+ R.exec is the main entry point. You pass it a block, within which you have access to a few methods corresponding directly to the RServe gem API:
28
+
29
+ a - assign
30
+ e - eval
31
+ ve - void eval
32
+
33
+ assign serializes the given Ruby object, sends it over to Rserve. eval sends a given string to Rserve, executes it, and returns the result. void eval executes the string on Rserve, but does not return the results.
34
+
35
+ ```ruby
36
+ require 'rsugar'
37
+
38
+ R.exec do
39
+ a 'x', 2
40
+ a 'y', 4
41
+ ve 'x = -x'
42
+ xy = e "x + y"
43
+ xdivy = e "x / y"
44
+ [xy, xdivy]
45
+ end
46
+ => [2, -0.5]
47
+ ```
48
+
49
+ You can optionally pass a hash of Ruby objects, which will automatically be passed and initialized in the R environment.
50
+
51
+ ```ruby
52
+ R.exec({xvals: [1,2,3,4], yvals: [4,3,2,1]}) do
53
+ e 'xvals + yvals'
54
+ end
55
+ => [5,5,5,5]
56
+ ```
57
+
58
+ ### Packages
59
+
60
+ Finally, you can ask rserver to use specific R packages.
61
+
62
+ ```ruby
63
+ R.exec do
64
+ library('xtable')
65
+ e 'd <- data.frame(col1=c(1,2,3))'
66
+ e('xtable(d)')
67
+ end.to_a
68
+ => [[1.0, 2.0, 3.0]]
69
+ ```
70
+
71
+ The 'library' method also tries to install the package for you if it doesn't exist. This requires the creation of an r-packages directory, and is somewhat dicey, since it can fail for a number of reasons (network connectivity, inability of R to install dependant packages, for example).
72
+
73
+ For production systems, it's recommended you manually install R packages first.
74
+
75
+ #### Default locations:
76
+
77
+ | Environment | Location |
78
+ | ------------------ | ------------------------------------: |
79
+ | Rails defined? | File.join(Rails.root, 'r-packages') |
80
+ | Rails not defined? | ./r-packages |
81
+
82
+ ### Note:
83
+
84
+ There are some ramifications to using R this way, the first is that its sloooooow, since Ruby objects have to be serialized, transmitted over rserve's wire protocol, executed and round-tripped back to the Ruby process.
85
+
86
+ In addition, each time R.exec is called, it creates a seperate connection to rserver, via Rserve::Connection.new. After the block returns, this connection should be freed/garbage collected. There is probably some not insignificant overhead with this, but you probably won't run into issues unless you're calling R.exec dozens or hundreds of times in an inner loop. Which is a bad idea anyway since rserver is sloooooow.
87
+
88
+ ## Contributing
89
+
90
+ 1. Fork it
91
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
92
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
93
+ 4. Push to the branch (`git push origin my-new-feature`)
94
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/r.rb ADDED
@@ -0,0 +1,115 @@
1
+ require 'rserve'
2
+ require 'timeout'
3
+ require 'matrix' # seems to be required under the hood by rserve gem
4
+
5
+ # R server wrapper
6
+ #
7
+ # The purpose of this class is to allow us to easily send R commands to the R server, process and return the results.
8
+ #
9
+ # R.exec is the key entry point
10
+ class R
11
+ # Execute a block of R commands, using optional syntactic sugar, returning the results
12
+ #
13
+ # Creates a new connection to the RServe after every connection.
14
+ #
15
+ # Example:
16
+ #
17
+ # require 'r'
18
+ #
19
+ # R.exec do
20
+ # a 'x', 2
21
+ # a 'y', 4
22
+ # ve 'x = -x'
23
+ # xy = e "x + y"
24
+ # xdivy = e "x / y"
25
+ # [xy, xdivy]
26
+ # end
27
+ # => [2, -0.5]
28
+ #
29
+ def self.exec args={}, &block
30
+ raise "Expected hash, keys will be defined as R variable names, example: {xvals: [1,2,3], yvals: [3,2,1]}" unless args.is_a?(Hash)
31
+ begin
32
+ conn = Rserve::Connection.new
33
+ dsl = R::RserveConnectionDsl.new conn, args
34
+ res = dsl.instance_eval &block
35
+ dsl.clear # since connection is recycled, make an attempt to clean up all defined vars
36
+ return res
37
+ rescue => e
38
+ conn = dsl = nil # ensure cleanup
39
+ raise e
40
+ end
41
+ end
42
+
43
+ # defines nice dsl for working with rserve-client
44
+ class RserveConnectionDsl
45
+ attr_accessor :conn, :args, :pkg_dir
46
+
47
+ def initialize conn, args={}
48
+ self.conn = conn
49
+ self.args = args
50
+ self.pkg_dir ||= args.delete(:pkg_dir)
51
+ self.pkg_dir ||= File.join(Rails.root, '/r-packages') if defined?(Rails)
52
+ self.pkg_dir ||= 'r-packages'
53
+ args.each{|k,v| a(k.to_s, v)} if args.any?
54
+ end
55
+
56
+ def do_conn meth, *args
57
+ conn.send(meth, *args)
58
+ end
59
+
60
+ def assert_defined *keys
61
+ keys.map do |k|
62
+ begin
63
+ e k.to_s
64
+ rescue
65
+ raise ArgumentError.new("expected #{k} to be defined in R envionment, but was not")
66
+ end
67
+ end
68
+ end
69
+
70
+ # short for 'eval', execute given arguments, collect results, convert to ruby
71
+ def e *args
72
+ do_conn(:eval, *args).to_ruby
73
+ end
74
+
75
+ # short for 'void_eval', just execute the arguments, don't return results
76
+ def ve *args
77
+ do_conn :void_eval, *args
78
+ end
79
+
80
+ # short for 'assign', send ruby objects to r, save as variable
81
+ def a *args
82
+ args[1] = args[1].to_s if args[1].is_a?(Symbol)
83
+ do_conn :assign, *args
84
+ end
85
+
86
+ def package_installed? pkgname
87
+ e "('#{pkgname}' %in% rownames(installed.packages()) == TRUE)"
88
+ end
89
+
90
+ def installed_packages
91
+ e "rownames(installed.packages())"
92
+ end
93
+
94
+ def install_package pkgname
95
+ FileUtils.mkdir_p pkg_dir
96
+
97
+ unless package_installed? pkgname
98
+ ve "install.packages('#{pkgname}', repos='http://cran.cnr.Berkeley.edu/', depends=c('Depends'))"
99
+ end
100
+ end
101
+
102
+ # attempts to install given R package if necessary and require in the environment via 'library'
103
+ def library pkgname, fail_after=300
104
+ Timeout::timeout(fail_after) do
105
+ install_package pkgname
106
+ e "library(#{pkgname})"
107
+ end
108
+ end
109
+
110
+ # remove defined variables from the R environment
111
+ def clear
112
+ ve 'rm(list=ls())'
113
+ end
114
+ end
115
+ end
data/lib/rsugar.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "rsugar/version"
2
+ require 'rserve'
3
+ require 'r'
4
+
5
+ module Rsugar
6
+ end
@@ -0,0 +1,3 @@
1
+ module Rsugar
2
+ VERSION = "0.0.1"
3
+ end
data/rsugar.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rsugar/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rsugar"
8
+ spec.version = Rsugar::VERSION
9
+ spec.authors = ["Justin Wiley"]
10
+ spec.email = ["justin.wiley@gmail.com"]
11
+ spec.description = %q{RSugar allows you to execute R language commands from Ruby. It wraps rserve_client gem, providing some syntactic sugar and a few helper methods.}
12
+ spec.summary = %q{RSugar allows you to execute R language commands from Ruby. It wraps rserve_client gem, providing some syntactic sugar and a few helper methods.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "rserve-client"
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec"
25
+ end
@@ -0,0 +1,118 @@
1
+ require 'pry'
2
+ require_relative '../../lib/rsugar'
3
+
4
+ describe R do
5
+ let(:conn) { Rserve::Connection.new }
6
+ let(:dsl) { R::RserveConnectionDsl.new conn }
7
+
8
+ before do
9
+ FileUtils.rm_rf(dsl.pkg_dir) if File.exist?(dsl.pkg_dir)
10
+ end
11
+
12
+ it 'should execute R language statements using the DSL, and return results' do
13
+ R.exec do
14
+ a 'x', 2
15
+ a 'y', 4
16
+ ve 'x = -x'
17
+ xy = e "x + y"
18
+ xdivy = e "x / y"
19
+ [xy, xdivy]
20
+ end.should == [2, -0.5]
21
+ end
22
+
23
+ it 'should accept an optional hash of values, assign to named R values' do
24
+ R.exec({xvals: [1,2,3,4], yvals: [4,3,2,1]}) do
25
+ e 'xvals + yvals'
26
+ end.should == [5,5,5,5]
27
+ end
28
+
29
+ it 'should convert assigned values to string if they are symbol to prevent Rserve exceptions' do
30
+ R.exec({foobar: :stuff}){ e('foobar') }.should == 'stuff'
31
+ end
32
+
33
+ it 'should clean up defined R variables after block exists' do
34
+ R.exec { a 'x', 1 }
35
+ expect do
36
+ R.exec { e('x') }
37
+ end.to raise_error(Rserve::Connection::EvalError)
38
+ end
39
+
40
+ describe R::RserveConnectionDsl do
41
+ let(:pkgname) { "base" }
42
+
43
+ it 'should expose Rserve methods with shorter syntactic sugar accessors' do
44
+ val = "1"
45
+ return_val = Rserve::REXP::Integer.new 1
46
+ {eval: :e, assign: :a, void_eval: :ve}.each do |rserve_method, sugar|
47
+ conn.should_receive(rserve_method).with(val).and_return(return_val)
48
+ dsl.send(sugar, val)
49
+ end
50
+ end
51
+
52
+ it '#assert_defined should raise if given variables do not exist in R' do
53
+ dsl.a 'x', 2
54
+ dsl.assert_defined('x')
55
+ expect { dsl.assert_defined('y') }.to raise_error(ArgumentError)
56
+ end
57
+
58
+ it '#clear should remove bound variables from R environment (to the extent R allows this)' do
59
+ dsl.should_receive(:ve).with('rm(list=ls())')
60
+ dsl.clear
61
+ end
62
+
63
+ context 'package management' do
64
+ it '#installed_packages should return a list of installed R packages available on the server' do
65
+ dsl.installed_packages.should include("base")
66
+ end
67
+
68
+ it '#package_installed? should return true if specified package installed, false if not' do
69
+ dsl.package_installed?("base").should be_true
70
+ dsl.package_installed?("foobar").should be_false
71
+ end
72
+ end
73
+
74
+ context '#install_package' do
75
+ let(:do_install) { dsl.install_package(pkgname) }
76
+
77
+ it 'should -attempt- to install missing R library and all dependencies if it doesnt exist (dicey)' do
78
+ dsl.should_receive(:package_installed?).and_return(false)
79
+ dsl.should_receive(:ve).with("install.packages('#{pkgname}', repos='http://cran.cnr.Berkeley.edu/', depends=c('Depends'))")
80
+ do_install
81
+ end
82
+
83
+ it 'should not attempt to install if library already exists' do
84
+ dsl.should_receive(:package_installed?).and_return(true)
85
+ dsl.should_not_receive(:ve)
86
+ do_install
87
+ end
88
+
89
+ it 'should create a directory to store R packages if it doesnt already exist' do
90
+ File.exist?(dsl.pkg_dir).should be_false
91
+ do_install
92
+ File.exist?(dsl.pkg_dir).should be_true
93
+ end
94
+ end
95
+
96
+ context '#library' do
97
+ let(:do_library) { dsl.library(pkgname) }
98
+
99
+ it 'should install package if it doesnt exist and require R library' do
100
+ dsl.should_receive(:package_installed?).and_return(false)
101
+ dsl.should_receive(:e).with("library(#{pkgname})")
102
+ do_library
103
+ end
104
+
105
+ it 'should just require the package via R library function if it exists' do
106
+ dsl.should_receive(:package_installed?).and_return(true)
107
+ dsl.should_receive(:e).with("library(#{pkgname})")
108
+ do_library
109
+ end
110
+
111
+ it 'should actually do all this outside of stubs' do
112
+ dsl.library('xtable')
113
+ dsl.e 'd <- data.frame(col1=c(1,2,3))'
114
+ dsl.e('xtable(d)').should == [[1.0, 2.0, 3.0]]
115
+ end
116
+ end
117
+ end
118
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rsugar
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Justin Wiley
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rserve-client
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: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: RSugar allows you to execute R language commands from Ruby. It wraps
70
+ rserve_client gem, providing some syntactic sugar and a few helper methods.
71
+ email:
72
+ - justin.wiley@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - .gitignore
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - lib/r.rb
83
+ - lib/rsugar.rb
84
+ - lib/rsugar/version.rb
85
+ - rsugar.gemspec
86
+ - spec/lib/r_spec.rb
87
+ homepage: ''
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.0.3
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: RSugar allows you to execute R language commands from Ruby. It wraps rserve_client
111
+ gem, providing some syntactic sugar and a few helper methods.
112
+ test_files:
113
+ - spec/lib/r_spec.rb