lib/modules/file_type.rb
module Modules
module Type
def friend_name(type:)
...
end
end
end
app/models/car.rb
class Car < ApplicationRecord
include Modules::Type
def self.to_array
...
name = friend_name(type: 'test')
...
end
end
But I am getting this error:
undefined method `friend_name'
I am not sure why I am getting this error.
Anyone can help me?
If friend_name is a class method then instead of include use extend in Car model
extend Modules::Type
More info about difference between include and extend could be found here -
What is the difference between include and extend in Ruby?
Hope that helps!
Related
I have added a module in rails concern to update two attributes with a default value.
But not able to do it. Could someone please explain it?
module DefaultValueForCreateAndUpdateUserid
extend ActiveSupport::Concern
before_save do
self.create_uid = 'x' if !self.create_uid?
self.last_updt_uid = 'x' if !self.last_updt_uid?
end
end
end
But it is giving this error:
NoMethodError: undefined method `before_save' for DummyValueForCreateAndUpdateUserid:Module
You can use included and also ||= to check and assign attribute
module DefaultValueForCreateAndUpdateUserid
extend ActiveSupport::Concern
included do
before_save :assign_absent_attributes
end
def assign_absent_attributes
self.create_userid ||= 'f'
self.last_updt_userid ||= 'f'
end
end
And than include this concern in the model
class MyModel < ApplicationRecord
include DefaultValueForCreateAndUpdateUserid
end
This should fit your needs. 🙂
module DefaultValueForCreateAndUpdateUserid
extend ActiveSupport::Concern
included do
before_save do
self.create_userid = 'f' if !self.create_userid?
self.last_updt_userid = 'f' if !self.last_updt_userid?
end
end
end
class MyClass < ApplicationRecord
include DefaultValueForCreateAndUpdateUserid
end
I did everything pretty much as described here: question
But I keep getting error:
NoMethodError: undefined method `parent_model' for Stream (call 'Stream.connection' to establish a connection):Class
In model/concerns faculty_block.rb
module FacultyBlock
extend ActiveSupport::Concern
included do
def find_faculty
resource = self
until resource.respond_to?(:faculty)
resource = resource.parent
end
resource.faculty
end
def parent
self.send(self.class.parent)
end
end
module ClassMethods
def parent_model(model)
##parent = model
end
end
end
[Program, Stream, Course, Department, Teacher].each do |model|
model.send(:include, FacultyBlock)
model.send(:extend, FacultyBlock::ClassMethods) # I added this just to try
end
In initializers:
require "faculty_block"
method call:
class Stream < ActiveRecord::Base
parent_model :program
end
It seems that the Stream is loaded before loading concern, make sure that you have applied the concerns inside the class definition. When rails loader matches class name for Stream constant, it autoloads it before the finishing evaliation of the faculty_block, so replace constants in it with symbols:
[:Program, :Stream, :Course, :Department, :Teacher].each do |sym|
model = sym.to_s.constantize
model.send(:include, FacultyBlock)
model.send(:extend, FacultyBlock::ClassMethods) # I added this just to try
end
I'm trying to solve a strange issue.
I'm extending ActiveRecord using a module.
module StringyAssociationIds
def stringy_ids(association)
define_method("stringy_#{association}_ids=") do |comma_seperated_ids|
self.send("#{association}_ids=", comma_seperated_ids.to_s.split(","))
end
define_method("stringy_#{association}_ids") do
send("#{association}_ids").join(",")
end
end
end
ActiveRecord::Base.extend(StringyAssociationIds)
I have a class "Gate" where I have an association.
class Gate < ActiveRecord::Base
include Productable
stringy_ids :product
end
The association is defined with a join table:
module Productable
extend ActiveSupport::Concern
included do
has_many :productable_products, as: :productable
has_many :products, through: :productable_products
end
end
When I try to create a new Gate I have an error:
undefined method `stringy_ids' for #<Class:0x007f91e12bb7e8>
Where is my fault?
Edit: I try also to add an extension inside the lib directory (autoloaded by application.rb)
module ActiveRecordExtension
extend ActiveSupport::Concern
def stringy_ids(association)
define_method("stringy_#{association}_ids=") do |comma_seperated_ids|
self.send("#{association}_ids=", comma_seperated_ids.to_s.split(","))
end
define_method("stringy_#{association}_ids") do
send("#{association}_ids").join(",")
end
end
end
# include the extension
ActiveRecord::Base.send(:include, ActiveRecordExtension)
I try also in console:
ActiveRecordExtension.instance_methods
=> [:stringy_ids]
So my extension is loaded...
Your class method stringy_ids is defined on ActiveRecord::Base, not Gate. Unlike instance methods, class methods are not inherited because the singleton class of Gate is not a subclass of the singleton class of ActiveRecord::Base.
StringyAssociationIds is not extended.
Actually, ActiveRecord::Base.extend(StringyAssociationIds) does not run. Move this code in config/initializer
I am trying to DRY my code by implementing modules. However, I have constants stored in models (not the module) that I am trying to access with self.class.
Here are (I hope) the relevant snippets:
module Conversion
def constant(name_str)
self.class.const_get(name_str.upcase)
end
end
module DarkElixir
def dark_elixir(th_level)
structure.map { |name_str| structure_dark_elixir(name_str, th_level) if constant(name_str)[0][:dark_elixir_cost] }.compact.reduce(:+)
end
end
class Army < ActiveRecord::Base
include Conversion, DarkElixir
TH_LEVEL = [...]
end
def structure_dark_elixir(name_str, th_level)
name_sym = name_str.to_sym
Array(0..send(name_sym)).map { |level| constant(name_str)[level][:dark_elixir_cost] }.reduce(:+) * TH_LEVEL[th_level][sym_qty(name)]
end
When I place the structure_dark_elixir method inside the DarkElixir module, I get an error, "uninitialized constant DarkElixir::TH_LEVEL"
While if I place it inside the Army class, it finds the appropriate constant.
I believe it is because I am not scoping the self.constant_get correctly. I would like to keep the method in question in the module as other models need to run the method referencing their own TH_LEVEL constants.
How might I accomplish this?
Why not just use class methods?
module DarkElixir
def dark_elixir(th_level)
# simplified example
th_level * self.class.my_th_level
end
end
class Army < ActiveRecord::Base
include DarkElixir
def self.my_th_level
5
end
end
Ugh. Method in question uses two constants. It was the second constant that was tripping up, not the first. Added "self.class::" prior to the second constant--back in business.
def structure_dark_elixir(name_str, th_lvl)
name_sym = name_str.to_sym
Array(0..send(name_sym)).map { |level| constant(name_str)[level][:dark_elixir_cost] }.reduce(:+) * self.class::TH_LEVEL[th_lvl][sym_qty(name_str)]
end
I have a lib file lister_extension.rb
module ListerExtension
def lister
puts "#{self.class}"
end
end
And Post model
class Post < ActiveRecord::Base
has_many :reviews
extend ListerExtension
def self.puts_hello
puts "hello123123"
end
end
All is good when I call this in rails c:
2.1.1 :003 > Post.lister
Class
=> nil
But what happens when I want to add a class to my module?
For example:
module ListerExtension
class ready
def lister
puts "#{self.class}"
end
end
end
I get this error
TypeError: wrong argument type Class (expected Module)
When I call Post.first in rails c
From the doc for extend:
Adds to obj the instance methods from each module given as a
parameter.
Hence, you can't access this class through extended class. Have a look into including modules instead of extending them (read about ActionSupport::Concern module as well) or have a go with self.extended method (ref here)
TL;DR , in ruby you can´t extend with classes, you extend/include with modules
regards
updated: example for concern
include / extend with activesupport concern
module Ready
extend ActiveSupport::Concern
# this is an instance method
def lister
....
end
#this are class methods
module ClassMethods
def method_one(params)
....
end
def method_two
....
end
end
end
then in a ActiveRecord Model , like Post
class Post < AR
include Ready
end
with this procedure you will get the instance methods and class methods for free, also you can set some macros like when use a included block,
module Ready
extend ActiveSupport::Concern
included do
has_many :likes
end
end
hope that helps,
regards