Setting local variables in a binding

Why does this not work?

ruby-1.9.2-p0 > b = binding
=> #<Binding:0x9adc81c>
ruby-1.9.2-p0 > eval("x = 5", b)
=> 5
ruby-1.9.2-p0 > x
NameError: undefined local variable or method `x' for main:Object
  from (irb):6
  from /home/martin/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'
ruby-1.9.2-p0 > eval("x")
NameError: undefined local variable or method `x' for main:Object
  from (irb):7:in `eval'
  from (irb):7:in `eval'
  from (irb):7
  from /home/martin/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'
ruby-1.9.2-p0 > eval("x", b)
=> 5

martin

Martin DeMello wrote:

Why does this not work?

ruby-1.9.2-p0 > b = binding
=> #<Binding:0x9adc81c>
ruby-1.9.2-p0 > eval("x = 5", b)
=> 5
ruby-1.9.2-p0 > x
NameError: undefined local variable or method `x' for main:Object
  from (irb):6
  from /home/martin/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'

irb(main):001:0> b = binding
=> #<Binding:0x7f9079ec7460>
irb(main):002:0> eval("x = 5", b)
=> 5
irb(main):003:0> x
=> 5
irb(main):004:0> RUBY_DESCRIPTION
=> "ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]"

It might be an irb-ism in 1.9. Have you tried it in a standalone .rb
program?

···

--
Posted via http://www.ruby-forum.com/\.

Martin DeMello wrote:

Why does this not work?

ruby-1.9.2-p0 > b = binding
=> #<Binding:0x9adc81c>
ruby-1.9.2-p0 > eval("x = 5", b)
=> 5
ruby-1.9.2-p0 > x
NameError: undefined local variable or method `x' for main:Object
  from (irb):6
  from /home/martin/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'
ruby-1.9.2-p0 > eval("x")
NameError: undefined local variable or method `x' for main:Object
  from (irb):7:in `eval'
  from (irb):7:in `eval'
  from (irb):7
  from /home/martin/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'
ruby-1.9.2-p0 > eval("x", b)
=> 5

martin

Hmmm, it seems to work when x is already defined (in 1.9.2):

irb(main):001:0> x=4
=> 4
irb(main):002:0> b=binding
=> #<Binding:0xfa2ab0>
irb(main):003:0> eval("x=5", b)
=> 5
irb(main):004:0> x
=> 5
irb(main):005:0>

If I do not set x to a value beforehand, I get the same error. Do not
know why though.

js

···

--
Posted via http://www.ruby-forum.com/\.

By the way, I've been busy and not checking the forums too much lately.
However, I just noticed the concurrent thread of:

sort_by: multiple fields with reverse sort.

http://www.ruby-forum.com/topic/224672#new

In my world I would do:

class B < EnumerableObjectClass::Base
  attr_accessor :field_1, :field_0, :field_2 # sets default sort order.

  def initialize(field_0, field_1, field_2)
    super()
    @field_0, @field_1, @field_2 = field_0, field_1, field_2
  end
end

B.new("radio", 30, 5)
B.new("radio", 20, 5)
B.new("archie", 20, 5)
B.new("newton", 10, 3)

# default sort - :field_1, field_0, field_2 order, normal direction.
p B.enum{}.sort.collect(:field_0, :field_1, :field_2)
# => [["newton", 10, 3], ["archie", 20, 5], ["radio", 20, 5], ["radio",
30, 5]]

# sort by field_1 reverse, field_0 normal (same as the OP 3rd sort_by in
that thread).
p B.enum{}.sort(':field_1___', :field_0).collect(:field_0, :field_1,
:field_2)
# => [["radio", 30, 5], ["archie", 20, 5], ["radio", 20, 5], ["newton",
10, 3]]

# sort by field_0 reverse, field_2 normal, field_1 reverse.
p B.enum{}.sort(':field_0___', :field_2,
':field_1___').collect(:field_0, :field_1, :field_2)
# => [["radio", 30, 5], ["radio", 20, 5], ["newton", 10, 3], ["archie",
20, 5]]

Easy! Note the triple underscore suffix denotes reverse order sorting
for that parameter. But this code uses the evil eval.

js

···

--
Posted via http://www.ruby-forum.com/.

Yes, didn't work there either. Didn't think to try 1.8 - wonder why
the behaviour changed.

martin

···

On Wed, Oct 6, 2010 at 6:13 PM, Brian Candler <b.candler@pobox.com> wrote:

It might be an irb-ism in 1.9. Have you tried it in a standalone .rb
program?

Oddly enough, if x is defined beforehand you don't even need the binding:

ruby-1.9.2-p0 > x = 5
=> 5
ruby-1.9.2-p0 > eval "x = 42"
=> 42
ruby-1.9.2-p0 > x
=> 42

martin

···

On Thu, Oct 7, 2010 at 12:38 AM, John Sikora <john.sikora@xtera.com> wrote:

Hmmm, it seems to work when x is already defined (in 1.9.2):

irb(main):001:0> x=4
=> 4
irb(main):002:0> b=binding
=> #<Binding:0xfa2ab0>
irb(main):003:0> eval("x=5", b)
=> 5
irb(main):004:0> x
=> 5
irb(main):005:0>

If I do not set x to a value beforehand, I get the same error. Do not
know why though.

Note that "binding" is the default argument of eval if you don't specify
one, so capturing binding as a variable and passing it explicitly is
redundant.

···

On Wed, Oct 6, 2010 at 1:53 PM, Martin DeMello <martindemello@gmail.com>wrote:

On Thu, Oct 7, 2010 at 12:38 AM, John Sikora <john.sikora@xtera.com> > wrote:
> Hmmm, it seems to work when x is already defined (in 1.9.2):
>
> irb(main):001:0> x=4
> => 4
> irb(main):002:0> b=binding
> => #<Binding:0xfa2ab0>
> irb(main):003:0> eval("x=5", b)
> => 5
> irb(main):004:0> x
> => 5
> irb(main):005:0>
>
> If I do not set x to a value beforehand, I get the same error. Do not
> know why though.

Oddly enough, if x is defined beforehand you don't even need the binding:

ruby-1.9.2-p0 > x = 5
=> 5
ruby-1.9.2-p0 > eval "x = 42"
=> 42
ruby-1.9.2-p0 > x
=> 42

martin

--
Tony Arcieri
Medioh! A Kudelski Brand

Tony Arcieri wrote:

Note that "binding" is the default argument of eval if you don't specify
one, so capturing binding as a variable and passing it explicitly is
redundant.

You are correct but the problem 'persists'. The following irb session in
1.9.2 shows the same thing without the binding:

irb(main):001:0> eval("x=5")
=> 5
irb(main):002:0> x
NameError: undefined local variable or method `x' for main:Object
        from (irb):2
        from C:/Ruby192/bin/irb:12:in `<main>'
irb(main):003:0> x=4
=> 4
irb(main):004:0> eval("x=5")
=> 5
irb(main):005:0> x
=> 5
irb(main):006:0>

In order for this to work, the x needs to be @x per the following irb
session (again 1.9.2):

irb(main):001:0> eval("@x=5")
=> 5
irb(main):002:0> @x
=> 5
irb(main):003:0>

The behavior seen in the first irb session was the same in 1.8.6, 1.9.1,
and 1.9.2 when run standalone. However, it works in irb for 1.8.6 (x w/o
the @).

I have sometimes seen 'local' or 'unassociated' instance variables (not
sure if my terminology is correct) in example programs and I have
wondered why they are used. I guess this is one reason although most
people don't like to use eval.

I use eval in personal scripts since it is so convenient. Had to go back
and look at some code to remind myself how I got around the behavior
seen in the first irb session. I don't remember if I read about using
the local instance variable or just played around with it until I got it
to work.

js

···

--
Posted via http://www.ruby-forum.com/\.

Tony Arcieri wrote:

Note that "binding" is the default argument of eval if you don't specify
one, so capturing binding as a variable and passing it explicitly is
redundant.

You are correct but the problem 'persists'. The following irb session in
1.9.2 shows the same thing without the binding:

irb(main):001:0> eval("x=5")
=> 5
irb(main):002:0> x
NameError: undefined local variable or method `x' for main:Object
from (irb):2
from C:/Ruby192/bin/irb:12:in `<main>'
irb(main):003:0> x=4
=> 4
irb(main):004:0> eval("x=5")
=> 5
irb(main):005:0> x
=> 5
irb(main):006:0>

The reason is that Ruby looks at the source code to find out which are
local variables. This is basically the same as doing

13:00:57 ~$ ruby19 -e 'def f;puts "x=10";p x end;f'
x=10
-e:1:in `f': undefined local variable or method `x' for main:Object (NameError)
        from -e:1:in `<main>'
13:00:59 ~$ ruby19 -e 'def f;eval "x=10";p x end;f'
-e:1:in `f': undefined local variable or method `x' for main:Object (NameError)
        from -e:1:in `<main>'
13:01:02 ~$

In other words: the interpreter does not know that there is an
assignment to x in the eval code so x is undefined.

In order for this to work, the x needs to be @x per the following irb
session (again 1.9.2):

irb(main):001:0> eval("@x=5")
=> 5
irb(main):002:0> @x
=> 5
irb(main):003:0>

That's an instance variable which is a completely different beast.

The behavior seen in the first irb session was the same in 1.8.6, 1.9.1,
and 1.9.2 when run standalone. However, it works in irb for 1.8.6 (x w/o
the @).

irb has some specialities with handling local variables which is the
reason it behaves differently. The reason is that it will not parse
the code in one go because it only ever sees a chunk (every time you
hit enter). If the interpreter's regular behavior would be retained
you could not use local variables in irb which would be inconvenient.

I have sometimes seen 'local' or 'unassociated' instance variables (not
sure if my terminology is correct)

Not sure what you actually mean by that.

in example programs and I have
wondered why they are used. I guess this is one reason although most
people don't like to use eval.

The reasons against eval are more in the area of safety and
performance. If you know which variable you want to assign you can
use it directly. If you do not know you typically use a Hash.

I use eval in personal scripts since it is so convenient. Had to go back
and look at some code to remind myself how I got around the behavior
seen in the first irb session. I don't remember if I read about using
the local instance variable or just played around with it until I got it
to work.

What do you use eval for? In cases where the set of variables is not
fixed at coding time chances are that you usually want a Hash. Using
eval should be really be rare and for specific cases but not an
everyday habit IMHO.

Kind regards

robert

···

On Thu, Oct 7, 2010 at 4:19 AM, John Sikora <john.sikora@xtera.com> wrote:

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

Robert Klemme wrote:

I have sometimes seen 'local' or 'unassociated' instance variables (not
sure if my terminology is correct)

Not sure what you actually mean by that.

I mean that I have seen examples where people use, say '@var', outside
of any class, module, or method; in the 'main body' of the program. I
would think that they would just use 'var' instead. I do not see why it
would be advantageous to use '@var'. Is there a reason to do this?

I use eval in personal scripts since it is so convenient. Had to go back
and look at some code to remind myself how I got around the behavior
seen in the first irb session. I don't remember if I read about using
the local instance variable or just played around with it until I got it
to work.

What do you use eval for? In cases where the set of variables is not
fixed at coding time chances are that you usually want a Hash. Using
eval should be really be rare and for specific cases but not an
everyday habit IMHO.

I mainly use 'eval' to evaluate syntactic sugar expressions for
enumerable methods such as 'sort', 'find_all', 'all?', etc. I have
created a Module with enumerable classes (they have an 'each' method)
and I have added syntactic sugar to some of the methods. So I can be
lazy (and quick) and write things like:

Slot.enum.find_all(‘:slot_num >= 8’, ‘:slot_num <= 22’, “:rate ==
‘ufec’”).all?(‘:errors == 0’)

As you can probably guess, anything with a ':' before it is an attribute
that gets evaluated. Then the expression gets evaluated. I know I could
use ‘send’ for the first evaluation, but sometimes I chain together
attributes of attributes, use variables in the expressions, etc., and it
gets more complicated. 'eval' is my easy way out (I’m just a hardware
engineer trying to make my life easier). In case anyone is wondering,
the parameters given in 'find_all' have the option of an ‘and’ or ‘or’
function. Default is ‘and’ as in the example.

Speed is generally not an issue and I am somewhat aware of the dangers
of using 'eval'. I try to isolate the 'eval' expressions by limiting the
user (me, mostly) to commands that have pre-programmmed options. A
malicious user could probably find a way around it though since I am not
too savvy in this area.

I am always looking to improve the code and if anyone can suggest ways
of eliminating the 'eval' statements while keeping the same
functionality, I will certainly listen. I am also trying to read up on
the latest dynamic techniques that rely less upon 'eval'.

js

···

On Thu, Oct 7, 2010 at 4:19 AM, John Sikora <john.sikora@xtera.com> > wrote:

--
Posted via http://www.ruby-forum.com/\.

Robert Klemme wrote:

I have sometimes seen 'local' or 'unassociated' instance variables (not
sure if my terminology is correct)

Not sure what you actually mean by that.

I mean that I have seen examples where people use, say '@var', outside
of any class, module, or method; in the 'main body' of the program. I
would think that they would just use 'var' instead. I do not see why it
would be advantageous to use '@var'. Is there a reason to do this?

I use eval in personal scripts since it is so convenient. Had to go back
and look at some code to remind myself how I got around the behavior
seen in the first irb session. I don't remember if I read about using
the local instance variable or just played around with it until I got it
to work.

What do you use eval for? In cases where the set of variables is not
fixed at coding time chances are that you usually want a Hash. Using
eval should be really be rare and for specific cases but not an
everyday habit IMHO.

I mainly use 'eval' to evaluate syntactic sugar expressions for
enumerable methods such as 'sort', 'find_all', 'all?', etc. I have
created a Module with enumerable classes (they have an 'each' method)

So you have something like

class Foo
  def self.each
    yield "whatever"
    self
  end
end

? Did you also do

class Foo
  def self.each
    yield "whatever"
  end

  extend Enumerable
end

? That would give you all methods like #find_all etc. for free.

and I have added syntactic sugar to some of the methods. So I can be
lazy (and quick) and write things like:

Slot.enum.find_all(‘:slot_num >= 8’, ‘:slot_num <= 22’, “:rate ==
‘ufec’”).all?(‘:errors == 0’)

Is this equivalent to

enum.find_all {|s| (8..22) === s.slot_num && s.rate == 'ufec'}.all?
{|s| e.errors == 0}

? If yes, I do not really see the advantage of your approach. It's
slower needs a similar amount of typing and will detect errors later
(at execution time vs. at parse time).

As you can probably guess, anything with a ':' before it is an attribute
that gets evaluated. Then the expression gets evaluated. I know I could
use ‘send’ for the first evaluation, but sometimes I chain together
attributes of attributes, use variables in the expressions, etc., and it
gets more complicated. 'eval' is my easy way out (I’m just a hardware
engineer trying to make my life easier). In case anyone is wondering,
the parameters given in 'find_all' have the option of an ‘and’ or ‘or’
function. Default is ‘and’ as in the example.

If my assumption above is correct I do not understand where the "easy
way out" is.

Speed is generally not an issue and I am somewhat aware of the dangers
of using 'eval'. I try to isolate the 'eval' expressions by limiting the
user (me, mostly) to commands that have pre-programmmed options. A
malicious user could probably find a way around it though since I am not
too savvy in this area.

I am always looking to improve the code and if anyone can suggest ways
of eliminating the 'eval' statements while keeping the same
functionality, I will certainly listen. I am also trying to read up on
the latest dynamic techniques that rely less upon 'eval'.

(see above)

Kind regards

robert

···

On Fri, Oct 8, 2010 at 6:06 AM, John Sikora <john.sikora@xtera.com> wrote:

On Thu, Oct 7, 2010 at 4:19 AM, John Sikora <john.sikora@xtera.com> >> wrote:

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

I don't like to top post even a little bit, but I want to apologize for
the long post (don't really like attachements), and I'm sorry if the way
I use Ruby makes some people cringe, but Ruby really makes my job easier
for everything from testing hardware units to comparing and sorting
bills of material, etc. Some people may want to skip to the end where
there are some examples.

Robert Klemme wrote:

So you have something like

class Foo
  def self.each
    yield "whatever"
    self
  end
end

? Did you also do

class Foo
  def self.each
    yield "whatever"
  end

  extend Enumerable
end

? That would give you all methods like #find_all etc. for free.

Robert,
Note that I have kept the code and comments to a minimum here as I did
not want the post to be even longer. I know that you are quite
knowledgeable and that you should have little trouble following what I
am doing - although you might not necessarily understand why I chose to
do it this way:

class Base
  @class_all_enum_objects = Array.new

  class << self
    attr_accessor :class_all_enum_objects
  end

  def initialize
    self.class.class_all_enum_objects << self
  end

  def self.enum
     EnumeratorEOC.new(self, :each)
  end

  def self.each(&code_block)
    self.class_all_enum_objects.each do |object|
      yield object
    end
  end
end

module EnumeratorModifierEOC
  def find_all(*condition_strings, &code_block)
     if block_given?
       find_array = super
     else
       find_array = # process condition strings (syntactic sugar) here.
     end
     ArrayEOC.new(find_array)
  end

  # code for other methods such as sort, min, max, all?, etc. here.
end

class EnumeratorEOC < Enumerator # avoids modifying Enumerator.
  include EnumeratorModifierEOC

  def initilize(object, method_sym)
    super
  end
end

class ArrayEOC < Array # avoids modifying Array.
  include EnumeratorModifierEOC

  def initialize(array = )
    super(array)
  end
end

To suit my purposes: 1) I want the class itself to keep track of all of
the class instances, as shown above, 2) if an attribute has more than
one value or is more complex than an Array, I prefer to define a class
for it (inherited from Base) and assign attributes, 3) I want syntactic
sugar for enumerables, 4) especially for sort, including default sort
parameters (min and max get defaults for free as a result of the
necessary <=> definition). 5) I want to be able to define new functions
and to be able to use syntactic sugar with them. See below. 6) I also
keep track of descendants of classes although this is only partly shown
below. There is a little more on this at the end if anyone is still
reading.

and I have added syntactic sugar to some of the methods. So I can be
lazy (and quick) and write things like:

Slot.enum.find_all(‘:slot_num >= 8’, ‘:slot_num <= 22’, “:rate ==
‘ufec’”).all?(‘:errors == 0’)

Is this equivalent to

enum.find_all {|s| (8..22) === s.slot_num && s.rate == 'ufec'}.all?
{|s| e.errors == 0}

Yes, you are correct (e.errors is a typo - should be s.errors), they are
equivalent.

? If yes, I do not really see the advantage of your approach. It's
slower needs a similar amount of typing and will detect errors later
(at execution time vs. at parse time).
>
If my assumption above is correct I do not understand where the "easy
way out" is.

I think 'sort' is the main reason for my 'easy way out'. Plus the use of
additional functions. To illustrate, I have included in the code below
one such function, 'sum_total_eoc' which is just 'inject' set up for
addition, but with the ability to use syntactic sugar (of course).

You may or may not agree with my 'easy way out', but here goes:

First, a short explanation: I have attribute and expression comparables
for each class. The comparables are compared in the order that they are
given in attr_accessor. There is also a class method called
set_comparables_order that allows the user to re-arrange the order if
desired (it also allows expressions, not just attributes). Child classes
inherit the comparables and their order from the parent class. Any new
attributes defined in attr_accessor in the child class are added to the
comparables up front.

To do this, the following is added to the code above (stripped of error
checking, etc. to avoid clutter):

class Base
  @class_comparables = Array.new

  class << self
    attr_accessor :class_comparables
  end

  self.inherited(child_class)
    child_class.class_all_enum_objects = Array.new # init for the new
class.
    child_class.class_comparables = self.class_comparables # inherit.
  end

  def self.attr_accessor(*accessor_names)
    self.class_comparables.replace(accessor_names +
self.class_comparables).uniq
    super
    # also update descendants' class_comparables if needed.
  end

  def self.set_comparables_order(*attr_and_expr)
    self.class_comparables.replace(attr_and_expr +
self.class_comparables).uniq
   # also update descendants' class_comparables if needed.
  end

  def self.enum
     EnumeratorEOC.new(self, :each, class_comparables) # added
class_comparables
  end

  def <=>(other)
     # use the comparables list here.
  end
end

class EnumeratorEOC
  def initilize(object, method_sym, comparables)
    @comparables = comparables
    super(object, method_sym)
  end
end

class ArrayEOC
   def initialize(array = , comparables)
    @comparables = comparables
    super(array)
  end

  def sum_total_eoc(*params_exprs) # added this function.
    self.inject(0) do |sum, object|
      # process params_exprs syntactic sugar on object here.
    end
  end
end

module EnumeratorModifierEOC
  def sort(*params_exprs, &code_block) # sho w sort, since it is of
interest.
    if block_given?
      return_array = super
    else
      return_array = # process params_exprs including setting
comparables.
    end
    ArrayEOC.new(return_array, comparables) # comparables propagate.
   end
end

···

On Fri, Oct 8, 2010 at 6:06 AM, John Sikora <john.sikora@xtera.com> > wrote:

--------------
OK, so what can I do with this? The following are some simplified
examples:

class Slot < EnumerableObjectClass::Base
  attr_accessor :slot_num, :rate, :error_info # <--Array of ErrorInfo
objects.

  def initialize(slot_num, rate, error_info)
    super()
    @slot_num, @rate, @error_info = slot_num, rate, error_info
  end
end

class ErrorInfo < EnumerableObjectClass::Base
  attr_accessor :time_stamp, :num_errors

  def initialize(time_stamp, num_errors)
    super()
    @time_stamp, @num_errors = time_stamp, num_errors
  end
end

Slot.new(3, 'efec', [ErrorInfo.new('10-07-10', 500),
ErrorInfo.new('10-08-10', 1000)])
Slot.new(1, 'ufec', [ErrorInfo.new('10-04-10', 1000),
ErrorInfo.new('10-05-10', 2000)])
Slot.new(8, 'ufec', [ErrorInfo.new('10-07-10', 1500),
ErrorInfo.new('10-08-10', 1200)])
Slot.new(4, 'efec', [ErrorInfo.new('10-07-10', 3500)])
Slot.enum{}.find(':slot_num == 4').error_info <<
ErrorInfo.new('10-08-10', 2500)

-------------
# note - most of the examples below do not use variables. However, this
is not a limitation and a couple of examples are given that use
variables.

# default sort is sorted by :slot_num, etc. - order of attr_accessor
p Slot.enum.sort.collect(:slot_num)
# => [[1], [3], [4], [8]]

# sort by total errors.
p
Slot.enum.sort(':error_info.sum_total_eoc(:num_errors)').collect(:rate,
':error_info.sum_total_eoc(:num_errors)')
# => [["efec", 1500], ["ufec", 2700], ["ufec", 3000], ["efec", 6000]]

# sort by rate, total errors.
p Slot.enum.sort(:rate,
':error_info.sum_total_eoc(:num_errors)').collect(:rate,
':error_info.sum_total_eoc(:num_errors)')
# => [["efec", 1500], ["efec", 6000], ["ufec", 2700], ["ufec", 3000]]

# sort by rate, total errors, with total errors sorted high to low by
ending with '___' triple underscore.
p Slot.enum.sort(:rate,
':error_info.sum_total_eoc(:num_errors)___').collect(:rate,
':error_info.sum_total_eoc(:num_errors)', :slot_num)
# => [["efec", 6000, 4], ["efec", 1500, 3], ["ufec", 3000, 1], ["ufec",
2700,8]]

I think that the above would be hard to do in a one-liner w/o syntactic
sugar, so this is my 'easy way out' (although I would not be surprised
if you came up with one Robert). I think this is pretty easy and it
saves me time.

# total errors of all slots.
p Slot.enum.sum_total_eoc(':error_info.sum_total_eoc(:num_errors)')
# => 13200

# slots that have >= 3000 total errors.
p Slot.enum.find_all(':error_info.sum_total_eoc(:num_errors) >=
3000').collect(:slot_num)
# => [[1], [4]]

# use a variable for sort parameter.
var = ':slot_num'
p Slot.enum.sort("#{var}").collect("#{var}")
# => [[1], [3], [4], [8]]

# use a variable w/o interpolation - must pass a binding with enum{}
(which is where this topic started by the way). It is carried along as
an attribute to ArrayEOC and/or EnumeratorEOC, similar to coparables.
var = ':slot_num'
p Slot.enum{}.sort(var).collect(var)
# => [[1], [3], [4], [8]]

# slots that have >= 2500 errors in a single time period.
p Slot.enum.find_all(":error_info.any?(:num_errors >=
2500)").collect(:slot_num)
# => Exceptions galore! my simple 'parser' breaks down. will have to use
the 'old fashioned way' of a code block. maybe I will try to get this to
work in the future.

In case anyone is wondering, if data is input as an Array, it comes out
as an ArrayEOC the first time it is read. In the simplified code above,
I show a call to super in attr_accessor, but really for the reader part,
I add a line that converts an Array to an ArrayEOC. I do this so I can
use the ArrayEOC methods as mofified since I want to keep Array's
methods unmodified.

Since I already use self.inherited, I am able to keep track of
descendants (a recursive jaunt through the child classes using inject to
build an array), so there is enum_only{} which enumerates only the given
class, and enum{} which enumerates the given class and all descendants.
I do this because I have a number of units of similar type which follow
the classic inheritance OO model (even though it is apparent from posts
that this has fallen out of favor). Oh well, it seems that I am behind
the times on a lot of things.

Oh, and since Ruby is dynamic, I had to modify remove_method and
undefine_method to remove any comparables, if applicable (there is a
difference between the two however). And if there are subsequent calls
to attr_accessor, I have to add those methods to the class comparables
in the class and any descendant classes. I allow the user to reset the
comparables to those given in the class and its ancestors. I also tried
to make the code reflective, by making the comparables, child_classes,
descndants, etc. availble. Writing this code has certainly been fun and
educational!

js

--
Posted via http://www.ruby-forum.com/\.

Robert Klemme wrote:

So you have something like

class Foo
   def self.each
     yield "whatever"
     self
   end
end

? Did you also do

class Foo
   def self.each
     yield "whatever"
   end

   extend Enumerable
end

? That would give you all methods like #find_all etc. for free.

Robert,
Note that I have kept the code and comments to a minimum here as I did
not want the post to be even longer. I know that you are quite
knowledgeable and that you should have little trouble following what I
am doing - although you might not necessarily understand why I chose to
do it this way:

Fluent speaking of Ruby makes it somewhat easier but even for me the length of the post needs some digesting. So I won't come up with a full coverage of everything you wrote for now. I hope my remarks are useful nevertheless.

class Base
   @class_all_enum_objects = Array.new

   class<< self
     attr_accessor :class_all_enum_objects
   end

   def initialize
     self.class.class_all_enum_objects<< self
   end

   def self.enum
      EnumeratorEOC.new(self, :each)
   end

   def self.each(&code_block)
     self.class_all_enum_objects.each do |object|
       yield object
     end
   end
end

module EnumeratorModifierEOC

This should rather be a EnumerableModifierEOC IMHO since #find is defined in Enumerable and this module contains all the enumerating methods which are based on #each (e.g. find_all, inject, map).

   def find_all(*condition_strings,&code_block)
      if block_given?
        find_array = super
      else
        find_array = # process condition strings (syntactic sugar) here.
      end
      ArrayEOC.new(find_array)
   end

   # code for other methods such as sort, min, max, all?, etc. here.
end

class EnumeratorEOC< Enumerator # avoids modifying Enumerator.
   include EnumeratorModifierEOC

   def initilize(object, method_sym)
     super
   end
end

class ArrayEOC< Array # avoids modifying Array.
   include EnumeratorModifierEOC

   def initialize(array = )
     super(array)
   end
end

Although no additional methods are shown (these are presented below if I am not mistaken) the purpose of EnumeratorEOC and ArrayEOC is mainly to augment standard types with additional functionality it seems. Correct?

To suit my purposes: 1) I want the class itself to keep track of all of
the class instances, as shown above, 2) if an attribute has more than
one value or is more complex than an Array, I prefer to define a class
for it (inherited from Base) and assign attributes, 3) I want syntactic
sugar for enumerables, 4) especially for sort, including default sort
parameters (min and max get defaults for free as a result of the
necessary<=> definition). 5) I want to be able to define new functions
and to be able to use syntactic sugar with them. See below. 6) I also
keep track of descendants of classes although this is only partly shown
below. There is a little more on this at the end if anyone is still
reading.

and I have added syntactic sugar to some of the methods. So I can be
lazy (and quick) and write things like:

Slot.enum.find_all(�:slot_num>= 8�, �:slot_num<= 22�, �:rate ==
�ufec��).all?(�:errors == 0�)

Is this equivalent to

enum.find_all {|s| (8..22) === s.slot_num&& s.rate == 'ufec'}.all?
{|s| e.errors == 0}

Yes, you are correct (e.errors is a typo - should be s.errors), they are
equivalent.

Sorry, that was a typo. Thanks for catching that.

? If yes, I do not really see the advantage of your approach. It's
slower needs a similar amount of typing and will detect errors later
(at execution time vs. at parse time).

If my assumption above is correct I do not understand where the "easy
way out" is.

I think 'sort' is the main reason for my 'easy way out'. Plus the use of
additional functions. To illustrate, I have included in the code below
one such function, 'sum_total_eoc' which is just 'inject' set up for
addition, but with the ability to use syntactic sugar (of course).

In the code above I do not see any usage of #sort so I don't really see the easy way out in the example above. Even if you would need the String argument eval solution for sorting it would not be necessary for the code above which uses #find_all and #all?.

You may or may not agree with my 'easy way out', but here goes:

First, a short explanation: I have attribute and expression comparables
for each class. The comparables are compared in the order that they are
given in attr_accessor. There is also a class method called
set_comparables_order that allows the user to re-arrange the order if
desired (it also allows expressions, not just attributes). Child classes
inherit the comparables and their order from the parent class. Any new
attributes defined in attr_accessor in the child class are added to the
comparables up front.

A problem of your design is that you change the classes idea of default ordering. What I mean is this: you need to modify a class to achieve a particular ordering. Now the standard behavior of sorting has changed and you can never tell what ordering you will get by only looking at a particular piece of code which only contains the call to #sort.

IMHO it would be better to not allow this but instead create a mechanism which makes the ordering explicit. Here is one approach

module EnumeratorModifierEOC # or Enumerable
   def sort_by_fields(*fields)
     sort_by {|x| fields.map {|f| x.send(f)}}
   end
end

This might not be the most efficient solution because of the repeated invocation of #map. Alternatively you can easily stick with #sort_by by doing

enum.sort_by {|x| [x.field_1, x.field_2]}

which is pretty short, readable and efficient.

You could improve this by creating something like a Sorter:

def Sorter(*fields)
   eval "lambda {|enum| enum.sort_by{|x| [#{fields.map {|f| "x.#{f}"}.join(', ')}]}}"
end

MAIN = Sorter(:foo, :bar)
...
sorted = MAIN[enum]

Now you actually have your eval but you use it to compile code which you can then use more often efficiently. :slight_smile:

You can even make this more modular by removing the sorting and compiling only the field extraction:

def FieldExtractor(*field)
   eval "lambda {|x| [#{fields.map {|f| "x.#{f}"}.join(', ')}]}"
end

MAIN = FieldExtractor(:foo, :bar)
...
sorted = enum.sort_by &MAIN

OK, so what can I do with this? The following are some simplified
examples:

class Slot< EnumerableObjectClass::Base
   attr_accessor :slot_num, :rate, :error_info #<--Array of ErrorInfo
objects.

   def initialize(slot_num, rate, error_info)
     super()
     @slot_num, @rate, @error_info = slot_num, rate, error_info
   end
end

class ErrorInfo< EnumerableObjectClass::Base
   attr_accessor :time_stamp, :num_errors

   def initialize(time_stamp, num_errors)
     super()
     @time_stamp, @num_errors = time_stamp, num_errors
   end
end

Slot.new(3, 'efec', [ErrorInfo.new('10-07-10', 500),
ErrorInfo.new('10-08-10', 1000)])
Slot.new(1, 'ufec', [ErrorInfo.new('10-04-10', 1000),
ErrorInfo.new('10-05-10', 2000)])
Slot.new(8, 'ufec', [ErrorInfo.new('10-07-10', 1500),
ErrorInfo.new('10-08-10', 1200)])
Slot.new(4, 'efec', [ErrorInfo.new('10-07-10', 3500)])
Slot.enum{}.find(':slot_num == 4').error_info<<
ErrorInfo.new('10-08-10', 2500)

-------------
# note - most of the examples below do not use variables. However, this
is not a limitation and a couple of examples are given that use
variables.

# default sort is sorted by :slot_num, etc. - order of attr_accessor
p Slot.enum.sort.collect(:slot_num)
# => [[1], [3], [4], [8]]

# sort by total errors.
p
Slot.enum.sort(':error_info.sum_total_eoc(:num_errors)').collect(:rate,
':error_info.sum_total_eoc(:num_errors)')
# => [["efec", 1500], ["ufec", 2700], ["ufec", 3000], ["efec", 6000]]

# sort by rate, total errors.
p Slot.enum.sort(:rate,
':error_info.sum_total_eoc(:num_errors)').collect(:rate,
':error_info.sum_total_eoc(:num_errors)')
# => [["efec", 1500], ["efec", 6000], ["ufec", 2700], ["ufec", 3000]]

# sort by rate, total errors, with total errors sorted high to low by
ending with '___' triple underscore.
p Slot.enum.sort(:rate,
':error_info.sum_total_eoc(:num_errors)___').collect(:rate,
':error_info.sum_total_eoc(:num_errors)', :slot_num)
# => [["efec", 6000, 4], ["efec", 1500, 3], ["ufec", 3000, 1], ["ufec",
2700,8]]

I think that the above would be hard to do in a one-liner w/o syntactic
sugar, so this is my 'easy way out' (although I would not be surprised
if you came up with one Robert). I think this is pretty easy and it
saves me time.

The tricky thing I did not consider yet is that you have nested access to fields and not just one level. In my world this would look like

p Slot.enum.sort_by {|x| [x.rate, x.error_info.sum_total_eoc(:num_errors)___]}.
map {|x| [x.:rate, x.error_info.sum_total_eoc(:num_errors), x.slot_num]}

That's certainly not nice to squeeze on one line but your solution does not look that much shorter.

# total errors of all slots.
p Slot.enum.sum_total_eoc(':error_info.sum_total_eoc(:num_errors)')
  # => 13200

# slots that have>= 3000 total errors.
p Slot.enum.find_all(':error_info.sum_total_eoc(:num_errors)>=
3000').collect(:slot_num)
# => [[1], [4]]

# use a variable for sort parameter.
var = ':slot_num'
p Slot.enum.sort("#{var}").collect("#{var}")
# => [[1], [3], [4], [8]]

# use a variable w/o interpolation - must pass a binding with enum{}
(which is where this topic started by the way). It is carried along as
an attribute to ArrayEOC and/or EnumeratorEOC, similar to coparables.
var = ':slot_num'
p Slot.enum{}.sort(var).collect(var)
# => [[1], [3], [4], [8]]

# slots that have>= 2500 errors in a single time period.
p Slot.enum.find_all(":error_info.any?(:num_errors>=
2500)").collect(:slot_num)
# => Exceptions galore! my simple 'parser' breaks down. will have to use
the 'old fashioned way' of a code block. maybe I will try to get this to
work in the future.

In case anyone is wondering, if data is input as an Array, it comes out
as an ArrayEOC the first time it is read. In the simplified code above,
I show a call to super in attr_accessor, but really for the reader part,
I add a line that converts an Array to an ArrayEOC. I do this so I can
use the ArrayEOC methods as mofified since I want to keep Array's
methods unmodified.

Since I already use self.inherited, I am able to keep track of
descendants (a recursive jaunt through the child classes using inject to
build an array), so there is enum_only{} which enumerates only the given
class, and enum{} which enumerates the given class and all descendants.
I do this because I have a number of units of similar type which follow
the classic inheritance OO model (even though it is apparent from posts
that this has fallen out of favor). Oh well, it seems that I am behind
the times on a lot of things.

Oh, and since Ruby is dynamic, I had to modify remove_method and
undefine_method to remove any comparables, if applicable (there is a
difference between the two however). And if there are subsequent calls
to attr_accessor, I have to add those methods to the class comparables
in the class and any descendant classes. I allow the user to reset the
comparables to those given in the class and its ancestors. I also tried
to make the code reflective, by making the comparables, child_classes,
descndants, etc. availble.

Your need to redefine remove_method etc. is fallout of your design decision to change the default ordering. As I said, I believe there are better and more efficient designs.

Writing this code has certainly been fun and
educational!

That's good!

There are still some things that I didn't yet wrap my head around: why do you want to make classes keep track of all their instances? This essentially makes classes global variables - but less obvious. During the course of a program usually you create instances and let forget them again. But in your situation you will keep all instances around and there is no clear demarcation. If you want automated tracking of instances there are other options, e.g.

class InstanceTracker
   def initialize
     @all = Hash.new {|h,k| h[k] = }
   end

   def new(cl,*args, &b)
     @all[cl] << cl.new(*args, &b)
   end

   def clear
     @all.clear
     self
   end

   def clear_class(cl)
     @all.delete(cl)
     self
   end

   include Enumerable

   def each(&b)
     if b
       @all.each {|k,v| v.each(&b)}
       self
     else
       Enumerator.new(self, :each)
     end
   end

   def each_class(cl, &b)
     if b
       @all[cl].each(&b)
       self
     else
       Enumerator.new(self, :each_class, cl)
     end
   end

   def each_sub_classes(cl, &b)
     if b
       @all.each {|k,v| v.each(&b) if k <= cl}
       self
     else
       Enumerator.new(self, :each_sub_classes, cl)
     end
   end
end

it = InstanceTracker.new

it.new(Foo, 1, 2)
it.each_class(Foo).find_all {|f| f.size > 0}

etc.

Kind regards

  robert

···

On 09.10.2010 08:25, John Sikora wrote:

On Fri, Oct 8, 2010 at 6:06 AM, John Sikora<john.sikora@xtera.com> >> wrote:

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

Robert Klemme wrote in post #947187:

Fluent speaking of Ruby makes it somewhat easier but even for me the
length of the post needs some digesting. So I won't come up with a full
coverage of everything you wrote for now. I hope my remarks are useful
nevertheless.

Your remarks are always useful, and I appreciate them.

module EnumeratorModifierEOC

This should rather be a EnumerableModifierEOC IMHO since #find is
defined in Enumerable and this module contains all the enumerating
methods which are based on #each (e.g. find_all, inject, map).

True, especially since I also include the module in a subclass of Array,
not just a subclass of Enumerator.

Although no additional methods are shown (these are presented below if I
am not mistaken) the purpose of EnumeratorEOC and ArrayEOC is mainly to
augment standard types with additional functionality it seems. Correct?

Correct. All of the modified methods such as sort, find_all, collect,
all?, etc. are in EnumeratorModifierEOC although I presented only one or
two methods. I originally had modified Array and Enumerator and aliased
the methods for normal code block use (non syntactic sugar), but after
some correspondence in this forum decided that it was not a good idea to
do that. So now I modify the methods in a subclass and call super to
pass on the code block if syntactic sugar (passed parameter string(s) or
symbol(s)) is not used.

A problem of your design is that you change the classes idea of default
ordering. What I mean is this: you need to modify a class to achieve a
particular ordering. Now the standard behavior of sorting has changed...

I do not need to modify a class to achieve a particular ordering, I need
to define a class. The default ordering is set during the class
definition with attr_accessor and set_comparables_order. However, if I
do modify the class (with the same two methods), the default ordering
will change.

Or did you mean that a subclass can / will have a different default sort
order than it's superclass? This can certainly be the case and is
actually encouraged to add flexibility. Are these element of a bad
design? If so, why? (You can be brief, hopefully I will understand.)

...and you can never tell what ordering you will get by only looking at a
particular piece of code which only contains the call to #sort.

True, you would have to look at the attr_accessor and
set_comparables_order methods elsewhere in the code (possibly multiple
places). If these have been modified dynamically, the
ClassName#comparables method can be used to return the default
comparables.

enum.sort_by {|x| [x.field_1, x.field_2]}

which is pretty short, readable and efficient.

When I was looking over the Enumerable methods that I wanted to modify,
I skipped sort_by for some reason. You are right, it is pretty short and
readable. May need to take another look at it.

Your need to redefine remove_method etc. is fallout of your design
decision to change the default ordering. As I said, I believe there are
better and more efficient designs.

True. I said this to make the point that I have thought of things that I
need to do to try to keep things from breaking since Ruby is dynamic. I
think you are saying that by coding this way, I am making it tough on
myself since Ruby is dynamic. Hmmm, need to think about this, because I
know that there will be cases out there that I do not think to cover. I
guess this is a way to tell a good design from a bad one.

There are still some things that I didn't yet wrap my head around: why
do you want to make classes keep track of all their instances?

When I was learing Ruby, I came across ObjectSpace.each_object and
thought that since Ruby makes this method available, why not use it
instead of setting up my own containers? So early versions of the code
used ObjectSpace. Then I discovered self.inherited and class instance
variables. I decided to use self.inherited to pass along the values of
certain class instance variables that I want inherited (and slightly
modified for that subclass). Since keeping track of child classes was
fairly easy with self.inherited and I could also use it to initialize
@class_all_enum_objects, I dropped the use of ObjectSpace. It seems like
ObjectSpace would be less efficient too.

This essentially makes classes global variables - but less obvious.

Never thought of that. See my comments below on my lack of having to
interface with other users' code.

During the course of a program usually you create instances and let forget them
again. But in your situation you will keep all instances around and
there is no clear demarcation.

I do provide a delete method that will delete an EOC object from the
@class_all_enum_objects class instance variable. I also even provide a
recursive delete object method which deletes the EOC object and any EOC
object that it finds as an attribute (for example, a Slot object can
have an attribute :xp which is an Xp object (transponder, in case you
are wondering).

If you want automated tracking of
instances there are other options, e.g.

class InstanceTracker
  ... lines of code
> end

it = InstanceTracker.new

it.new(Foo, 1, 2)
it.each_class(Foo).find_all {|f| f.size > 0}

I see, but why have a seperate class for this? Aren't you doing the same
thing?

I think that the reason that my code is the way it is, is partially due
to the fact that I am writing my code in isolation; I do
not have to interface to other code to perform a broader function. In
fact, I have no experience writing code with any kind of interface
(explains a lot, huh?). Well, I guess I do use gems, so I am not in
total isolation, and at least I have given it some thought since I
turned away from mofidying Array and Enumerator directly.

js

···

--
Posted via http://www.ruby-forum.com/\.

In case you get two copies of this: First send attempt was returned by
a spamassassin crash...

Robert Klemme wrote in post #947187:

Fluent speaking of Ruby makes it somewhat easier but even for me the
length of the post needs some digesting. So I won't come up with a full
coverage of everything you wrote for now. I hope my remarks are useful
nevertheless.

Your remarks are always useful, and I appreciate them.

Thanks for the feedback!

A problem of your design is that you change the classes idea of default
ordering. What I mean is this: you need to modify a class to achieve a
particular ordering. Now the standard behavior of sorting has changed...

I do not need to modify a class to achieve a particular ordering, I need
to define a class. The default ordering is set during the class
definition with attr_accessor and set_comparables_order. However, if I
do modify the class (with the same two methods), the default ordering
will change.

That's what I mean. Maybe renaming "set_comparables" to something
else is enough (e.g. "attr_sort_order" which IMHO makes it look more
like a keyword like "private"). The name "set_comparables" seems to
suggest that the order can be changed at will. People might be
inclined to do that multiple times in a program and since at any point
in time there is only one such order per class this also poses issues
for multithreaded programs (assuming different ordering would be
employed by different threads). An alternative would be to allow to
call set_comparables at most once per class.

Or did you mean that a subclass can / will have a different default sort
order than it's superclass? This can certainly be the case and is
actually encouraged to add flexibility. Are these element of a bad
design? If so, why? (You can be brief, hopefully I will understand.)

No, the issue I am seeing has nothing to do with inheritance. See above.

...and you can never tell what ordering you will get by only looking at a
particular piece of code which only contains the call to #sort.

True, you would have to look at the attr_accessor and
set_comparables_order methods elsewhere in the code (possibly multiple
places). If these have been modified dynamically, the
ClassName#comparables method can be used to return the default
comparables.

My point is that sort order is something that belongs to a particular
ordering, i.e. the place in code that uses sorting. If you make this
a property of the class which is allowed to change you open your
application for all sorts of nasty effects caused by the fact that
different pieces of code (not necessarily in separate threads) use
that "global variable" in different ways.

Your need to redefine remove_method etc. is fallout of your design
decision to change the default ordering. As I said, I believe there are
better and more efficient designs.

True. I said this to make the point that I have thought of things that I
need to do to try to keep things from breaking since Ruby is dynamic. I
think you are saying that by coding this way, I am making it tough on
myself since Ruby is dynamic. Hmmm, need to think about this, because I
know that there will be cases out there that I do not think to cover. I
guess this is a way to tell a good design from a bad one.

:slight_smile:

There are still some things that I didn't yet wrap my head around: why
do you want to make classes keep track of all their instances?

When I was learing Ruby, I came across ObjectSpace.each_object and
thought that since Ruby makes this method available, why not use it
instead of setting up my own containers? So early versions of the code
used ObjectSpace. Then I discovered self.inherited and class instance
variables. I decided to use self.inherited to pass along the values of
certain class instance variables that I want inherited (and slightly
modified for that subclass). Since keeping track of child classes was
fairly easy with self.inherited and I could also use it to initialize
@class_all_enum_objects, I dropped the use of ObjectSpace. It seems like
ObjectSpace would be less efficient too.

This essentially makes classes global variables - but less obvious.

Never thought of that. See my comments below on my lack of having to
interface with other users' code.

I am not sure whether this is only related to interfacing with foreign
code. Basically my main theme is modularity. By tying tracking
functionality into the classes your design is a tad less modular. One
consequence of this is that since each class is a singleton you can
only ever track all instances of a class in one place. Assuming your
application grows and you need this tracking in different places but
independently you are screwed. If you separate the tracking as I have
tried to demonstrate it's as easy as creating another InstanceTracker.

If you want automated tracking of
instances there are other options, e.g.

class InstanceTracker
... lines of code
> end

it = InstanceTracker.new

it.new(Foo, 1, 2)
it.each_class(Foo).find_all {|f| f.size > 0}

I see, but why have a seperate class for this? Aren't you doing the same
thing?

From a functionality point of view, yes. But I choose to distribute
the functionality in a different (more modular) way across language
artifacts (classes and methods). Separating concerns is an important
task of a software engineer: all the time when coding we decide where
we place functionality. For small scripts it's OK to lump everything
together. If applications grow you often have to go through a painful
refactoring process to untangle different aspects. If you start out
modular you _may_ have increased effort initially but it pays off mid
to long term. Also, it _can_ help to make code more readable.

I think that the reason that my code is the way it is, is partially due
to the fact that I am writing my code in isolation; I do
not have to interface to other code to perform a broader function. In
fact, I have no experience writing code with any kind of interface
(explains a lot, huh?). Well, I guess I do use gems, so I am not in
total isolation, and at least I have given it some thought since I
turned away from mofidying Array and Enumerator directly.

Well, that's perfectly OK. Software is soft and so we change it over
time. Also, we as humans learn while we go along. My coding
certainly has changed over the years. That's only natural. And
discussing things like these helps in thinking differently about code.
It's a creative process that increases knowledge on all sides.

Kind regards

robert

···

On Wed, Oct 13, 2010 at 9:23 PM, John Sikora <john.sikora@xtera.com> wrote:

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