Created
February 14, 2010 02:26
-
-
Save ratbeard/303794 to your computer and use it in GitHub Desktop.
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
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) | |
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
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! |
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
# 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