Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

About this user

Matt Scilipoti

« Newer Snippets
Older Snippets »
Showing 11-19 of 19 total

pretty tables for rails models

From http://www.rubyinside.com/columnized-text-datasets-in-rails-71.html:
Inspired by: http://blog.caboo.se/articles/2006/06/10/pretty-tables-for-ruby-objects

It gives a MySQL-command-line-client style textual view of data stored in your Rails database. The syntax worked like this:
Something.find(:all, :conditions => ‘whatever‘).pretty_print


class Array

  protected

    def columnized_row(fields, sized)
      r = []
      fields.each_with_index do |f, i|
        r << sprintf(�%0-#{sized[i]}s“, f.to_s.gsub(/\n|\r/, ‘’).slice(0, sized[i]))
      end
      r.join(’ | ‘)
    end

  public

  def columnized(options = {})
    sized = {}
    self.each do |row|
      row.attributes.values.each_with_index do |value, i|
        sized[i] = [sized[i].to_i, row.attributes.keys[i].length, value.to_s.length].max
        sized[i] = [options[:max_width], sized[i].to_i].min if options[:max_width]
      end
    end

    table = []
    table << header = columnized_row(self.first.attributes.keys, sized)
    table << header.gsub(/./, ‘-‘)
    self.each { |row| table << columnized_row(row.attributes.values, sized) }
    table.join(�\n“)
  end
end

class ActiveRecord::Base
  def columnized(options = {})
    [*self].columnized(options)
  end
end



To use:
>> puts Post.find(:all).columnized(:max_width => 10)
updated_at | title      | private | url | thumb      | metadata | movie      | id  | views | content    | user_id | created_at
——————————————————————————————————————————
Wed May 31 | tetwer     | 0       |     |            |          |            | 909 | 0     | video:xyzz | 1       | Wed May 31
Wed May 31 | bbbb       | 0       |     |            |          |            | 1   | 15    | // descrip | 1       | Tue May 23
Wed May 31 | cxzcxzx    | 0       |     |            |          |            | 906 | 19    | // descrip | 1       | Tue May 23
Wed May 31 | jklklkl;   | 0       |     |            |          |            | 907 | 35    | // descrip | 1       | Tue May 23


If you want to use it with your project, put the code into lib/columnized.rb, use require ‘columnized’, and you’re ready to roll. Unlike courtenay’s version, mine only supports max_width, but I didn’t consider changing the column separator too important.

Rails: Inner joins by association names

From http://habtm.com/articles/2006/05/10/inner-joins-by-association-names:

We can enjoy eager loading with association names.
Member.find(:all, :include=>[:group, :profile])

But :joins option forces us to write raw SQL like this.
Member.count(:joins=>"INNER JOIN groups ON groups.id = members.group_id ...")

Why should we do it by hand although those AR classes know their reflections? It’s too boring and painful, and far from DRY.
expand join query

So, why don’t you put this code to ‘vendor/plugins/expandjoinquery/init.rb’?
class ActiveRecord::Base
  class << self
    private
    def add_joins!(sql, options, scope = :auto)
      scope = scope(:find) if :auto == scope
      join = (scope && scope[:joins]) || options[:joins]
      sql << " #{expand_join_query(join)} " if join
    end

    def expand_join_query(*joins)
      joins.flatten.map{|join|
        case join
        when Symbol
          ref = reflections[join] or
            raise ActiveRecord::ActiveRecordError, "Could not find the source association :#{join} in model #{self}"
          case ref.macro
          when :belongs_to
            "INNER JOIN %s ON %s.%s = %s.%s" % [ref.table_name, ref.table_name, primary_key, table_name, ref.primary_key_name]
          else
            "INNER JOIN %s ON %s.%s = %s.%s" % [ref.table_name, ref.table_name, ref.primary_key_name, table_name, primary_key]
          end
        else
          join.to_s
        end
      }.join(" ")
    end
  end
end

Now, we can enjoy :joins option as same as :include one!
# simply inner join
Member.find(:first, :joins=>:group)
=> SELECT * FROM members INNER JOIN groups ON groups.id = members.group_id LIMIT 1

# two inner joins
Member.count(:joins=>[:group, :profile])
=> SELECT count(*) AS count_all FROM members
   INNER JOIN groups   ON groups.id = members.group_id
   INNER JOIN profiles ON profiles.member_id = members.id 

# symbol and raw SQL
Member.find(:all, :joins=>[:group, "INNER JOIN addresses USING(address_id)"])
=> SELECT * FROM members
   INNER JOIN groups ON groups.id = members.group_id 
   INNER JOIN addresses USING(address_id) 

restrictions

This is just an idea and not deeply considered yet. So, there are following restrictions.

* cascading is not supported
* not work with :include option (i think)

I hope someone will polish this up :)

On booleans and database portability

From Coda Hale: http://blog.codahale.com/2006/04/20/on-booleans-and-database-portability/.

On booleans and database portability

Oh, booleans. Simplest and most elusive of database types. How to specify you? Y and N? T and F? 0 or NULL and anything else? If only we knew…

When working on a Rails app that uses different databases (say, SQLite for development/testing and MySQL for production), be sure that your conditions clauses aren’t assuming a particular form of boolean representation.

This will return nothing in SQLite:
@monkeys = Monkey.find(:all,
  :conditions => 'rabid = 0')

But it’ll work in MySQL.
A Solution

Autogenerate that sucker!
@monkeys = Monkey.find(:all,
  :conditions => ['rabid = ?', false])

Yay!

User Friendly Time Entry

http://www.railtie.net/articles/2006/04/22/user-friendly-time-entry
Posted by Bob Silva Sat, 22 Apr 2006 08:49:00 GMT

Have a need to track time spent on something? Here's an easy way to allow your users to enter their time in a smart way (anyway they want). This example accepts fractional hours, overflowing minutes and converts and displays them as the user would expect. They are stored in your database as minutes (integer) and displayed as hours/minutes (regardless of how they were input).

Model Code (model.rb):

def set_travel_time(hours, minutes)
  self.travel_time = ((hours.to_f * 60) + minutes.to_i).to_i
end

def get_travel_time
  travel_time.to_i.divmod(60)
end


Controller Code (models_controller.rb):

def create
  @model.new(...)
  ...
  @model.set_travel_time(params[:hours], params[:minutes])
  ...
  if @model.save
  ...
end

def edit
  @model = Model.find(...)
  ...
  @hours, @minutes = @model.get_travel_time
  ...
end


View Code (_form.rhtml):

<%= text_field_tag 'hours', @hours -%> hours 
<%= text_field_tag 'minutes', @minutes -%> minutes 



For plugin ideas, see dollars_and_cents: http://blog.codahale.com/2006/05/18/dollars_and_cents-a-rails-plugin/

One of the big problems with a database-agnostic framework like ActiveRecord is that it doesn’t have a decent data type for money. Yes, you can use a FLOAT, but then you end up charging someone $12.3000000000000001, which is just awkward. This plugin stores as INT, but displays as Currency.

Mongrel on Win32 as Service

Mongrel now has support for running as a Win32 service right out of the box. The support is still rough but works well enough that we decided to release it. You can thank Luis Lavena for working on this and making it so nice.

After you do the gem install, find a Rails application you want to run and do:
$ mongrel_rails_service install -n myapp -r c:\my\path\to\myapp -p 4000 -e production
$ mongrel_rails_service start -n myapp

Now hit the port and poof, works. Stopping the app is just done with:
$ mongrel_rails_service stop -n myapp

And, you can even set the CPU processor affinity for the service when yourun the install command. Can’t even do that on POSIX yet. Now that’s hot.

If you run into an app that’s not running right, my suggestion is to run it with the regular mongrel_rails runner:
$ cd c:\my\path\to\myapp
$ mongrel_rails start -p 4500

Since that will spit out error messages and stuff to the console. Use CTRL-Pause/Break to stop.

Prototype and window.onload

from: http://happygiraffe.net/blog/archives/2006/03/05/prototype-and-window-onload


We’ve known for some time that simply assigning to window.onload is bad. Simon Willison created addLoadEvent a long time back to work around the problem. But I’m in Rails, and we have prototype. So what’s the correct idiom?

Well, after a bit of playing, it seems to be this:
  <% content_for("page_scripts") do %>
    Event.observe(window, 'load',
      function() { $('username').focus() }
    );
  <% end %>

I do love that $() function.

This assumes that you have a layout that looks something like this, in order to insert bits of JavaScript into the head.
  <script type="text/javascript">
    <%= @content_for_page_scripts %>
  </script>

Anyway, the solution seems a little more verbose than addLoadEvent(), but not disastrously so.

tip: recover from failed rails 'down' migration

From: http://jamis.jamisbuck.org/articles/2005/12/14/two-tips-for-working-with-databases-in-rails

The second tip is handy when you’re working on a migration. I find that the process (for me) works like this:

* Create the migration and run it.
* Discover I forgot something.
* Migrate down to the previous schema version.
* Change the migration and run it again.

(Repeat as necessary.) However, being the imperfect programmer that I am, I find that I often implement the #down method incorrectly, forgetting to drop a table or remove a column. Thus, when I try to run the migration again, it fails saying that the table/column already exists.

Using script/console and ActiveRecord::Schema, it becomes a cinch to clean up the artifacts:
ActiveRecord::Schema.define do
    drop_table :foos
    remove_column :bars, :blitz
    remove_column :bars, :things
  end

tip: Database, For 'Event occurred' use bool from date.

From: http://jamis.jamisbuck.org/articles/2005/12/14/two-tips-for-working-with-databases-in-rails
First tip: I’ve found recently that if I have a boolean field in the database that is being used to mark whether some event occurred (referrals.pending, or feeds.subscribed) it is often more effective to make the field a datetime and record the moment that the event occurred. Then, a NULL can be used to indicate that the event has not yet occurred. Thus, you have referrals.applied_at with a method on Referral like this:
  def pending?
    applied_at.nil?
  end


This gives you the capability down the road to not only report whether the event occurred, but how frequently over various periods of time.

Auto-login

Auto-login: http://www.onrails.org/articles/2006/02/18/auto-login

Posted by Daniel Wanja Sat, 18 Feb 2006 21:41:00 GMT

One of my midnight Rails projects is a “time tracking� application for which I needed auto-login. You know, the “Remember me� check box so that you don’t have to login each time you visit the application. I found a nice article written by Matt McCray describing how this was implemented for TaskThis.com at http://www.mattmccray.com/archives/category/software/rails/taskthis/. Even further he provides the full source code for the application. I didn’t take directly his auto_login.rb module but was greatly inspired by it. I also used the Login Engine Plugin that was not providing this feature, maybe this changed, so it could be simpler, but how simple implementing the auto-login can be. Note these are not the full classes just pertinent code extracts.

1. Remember me

When the user login and checks the “Remember me� checkbox, the :save_login parameter is set, the User instance remember_me method invoked and the :auth_token cookie set.
class AccountController < ApplicationController
  def login
    case @request.method
      when :post
      if @session[:user] = User.authenticate(@params[:user_login], @params[:user_password])
        flash['notice']  = "Login successful"
        if @params[:save_login] == "1"
          @session[:user].remember_me
          cookies[:auth_token] = { :value => @session[:user].remember_token , :expires => @session[:user].remember_token_expires }
        end
        redirect_back_or_default :controller => "time"
      else
        flash.now['notice']  = "Login unsuccessful"
        @login = @params[:user_login]
      end
    end
  end

  def logout
    @session[:user].forget_me if @session[:user]
    @session[:user] = nil
    cookies.delete :auth_token
  end
end

2. login_from_cookie

The next time the user visits the website the “login_from_cookie� filter is triggered. This method checks that the user is not logged in and that the :auth_token cookie is set. If that’s the case the user matching the :auth_token is searched and the token_expiration verified the the user is automatically logged in. Et voila! I guess auto_login would be more appropriate as method name.
class ApplicationController < ActionController::Base
   before_filter :login_from_cookie
   def login_from_cookie
      return unless cookies[:auth_token] && @session[:user].nil?
      user = User.find_by_remember_token(cookies[:auth_token]) 
      if user && !user.remember_token_expires.nil? && Time.now < user.remember_token_expires 
         @session[:user] = user
      end
   end
end

3. the User class

The User class has two methods to set and remove the token from the database. It’s pretty secure as from the token the user cannot be identified without having the salt, the email, and the token expiration, which is most unlikely to be recreated. It could be even more secure by just encrypting some random unique identifier. The only issue I encountered was that the user class always forces the password validation and encryption when saving. For now I just bypass validation and encryption when setting and clearing the remember_me token.
class User < ActiveRecord::Base
  def remember_me
    self.remember_token_expires = 2.weeks.from_now
    self.remember_token = Digest::SHA1.hexdigest("#{salt}--#{self.email}--#{self.remember_token_expires}")
    self.password = ""  # This bypasses password encryption, thus leaving password intact
    self.save_with_validation(false)
  end

  def forget_me
    self.remember_token_expires = nil
    self.remember_token = nil
    self.password = ""  # This bypasses password encryption, thus leaving password intact
    self.save_with_validation(false)
  end
end

« Newer Snippets
Older Snippets »
Showing 11-19 of 19 total