Last active
December 18, 2015 03:39
-
-
Save tamouse/5719507 to your computer and use it in GitHub Desktop.
Playing with Struct, Enumerable, and Comparable
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
irb(main):002:0> c = MyClass.new | |
#<struct MyClass stuff=[], and={}, nonsense=""> | |
irb(main):003:0> c.keys # I want this to return the keys in c.and | |
struct_play.rb@76 in method_missing method: :keys, args: [] | |
ArgumentError: wrong number of arguments (1 for 0) | |
from struct_play.rb:80:in `keys' | |
from struct_play.rb:80:in `method_missing' | |
from (irb):3 | |
from /home/tamara/.rvm/rubies/ruby-2.0.0-p195/bin/irb:13:in `<main>' | |
Is there a way to test how many (none, one, many) args a method takes by introspection? |
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
irb(main):001:0> load 'struct_play.rb' | |
true | |
irb(main):002:0> c = MyClass.new | |
#<struct MyClass stuff=[], and={}, nonsense=""> | |
irb(main):003:0> c << 1 << 'a' << :one | |
[1, "a", :one] | |
irb(main):004:0> c.nonsense = "This is nonsense" | |
"This is nonsense" | |
irb(main):005:0> c[:a] = %{some words of dissent} | |
"some words of dissent" | |
irb(main):006:0> c['wax'] = %w{wax on wax off} | |
["wax", "on", "wax", "off"] | |
irb(main):007:0> puts c.to_s | |
This is nonsense {:a=>"some words of dissent", "wax"=>["wax", "on", "wax", "off"]} [1, "a", :one] | |
nil | |
irb(main):008:0> c1 = MyClass.new | |
#<struct MyClass stuff=[], and={}, nonsense=""> | |
irb(main):009:0> c1.nonsense = "Another fine mess" | |
"Another fine mess" | |
irb(main):010:0> c2 = MyClass.new | |
#<struct MyClass stuff=[], and={}, nonsense=""> | |
irb(main):011:0> c2.nonsense = "the bees knees" | |
"the bees knees" | |
irb(main):012:0> [c,c1,c2].sort | |
[#<struct MyClass stuff=[], and={}, nonsense="Another fine mess">, #<struct MyClass stuff=[1, "a", :one], and={:a=>"some words of dissent", "wax"=>["wax", "on", "wax", "off"]}, nonsense="This is nonsense">, #<struct MyClass stuff=[], and={}, nonsense="the bees knees">] | |
irb(main):013:0> [c,c1,c2].each {|x| puts x} | |
This is nonsense {:a=>"some words of dissent", "wax"=>["wax", "on", "wax", "off"]} [1, "a", :one] | |
Another fine mess {} [] | |
the bees knees {} [] | |
[#<struct MyClass stuff=[1, "a", :one], and={:a=>"some words of dissent", "wax"=>["wax", "on", "wax", "off"]}, nonsense="This is nonsense">, #<struct MyClass stuff=[], and={}, nonsense="Another fine mess">, #<struct MyClass stuff=[], and={}, nonsense="the bees knees">] | |
irb(main):014:0> [c,c1,c2].sort.each {|x| puts x} | |
Another fine mess {} [] | |
This is nonsense {:a=>"some words of dissent", "wax"=>["wax", "on", "wax", "off"]} [1, "a", :one] | |
the bees knees {} [] | |
[#<struct MyClass stuff=[], and={}, nonsense="Another fine mess">, #<struct MyClass stuff=[1, "a", :one], and={:a=>"some words of dissent", "wax"=>["wax", "on", "wax", "off"]}, nonsense="This is nonsense">, #<struct MyClass stuff=[], and={}, nonsense="the bees knees">] |
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
class MyClass < Struct.new :stuff, :and, :nonsense | |
def initialize | |
self.stuff = Array.new | |
self.and = Hash.new | |
self.nonsense = String.new | |
end | |
include Enumerable | |
# Defining the #each method gives a whole lot of other goodies coming from Enumerable | |
def each &block | |
self.stuff.each do |s| | |
if block_given? | |
block.call s | |
else | |
yield s | |
end | |
end | |
end | |
# This little ditty appends objects to the stuff inst_var so you can do | |
# neat things like: | |
# | |
# c << 1 << 'a' << :one | |
def << obj | |
self.stuff << obj | |
end | |
# Give a nice output for the object | |
def to_s | |
"#{self.nonsense.to_s} #{self.and.inspect} #{self.stuff.inspect}" | |
end | |
# Provides a means of getting at both containers in the class. | |
# Integer values for idx simply return the value at stuff[idx] while | |
# Non-Integer values act as keys to the hash and. | |
def [] idx | |
case idx | |
when Integer | |
self.stuff[idx] | |
else | |
self.and[idx] | |
end | |
end | |
# Counterpart to the above, this is the setter | |
def []= idx, obj | |
case idx | |
when Integer | |
self.stuff[idx]=obj | |
else | |
self.and[idx]=obj | |
end | |
end | |
# Makes this class's objects comparable, so they can be sorted | |
# and other neat tricks. | |
include Comparable | |
# The basic comparison operator. | |
# In this case, it's just the nonsense content that's being compared. | |
def <=> other | |
self.nonsense <=> other.nonsense | |
end | |
# This is going to be fun | |
ArrayMethods = Array.instance_methods(false) | |
HashMethods = Hash.instance_methods(false) | |
StringMethods = String.instance_methods(false) | |
# If the caller sends a method to us, we dispatch it to the attributes | |
# in order of declaration. This is magically accomplished via | |
# the method_missing method! | |
def method_missing(method, *args) | |
STDERR.puts "#{File.basename(__FILE__)}@#{__LINE__} in #{__method__} method: #{method.inspect}, args: #{args.inspect}" | |
if ArrayMethods.include? method | |
self.stuff.send(method, args) | |
elsif HashMethods.include? method | |
self.and.send(method, args) | |
elsif StringMethods.include? method | |
self.nonsense.send(method, args) | |
end | |
super | |
end | |
end |
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
class MyClass < Struct.new :stuff, :and, :nonsense | |
def initialize | |
self.stuff = Array.new | |
self.and = Hash.new | |
self.nonsense = String.new | |
end | |
include Enumerable | |
# Defining the #each method gives a whole lot of other goodies coming from Enumerable | |
def each &block | |
self.stuff.each do |s| | |
if block_given? | |
block.call s | |
else | |
yield s | |
end | |
end | |
end | |
# This little ditty appends objects to the stuff inst_var so you can do | |
# neat things like: | |
# | |
# c << 1 << 'a' << :one | |
def << obj | |
self.stuff << obj | |
end | |
# Give a nice output for the object | |
def to_s | |
"#{self.nonsense.to_s} #{self.and.inspect} #{self.stuff.inspect}" | |
end | |
# Provides a means of getting at both containers in the class. | |
# Integer values for idx simply return the value at stuff[idx] while | |
# Non-Integer values act as keys to the hash and. | |
def [] idx | |
case idx | |
when Integer | |
self.stuff[idx] | |
else | |
self.and[idx] | |
end | |
end | |
# Counterpart to the above, this is the setter | |
def []= idx, obj | |
case idx | |
when Integer | |
self.stuff[idx]=obj | |
else | |
self.and[idx]=obj | |
end | |
end | |
# Makes this class's objects comparable, so they can be sorted | |
# and other neat tricks. | |
include Comparable | |
# The basic comparison operator. | |
# In this case, it's just the nonsense content that's being compared. | |
def <=> other | |
self.nonsense <=> other.nonsense | |
end | |
# This is going to be fun | |
ArrayMethods = Array.instance_methods(false) | |
HashMethods = Hash.instance_methods(false) | |
StringMethods = String.instance_methods(false) | |
# If the caller sends a method to us, we dispatch it to the attributes | |
# in order of declaration. This is magically accomplished via | |
# the method_missing method! | |
def method_missing(m, *args) | |
STDERR.puts "#{File.basename(__FILE__)}@#{__LINE__} in #{__method__} m: #{m.inspect}, args: #{args.inspect}" if $debug | |
if ArrayMethods.include? m | |
self.stuff.send(m, *args) # splat the args | |
elsif HashMethods.include? m | |
self.and.send(m, *args) | |
elsif StringMethods.include? m | |
self.nonsense.send(m, *args) | |
else | |
super # only call super if the other conditions are not met | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The problem lies at line 84.
super
should only be called if the other conditions are not met; not all the time. Also, the args in the send calls should be splatted: *args.