Loading presentation...

Present Remotely

Send the link below via email or IM

Copy

Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Make your likes visible on Facebook?

Connect your Facebook account to Prezi and let your likes appear on your timeline.
You can change this under Settings & Account at any time.

No, thanks

Distributed Performance using Rails and HTML5

Harnessing the power of Ruby on Rails and HTML5 to make a distributed performance art framework.
by

Jesse Allison

on 1 June 2011

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of Distributed Performance using Rails and HTML5

Distributed Performance Ruby on Rails HTML 5 HTML5 Ruby on Rails Setup Mac PC Ships with Ruby 1.8.7 & Rails 2.3.5 Fine to get started, but Ruby 1.9 and Rails 3.x are here, might as well use them. Upgrade Ruby - Use RVM (Ruby Version Manager)
https://rvm.beginrescueend.com/rvm/install/
make sure to run the script to add rvm to your profile
rvm list known (gives a list of typical ruby installations)
rvm install 1.9.2 (or whatever version you would like)
rvm use 1.9.2 ( to change back, just list the appropriate version like rvm use system) Install Git - This is a version management software that many ruby developers use to distribute their code. This makes things easier down the line.
http://git-scm.com/ Download and install. Upgrade Rails - Use gems to handle all sorts of ruby frameworks and addons. (FYI each version of Ruby in RVM will have its own set of gems)
gem list -> displays the gems installed for this version of ruby
gem install rails -> installs the latest release of rails (3.0.7 as of today)
If you want something later, check out http://everydayrails.com/2011/05/08/rails-3.1-beta-rvm.html to create an RVM Gemset with a beta version of Rails. Aptana integrates an installation of rails and inbuilt terminal in its IDE: http://aptana.com/products/radrails
http://beans.seartipy.com/2008/06/09/setting-up-rails-development-environment-on-windows-vistaxp/
Looks interesting http://instantrails.rubyforge.org/wiki/wiki.pl?Getting_Started What is Rails??? in-place editing <canvas> + javascript semantic markup Embedded Audio/Video Offline Storage Database http://caniuse.com Web Sockets Simple Button using Canvas Setup <canvas> for usage: <body onLoad="init();">
<canvas id="button_canvas" width="100" height="100"></canvas>
</body> Make a <canvas> inside the body of the html document
Add an onLoad function that will eventually draw to the canvas. <script type="text/javascript" src="/prototype.js"></script> Load prototype.js in the <head> of the document for easing AJAX usage, et al. (or jQuery if you'd rather) Make your own javascript. Get the canvas element and get the drawing context. We'll see where these go in a second. Now you are ready to make the button! <script type="application/x-javascript">
var button_canvas = document.getElementById("button_canvas");
var button_ctx = button_canvas.getContext("2d");
</script> Draw the Button Just inside your <script> tags, set up variables to handle anything you'd like to change var button_color = "rgba(0, 0, 160, 0.5)";
var pressed_color = "rgba(100, 0, 120, 0.8)";
var pressed = 0; // pressed or not Make a draw() function to draw the button
notice that we've moved the getContext method inside function draw() {
var button_ctx = button_canvas.getContext("2d");
button_ctx.clearRect (10, 10, 90, 90);

button_ctx.fillStyle = "rgb(40,0,0)";
button_ctx.fillRect (0, 0, 90, 90);
if(pressed == 1) {
button_ctx.fillStyle = pressed_color;
} else {
button_ctx.fillStyle = button_color;
}
button_ctx.fillRect (10, 10, 90, 90);
} Order of events
Get the drawing context - all draw commands will be run on this variable.
Set the color [fillStyle] any other drawing setup can also go here lineWidth, etc.
Draw a background rectangle using .fillRect [fillRect, rect, strokeRect, arc, fill, lineTo, moveTo, etc.]
Set the color again for the foreground button. This uses the color variables defined above, and will choose one or the other dependent on the button being pressed or not.
draw the foreground button rectangle. pressed normal state function init() {
var button_canvas = document.getElementById("button_canvas");
draw();
} Make an init() function to perform any setup, get the canvas object we want to draw to, and call the first draw() Add Interaction Associate event handlers with the canvas.
Notice that we've associated both clicks and touch events with the same function. function init() {
var button_canvas = document.getElementById("button_canvas");
draw();
button_canvas.addEventListener("mousedown", press, false);
button_canvas.onTouchStart = press;
button_canvas.addEventListener("mouseup", release, false);
button_canvas.onTouchEnd = release;
} Make a function to receive the events.
Change the state of the object [pressed | not pressed]
re-draw the button dependent on the state
Send an Ajax request with any messages you want to send back to the web application serving the page. function press() {
pressed = 1;
draw();
new Ajax.Request('/button_press', {parameters: {button: '1'}});
}

function release() {
pressed = 0;
draw();
} <html>
<head>
<title><canvas> button</title>
<script type="text/javascript" src="/prototype.js"></script>

<script type="application/x-javascript">

var button_color = "rgba(0, 0, 160, 0.5)";
var pressed_color = "rgba(100, 0, 120, 0.8)";
var pressed = 0; // pressed or not

function init() {
var button_canvas = document.getElementById("button_canvas");
draw();
button_canvas.addEventListener("mousedown", press, false);
button_canvas.onTouchStart = press;
button_canvas.addEventListener("mouseup", release, false);
button_canvas.onTouchEnd = release;
}

function draw() {
var button_ctx = button_canvas.getContext("2d");
button_ctx.clearRect (10, 10, 90, 90);

button_ctx.fillStyle = "rgb(40,0,0)";
button_ctx.fillRect (0, 0, 90, 90);
if(pressed == 1) {
button_ctx.fillStyle = pressed_color;
} else {
button_ctx.fillStyle = button_color;
}
button_ctx.fillRect (10, 10, 90, 90);
}

function press() {
pressed = 1;
draw();
new Ajax.Request('/button_press', {parameters: {button: '1'}});
}

function release() {
pressed = 0;
draw();
}


</script>

</head>

<body onLoad="init();">
<canvas id="button_canvas" width="100" height="100">If you see this, your browser does not support the canvas tag.</canvas>

</body>
</html> Framework for creating web applications
Database
routing & event handling
asset management
MVC architecture for sustainable code Rails App Hop into the terminal and hold on tight.
Navigate to your Sites folder (or wherever) Create a Rails App $ cd ~/Sites
$ rails new delayer This made a new rails application. Test things by opening up a terminal window and typing:
you$ ruby -v
you$ rails -v

This should give you the relevant versions that you will be working with. Text Editor: You will need one. You can use an IDE like X-Code, Visual Studio, Eclipse, etc. or you can just use a text editor like Text Edit, or the perenial favorite TextMate. $ cd delayer
$ ls
Gemfile
Rakefile
config
db
lib
public
test
vendor
README
app
config.ru
doc
log
script
tmp rosc, Bundler, & Gemfile Routing Requests Rails handles all incoming page requests from the server in MVC fashion:

routes.rb
controller
view

The routes.rb file specifies any and all urls that will function for this site. If it is not listed here, it will give an error or a 404.
Generally, resources are used to for database access pages and match is used for generic pages.

To see what routes are in your app, go to the root folder and type:
$ rake routes

Once the routes.rb file has matched an incoming request, it directs it to the specified controller and action.

The Controller takes the request, gathers any information needed from the Model, and then sends the information to the View to be rendered.

The View files are the typical html documents. They are usually broken into a Layout file with the <html> and <head> sections, and the view for the action called in the controller which holds the body.

This view can access any ruby variables defined with the @ sign in the controller. Making a Page of Our Own The most important being the app folder which contains all of the files for the Controller, dynamic Views, and functional Model code.

The config folder contains all of the configuration files for the database, various environments - development, test, and production, initializers, and routes.

The db folder contains all of the migration information for the database. These are step by step instructions on creating the database and basic relationships that you create as you need them. Very useful

The public folder is used for static content - web pages, media assets, javascripts and css files and anything else you want to directly link to. At this point you may get an error saying that the sqlite3 gem is missing. To rectify this, try

$ gem install sqlite3

This installs the sqlite3 gem in our ruby 3.0.7 gem folder. This means the gem is available for any application that requires the sqlite3 gem. You can verify the installation with:

$ gem list

This is a great way to manage your gems, but if you ever take your rails app from one computer to another, or worse, to a server you don't control, you may not be able to install the gem there, or there may be another version of the gem that isn't compatible with what you want to do. Needless to say, there is a more rails-oriented way. We'll take a look at that in just a minute. $ rails server

This starts a mongrel server running on port 3000 by default. At this point you should be able to point a browser to:

http://localhost:3000/

and get a Welcome Aboard page. This page is the index.html page from the /public folder. Start the server: Missing sqlite3? Add a Controller: We're going to add a UI controller and views to serve up the main page of our site. To do that, we can use one of rails handy generators. From the root folder, type:

$ rails generate controller ui index

This will generate a controller file called ui_controller.rb that contains an index action. Go ahead and open it up.

You'll see the:

def index
end

section, which doesn't look like much, but demonstrates the power of Rails. By convention, any incoming request will automatically be passed to a view called by the same name as the action. This function is indeed functional, rendering the /app/views/ui/index.html.erb file by default.

If you fire up the server and navigate to:
http://localhost:3000/

You'll see that we haven't made an index.html.erb file. Let's fix that. Defining the content
Create a Scaffold
Run the migration
New (making a manual entry)
Update (changing a field) Memory:
Using a Database Creating a scaffold:

$ rails generate scaffold delays count:integer delay_time:integer decay:float

This creates the typical CRUD code to create, read, update and destroy database entries. It also creates a migration file that dictates how the database will be created. This file can be updated to adjust for different fields.

$ rake db:migrate

Updates the database with any new migrations. If you want to move to a different migration, use VERSION. Be aware that this will drop tables and revert changes which may loose any content that was in the database.

$ rake db:migrate VERSION=123456789 New entries are simple to make.

@new_delay = Delay.new
@new_delay.count = 1
@new_delay.id = @@user_number
@new_delay.delay_time = 500
@new_delay.decay = 0.5

@new_delay.save

The save function returns true/false so you can use this in an if statement to make adjustments if it fails. Updating is fairly simple as well

delay_to_update = Delay.find(delay.id)
delay_hash = { "count" => delay_to_update.count + 1}
delay_to_update.update_attributes(delay_hash)

the .update_attributes function takes a hash of attribute => value pairs to update in the database. All other attributes remain the same.

Note: forms on a webpage are submitted to the controller as a parameter => value hash called params[]. This is ideal for updating as is witnessed in the scaffold's update method.

@delay = Delay.find(params[:id])
@delay.update_attributes(params[:delay]) Using the data from the database is fun. You can grab an entry by id.

@delay = Delay.find(id)

@all_delays Delay.find_all()

@short_delays = Delay.find_by_delay_time(delay_time < 500)

fields can be accessed on a single entry by:

@delay.count

If there is more than one record it is in a hash of entries which you can cycle through:

@all_delays.each do |delay|
put delay.delay_time
end You can also make named_scopes by placing a search inside the model file. If I place this inside the app/model/delay.rb file it will be accessible in the controller.

scope :short_delays, where("delay_time <= ?", 300)
scope :ascending_delay, order('delay_time ASC')

now in the controller you can call

@short_delays = Delay.short_delays

even better, you can chain scopes.

@short_delays = Delay.short_delays.ascending_delay Defining the Content:

As with any database, we need to decide what content we want to hold and what data type it should be. In Rails, we create migrations that specify this data. Everytime we want to add more tables, fields, or relationships, we simply make another migration. In this way, we can iteratively create, add to, and change the database without having to start from the beginning. Rails keeps track of the current state of the database and we then migrate to the newest version when we are ready. If anything goes wrong, we can migrate back to any previous state of the database.

In our rails app, we will have an entry for each interface that is passed out to a browser - essentially, each performer. The data that we will track is the number of times that each button is pressed, the delay time, and the decay amount.

count - will be an integer
delay_time - will be an integer
decay - will be a floating point number form_remote_for delay & decay Why do this??? Make a Route: Open up the /config/routes.rb file. You can scan through the file to get an overview of the options. Down towards the bottom of the page is a commented out root :to => section. Uncomment it and change it to:

root :to => "ui#index"

This specifies that the root of our site:
http://localhost:3000/
will be directed to the ui_controller.rb file and index action.

The last thing we need to do is get rid of the /public/index.html file as anytime a static file is in the public folder, that is displayed instead of the dynamic route.

Once you do that, you should get a nice Routing Error. We haven't made the ui_controller yet! Make a View Create the /app/views/ui/index.html.erb file

the .html part indicates to rails that this is indeed an html file. It could also be .xml, .js, .yaml, etc. The .erb stands for embedded ruby. Essentially, we have an html file with ruby embedded in it via the <% %> and <%= %> tags. The first set <% %> to evaluate the ruby code inside and the second set <%= %> to evaluate the code and insert any results.

If you just type

hi

