Created
August 29, 2011 12:16
-
-
Save caleon/1178278 to your computer and use it in GitHub Desktop.
Some of my favorite core extensions for Ruby and Rails used on practically all of my projects these days
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This file meant for globally available, utility methods. | |
############################### | |
## Ruby-level Extensions ## | |
############################### | |
class Object | |
def as(&block) | |
yield self | |
end | |
# Logic utilities | |
def or_else(&block) | |
self || yield(self) | |
end | |
def and_also(&block) | |
self && yield(self) | |
end | |
# Convenience methods for a more idiomatic code than [ "users", "purchases" ].include?(controller_name) so that | |
# the same can now be written as controller_name.is_included_in?('users', 'purchases') | |
def is_included_in?(*others) | |
others.flatten_splat.include?(self) | |
end | |
def is_excluded_from?(*others) | |
others.flatten_splat.exclude?(self) | |
end | |
# Similar to is_a? or kind_of? but with an array of possible classes. Returns the matching class or a nil. | |
def is_one_kind_of?(*klasses) | |
klasses.flatten_splat.detect {|klass| klass === self } | |
end | |
end | |
module Enumerable | |
def map_select(value_for_skip = nil) | |
self.inject([]) do |acc, item| | |
(value = yield(item)) == value_for_skip ? acc : acc << value | |
end | |
end | |
def map_detect(value_for_no_matching = nil) | |
self.each { |el| if result = yield(el) then return result end } | |
value_for_no_matching | |
end | |
end | |
class Set | |
def not_subset?(set) | |
set.is_a?(Set) or raise ArgumentError, "value must be a set" | |
return true if set.size > size | |
any? { |o| !set.include?(o) } | |
end | |
end | |
class Array | |
# Compares two arrays to see if the elements are same but simply rearranged. | |
def rearranges?(*other_ary) | |
Set.new(self) == Set.new(other_ary.flatten_splat) | |
end | |
def subset?(other_ary) | |
other_ary.is_a?(Array) or raise ArgumentError, "Other array in argument must be an Array" | |
Set.new(self).subset?(Set.new(other_ary)) | |
end | |
def is_included_in?(other_ary) | |
other_ary.is_a?(Array) or raise ArgumentError, "Other array in argument must be an Array" | |
super || subset?(other_ary) | |
end | |
# Careful with this. This means 4 out of 5 elements can be the same but if not all of this self Array object are a part of other_ary, | |
# this returns true, as in "Yes, depite having 4 out of 5 in other_ary, I am excluded from the other_ary." | |
def is_excluded_from?(other_ary) | |
other_ary.is_a?(Array) or raise ArgumentError, "Other array in argument must be an Array" | |
super || not_subset?(other_ary) | |
end | |
alias_method :include_one?, :include? | |
def include_all?(*other_ary) | |
other_ary.flatten_splat! | |
include_one(other_ary) || superset?(other_ary) | |
end #alias_method :include?, :include_all? | |
############################# | |
## Quick selectors/filters ## | |
############################# | |
def select_kind_of(klass); select { |el| el.kind_of?(klass) }; end | |
def reject_kind_of(klass); reject { |el| el.kind_of?(klass) }; end | |
def select_kind_of!(klass); select! { |el| el.kind_of?(klass) }; end | |
def reject_kind_of!(klass); reject! { |el| el.kind_of?(klass) }; end | |
# flatten_splat! is used for dealing conveniently with the common pattern where a method's arguments has a splat (*) operator, | |
# but the developer wants to provide the option of the method accepting either a list or an array for the argument. Typically | |
# dealt with in the following manner: | |
# | |
# BEFORE: NOW: | |
# def do_something(*args) def do_something(*args) | |
# args = args.shift if args.one? && args.first.is_a?(Array) args.flatten_splat! | |
# end end | |
def flatten_splat(with_bang=false) | |
flatten_splat_needed? ? with_bang ? flatten! : flatten : self | |
end | |
def flatten_splat! | |
flatten_splat(true) | |
end | |
def flatten_splat_needed? | |
self.size == 1 and first.is_a?(Array) | |
end | |
private :flatten_splat_needed? | |
###################################### | |
## Array#extract_options extensions ## | |
###################################### | |
def extract_options | |
options_extractable? ? last : {} | |
end | |
def extract_options! | |
options_extractable? ? pop : {} | |
end | |
def options_extractable? | |
last.is_a?(Hash) && last.extractable_options? | |
end | |
def extract_options_with_merge(update_hash={}) | |
extract_options_without_merge.merge(update_hash || {}) | |
end; alias_method_chain :extract_options, :merge | |
def extract_options_with_merge!(update_hash={}) | |
extract_options_without_merge!.merge(update_hash || {}) | |
end; alias_method_chain :extract_options!, :merge | |
def merge_options(update_hash={}) | |
endex, base_hash = options_extractable? ? [ -2, last ] : [ -1, {} ] | |
Array[ *self[0..endex], base_hash.merge(update_hash || {}) ] | |
end; alias_method :merge_opts, :merge_options | |
def merge_options!(update_hash={}) | |
push(extract_options!(update_hash)) | |
end; alias_method :merge_opts!, :merge_options! | |
def arguments_and_options(update_hash={}) | |
merge_options(update_hash).args_and_opts! | |
end | |
def arguments_and_options!(update_hash={}) | |
[ extract_options_with_merge!(update_hash), self ].rotate | |
end | |
[ :args_and_opts!, :args_and_opts_with_merge!, :arguments_and_options_with_merge! ].each do |meth| | |
alias_method meth[0..-2].intern, :arguments_and_options | |
alias_method meth, :arguments_and_options! | |
end | |
# duplicated logic from argumentation | |
def constrained_by_archetype_and_method(archetype, method_name) | |
# all_proc = lambda { |type, m| type != :rest } | |
limit = [ archetype.method(method_name).parameters.if?(".all? { |type, m| type != :rest }", :else => (1.0/0)).count, | |
archetype.method(method_name).arity.abs ].max | |
take(limit.unless?(:finite?) { self.size }) | |
rescue | |
self | |
end | |
private | |
def method_missing(method_sym, *arguments, &block) | |
return super unless method_sym =~ /^#{%w(arg(?:ument)?s opt(?:ion)?s).zip(Array.new(2, REGEX[:with_method_name].source)).map(&:join).join('_and_')}!$/ | |
args_and_opts.tap do |argopts| | |
[ $1, $2 ].each_with_index do |meth, i| | |
next unless meth.significant? | |
params = (that_meth = (obj = argopts[-i]).method(meth.intern)).parameters | |
argopts[-i] = (meth && obj.if?([ :respond_to?, meth.intern ]).send(*(i == 0 ? meth.intern : [ meth.intern, *arguments.constrained_by_archetype_and_method(obj, meth) ]))) | |
end | |
end # end args_and_opts.tap | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment