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.
*1 in emacs TAGS format
Keyword(s):[blog] [ruby] [frontpage] [rails] [rcodetools] [extend] [include]
References: