Compassionate Communications. A different kind of Rails application. 3

Posted by Daniel Wanja Thu, 19 Jun 2008 04:29:00 GMT

I have been working with Sean and Lee on Compassionate Communications a Ruby on Rails website. My role was small thanks to the ActiveMerchant plugin, I helped with the online payment but my part was done in no time. The site launched just before Rails Conference and I wanted to write about what the site is. It's about giving, reaching out, helping...but I didn't find the right words to describe it. The team at Compassionate Communications made the following video that captures the essence of what they want to achieve way better I could describe..so go check it out.

Advanced Rails Studio: Custom Form Builder 8

Posted by Daniel Wanja Fri, 13 Jun 2008 21:42:00 GMT

Custom Form Builder

Use a custom form builder to clean up your html.erb files.

lib/label_form_builder.rb
class LabelFormBuilder < ActionView::Helpers::FormBuilder
  helpers = field_helpers +
            %w{date_select datetime_select time_select} +
            %w{collection_select select country_select time_zone_select} -
            %w{hidden_field label fields_for} # Don't decorate these

  helpers.each do |name|
    define_method(name) do |field, *args|
      options = args.last.is_a?(Hash) ? args.pop : {}
      label = label(field, options[:label], :class => options[:label_clas])
      @template.content_tag(:p, label +'<br/>' + super)  #wrap with a paragraph 
    end
  end
end

Then you can remove all the <p> and label tags from you form.

app/views/users/edit.html.erb
<h1>Editing user</h1>

<% form_for(@user, :builder => LabelFormBuilder) do |f| %>
  <%= f.error_messages %>
    <%= f.text_field :name %>
    <%= f.text_field :address %>
    <%= f.text_area :comment %>
    <%= f.check_box :check %>
    <%= f.submit "Update" %>
<% end %>

<%= link_to 'Show', @user %> |
<%= link_to 'Back', users_path %>

Add this to your application initializer to have all form use this form builder

ActionView::Base.default_form_builder = LabelFormBuilder
Then you can replace
<% form_for(@user, :builder => LabelFormBuilder) do |f| %>
with
<% form_for(@user) do |f| %>

Now the same form with no custom builder was looking like this before.

<h1>Editing user</h1>

<% form_for(@user) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :address %><br />
    <%= f.text_field :address %>
  </p>
  <p>
    <%= f.label :comment %><br />
    <%= f.text_area :comment %>
  </p>
  <p>
    <%= f.label :check %><br />
    <%= f.check_box :check %>
  </p>
  <p>
    <%= f.submit "Update" %>
  </p>
<% end %>

<%= link_to 'Show', @user %> |
<%= link_to 'Back', users_path %>

Advanced Rails Studio: Meta Programming 3

Posted by Daniel Wanja Fri, 13 Jun 2008 16:57:00 GMT

Chad is giving a very nice presentation walking us through meta programming step by step. You can see the code examples we are creating during his talk, but just looking at the code will note give the whole picture.

# ruby it self uses meta programming
class Person
   attr_accessor :name
end

chad = Person.new
chad.name = 'chad'

# classes are open

# create new class
class Blah
  def greeting
    puts "hello"
  end
end

# reopen class and return id
class Blah
  def do_something!
    greeting
  end
end

b = Blah.new
b.greeting
b.do_something!

# reopen existing class
class String
  def encrypt
    tr "a-z", "b-za"
  end
end

puts "cat".encrypt

# Conceptually ruby (the virtual machine) creates a structure to represent the class
# And this structure can dynamically be defined and changed at runtime.

{
  :String => {:name => "String",
              :methods => {
                :ecryypt => '<method body>',
                :tr  => '<method body>',
                :update => '<method body>',
              },
              :instance_variables => {
                "@name" => "Chad"
              }
  }
}

# replace existing method
class String
  def encrypt
    upcase.reverse
  end
end

puts "cat".encrypt

# Rails extends base classes in activesupport. I.e Fixnum 20.minutes.ago
class Fixnum
  def minutes
    self*60
  end
end

puts 20.minutes

class Fixnum
  def from_now
    Time.now + self
  end
  def ago
    Time.now - self
  end
end

puts 20.minutes.from_now
puts 20.minutes.ago

# Class definition are executed line by line
class Chad
  #exit  #uncomment this and the program will halt here!
  puts "Hello, defining #{self}"
end
puts Chad.new.inspect

