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


(Leam Hall) #21

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?)

Check out "class Crew" near the end. It takes in JSON and sets attributes based on values. In theory it could be more complex code, but that's the basics.

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.)

Not all will, and there are lots of cases where there will be very minimal details. For example, something like this (mostly pseudo code.)

stranger = Person.new
"You get a message from #{stranger.name}. #{stranger.gender == 'M' ? 'He' : 'She'} seems to know a lot about what you're doing."

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).

Each program needs certain data about the instance. An instance of Crew needs rank and skills, so it would pull in the JSON and test for those things. If they did not exist, use the shared Character_Tools module to generate them. Each program only needs to ensure what it needs is there.

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.

That was the original design but it kept growing into a mess. Using at hoc modules seems to clean it up.

Leam

···

On 07/27/2018 06:54 AM, Andy Jones wrote:

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.

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


(Leam Hall) #22

Ah...I may be understanding. Slowly. Let me see if I have it.

1. There is a file called 'person.rb' that sets the basic Person class.
  class Person
    def initialize(data)
      @name = data[:name]
    end
  end

2. There is a program called "Starship" that uses 'crew.rb', a file that opens the Person class.
  class Person
    attr_reader :rank
    def setup_crew(data)
      @rank = data[:rank]
    end
  end

3. Starship looks something like this:
  # Create data from UDS pull.
  data = {:name => 'Al', :rank => 'Captain'}

  al = Person.new(data)
  require 'crew'
  al.setup_crew(data)
  puts al.rank # "Captain" She was promoted. :slight_smile:

Is that what you're suggesting? I can pull the data from the UDS, put it into a hash, and then send it to each "setup_" method.

Leam

···

On 07/27/2018 06:54 AM, Andy Jones wrote:

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.

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


(Dmitriy Non) #23

Hi!

Sorry, I didn't read the whole thread. I was just curious what was the initial question.

I have some thoughts/advices/opinions on this:

* Role::Person naming is just bad. I had to actually read the implementation to understand
  what it is.I advice you not to use same constant names in Ruby. Even though it doesnt really "break" ruby
  you can shoot yourself in a leg with it.
* Well, at this point I know that I got a wrong idea about what you were trying to achieve.
  Problem in your code is that you think `class Role; class Person ...` actually changes original Person class.
  Well, it doesn't (I guess someone already pointed that for you). Classes are namespaces too, so `Person` and `Role::Person`
  are actually absolutely different constants that are not connected in any way.
* I like the idea of "mutability is bad". It really helps you to write better code.
  So don't mutate original instance with decorators. Try using more "classic" (or java-ish) approach:

  class Person
    # ...
  end

  class PersonWithRole
    attr_accessor :role
    attr_reader :decorated_person
    # it's from rails but you get the idea
    delegate :name, to: :decorated_person

    def initialize(person)
      @decorated_person = person
    end

    # ...
  end

  Decorator is like a wrapper over some object that provides additional functionality.
  My `PersonWithRole` does not even care about implementation of decorated person. It just adds `role` accessor
  that is stored in wrapper itself and proxies method calls to wrapped object (I did it with `delegate` but I guess
  that `method_missing` is better approach).
  If you want different namespace for decorators you can try something like `PersonDecorators::WithRole`

You should definetely read about Ruby constant lookup. I actually wrote a post on the toppic but it's in russian:(
I recommend you to read:

* Chapter 7.9 of "The Ruby Programming Language" (from Matz)
* https://guides.rubyonrails.org/autoloading_and_reloading_constants.html
* Play around with [Module.nesting](http://ruby-doc.org/core-2.2.0/Module.html#method-c-nesting)

I hope my late letter will be helpful. Sorry for my english

···

--------------------------------------
Dmitriy Non
non.dmitriy@gmail.com

On 19 Jul 2018, at 12:58, 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"
###

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


(Dmitriy Non) #24

I forgot to mention that "classic" decorators are nice because
they compose just fine as long as they don't have colliding public methods.

For example:

class Person
  def say_hi; 'hi!' end  
end

class PersonDecorator
  attr_reader :decorated_person
  def initialize(person)
    @decorated_person = person
  end

  # just send other calls to original person
  def method_missing(m, *args, &block)
    decorated_person.send(m, *args, &block)
  end
end

class PersonWithName < PersonDecorator
  attr_reader :name
  def initialize(person, name)
    super(person)
    @name = name
  end
end

class PersonWithAge < PersonDecorator
  attr_reader :age
  def initialize(person, age)
    super(person)
    @age = age
  end
end

person = PersonWithName.new(PersonWithAge.new(Person.new, 18), 'John Doe')
puts person.name
puts person.age
puts person.say_hi

Though there may be some problems when you are overriding original methods
but I am sure you will know how to solve this issue when you face it.

···

--------------------------------------
Dmitriy Non
non.dmitriy@gmail.com

On 29 Jul 2018, at 02:05, Dmitriy Non <non.dmitriy@gmail.com> wrote:

Hi!

Sorry, I didn't read the whole thread. I was just curious what was the initial question.

I have some thoughts/advices/opinions on this:

* Role::Person naming is just bad. I had to actually read the implementation to understand
what it is.I advice you not to use same constant names in Ruby. Even though it doesnt really "break" ruby
you can shoot yourself in a leg with it.
* Well, at this point I know that I got a wrong idea about what you were trying to achieve.
Problem in your code is that you think `class Role; class Person ...` actually changes original Person class.
Well, it doesn't (I guess someone already pointed that for you). Classes are namespaces too, so `Person` and `Role::Person`
are actually absolutely different constants that are not connected in any way.
* I like the idea of "mutability is bad". It really helps you to write better code.
So don't mutate original instance with decorators. Try using more "classic" (or java-ish) approach:

 class Person
   # ...
 end

 class PersonWithRole
   attr_accessor :role
   attr_reader :decorated_person
   # it's from rails but you get the idea
   delegate :name, to: :decorated_person

   def initialize(person)
     @decorated_person = person
   end

   # ...
 end

Decorator is like a wrapper over some object that provides additional functionality.
My `PersonWithRole` does not even care about implementation of decorated person. It just adds `role` accessor
that is stored in wrapper itself and proxies method calls to wrapped object (I did it with `delegate` but I guess
that `method_missing` is better approach).
If you want different namespace for decorators you can try something like `PersonDecorators::WithRole`

You should definetely read about Ruby constant lookup. I actually wrote a post on the toppic but it's in russian:(
I recommend you to read:

* Chapter 7.9 of "The Ruby Programming Language" (from Matz)
* https://guides.rubyonrails.org/autoloading_and_reloading_constants.html
* Play around with [Module.nesting](http://ruby-doc.org/core-2.2.0/Module.html#method-c-nesting)

I hope my late letter will be helpful. Sorry for my english

--------------------------------------
Dmitriy Non
non.dmitriy@gmail.com

On 19 Jul 2018, at 12:58, 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"
###

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


(Andy Jones) #25

Ah...I may be understanding. Slowly. Let me see if I have it.

1. There is a file called 'person.rb' that sets the basic Person class.
class Person
  def initialize(data)
    @name = data[:name]
  end
end

2. There is a program called "Starship" that uses 'crew.rb', a file that
opens the Person class.
class Person
  attr_reader :rank
  def setup_crew(data)
    @rank = data[:rank]
  end
end

3. Starship looks something like this:
# Create data from UDS pull.
data = {:name => 'Al', :rank => 'Captain'}

al = Person.new(data)
require 'crew'
al.setup_crew(data)
puts al.rank# "Captain" She was promoted. :slight_smile:

Is that what you're suggesting? I can pull the data from the UDS, put it
into a hash, and then send it to each "setup_" method.

Leam

···

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

On 07/27/2018 06:54 AM, Andy Jones wrote:

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.

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.


(Andy Jones) #26

TL;DR: Okay, look, if it were me I _really_ wouldn't do it this way, because it's going to be hell on wheels to debug. You're free to do it how you like, of course, but...

2. There is a program called "Starship" that uses 'crew.rb', a file that
opens the Person class.

Now every time you look at code that uses Person you will have to work out whether that code has also required crew.rb previously, because that will change the behaviour of Person. (In addition to working out which decorators were run on the specific instance of Person you are looking at...)

For my money it's much, much better to put the definition of Person in one file, person.rb, and know it won't change during the project. This is kind of fundamental to OOP, IMO: a class is a thing. It's not one of two things, depending.

3. Starship looks something like this:
# Create data from UDS pull.
data = {:name => 'Al', :rank => 'Captain'}

Where is this JSON data coming from? Does every class have the responsibility of parsing that JSON file? What if sections of that JSON file end up requiring non-trivial code to parse -- how many classes will that effect? If the format of the JSON file actually changes, how many classes will you have to change?

Again, in my opinion it's way better to have a single class that parses the JSON and creates a person from it. Then pass instances of that class to the other classes, not the JSON data.

Again, this is kind of fundamental to OOP: the "Single Responsibility Principal". Each class should do just one thing.

IMO the logical name for the class that represents person data to the rest of the application is ... Person. That's your person class.

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) #27

Yes, changes to Person and the JSON data will propagate. Already did
that with the UPP going from a string of Hexidecimals to a Hash of
Int.

However, there's really just one Person class. The data is passed to
it to create the base person object and then supplementary modules are
mixed in to expand the methods and attributes Person is aware of.

···

On Mon, Jul 30, 2018 at 3:29 AM, Andy Jones <Andy.Jones@jameshall.co.uk> wrote:

TL;DR: Okay, look, if it were me I _really_ wouldn't do it this way, because it's going to be hell on wheels to debug. You're free to do it how you like, of course, but...

2. There is a program called "Starship" that uses 'crew.rb', a file that
opens the Person class.
<<<<<<<<

Now every time you look at code that uses Person you will have to work out whether that code has also required crew.rb previously, because that will change the behaviour of Person. (In addition to working out which decorators were run on the specific instance of Person you are looking at...)

For my money it's much, much better to put the definition of Person in one file, person.rb, and know it won't change during the project. This is kind of fundamental to OOP, IMO: a class is a thing. It's not one of two things, depending.

3. Starship looks something like this:
# Create data from UDS pull.
data = {:name => 'Al', :rank => 'Captain'}
<<<<<<<<

Where is this JSON data coming from? Does every class have the responsibility of parsing that JSON file? What if sections of that JSON file end up requiring non-trivial code to parse -- how many classes will that effect? If the format of the JSON file actually changes, how many classes will you have to change?

Again, in my opinion it's way better to have a single class that parses the JSON and creates a person from it. Then pass instances of that class to the other classes, not the JSON data.

Again, this is kind of fundamental to OOP: the "Single Responsibility Principal". Each class should do just one thing.

IMO the logical name for the class that represents person data to the rest of the application is ... Person. That's your person class.

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) #28

Funny note: I had set POODR down for a week or two (or three). Picked it up last night before bed and read a section on hook methods which may exactly solve this issue. Code when I have it.