Do you know the autocomplete for usernames on twitter?

If you enter and @-sign into the textbox, a autocomplete will be shown.

I havn’t found an existing plugin for that, so I have written one. autocompleteTrigger is based on jQuery-UI Autocomplete and set/overwrite some functions only.

Url: https://github.com/experteer/autocompleteTrigger

Demo: http://jsfiddle.net/dmattes/2YRgW/1/

Code Example

$('input,textarea').autocompleteTrigger({
      triggerStart : '@',
      triggerEnd: '',
      source: [
        "Asp",
        "BASIC",
        "COBOL",
        "ColdFusion",
        "Erlang",
        "Fortran",
        "Groovy",
        "Java",
        "JavaScript",
        "Lisp",
        "Perl",
        "PHP",
        "Python",
        "Ruby",
        "Scala",
        "Scheme"
      ]
    });

Dan Chak explained in his book Enterprise Rails, why domain data should be stored in database tables. Domain data don’t change frequently and normally not through an interaction with the application.

For example, domain data can be the roles of users.

Role.create(:key => 'user', :description => 'A normal user')
Role.create(:key => 'admin', :description => 'The admin of the page')

The role constants can be set in the following way (based on the example in Enterprise Rails, Chapter 7)

class Role < ActiveRecord::Base
  USER = Role.find_by_key('user')
  ADMIN = Role.find_by_key('admin')
end

Now you can use this constants like

my_user.role = Role::ADMIN

My solution is, to set the constants dynamicly

class Role < ActiveRecord::Base
  # set role contants like Role::ADMIN,..
  Role.all.each do |role|
    Role.const_set(role.key.upcase, role)
  end
end

The big advantage by setting a constant through the database is that the select query will be executed only once for a running application. If you restart the application, the constants will be refreshed.

For working with my MySQL database on Ubuntu, I’m using HeidiSQL under Wine.

Each day, HeidiSQL publishs a Nightly Build, which I wanted to have.
Therefore I wrote a little shell-script, which makes that work for me.

#!/bin/bash
path=/home/mattesd/.wine/drive_c/prog/HeidiSQL
abs_filename=$path/heidisql.`date +%s`.exe

wget http://www.heidisql.com/latest_build.php -O $abs_filename
rm $path/heidisql.exe
ln -s $abs_filename $path/heidisql.exe

Each build will be downloaded as heidisql.<TIMESTAMP>.exe (e. g. heidisql.1263728741.exe) and linked to heidisql.exe.

Note:
I created the softlink prog before.
ln -s home/mattesd/.wine/drive_c/Program\ Files home/mattesd/.wine/drive_c/prog

RailRoad is a very good class diagram generator for Ruby on Rails applications.

Roy Wright modified it to support the state machine AASM.

Following you see an example of a model diagram, generated by
railroad -Mt | neato -Tpng > models_default.png

This is very great, but in bigger projects, I wanted to see the magic fields like id, user_id, post_id,…

Therefore, I modified the gem by adding an additional parameter  --show-magic to show the magic fields.

railroad -Mt --show-magic | neato -Tpng > models_with_magics.png creates the following diagram

The plugin can be installed via gemcutter.org
gem install dmattes-railroad_xing

Source is available on Github: http://github.com/dmattes/railroad_xing

 

I had to update some attributes of an object. This can easily be done by my_obj.update_attributes(params). If you want to know the updated attributes …it’s not so easy.

update_attributes internally calls the save-method, so that my_obj.changes can’t be called after update_attributes. My solution was to patch ActiveRecord, so that it additionally returns the changed attributes.

Usage example

status, changes = my_obj.update_attributes_changed(params)

config/initializers/active_record_patch.rb

module ActiveRecord
  class Base
    def update_attributes_changed(attributes)
      self.attributes = attributes
      changes = self.changes
      return save, changes
    end
  end
end

In my last blog about paperclip I described the rotation of images.
Since I have added more styles with different sizes for the attachment, I got some performance problems, because for every image, 7 sizes has to be reprocessed and this took a long of time.

My solution is, to handle the reprocessing in background with delayed_job and aasm (acts_as_state_machine).

Media-Model
This is the “normal” Media-Model. First at all, there is AASM included, to see the current state (pending, error, ready) of processing. Furthermore, to show the image, I create a url method, which give back the path to the image (if the state is :ready) or an placeholder (if the state is NOT :ready).

media.rb

class Media

  has_attached_file( :source,
	:styles => {
	:bigger => '1600x1600>',
	:big => '800x600>',
	:album => '560x420>',
	:album_preview => '150x150>',
	:album_folder => '70x70#',
	:profile => '180x250>',
	:avatar => '50x50#' },
	:storage => :filesystem,
  )

  include AASM

  aasm_column :status
  aasm_initial_state :pending

  aasm_state :pending
  aasm_state :error
  aasm_state :ready

  aasm_event :converted do
	transitions :to => :ready, :from => [:pending]
  end

  def url(style = :original)
    if(self.ready?)
      source.url(style)
    else
      # Image is processing, please wait
      Media.find(3).source.url(style)
    end
  end
end

 

The “Uploading” Media-Model
For uploading new images, the model MediaUpload will be used.
This model inherits from the Media-Model, but the paperclip configuration will be overriden.
There is only one style available, because it’s the feedback image for the uploading user.
After saving the image, a new MediaJob will be created in background with the help of delayed_job.

media_upload.rb

class MediaUpload < Media

  # paperclip
  has_attached_file( :source,
    :styles => { :avatar => '50x50#' },
      :storage => :filesystem
  )

  after_create :create_all_styles

  def create_all_styles
    Delayed::Job.enqueue MediaJob.new(self.id)
  end
end

 

The REPROCESSOR
At least, the media-job created all styles for the image …and it’s DONE.

media_jobs.rb

class MediaJob < Struct.new(:id)
  def perform
    m = Media.find(id)
    m.source.reprocess!
  end
end

Helpful links:

I’m using Boxy to display a modal window to edit privacy settings.
The data of the dialog will be loaded by an ajax-call.

privacy_edit

For closing the dialog, the following code is described in the boxy api, but it doesn’t run for me.
It runs for a “normal” boxy window, but not for an ajax-window.

Close dialog.

My solution is, to “link” the modal-window to an DOM element. You can do this, by using the actuator option.
In this example, the DOM element is a link with the id “address_name” and it opens the modal-window.


Nur für mich

For closing the modal-window, you can get the boxy object with Boxy.linkedTo() and can close it with hide() method.

function boxy_hide(obj_name){
  Boxy.linkedTo($('#' + obj_name)[0]).hide();
}

Helpful links:

In my current app, I’m using paperclip for handling images.

Paperclip includes the option for id_partition, but I prefer an hashed stucture (descriped in my first blog).

For example, the image url looks like

http://my_url/medias/0d/898/0d898414092854eacbf27f8db12ce4db_avatar.jpg

This is the basic configuration in my Media-Model

  # paperclip
  has_attached_file( :source,
    :styles => {
      :original => '1600x1600>',
      :avatar => '50x50#' },
    :storage => :filesystem,
    :url => "/:class/:hashed_public_id/:public_id_:style.:extension",
    :path => ":rails_root/public/:class/:hashed_public_id/:public_id_:style.:extension"
  )

To set the public id, I used the following code and create an md5-hash.
Surely, you can use what you want, for example ActiveSupport::SecureRandom.hex(32).
My set_public_id creates a random string like “6e374ca829eaead5090f6cdd26c08017″.

  def set_public_id
    self.public_id = Digest::MD5.hexdigest(self.source_file_name + self.source_file_size.to_s + rand.to_s + Time.now.to_i.to_s)
  end

For using hashed_public_id or public_id in the paperclip configuration :url => “/:class/:hashed_public_id/:public_id_:style.:extension”, you must set the following attachment interpolations.

  Paperclip::Attachment.interpolations[:public_id] = lambda do |attachment, style|
    # e. g. "6e374ca829eaead5090f6cdd26c08017"
    attachment.instance.public_id
  end

  Paperclip::Attachment.interpolations[:hashed_public_id] = lambda do |attachment, style|
    # e. g. "6e/374"
    File.join(attachment.instance.public_id[0..1], attachment.instance.public_id[2..4])
  end

Furthermore, I need to rotate an image. Therefore, I used the follwing code.
All styles of the image (e. g. original, avatar) will be rotated.

require 'RMagick'
def rotate(degrees)

    if(degrees.class == Fixnum && degrees % 90 == 0)
      each_attachment do |n, a|
        self.source.styles.each do |styles|
          image_path = a.path(styles.first)

          image   = Magick::ImageList.new(image_path)
          image   = image.rotate(degrees)
          image.write(image_path)
        end
        return true
      end
    end
    nil

  end

Helpful links:

I using RSpec for testing my Ruby on Rails app.
For testing, if the correct attr_accessible are set, I use the following code.

My Class Media

class Media < ActiveRecord::Base
  attr_accessible :caption
end

Usage in my media_spec.rb

it "should have protected attributes" do
  @media = Media.create!(@valid_attributes)
  @media.should accessible_attributes(:caption)
end

Here the used code in lib/rspec_attributes_accessible.rb

module RspecAttributesAccessible
  class AccessibleAttributes

    def initialize(*attributes)
      @attributes = attributes.to_a
    end

    def matches?(target)
      @target = target
      calculate_accessible_attributes()
      perform_check()
    end

    def failure_message
      "expected #{@failed_attribute} to be accessible"
    end

    def negative_failure_message
      "expected #{@failed_attribute} to not be accessible"
    end

    private

    def calculate_accessible_attributes()
      attr_access = @target.class.send("attr_accessible").to_a.map(&:to_sym)
      @failed_attribute = []
      if((@attributes - attr_access).length != 0 or (attr_access - @attributes).length != 0)
        @failed_attribute << (@attributes - attr_access)
        @failed_attribute << (attr_access - @attributes)
      end
      nil
    end   

    def perform_check()
      @failed_attribute.empty?
    end
  end

  def accessible_attributes(*attributes)
    AccessibleAttributes.new(*attributes)
  end
end

You have to include the lib in your spec_helper.rb

Spec::Runner.configure do |config|
   config.include RspecAttributesAccessible # lib/rspec_attributes_accessible.rb
end

Useful links:

Webseite mit sehr vielen Bildern, Audio-Dateien und Videos sollten nicht alle hochgeladenen Medien in ein Verzeichnis legen …sonst mag *nix bald nicht mehr ;-)

Hier meine verwendete Lösung, ausgelagert in lib/hashed_dir_structure.rb

module HashedDirStructure

  #p_filename md5 encrypted or number
  def HashedDirStructure.get_path_for_save(p_base_path, p_filename)

    if( p_filename.to_s.length != 32)
      p_filename = ("%05d" % p_filename)
    end

    part16 = p_filename[0..1]
    part256 = p_filename[2..4]

    path = File.join(p_base_path, part16, part256)

    # ensure the directory exists...
    FileUtils.mkdir_p(path)

    return File.join(p_base_path, part16, part256)
  end

  def HashedDirStructure.get_path_for_url(p_filename)

    if( p_filename.to_s.length != 32)
      p_filename = ("%05d" % p_filename)
    end

    part16 = p_filename[0..1]
    part256 = p_filename[2..4]

    return File.join(part16, part256)
  end

end

Verwendung:

Jedes Bild/Audio/Video ist als Medien-Objekt in der DB vorhanden. Diesen Objekten ist jeweils eine Public-ID zugewiesen, was ein MD5-Hash ist.

class Media < ActiveRecord::Base
  require 'digest/md5'

  # setzen der public_id
  # z. B. 6e374ca829eaead5090f6cdd26c08017
  def set_public_id
    self.public_id = Digest::MD5.hexdigest(rand.to_s)
  end

  def save
    # hashed_path hat z. B. folgenden Inhalt
    # "/www/medias/6e/374"
    hashed_path =" HashedDirStructure.get_path_for_save("/www/medias", self.public_id)

    # absoluter Dateiname zum speichern
    # "/www/medias/6e/374/6e374ca829eaead5090f6cdd26c08017.jpg"
    filepath = "#{hashed_path}/#{self.public_id}.jpg"

    #...jetzt kann das speichern beginnen
  end
end

Die Verzeichnisstruktur sieht dann wie folgt aus.

/www/medias
          /6e
          /...
          /6e/374
          /6e/7c0
          /6e/...
          /6e/374/6e374ca829eaead5090f6cdd26c08017.jpg

Links:

Switch to our mobile site