# Can conditionaly create class
class Chad
  #exit  #uncomment this and the program will halt here!
  puts "Hello, defining #{self}"
  puts "Type OK when prompted"
  response = gets.chomp
  if response == "OK"
    def greeting    
      puts "OK"
    end
  else 
    def greeting    
      puts "O NO!!!"
    end
  end  
end

puts Chad.new.greeting
# Could use this to have different code for RAILS_ENV is "Prodution" or "Development"
# Sending messages to Object. Object receive message, all method calls have received

"Chad".upcase         #,I.e. String "Chad" gets message upcase
puts "Hello"          # event 'puts' is a message
puts self.class.name  # Even when running script, running in context of an Object

class Person
  def initialize(name)
    @name = name
    greeting
    puts "self.inspect: #{self.inspect}"
  end

  def greeting
    puts "0, hello #{@name}."
  end

end
puts Person.new('daniel').greeting

# Calling class methods
class Person
  puts "Puts is send to self. Self is person:#{self} when defining class."
end

# Can point to a class
person_class = Person
puts person_class.class

class Man
end

class AstroMan
end

# Factory method, classes are just object that can be passed around
def man_or_astroman
  klass = (rand(2) > 0 ? Man : AstroMan)
  klass.new
end

puts man_or_astroman
puts man_or_astroman
puts man_or_astroman
puts man_or_astroman

# Constance in Ruby are not Constants
if false  #Don't run this
  String = "HAHAHA"   #You can even change the class constants implementation
  # You'll get a warning (Warning: already initialized constant String), but you can change it
  Integer = "bla"
  Array = 123
end

# Methods can be defined on Objects and not just Classes
animal = "Cat"
def animal.speak
  "woof"
end 
puts "animal.speak: #{animal.speak}"
# "dog".speak doesn't exists, only the specific animal instance has speak
# It's not often done in Ruby, but in another context you'll do it all the time...

# .. Adding class methods (singleton methods (not related to pattern)).
class Human
    def self.announce_self
        puts "I AM #{self}, and I AM BEING DEFINED"
    end
    announce_self # Can invoked defined class method while defining class
end

# Same as doing def Human.annouce_sef end
Human.announce_self

# We are getting closer to has_many
class Superman < Human
  announce_self
end

# Let's try doing something similar to ActiveRecord
module ActiveRecord
  class Base
    def self.has_many(*things)
      puts "#{self} has_many #{things}"
    end
  end
end

class BuzzLightYear < ActiveRecord::Base
  has_many :space_ships # does nothing for now but it's valid syntax
end
# This is more the way Rails works
# Can do included hook and extend
module ActiveRecord
    module Associations
        module HashManyAssocation
          def self.included(klass)
            klass.extend(ClassMethods)
          end
          module ClassMethods
            def has_many(things, options = {})
              # TODO: define methods
              puts "#{self} has many #{things}"
            end
          end
        end
    end
end

module ActiveRecord
  class Base
    # INCLUDE
    include ActiveRecord::Associations::HashManyAssocation  
  end
end

class AstorMan < ActiveRecord::Base
  has_many :space_ships
end
# But could simply extend
module ActiveRecord
    module Associations
        module HashManyAssocation
            def has_many(things, options = {})
              # TODO: define methods
              puts "#{self} has many #{things}"
            end
        end
    end
end

module ActiveRecord
  class Base
    # EXTENDS
    extend ActiveRecord::Associations::HashManyAssocation
  end
end

class AstroMan < ActiveRecord::Base
  has_many :space_ships
end

# acts_as_ ... to add functionality without extend ActiveRecord::Base
# We could use include with the included hook

module SuperHero
  def self.included(klass)
    klass.extend(ClassMethods)
  end
  module ClassMethods
    def acts_as_superhero
      puts "I'm a bird, I'm a plane, no I'm #{self}"
    end
  end
  def fight_crime
    puts "OK, fighting crime"
  end
end

# use include
class AstroMan < ActiveRecord::Base
  include SuperHero
end
AstroMan.new.fight_crime

# And include in your base class
class ActiveRecord::Base
  include SuperHero
end

# then acts_as is available
class SuperMan < ActiveRecord::Base
  acts_as_superhero
end
SuperMan.new.fight_crime
# Calling non-existant methods
class Chad  
  def method_missing(method_name, *args)
    puts "You called #{method_name} with #{args.inspect}"
  end
end
Chad.new.just_do_it('again', 'and again')

# Calling non-existant classes
def Object.const_missing(name)
  puts "Trying to get to non existing clas #{name}"
  # trick: could require the file if class is missing
end
AnythingClass
# Now that we went through the concepts let's do some meta programming

# eval
def evaluator(str, a_binding)
  a_value = 123
  eval(str, a_binding)
end

str = "puts a_value"
a_value = 321
evaluator(str, binding)  # -> 321. binding is the current scope of the program
evaluator(str, nil)      # -> 123. don't pass binding

# instance_eval
class Thing
    def a_value
        123
    end
end

Thing.new.instance_eval("puts self.a_value") # -> 123. run in context of an instance

# Two more 'eval': class_eval and module_val

#class_eval 
class Person
end

Person.class_eval do  # Be in context of class
  p self            # -> Person
  def greeting
    puts "Hello"
  end
end

Person.new.greeting  # defining instance method

def add_greeting_to(klass)
  klass.class_eval do
    def greeting
      puts "Greeting"
    end
  end  
end
add_greeting_to(String)
"asdf".greeting    # -> Greeting

# module_eval is basically same thing as class_eval

# define_method
class Chad
  define_method(:foo) do |arg1|
    puts "hello, #{arg1}"
  end
end

Chad.new.foo(:bar)

RailsConf registration opens today. Be ready! 2

Posted by Daniel Wanja Tue, 29 Jan 2008 14:10:53 GMT

May 29-June 1, 2008 in Portland, Oregon,

UPDATE: registration is now open.

UPDATE2: I’ll be presenting with Tony a 3 hour tutorial on Powering AIR Applications with Rails. See you all there!

Rails 2.0 17

Posted by Daniel Wanja Fri, 07 Dec 2007 16:07:46 GMT

Rails 2.0 is out! Thanks guys for all the hard work, this release is just impressive.

Acts_as_nested_set ActiveRecord rendered with mx:Tree in Flex. 9

Posted by Daniel Wanja Sat, 24 Nov 2007 03:30:13 GMT

ActiveRecord: app/models/category.rb
app/models/category.rb
class Category < ActiveRecord::Base
  acts_as_nested_set
end
Controller: app/controllers/categories_controller.rb
app/controllers/categories_controller.rb
class CategoriesController < ApplicationController
  def index
     Category.result_to_attributes_xml(Category.root.full_set)
  end
end
Flex Application: ActsAsNestedSet.mxml
ActsAsNestedSet.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
    layout="vertical"
    applicationComplete="categories.send()">
<mx:HTTPService id="categories" url="http://localhost:3000/categories" resultFormat="e4x" />
<mx:Tree dataProvider="{categories.lastResult}" 
    labelField="@name"
    width="100%" height="100%" />    
</mx:Application>

Result: 20071123_categories.jpg

XML generated by Category.result_to_attributes_xml(Category.root.full_set):
XML generated by Category.result_to_attributes_xml(Category.root.full_set)
<node name="Main Category" id="15" description="">
  <node name="Cameras &amp; Photo" id="16" description="">
    <node name="Bags" id="17" description=""/>
    <node name="Accessories" id="18" description=""/>
    <node name="Analog Cameras" id="19" description=""/>
    <node name="Digital Cameras" id="20" description=""/>
  </node>
  <node name="Cell Phones" id="21" description="">
    <node name="Accessories" id="22" description=""/>
    <node name="Phones" id="23" description=""/>
    <node name="Prepaid Cards" id="24" description=""/>
  </node>
  <node name="Dvds" id="25" description="">
    <node name="Blueray" id="26" description=""/>
    <node name="HD DVD" id="27" description=""/>
    <node name="DVD" id="28" description=""/>
  </node>
</node>

I used the http://wiki.rubyonrails.org/rails/pages/BetterNestedSet plugin.

Too cool!

UPDATE: The BetterNestedSet plugin doesn’t work out of the box with Rails 2.0 RC1. Thanks Joel for that info. Read more in the comment of this blog entry.

UPDATE2: Thanks Fabien, BetterNestedSet now works with Rails 2.0!

RailsLogVisualizer0.7 for AIR beta 2. 6

Posted by Daniel Wanja Thu, 08 Nov 2007 04:12:00 GMT

I recompiled the RailsLogVisualizer for AIR beta. I added drag&drop of log files to bypass an AIR bug on Leopard. File.browseForOpen doesn't trigger the Event.SELECT when the file is selected. I haven't yet tried this version of the application on older versions of OSX or on Windows. Let me know how it works. Also the feedback when loading large log files could be improved, as the application seems to freeze once the progress bar is complete. Just be a little patient as the AVM is working hard for you to compute all these number.

Install RailsLogVisualizer0.7.air

Install Manually

1) Instal Adobe AIR beta 2. (See release notes if previous version was installed)
Download AIR for OSX Download AIR for Windows
Learn more on AIR

2) Download and install http://myspyder.net/tools/railslogvisualizer/RailsLogVisualizer0.7.air

For time.onrails.org the log file is currently 98Mb and is loaded and process in less than a minute. Here are the loading details:

Loaded 98571986bytes in 28093 milliseconds.
Parsing file. Please Wait this may take some time....
Parsing. Split 1639453entries in 1447 milliseconds.
found:220767 in 1925 milliseconds.
Aggregating data.
	aggregated:220767 in 13426 milliseconds.
	Aggregated:89135
	aggregated String :4440464(bytes) in 2790 milliseconds.
Then you can navigation through time and see how many request where processed and drill down in specific action and specific methods. For example, here we can quickly see that for October 99 people signed up, 869 did login, 22 forgot their password.
20071106_railslogvisualizer.jpg
Enjoy, Daniel.

Sweet way to write Flex Unit tests for Rails

Posted by Daniel Wanja Mon, 05 Nov 2007 15:11:56 GMT

Using ActiveResources from Flex? Using FlexUnit? Here is a nice way to write your tests.

Example Test Case
package tests
{
    import flexunit.framework.*;    
    import mx.rpc.AsyncToken;
    import mx.rpc.events.ResultEvent;
    import resources.Raffles;

    public class TestRaffles extends BaseTestCase
    {    
        private var raffles:Raffles;        
        public function TestRaffles(name : String = null)
        {
            super(name);
            fixtures(["raffles"]);
             raffles = new Raffles();
        }            
        public function testRemoteFindRaffle():void
        {
            assertRemote(raffles.show(1));
        }
        public function assertRemote_testRemoteFindRaffle(data:Object):void
        {
            Assert.assertTrue("Raffle show successfully called", data is ResultEvent);  
            assertEquals("MyString", data.result.name);
        }        

    }
}

Note this code is not yet a plugin and is using code you can find here: http://code.google.com/p/flexonrails/source. I was starting to use it on multiple projects so I thought it was to time find a home for it. Also it is using the org.onrails.rails.ActiveResourceClient Flex class. I would recommend that you use Alex MacCaw’s ActvieResrouce for Actionscript. I still need to talk with Alex and integrate this fixture loading code with his code.

The BaseTestCase Flex class is an extended TestCase that provides support for fixtures. Now in your constructor you can define which fixtures you want to reload between each test. Only tests methods starting with “testRemote” will trigger refreshing the fixtures. As you know, when using AMF or HttpService remote invocations are asynchronous and you cannot test the result of a remote call in the same method than where the call is made from. That’s why I added the assertRemote method which takes an AsyncToken as parameters. This will automatically invoke a method whos name starts with assertRemote_ followed by the test method name. This simplifies greatly writing asynchronous tests. FlexUnit provides the addAsync method, we just add the convenience assertRemote function to setup all the callbacks.

To make this work for you Flex with Rails project. You need to fixtures_controller.rb to your controllers and setup the following routes:

  if RAILS_ENV == "test"
    map.resources :fixtures, :new => { :test_results => :post }
    map.crossdomain '/crossdomain.xml', :controller => 'fixtures', :action => 'crossdomain'
  end

You need to extend your Flex TestCase from tests.BaseTestCase.

Enjoy, Daniel.

Installing RMagick on Leopard (without MacPorts or Fink) 42

Posted by Solomon White Sat, 03 Nov 2007 00:07:00 GMT

I’ve recently upgraded to OS X 10.5 (Leopard), and all-in-all, I’m pleased with the experience. My biggest issue has been the default stacks behavior—the icon changes to the last thing added to the stack, making visual identification unnecessarily cumbersome. I worked around this annoyance (as outlined here) by changing the sort to name rather than date added, and adding a dummy folder named “_1” that will sort to the top. For extra bonus points, I customized the icon of the dummy folder. For some yet unknown reason, the most recently downloaded item still peeks through from time to time, but it’s much better than before.