and save the file, you'll see when you reload the page, that "hi" is what you get. Notice that the <html>, <head>, and <body> tags are handled for you. They are contained in the /app/views/layouts/application.html.erb file.

Let's copy and paste all of the code we made for the button into these files.

Take the stuff that we had in the <head> section and add it in the layout file. Make sure to pull out the <script> that loads the prototype.js file as it is already included in the embedded ruby
<%= javascript_include_tag :defaults %>

Also make sure that you put the onLoad function into the <body> tag.

Next, take the <canvas> line that was contained in the body and add it to the index.html.erb file.

Reload the page and your button is there. Add rosc for handling osc messaging. Add the following line to /gemfile

gem 'rosc'

Then run:
$ bundle install

This installs any gems needed for the application. To set up rosc, create the file /config/initializers/osc_init.rb
__________________
require 'osc'

OSCHost = "localhost"
OSCReceivePort = 7474
OSCSendPort = 7475

puts " -------- Starting OSC Server __________ "
OSCSender = OSC::UDPSocket.new

@@user_number = 0
__________________ def button_press
button = params[:button]
m = OSC::Message.new("/press", nil, button.to_i)
OSCSender.send(m, 0, OSCHost, OSCSendPort)

render :nothing => true
end Interaction We're nearly there! You may have noticed that when you started the server, that terminal window stays active. Anytime a page request comes in, information about it - and any problems - are printed out. This is called the log. Currently we are running in the development environment and the log is very active.

If you click our button, it will attempt to send an AJAX request to /button)press and an error pops up. You'll see that we have a routing error as we haven't set up a route to handle this request. We may as well set up all three of the interactions we'll be using in the routes.rb file:

match 'button_press/' => 'ui#button_press'
match 'delay_time/' => 'ui#delay_time'
match 'decay/' => 'ui#decay' Counting Each UI Distributed To keep track of how many user interfaces we have distributed, we can create a global variable to increment each time. Global Variables can be placed in an initializer file. In this case, either make a new file in /config/initializer/ or just add it to the osc_init.rb file created for rosc. Add:

@@user_number = 0

Then in the ui_controller#index method we can add:

@@user_number = @@user_number + 1
@button_number = @@user_number

The class variable @button_number is used to pass the value to the view for erb. It can be displayed thus:

<%= @button_number %> Adding a Slider This code is coming from the NEXUS Distributed Performance Project. http://allisonic.com/nexus/ for the latest information.

Add the spline_slider.js file

Create another Canvas in your view with a different id
<canvas id="slider_1" width="50" height="150"></canvas>

Add a <script> somewhere after the <canvas> to initialize the <canvas>

<script>
slider_obj=new splineSlider("slider_1", button_number);
</script>

def decay
m = OSC::Message.new("/decay", nil, params[:button].to_i, params[:decay].to_f)
OSCSender.send(m, 0, OSCHost, OSCSendPort)
render :nothing => true
end Create the Form in a view file:
<%= form_tag({:action => :delay_time}, {:method => :put, :remote => :true}) do %>
<div class="field">
<%= label :delay, :delay_time %><br />
<%= text_field_tag :delay_time, 500 %>
</div>
<%= hidden_field_tag 'button', @button_number.id%>
<% end %>

This is a fairly standard usage of Rails. Form_tag is for submitting forms that don't have a model. The :remote attribute specifies that this is an AJAX call, not a call to refresh the page with new content. text_field_tag specifies "delay_time" as the parameter being set and 500 as the initial value. The hidden_field_tag provides another parameter "button" that has an id of the button number.

Make the method to handle the AJAX Request. Very similar to our button_press code.

def delay_time
m = OSC::Message.new("/delay_time", nil, params[:button].to_i, params[:delay_time].to_i)
OSCSender.send(m, 0, OSCHost, OSCSendPort)
render :nothing => true
end Using a Text Field as Input Add the ui.css file

Move the button script into button.js Clean Up Distributed Performance Systems using Rails Dr. Jesse Allison Louisiana State University
Experimental Music | Digital Media
jtallison@lsu.edu
jesse@electrotap.com Get Canvas Element
Draw initial state
Attach event handlers Setup Variables Draw commands on button context
Change button color based on pressed state Press & release - update the pressed state and draw
Send AJAX request http://emdm.music.lsu.edu
http://allisonic.com/nexus/ Getting Started the Nexus Project, an approach to Jesse Allison
Full transcript