Skip to content

Instantly share code, notes, and snippets.

@aalin
Created January 27, 2012 13:12
Show Gist options
  • Select an option

  • Save aalin/1688705 to your computer and use it in GitHub Desktop.

Select an option

Save aalin/1688705 to your computer and use it in GitHub Desktop.
Any thoughts?
# This is not the best example, but imagine a Rails project with lots of complex logic and methods and stuff...
# Imagine that Base is an 500 line ActiveRecord model... :)
require 'rubygems'
require 'active_support/core_ext'
Customer = Struct.new(:id, :first_name, :last_name, :company_name)
## Implementation directly in the class
# Pros:
# * Easy to find the implementation of all methods
#
# Cons:
# * Could easily turn into a huge, messy file.
class Customer1 < Customer
def name
if company_name.present?
if joined_names.present?
"#{ company_name } (#{ joined_names })"
else
company_name
end
elsif joined_names.present?
joined_names
else
generic_name
end
end
def id_and_name
"#{id}. #{name}"
end
private
def joined_names
[first_name, last_name].reject(&:blank?).join(" ").presence
end
def generic_name
"#{ self.class.name } ##{ id }"
end
end
## Including modules
# Pros:
# * Small file.
# Cons:
# * Adds an unknown number of methods to the class that might collide with existing method names,
# and you have no clue what's going on. And you go crazy when you try to find why things are broken
# and where they are defined...
module Naming
def name
if company_name.present?
if joined_names.present?
"#{ company_name } (#{ joined_names })"
else
company_name
end
elsif joined_names.present?
joined_names
else
generic_name
end
end
def id_and_name
"#{id}. #{name}"
end
private
def joined_names
[first_name, last_name].reject(&:blank?).join(" ").presence
end
def generic_name
"#{ self.class.name } ##{ id }"
end
end
class Customer2 < Customer
include Naming
end
## Implementation in another class
# Pros:
# * Puts all logic in another class
# * You can use the same logic with different objects
# * Easy to jump to the classes (with gf in Vim)
# * Easy to test separately
# Cons:
# * Help me out on this one!
class CustomerNaming < Struct.new(:customer)
def name
if company_name.present?
if joined_names.present?
"#{ company_name } (#{ joined_names })"
else
company_name
end
elsif joined_names.present?
joined_names
else
generic_name
end
end
def id_and_name
"#{id}. #{name}"
end
private
def method_missing(method, *args)
customer.send(method, *args)
end
def joined_names
[first_name, last_name].reject(&:blank?).join(" ").presence
end
def generic_name
"#{ self.class.name } ##{ id }"
end
end
class Customer3 < Customer
def naming
CustomerNaming.new(self)
end
end
## Class with helper method
# Pros:
# * Same as the previous, but shorter.
# * Easy to see which classes are being used.
# Cons:
# * HELP!!!
class Customer
def self.extension(methods_and_classes)
methods_and_classes.each do |method, klass|
define_method(method) { klass.new(self) }
end
end
end
class Customer4 < Customer
extension :naming => CustomerNaming
delegate :name, :id_and_name, :to => :naming
end
## Usage
puts Customer1.new(123, "First", "One").id_and_name
puts Customer2.new(123, "Second", "Two").id_and_name
puts Customer3.new(123, "Third", "Three").naming.id_and_name
puts Customer4.new(123, "Fourth", "Four").name
puts Customer4.new(123, "Fourth", "Four").id_and_name
@henrik
Copy link
Copy Markdown

henrik commented Feb 24, 2012

The last example fleshed out: https://gist.github.com/1900793

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment