21 Sep Modules in ruby using metaprogramming, activesupport concern
We have two files and the content is the next:
image_utils.rb
def preview(image) end def transfer(image, destination) end
run.rb
require ‘./image_utils’ preview “something”
The problem with the before example is that in the future we can mix differents methods for that problema we can use the namespace, using module the before example will looks like:
image_utils.rb
module Mymodule def self.preview image puts image end def self.transferi image, destination end end
run.rb
require './image_utils' Mymodule.preview "un mensaje"
In ruby we can use only one superclass for inheritance for example
class Car < SuperCar end
The diference is that we can use severals modules for example
module Share def somemethod end end module Favorite end class Oneclass include Share include Favorite end class ClassTwo include Share include Favorite end
if we use include module the module will be included like instance methods:
If we use the extend Module the module is included like a class methods, in other words we can invoke using direcly our class for example
OneClass.somemethod
Namespacing
Create a module named GameUtils and place the lend_to_friend method inside the module. Changelend_to_friend to a class method by prefixing it with self..
You won’t need to require this module since it’ll be inside the same file (already required), but you will have to namespace your method call.
module GameUtils def self.lend_to_friend(game, friend_email) puts "#{game} y #{friend_email}" end end GameUtils::lend_to_friend("contra super 7", "gregg@codeschool.com") #or #GameUtils.lend_to_friend("contra super 7", "gregg@codeschool.com")
Mixin
Re-open the Game class and include the GameUtils module so its methods are exposed as instance methods. Make sure to do this before it is called.
class Game include GameUtils end game = Game.new("contra") game.lend_to_friend("Gregg")
Extend
Good job! Now expose the methods from the GameUtils module as class methods of the Game class.
class Game extend GameUtils end Game.find_all_from_user("Gregg")
Object Extend
Extend the single game object with the Playable module, so we can call the play method on it.
class Game end game = Game.new("Contra") game.extend(Playable) game.play
Hook Methods
Define a new self.included method hook for the LibraryUtils module which will extend the ClassMethods on the passed in class. Also, since we’ll now be extending ClassMethods when LibraryUtils is included, remove duplicate code in the AtariLibrary class.
module LibraryUtils def self.included(base) base.extend(ClassMethods) end def add_game(game) end def remove_game(game) end module ClassMethods def search_by_game_name(name) end end end class AtariLibrary include LibraryUtils end
ActiveSupport::Concern
Now refactor the following code to use ActiveSupport::Concern’s ability to expose class methods from a module.
module LibraryUtils extend ActiveSupport::Concern include do search_by_game_name end def add_game(game) end def remove_game(game) end module ClassMethods def search_by_game_name(name) end end end
ActiveSupport::Concern – Part II
Call the included method from inside the LibraryUtils module and pass in a block that calls the load_game_list class method.
module LibraryUtils extend ActiveSupport::Concern included do load_game_list end def add_game(game) end def remove_game(game) end module ClassMethods def search_by_game_name(name) end def load_game_list end end end
ActiveSupport::Concern
Make sure the AtariLibrary class includes only the LibraryUtils module and let ActiveSupport::Concern take care of loading its dependencies. Then, refactor the self.included method on LibraryUtils to use the included method.
module LibraryLoader extend ActiveSupport::Concern module ClassMethods def load_game_list end end end module LibraryUtils extend ActiveSupport::Concern include LibraryLoader included do load_game_list end end class AtariLibrary include LibraryUtils end
Other example using model validations with a module
module ModelHelpers module Card::Validator extend ActiveSupport::Concern included do validates :cvn_number, format: { with: /^[0-9]{3,4}$/, message: 'Please enter a valid card security code.'} validates :card_first_name, presence: { message: 'Please enter a valid first name.' } validates :agreed_terms, acceptance: { accept: true, message: 'Please agree to the Terms and Conditions.'} validates :account, uniqueness: { message: 'the card is already registered' } validates :card_number, length: {minimum: 16, maximum: 19, message: 'Debit Card number is not valid...'} end end end
And in our model:
class Card < ActiveRecord::Base include ModelHelpers::Card::Validator end
And that’s it
Jason
Posted at 12:23h, 19 JulyLooks like you lifted this directly from CodeSchool.