eigenclass logo
MAIN  Index  Search  Changes  PageRank  Login

Railisms (core/stdlib extensions in Ruby on Rails)

If you've ever written a piece of code that worked fine in a Rails app but bombed elsewhere, you've probably been relying on a Railism unknowingly.

I used rcodetools' rct-meth-args to generate a list of Railisms (extensions to the core/stdlib classes), by processing an intermediate tags file*1.

The list is fairly comprehensive, since it takes into account included and extended classes/modules in addition to class-reopening. It also includes information about the method arguments (names and default values), as returned by rct-meth-args.

There are over 220 Railisms. You can find the full list (corresponding to activesupport-1.4.0) below.

This is the script that analyzes the TAGS file generated by rct_meth_args:

#!/usr/bin/env ruby

core = []
ObjectSpace.each_object(Module){|kl| core << kl.to_s}
require 'set'
CORE = Set.new(core)

Definition = Struct.new(:name, :text, :pos)
tags = ARGF.read
file_data = Hash.new{|h,k| h[k] = []}
tags.split(/\f/).reject{|x| x.empty?}.each do |def_block|
  file, defs = /\s*(\S+),0\n(.*)/m.match(def_block).captures
  defs.split(/\n/).each do |defline|
    text, name, pos = /\s*(.*)\177(.*)\001(.*)/.match(defline).captures
    file_data[file] << Definition.new(name, text, pos)
  end
end

all_meth_defs = Hash.new{|h,k| h[k] = {} }
included  = Set.new
extending = Set.new

def each_railism(file_data)
  file_data.each_pair do |filename, defs|
    next unless filename["active_support/core_ext"]
    defs.each do |definition|
      yield definition
    end
  end
end

each_railism(file_data) do |definition|
  case definition.text
  when /^include/
    included << definition.text[/include (\S+)/, 1]
    next
  when /^extend/
    extending << definition.text[/extend (\S+)/, 1]
    next
  when /(.*)\.extend\(.*\)/
    extending << definition.text[/(.*)\.extend\(.*\)/, 2]
    next
  when /^(def|attr|attr_\w+)\b/
    klass, meth = /::(.*)[#.](.*)/.match(definition.name).captures
    all_meth_defs[klass][meth] = definition.text
  end
end

each_railism(file_data) do |definition|
  if md = /CoreExtensions::([^:]+)/.match(definition.name)
    klass = md[1]
  else
    klass = definition.name[/::([^.#]+)/, 1]
  end
  case definition.text
  when /^(def|attr)/
    #next unless CORE.include?(klass)
    actual_klass, type, meth = /::(.*)([#.])(.*)/.match(definition.name).captures
    next if included.include?(actual_klass) or extending.include?(actual_klass)
    puts "%s#{type}%s  %s" % [klass, meth, definition.text]
  when /^(include|extend) /
    included = definition.text[/(?:include|extend) (\S+)/, 1]
    all_meth_defs[included].each_pair do |methname, methtext|
      puts "%s#%s  %s" % [klass, methname, methtext]
    end
  when re = /\.extend\((\S+)\)/
    extendwith = definition.text[re, 1] 
    if all_meth_defs.has_key?(extendwith)
      key = extendwith
    else
      candidates = all_meth_defs.keys.grep(/CoreExtensions::#{klass}\S*#{Regexp.escape(extendwith)}/)
      if candidates.size == 1
        key = candidates.first
        $stderr.puts "RESOLVED extend(#{extendwith}) as extend #{key}"
      else
        $stderr.puts "CANNOT RESOLVE #{klass}"
        next
      end
    end
    all_meth_defs[key].each_pair do |methname, methtext|
      puts "%s#%s" % [klass, methname]
    end
  end
end

List of Railisms

Array

Array#in_groups_of
   def in_groups_of(number, fill_with = nil, &block)
Array#split
   def split(value = nil, &block)
Array#to_formatted_s
   def to_formatted_s(format = :default)
Array#to_param
   def to_param
Array#to_sentence
   def to_sentence(options = {})
Array#to_xml
   def to_xml(options = {})

BigDecimal

BigDecimal#to_s
   def to_s(format="F")

CGI

CGI#escape_skipping_slashes
   def escape_skipping_slashes(str)

Class

Class#cattr_accessor
   def cattr_accessor(*syms)
Class#cattr_reader
   def cattr_reader(*syms)
Class#cattr_writer
   def cattr_writer(*syms)
Class#class_inheritable_accessor
   def class_inheritable_accessor(*syms)
Class#class_inheritable_array
   def class_inheritable_array(*syms)
Class#class_inheritable_array_writer
   def class_inheritable_array_writer(*syms)
Class#class_inheritable_hash
   def class_inheritable_hash(*syms)
Class#class_inheritable_hash_writer
   def class_inheritable_hash_writer(*syms)
Class#class_inheritable_reader
   def class_inheritable_reader(*syms)
Class#class_inheritable_writer
   def class_inheritable_writer(*syms)
Class#inheritable_attributes
   def inheritable_attributes
Class#read_inheritable_attribute
   def read_inheritable_attribute(key)
Class#remove_class
   def remove_class(*klasses)
Class#remove_subclasses
   def remove_subclasses
Class#reset_inheritable_attributes
   def reset_inheritable_attributes
Class#subclasses
   def subclasses
Class#write_inheritable_array
   def write_inheritable_array(key, elements)
Class#write_inheritable_attribute
   def write_inheritable_attribute(key, value)
Class#write_inheritable_hash
   def write_inheritable_hash(key, hash)

Date

Date#to_date
   def to_date
Date#to_formatted_s
   def to_formatted_s(format = :default)
Date#to_time
   def to_time(form = :local)
Date#xmlschema
   def xmlschema

Enumerable

Enumerable#group_by
   def group_by
Enumerable#index_by
   def index_by
Enumerable#sum
   def sum(identity = 0, &block)

Exception

Exception#application_backtrace
   def application_backtrace
Exception#clean_backtrace
   def clean_backtrace
Exception#clean_message
   def clean_message
Exception#framework_backtrace
   def framework_backtrace

FalseClass

FalseClass#blank?
   def blank?

Hash

Hash#assert_valid_keys
   def assert_valid_keys(*valid_keys)
Hash#create_from_xml
   def create_from_xml(xml)
Hash#diff
   def diff(h2)
Hash#from_xml
   def from_xml(xml)
Hash#reverse_merge
   def reverse_merge(other_hash)
Hash#reverse_merge!
   def reverse_merge!(other_hash)
Hash#reverse_update
   def reverse_merge(other_hash)
Hash#stringify_keys
   def stringify_keys
Hash#stringify_keys!
   def stringify_keys!
Hash#symbolize_keys
   def symbolize_keys
Hash#symbolize_keys!
   def symbolize_keys!
Hash#to_options
   def symbolize_keys
Hash#to_options!
   def symbolize_keys!
Hash#to_xml
   def to_xml(options = {})

HashWithIndifferentAccess

HashWithIndifferentAccess#convert_key
   def convert_key(key)
HashWithIndifferentAccess#convert_value
   def convert_value(value)
HashWithIndifferentAccess#default
   def default(key = nil)
HashWithIndifferentAccess#[]=
   def []=(key, value)

Hash

Hash#with_indifferent_access
   def with_indifferent_access

HashWithIndifferentAccess

HashWithIndifferentAccess#delete
   def delete(key)
HashWithIndifferentAccess#dup
   def dup
HashWithIndifferentAccess#fetch
   def fetch(key, *extras)
HashWithIndifferentAccess#has_key?
   def key?(key)
HashWithIndifferentAccess#include?
   def key?(key)
HashWithIndifferentAccess#initialize
   def initialize(constructor = {})
HashWithIndifferentAccess#key?
   def key?(key)
HashWithIndifferentAccess#member?
   def key?(key)
HashWithIndifferentAccess#merge
   def merge(hash)
HashWithIndifferentAccess#merge!
   def update(other_hash)
HashWithIndifferentAccess#stringify_keys!
   def stringify_keys!; self end
HashWithIndifferentAccess#symbolize_keys!
   def symbolize_keys!; self end
HashWithIndifferentAccess#update
   def update(other_hash)
HashWithIndifferentAccess#values_at
   def values_at(*indices)

Integer

Integer#even?
   def even?
Integer#multiple_of?
   def multiple_of?(number)
Integer#odd?
   def odd?
Integer#ordinalize
   def ordinalize

Kernel

Kernel#daemonize
   def daemonize
Kernel#enable_warnings
   def enable_warnings
Kernel#require_library_or_gem
   def require_library_or_gem(library_name)
Kernel#silence_stderr
   def silence_stderr #:nodoc:
Kernel#silence_stream
   def silence_stream(stream)
Kernel#silence_warnings
   def silence_warnings
Kernel#suppress
   def suppress(*exception_classes)

LoadError

LoadError#new
   

Logger

Logger.define_around_helper
   def self.define_around_helper(level)

MissingSourceFile

MissingSourceFile.from_message
   def self.from_message(message)
MissingSourceFile#initialize
   def initialize(message, path)
MissingSourceFile#is_missing?
   def is_missing?(path)
MissingSourceFile#path
   attr_reader :path

Module

Module#alias_attribute
   def alias_attribute(new_name, old_name)
Module#alias_method_chain
   def alias_method_chain(target, feature)
Module#as_load_path
   def as_load_path
Module#attr_internal_accessor
   def attr_internal_accessor(*attrs)
Module#attr_internal
   def attr_internal_accessor(*attrs)
Module#attr_internal_reader
   def attr_internal_reader(*attrs)
Module#attr_internal_writer
   def attr_internal_writer(*attrs)
Module#delegate
   def delegate(*methods)
Module#included_in_classes
   def included_in_classes
Module#mattr_accessor
   def mattr_accessor(*syms)
Module#mattr_reader
   def mattr_reader(*syms)
Module#mattr_writer
   def mattr_writer(*syms)
Module#parent
   def parent
Module#parents
   def parents

NameError

NameError#missing_name
   def missing_name
NameError#missing_name?
   def missing_name?(name)

NilClass

NilClass#blank?
   def blank?

Numeric

Numeric#ago
   def ago(time = ::Time.now)
Numeric#blank?
   def blank?
Numeric#byte
   def bytes
Numeric#bytes
   def bytes
Numeric#day
   def days
Numeric#days
   def days
Numeric#exabyte
   def exabytes
Numeric#exabytes
   def exabytes
Numeric#fortnight
   def fortnights
Numeric#fortnights
   def fortnights
Numeric#from_now
   def since(time = ::Time.now)
Numeric#gigabyte
   def gigabytes
Numeric#gigabytes
   def gigabytes
Numeric#hour
   def hours
Numeric#hours
   def hours
Numeric#kilobyte
   def kilobytes
Numeric#kilobytes
   def kilobytes
Numeric#megabyte
   def megabytes
Numeric#megabytes
   def megabytes
Numeric#minute
   def minutes
Numeric#minutes
   def minutes
Numeric#month
   def months
Numeric#months
   def months
Numeric#petabyte
   def petabytes
Numeric#petabytes
   def petabytes
Numeric#second
   def seconds
Numeric#seconds
   def seconds
Numeric#since
   def since(time = ::Time.now)
Numeric#terabyte
   def terabytes
Numeric#terabytes
   def terabytes
Numeric#until
   def ago(time = ::Time.now)
Numeric#week
   def weeks
Numeric#weeks
   def weeks
Numeric#year
   def years
Numeric#years
   def years

Object

Object#blank?
   def blank?
Object#copy_instance_variables_from
   def copy_instance_variables_from(object, exclude = [])
Object#`
   def `(command) #:nodoc:
Object#extended_by
   def extended_by
Object#extend_with_included_modules_from
   def extend_with_included_modules_from(object)
Object#instance_exec
   def instance_exec(*arguments, &block)
Object#instance_values
   def instance_values
Object#remove_subclasses_of
   def remove_subclasses_of(*superclasses)
Object#returning
   def returning(value)
Object#subclasses_of
   def subclasses_of(*superclasses)
Object#to_json
   def to_json
Object#with_options
   def with_options(options)

Pathname

Pathname#clean_within
   def clean_within(string)

Proc

Proc#bind
   def bind(object)

Range

Range#to_formatted_s
   def to_formatted_s(format = :default)

String

String#at
   def at(position)
String#blank?
   def blank?
String#camelcase
   def camelize(first_letter = :upper)
String#camelize
   def camelize(first_letter = :upper)
String#chars
   def chars
String#classify
   def classify
String#constantize
   def constantize
String#dasherize
   def dasherize
String#demodulize
   def demodulize
String#each_char
   def each_char
String#ends_with?
   def ends_with?(suffix)
String#first
   def first(limit = 1)
String#foreign_key
   def foreign_key(separate_class_name_and_id_with_underscore = true)
String#from
   def from(position)
String#humanize
   def humanize
String#is_utf8?
   def is_utf8?
String#last
   def last(limit = 1)
String#pluralize
   def pluralize
String#singularize
   def singularize
String#starts_with?
   def starts_with?(prefix)
String#tableize
   def tableize
String#titlecase
   def titleize
String#titleize
   def titleize
String#to_date
   def to_date
String#to
   def to(position)
String#to_time
   def to_time(form = :utc)
String#underscore
   def underscore

Symbol

Symbol#to_proc
   def to_proc

Time

Time#advance
   def advance(options)
Time#ago
   def ago(seconds)
Time#at_beginning_of_day
   def beginning_of_day
Time#at_beginning_of_month
   def beginning_of_month
Time#at_beginning_of_quarter
   def beginning_of_quarter
Time#at_beginning_of_week
   def beginning_of_week
Time#at_beginning_of_year
   def beginning_of_year
Time#at_end_of_month
   def end_of_month
Time#at_midnight
   def beginning_of_day
Time#beginning_of_day
   def beginning_of_day
Time#beginning_of_month
   def beginning_of_month
Time#beginning_of_quarter
   def beginning_of_quarter
Time#beginning_of_week
   def beginning_of_week
Time#beginning_of_year
   def beginning_of_year
Time#change
   def change(options)
Time#days_in_month
   def days_in_month(month, year=nil)
Time#end_of_month
   def end_of_month
Time#in
   def since(seconds)
Time#last_month
   def last_month
Time#last_year
   def last_year
Time#midnight
   def beginning_of_day
Time#monday
   def beginning_of_week
Time#months_ago
   def months_ago(months)
Time#months_since
   def months_since(months)
Time#next_month
   def next_month
Time#next_week
   def next_week(day = :monday)
Time#next_year
   def next_year
Time#seconds_since_midnight
   def seconds_since_midnight
Time#since
   def since(seconds)
Time#to_date
   def to_date
Time#to_formatted_s
   def to_formatted_s(format = :default)
Time#tomorrow
   def tomorrow
Time#to_time
   def to_time
Time#years_ago
   def years_ago(years)
Time#years_since
   def years_since(years)
Time#yesterday
   def yesterday

TrueClass

TrueClass#blank?
   def blank?


facets compare - trans (2007-01-24 (Wed) 17:15:13)

nice! when i get back from vacation i'll try this out on facets and then make a comparison of the two. will be interesting.

t.

mfp 2007-01-25 (Thr) 05:08:57

I'd expect most Railisms to be present in Facets (many/most? were taken from there to begin with). Which leads to the question: if something originated/is used outside Rails, is it still a Railism? I'd still say it is, since the overwhelming majority will have learned it from Rails.


̵�� - rubikitch (2007-01-25 (��) 04:28:57)

I wonder what the rct-meth-args command line is.

 cd $gemdir/gems/activesupport-1.4.0/lib
 rct-meth-args -t -I. active_support/core_ext.rb > $OLDPWD/TAGS

got LoadError (xml_simple).

 rct-meth-args -t -I. active_support > $OLDPWD/TAGS

went into an infinite loop. (maybe rct-meth-args's bug)

And I found the C-l character was erased by hiki.

mfp 2007-01-25 (Thr) 05:01:04

The command line was

rct-meth-args -t -Ilib -I lib/active_support/vendor/ lib/active_support/core_ext.rb > /tmp/TAGS

from the activesupport-1.4.0 directory.

On my browser, ^L is shown as a box with the codepoint (0xc), but I'm replacing it with \f for the sake of clarity.


Great idea. - Zev (2007-01-25 (Thr) 20:47:44)

This is very cool. I have something somewhat similar in the kwala project that uses the ri yaml output to show how code has changed from one build of ri to the next.



Last modified:2007/01/24 05:03:37
Keyword(s):[blog] [ruby] [frontpage] [rails] [rcodetools] [extend] [include]
References:

*1 in emacs TAGS format