Undefined local variable or method for module - ruby-on-rails

I have module CurrencyExchange with following methods
CURRENCIES = %w(uah rub eur usd)
def available_currencies
CURRENCIES.join(' ').downcase.split.permutation(2)
end
and when i want to use available_currencies with
define_method
available_currencies.each do |(c1, c2)|
define_method(:"#{c1}_to_#{c2}") do |cr| ... end end
i have got an error
undefined local variable or method `available_currencies'
for CurrencyExchange:Module (NameError)
but when i use it like
CURRENCIES.join(' ').downcase.split.permutation(2).each do |(c1, c2)|
define_method(:"#{c1}_to_#{c2}") .... end end
it works fine
Why it happens?

I think you need to write def self.available_currencies

You try to create other method in the class and Ruby searches in the loop for a class method .available_currencies.
You have to change your class method .available_currencies into a instance method #available_currencies or create the methods in your initializer.
Method 1:
class MyClass
def self.available_currencies
# Your logic...
end
# Your logic...
end
Method 2:
class MyClass
def init
available_currencies.each do |c|
define_method(c) do
# Whatever you want to do ...
end
end
end
def available_currencies
# Your logic...
end
end
I would recommend you the first way since you maybe want to use the currencies in classes. I would recommend you the second way, if you want for example different currencies for different instances.
Happy coding :)

Related

Calling a method inside model

I am trying to trigger a method from inside the model where it is defined. But I am getting an "undefined method `completed_mission_names'" when I try to start my server. Can anybody help me find what I'm doing wrong ?
class MenteeProfile < ActiveRecord::Base
# Update trackable attributes with succeeded missions
MenteeProfile.completed_mission_names
protected
def last_completed_mission_action
end
def self.completed_mission_names
end
end
Simplified to the max, you are trying to do this:
class A
A.foo
def self.foo
puts 'Calling foo!'
end
end
This does not work because the method foo is not defined when you try to invoke it. You must define it first, then you can call it. Like so:
class B
def self.foo
puts 'Calling foo!'
end
B.foo
end
You could also call just foo instead of B.foo from within the class definition. You can add the protected keyword anywhere you like, it will not have any impact on class methods whatsoever.

Inherit a class from a gem and add local methods

I use a gem to manage certain attributes of a gmail api integration, and I'm pretty happy with the way it works.
I want to add some local methods to act on the Gmail::Message class that is used in that gem.
i.e. I want to do something like this.
models/GmailMessage.rb
class GmailMessage < Gmail::Message
def initialize(gmail)
#create a Gmail::Message instance as a GmailMessage instance
self = gmail
end
def something_clever
#do something clever utilising the Gmail::Message methods
end
end
I don't want to persist it. But obviously I can't define self in that way.
To clarify, I want to take an instance of Gmail::Message and create a GmailMessage instance which is a straight copy of that other message.
I can then run methods like #gmail.subject and #gmail.html, but also run #gmail.something_clever... and save local attributes if necessary.
Am I completely crazy?
You can use concept of mixin, wherein you include a Module in another class to enhance it with additional functions.
Here is how to do it. To create a complete working example, I have created modules that resemble what you may have in your code base.
# Assumed to be present in 3rd party gem, dummy implementation used for demonstration
module Gmail
class Message
def initialize
#some_var = "there"
end
def subject
"Hi"
end
end
end
# Your code
module GmailMessage
# You can code this method assuming as if it is an instance method
# of Gmail::Message. Once we include this module in that class, it
# will be able to call instance methods and access instance variables.
def something_clever
puts "Subject is #{subject} and #some_var = #{#some_var}"
end
end
# Enhance 3rd party class with your code by including your module
Gmail::Message.include(GmailMessage)
# Below gmail object will actually be obtained by reading the user inbox
# Lets create it explicitly for demonstration purposes.
gmail = Gmail::Message.new
# Method can access methods and instance variables of gmail object
p gmail.something_clever
#=> Subject is Hi and #some_var = there
# You can call the methods of original class as well on same object
p gmail.subject
#=> "Hi"
Following should work:
class GmailMessage < Gmail::Message
def initialize(extra)
super
# some additional stuff
#extra = extra
end
def something_clever
#do something clever utilising the Gmail::Message methods
end
end
GmailMessage.new # => will call first the initializer of Gmail::Message class..
Building upon what the other posters have said, you can use built-in class SimpleDelegator in ruby to wrap an existing message:
require 'delegate'
class MyMessage < SimpleDelegator
def my_clever_method
some_method_on_the_original_message + "woohoo"
end
end
class OriginalMessage
def some_method_on_the_original_message
"hey"
end
def another_original_method
"zoink"
end
end
original = OriginalMessage.new
wrapper = MyMessage.new(original)
puts wrapper.my_clever_method
# => "heywoohoo"
puts wrapper.another_original_method
# => "zoink"
As you can see, the wrapper automatically forwards method calls to the wrapped object.
I'm not sure why you can't just have a simple wrapper class...
class GmailMessage
def initialize(message)
#message = message
end
def something_clever
# do something clever here
end
def method_missing(m, *args, &block)
if #message.class.instance_methods.include?(m)
#message.send(m, *args, &block)
else
super
end
end
end
Then you can do...
#my_message = GmailMessage.new(#original_message)
#my_message will correctly respond to all the methods that were supported with #original_message and you can add your own methods to the class.
EDIT - changed thanks to #jeeper's observations in the comments
It's not the prettiest, but it works...
class GmailMessage < Gmail::Message
def initialize(message)
message.instance_variables.each do |variable|
self.instance_variable_set(
variable,
message.instance_variable_get(variable)
)
end
end
def something_clever
# do something clever here
end
end
Thanks for all your help guys.

Rails - How to use variable as part of method name

I am trying to add variable name into method_name in rails. I am getting error.
**Controller ACTION**
=====================
def my_action(state)
method_#{state}
end
**Model methods**
====================
def method_start
end
def method_end
end
how to call method with variable name i am not getting.
Use Object.send to call method by name. For example,
def my_action(state)
if [:start, :end].include?(state)
model.send("method_#{state}")
end
end
Make sure to validate state variable for security. Object.send can call any method including private ones.

Pass a symbol to a method and call the corresponding method

In a Rails controller you can pass a symbol to the layout method that corresponds to a method in you controller that will return the layout name like this:
layout :my_method
def my_method
'layout_1'
end
I want to have a similar functionality to likewise pass a symbol to my classes method and that class should call the corresponding function and use its return value, like this
myClass.foo :my_method
def my_method
'layout_1'
end
I've read posts[1] that tell me I need to pass
myClass.foo(method(:my_method))
which I find ugly and inconvenient. How is rails here different allowing to pass just the symbol without any wrapper? Can this be achieved like Rails does it?
[1] How to implement a "callback" in Ruby?
If you want to only pass a :symbol into the method, then you have to make assumptions about which method named :symbol is the one you want called for you. Probably it's either defined in the class of the caller, or some outer scope. Using the binding_of_caller gem, we can snag that information easily and evaluate the code in that context.
This surely has security implications, but those issues are up to you! :)
require 'binding_of_caller'
class Test
def foo(sym)
binding.of_caller(1).eval("method(:#{sym})").call
end
end
class Other
def blork
t = Test.new
p t.foo(:bar)
p t.foo(:quxx)
end
def bar
'baz'
end
end
def quxx
'quxx'
end
o = Other.new
o.blork
> "baz"
> "quxx"
I still don't understand, what is author asking about. He's saying about "callbacks", but only wrote how he wants to pass parameter to some method. What that method(foo) should do - i have no idea.
So I tried to predict it's implementation. On class initialising it gets the name of method and create private method, that should be called somewhere under the hood. It possible not to create new method, but store method name in class variable and then call it somewhere.
module Foo
extend ActiveSupport::Concern
module ClassMethods
def foo(method_name)
define_method :_foo do
send method_name
end
end
end
end
class BaseClass
include Foo
end
class MyClass < BaseClass
foo :my_method
private
def my_method
"Hello world"
end
end
MyClass.new.send(:_foo)
#=> "Hello world"
And really, everything is much clearer when you're not just wondering how it works in rails, but viewing the source code: layout.rb

How I can store variable like instance in model.rb

For example, I have this array:
tags_array=['<code>','<span>','<div>', '<label>','<a>', '<br>', '<p>' '<b>','<i>', '<del>', '<strike>', '<u>', '<img>', '<video>', '<audio>', '<iframe>', '<object>', '<embed>', '<param>', '<blockquote>', '<mark>', '<cite>', '<small>', '<ul>', '<ol>', '<li>', '<hr>', '<dl>', '<dt>', '<dd>', '<sup>', '<sub>', '<big>', '<pre>', '<code>', '<figure>', '<figcaption>', '<strong>', '<em>', '<table>', '<tr>', '<td>', '<th>', '<tbody>', '<thead>', '<tfoot>', '<h1>', '<h2>', '<h3>', '<h4>', '<h5>','<h6>']
I don't want to define it in each model method where I use it, it seems not very smart copy-pasting.
And of course I don't want separate DB row for that.
How can I define it once, so it would be visible for each model method, like instance initialize method.
Like
class MyModel<ActiveRecord::Base
#...
#tags_array=['','',...]
def onemethod
#tags_array.split!
#...
end
def twomethod
#tags_array.capitalize!
#...
end
end
You could define it as a module and mix it in.
module TagsArray
tags_array=['','',...]
def self.onemethod
tags_array.split
#...
end
def self.twomethod
tags_array.capitalize
#...
end
end
Then you can just mix it in to your Models.
class MyModel<ActiveRecord::Base
include TagsArray
end
Hope this helps.
Also you can drop the ! from the end of split and capitalize unless you want to alter the tags_array permanently each time you call the method.

Resources