Skip to content

Instantly share code, notes, and snippets.

@tamouse
Last active December 18, 2015 03:39
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 tamouse/5719507 to your computer and use it in GitHub Desktop.
Save tamouse/5719507 to your computer and use it in GitHub Desktop.
Playing with Struct, Enumerable, and Comparable
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?
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">]
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
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
@tamouse
Copy link
Author

tamouse commented Jun 7, 2013

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.

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