Skip to content

Instantly share code, notes, and snippets.

@cupakromer
Created October 3, 2012 03:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cupakromer/3824865 to your computer and use it in GitHub Desktop.
Save cupakromer/3824865 to your computer and use it in GitHub Desktop.
Abusing Class and Struct

In tonight's meeting I tried, and failed, to show how to create a Struct with a custom method. Turns out, you can't do that. But you can use Class.new to accomplish this. UPDATE: You can do this. I don't remember what syntax I was trying before, but in 1.9.3 you can successfully pass a block to Struct.new; though this isn't documented.

The end result is the same:

  • A new Class object
  • Default initialize handling the attributes
  • attr_accessor methods defined for those attributes

Big thanks to @steveklabnik for his two posts which inspired me to try to show this horribleness (that's a reflection on my silliness, not Steve):

At this point, other than just showing language abuse, I could see this being used in test stubbing and possible as you build out domain concept ideas.

# Abuse of Classes and Structs
class Person < Struct.new(:first_name, :last_name)
def full_name
"#{first_name.capitalize} #{last_name.capitalize}"
end
end
# This also works but is not clear at all.
# Don't do this
Person = Class.new(Struct.new(:first_name, :last_name)) {
def full_name
"#{first_name.capitalize} #{last_name.capitalize}"
end
}
fred_jones = Person.new "fred", "jones"
#=> #<struct Person first_name="fred", last_name="jones">
fred_jones.first_name
#=> "fred"
fred_jones.full_name
#=> "Fred Jones"
# This is the better way, as it doesn't create a parent class which will mess with ancestors and `super`
# Google around for more details on why this is the way to go.
Person = Struct.new(:first_name, :last_name) {
def full_name
"#{first_name.capitalize} #{last_name.capitalize}"
end
}
# Alternatively
Person = Struct.new(:first_name, :last_name)
class Person
def full_name
"#{first_name.capitalize} #{last_name.capitalize}"
end
end
# Just for giggles, the same thing using metaprogramming
# Don't do this
Person = Struct.new(:first_name, :last_name).tap{ |s|
s.class_eval do
def full_name
"#{first_name.capitalize} #{last_name.capitalize}"
end
end
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment