How to Implement Ruby's Extend Module in JavaScript

How to implement Ruby's extend module in JavaScript

This seems to work for me:

> class Test { test1(){ console.log("test1") }}    
> class Other { test2() { console.log("test2") }}
> test = new Test
Test {}
> other = new Other
Other {}
> test.test1()
test1
> test["test2"] = other.test2
> test.test2()
test2

An instance is really just an array of functions (in this case). So, when you call:

other.test2

It returns the test2 element of other which is the function test2. And this:

> test["test2"] = other.test2

just add that function to the array of functions for test. Which you can then call with:

> test.test2()

How is the ruby's extend syntax work?

In the first snippet you call extend during class declaration, i. e. on Toy object, which is apparently a class. It is the same as calling Toy.extend(Discount).

In the second snippet you extend the Toy.new, which is apparently a Toy instance.

Include vs extend within another module

extend - adds the specified module's methods and constants to the target's metaclass (i.e. the singleton class)
e.g.

  • if you call Klazz.extend(Mod), now Klazz has Mod's methods (as class methods)
  • if you call obj.extend(Mod), now obj has Mod's methods (as instance methods), but no other instance of of obj.class has those methods.
  • extend is a public method

include - By default, it mixes in the specified module's methods as instance methods in the target module/class.
e.g.

  • if you call class Klazz; include Mod; end;, now all instances of Klazz have access to Mod's methods (as instance methods)
  • include is a private method, because it's intended to be called from within the container class/module.

However, modules very often override include's behavior by monkey-patching the included method. This is very prominent in legacy Rails code. more details from Yehuda Katz.

Further details about include, with its default behavior, assuming you've run the following code

class Klazz
include Mod
end
  • If Mod is already included in Klazz, or one of its ancestors, the include statement has no effect
  • It also includes Mod's constants in Klazz, as long as they don't clash
  • It gives Klazz access to Mod's module variables, e.g. @@foo or @@bar
  • raises ArgumentError if there are cyclic includes
  • Attaches the module as the caller's immediate ancestor (i.e. It adds Mod to Klazz.ancestors, but Mod is not added to the chain of Klazz.superclass.superclass.superclass. So, calling super in Klazz#foo will check for Mod#foo before checking to Klazz's real superclass's foo method. See the RubySpec for details.).

Of course, the ruby core documentation is always the best place to go for these things. The RubySpec project is also a fantastic resource, because they document the functionality precisely.

  • include RubySpec rubydoc

  • included RubySpec rubydoc

  • extend RubySpec rubydoc

  • extended RubySpec rubydoc

  • extend_object RubySpec rubydoc

  • append_features RubySpec rubydoc

ref: John

Extend and Include

module Foo
def self.included(base)
base.extend Foo::ClassMethods
end

module ClassMethods
def guitar
"gently weeps"
end
end
end

class Bar
include Foo
end

puts Bar.guitar

Ruby extend & include tracing code

When you extend a module, the methods in that module become "class methods"**. So, when you extend Inventoryable, create becomes available as a method on the Shirt class.

When you include a module, the methods in that module become "instance methods"**. So, when you include Inventoryable, create is not available on the Shirt class (but is available on an instance of Shirt).

To make create available on the Shirt class when using include, you can use the included hook. That might look something like:

module Inventoryable
module ClassMethods

def create
puts "create!"
end

end

module InstanceMethods

end

def self.included(receiver)
receiver.extend ClassMethods
receiver.include InstanceMethods
end
end

Then if you do:

class Shirt
include Invetoryable
end

You can do:

> Shirt.create
create!
=> nil

** The ruby purists in the crowd will correctly point out that, in ruby, everything is an instance method and that there are no class methods. That is formally 100% correct, but we'll use the colloquial meaning of class and instance methods here.

How can I dynamically add/extend methods from a Class?

module ExtendRubyEdit

def self.included(base)
base.extend(ClassMethods)
end

def draw
end

def event
end

module ClassMethods
def redraw
end
end
end

class RubyEdit
include ExtendRubyEdit
end

How to extend a class within a Ruby module

module OilyPNG
class Canvas < ChunkyPNG::Canvas # You must match the superclass when opening class and adding more definitions
def your_method
puts "hello"
end
include Hola # this will "insert" the Hola module at this point in the code... not sure what Hola contains so, not sure if that works or not.
end
end

Looking at the source for OilyPNG the above should work...

Not sure why you got that error. Given this works here is why: You have to match the classes, super class when reopening it.

Based on this article: http://www.rubyinside.com/how-to-create-a-ruby-extension-in-c-in-under-5-minutes-100.html by the great Peter Cooper, I believe you should be using rb_define_method, not rb_define_singleton_method

If you are still having trouble, start with peter's example, then adapt it to your needs.

How do I extend a Ruby class inside a module?

You're close.

module Wireshark
module File
def write_header
self.puts '/* ' + Wireshark::AUTOGEN_LABEL + ' */'
self.puts '/* ' + Wireshark::timestamp + ' */'
end
end

# Extend File with the methods in the Wireshark::File module
::File.send :include, Wireshark::File

# Read a file
def read
begin
file = ::File.read 'file'
rescue IOError
STDERR.puts 'Error reading file.'
return
end
end
end

The general idea here is that we define a Wireshark::File module that holds the methods you want to include on the File class, then you can just include them on File directly.

You'll also notice that in the read method, I changed File to ::File. Ruby will walk up the tree to try to find the nearest matching value for a given constant, so since you are in the Wireshark module, and there is a constant named File in that scope, using just File gets you Wireshark::File. Specifying ::File means "The File constant at the top-level namespace".



Related Topics



Leave a reply



Submit