`

Include vs Extend in Ruby

阅读更多

Posted 3 days back at RailsTips.org - Home

In which I show the difference between include and extend when working with modules in Ruby.

Now that we know the difference between an instance method and a class method, let’s cover the difference between include and extend in regards to modules. Include is for adding methods to an instance of a class and extend is for adding class methods. Let’s take a look at a small example.

module Foo
  def foo
    puts 'heyyyyoooo!'
  end
end

class Bar
  include Foo
end

Bar.new.foo # heyyyyoooo!
Bar.foo # NoMethodError: undefined method ‘foo’ for Bar:Class

class Baz
  extend Foo
end

Baz.foo # heyyyyoooo!
Baz.new.foo # NoMethodError: undefined method ‘foo’ for #<Baz:0x1e708>

As you can see, include makes the foo method available to an instance of a class and extend makes the foo method available to the class itself.
Include Example

If you want to see more examples of using include to share methods among models, you can read my article on how I added simple permissions to an app. The permissions module in that article is then included in a few models thus sharing the methods in it. That is all I’ll say here, so if you want to see more check out that article.
Extend Example

I’ve also got a simple example of using extend that I’ve plucked from the Twitter gem. Basically, Twitter supports two methods for authentication—httpauth and oauth. In order to share the maximum amount of code when using these two different authentication methods, I use a lot of delegation. Basically, the Twitter::Base class takes an instance of a “client”. A client is an instance of either Twitter::HTTPAuth or Twitter::OAuth.

Anytime a request is made from the Twitter::Base object, either get or post is called on the client. The Twitter::HTTPAuth client defines the get and post methods, but the Twitter::OAuth client does not. Twitter::OAuth is just a thin wrapper around the OAuth gem and the OAuth gem actually provides get and post methods on the access token, which automatically handles passing the OAuth information around with each request.

The implementation looks something like this (full file on github):

module Twitter
  class OAuth
    extend Forwardable
    def_delegators :access_token, :get, :post

    # a bunch of code removed for clarity
  end
end

Rather than define get and post, I simply delegate the get and post instance methods to the access token, which already has them defined. I do that by extending the Forwardable module onto the Twitter::OAuth class and then using the def_delegators class method that it provides. This may not be the most clear example, but it was the first that came to mind so I hope it is understandable.
A Common Idiom

Even though include is for adding instance methods, a common idiom you’ll see in Ruby is to use include to append both class and instance methods. The reason for this is that include has a self.included hook you can use to modify the class that is including a module and, to my knowledge, extend does not have a hook. It’s highly debatable, but often used so I figured I would mention it. Let’s look at an example.

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

  module ClassMethods
    def bar
      puts 'class method'
    end
  end

  def foo
    puts 'instance method'
  end
end

class Baz
  include Foo
end

Baz.bar # class method
Baz.new.foo # instance method
Baz.foo # NoMethodError: undefined method ‘foo’ for Baz:Class
Baz.new.bar # NoMethodError: undefined method ‘bar’ for #<Baz:0x1e3d4>

There are a ton of projects that use this idiom, including Rails, DataMapper, HTTParty, and HappyMapper. For example, when you use HTTParty, you do something like this.

class FetchyMcfetcherson
  include HTTParty
end

FetchyMcfetcherson.get('http://foobar.com')

When you add the include to your class, HTTParty appends class methods, such as get, post, put, delete, base_uri, default_options and format. I think this idiom is what causes a lot of confusion in the include verse extend understanding. Because you are using include it seems like the HTTParty methods would be added to an instance of the FetchyMcfetcherson class, but they are actually added to the class itself.
Conclusion

Hope this helps those struggling with include verse extend. Use include for instance methods and extend for class methods. Also, it is sometimes ok to use include to add both instance and class methods. Both are really handy and allow for a great amount of code reuse. They also allow you to avoid deep inheritance, and instead just modularize code and include it where needed, which is much more the ruby way.
分享到:
评论

相关推荐

    Ruby on Rails中的include和extend的常见用法

    本文将介绍浅谈Ruby on Rails中的include和extend。include主要用来将一个模块插入到一个类或者其它模块。extend用来在一个对象中引入一个模块,这个类从而也具备了这个模块的方法。

    Ruby中require、load、include、extend的区别介绍

    主要介绍了Ruby中require、load、include、extend的区别介绍,require、load用于文件,如.rb等等结尾的文件,include、load则用于包含一个文件中的模块,需要的朋友可以参考下

    interface:ruby 中的可实现接口

    Ruby 1.9+用法 只需使用您希望其实现对象定义的任何方法创建一个模块module RemoteControl # turns the device on def on end # turns the device off def off endend然后在您的类中使用implements方法(也别名为...

    Ruby 中的 module_function 和 extend self异同

    module 中的method 又可分为 instance method 和 module method, 当一个 module 被 include 进一个 class ,那么 module 中的 method (注:没有被 module_function 标记的 method)就是 class 中的 instance method, ...

    ruby-comparateur:计算两个 HTML 文档的结构相似度

    这就是为什么您必须创建一个类并使用Comparateur include或extend它并根据需要使用它。 此实现还允许您构建自己的缓存系统。 安装 将此行添加到应用程序的 Gemfile 中: gem 'comparateur' 然后执行: $ bundle...

    sord:将YARD文档转换为Sorbet RBI和Ruby 3Steep RBS文件

    索德 概述 Sord是和跨界车。 通过查看YARD文档注释中指定...识别mixin( include和extend ) 支持通用类型,例如Array和Hash 可以推断命名空间的类( [Bar]可以成为GemName::Foo::Bar ) 处理可以为nil返回类型( T

    Advanced Flex 3 2008

    the help of two fully functional case studies that include Apache Struts and Ruby on Rails applications, respectively. Chapter 12: Sculpting Interactive Business Intelligence Interfaces Business ...

    microevent.rb:Ruby 对象的事件(又名具有发布-订阅功能的对象,又名观察者模式)

    MicroEvent.rb 是一个事件发射器库,它为 Ruby 对象提供观察者模式。 它的灵感来自 ,用不到。 设置 添加到您的Gemfile gem 'microevent' 或将复制到您的项目中。 如何使用它 假设您有一个类Klass ,并且您希望...

    Redis-Essentials.pdf

    The pitfalls in this chapter include using the wrong data type for a given problem, using too much swap space, and using inefficient backup strategies. Chapter 7, Security Techniques (Guard Your Data...

    Wrox.Professional.jQuery 2012

    It is important to know how to extend the power of jQuery with custom methods as it is a fundamental skill for a top jQuery developer. Chapter 13 introduces the jQuery Deferred Object, which was ...

    Ruby中钩子方法的运用实例解析

    通过使用钩子方法,可以让我们在Ruby的类或模块的生命周期中进行干预,可以极大的提高编程的灵活性。 与生命周期相关的钩子方法有下面这些: 类与模块相关 Class#inherited Module#include Module#prepended ...

    class_profiler:简单的性能分析器,带有一些强大的元编程酒精

    类分析器 用于Ruby类的简单性能分析器。 只需将其包含在您的类的底部,然后让它分析您... include ClassProfiler #include it just before closing the class end 或者,如果您想要更可配置的内容,以仅测量特定的方法

    julia-1.1.0-win64

    In addition to the above, some advantages of Julia over comparable systems include: Free and open source (MIT licensed) User-defined types are as fast and compact as built-ins No need to vectorize ...

    eldritch:RubyDSL,它添加了并发编程结构以简化并行性

    # The DSL is available in this class end 发展历程 建立 bundler install 运行测试 bundler exec rake 运行示例 ruby -I lib examples/{your favorite example}.rb 产生文件 bundle exec rake doc 产品特点 异步...

    slugity:又一个猛烈的宝石

    描述 另一个 slugging gem,使用自定义映射选项将字符串转换为 slug。 安装 gem install slugity ... require 'slugity/extend_string' "one + two = three" . to_slug # =&gt; "one-plus-two-equals-three" 还

    refile-mongoid:允许MongoID与重新归档一起很好地播放

    Refile :: Mongoid 于扩展名。 安装 将此行添加到您的应用程序的Gemfile中: gem 'refile-mongoid' 用法 require "refile/mongoid" ... extend Refile :: Mongoid :: Attachment attachment :profile_image end 执照

    artist-song-modules-v-000

    使用模块重构 目标 识别表明需要重构的“代码气味”。 使用模块重构多余的代码。 概述 在本实验中,我们有一个Artist类和一个Song类。... 然后,我们将使用extend和include关键字将模块的功能借给Ar

    artist-song-modules-online-web-sp-000

    使用模块重构 目标 识别表明需要重构的“代码气味”。 使用模块重构多余的代码。 概述 在本实验中,我们有一个Artist类和一个Song类。...然后,我们将使用extend和include关键字将模块的功能借给Artist和Song

Global site tag (gtag.js) - Google Analytics