RAILS_ROOT/app Organization

As an app grows, so does the amount of files in app/models. Here is a simple tip to help reduce the clutter: move mailers, observers, and sweepers out of app/models into app/mailers, app/observers, and app/sweepers, respectively. For rails to still load them, you will need to modify config/environment.rb:

# clean up app/models a bit
%w{mailers observers sweepers}.each do |dir|
    config.load_paths << "#{RAILS_ROOT}/app/#{dir}"
end

As a bonus, you can now automatically record your observers with ActiveRecord instead of having to add them individually in environment.rb:

config.active_record.observers = Dir.glob("#{RAILS_ROOT}/app/observers/*.rb").collect do |filename|
    filename.split('/').last.split('.').first.to_sym
end
One issue with this reorganization is that the generators will continue to create mailers, observers, and sweepers in app/models. For me, that is not a big deal – I don’t create them often, and manually move them after generation. I suspect it would be trivial to adjust the generators to use the new path, but have not looked in to it.

server_remote gem

I just re-released my server_remote plugin as a gem server_remote on github. I converted it to a gem to make it easier to use in multiple apps, and to make it easier to update (see here for the blog entry on the (now obselete) plugin).

It is a gem that provides support for running commands on remote server. Once set up, it provides commands via script/remote:

  • remote shell – same as ssh’ing to the server (this is the default command, so it can be called with just remote)
  • remote console – executes a script/console on the server
  • remote logtail – executes tail -f log/<environment>.log on the server
  • remote cmd <some command> executes command on the server, displaying the result. It cd‘s to the remote app root first.
  • remote scp <local_file> :<remote_file> provides scp. Prefix remote files with ‘:’
Configuration is in config/server_remote.yml, and is grouped into profiles.

Here is the output of remote usage:

Executes commands on a remote server over ssh. Configuration is in:
/Users/tobias/customers/DealerIgnition/dealer_ignition/script/../config/server_remote.yml
 
You can override the profile used with -p profile. The default profile is: app
 
Learn more in the readme:
/opt/local/lib/ruby/gems/1.8/gems/tobias-server_remote-0.2.0/lib/server_remote/../../README.textile
 
remote commands are:
 
DEFAULT COMMAND   shell
 
cmd               executes an arbitrary command on the server after a cd to the app path
commands          List all 'remote' commands
console           executes remote console
help              Provide help documentation for a command
logtail           executes remote tail -f on the log
scp               copies files over scp (prefix remote files with ':')
shell             executes remote shell
usage             prints usage message
 
For help on a particular command, use 'remote help COMMAND'.

It uses remi’s simplecli gem.

I plan to add other commands as needed, or you can open up Remote::Commands and add your own.

Installation

You will need to install the gem (only once), then setup any apps where you want to use it.

sudo gem install tobias-server_remote --source http://gems.github.com/
server_remotify path_to_app

credit_card_validator gem

I recently published a gem that provides credit card validation. It is basically a ruby port of the javascript credit card validator by Thomas Fuchs (madrobby).

Usage:

  CreditCardValidator::Validator.valid?('1111 2222 3333 4444')
 
# allow test numbers to be valid (for development) 
CreditCardValidator::Validator.options[:test_numbers_are_valid] = true
CreditCardValidator::Validator.valid?('1111 2222 3333 4444')
 
# limit the card types you allow
CreditCardValidator::Validator.options[:allowed_card_types] = [:visa, :mastercard]
CreditCardValidator::Validator.valid?('1111 2222 3333 4444')

Supported card types:

  :amex, :discover, :diners_club, :master_card, :visa

Whitespace is stripped from the number automatically.

The following things are tested:

1. does the luhn validation code add up? (see http://en.wikipedia.org/wiki/Luhn_algorithm)

2. does the number range and length seem right? (see http://en.wikipedia.org/wiki/Bank_card_number)

3. is it one of several well-known test numbers?

Note: this only validates that the number is of a valid format, it does not check if it is an actual credit card number. You will need to talk to your payment gateway to learn that.

You can also use the validator to learn about the type of the card:

  # gives the type back as a string (visa, master_card, etc)
CreditCardValidator::Validator.card_type(number)
 
CreditCardValidator::Validator.is_visa?(number)
CreditCardValidator::Validator.is_master_card?(number)
# etc. - works for all of the supported card types
 
CreditCardValidator::Validator.is_allowed_card_type?(number)

To Install:

gem install tobias-credit_card_validator --source http://gems.github.com

The source is available on github

script_finder gem

I recently published a ruby gem on github that simplifies running scripts from a script/ dir from anywhere in your project (rails or otherwise).

script_finder provides a script (called s@) in your path that searches in and up from the current dir for a folder (default: @script/) containing an executable file uniquely identified by the a prefix given as the first argument. It then calls that executable, passing the rest of the arguments to the called executable. If the given prefix is ambiguous, the script suggests unique prefixes.

Examples (in a rails app):

~/rails_app/app/views$ s c
--> calling '/Users/tobias/rails_app/script/console'
Loading development environment (Rails 2.1.0)
RowsLogger plugin enables mysql
>> exit
~/rails_app/app/views$ s r 'some ruby'
's r' was too ambiguous. Try:
's ru' for 'script/runner'
's re' for 'script/remote'
~/rails_app/app/views$ s ru 'some ruby'
--> calling '/Users/tobias/rails_app/script/runner some ruby'
...

The gem is not rails specific – out of the box it will work with any project that has a script/ directory. If you want to make your own version of the s@ script that looks for executables in a different dir (I would save this one as @c):

  #!/usr/bin/env ruby
 
require 'script_finder'
 
# looks for executables in a commands/ dir instead of script/.
ScriptFinder.find_and_execute(ARGV, 'commands')

Let me know if you have any problems/questions.

method_missing tip

When using ruby’s method_missing, don’t alter the method parameter before calling super (you are calling super, aren’t you?). Otherwise, you will get a very clear exception:

  ArgumentError: no id given

Thanks to google and Nicholas Schlueter for the tip.