blog
Ajax Flavoured Sliding DatePicker

When I hate something there is no way I want to put up with it.. So, approaching a new project where users will be facing a lot of calendar use, I started brainstorming(!) an edulcorated solution.. The result is interesting so far, check the demo page!

Yes, there is a small oops when friendy Feb is 29 days long, but I’m just too sleepy to take care of that now.. ;)

Interactive script.aculo.us Handlers in a single Slider

If you need to create a Slider with two Handlers for posting an interval where min_value must be less then max_value and you want the interface to respect this rule:


AVERAGE DAY RATE

<input id="r_value_min" name="r_value_min" readonly="readonly"  size="5" style="display:none;" type="text" value="0" />
<input id="r_value_max" name="r_value_max" readonly="readonly"  size="5" style="display:none;" type="text" value="5000" />
BETWEEN 
 <span id="rate_value_min" class="invisible">0</span> AND 
 <span id="rate_value_max" class="invisible">5000</span> USD
<br>
  <div id="rate_slider" class="slider">
    <div class="handler_min"></div>
    <div class="handler_max"></div>
  </div>
<br> 

<script type="text/javascript">
  (function() {

  min = new Control.Slider($('rate_slider').down('.handler_min'), $('rate_slider'), {
      range: $R(0, 4999),
      sliderValue: 0,
      onSlide: function(value) {
   if (value <  $('r_value_max').value) {
         $('r_value_min').value = value.toFixed();
   $('rate_value_min').innerHTML = value.toFixed();
   } else {
         $('r_value_min').value = value.toFixed();
   $('rate_value_min').innerHTML = value.toFixed();
      $('r_value_max').value = (value+1).toFixed();
         $('rate_value_max').innerHTML = (value+1).toFixed();
   max.setValue((value+1).toFixed());
  }
  
      }
    });

   max =  new Control.Slider($('rate_slider').down('.handler_max'), $('rate_slider'), {
      range: $R(1, 5000),
      sliderValue: 5000,
      onSlide: function(value) {
   if (value >  $('r_value_min').value) {
      $('r_value_max').value = value.toFixed();
         $('rate_value_max').innerHTML = value.toFixed();
   } else {
      $('r_value_max').value = value.toFixed();
         $('rate_value_max').innerHTML = value.toFixed();
   $('r_value_min').value = (value-1).toFixed();
         $('rate_value_min').innerHTML = (value-1).toFixed();
   min.setValue((value+1).toFixed());
  }
      }
    });

  })();
</script>
</div>
</div>

In this implementation the min_slider pushes up the max_slider when the interval is reduced to 0 and viceversa. I decided not to show text fields, but labes for better formatting..
Happy Sliding! =)

see the demo
grab the source

HUGE file upload with Ruby On Rails and Adobe FLEX

I spent 10 years now in web-based development and I never came across anything worse than trying to post big files to a web server.. Not only the problem of not being able to finish the upload because you kill the server but also the horrible wait for the operation to finish without any clue about the real status.. but now I have the final and robust solution for this task (with a cute and precise progress bar)!!

This example is obviously realized with RoR, but probably it’s not too hard to implement with PHP.

Let’s assume you have a User model in your application and let’s create a UserAttachment model to store and manage the uploaded files:


class CreateUserAttachments < ActiveRecord::Migration
 def self.up
  create_table :user_attachments do |t|
    t.column :title, :string, :null => false
    t.column :caption, :string
    t.column :content_type, :string
    t.column :original_filename, :string
    t.column :crypted_filename,:string, :limit => 40
    t.column :size,:integer
    t.column :file_ext, :string, :limit => 4, :default => 0
    t.column :is_public, :string, :limit => 1, :default => 0
    t.column :needs_parsing, :string, :limit => 1, :default => 0
    t.column :created_at, :datetime
    t.column :updated_at, :datetime
    t.column :user_id,:integer
  end
 end

 def self.down
  drop_table :user_attachments
 end
end

The model will have some useful method for file management:


class UserAttachment < ActiveRecord::Base
  belongs_to :user
  before_destroy :delete_file
  
  def write_path
    user_dir = "%05d" % self.user_id
    write_path = "#{RAILS_ROOT}/public/attachments/#{user_dir}"
    Dir.mkdir(write_path) if !FileTest.exist?(write_path)
    write_path
  end
  
  def full_path
    "#{self.write_path}/#{self.crypted_filename}.#{self.file_ext}"
  end
  
  def full_url
    user_dir = "%05d" % self.user_id
    #write_path = "/attachments/#{user_dir}/#{self.crypted_filename}.#{self.file_ext}"
    "/attachments/#{user_dir}/#{self.crypted_filename}.#{self.file_ext}"
  end
  
  def delete_file
    if FileTest.exist?(self.full_path)
      File.delete(self.full_path)
    end
  end
  
end

Now let’s face another little problem: when I realized I did not have any active session available to authenticate my user while using the upload interface (and I was too lazy to google any solution) I first thought I could pass the user_id to the flex interface and open the biggest security hole ever seen on earth but then I realized that using a ‘token’ could help:


class CreateRandomizer < ActiveRecord::Migration
  def self.up
    create_table :randomizers do |t|
      t.column :random_user,      :integer
      t.column :random_token,     :string, :limit => 40
      t.column :created_at,       :datetime
    end
  end

  def self.down
  drop_table :randomizers
  end
end

I’m sure there are many better solutions.. but I like this one! (and I’m lazy) =)


in media_controller.rb
def upload
  @randomized = current_user.get_random
end

in user.rb
def get_random
  random_token = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
  Randomizer.create(:random_user => self.id, :random_token => random_token)
  return random_token
end

In the view upload.rhtml the SHA is passed to the FLEX, grabbed and passed along with the file:


<param name="FlashVars" value="member=<%= @randomized %>"

And now the magic of FLEX posting the file.. A good idea would be to cron/trigger delete all tokens older than one our in the table if you are paranoid..


in media_controller.rb
def receive
  render(:xml => "true") if saveFileAttachment(params[:Filedata], params[:member])
end

#######
private
#######

def saveFileAttachment(pFile, random_token)
  randomizer = Randomizer.find_by_random_token(random_token)
  return false unless randomizer
  current_user = User.find_by_id(randomizer.random_user)
  return false unless current_user
  randomizer.destroy
 
  attachment = UserAttachment.new(to_attachment(pFile))
  attachment.user_id = current_user.id
  attachment.save if File.open(attachment.full_path, ”wb”) { |vBuffer| vBuffer.write(pFile.read)}
  true
end

def to_attachment(pFile)
  content_type = MIME::Types.type_for(pFile.original_filename)
  { :content_type => content_type.to_s, 
    :original_filename => pFile.original_filename,
    :file_ext => pFile.original_filename.split(”.”).last || ”ext”,
    :crypted_filename => Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join ),
    :size => pFile.size }
end

I use MIME::Types.type_for to determine the content type because, as you noticed from the UserAttachment definition I plan to take different actions according to the file type such ad ffmpeg encoding for video files.. MIME can be installed as ’sudo gem install mime-types’ and included in your enviroment.rb with require ‘mime/types’.

It simply works so smooth I could not believe it! 5, 10, 20 megabytes on DreamHost and up to 97MB! Just FUNTASTIC..

Grab the source for file_uploader.mxml and go uploading!

I would not mind (team) getting a little deeper in this project, add a bunch of configurable options and plug-in-ize it..


I LOVE DREAMHOST!