Maybe it’s my Windows history showing through, but I went with the “clean-sweep” erase and install method. For a non-developer, I’d probably recommend the upgrade (and in fact I used that method for my Father-in-law’s MacBook), but I had lots of custom bits scattered about my machine, and didn’t want to be chasing any incompatibility gremlins.

So now, to get my development environment set up on the new machine… Leopard includes a fairly complete Rails stack out of the box, with a non-broken Ruby, readline support, and most of the commonly used gems. Read more here.

MySQL was not included, but the latest installer (mysql-5.0.45-osx10.4-i686.dmg) for 10.4 from dev.mysql.com downloads worked (mostly) fine. The Server and the StartupItem install and operate correctly. The PrefPane installs, but does not appear to actually do … anything. I’ll have to work on that, but I can live without it for now. After a bit of manual hacking on my database dump file from Tiger (where I was running a 5.1.x beta of MySQL), all my databases are back in place.

One last piece that I needed for my Rails apps—RMagick. I know it’s possible to install RMagick and its dependencies, um, “autoRMagickally” via a package management system like MacPorts or Fink, but I prefer not to. For some background on why not, you can read this article at hivelogic. The last time I was rebuilding my laptop and desktop near the same time, I put together a shell script to automate the process of installing RMagick. I got it back out and dusted off the cobwebs, and voila! RMagick on Leopard. (note: replace wget with “curl -O”, if you don’t have wget installed on your machine) Here’s the code:

install_rmagick.sh
#!/bin/sh
wget http://download.savannah.gnu.org/releases/freetype/freetype-2.3.5.tar.gz
tar xzvf freetype-2.3.5.tar.gz
cd freetype-2.3.5
./configure --prefix=/usr/local
make
sudo make install
cd ..

wget http://superb-west.dl.sourceforge.net/sourceforge/libpng/libpng-1.2.22.tar.bz2
tar jxvf libpng-1.2.22.tar.bz2
cd libpng-1.2.22
./configure --prefix=/usr/local
make
sudo make install
cd ..

wget ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz
tar xzvf jpegsrc.v6b.tar.gz
cd jpeg-6b
ln -s `which glibtool` ./libtool
export MACOSX_DEPLOYMENT_TARGET=10.5
./configure --enable-shared --prefix=/usr/local
make
sudo make install
cd ..

wget ftp://ftp.remotesensing.org/libtiff/tiff-3.8.2.tar.gz
tar xzvf tiff-3.8.2.tar.gz
cd tiff-3.8.2
./configure --prefix=/usr/local
make
sudo make install
cd ..

wget http://jaist.dl.sourceforge.net/sourceforge/wvware/libwmf-0.2.8.4.tar.gz
tar xzvf libwmf-0.2.8.4.tar.gz
cd libwmf-0.2.8.4
make clean
./configure
make
sudo make install
cd ..

wget http://www.littlecms.com/lcms-1.17.tar.gz
tar xzvf lcms-1.17.tar.gz
cd lcms-1.17
make clean
./configure
make
sudo make install
cd ..

wget ftp://mirror.cs.wisc.edu/pub/mirrors/ghost/GPL/gs860/ghostscript-8.60.tar.gz
tar zxvf ghostscript-8.60.tar.gz
cd ghostscript-8.60/
./configure  --prefix=/usr/local
make
sudo make install
cd ..

wget ftp://mirror.cs.wisc.edu/pub/mirrors/ghost/GPL/current/ghostscript-fonts-std-8.11.tar.gz
tar zxvf ghostscript-fonts-std-8.11.tar.gz
sudo mv fonts /usr/local/share/ghostscript

wget http://imagemagick.site2nd.org/imagemagick/ImageMagick-6.3.5-9.tar.gz
tar xzvf ImageMagick-6.3.5-9.tar.gz
cd ImageMagick-6.3.5
export CPPFLAGS=-I/usr/local/include
export LDFLAGS=-L/usr/local/lib
./configure --prefix=/usr/local --disable-static --with-modules --without-perl --without-magick-plus-plus --with-quantum-depth=8 --with-gs-font-dir=/usr/local/share/ghostscript/fonts
make
sudo make install
cd ..

sudo gem install RMagick

Rails Rocks! 3

Posted by Daniel Wanja Tue, 02 Oct 2007 17:37:00 GMT

I am trying so see the result of a change in a Java program….5 minutes compilation…5 minutes deployment. Arggggggggggg!

Rails Rocks! Change + refresh = done!

Short term memory is good, hopefully I will forget that experience soon.

;-)

Older posts: 1 2 3 ... 6