sebastian.martinez

Drag & Drop Sortable Lists

Posted by sebastian.martinez
on July 27, 2009

Time has come for us to make a sortable list, and let's face it, drag&drop are the prettiest ones. So, let me explain how to proceed.

Suppose you have a playlist with many videos, and want to establish an order on which they will be played. First thing you will need is to add a 'position' attribute to your Video model. To do that, we'll generate a migration first:

script/generate migration add_position_to_videos position:integer

Now just run rake db:migrate.

View Code

First thing you need to make sure, is that you have the prototype and scriptaculous libraries in your application. Now let's see how your index.html.erb file should look like

   

Videos

    <% @videos.each do |video| %> <% content_tag_for :li, video do %> <%= link_to video.name, video_path(video) %> <% end %> <% end %>
<%= link_to "New Video, new_video_path %>

Now all we have to do to make the list sortable is add the sortable_element helper method to the index view. You need to pass it the id of the element you want to be sortable, and a URL that is called via an AJAX request so that the updated positions can be stored in the database.

   <%= sortable_element('videos', :url => 'sort_videos_path') %>  

The videos in the list can now be dragged and dropped, but the new order isn’t persisted back to the database. So let's code the sort method in the videos_controller.rb

   def sort  
     params[:videos].each_with_index do |id, index|  
       Video.update_all([’position=?’, index+1], [’id=?’, id])  
     end  
     render :nothing => true  
   end  

And that's it. We are now updating the position of the videos. Most probably, we would want now our videos to be shown in the index in the correct order, so we just need to touch the index method on the controller a bit.

   def index  
     @videos = Video.all(:order => ’position’)  
   end  

At this point we are almost set to go...did you guess what's missing? Yes, we need to add the route to the sort method :)

   map.resources :videos, :collection => { :sort => :post}  

The line above adds the new action and makes it a POST request, which is the type that our AJAX call uses when making XMLHTTP requests. And that's pretty much it. Now you have a fully functional sortable list of videos.

There are some more tricks we can use to improve this, for example, the sortable_element helper receives one option called 'handle'. What's this? The way to specify the draggable area of the element. This I think is the most used one. Let's give it a try:

We should first add a span tag on the index view:

 <% content_tag_for :li, video do %>  
   [drag]  
   <%= link_to video.name, video_path(video) %>  
 <% end %> 

Note the span has a 'handle' class, and that's what you are going to set as draggable, using the :handle option:

   <%= sortable_element(’videos’), :url => sort_videos_path, :handle => ’handle’ %>  

You can style this a bit with a CSS, adding a line similar to this one:

 li .handle { color: #777; cursor: move; font-size: 12px; } 

Beautiful....just beautiful...

Ok, we have this magical sortable list now, but we added a 'position' field to videos...we can handle this manually when creating or updating the videos, or we can use the acts_as_list plugin, which I recommend. Just install it by typing

script/plugin install git://github.com/rails/acts_as_list.git
in your console. All configuration this needs to work is to add acts_as_list in our model, that should look like this:

   class Video < ActiveRecord::Base  
     acts_as_list  
   end  

Congrats!!! Enjoy your sortable list !!!

| |
jose.costa

RVideo for video processing and inspection

Posted by jose.costa
on July 20, 2009

At WyeWorks headquarters, every once in a while, we come across some project that needs a media edition/transcoding solution to build into. This was the case of our latest project in which we built a pretty simple interface with Brightcove(Brightcove - The Leading Online Video Platform.), a powerful video platform on which we may write something about it in our forthcoming posts, but it's not the point right now.

Turns out to be that Brightcove recommends that files should be encoded using either H.264 or VP6. As usual, we ask ffmpeg for salvation when we need to transcode media files and this was not the exception. But we didn't want to transcode just every file nor make the choice based on the file's extension. We wanted a way to check the current file encoding.

Searches made at that time lead us to think that the usual way to get a media file encoding is by running:

ffmpeg -i

which i must say that it's pretty ugly for me since that command it's supposed to be used for conversion and as far as i know it doesn't offer some flag to get only the file information. In fact, that command returns an error (but still prints the information we need):

jose:~$ ffmpeg -i barsandtone.flv FFmpeg version 0.5-svn17737+3:0.svn20090303-1ubuntu6, Copyright (c) 2000-2009 Fabrice Bellard, et al.

configuration: --enable-gpl --enable-postproc --enable-swscale --enable-x11grab --extra-version=svn17737+3:0.svn20090303-1ubuntu6 --prefix=/usr --enable-avfilter --enable-avfilter-lavf --enable-libgsm --enable-libschroedinger --enable-libspeex --enable-libtheora --enable-libvorbis --enable-pthreads --disable-stripping --disable-vhook --enable-libdc1394 --disable-armv5te --disable-armv6 --disable-armv6t2 --disable-armvfp --disable-neon --disable-altivec --disable-vis --enable-shared --disable-static
libavutil     49.15. 0 / 49.15. 0
libavcodec    52.20. 0 / 52.20. 0
libavformat   52.31. 0 / 52.31. 0
libavdevice   52. 1. 0 / 52. 1. 0
libavfilter    0. 4. 0 /  0. 4. 0
libswscale     0. 7. 1 /  0. 7. 1
libpostproc   51. 2. 0 / 51. 2. 0
built on Apr 10 2009 23:18:41, gcc: 4.3.3

Input #0, flv, from 'barsandtone.flv':

Duration: 00:00:06.00, start: 0.000000, bitrate: 505 kb/s
  Stream #0.0: Video: *vp6f*, yuv420p, 360x288, 409 kb/s, 1k tbr, 1k tbn, 1k tbc
  Stream #0.1: Audio: mp3, 44100 Hz, stereo, s16, 96 kb/s
**Must supply at least one output file**

The information we need is the video codec (in this case vp6f) to determine if we need to transcode it.

Another thing to mention is that nothing that you see as the output there outputs to the stdout. No rocket science needed but i was kind of lazy to parse this from scratch. Lucky for me, there was this not so new rvideo(rvideo) gem (still unknown for me) that made me save some precious time.

RVideo(rvideo) still relies in ffmpeg command and also it's internal work to get the information we need involves that same output parsing, you just don't have to do it yourself. After installation (not covered here, just check rvideo readme file), you can do things as shown below:

inspector = RVideo::Inspector.new(:file => file.path) inspector.video_codec => "vp6f" inspector.duration => 6000 inspector.audio_codec => "mp3"

We've just made a tiny introduction to inspection. I'm not covering video processing. Check rvideo(rvideo) for more details! Enjoy!

| |
santiago.pastorino

Paperclip file rename

Posted by santiago.pastorino
on July 13, 2009

While developing an application with Sebastián that allow users to upload videos with some file name restrictions, meaning that it must contain only A-Z and 0-9 digits, underscores (_) as a valid component as well, and also the name must be preceded by it's own #id, we came up with the need of applying this custom filter to each uploaded video. After doing some research on paperclip source code and internet tutorials, we suggest the following solution:

class Video < ActiveRecord::Base
  has_attached_file :video,
    :path => ":rails_root/public/system/:attachment/:id/:style/:normalized_video_file_name",
    :url => "/system/:attachment/:id/:style/:normalized_video_file_name"

  Paperclip.interpolates :normalized_video_file_name do |attachment, style|
    attachment.instance.normalized_video_file_name
  end

  def normalized_video_file_name
    "#{self.id}-#{self.video_file_name.gsub( /[^a-zA-Z0-9_\.]/, '_')}"
  end
end

What are we doing here? Easy, in has_attached_file we edit the way paperclip returns the path and url by default, the most relevant components when saving and loading the file in order to display it. Paperclip default values are:

path default => ":rails_root/public/system/:attachment/:id/:style/:filename"
url default => "/system/:attachment/:id/:style/:filename"

Values preceded by ':' are the standard interpolations paperclip has. For further information on this visit http://wiki.github.com/thoughtbot/paperclip/interpolations(Interpolations)

What we did was change :filename with :normalized_video_file_name in both path and url, being the second a custom interpolation and then added the 'normalized_video_file_name' method to video.rb.

By doing this we not only achieve a way for paperclip to handle the file by this normalized way, but also have a method to access the normalized file name, plus being able to access the original file name through paperclip video_file_name method.

So remember on video_file_name you have the uploaded filename and on normalized_video_file_name you have the server filename.

| |
martin.alcala

Is your product Innovative?

Posted by martin.alcala
on July 06, 2009

That's a good question, in fact, can we quantify or measure how much "innovation" quantity exists in our product or services?. Well, there are as much answers as successful products ;-) but in general terms we can group Innovation in three categories:

* Business Model

      Is your Business Model Innovative? did you find out a new way to sell? did you find out an hidden need of the community? Starbucks would be a nice example.
* Tech

      Did you invent new super algorithm to ultra-fast classify all the info on the web ;-) - that a techy innovation -. Tech is a great point for innovation, but not the only one, think about twitter for example.
* Design

       Design in fact is key a factor for innovation, for example think about the IPhone or the IPod, the key was the design and the marketing.

 So you reader, Rails Enthusiast, Ruby Rockstar, Code Ninja, are you ready to innovate in the wide sense or the word?
| |
Twitter me