[#408634] How do I make lots of classes aware of each other? — "Andrew S." <lists@...>

I'm apparently missing something fundamental in my knowledge of classes

10 messages 2013/07/02

[#408712] Ruby web service with REST support — "Shubhada S." <lists@...>

Hi All,

17 messages 2013/07/05

[#408812] create variables depending on counter — stefan heinrich <lists@...>

Hi community,

21 messages 2013/07/09

[#408854] execute commands within SMTP email code: send content in variables and not actual variables — dJD col <lists@...>

I am trying to send an email using the code below. I am able to send the

9 messages 2013/07/10

[#409031] tap { break } idiom deserves its own Kernel method? — Andy Lowry <lists@...>

I use this idiom from time to time:

13 messages 2013/07/22

[#409072] Link To Masses Of External Data In Openoffice? — "Austin J." <lists@...>

This is what I want to do.

19 messages 2013/07/23
[#409102] Re: Link To Masses Of External Data In Openoffice? — Tamara Temple <tamouse.lists@...> 2013/07/24

[#409103] Re: Link To Masses Of External Data In Openoffice? — "Austin J." <lists@...> 2013/07/25

tamouse m. wrote in post #1116598:

[#409122] Re: Link To Masses Of External Data In Openoffice? — Tamara Temple <tamouse.lists@...> 2013/07/26

[#409142] Re: Link To Masses Of External Data In Openoffice? — "Austin J." <lists@...> 2013/07/26

tamouse m. wrote in post #1116750:

[#409073] class <=> module — Bráulio Bhavamitra <lists@...>

Hello all,

17 messages 2013/07/23

[#409104] Ruby newbie question on Methods (NoMethoderror) — "Crispian A." <lists@...>

I have recently started learning ruby and so I am writing a small little

10 messages 2013/07/25

[#409170] Working through Ch.10 for learning to program 2.0 (Chris Pine) — JD JD <lists@...>

So, I have been working through this book, and have been doing ok up

33 messages 2013/07/28
[#409195] Re: Working through Ch.10 for learning to program 2.0 (Chris Pine) — Harry Kakueki <list.push@...> 2013/07/29

I tried this and came up with a one-liner that seems to do it. It sorts the

[#409258] WATIR - ScriptError popup on IE - Unable to get rid of! — Graeme Halls <lists@...>

I am new to Ruby & Watir, and I am having a nightmare with IE and Script

11 messages 2013/07/31

Re: Implementing sort/sort!

From: Robert Klemme <shortcutter@...>
Date: 2013-07-03 07:30:33 UTC
List: ruby-talk #408672
On Wed, Jul 3, 2013 at 4:39 AM, Kyle Hunter <lists@ruby-forum.com> wrote:

> Hello,
>
> I was wondering about implementing sort/sort!. Basically, I have a Point
> class, which contains x, y variables of a 2d Cartesian point. It also
> includes the Comparable mixin and thus implements a <=> method.
>
> I also have a Polygon class which is really just a container for an
> array of the aforementioned Point class. I guess the name is a misnomer
> but I'm working on adding functionality. Anyway, I want Polygon.sort to
> return a new Polygon with the points sorted, and Polygon.sort! to modify
> the points inside of the current polygon.
>
> I've attempted it, and it seems to work, but I was wondering if anyone
> could recommend a more canonical or idiomatic way to do it, or point out
> any flaws with my approach.
>
> class Point
>   include Comparable
>   attr_accessor :x, :y
>
>   def initialize(x, y)
>     @x = x
>     @y = y
>   end
>
>   def <=>(other)
>     x_cmp = @x <=> other.x
>     x_cmp != 0 ? x_cmp : @y <=> other.y
>   end
> end
>
> class Polygon
>   include Enumerable
>   attr_accessor :points
>

Usually a bad idea because it allows someone to mess with the internal
state of your class Polygon.  You can even update @points to point to a
single Fixnum which will break a lot of logic of your class.  You violate
encapsulation here.


>   alias_method :_sort, :sort
>

  def initialize(points)
>     @points = points
>   end
>

You could add a check which ensures points does actually contain what it is
supposed to contain.  Example

def initialize(points)
  @points = points.each {|x| raise "Type error" unless Point === x}
end

Note: this will also flag an error if points does not have Each - an
additional safety against instances of wrong types passed in.

  def each(&blk)
>     @points.each(&blk)
>   end
>

Conventionally #each returns self. That is missing here.


>   def sort
>     Polygon.new _sort
>   end
>

Usually with pairs of bang and non bang methods one can do

def meth!
  # logic
  self
end

def meth
  dup.tap {|copy| copy.meth!}
end


>   def sort!
>     @points = _sort
>   end
>

Wrong return value.  Should be self.


> end
>
> # Example usage
> poly = Polygon.new [Point.new(3,4), Point.new(1,2)]
> poly.sort
> poly.sort!
>

I think there is one design issue here: you include Enumerable and hence
Enumerable#sort and #sort_by.  Those methods always return an Array.  You
are returning Polygon here which is a change in interface.  Code like this
will break when handed a Polygon:

def sorted_size(x)
  case x
  when Enumerable
    x.sort.size
  when ...
  else
   ...
  end
end

Changing an interface in this way is usually a bad thing to do.  If you
want to include Enumerable but also provide a means to sort Points you
could provide a separate method to sort Points, e.g.

def sort_points
  @points.sort!
  self
end

Kind regards

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

In This Thread