[info] Using Ruby Modules

[info] Using Ruby Modules

Postby Dan Rathbun » Thu Jul 15, 2010 9:13 am


[info] Using Ruby Modules

This is an informational thread.
Please do not use this thread to discuss issues relating to Using Ruby Modules,
instead please use the talk thread: [talk] Using Ruby Modules


ben.doherty in Re: entering components wrote: I am having lots of trouble with modules though, if I ask the ruby console for Voyeur::FaceMaker::instance_methods it gives me back a list of the methods as I'd expect, but if I say Voyeur::FaceMaker.makeAnalysisFaces it throws a #<NoMethodError.... any idea how to fix that?

OK...

(1) instance methods are really meant for Classes, where an instance (a child copy,) of a class inherits the method, that is always evaluated within the context of the instance copy, and not the Class itself. This is the opposite of a Class method.

(2) "But why.." you ask "then are instance methods allowed to be declared within modules?"
The answer is that modules can be used in a special purpose library way, called a "Mixin Module". Where you can encapsulate a set of instance methods in the "Mixin", and then mix it into a normal module, a class defintion, and even a specific instance object; and they will then have those methods.
However what "kind" of method that the resultant method becomes in the "mixee" object, depends on:
  1. what kind of method it was in the Mixin Library module
  2. whether the "mixee" is a Module or Class or Instance
  3. and whether the "mixing" is done by include or extend.
It IS confusing and I have trouble keeping track, myself. I started to make up a table showing the various combinations, as a quick reference, and using a test mixin module, but have not yet completed it.

(3) Your not doing a mixin module, so you don't want to worry about that right now. What you want, when you wish to call methods from outside the module, is called a "module method" or a "module function". Where the name of the specfic module (with or without namespace qualification,) is used as the "receiver" in the call, as your example shows:
Voyeur::FaceMaker.makeAnalysisFaces

There are 3 ways, within the module definition to explicitly, define a method as a module method:
  • def Facemaker.makeAnalysisFaces
  • def self.makeAnalysisFaces
  • use module_function(:makeAnalysisFaces) after the method definition. The arg list can list more than one methodname. Or you can use it at the top (without an argument,) before multiple method def blocks and all of them are effected until you declare public or private again. However, that feature actually creates two copies of each method within the module, which doubles it's memory cost.
But the easiest way, (the implicit form,) if you have many methods that need to be module methods, is to wrap ALL of them within a class << self block.
Example:
Code: Select all
module Voyeur
  module FaceMaker

if( not file_loaded?("makeTheFaces.rb") )
  # menu setup
  file_loaded("makeTheFaces.rb") # <<-- NEED
end

class << self

  private

  def methOne
    return "This is from methOne "
  end

  def methTwo
    return "and from methTwo."
  end

  public

  def methExternal
    return methOne << methTwo
  end

  # etc.

end # self

  end # Facemaker
end # Voyeur
EDIT
As a sidenote, it can be done as is (I think,) in a convoluted way, via:
Voyeur::FaceMaker.method('makeAnalysisFaces').call
but of course that's silly, and defeats the purpose of modular programmimg and elegance.
Nope.. not right. Cannot be done that way. Instance methods are just only meant for Mixin Modules.

So my advice is:

(a) In normal modules: wrap ALL methods in a class << self block.
(b) use the methods public and private to mark which ones are externally called and only internally called (respectively.)
(c) Ruby likes to have methods defined before they get called, so probably those that are private will be at the top of the block.
[Example above shows this...]

NOW.. most important to coders migrating to using modules: intenal private methods can now be called from other internal methods without qualificaton (ie: methodname instead of self.methodname )


