Skip to content

Instantly share code, notes, and snippets.

@ratbeard
Created February 14, 2010 02:26
Show Gist options
  • Save ratbeard/303794 to your computer and use it in GitHub Desktop.
Save ratbeard/303794 to your computer and use it in GitHub Desktop.
a `module` is about == to a namespace
`@name` is an instance variable in a class
self == this
puts == console.printLn
initialize == constructor
`def method(*args)` takes an arbitrary number of arguments, and 'splats' them in to an array in the `args` variable. so we can call: `method(1,2,3,4,7775,5)
module Song
class Note
def initialize(note)
@note = note
end
# Make a note sharp or flat: `+note` or `-note`
def +@
@sharp, @flat = true, false
self
end
def -@
@sharp, @flat = false, true
self
end
def sharp?() @sharp end
def flat?() @flat end
def to_s
modifier = sharp? && '♯' || flat? && '♭'
"#{@note}#{modifier}"
end
# We lack dotnet's rich Console.beep support :/
def play
puts to_s
end
end
class Song
def initialize
@name = "Untitled"
@notes = []
@tempo = 100
end
# Could metaprogram this out…
# `attr_writer :tempo` would build us `tempo=`, not `tempo`
def name(name) @name = name end
def tempo(tempo) @tempo = tempo end
def notes(*notes) @notes = notes end
def play
puts "Playing '#@name'…"
@notes.each do |note|
note.play
sleep 60.0 / @tempo
end
puts "… Crowd cheers!"
end
def method_missing(name, *args, &block)
Note.new(name)
end
end
def self.play(&block)
song = Song.new
song.instance_eval(&block)
song.play
song
end
end
Song.play do
name "Chill Tune"
tempo 280
notes c6, -g6, __, d5,
c6, +g6, a2, d5
end
# Output:
# Playing 'Chill Tune'…
# c6
# g6♭
# __
# d5
# c6
# g6♯
# a2
# d5
# … Crowd cheers!
# Starting with a boring class…
class Person
end
# … We can 'reopen' it and add methods later
class Person
def is_human?
true
end
end
Person.new.is_human? # => true
# Manually building a getter and setter for name:
class Person
def name=(name)
@name = name
end
def name
@name
end
end
# name=, name, @name, name. We are stuttering!
# Instead, use the builtin class method instead:
class Person
attr_accessor :name
end
# Now we have a 'name' and 'name=' method defined for us
# Whats that? Our language creator didn't make that for us?
# Then lets build it ourselves:
module GetterAndSetter
def getter(*variable_names)
variable_names.each do |name|
define_method(name) do
instance_variable_get("@#{name}")
end
end
end
def setter(*variable_names)
variable_names.each do |name|
define_method("#{name}=") do |value|
instance_variable_set("@#{name}", value)
end
end
end
def getter_and_setter(*variable_names)
getter(*variable_names)
setter(*variable_names)
end
end
# Now all we need to do is `extend` our class with this module
# and we will get those as class methods:
class Person
extend GetterAndSetter
end
Person.respond_to? :getter # => true
# as an FYI if we had done `include GetterAndSetter`, those
# methods would have been added to INSTANCES of Person, not
# the CLASS Person. Then we couldn't have done:
class Person
getter_and_setter :name, :age
end
# This is me:
mike = Person.new
mike.name = 'mike'
mike.age = 24
puts "#{mike.name} is #{mike.age}"
# We just implemented auto-generated getters and setters
# note, we could have made these do more interesting things, like:
# getter_and_setter :age, :default => 18, :required => true
# or whatever else we wanted.
# But lets inplement some more "cool" stuff!
# Its nice to be able to assign "properties" when we initialize
# an object:
module InitializeWithAttributes
def initialize(attributes={})
attributes.each do |name, value|
send("#{name}=", value) if respond_to? "#{name}="
end
end
end
# Remember, because we are implementing these methods in a module,
# we can include/extend any class with them, and get the
# implementation. We are not forced in to an inheritance hierarchy.
class Person
include InitializeWithAttributes
end
mike = Person.new :name => 'mike', :age => 25, :extra => 1
puts "#{mike.name} is #{mike.age}."
puts ":extra was ignored, I don't respond to that method"
# Dear Language gods,
# I can no longer wait for you to add these 4 lines yourself!
def using(object)
yield object # pass the given object tot he given block
object.dispose # now trash it!
end
class Person
def dispose
puts "bye"
end
end
using(Person.new :name => 'Disposable Joe') do |peep|
puts "hi, I'm #{peep.name}"
end
puts "see ya joe"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment