Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: extract features of a Stack from the Array class

Tags:

stack

ruby

I have the need to use a Stack-like data structure for a program that I am writing and I know that Ruby doesn't have an explicit Stack data-structure, but that the Array class has all of the properties that make a Stack: push, pop, size, clear, isEmpty, inspect, to_s.

In searching online I found various posts using this syntax to extract features of the Array class into a subclass:

Stack = Array.extract([
  :push,
  :pop,
  :size,
  :clear,
  :inspect,
  :to_s
])

s = Stack.new
s.push 1
s.push 2
s.push 3
s       # => [1, 2, 3]
s.pop   # => 3
s       # => [1, 2]

I would like to do something similar to this so my subclass of Array is restricted in what calls it can make, but it appears that the extract method is no longer in the Array class API.

Questions:

  1. This feature was removed for a reason, what is the detriment of something like this?
  2. How can achieve functionality similar to this using Ruby 1.9.3? Right now I am just delegating the calls that I need to the Array class, but all the other methods in the Array class can still be called on my Stack object, which I don't want to allow.
like image 453
Hunter McMillen Avatar asked Dec 16 '22 02:12

Hunter McMillen


2 Answers

Take this code, for example:

class Stack
  def initialize
    @array = []
  end

  def push val
    @array.push val
  end

  def pop 
    @array.pop
  end
end

Here you have private instance var to which you delegate selected methods. Other methods cannot be called directly. The syntax can be sweetened and made more "rubesque" with some metaprogramming, but basic idea is as shown above.

Of course, one can always get to that private var via instance_variable_get and there's nothing you can do about it. This is Ruby!

Make clean safe public interface. And if someone tries to meddle with inner parts and breaks something, it's his problem.

Update

If you're using ActiveSupport (which comes with Rails), then there's a simpler way of doing this.

# load ActiveSupport if not in Rails env
require 'active_support/core_ext'

class Stack
  def initialize
    @impl = []
  end

  # the "extract" part :)
  delegate :count, :push, :pop, to: :@impl
end

s = Stack.new
s.push(3).push(4)
s.count # => 2

Or look at similar answer by @AndrewGrimm.

like image 111
Sergio Tulentsev Avatar answered Jan 08 '23 00:01

Sergio Tulentsev


If you're going to be doing a lot of delegating, then you may want to use the delegation methods Ruby provides.

require "forwardable"

class Stack
  extend Forwardable
  def_delegators :@array, :push, :pop

  def initialize
    @array = []
  end
end

Not only does it mean less typing, but if you're familiar with Forwardable, it's easier to read. You know that your class is merely delegating, without having to read the code for Stack#push or Stack#pop.

like image 41
Andrew Grimm Avatar answered Jan 08 '23 02:01

Andrew Grimm