Please do not use this thread to discuss issues relating to Using Ruby Modules,
instead please use the talk thread: [talk] Using Ruby Modules
0
Last edited by Dan Rathbun on Mon Jul 26, 2010 12:40 am, edited 1 time in total.
    I'm not here much anymore. But a PM will fire email notifications.
    User avatar
    Dan Rathbun 
    PluginStore Author
    PluginStore Author
     

    Re: [info] Ruby Modules - are they necessary?

    Postby Dan Rathbun » Sat Jul 24, 2010 8:11 pm

    Condensed, Simplified and Reposted from another topic thread.

    Modules are important and very necessary.

    If you (speaking generally to all readers and especially newbies,) do not understand the importance of modules, then you do not understand how Ruby operates and how code is (and should be,) loaded.

    Modules separate your code from other people's code, and prevent your code from "crapping" on the ObjectSpace (which is the same as "crapping" on everyone else's modules and classes, including Google's and Ruby's.) ANY script that defines methods, instance variables, class variables or constants in the ObjectSpace IS "crap." It does not matter if Google did it in their examples, it is still "crap."

    Not only should you be using A module around your script... you should be using nested modules. The outermost is your TOP_LEVEL namespace.

    Any tool or plugin you create should be in a nested module (submodule,) of your TOP_LEVEL namespace. This also allows individual tools or plugins to be removed from memory when they are no longer being used.
    (It also means Google incorrectly put their modules at the toplevel, when they should have been within module Google. What happens in the future when Layout gets an API, and/or Picasa gets a Ruby API that integrates to Sketchup and Layout? But that could be a whole other discussion topic.)

    Concerning indenting... anticpating a chorus of whiney voices, thus:

    "Oh man, nested modules mean I have to waste so much space on the left of my code, and push the "meat" of my rubies way to the right! And then I have to hit that TAB key all the time on every line... boohoo!"

    No you don't! Ruby allows multiple scope defintions !

    If I wish to define a class within a submodule of my toplevel module, I can do so and only have 1 indent:
    Code: Select all
    # The Outer namespace modules must exist before referencing them.

    require('myfolder/MyTopLevel.rb')
    require('myfolder/myplugin/MyPlugin.rb')

    class MyTopLevel::MyPlugin::ThisClass
      def initialize(*args)
        # INIT CODE GOES HERE
      end # def
    end # class MyTopLevel::MyPlugin::ThisClass


    I can also save an indent when defining a plugin module by doing:
    Code: Select all
    # The Outer namespace module must exist before referencing it.

    require('myfolder/MyTopLevel.rb')

    module MyTopLevel::MyPluginTwo
      class<<self
        private
        def method_one
          # .. code ..
        end
        public
        def method_two
          # .. code ..
        end
        # .. etc ..
      end # self
    end # module MyTopLevel::MyPluginTwo
    EDIT: Ruby v1.8.0 does not allow the TopLevel scope operator prefix, ie:
    class ::MyTopLevel::MyPlugin::ThisClass
    v1.8.6 does allow it. It basically tells Ruby to start looking at the TopLevel for an indentifier, instead of backing up thru each nesting level from the current level.
    (I've removed the use of it in these examples, as many users will be running v1.8.0 on Win32, and not know how to update their Sketchup Ruby.)


    instead of doing this:
    Code: Select all
    module MyTopLevel
      module MyPluginTwo
        class<<self
          private
          def method_one
            # .. code ..
          end
          public
          def method_two
            # .. code ..
          end
          # .. etc ..
        end # self
      end # module MyPluginTwo
    end # module MyTopLevel


    So choose a TopLevel namespace (module name,) that is unique. Use part of your surname. Or if your plugins will be distributed under a brand or company name, then that would be a good toplevel module name. Or you could use your SCF screen name, as long as it's unique.

    Just don't use a trademarked brandname(s) if your not the owner of the trademark!!


    Please do not use this thread to discuss issues relating to Using Ruby Modules,
    instead please use the talk thread: [talk] Using Ruby Modules
    0
    Last edited by Dan Rathbun on Sat Jul 31, 2010 3:59 am, edited 3 times in total.
      I'm not here much anymore. But a PM will fire email notifications.
      User avatar
      Dan Rathbun 
      PluginStore Author
      PluginStore Author
       

      Re: [info] Ruby Modules - double wrapping?

      Postby Dan Rathbun » Sat Jul 24, 2010 8:15 pm

      Chris Fullmer wrote: @Dan - the double wrapping thing sounds excessive,...

      It's not really excessive... didn't Google wrap (most) all their classes inside the Sketchup namespace ?? (and a few others inside the Geom and UI namespaces.)
      Think of these analogies...
      (1) When you receive your paycheck.. do you go to the bank and have the teller randomly distribute the funds into other people's accounts? Of course not. Because the whole purpose of accounts (ie, accounting,) is to identify and separate whose funds belong to whom. Imagine the chaos if everyone in your town had to share the same bank account.
      (2) You own valuable personal posessions (papers, jewelery, antiques etc.) Do you store these personal items in the homes of strangers or your own home? Of course you store them in your own home. Your "homespace" is a separation between those things that belong to you, and the world of things that belong to others or the public.

      Restating what I've said earlier, and in other threads, YOUR toplevel namespace:
      (a) Prevents you from trashing the ObjectSpace (which will trash the rest of the world's classes and modules.)
      (b) Protects their code from your code.
      (c) Protects your code from other people's code (if they don't trash the ObjectSpace.)
      (d) Allows you to not worry about whether someone else has used the same Identifier for a plugin or tool. Example: a "Cylinder" plugin (there are several of these all named 'cylinder.rb' floating around.) But yours would be (examplizing,) FullmerCode::Cylinder and say TIG's would be TIG::Tool::Cylinder they would not clash. They are different modules or classes. It's simply jurisdictional addressing. (Like how [in a perfect world,] the mail addressed to you gets delivered to your house instead of TIG's.)

      Chris Fullmer wrote:...unless it is true that you say it makes it so SU can unload tools or modules from memmory when they are not being used? If that is true, then I think I'll jump on your double wrapping bandwagon.

      Well.. I prefer not to "talk out my rear-end" so I wouldn't purposely mislead you or anyone else. Butt (pun intended,) your question made made double check, and I sort of mis-read or mis-assumed more into what the book said. I'll explain more below, however it does not negate the good reasons listed above for using a personal toplevel namespace, which are the reasons that they were invented to begin with. (read on..)

      Chris Fullmer wrote:I have only done a limited amount of reading on Ruby and never seen that mentioned, but I have not read very much in depth info like that.

      Let me endeavour to provide some citations.. here:
      From the "Pick-Axe book" (aka "Programming Ruby: The Pragmatic Programmer's Guide" by Dave Thomas)http://phrogz.net/ProgrammingRuby/ref_c_module.html#Module.remove_const
      from the 'Pick-Axe book' wrote:
      class Module
      private methods

      remove_const( aSymbol ) -> anObject

      "Removes the definition of the given constant, returning that constant's value. Predefined classes and singleton objects (such as true) cannot be removed."
      Remember that module and class identifiers are actually Constants that reference Ruby objects. A custom written class or module is actually an instance of class Class and class Module, respectively. The call to the new method that creates these instances is really done by the interpreter (transparently,) when it parses a class or module definition.
      When a Ruby object becomes unreferenced, it can then be garbage collected via GC.start, and this includes instances of class Class and class Module. However it would be a smart thing to first check that all instances of a class are unreferenced (GC'd) before removing their class definition.

      Drawing your attention to the 2nd sentence, this is where I made the wrong assuption.

      I assumed the reason was that remove_const is a private method of class Module, and that Object, being the superclass of all classes, would not have that method. But it does. It gets it from module Kernel, which is a module, and is 'mixed-in' to class Object. So.. Object inherits a private remove_const method.

      Base classes are not supposed to be removable, because the Ruby C source for the remove_const method is "supposedly" hardcoded not to. But there is (IMHO,) a bug in 1.8.6 that still removes the Constant pointer at the Toplevel. If I run:
      Object.instance_eval("remove_const(:Numeric)")
      I can no longer refer to the class by 'Numeric', but it's not gone. ie:
      Float.superclass.name >> Numeric

      Now.. this is a Ruby bug. And it may be fixed in the 1.9.x branch, but I'll need to do some checking. Just because it does not yet work the way they intended, doesn't mean it won't when they fix it. (Oh that was brillant!)

      Think of this... if you wish to manage the memory use of your plugins, where are you going to do it? Wouldn't the best place for it be within your Toplevel module (namespace) AND therefore, in the outer scope of the plugin submodule that you will remove? Since remove_const is a private method, it's best called from within that Toplevel module, although as I showed above, it might be done from outside using the instance_eval method. However I personally remove that method (as well as eval and module_eval,) from any of my modules that I don't want "outsiders" messing with when I can't freeze it for other reasons.

      In another thread post, I mentioned a Toilet module with a flush method.
      Code: Select all
      module Toilet
        def flush(arg)
          if arg.class==(Symbol) || arg.class.kind_of?(String)
            arg=arg.to_s
            if Object.eval("defined?(::Toilet::#{arg})=='module'")
              remove_const(arg)
            else
              $stderr.write("Warning: ::Toilet::#{arg} was undefined or not a module.")
            end
          else
            raise(TypeError,"String or Symbol argument required.")
          end
        end
      end
      So any submodule name could be passed to the Toplevel module's flush method, not just submodule :Crap. Your choice is to either have your Toplevel module be the manager of your plugins, or create a manager submodule that does it.


      Please do not use this thread to discuss issues relating to Using Ruby Modules,
      instead please use the talk thread: [talk] Using Ruby Modules
      0
      Last edited by Dan Rathbun on Mon Jul 26, 2010 12:43 am, edited 1 time in total.
        I'm not here much anymore. But a PM will fire email notifications.
        User avatar
        Dan Rathbun 
        PluginStore Author
        PluginStore Author
         

        Re: [info] Using Ruby Modules

        Postby Morgan74 » Mon Jul 26, 2010 10:49 am

        Thanks Dam for this interesting topic. I was rewriting all my plugin's code when I saw this thread.
        So the only thing I need to do to follow you is adding a "TOP_LEVEL" namespace to my modules.

        But I have a question about adding methods to Sketchup classes :
        I want to add a method to "Geom::Point3d", how can I use a special name ??

        Here my current code :
        Code: Select all
        class Geom::Point3d
          def myFirstMethod
            return(self.z*self.z)
          end
        end
        0

        Morgan74 
         

        Re: [info] Using Ruby Modules

        Postby thomthom » Mon Jul 26, 2010 10:53 am

        Morgan74 wrote:But I have a question about adding methods to Sketchup classes :
        I want to add a method to "Geom::Point3d", how can I use a special name ??

        Here my current code :
        Code: Select all
        class Geom::Point3d
          def myFirstMethod
            return(self.z*self.z)
          end
        end

        It's impossible to be 100% sure no one else implements a method of the same name as your when you extend a shared module/class. But if you for instance prefix with your initials you decrease the likely hood that your method is overwritten.

        Personally I don't extend the base Ruby and SketchUp classes because of the potential of name conflict. (Doesn't look as good - but it's safe.)
        0
        Thomas Thomassen — SketchUp Monkey & Coding addict
        List of my plugins and link to the CookieWare fund
        User avatar
        thomthom 
        PluginStore Author
        PluginStore Author
         

        Re: [info] Using Ruby Modules

        Postby Morgan74 » Mon Jul 26, 2010 12:46 pm

        Oops I have used the wrong thread :oops: :oops:

        Shame on me :roll: :roll:
        0

        Morgan74 
         

        SketchUcation One-Liner Adverts

        by Ad Machine » 5 minutes ago



        Ad Machine 
        Robot
         



         

        Return to Developers' Forum

        Who is online

        Users browsing this forum: victoriahoan and 11 guests

        Visit our sponsors: