Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ruby include vs extend

I am trying to abstract some of the logic required for cropping images into a module so that its not messing up my models. The code is based on http://railscasts.com/episodes/182-cropping-images

module CroppableImage
  def croppable_image(*image_names)
    image_names.each do |image_name|

      define_method "#{image_name}_sizes" do
        { :cropped => read_attribute("#{image_name}_size").to_s, :large => "800x800>" }
      end

      define_method "#{image_name}_geometry" do |style|
        style ||= :original
        @geometry ||= {}
        @geometry[style] ||= Paperclip::Geometry.from_file(eval "#{image_name}.path(style)")
      end

      define_method "#{image_name}_aspect_ratio" do
        width, height = read_attribute("#{image_name}_size").split("x")
        width.to_f / height.to_f
      end

      private

        define_method "reprocess_#{image_name}" do
          eval "#{image_name}.reprocess!"
        end

    end
  end

end

To include this into my model it seems that I have to use extend. I thought extend was for including class methods. I am from a java background - I thought using extend basically created static method on the class.

class Website < ActiveRecord::Base
  extend CroppableImage
  croppable_image :logo, :footer_image

-- this works

It seems then that what I am really wanting is to create instance methods.

class Website < ActiveRecord::Base
  include CroppableImage
  croppable_image :logo, :footer_image

-- This throws the error "undefined method `croppable_image' for #"

Can anyone please explain whats going on and if I should be using include or extend in this case. Thanks guys

like image 685
Nick Avatar asked Mar 15 '12 08:03

Nick


People also ask

What's the difference between extend prepend and include?

The only difference is where in the ancestor chain the module is added. With include , the module is added after the class in the ancestor chain. With prepend, the module is added before the class in the ancestor chain.

What is included in Ruby?

With included a class simply includes the module. It allows the module to be one neat package: instance methods, class methods, and setup code. Without included a module can only inject instance methods. Class methods would have to be added separately, as well as executing any setup code.

Can a Ruby module be inherited?

The Ruby class Class inherits from Module and adds things like instantiation, properties, etc – all things you would normally think a class would have. Because Module is literally an ancestor of Class , this means Modules can be treated like classes in some ways. As mentioned, you can find Module in the array Class.

What is the difference between modules and classes in Ruby?

What is the difference between a class and a module? Modules are collections of methods and constants. They cannot generate instances. Classes may generate instances (objects), and have per-instance state (instance variables).


2 Answers

extend M internally is similar to class << self; include M; end - extend includes module into singleton class of an object (and makes instance methods of the module to be a singleton methods of a class you extend).

In your case you call croppable_image in the context of a class definition and thus croppable_image should be an instance method of a Class class or a singleton method of Website class.

This is why you should extend Website class with a module CroppableImage by using extend CroppableImage - it adds instance method croppable_image as a singleton method of Website class.

like image 59
Aliaksei Kliuchnikau Avatar answered Oct 14 '22 07:10

Aliaksei Kliuchnikau


you can use both logic together. Ruby has callbacks for extend and include Example of using included callback

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

  module ClassMethods
    def bar
      puts 'class method'
    end
  end

  def foo
    puts 'instance method'
  end
end
like image 21
Fivell Avatar answered Oct 14 '22 08:10

Fivell