rack-r 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.rvmrc +81 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +106 -0
- data/Rakefile +2 -0
- data/TODO +0 -0
- data/lib/rack_r.rb +2 -0
- data/lib/rack_r/middleware.rb +224 -0
- data/lib/rack_r/railtie.rb +25 -0
- data/lib/rack_r/version.rb +3 -0
- data/rack-r.gemspec +20 -0
- data/test/example.r +38 -0
- data/test/helper.rb +11 -0
- data/test/test_rack_r.rb +153 -0
- metadata +81 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
|
7
|
+
environment_id="ruby-1.8.7-p357@rack-r"
|
8
|
+
|
9
|
+
#
|
10
|
+
# Uncomment the following lines if you want to verify rvm version per project
|
11
|
+
#
|
12
|
+
# rvmrc_rvm_version="1.10.2" # 1.10.1 seams as a safe start
|
13
|
+
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
14
|
+
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
15
|
+
# return 1
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
|
19
|
+
#
|
20
|
+
# Uncomment following line if you want options to be set only for given project.
|
21
|
+
#
|
22
|
+
# PROJECT_JRUBY_OPTS=( --1.9 )
|
23
|
+
#
|
24
|
+
# The variable PROJECT_JRUBY_OPTS requires the following to be run in shell:
|
25
|
+
#
|
26
|
+
# chmod +x ${rvm_path}/hooks/after_use_jruby_opts
|
27
|
+
#
|
28
|
+
|
29
|
+
#
|
30
|
+
# First we attempt to load the desired environment directly from the environment
|
31
|
+
# file. This is very fast and efficient compared to running through the entire
|
32
|
+
# CLI and selector. If you want feedback on which environment was used then
|
33
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
34
|
+
#
|
35
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
|
36
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
37
|
+
then
|
38
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
39
|
+
|
40
|
+
if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
|
41
|
+
then
|
42
|
+
. "${rvm_path:-$HOME/.rvm}/hooks/after_use"
|
43
|
+
fi
|
44
|
+
else
|
45
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
46
|
+
if ! rvm --create use "$environment_id"
|
47
|
+
then
|
48
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
49
|
+
return 1
|
50
|
+
fi
|
51
|
+
fi
|
52
|
+
|
53
|
+
#
|
54
|
+
# If you use an RVM gemset file to install a list of gems (*.gems), you can have
|
55
|
+
# it be automatically loaded. Uncomment the following and adjust the filename if
|
56
|
+
# necessary.
|
57
|
+
#
|
58
|
+
# filename=".gems"
|
59
|
+
# if [[ -s "$filename" ]]
|
60
|
+
# then
|
61
|
+
# rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
|
62
|
+
# fi
|
63
|
+
|
64
|
+
# If you use bundler, this might be useful to you:
|
65
|
+
# if [[ -s Gemfile ]] && ! command -v bundle >/dev/null
|
66
|
+
# then
|
67
|
+
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
|
68
|
+
# gem install bundler
|
69
|
+
# fi
|
70
|
+
# if [[ -s Gemfile ]] && command -v bundle
|
71
|
+
# then
|
72
|
+
# bundle install
|
73
|
+
# fi
|
74
|
+
|
75
|
+
if [[ $- == *i* ]] # check for interactive shells
|
76
|
+
then
|
77
|
+
echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
|
78
|
+
else
|
79
|
+
echo "Using: $GEM_HOME" # don't use colors in interactive shells
|
80
|
+
fi
|
81
|
+
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Phil Hofmann
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
RackR - Use R in your Rack stack
|
2
|
+
================================
|
3
|
+
|
4
|
+
RackR is a Rack middleware which integrates R into a Rack based web
|
5
|
+
application (like Rails). With RackR in place displaying graphs is as
|
6
|
+
simple as rendering R code into your output. E.g.
|
7
|
+
|
8
|
+
<script type='text/r'>
|
9
|
+
png('sinus.png')
|
10
|
+
plot(sin)
|
11
|
+
</script>
|
12
|
+
|
13
|
+
RackR will pick this up on its way out and replace it with an empty
|
14
|
+
container and some JavaScript code to perform an immediate async
|
15
|
+
request for processing the R code.
|
16
|
+
|
17
|
+
RackR will answer to this reqyest with processing the R code in a temp
|
18
|
+
directory, then searching this directory for displayable content and
|
19
|
+
finally return the html code for display. So the example above will
|
20
|
+
eventually turn into something like this.
|
21
|
+
|
22
|
+
<div>
|
23
|
+
<img src='/path/to/sinus.png' />
|
24
|
+
<pre>
|
25
|
+
content of Rout file (stdout of R)
|
26
|
+
</pre>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
Almost everything can conveniently be configured in a YAML file. RackR
|
30
|
+
will create a sample config file in `config/rack-r.yml` or any other
|
31
|
+
path given.
|
32
|
+
|
33
|
+
|
34
|
+
Install in Rails
|
35
|
+
----------------
|
36
|
+
|
37
|
+
Put the following in your Gemfile and run `bundle install` or let
|
38
|
+
[Guard](https://github.com/guard/guard-bundler) kick in.
|
39
|
+
|
40
|
+
gem 'rack-r', :require => 'rack_r'
|
41
|
+
|
42
|
+
In Rails 3.2 you will have to put this somewhere
|
43
|
+
|
44
|
+
require 'rack_r/railtie'
|
45
|
+
|
46
|
+
|
47
|
+
Using RackR outside of Rails
|
48
|
+
----------------------------
|
49
|
+
|
50
|
+
require 'rack_r/middleware'
|
51
|
+
use RackR::Middleware, :config => 'path/to/config/rack-r.yml'
|
52
|
+
|
53
|
+
|
54
|
+
Dependencies
|
55
|
+
------------
|
56
|
+
|
57
|
+
These instructions are for Debian Squeeze. Install R.
|
58
|
+
|
59
|
+
apt-get install r-base r-cran-dbi
|
60
|
+
|
61
|
+
Alternatively you can use the `rodbc` package.
|
62
|
+
|
63
|
+
apt-get install r-cran-rodbc
|
64
|
+
|
65
|
+
The RackR-Header is a pice of R code that gets prepended to every R
|
66
|
+
script which is processed by RackR. Idealy it will read you database
|
67
|
+
config and provide a seperate connection to your database via a DBI
|
68
|
+
compatible `con` object.
|
69
|
+
|
70
|
+
If you want RackR to automatically connect R script to yoyr Rails
|
71
|
+
database it is a good idea to install Jeremy Stephens' YAML for R.
|
72
|
+
|
73
|
+
wget http://cran.r-project.org/src/contrib/yaml_2.1.4.tar.gz
|
74
|
+
R CMD INSTALL yaml_2.1.4.tar.gz
|
75
|
+
|
76
|
+
### SQLite
|
77
|
+
|
78
|
+
wget http://cran.r-project.org/src/contrib/RSQLite_0.11.1.tar.gz
|
79
|
+
R CMD INSTALL RSQLite_0.11.1.tar.gz
|
80
|
+
|
81
|
+
### MySQL
|
82
|
+
|
83
|
+
apt-get install r-cran-rmysql
|
84
|
+
|
85
|
+
The whole RackR-Header is a work in progress, if you have to adjust it
|
86
|
+
to your database config, please consider to contribute your addition.
|
87
|
+
|
88
|
+
|
89
|
+
Trouble shooting
|
90
|
+
----------------
|
91
|
+
|
92
|
+
Browse to /rack-r/ (including the trailing slash!) to see if RackR is
|
93
|
+
working. If so it should respond with "RackR OK."
|
94
|
+
|
95
|
+
|
96
|
+
Patches and the like
|
97
|
+
--------------------
|
98
|
+
|
99
|
+
If you run into bugs, have suggestions or patches feel free to drop me
|
100
|
+
a line.
|
101
|
+
|
102
|
+
|
103
|
+
License
|
104
|
+
-------
|
105
|
+
|
106
|
+
RackR is released under MIT License, see LICENSE.
|
data/Rakefile
ADDED
data/TODO
ADDED
File without changes
|
data/lib/rack_r.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'yaml'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
require 'csv'
|
6
|
+
|
7
|
+
module RackR
|
8
|
+
class Middleware < Struct.new :app, :options
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
@env = env
|
12
|
+
return call_app unless config.enabled
|
13
|
+
if get? and md = match_path
|
14
|
+
key = md.to_a.last
|
15
|
+
return [200, {}, ['RackR OK.']] if key.empty?
|
16
|
+
process key
|
17
|
+
else
|
18
|
+
extract
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def call_app
|
23
|
+
app.call(@env)
|
24
|
+
end
|
25
|
+
|
26
|
+
def extract
|
27
|
+
status, headers, response = call_app
|
28
|
+
if headers["Content-Type"] =~ /text\/html|application\/xhtml\+xml/
|
29
|
+
# TODO if contains <script type="text/r">
|
30
|
+
body = ""
|
31
|
+
response.each { |part| body << part }
|
32
|
+
while md = body.match(node_regex)
|
33
|
+
tempdir = temp_dir
|
34
|
+
key = File.basename(tempdir)
|
35
|
+
r_script = ERB.new(config.r_header).result(binding) + md.to_a.last
|
36
|
+
write_file(File.join(tempdir, config.r_file), r_script)
|
37
|
+
body.sub!(node_regex, ajaxer(key))
|
38
|
+
end
|
39
|
+
headers["Content-Length"] = body.length.to_s
|
40
|
+
response = [ body ]
|
41
|
+
end
|
42
|
+
[ status, headers, response ]
|
43
|
+
end
|
44
|
+
|
45
|
+
def process(key)
|
46
|
+
path = temp_dir(key)
|
47
|
+
return call_app unless File.directory?(path) # TODO render error msg
|
48
|
+
time = exec_r_script(config.r_file, path)
|
49
|
+
files = Dir.entries(path).sort
|
50
|
+
# build body
|
51
|
+
body = [ config.html.prefix ]
|
52
|
+
files.each do |file|
|
53
|
+
config.templates.each do |template|
|
54
|
+
if file =~ Regexp.new(template['pattern'], Regexp::IGNORECASE)
|
55
|
+
src = File.join(path, file)
|
56
|
+
eval(template['process']) unless template['process'].nil?
|
57
|
+
body << ERB.new(template['template']).result(binding)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
# TODO remove temp dir
|
62
|
+
body << config.html.suffix
|
63
|
+
[ 200, { "Content-Type" => "text/html" }, body ]
|
64
|
+
end
|
65
|
+
|
66
|
+
# return true if request method is GET
|
67
|
+
def get?
|
68
|
+
@env["REQUEST_METHOD"] == "GET"
|
69
|
+
end
|
70
|
+
|
71
|
+
def path_regex
|
72
|
+
Regexp.new "^#{config.url_scope}/(.*)"
|
73
|
+
end
|
74
|
+
|
75
|
+
def node_regex
|
76
|
+
opts = Regexp::IGNORECASE + Regexp::MULTILINE
|
77
|
+
Regexp.new config.node_regex, opts
|
78
|
+
end
|
79
|
+
|
80
|
+
def path_info
|
81
|
+
@env["PATH_INFO"]
|
82
|
+
end
|
83
|
+
|
84
|
+
def match_path
|
85
|
+
path_info.match(path_regex)
|
86
|
+
end
|
87
|
+
|
88
|
+
def exec_r_script(file, path=nil)
|
89
|
+
cmd = path.nil? ? "R CMD BATCH #{file}" :
|
90
|
+
"(cd #{path} && R CMD BATCH #{file})"
|
91
|
+
%x[#{cmd}]
|
92
|
+
end
|
93
|
+
|
94
|
+
def temp_dir(key=nil)
|
95
|
+
return Dir.mktmpdir(config.temp.prefix, config.temp.dir) if key.nil?
|
96
|
+
File.join config.temp.dir, key
|
97
|
+
end
|
98
|
+
|
99
|
+
def write_file(path, content)
|
100
|
+
::File.open(path, 'w') { |f| f.puts content }
|
101
|
+
end
|
102
|
+
|
103
|
+
def public_path
|
104
|
+
config.public_path.tap do |path|
|
105
|
+
FileUtils.mkdir_p(path) unless File.directory?(path)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def ajaxer(key)
|
110
|
+
ERB.new(config.ajaxer).result(binding)
|
111
|
+
end
|
112
|
+
|
113
|
+
def default_config
|
114
|
+
::File.read(__FILE__).gsub(/.*__END__\n/m, '')
|
115
|
+
end
|
116
|
+
|
117
|
+
def config_path
|
118
|
+
options[:config].tap do |path|
|
119
|
+
raise "no config path given" unless path
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def config
|
124
|
+
return @config unless @config.nil?
|
125
|
+
write_file(config_path, default_config) unless ::File.exist?(config_path)
|
126
|
+
@config = deep_ostruct(::File.open(config_path) { |yf| YAML::load(yf) })
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def deep_ostruct(hash)
|
132
|
+
OpenStruct.new(hash).tap do |ostruct|
|
133
|
+
hash.keys.each do |key|
|
134
|
+
if ostruct.send(key).is_a?(Hash)
|
135
|
+
ostruct.send("#{key}=", deep_ostruct(hash[key]))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
__END__
|
145
|
+
# RackR -- process R on the fly
|
146
|
+
# RackR depends on jQuery >= 1.4.4
|
147
|
+
#
|
148
|
+
# changes to this file will not be picked up
|
149
|
+
# without a restart of your app
|
150
|
+
#
|
151
|
+
enabled: true
|
152
|
+
url_scope: /rack-r
|
153
|
+
public_path: public/system/rack-r
|
154
|
+
public_url: /system/rack-r
|
155
|
+
temp:
|
156
|
+
dir: /tmp
|
157
|
+
prefix: rr_
|
158
|
+
r_file: script.R
|
159
|
+
r_header: |
|
160
|
+
# modify this header in rack-r config file
|
161
|
+
library(yaml)
|
162
|
+
library(DBI)
|
163
|
+
library(RSQLite)
|
164
|
+
root <- '<%= Rails.root %>'
|
165
|
+
dbconf <- yaml.load_file(paste(root, '/config/database.yml', sep=''))
|
166
|
+
dbfile <- paste(root, '/', dbconf$development$database, sep='')
|
167
|
+
drv <- dbDriver("SQLite")
|
168
|
+
con <- dbConnect(drv, dbname=dbfile)
|
169
|
+
ajaxer: |
|
170
|
+
<div class='rack_r' id='<%= key %>'>Processing R...</div>
|
171
|
+
<script type='text/javascript'>
|
172
|
+
var url = '<%= config.url_scope %>/<%= key %>';
|
173
|
+
$.ajax(url, { success: function(data) { $('#<%= key %>').html(data); } });
|
174
|
+
</script>
|
175
|
+
html:
|
176
|
+
prefix: <div class='rack_r_out'>
|
177
|
+
suffix: </div>
|
178
|
+
templates:
|
179
|
+
- pattern: .svg$
|
180
|
+
process: |
|
181
|
+
svg = File.read(src)
|
182
|
+
template: |
|
183
|
+
<%= svg %>
|
184
|
+
- pattern: .(jpg|jpeg|png)$
|
185
|
+
process: |
|
186
|
+
# TODO build dst with key, otherwise may lead to undesired results
|
187
|
+
dst = File.join(public_path, file)
|
188
|
+
FileUtils.cp(src, dst)
|
189
|
+
url = "#{config.public_url}/#{file}"
|
190
|
+
template: |
|
191
|
+
<img src='<%= url %>' />
|
192
|
+
- pattern: .csv$
|
193
|
+
process: |
|
194
|
+
table = CSV.read(src)
|
195
|
+
# TODO build dst with key, otherwise may lead to undesired results
|
196
|
+
dst = File.join(public_path, file)
|
197
|
+
FileUtils.cp(src, dst)
|
198
|
+
url = "#{config.public_url}/#{file}"
|
199
|
+
template: |
|
200
|
+
<a href='<%= url %>'><%= file %></a>
|
201
|
+
<table>
|
202
|
+
<% table.each do |row| %>
|
203
|
+
<tr>
|
204
|
+
<% row.each do |col| %>
|
205
|
+
<td><%= col %></td>
|
206
|
+
<% end %>
|
207
|
+
</tr>
|
208
|
+
<% end %>
|
209
|
+
</table>
|
210
|
+
- pattern: .Rout$
|
211
|
+
process: |
|
212
|
+
rout = File.read(src)
|
213
|
+
template: |
|
214
|
+
<pre><%= rout %></pre>
|
215
|
+
node_regex: <script\s+type=['"]text/r['"]\s*>(.*?)</script>
|
216
|
+
#
|
217
|
+
# uncomment the following two lines, if your project
|
218
|
+
# doesn't use jquery already
|
219
|
+
# javascripts:
|
220
|
+
# - http://code.jquery.com/jquery-1.5.1.min.js
|
221
|
+
# or this line if you want to ship it yourself
|
222
|
+
# - /javascripts/jquery-1.5.1.min.js
|
223
|
+
# uncomment the following line, in case your project uses prototype
|
224
|
+
# jquery_noconflict: true
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rack_r/middleware'
|
2
|
+
|
3
|
+
module RackR
|
4
|
+
|
5
|
+
major, minor, patch = Rails.version.split('.').map { |s| s.to_i }
|
6
|
+
|
7
|
+
case major
|
8
|
+
when 2
|
9
|
+
Rails.configuration.middleware.use RackR::Middleware,
|
10
|
+
:config => File.expand_path('config/rack-r.yml', RAILS_ROOT)
|
11
|
+
|
12
|
+
when 3
|
13
|
+
class Railtie < Rails::Railtie
|
14
|
+
initializer "rack_r.insert_middleware" do |app|
|
15
|
+
app.config.middleware.use "RackR::Middleware",
|
16
|
+
:config => File.expand_path('config/rack-r.yml', Rails.root)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
else
|
21
|
+
raise "Unknown Rails version #{Rails.version}"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
data/rack-r.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "rack_r/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "rack-r"
|
6
|
+
s.version = RackR::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["Phil Hofmann"]
|
9
|
+
s.email = ["phil@branch14.org"]
|
10
|
+
s.homepage = "http://branch14.org/rack-r"
|
11
|
+
s.summary = %q{Use R in your Rack stack}
|
12
|
+
s.description = %q{Use R in your Rack stack}
|
13
|
+
|
14
|
+
# s.rubyforge_project = "rack-r"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib", "rails"]
|
20
|
+
end
|
data/test/example.r
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# y mntl. Rate
|
2
|
+
# f Franchise
|
3
|
+
# s Selbstbehalt
|
4
|
+
# m Selbstbehalt max.
|
5
|
+
|
6
|
+
png('insurance.png')
|
7
|
+
|
8
|
+
costf <- function(y, f, s=0.1, m=700) {
|
9
|
+
return(function(x) {
|
10
|
+
(y * 12) + min(f, x) + max(0, min(m - f, s * (max(f, x) - f)))
|
11
|
+
})
|
12
|
+
}
|
13
|
+
|
14
|
+
f0 <- costf( 95.70, 0 )
|
15
|
+
f1 <- costf( 90.00, 100 )
|
16
|
+
f2 <- costf( 84.10, 200 )
|
17
|
+
f4 <- costf( 72.40, 400 )
|
18
|
+
f6 <- costf( 60.70, 600 )
|
19
|
+
|
20
|
+
x_max <- 7250
|
21
|
+
x <- seq(0, x_max, by=10)
|
22
|
+
plot(x, x, type='l', lty=3,
|
23
|
+
xlim=range(0, x_max),
|
24
|
+
ylim=range(500, 2000),
|
25
|
+
xlab='Kosten Verursacht',
|
26
|
+
ylab='Kosten Gezahlt')
|
27
|
+
|
28
|
+
lines(x, apply(array(x), 1, f0), col='green')
|
29
|
+
lines(x, apply(array(x), 1, f1))
|
30
|
+
lines(x, apply(array(x), 1, f2))
|
31
|
+
lines(x, apply(array(x), 1, f4))
|
32
|
+
lines(x, apply(array(x), 1, f6), col='red')
|
33
|
+
|
34
|
+
f <- function(x) { return(f0(x) - f6(x)) }
|
35
|
+
lower <- uniroot(f, c(400, 600))
|
36
|
+
upper <- uniroot(f, c(2500, 3000))
|
37
|
+
abline(v=lower$root, lty=2)
|
38
|
+
abline(v=upper$root, lty=2)
|
data/test/helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
|
+
require 'rack_r'
|
8
|
+
require 'rack_r/middleware'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
end
|
data/test/test_rack_r.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
require 'rack/mock'
|
3
|
+
|
4
|
+
class TestRackR < Test::Unit::TestCase
|
5
|
+
|
6
|
+
CONFIGFILE = '/tmp/test_rack_r'
|
7
|
+
File.unlink(CONFIGFILE) if File.exist?(CONFIGFILE)
|
8
|
+
|
9
|
+
EXPECTED_CODE = /class='rack_r'/
|
10
|
+
|
11
|
+
context "Util" do
|
12
|
+
should "create proper temp dir" do
|
13
|
+
td = app.temp_dir
|
14
|
+
assert File.directory?(td)
|
15
|
+
|
16
|
+
base = File.basename(td)
|
17
|
+
assert_equal td, app.temp_dir(base)
|
18
|
+
|
19
|
+
assert FileUtils.rm_rf(td)
|
20
|
+
assert !File.exist?(td)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "Proper regexs" do
|
25
|
+
should "detect r code if present" do
|
26
|
+
assert_match app.node_regex, HTML_POSITIVE
|
27
|
+
end
|
28
|
+
|
29
|
+
should "not detect r code if none is present" do
|
30
|
+
assert_no_match app.node_regex, HTML_NEGATIVE
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "Embedding payload" do
|
35
|
+
should "place the payload in body of an HTML request" do
|
36
|
+
assert_match EXPECTED_CODE, request.body
|
37
|
+
end
|
38
|
+
|
39
|
+
should "place the payload in body of an XHTML request" do
|
40
|
+
response = request(:content_type => 'application/xhtml+xml')
|
41
|
+
assert_match EXPECTED_CODE, response.body
|
42
|
+
end
|
43
|
+
|
44
|
+
should "not place the payload in a non HTML request" do
|
45
|
+
response = request(:content_type => 'application/xml', :body => [XML])
|
46
|
+
assert_no_match EXPECTED_CODE, response.body
|
47
|
+
end
|
48
|
+
|
49
|
+
should "not place the playload in a document not containing r code" do
|
50
|
+
response = request(:body => HTML_NEGATIVE)
|
51
|
+
assert_no_match EXPECTED_CODE, response.body
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "Processing R" do
|
56
|
+
should 'detect get request' do
|
57
|
+
request(:path => "/rack-r/rr-some-random-key")
|
58
|
+
assert @app.get?
|
59
|
+
end
|
60
|
+
|
61
|
+
should 'detect path' do
|
62
|
+
request(:path => "/rack-r/rr-some-random-key")
|
63
|
+
assert @app.match_path
|
64
|
+
end
|
65
|
+
|
66
|
+
should 'properly respond to rack_r request' do
|
67
|
+
key = 'rr-some-key'
|
68
|
+
path = app.temp_dir(key)
|
69
|
+
FileUtils.rm_rf(path) if File.exist?(path)
|
70
|
+
Dir.mkdir(path)
|
71
|
+
src = File.expand_path('../example.r', __FILE__)
|
72
|
+
dst = File.join(path, app.config.r_file)
|
73
|
+
FileUtils.cp(src, dst)
|
74
|
+
response = request(:path => "/rack-r/#{key}")
|
75
|
+
assert Dir.entries(path).include?('insurance.png')
|
76
|
+
regex = Regexp.new("src='#{@app.config.public_url}/insurance.png'")
|
77
|
+
assert_match regex, response.body
|
78
|
+
FileUtils.rm_rf(path)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# context "Deliver payload" do
|
83
|
+
# setup do
|
84
|
+
# @expected_file = File.expand_path(File.join(%w(.. .. public) << PAYLOAD), __FILE__)
|
85
|
+
# @response = request({}, "/#{PAYLOAD}")
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# should "deliver #{PAYLOAD}" do
|
89
|
+
# expected = File.read(@expected_file)
|
90
|
+
# assert expected, @response.body
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# should "set the content-type correctly" do
|
94
|
+
# assert 'text/javascript', @response.body
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
HTML_POSITIVE = <<-EOHTML
|
101
|
+
<html>
|
102
|
+
<head>
|
103
|
+
<title>Sample Page</title>
|
104
|
+
</head>
|
105
|
+
<body>
|
106
|
+
<h2>Here goes some R</h2>
|
107
|
+
<script type='text/r'>
|
108
|
+
some invalid r code
|
109
|
+
</script>
|
110
|
+
</body>
|
111
|
+
</html>
|
112
|
+
EOHTML
|
113
|
+
|
114
|
+
HTML_NEGATIVE = <<-EOHTML
|
115
|
+
<html>
|
116
|
+
<head>
|
117
|
+
<title>Sample page with no matching node</title>
|
118
|
+
</head>
|
119
|
+
<body>
|
120
|
+
<p>Empty page.</p>
|
121
|
+
</body>
|
122
|
+
</html>
|
123
|
+
EOHTML
|
124
|
+
|
125
|
+
XML = <<-EOXML
|
126
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
127
|
+
<user>
|
128
|
+
<name>Some Name</name>
|
129
|
+
<age>Some Age</age>
|
130
|
+
</user>
|
131
|
+
EOXML
|
132
|
+
|
133
|
+
def request(options={})
|
134
|
+
path = options.delete(:path) || '/'
|
135
|
+
@app = app(options)
|
136
|
+
request = Rack::MockRequest.new(@app).get(path)
|
137
|
+
yield(@app, request) if block_given?
|
138
|
+
request
|
139
|
+
end
|
140
|
+
|
141
|
+
def app(options={})
|
142
|
+
options = options.clone
|
143
|
+
options[:content_type] ||= "text/html"
|
144
|
+
options[:body] ||= [ HTML_POSITIVE ]
|
145
|
+
rack_app = lambda do |env|
|
146
|
+
[ 200,
|
147
|
+
{ 'Content-Type' => options.delete(:content_type) },
|
148
|
+
options.delete(:body) ]
|
149
|
+
end
|
150
|
+
RackR::Middleware.new(rack_app, :config => CONFIGFILE)
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-r
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Phil Hofmann
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-04-23 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Use R in your Rack stack
|
22
|
+
email:
|
23
|
+
- phil@branch14.org
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- .gitignore
|
32
|
+
- .rvmrc
|
33
|
+
- Gemfile
|
34
|
+
- LICENSE
|
35
|
+
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- TODO
|
38
|
+
- lib/rack_r.rb
|
39
|
+
- lib/rack_r/middleware.rb
|
40
|
+
- lib/rack_r/railtie.rb
|
41
|
+
- lib/rack_r/version.rb
|
42
|
+
- rack-r.gemspec
|
43
|
+
- test/example.r
|
44
|
+
- test/helper.rb
|
45
|
+
- test/test_rack_r.rb
|
46
|
+
homepage: http://branch14.org/rack-r
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
- rails
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 3
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.8.23
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: Use R in your Rack stack
|
80
|
+
test_files: []
|
81
|
+
|