Class that opens and adds variables to instances of another class?


(Leam Hall) #1

I think I'm trying to figure out the Decorator pattern. What I want is to have a <thing> that takes an instance of <something> and changes it. The changes can include modifying and adding instance variables.

Haven't quite figured it out yet, thoughts?

Thanks!

Leam

Here's the error:

···

###
Traceback (most recent call last):
  3: from test_decorator.rb:28:in `<main>'
  2: from test_decorator.rb:28:in `new'
  1: from test_decorator.rb:17:in `initialize'
test_decorator.rb:20:in `assign_role': undefined method `role=' for #<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
###

Code:
###
   1 class Person
   2 def initialize(data)
   3 @data = data
   4 end
   5 def name
   6 @data[:name] || "Fred"
   7 end
   8 end
   9
  10 class Role
  11 class Person
  12 attr_accessor :role
  13 end
  14
  15 def initialize(data)
  16 @person = data[:person]
  17 assign_role(data[:role])
  18 end
  19 def assign_role(role)
  20 @person.role = role
  21 end
  22 end
  23
  24 data = {:name => 'Al'}
  25
  26 al = Person.new(data)
  27 role_data = {:person => al, :role => 'Cadet'}
  28 Role.new(role_data)
  29 puts al.name
  30 #al.role = "Cadet Sergeant"
###


(Robert K.) #2

I would model this differently: a Person has many Roles. A Role does
not record all the persons it is assigned to. Rather, if there is
logic to be executed in the Role a Person is passed in (incomplete
example):

class Person
  attr_accessor :roles

  def initialize(data)
    @data = data
    @roles = []
  end

  def add_role(r)
    @roles.include?(r) or @roles << r
    self
  end
end

class Role
  def assign(person)
    person.add_role(self)
  end

  def jump(person)
    raise "Not a valid combination #{self} - #{person}" unless
person.roles.include?(self)
    puts "#{person.name} jumps #{person.strength * 5}m high!"

  end
end

My initial reaction to the word "decorator" was to use SimpleDelegator
but that does not work well for multiple roles which can be added and
removed.

Kind regards

robert

···

On Thu, Jul 19, 2018 at 11:59 AM Leam Hall <leamhall@gmail.com> wrote:

I think I'm trying to figure out the Decorator pattern. What I want is
to have a <thing> that takes an instance of <something> and changes it.
The changes can include modifying and adding instance variables.

Haven't quite figured it out yet, thoughts?

Thanks!

Leam

Here's the error:
###
Traceback (most recent call last):
        3: from test_decorator.rb:28:in `<main>'
        2: from test_decorator.rb:28:in `new'
        1: from test_decorator.rb:17:in `initialize'
test_decorator.rb:20:in `assign_role': undefined method `role=' for
#<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
###

Code:
###
   1 class Person
   2 def initialize(data)
   3 @data = data
   4 end
   5 def name
   6 @data[:name] || "Fred"
   7 end
   8 end
   9
  10 class Role
  11 class Person
  12 attr_accessor :role
  13 end
  14
  15 def initialize(data)
  16 @person = data[:person]
  17 assign_role(data[:role])
  18 end
  19 def assign_role(role)
  20 @person.role = role
  21 end
  22 end
  23
  24 data = {:name => 'Al'}
  25
  26 al = Person.new(data)
  27 role_data = {:person => al, :role => 'Cadet'}
  28 Role.new(role_data)
  29 puts al.name
  30 #al.role = "Cadet Sergeant"
###

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/


(Paul Martensen) #3

Hey!

I think the problem is that

class Role
class Person
end
end

Defines a new constant Role::Person which is different to just Person.

If you want to define stuff like this you don't have to reopen the class to give it new accessors.

class Role
::Person.attr_accessor :role
...
end

Would work as well. Note that `::Person` makes sure to get the top-level-namespace Person and not anything else.

Cheers,
Paul

···

On 07/19/18 11:58, Leam Hall wrote:

I think I'm trying to figure out the Decorator pattern. What I want is to have a <thing> that takes an instance of <something> and changes it. The changes can include modifying and adding instance variables.

Haven't quite figured it out yet, thoughts?

Thanks!

Leam

Here's the error:
###
Traceback (most recent call last):
3: from test_decorator.rb:28:in `<main>'
2: from test_decorator.rb:28:in `new'
1: from test_decorator.rb:17:in `initialize'
test_decorator.rb:20:in `assign_role': undefined method `role=' for #<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
###

Code:
###
1 class Person
2 def initialize(data)
3 @data = data
4 end
5 def name
6 @data[:name] || "Fred"
7 end
8 end
9
10 class Role
11 class Person
12 attr_accessor :role
13 end
14
15 def initialize(data)
16 @person = data[:person]
17 assign_role(data[:role])
18 end
19 def assign_role(role)
20 @person.role = role
21 end
22 end
23
24 data = {:name => 'Al'}
25
26 al = Person.new(data)
27 role_data = {:person => al, :role => 'Cadet'}
28 Role.new(role_data)
29 puts al.name
30 #al.role = "Cadet Sergeant"
###

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>


(Andy Jones) #4

You have to ask yourself: composition or mixin? The easiest way is with a mixin, but it has limitations.

···

~~~~
module Engineer
  def can_do_engines?; true; end
end

class Person
  def initialize(name); @name = name; end
end

p = Person.new("fred")
p.extend Engineer
puts p.can_do_engines?
~~~~

Note that you decorate an object --- not a class. If you want to add to the functionality of every instance of a class, I'm not sure that counts as Decorator pattern?

If you want to do it with Composition, instead, have a look at the Ruby documentation for Forwardable. (Sorry this is a bit rushed; chaotic today...)

I think that this might be the article that originally clued me into Decorator? Not sure: https://robots.thoughtbot.com/evaluating-alternative-decorator-implementations-in

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam Hall
Sent: 19 July 2018 10:59
To: Ruby users
Subject: Class that opens and adds variables to instances of another class?

I think I'm trying to figure out the Decorator pattern. What I want is
to have a <thing> that takes an instance of <something> and changes it.
The changes can include modifying and adding instance variables.

Haven't quite figured it out yet, thoughts?

Thanks!

Leam

Here's the error:
###
Traceback (most recent call last):
3: from test_decorator.rb:28:in `<main>'
2: from test_decorator.rb:28:in `new'
1: from test_decorator.rb:17:in `initialize'
test_decorator.rb:20:in `assign_role': undefined method `role=' for
#<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
###

Code:
###
   1 class Person
   2 def initialize(data)
   3 @data = data
   4 end
   5 def name
   6 @data[:name] || "Fred"
   7 end
   8 end
   9
  10 class Role
  11 class Person
  12 attr_accessor :role
  13 end
  14
  15 def initialize(data)
  16 @person = data[:person]
  17 assign_role(data[:role])
  18 end
  19 def assign_role(role)
  20 @person.role = role
  21 end
  22 end
  23
  24 data = {:name => 'Al'}
  25
  26 al = Person.new(data)
  27 role_data = {:person => al, :role => 'Cadet'}
  28 Role.new(role_data)
  29 puts al.name
  30 #al.role = "Cadet Sergeant"
###

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.


(Leam Hall) #5

Hmm..good question. This stems from my Traveller stuff and the game I'm
writing, which have two different uses for the Person class. Here's the
logic:

1. Every instance of Person
    Has a name, gender, and UPP. (character stats)
    Can be stored in a language agnostic manner (using JSON)

2. CharacterCreation adds to an instance of Person
    Adds physical description, personality

3. Careers add to the instance of person
    Skills, rank, notes, money, "stuff"
    A character can go through multiple careers.

4. FreeTrader (the game) uses a limited subset of the Person + Additional
stuff.
    Ship position(role),
    Game uses JSON
    Uses Careers, might use CharacterCreation.

5. CharacterData (the database) stores as much information about each
character as possible.
    Planning to use Hanami/Sinatra? and a MongoDB backend.
    Used to track details of characters for games and books I'm writing.

FreeTrader and CharacterData are separate projects but I want to build from
the same Person base and extend from there. That's why the instance of a
Person might have some attributes and not others.

Leam

···

On Thu, Jul 19, 2018 at 6:12 AM, Andy Jones <Andy.Jones@jameshall.co.uk> wrote:

You have to ask yourself: composition or mixin? The easiest way is with a
mixin, but it has limitations.

~~~~
module Engineer
  def can_do_engines?; true; end
end

class Person
  def initialize(name); @name = name; end
end

p = Person.new("fred")
p.extend Engineer
puts p.can_do_engines?
~~~~

Note that you decorate an object --- not a class. If you want to add to
the functionality of every instance of a class, I'm not sure that counts as
Decorator pattern?

If you want to do it with Composition, instead, have a look at the Ruby
documentation for Forwardable. (Sorry this is a bit rushed; chaotic
today...)

I think that this might be the article that originally clued me into
Decorator? Not sure: https://robots.thoughtbot.com/evaluating-alternative-
decorator-implementations-in

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of
Leam Hall
Sent: 19 July 2018 10:59
To: Ruby users
Subject: Class that opens and adds variables to instances of another class?

I think I'm trying to figure out the Decorator pattern. What I want is
to have a <thing> that takes an instance of <something> and changes it.
The changes can include modifying and adding instance variables.

Haven't quite figured it out yet, thoughts?

Thanks!

Leam

Here's the error:
###
Traceback (most recent call last):
3: from test_decorator.rb:28:in `<main>'
2: from test_decorator.rb:28:in `new'
1: from test_decorator.rb:17:in `initialize'
test_decorator.rb:20:in `assign_role': undefined method `role=' for
#<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
###

Code:
###
   1 class Person
   2 def initialize(data)
   3 @data = data
   4 end
   5 def name
   6 @data[:name] || "Fred"
   7 end
   8 end
   9
  10 class Role
  11 class Person
  12 attr_accessor :role
  13 end
  14
  15 def initialize(data)
  16 @person = data[:person]
  17 assign_role(data[:role])
  18 end
  19 def assign_role(role)
  20 @person.role = role
  21 end
  22 end
  23
  24 data = {:name => 'Al'}
  25
  26 al = Person.new(data)
  27 role_data = {:person => al, :role => 'Cadet'}
  28 Role.new(role_data)
  29 puts al.name
  30 #al.role = "Cadet Sergeant"
###

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<
http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data
protection regulations. Please refer to our website to view the ways in
which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>


(Andy Jones) #6

Well, first, it seems as if you might find it easier to keep the classes for character creation and the actual game somewhat different. Have the game read the JSON into a separate “model” class that keeps it away from the complexities of character creation, and just lets it deal with the resulting character? I don’t know.

Second, it seems to me that all your characters want to partake in all your addon functionality. The use case for Decorator Pattern is rather different. It’s popular in game design, but not usually used in that way, but rather like this:

flora = Character.new(“Flora the elven druid”)
flora.exend Elf
flora.exend Druid

varian = Character.new(“Varian the dwarven undead druid”)
varian.extend Dwarf
varian.extend Undead
varian.extend Druid

(Apologies if the formatting is odd, Outlook is playing Silly Devils)

If I understand correctly, you might want to go to what Smalltalk used to call “Method Classes” – where you farm off some of your functionality to a helper class that does a job, returns a result, and then never gets called again. It seems like your CharacterCreation might best be that sort of class. But I don’t know it well enough to say for sure.

class Person
  def initialize(name, strength)
    @name, @strength = name, strength
    @foo = ValueOfFooFigurerOuter.new(strength).go
  end
end

In the above example, the Person class doesn’t have to care how @foo is calculated. A helper class does that for it.

Hope that helps…

···

From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of leam hall
Sent: 19 July 2018 13:57
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another class?

Hmm..good question. This stems from my Traveller stuff and the game I'm writing, which have two different uses for the Person class. Here's the logic:

1. Every instance of Person
    Has a name, gender, and UPP. (character stats)
    Can be stored in a language agnostic manner (using JSON)

2. CharacterCreation adds to an instance of Person
    Adds physical description, personality

3. Careers add to the instance of person
    Skills, rank, notes, money, "stuff"
    A character can go through multiple careers.

4. FreeTrader (the game) uses a limited subset of the Person + Additional stuff.
    Ship position(role),
    Game uses JSON
    Uses Careers, might use CharacterCreation.

5. CharacterData (the database) stores as much information about each character as possible.
    Planning to use Hanami/Sinatra? and a MongoDB backend.
    Used to track details of characters for games and books I'm writing.

FreeTrader and CharacterData are separate projects but I want to build from the same Person base and extend from there. That's why the instance of a Person might have some attributes and not others.

Leam

On Thu, Jul 19, 2018 at 6:12 AM, Andy Jones <Andy.Jones@jameshall.co.uk<mailto:Andy.Jones@jameshall.co.uk>> wrote:
You have to ask yourself: composition or mixin? The easiest way is with a mixin, but it has limitations.

~~~~
module Engineer
  def can_do_engines?; true; end
end

class Person
  def initialize(name); @name = name; end
end

p = Person.new("fred")
p.extend Engineer
puts p.can_do_engines?
~~~~

Note that you decorate an object --- not a class. If you want to add to the functionality of every instance of a class, I'm not sure that counts as Decorator pattern?

If you want to do it with Composition, instead, have a look at the Ruby documentation for Forwardable. (Sorry this is a bit rushed; chaotic today...)

I think that this might be the article that originally clued me into Decorator? Not sure: https://robots.thoughtbot.com/evaluating-alternative-decorator-implementations-in

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org<mailto:ruby-talk-bounces@ruby-lang.org>] On Behalf Of Leam Hall
Sent: 19 July 2018 10:59
To: Ruby users
Subject: Class that opens and adds variables to instances of another class?

I think I'm trying to figure out the Decorator pattern. What I want is
to have a <thing> that takes an instance of <something> and changes it.
The changes can include modifying and adding instance variables.

Haven't quite figured it out yet, thoughts?

Thanks!

Leam

Here's the error:
###
Traceback (most recent call last):
3: from test_decorator.rb:28:in `<main>'
2: from test_decorator.rb:28:in `new'
1: from test_decorator.rb:17:in `initialize'
test_decorator.rb:20:in `assign_role': undefined method `role=' for
#<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
###

Code:
###
   1 class Person
   2 def initialize(data)
   3 @data = data
   4 end
   5 def name
   6 @data[:name] || "Fred"
   7 end
   8 end
   9
  10 class Role
  11 class Person
  12 attr_accessor :role
  13 end
  14
  15 def initialize(data)
  16 @person = data[:person]
  17 assign_role(data[:role])
  18 end
  19 def assign_role(role)
  20 @person.role = role
  21 end
  22 end
  23
  24 data = {:name => 'Al'}
  25
  26 al = Person.new(data)
  27 role_data = {:person => al, :role => 'Cadet'}
  28 Role.new(role_data)
  29 puts al.name<http://al.name>
  30 #al.role = "Cadet Sergeant"
###

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org<mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org<mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.


(Leam Hall) #7

Hah! I may need a Smalltalk primer, I keep seeing references to it.

Yes, I think your last example is closer to the need and a Decorator might
not be what I'm looking for. The simple explination is:

al = Person.new
Something_That_Changes_Character.new(al)
Something_Else_That_Also_Changes_Character.new(al)

In each case "al" has several attributes modified or added. The "al" passed
to the second Something carries the modifications from the first.

Leam

···

On Thu, Jul 19, 2018 at 9:22 AM, Andy Jones <Andy.Jones@jameshall.co.uk> wrote:

Well, first, it seems as if you might find it easier to keep the classes
for character creation and the actual game somewhat different. Have the
game read the JSON into a separate “model” class that keeps it away from
the complexities of character creation, and just lets it deal with the
resulting character? I don’t know.

Second, it seems to me that all your characters want to partake in all
your addon functionality. The use case for Decorator Pattern is rather
different. It’s popular in game design, but not usually used in that way,
but rather like this:

flora = Character.new(“Flora the elven druid”)

flora.exend Elf

flora.exend Druid

varian = Character.new(“Varian the dwarven undead druid”)

varian.extend Dwarf

varian.extend Undead

varian.extend Druid

(Apologies if the formatting is odd, Outlook is playing Silly Devils)

If I understand correctly, you might want to go to what Smalltalk used to
call “Method Classes” – where you farm off some of your functionality to a
helper class that does a job, returns a result, and then never gets called
again. It seems like your CharacterCreation might best be that sort of
class. But I don’t know it well enough to say for sure.

class Person

  def initialize(name, strength)

    @name, @strength = name, strength

    @foo = ValueOfFooFigurerOuter.new(strength).go

  end

end

In the above example, the Person class doesn’t have to care how @foo is
calculated. A helper class does that for it.

Hope that helps…

*From:* ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] *On Behalf Of *leam
hall
*Sent:* 19 July 2018 13:57
*To:* Ruby users
*Subject:* Re: Class that opens and adds variables to instances of
another class?

Hmm..good question. This stems from my Traveller stuff and the game I'm
writing, which have two different uses for the Person class. Here's the
logic:

1. Every instance of Person

    Has a name, gender, and UPP. (character stats)

    Can be stored in a language agnostic manner (using JSON)

2. CharacterCreation adds to an instance of Person

    Adds physical description, personality

3. Careers add to the instance of person

    Skills, rank, notes, money, "stuff"

    A character can go through multiple careers.

4. FreeTrader (the game) uses a limited subset of the Person + Additional
stuff.

    Ship position(role),

    Game uses JSON

    Uses Careers, might use CharacterCreation.

5. CharacterData (the database) stores as much information about each
character as possible.

    Planning to use Hanami/Sinatra? and a MongoDB backend.

    Used to track details of characters for games and books I'm writing.

FreeTrader and CharacterData are separate projects but I want to build
from the same Person base and extend from there. That's why the instance of
a Person might have some attributes and not others.

Leam

On Thu, Jul 19, 2018 at 6:12 AM, Andy Jones <Andy.Jones@jameshall.co.uk> > wrote:

You have to ask yourself: composition or mixin? The easiest way is with a
mixin, but it has limitations.

~~~~
module Engineer
  def can_do_engines?; true; end
end

class Person
  def initialize(name); @name = name; end
end

p = Person.new("fred")
p.extend Engineer
puts p.can_do_engines?
~~~~

Note that you decorate an object --- not a class. If you want to add to
the functionality of every instance of a class, I'm not sure that counts as
Decorator pattern?

If you want to do it with Composition, instead, have a look at the Ruby
documentation for Forwardable. (Sorry this is a bit rushed; chaotic
today...)

I think that this might be the article that originally clued me into
Decorator? Not sure: https://robots.thoughtbot.com/evaluating-alternative-
decorator-implementations-in

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of
Leam Hall
Sent: 19 July 2018 10:59
To: Ruby users
Subject: Class that opens and adds variables to instances of another class?

I think I'm trying to figure out the Decorator pattern. What I want is
to have a <thing> that takes an instance of <something> and changes it.
The changes can include modifying and adding instance variables.

Haven't quite figured it out yet, thoughts?

Thanks!

Leam

Here's the error:
###
Traceback (most recent call last):
3: from test_decorator.rb:28:in `<main>'
2: from test_decorator.rb:28:in `new'
1: from test_decorator.rb:17:in `initialize'
test_decorator.rb:20:in `assign_role': undefined method `role=' for
#<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
###

Code:
###
   1 class Person
   2 def initialize(data)
   3 @data = data
   4 end
   5 def name
   6 @data[:name] || "Fred"
   7 end
   8 end
   9
  10 class Role
  11 class Person
  12 attr_accessor :role
  13 end
  14
  15 def initialize(data)
  16 @person = data[:person]
  17 assign_role(data[:role])
  18 end
  19 def assign_role(role)
  20 @person.role = role
  21 end
  22 end
  23
  24 data = {:name => 'Al'}
  25
  26 al = Person.new(data)
  27 role_data = {:person => al, :role => 'Cadet'}
  28 Role.new(role_data)
  29 puts al.name
  30 #al.role = "Cadet Sergeant"
###

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<
http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data
protection regulations. Please refer to our website to view the ways in
which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.
<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data
protection regulations. Please refer to our website to view the ways in
which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>


(Andy Jones) #8

<<<<<<<<
al = Person.new
Something_That_Changes_Character.new(al)
Something_Else_That_Also_Changes_Character.new(al)

In each case "al" has several attributes modified or added. The "al" passed to the second Something carries the modifications from the first.
<<<<<<<<

Modified – yes, added – no. Otherwise how will you know which attributes a1 has when you come to use it?

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.


(Leam Hall) #9

<<<<<<<<

al = Person.new

Something_That_Changes_Character.new(al)

Something_Else_That_Also_Changes_Character.new(al)

In each case "al" has several attributes modified or added. The "al"
passed to the second Something carries the modifications from the first.

<<<<<<<<

Modified – yes, added – no. Otherwise how will you know which attributes
a1 has when you come to use it?

The using program (game, creator, data) will track what it needs. So
CharacterData would have a schemaless datastore to include "Notes",
"Associates" and the like. "FreeTrader" the game would just use "Name, UPP,
Skills, Rank" and the like.

Some attributes are expected 90% of the time. For example, Skills is a hash
with a String:Int key:value pair. Instead of added everything to every
person, which seem bloated and ugly, I want to let the using program
add/modifiy what it needs.

···

On Thu, Jul 19, 2018 at 9:44 AM, Andy Jones <Andy.Jones@jameshall.co.uk> wrote:


(Leam Hall) #10

The overall plan is to get better at object oriented thinking and
programming. Trying to do that with Ruby since I enjoy it most of the
languages I've tried. I've done a few languages and gotten 3-6 months of
"experience" spread over 2-4 years each. Through the rest of 2018 I want to
just focus on OOP concepts and doings.

That's why the drive for patterns, TDD, using objects where structs would
be just as good, etc. Going overboard on OO to learn where it works and
where it doesn't.

···

On Thu, Jul 19, 2018 at 9:52 AM, leam hall <leamhall@gmail.com> wrote:

On Thu, Jul 19, 2018 at 9:44 AM, Andy Jones <Andy.Jones@jameshall.co.uk> > wrote:

<<<<<<<<

al = Person.new

Something_That_Changes_Character.new(al)

Something_Else_That_Also_Changes_Character.new(al)

In each case "al" has several attributes modified or added. The "al"
passed to the second Something carries the modifications from the first.

<<<<<<<<

Modified – yes, added – no. Otherwise how will you know which attributes
a1 has when you come to use it?

The using program (game, creator, data) will track what it needs. So
CharacterData would have a schemaless datastore to include "Notes",
"Associates" and the like. "FreeTrader" the game would just use "Name, UPP,
Skills, Rank" and the like.

Some attributes are expected 90% of the time. For example, Skills is a
hash with a String:Int key:value pair. Instead of added everything to every
person, which seem bloated and ugly, I want to let the using program
add/modifiy what it needs.


(Leam Hall) #11

This will be part of the weekend's joy.

There are lots of roles, and most instances of Person get at least one. A
few instances get more than one, but which role a particular instance gets
varies widely.

More questions as I stumble through...

Leam

···

On Thu, Jul 19, 2018 at 6:12 AM, Andy Jones <Andy.Jones@jameshall.co.uk> wrote:

You have to ask yourself: composition or mixin? The easiest way is with a
mixin, but it has limitations.

~~~~
module Engineer
  def can_do_engines?; true; end
end

class Person
  def initialize(name); @name = name; end
end

p = Person.new("fred")
p.extend Engineer
puts p.can_do_engines?
~~~~

Note that you decorate an object --- not a class. If you want to add to
the functionality of every instance of a class, I'm not sure that counts as
Decorator pattern?

If you want to do it with Composition, instead, have a look at the Ruby
documentation for Forwardable. (Sorry this is a bit rushed; chaotic
today...)

I think that this might be the article that originally clued me into
Decorator? Not sure: https://robots.thoughtbot.com/evaluating-alternative-
decorator-implementations-in

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of
Leam Hall
Sent: 19 July 2018 10:59
To: Ruby users
Subject: Class that opens and adds variables to instances of another class?

I think I'm trying to figure out the Decorator pattern. What I want is
to have a <thing> that takes an instance of <something> and changes it.
The changes can include modifying and adding instance variables.

Haven't quite figured it out yet, thoughts?

Thanks!

Leam

Here's the error:
###
Traceback (most recent call last):
3: from test_decorator.rb:28:in `<main>'
2: from test_decorator.rb:28:in `new'
1: from test_decorator.rb:17:in `initialize'
test_decorator.rb:20:in `assign_role': undefined method `role=' for
#<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
###

Code:
###
   1 class Person
   2 def initialize(data)
   3 @data = data
   4 end
   5 def name
   6 @data[:name] || "Fred"
   7 end
   8 end
   9
  10 class Role
  11 class Person
  12 attr_accessor :role
  13 end
  14
  15 def initialize(data)
  16 @person = data[:person]
  17 assign_role(data[:role])
  18 end
  19 def assign_role(role)
  20 @person.role = role
  21 end
  22 end
  23
  24 data = {:name => 'Al'}
  25
  26 al = Person.new(data)
  27 role_data = {:person => al, :role => 'Cadet'}
  28 Role.new(role_data)
  29 puts al.name
  30 #al.role = "Cadet Sergeant"
###

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<
http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data
protection regulations. Please refer to our website to view the ways in
which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>


(Leam Hall) #12

And this is how I spend my Friday night...

Thoughts? In this case Career_Tools has to account for anything in any of the careers. Most of the time those things are pretty simple.

···

####

class Person
   def initialize(data)
     @name = data[:name]
   end
   def name
     @name
   end
end

module Career_Tools
   def rank
     @rank
   end
   def rank=(career)
     @rank = career.ranks
   end
end

module Relationships
   def friends
     @friends
   end
   def friends=(l)
     if @friends.nil?
       @friends = Array.new
     end
     @friends << l
   end
end

class Cadet
   def ranks
     ['Cadet', 'Cadet Corporal', 'Cadet Sergeant', 'Cadet Leiutenant'].sample
   end
end

### main
person_data = {:name => 'Al'}
al = Person.new(person_data)
puts al.name
al.extend Career_Tools
al.extend Relationships
al.friends = "CC One"
al.friends = "Wilbur"
puts al.friends.join(", ")
career = Cadet.new
al.rank=(career)
puts al.rank

####

On 07/20/2018 03:48 PM, leam hall wrote:

This will be part of the weekend's joy.

There are lots of roles, and most instances of Person get at least one. A few instances get more than one, but which role a particular instance gets varies widely.

More questions as I stumble through...

Leam

On Thu, Jul 19, 2018 at 6:12 AM, Andy Jones <Andy.Jones@jameshall.co.uk > <mailto:Andy.Jones@jameshall.co.uk>> wrote:

    You have to ask yourself: composition or mixin? The easiest way is
    with a mixin, but it has limitations.

    ~~~~
    module Engineer
      def can_do_engines?; true; end
    end

    class Person
      def initialize(name); @name = name; end
    end

    p = Person.new("fred")
    p.extend Engineer
    puts p.can_do_engines?
    ~~~~

    Note that you decorate an object --- not a class. If you want to
    add to the functionality of every instance of a class, I'm not sure
    that counts as Decorator pattern?

    If you want to do it with Composition, instead, have a look at the
    Ruby documentation for Forwardable. (Sorry this is a bit rushed;
    chaotic today...)

    I think that this might be the article that originally clued me into
    Decorator? Not sure:
    https://robots.thoughtbot.com/evaluating-alternative-decorator-implementations-in
    <https://robots.thoughtbot.com/evaluating-alternative-decorator-implementations-in>

    -----Original Message-----
    From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org
    <mailto:ruby-talk-bounces@ruby-lang.org>] On Behalf Of Leam Hall
    Sent: 19 July 2018 10:59
    To: Ruby users
    Subject: Class that opens and adds variables to instances of another
    class?

    I think I'm trying to figure out the Decorator pattern. What I want is
    to have a <thing> that takes an instance of <something> and changes it.
    The changes can include modifying and adding instance variables.

    Haven't quite figured it out yet, thoughts?

    Thanks!

    Leam

    Here's the error:
    ###
    Traceback (most recent call last):
    3: from test_decorator.rb:28:in `<main>'
    2: from test_decorator.rb:28:in `new'
    1: from test_decorator.rb:17:in `initialize'
    test_decorator.rb:20:in `assign_role': undefined method `role=' for
    #<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
    ###

    Code:
    ###
      1 class Person
      2 def initialize(data)
      3 @data = data
      4 end
      5 def name
      6 @data[:name] || "Fred"
      7 end
      8 end
      9
      10 class Role
      11 class Person
      12 attr_accessor :role
      13 end
      14
      15 def initialize(data)
      16 @person = data[:person]
      17 assign_role(data[:role])
      18 end
      19 def assign_role(role)
      20 @person.role = role
      21 end
      22 end
      23
      24 data = {:name => 'Al'}
      25
      26 al = Person.new(data)
      27 role_data = {:person => al, :role => 'Cadet'}
      28 Role.new(role_data)
      29 puts al.name <http://al.name>
      30 #al.role = "Cadet Sergeant"
    ###

    Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
    <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
    <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
    <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>

    Click here to view Company Information and Confidentiality
    Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer
    <http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>>

    Please note that we have updated our privacy policy in line with new
    data protection regulations. Please refer to our website to view the
    ways in which we handle your data.

    Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
    <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
    <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
    <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>


(Andy Jones) #13

Again, I'm dubious that this is the best approach, because when you get an unknown method error half way into your main, game code, and it turns out that that's because you called person.rank on a Person that doesn't have the Career_Tools mixin, that is going to be painful to debug.

Alternatively you will have to pepper your code with things like `if defined?(person.rank); rank_thing(person.rank); end` which will make the code much harder to follow and ALL the bugs harder to find.

The way I would do it, personally? I know that some Persons have a rank. So I put rank right there in the Person class. Civilians have a rank of nil. Nil values are a PITA but considerably less so than missing methods! So now I can at least do `rank_thing(person.rank)` and I guess rank_thing starts with `return unless rank`...

···

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam
Hall
Sent: 21 July 2018 1:19 am
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another
class?

And this is how I spend my Friday night...

Thoughts? In this case Career_Tools has to account for anything in any
of the careers. Most of the time those things are pretty simple.

####

class Person
   def initialize(data)
     @name = data[:name]
   end
   def name
     @name
   end
end

module Career_Tools
   def rank
     @rank
   end
   def rank=(career)
     @rank = career.ranks
   end
end

module Relationships
   def friends
     @friends
   end
   def friends=(l)
     if @friends.nil?
       @friends = Array.new
     end
     @friends << l
   end
end

class Cadet
   def ranks
     ['Cadet', 'Cadet Corporal', 'Cadet Sergeant', 'Cadet
Leiutenant'].sample
   end
end

### main
person_data = {:name => 'Al'}
al = Person.new(person_data)
puts al.name
al.extend Career_Tools
al.extend Relationships
al.friends = "CC One"
al.friends = "Wilbur"
puts al.friends.join(", ")
career = Cadet.new
al.rank=(career)
puts al.rank

####

On 07/20/2018 03:48 PM, leam hall wrote:
> This will be part of the weekend's joy.
>
> There are lots of roles, and most instances of Person get at least one.
> A few instances get more than one, but which role a particular instance
> gets varies widely.
>
> More questions as I stumble through...
>
> Leam
>
>
> On Thu, Jul 19, 2018 at 6:12 AM, Andy Jones <Andy.Jones@jameshall.co.uk > > <mailto:Andy.Jones@jameshall.co.uk>> wrote:
>
> You have to ask yourself: composition or mixin? The easiest way is
> with a mixin, but it has limitations.
>
> ~~~~
> module Engineer
> def can_do_engines?; true; end
> end
>
> class Person
> def initialize(name); @name = name; end
> end
>
> p = Person.new("fred")
> p.extend Engineer
> puts p.can_do_engines?
> ~~~~
>
> Note that you decorate an object --- not a class. If you want to
> add to the functionality of every instance of a class, I'm not sure
> that counts as Decorator pattern?
>
> If you want to do it with Composition, instead, have a look at the
> Ruby documentation for Forwardable. (Sorry this is a bit rushed;
> chaotic today...)
>
> I think that this might be the article that originally clued me into
> Decorator? Not sure:
> https://robots.thoughtbot.com/evaluating-alternative-decorator-
implementations-in
> <https://robots.thoughtbot.com/evaluating-alternative-decorator-
implementations-in>
>
>
> -----Original Message-----
> From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org
> <mailto:ruby-talk-bounces@ruby-lang.org>] On Behalf Of Leam Hall
> Sent: 19 July 2018 10:59
> To: Ruby users
> Subject: Class that opens and adds variables to instances of another
> class?
>
> I think I'm trying to figure out the Decorator pattern. What I want is
> to have a <thing> that takes an instance of <something> and changes it.
> The changes can include modifying and adding instance variables.
>
> Haven't quite figured it out yet, thoughts?
>
> Thanks!
>
> Leam
>
> Here's the error:
> ###
> Traceback (most recent call last):
> 3: from test_decorator.rb:28:in `<main>'
> 2: from test_decorator.rb:28:in `new'
> 1: from test_decorator.rb:17:in `initialize'
> test_decorator.rb:20:in `assign_role': undefined method `role=' for
> #<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
> ###
>
>
> Code:
> ###
> 1 class Person
> 2 def initialize(data)
> 3 @data = data
> 4 end
> 5 def name
> 6 @data[:name] || "Fred"
> 7 end
> 8 end
> 9
> 10 class Role
> 11 class Person
> 12 attr_accessor :role
> 13 end
> 14
> 15 def initialize(data)
> 16 @person = data[:person]
> 17 assign_role(data[:role])
> 18 end
> 19 def assign_role(role)
> 20 @person.role = role
> 21 end
> 22 end
> 23
> 24 data = {:name => 'Al'}
> 25
> 26 al = Person.new(data)
> 27 role_data = {:person => al, :role => 'Cadet'}
> 28 Role.new(role_data)
> 29 puts al.name <http://al.name>
> 30 #al.role = "Cadet Sergeant"
> ###
>
> Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
> <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>
>
>
> Click here to view Company Information and Confidentiality
> Notice.<http://www.jameshall.co.uk/index.php/small-print/email-
disclaimer
> <http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>>
>
> Please note that we have updated our privacy policy in line with new
> data protection regulations. Please refer to our website to view the
> ways in which we handle your data.
>
> Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
> <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>
>
>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.


(Leam Hall) #14

Understood. Here's how I see that being dealt with.

1. There is a Universal Data Store (UDS) of some unrestricted format. JSON, MOngoDB, or whatever. Each program pulls from the same data store.

2. The character generate program creates basic characters.

3. The game program:
   For a new game, generates crew. Thus uses the basic character generation program and adds specific stuff required by the game.
   For a saved game, those characters are pulled from the UDS.

4. The fiction tracker program:
   For new characters:
     Does basic character generation
     Adds data like appearance, mental workup, last known location, etc.
   Otherwise pulls the data from the UDS.
   Allows manual edits of data.
   Allows creation of new data fields.

5. The relationship tracker feeds data into Neo4J.
   A basic character from #2 has no relational data.
   A game character from #3 would have the rest of the crew as relationships.
   A fiction character might overload Neo4J, if you've seen how convoluted my fiction gets...

In each case the programs include the modules they need. The UDS has *all* the data, but each program only uses parts of it. The relationship tracker doesn't care about physical appearance, etc.

By keeping things in modules, each program can use the modules it needs. Does that solve the problems you are seeing?

I really appreciate the feedback, you are helping clarify my thinking.

Leam

···

On 07/23/2018 03:26 AM, Andy Jones wrote:

Again, I'm dubious that this is the best approach, because when you get an unknown method error half way into your main, game code, and it turns out that that's because you called person.rank on a Person that doesn't have the Career_Tools mixin, that is going to be painful to debug.

Alternatively you will have to pepper your code with things like `if defined?(person.rank); rank_thing(person.rank); end` which will make the code much harder to follow and ALL the bugs harder to find.

The way I would do it, personally? I know that some Persons have a rank. So I put rank right there in the Person class. Civilians have a rank of nil. Nil values are a PITA but considerably less so than missing methods! So now I can at least do `rank_thing(person.rank)` and I guess rank_thing starts with `return unless rank`...

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam
Hall
Sent: 21 July 2018 1:19 am
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another
class?

And this is how I spend my Friday night...

Thoughts? In this case Career_Tools has to account for anything in any
of the careers. Most of the time those things are pretty simple.

####

class Person
    def initialize(data)
      @name = data[:name]
    end
    def name
      @name
    end
end

module Career_Tools
    def rank
      @rank
    end
    def rank=(career)
      @rank = career.ranks
    end
end

module Relationships
    def friends
      @friends
    end
    def friends=(l)
      if @friends.nil?
        @friends = Array.new
      end
      @friends << l
    end
end

class Cadet
    def ranks
      ['Cadet', 'Cadet Corporal', 'Cadet Sergeant', 'Cadet
Leiutenant'].sample
    end
end

### main
person_data = {:name => 'Al'}
al = Person.new(person_data)
puts al.name
al.extend Career_Tools
al.extend Relationships
al.friends = "CC One"
al.friends = "Wilbur"
puts al.friends.join(", ")
career = Cadet.new
al.rank=(career)
puts al.rank

####

On 07/20/2018 03:48 PM, leam hall wrote:

This will be part of the weekend's joy.

There are lots of roles, and most instances of Person get at least one.
A few instances get more than one, but which role a particular instance
gets varies widely.

More questions as I stumble through...

Leam

On Thu, Jul 19, 2018 at 6:12 AM, Andy Jones <Andy.Jones@jameshall.co.uk >>> <mailto:Andy.Jones@jameshall.co.uk>> wrote:

     You have to ask yourself: composition or mixin? The easiest way is
     with a mixin, but it has limitations.

     ~~~~
     module Engineer
        def can_do_engines?; true; end
     end

     class Person
        def initialize(name); @name = name; end
     end

     p = Person.new("fred")
     p.extend Engineer
     puts p.can_do_engines?
     ~~~~

     Note that you decorate an object --- not a class. If you want to
     add to the functionality of every instance of a class, I'm not sure
     that counts as Decorator pattern?

     If you want to do it with Composition, instead, have a look at the
     Ruby documentation for Forwardable. (Sorry this is a bit rushed;
     chaotic today...)

     I think that this might be the article that originally clued me into
     Decorator? Not sure:
     https://robots.thoughtbot.com/evaluating-alternative-decorator-

implementations-in

     <https://robots.thoughtbot.com/evaluating-alternative-decorator-

implementations-in>

     -----Original Message-----
     From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org
     <mailto:ruby-talk-bounces@ruby-lang.org>] On Behalf Of Leam Hall
     Sent: 19 July 2018 10:59
     To: Ruby users
     Subject: Class that opens and adds variables to instances of another
     class?

     I think I'm trying to figure out the Decorator pattern. What I want is
     to have a <thing> that takes an instance of <something> and changes it.
     The changes can include modifying and adding instance variables.

     Haven't quite figured it out yet, thoughts?

     Thanks!

     Leam

     Here's the error:
     ###
     Traceback (most recent call last):
     3: from test_decorator.rb:28:in `<main>'
     2: from test_decorator.rb:28:in `new'
     1: from test_decorator.rb:17:in `initialize'
     test_decorator.rb:20:in `assign_role': undefined method `role=' for
     #<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
     ###

     Code:
     ###
         1 class Person
         2 def initialize(data)
         3 @data = data
         4 end
         5 def name
         6 @data[:name] || "Fred"
         7 end
         8 end
         9
        10 class Role
        11 class Person
        12 attr_accessor :role
        13 end
        14
        15 def initialize(data)
        16 @person = data[:person]
        17 assign_role(data[:role])
        18 end
        19 def assign_role(role)
        20 @person.role = role
        21 end
        22 end
        23
        24 data = {:name => 'Al'}
        25
        26 al = Person.new(data)
        27 role_data = {:person => al, :role => 'Cadet'}
        28 Role.new(role_data)
        29 puts al.name <http://al.name>
        30 #al.role = "Cadet Sergeant"
     ###

     Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
     <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
     <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
     <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>

     Click here to view Company Information and Confidentiality
     Notice.<http://www.jameshall.co.uk/index.php/small-print/email-

disclaimer

     <http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>>

     Please note that we have updated our privacy policy in line with new
     data protection regulations. Please refer to our website to view the
     ways in which we handle your data.

     Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
     <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
     <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
     <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>


(Andy Jones) #15

Well, your UDS will have to put the player data in an object of some class. If not Player, then what?

Put another way, when an object "pulls data from the UDS" -- where does it hold it?

Generally speaking when reading from a data store you want one of more "models" -- objects that represent the data to the rest of the application. It's fine if you don't want Player to be the model, but whatever "models" player information to the rest of the application will need that rank attribute (and all the others from all those decorators) or you will have the same problem.

···

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam Hall
Sent: 24 July 2018 10:58
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another class?

Understood. Here's how I see that being dealt with.

1. There is a Universal Data Store (UDS) of some unrestricted format.
JSON, MOngoDB, or whatever. Each program pulls from the same data store.

2. The character generate program creates basic characters.

3. The game program:
   For a new game, generates crew. Thus uses the basic character
generation program and adds specific stuff required by the game.
   For a saved game, those characters are pulled from the UDS.

4. The fiction tracker program:
   For new characters:
     Does basic character generation
     Adds data like appearance, mental workup, last known location, etc.
   Otherwise pulls the data from the UDS.
   Allows manual edits of data.
   Allows creation of new data fields.

5. The relationship tracker feeds data into Neo4J.
   A basic character from #2 has no relational data.
   A game character from #3 would have the rest of the crew as
relationships.
   A fiction character might overload Neo4J, if you've seen how
convoluted my fiction gets...

In each case the programs include the modules they need. The UDS has
*all* the data, but each program only uses parts of it. The relationship
tracker doesn't care about physical appearance, etc.

By keeping things in modules, each program can use the modules it needs.
Does that solve the problems you are seeing?

I really appreciate the feedback, you are helping clarify my thinking.

Leam

On 07/23/2018 03:26 AM, Andy Jones wrote:

Again, I'm dubious that this is the best approach, because when you get an unknown method error half way into your main, game code, and it turns out that that's because you called person.rank on a Person that doesn't have the Career_Tools mixin, that is going to be painful to debug.

Alternatively you will have to pepper your code with things like `if defined?(person.rank); rank_thing(person.rank); end` which will make the code much harder to follow and ALL the bugs harder to find.

The way I would do it, personally? I know that some Persons have a rank. So I put rank right there in the Person class. Civilians have a rank of nil. Nil values are a PITA but considerably less so than missing methods! So now I can at least do `rank_thing(person.rank)` and I guess rank_thing starts with `return unless rank`...

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam
Hall
Sent: 21 July 2018 1:19 am
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another
class?

And this is how I spend my Friday night...

Thoughts? In this case Career_Tools has to account for anything in any
of the careers. Most of the time those things are pretty simple.

####

class Person
    def initialize(data)
      @name = data[:name]
    end
    def name
      @name
    end
end

module Career_Tools
    def rank
      @rank
    end
    def rank=(career)
      @rank = career.ranks
    end
end

module Relationships
    def friends
      @friends
    end
    def friends=(l)
      if @friends.nil?
        @friends = Array.new
      end
      @friends << l
    end
end

class Cadet
    def ranks
      ['Cadet', 'Cadet Corporal', 'Cadet Sergeant', 'Cadet
Leiutenant'].sample
    end
end

### main
person_data = {:name => 'Al'}
al = Person.new(person_data)
puts al.name
al.extend Career_Tools
al.extend Relationships
al.friends = "CC One"
al.friends = "Wilbur"
puts al.friends.join(", ")
career = Cadet.new
al.rank=(career)
puts al.rank

####

On 07/20/2018 03:48 PM, leam hall wrote:

This will be part of the weekend's joy.

There are lots of roles, and most instances of Person get at least one.
A few instances get more than one, but which role a particular instance
gets varies widely.

More questions as I stumble through...

Leam

On Thu, Jul 19, 2018 at 6:12 AM, Andy Jones <Andy.Jones@jameshall.co.uk >>> <mailto:Andy.Jones@jameshall.co.uk>> wrote:

     You have to ask yourself: composition or mixin? The easiest way is
     with a mixin, but it has limitations.

     ~~~~
     module Engineer
        def can_do_engines?; true; end
     end

     class Person
        def initialize(name); @name = name; end
     end

     p = Person.new("fred")
     p.extend Engineer
     puts p.can_do_engines?
     ~~~~

     Note that you decorate an object --- not a class. If you want to
     add to the functionality of every instance of a class, I'm not sure
     that counts as Decorator pattern?

     If you want to do it with Composition, instead, have a look at the
     Ruby documentation for Forwardable. (Sorry this is a bit rushed;
     chaotic today...)

     I think that this might be the article that originally clued me into
     Decorator? Not sure:
     https://robots.thoughtbot.com/evaluating-alternative-decorator-

implementations-in

     <https://robots.thoughtbot.com/evaluating-alternative-decorator-

implementations-in>

     -----Original Message-----
     From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org
     <mailto:ruby-talk-bounces@ruby-lang.org>] On Behalf Of Leam Hall
     Sent: 19 July 2018 10:59
     To: Ruby users
     Subject: Class that opens and adds variables to instances of another
     class?

     I think I'm trying to figure out the Decorator pattern. What I want is
     to have a <thing> that takes an instance of <something> and changes it.
     The changes can include modifying and adding instance variables.

     Haven't quite figured it out yet, thoughts?

     Thanks!

     Leam

     Here's the error:
     ###
     Traceback (most recent call last):
     3: from test_decorator.rb:28:in `<main>'
     2: from test_decorator.rb:28:in `new'
     1: from test_decorator.rb:17:in `initialize'
     test_decorator.rb:20:in `assign_role': undefined method `role=' for
     #<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
     ###

     Code:
     ###
         1 class Person
         2 def initialize(data)
         3 @data = data
         4 end
         5 def name
         6 @data[:name] || "Fred"
         7 end
         8 end
         9
        10 class Role
        11 class Person
        12 attr_accessor :role
        13 end
        14
        15 def initialize(data)
        16 @person = data[:person]
        17 assign_role(data[:role])
        18 end
        19 def assign_role(role)
        20 @person.role = role
        21 end
        22 end
        23
        24 data = {:name => 'Al'}
        25
        26 al = Person.new(data)
        27 role_data = {:person => al, :role => 'Cadet'}
        28 Role.new(role_data)
        29 puts al.name <http://al.name>
        30 #al.role = "Cadet Sergeant"
     ###

     Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
     <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
     <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
     <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>

     Click here to view Company Information and Confidentiality
     Notice.<http://www.jameshall.co.uk/index.php/small-print/email-

disclaimer

     <http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>>

     Please note that we have updated our privacy policy in line with new
     data protection regulations. Please refer to our website to view the
     ways in which we handle your data.

     Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
     <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
     <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
     <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.


(Leam Hall) #16

Let me see if I can come up with some sample code in the next few days. Doing the Udemy "Ruby Metaprogramming" class and my brain is melting...

···

On 07/24/2018 06:08 AM, Andy Jones wrote:

Well, your UDS will have to put the player data in an object of some class. If not Player, then what?

Put another way, when an object "pulls data from the UDS" -- where does it hold it?

Generally speaking when reading from a data store you want one of more "models" -- objects that represent the data to the rest of the application. It's fine if you don't want Player to be the model, but whatever "models" player information to the rest of the application will need that rank attribute (and all the others from all those decorators) or you will have the same problem.

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam Hall
Sent: 24 July 2018 10:58
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another class?

Understood. Here's how I see that being dealt with.

1. There is a Universal Data Store (UDS) of some unrestricted format.
JSON, MOngoDB, or whatever. Each program pulls from the same data store.

2. The character generate program creates basic characters.

3. The game program:
    For a new game, generates crew. Thus uses the basic character
generation program and adds specific stuff required by the game.
    For a saved game, those characters are pulled from the UDS.

4. The fiction tracker program:
    For new characters:
      Does basic character generation
      Adds data like appearance, mental workup, last known location, etc.
    Otherwise pulls the data from the UDS.
    Allows manual edits of data.
    Allows creation of new data fields.

5. The relationship tracker feeds data into Neo4J.
    A basic character from #2 has no relational data.
    A game character from #3 would have the rest of the crew as
relationships.
    A fiction character might overload Neo4J, if you've seen how
convoluted my fiction gets...

In each case the programs include the modules they need. The UDS has
*all* the data, but each program only uses parts of it. The relationship
tracker doesn't care about physical appearance, etc.

By keeping things in modules, each program can use the modules it needs.
Does that solve the problems you are seeing?

I really appreciate the feedback, you are helping clarify my thinking.

Leam

On 07/23/2018 03:26 AM, Andy Jones wrote:

Again, I'm dubious that this is the best approach, because when you get an unknown method error half way into your main, game code, and it turns out that that's because you called person.rank on a Person that doesn't have the Career_Tools mixin, that is going to be painful to debug.

Alternatively you will have to pepper your code with things like `if defined?(person.rank); rank_thing(person.rank); end` which will make the code much harder to follow and ALL the bugs harder to find.

The way I would do it, personally? I know that some Persons have a rank. So I put rank right there in the Person class. Civilians have a rank of nil. Nil values are a PITA but considerably less so than missing methods! So now I can at least do `rank_thing(person.rank)` and I guess rank_thing starts with `return unless rank`...

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam
Hall
Sent: 21 July 2018 1:19 am
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another
class?

And this is how I spend my Friday night...

Thoughts? In this case Career_Tools has to account for anything in any
of the careers. Most of the time those things are pretty simple.

####

class Person
     def initialize(data)
       @name = data[:name]
     end
     def name
       @name
     end
end

module Career_Tools
     def rank
       @rank
     end
     def rank=(career)
       @rank = career.ranks
     end
end

module Relationships
     def friends
       @friends
     end
     def friends=(l)
       if @friends.nil?
         @friends = Array.new
       end
       @friends << l
     end
end

class Cadet
     def ranks
       ['Cadet', 'Cadet Corporal', 'Cadet Sergeant', 'Cadet
Leiutenant'].sample
     end
end

### main
person_data = {:name => 'Al'}
al = Person.new(person_data)
puts al.name
al.extend Career_Tools
al.extend Relationships
al.friends = "CC One"
al.friends = "Wilbur"
puts al.friends.join(", ")
career = Cadet.new
al.rank=(career)
puts al.rank

####

On 07/20/2018 03:48 PM, leam hall wrote:

This will be part of the weekend's joy.

There are lots of roles, and most instances of Person get at least one.
A few instances get more than one, but which role a particular instance
gets varies widely.

More questions as I stumble through...

Leam

On Thu, Jul 19, 2018 at 6:12 AM, Andy Jones <Andy.Jones@jameshall.co.uk >>>> <mailto:Andy.Jones@jameshall.co.uk>> wrote:

      You have to ask yourself: composition or mixin? The easiest way is
      with a mixin, but it has limitations.

      ~~~~
      module Engineer
         def can_do_engines?; true; end
      end

      class Person
         def initialize(name); @name = name; end
      end

      p = Person.new("fred")
      p.extend Engineer
      puts p.can_do_engines?
      ~~~~

      Note that you decorate an object --- not a class. If you want to
      add to the functionality of every instance of a class, I'm not sure
      that counts as Decorator pattern?

      If you want to do it with Composition, instead, have a look at the
      Ruby documentation for Forwardable. (Sorry this is a bit rushed;
      chaotic today...)

      I think that this might be the article that originally clued me into
      Decorator? Not sure:
      https://robots.thoughtbot.com/evaluating-alternative-decorator-

implementations-in

      <https://robots.thoughtbot.com/evaluating-alternative-decorator-

implementations-in>

      -----Original Message-----
      From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org
      <mailto:ruby-talk-bounces@ruby-lang.org>] On Behalf Of Leam Hall
      Sent: 19 July 2018 10:59
      To: Ruby users
      Subject: Class that opens and adds variables to instances of another
      class?

      I think I'm trying to figure out the Decorator pattern. What I want is
      to have a <thing> that takes an instance of <something> and changes it.
      The changes can include modifying and adding instance variables.

      Haven't quite figured it out yet, thoughts?

      Thanks!

      Leam

      Here's the error:
      ###
      Traceback (most recent call last):
      3: from test_decorator.rb:28:in `<main>'
      2: from test_decorator.rb:28:in `new'
      1: from test_decorator.rb:17:in `initialize'
      test_decorator.rb:20:in `assign_role': undefined method `role=' for
      #<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
      ###

      Code:
      ###
          1 class Person
          2 def initialize(data)
          3 @data = data
          4 end
          5 def name
          6 @data[:name] || "Fred"
          7 end
          8 end
          9
         10 class Role
         11 class Person
         12 attr_accessor :role
         13 end
         14
         15 def initialize(data)
         16 @person = data[:person]
         17 assign_role(data[:role])
         18 end
         19 def assign_role(role)
         20 @person.role = role
         21 end
         22 end
         23
         24 data = {:name => 'Al'}
         25
         26 al = Person.new(data)
         27 role_data = {:person => al, :role => 'Cadet'}
         28 Role.new(role_data)
         29 puts al.name <http://al.name>
         30 #al.role = "Cadet Sergeant"
      ###

      Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
      <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>

      Click here to view Company Information and Confidentiality
      Notice.<http://www.jameshall.co.uk/index.php/small-print/email-

disclaimer

      <http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>>

      Please note that we have updated our privacy policy in line with new
      data protection regulations. Please refer to our website to view the
      ways in which we handle your data.

      Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
      <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>


(Andy Jones) #17

Doing the Udemy "Ruby Metaprogramming" class and my brain is melting...

Metaprogramming will do that to you. To be usually filed under "too clever by half", and only occasionally justifiable...

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.


(Leam Hall) #18

time. Of course, it's also adding twice that number of confusing things...

···

On Tue, Jul 24, 2018 at 9:44 AM, Andy Jones <Andy.Jones@jameshall.co.uk> wrote:

> Doing the Udemy "Ruby Metaprogramming" class and my brain is melting...

Metaprogramming will do that to you. To be usually filed under "too
clever by half", and only occasionally justifiable...

Oddly enough it's explaining some things that have confused me for a long


(Leam Hall) #19

Hey Andy, this is my pre-workday proof of concept. Lots of ways to improve it, I'm sure. Instead of a formal DB connection I'm using JSON and passing it around. The last bit takes in the JSON and uses just what it needs. In reality, the code would add a career to the Person if there wasn't already one. I just wanted to see if I could pass the data around well.

Where there is a "# blah...blah" after a puts or a to_s, that's the output.

Does this answer the questions you raised?

Thanks!

Leam

···

#####

class Person
   require 'json'
   def initialize()
     @name = 'Al'
     @age = 14
     @gender = 'Female'
   end
   def to_s
     puts "#{@name} is a #{@age} year old #{@gender}."
   end
   def to_j
     @record = {:name => @name, :age => @age, :gender => @gender}
     @record.to_json
   end
end

al = Person.new
al.to_s # Al is a 14 year old Female.
puts al.to_j # {"name":"Al","age":14,"gender":"Female"}

module Appearance
   def hair
     @hair = 'Raider cut dishwater blond'
   end
   def frame
     @frame = 'skinny'
   end
   def appearance_update
     hair
     frame
     app_record = {:hair => @hair, :frame => @frame}
     @record = @record.merge(app_record)
     @record.to_json
   end
end

al.extend Appearance
al.appearance_update

module Cadet
   def rank
     @rank = 'Cadet Sergeant'
   end
   def age
     @age += 1
   end
   def career_to_s
     rank
     age
     puts "#{@name} is a #{@age} year old #{@gender} #{@rank}."
   end
   def career_to_j
     @career_record = {:age => @age, :rank => @rank}
     @record = @record.merge(@career_record)
     @record.to_json
   end
end

al.extend Cadet
al.career_to_s # Al is a 15 year old Female Cadet Sergeant.
al_json = al.career_to_j

puts al_json # {"name":"Al","age":15,"gender":"Female","hair":"Raider cut dishwater blond","frame":"skinny","rank":"Cadet Sergeant"}

class Crew
   require 'json'
   def initialize(data)
     @data = JSON.parse(data, :symbolize_names => true)
     @name = @data[:name]
     @rank = @data[:rank]
   end
   def to_s
     puts "Welcome aboard, #{@rank} #{@name}!"
   end
end
my_crew = Crew.new(al_json)
my_crew.to_s # Welcome aboard, Cadet Sergeant Al!

#####

On 07/24/2018 06:08 AM, Andy Jones wrote:

Well, your UDS will have to put the player data in an object of some class. If not Player, then what?

Put another way, when an object "pulls data from the UDS" -- where does it hold it?

Generally speaking when reading from a data store you want one of more "models" -- objects that represent the data to the rest of the application. It's fine if you don't want Player to be the model, but whatever "models" player information to the rest of the application will need that rank attribute (and all the others from all those decorators) or you will have the same problem.

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam Hall
Sent: 24 July 2018 10:58
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another class?

Understood. Here's how I see that being dealt with.

1. There is a Universal Data Store (UDS) of some unrestricted format.
JSON, MOngoDB, or whatever. Each program pulls from the same data store.

2. The character generate program creates basic characters.

3. The game program:
    For a new game, generates crew. Thus uses the basic character
generation program and adds specific stuff required by the game.
    For a saved game, those characters are pulled from the UDS.

4. The fiction tracker program:
    For new characters:
      Does basic character generation
      Adds data like appearance, mental workup, last known location, etc.
    Otherwise pulls the data from the UDS.
    Allows manual edits of data.
    Allows creation of new data fields.

5. The relationship tracker feeds data into Neo4J.
    A basic character from #2 has no relational data.
    A game character from #3 would have the rest of the crew as
relationships.
    A fiction character might overload Neo4J, if you've seen how
convoluted my fiction gets...

In each case the programs include the modules they need. The UDS has
*all* the data, but each program only uses parts of it. The relationship
tracker doesn't care about physical appearance, etc.

By keeping things in modules, each program can use the modules it needs.
Does that solve the problems you are seeing?

I really appreciate the feedback, you are helping clarify my thinking.

Leam

On 07/23/2018 03:26 AM, Andy Jones wrote:

Again, I'm dubious that this is the best approach, because when you get an unknown method error half way into your main, game code, and it turns out that that's because you called person.rank on a Person that doesn't have the Career_Tools mixin, that is going to be painful to debug.

Alternatively you will have to pepper your code with things like `if defined?(person.rank); rank_thing(person.rank); end` which will make the code much harder to follow and ALL the bugs harder to find.

The way I would do it, personally? I know that some Persons have a rank. So I put rank right there in the Person class. Civilians have a rank of nil. Nil values are a PITA but considerably less so than missing methods! So now I can at least do `rank_thing(person.rank)` and I guess rank_thing starts with `return unless rank`...

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam
Hall
Sent: 21 July 2018 1:19 am
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another
class?

And this is how I spend my Friday night...

Thoughts? In this case Career_Tools has to account for anything in any
of the careers. Most of the time those things are pretty simple.

####

class Person
     def initialize(data)
       @name = data[:name]
     end
     def name
       @name
     end
end

module Career_Tools
     def rank
       @rank
     end
     def rank=(career)
       @rank = career.ranks
     end
end

module Relationships
     def friends
       @friends
     end
     def friends=(l)
       if @friends.nil?
         @friends = Array.new
       end
       @friends << l
     end
end

class Cadet
     def ranks
       ['Cadet', 'Cadet Corporal', 'Cadet Sergeant', 'Cadet
Leiutenant'].sample
     end
end

### main
person_data = {:name => 'Al'}
al = Person.new(person_data)
puts al.name
al.extend Career_Tools
al.extend Relationships
al.friends = "CC One"
al.friends = "Wilbur"
puts al.friends.join(", ")
career = Cadet.new
al.rank=(career)
puts al.rank

####

On 07/20/2018 03:48 PM, leam hall wrote:

This will be part of the weekend's joy.

There are lots of roles, and most instances of Person get at least one.
A few instances get more than one, but which role a particular instance
gets varies widely.

More questions as I stumble through...

Leam

On Thu, Jul 19, 2018 at 6:12 AM, Andy Jones <Andy.Jones@jameshall.co.uk >>>> <mailto:Andy.Jones@jameshall.co.uk>> wrote:

      You have to ask yourself: composition or mixin? The easiest way is
      with a mixin, but it has limitations.

      ~~~~
      module Engineer
         def can_do_engines?; true; end
      end

      class Person
         def initialize(name); @name = name; end
      end

      p = Person.new("fred")
      p.extend Engineer
      puts p.can_do_engines?
      ~~~~

      Note that you decorate an object --- not a class. If you want to
      add to the functionality of every instance of a class, I'm not sure
      that counts as Decorator pattern?

      If you want to do it with Composition, instead, have a look at the
      Ruby documentation for Forwardable. (Sorry this is a bit rushed;
      chaotic today...)

      I think that this might be the article that originally clued me into
      Decorator? Not sure:
      https://robots.thoughtbot.com/evaluating-alternative-decorator-

implementations-in

      <https://robots.thoughtbot.com/evaluating-alternative-decorator-

implementations-in>

      -----Original Message-----
      From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org
      <mailto:ruby-talk-bounces@ruby-lang.org>] On Behalf Of Leam Hall
      Sent: 19 July 2018 10:59
      To: Ruby users
      Subject: Class that opens and adds variables to instances of another
      class?

      I think I'm trying to figure out the Decorator pattern. What I want is
      to have a <thing> that takes an instance of <something> and changes it.
      The changes can include modifying and adding instance variables.

      Haven't quite figured it out yet, thoughts?

      Thanks!

      Leam

      Here's the error:
      ###
      Traceback (most recent call last):
      3: from test_decorator.rb:28:in `<main>'
      2: from test_decorator.rb:28:in `new'
      1: from test_decorator.rb:17:in `initialize'
      test_decorator.rb:20:in `assign_role': undefined method `role=' for
      #<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
      ###

      Code:
      ###
          1 class Person
          2 def initialize(data)
          3 @data = data
          4 end
          5 def name
          6 @data[:name] || "Fred"
          7 end
          8 end
          9
         10 class Role
         11 class Person
         12 attr_accessor :role
         13 end
         14
         15 def initialize(data)
         16 @person = data[:person]
         17 assign_role(data[:role])
         18 end
         19 def assign_role(role)
         20 @person.role = role
         21 end
         22 end
         23
         24 data = {:name => 'Al'}
         25
         26 al = Person.new(data)
         27 role_data = {:person => al, :role => 'Cadet'}
         28 Role.new(role_data)
         29 puts al.name <http://al.name>
         30 #al.role = "Cadet Sergeant"
      ###

      Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
      <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>

      Click here to view Company Information and Confidentiality
      Notice.<http://www.jameshall.co.uk/index.php/small-print/email-

disclaimer

      <http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>>

      Please note that we have updated our privacy policy in line with new
      data protection regulations. Please refer to our website to view the
      ways in which we handle your data.

      Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
      <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>


(Andy Jones) #20

1) All your classes output JSON, which is nice, but you don't show any classes that read that JSON, and I'm pretty certain that at that point you will find you want a single object, a Person model, to read in the JSON and represent it to the rest of the code. (And if you are going to do that later, you might as well do that now?)

2a) If all your person objects get decorated with Appearance and Cadet, then there is no point using Decorator Pattern. You might as well put it all in one class. Or use some other way to split up the code, which I agree might be a good idea if that is your intent. (Method Classes still seem the way to go, for me, in that eventuality.)

2b) If only some of the person objects get decorated, then you will never be able to call `person.rank` safely because you won't know if the object has a rank method or not. If this isn't a problem because you never plan to use these objects once they have built JSON for you, then you might as well just have a class to build JSON for you, and not represent person as an object at all. Which brings us back to (1).

TL;DR: My recommendation would be: Design the object that needs to read and represent all this JSON about a person first. Call _that_ the Person class, and work from there. It might clarify your thinking, and probably make for much shorter code.

Hope that helps...

···

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam Hall
Sent: 27 July 2018 11:19
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another class?

Hey Andy, this is my pre-workday proof of concept. Lots of ways to
improve it, I'm sure. Instead of a formal DB connection I'm using JSON
and passing it around. The last bit takes in the JSON and uses just what
it needs. In reality, the code would add a career to the Person if there
wasn't already one. I just wanted to see if I could pass the data around
well.

Where there is a "# blah...blah" after a puts or a to_s, that's the output.

Does this answer the questions you raised?

Thanks!

Leam

#####

class Person
   require 'json'
   def initialize()
     @name = 'Al'
     @age = 14
     @gender = 'Female'
   end
   def to_s
     puts "#{@name} is a #{@age} year old #{@gender}."
   end
   def to_j
     @record = {:name => @name, :age => @age, :gender => @gender}
     @record.to_json
   end
end

al = Person.new
al.to_s # Al is a 14 year old Female.
puts al.to_j # {"name":"Al","age":14,"gender":"Female"}

module Appearance
   def hair
     @hair = 'Raider cut dishwater blond'
   end
   def frame
     @frame = 'skinny'
   end
   def appearance_update
     hair
     frame
     app_record = {:hair => @hair, :frame => @frame}
     @record = @record.merge(app_record)
     @record.to_json
   end
end

al.extend Appearance
al.appearance_update

module Cadet
   def rank
     @rank = 'Cadet Sergeant'
   end
   def age
     @age += 1
   end
   def career_to_s
     rank
     age
     puts "#{@name} is a #{@age} year old #{@gender} #{@rank}."
   end
   def career_to_j
     @career_record = {:age => @age, :rank => @rank}
     @record = @record.merge(@career_record)
     @record.to_json
   end
end

al.extend Cadet
al.career_to_s # Al is a 15 year old Female Cadet Sergeant.
al_json = al.career_to_j

puts al_json #
{"name":"Al","age":15,"gender":"Female","hair":"Raider cut dishwater
blond","frame":"skinny","rank":"Cadet Sergeant"}

class Crew
   require 'json'
   def initialize(data)
     @data = JSON.parse(data, :symbolize_names => true)
     @name = @data[:name]
     @rank = @data[:rank]
   end
   def to_s
     puts "Welcome aboard, #{@rank} #{@name}!"
   end
end
my_crew = Crew.new(al_json)
my_crew.to_s # Welcome aboard, Cadet Sergeant Al!

#####

On 07/24/2018 06:08 AM, Andy Jones wrote:

Well, your UDS will have to put the player data in an object of some class. If not Player, then what?

Put another way, when an object "pulls data from the UDS" -- where does it hold it?

Generally speaking when reading from a data store you want one of more "models" -- objects that represent the data to the rest of the application. It's fine if you don't want Player to be the model, but whatever "models" player information to the rest of the application will need that rank attribute (and all the others from all those decorators) or you will have the same problem.

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam Hall
Sent: 24 July 2018 10:58
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another class?

Understood. Here's how I see that being dealt with.

1. There is a Universal Data Store (UDS) of some unrestricted format.
JSON, MOngoDB, or whatever. Each program pulls from the same data store.

2. The character generate program creates basic characters.

3. The game program:
    For a new game, generates crew. Thus uses the basic character
generation program and adds specific stuff required by the game.
    For a saved game, those characters are pulled from the UDS.

4. The fiction tracker program:
    For new characters:
      Does basic character generation
      Adds data like appearance, mental workup, last known location, etc.
    Otherwise pulls the data from the UDS.
    Allows manual edits of data.
    Allows creation of new data fields.

5. The relationship tracker feeds data into Neo4J.
    A basic character from #2 has no relational data.
    A game character from #3 would have the rest of the crew as
relationships.
    A fiction character might overload Neo4J, if you've seen how
convoluted my fiction gets...

In each case the programs include the modules they need. The UDS has
*all* the data, but each program only uses parts of it. The relationship
tracker doesn't care about physical appearance, etc.

By keeping things in modules, each program can use the modules it needs.
Does that solve the problems you are seeing?

I really appreciate the feedback, you are helping clarify my thinking.

Leam

On 07/23/2018 03:26 AM, Andy Jones wrote:

Again, I'm dubious that this is the best approach, because when you get an unknown method error half way into your main, game code, and it turns out that that's because you called person.rank on a Person that doesn't have the Career_Tools mixin, that is going to be painful to debug.

Alternatively you will have to pepper your code with things like `if defined?(person.rank); rank_thing(person.rank); end` which will make the code much harder to follow and ALL the bugs harder to find.

The way I would do it, personally? I know that some Persons have a rank. So I put rank right there in the Person class. Civilians have a rank of nil. Nil values are a PITA but considerably less so than missing methods! So now I can at least do `rank_thing(person.rank)` and I guess rank_thing starts with `return unless rank`...

-----Original Message-----
From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Leam
Hall
Sent: 21 July 2018 1:19 am
To: Ruby users
Subject: Re: Class that opens and adds variables to instances of another
class?

And this is how I spend my Friday night...

Thoughts? In this case Career_Tools has to account for anything in any
of the careers. Most of the time those things are pretty simple.

####

class Person
     def initialize(data)
       @name = data[:name]
     end
     def name
       @name
     end
end

module Career_Tools
     def rank
       @rank
     end
     def rank=(career)
       @rank = career.ranks
     end
end

module Relationships
     def friends
       @friends
     end
     def friends=(l)
       if @friends.nil?
         @friends = Array.new
       end
       @friends << l
     end
end

class Cadet
     def ranks
       ['Cadet', 'Cadet Corporal', 'Cadet Sergeant', 'Cadet
Leiutenant'].sample
     end
end

### main
person_data = {:name => 'Al'}
al = Person.new(person_data)
puts al.name
al.extend Career_Tools
al.extend Relationships
al.friends = "CC One"
al.friends = "Wilbur"
puts al.friends.join(", ")
career = Cadet.new
al.rank=(career)
puts al.rank

####

On 07/20/2018 03:48 PM, leam hall wrote:

This will be part of the weekend's joy.

There are lots of roles, and most instances of Person get at least one.
A few instances get more than one, but which role a particular instance
gets varies widely.

More questions as I stumble through...

Leam

On Thu, Jul 19, 2018 at 6:12 AM, Andy Jones <Andy.Jones@jameshall.co.uk >>>> <mailto:Andy.Jones@jameshall.co.uk>> wrote:

      You have to ask yourself: composition or mixin? The easiest way is
      with a mixin, but it has limitations.

      ~~~~
      module Engineer
         def can_do_engines?; true; end
      end

      class Person
         def initialize(name); @name = name; end
      end

      p = Person.new("fred")
      p.extend Engineer
      puts p.can_do_engines?
      ~~~~

      Note that you decorate an object --- not a class. If you want to
      add to the functionality of every instance of a class, I'm not sure
      that counts as Decorator pattern?

      If you want to do it with Composition, instead, have a look at the
      Ruby documentation for Forwardable. (Sorry this is a bit rushed;
      chaotic today...)

      I think that this might be the article that originally clued me into
      Decorator? Not sure:
      https://robots.thoughtbot.com/evaluating-alternative-decorator-

implementations-in

      <https://robots.thoughtbot.com/evaluating-alternative-decorator-

implementations-in>

      -----Original Message-----
      From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org
      <mailto:ruby-talk-bounces@ruby-lang.org>] On Behalf Of Leam Hall
      Sent: 19 July 2018 10:59
      To: Ruby users
      Subject: Class that opens and adds variables to instances of another
      class?

      I think I'm trying to figure out the Decorator pattern. What I want is
      to have a <thing> that takes an instance of <something> and changes it.
      The changes can include modifying and adding instance variables.

      Haven't quite figured it out yet, thoughts?

      Thanks!

      Leam

      Here's the error:
      ###
      Traceback (most recent call last):
      3: from test_decorator.rb:28:in `<main>'
      2: from test_decorator.rb:28:in `new'
      1: from test_decorator.rb:17:in `initialize'
      test_decorator.rb:20:in `assign_role': undefined method `role=' for
      #<Person:0x0000562e9a870978 @data={:name=>"Al"}> (NoMethodError)
      ###

      Code:
      ###
          1 class Person
          2 def initialize(data)
          3 @data = data
          4 end
          5 def name
          6 @data[:name] || "Fred"
          7 end
          8 end
          9
         10 class Role
         11 class Person
         12 attr_accessor :role
         13 end
         14
         15 def initialize(data)
         16 @person = data[:person]
         17 assign_role(data[:role])
         18 end
         19 def assign_role(role)
         20 @person.role = role
         21 end
         22 end
         23
         24 data = {:name => 'Al'}
         25
         26 al = Person.new(data)
         27 role_data = {:person => al, :role => 'Cadet'}
         28 Role.new(role_data)
         29 puts al.name <http://al.name>
         30 #al.role = "Cadet Sergeant"
      ###

      Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
      <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>

      Click here to view Company Information and Confidentiality
      Notice.<http://www.jameshall.co.uk/index.php/small-print/email-

disclaimer

      <http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>>

      Please note that we have updated our privacy policy in line with new
      data protection regulations. Please refer to our website to view the
      ways in which we handle your data.

      Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
      <mailto:ruby-talk-request@ruby-lang.org>?subject=unsubscribe>
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk
      <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.