I have many different classes under /lib/ folder with many actions.
Before saving an object I need to call a method from a class that matches its name with an attribute inside the object i.e. given this
User.gateway = "something"
I need to call myfunction from something class before the object is saved.
Not sure how to do this.
your question is quite ambiguous,is this what you need?
# user.rb
before_save :myfunction
protected
def myfunction
g = self.gateway
case g
when String | Symbol
begin
g.classify.constantize.myfunction
rescue NameError
# if there is no something class
end
else
# no good value
end
end
enter code here
constantize and classify will do the job for you. Suppose you have:
class Foo
end
and the "foo" string. You can do:
"foo".classify.constantize.new.myfunction
Related
I joined Rails team and maintain the codes.
Some of the objects are controlled by Gem virtus, but I really don't understand like below code is doing.
I understand the result that the attribute 'latest_book' can collect latest book from Books but why it can be done? What 'books=(books)' is doing? and Why 'super books' is here?
class GetBooks
include Virtus.model
include ActiveModel::Model
attribute :books, Array[Book]
attribute :latest_book, Book
def books=(books)
self.latest_book = books.sort_by { |book| book['createdate'] }.last
super books
end
end
Could you help me?
def books=(books) is defining a method called books= which takes a single argument books. Yes, that's confusing. It should probably be def books=(value) or def books=(new_books).
And yes, the = is part of the method name. self.books = value is really syntax sugar for self.books=(value). Again, the method is books=.
super books is super(books). super calls the next inherited or included method of the same name; it's calling books= created by attribute :books, Array[Book]. This is a "method override" which allows you to add to the behavior of an existing method.
When books= is called it updates latest_books and then calls its the original method to set the books attribute.
gb = GetBooks.new
gb.books = [old_book, new_book]
p gb.latest_book # new_book
p gb.books # [old_book, new_book]
I have N number of tables and N number of functions. All functions have same code only table name changes. Can I make a common function to be used by all of these function.
Something like this
def funcN
common_func(tableN)
end
private
def common_func(tablename)
"Some Code"
end
I know there may be multiple ways.. What are the possible ways to do it?
You are very close. Just pass a table name as an argument to funcN:
def funcN(tableN)
common_func(tableN)
end
private
def common_func(tablename)
"Some Code"
end
What are all the possible ways to do it?
Theoretically there are indefinite number of ways to solve some problem, so you will never get an answer to this question.
P.S. Your naming does not follow the conventions. Here is how it would look if it did:
def func_n(table_name)
common_func(table_name)
end
private
def common_func(table_name)
# code omitted
end
If model name is static in funcN then just pass it as the string for example consider post then funcN("Post") or from a rails record funcN(#record.class.to_s)
in private method catch the string param as yours tablename and you can convert it into model by myModel = tablename.constantize
then you can carry on with your line of code on that model myModel
If the function, usually called a method in Ruby, is inside a model it can reference the table_name. You can share the common code using a module and including it in each model which needs it, such as:
class Person < AR::Base
include CommonCode
end
class Fruit < AR::Base
include CommonCode
end
module CommonCode
def do_something
self.table_name
end
end
Person.new.do_something # => 'people'
Fruit.new.do_something # => 'fruits'
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
I am very new to RoR and I have played around the source code. But I have a problem that I already built a 'def A' for creating first CSV file, and 'def B' for creating second CSV file. Each 'def' has its own button, but I have the third button to create all CSVs (to produce output from first and second CSV files.)
What is the possible way to do it?
def first_csv
...
end
def second_csv
..
end
def all_csv
<< how to call get first and second csv >>
end
Thanks in advance,
It should be as simple as you imagine:
def all_csv
first_csv
second_csv
end
Muntasim's answer is correct, but I have to add some additional information.
Ruby provides two types of methods..class methods and instance methods.
class MyClass < AvtiveRecord::Base
# class method
def self.foo
# do something
# within this scope the keyword "self" belongs to the class
end
# another class method which calls internally the first one
def self.bar
something = foo # self.foo could also be written
# do something with something
# within this scope the keyword "self" belongs to the class
end
# instance method
def foo
# do something
# if you use the keyword "self" within an instance method, it belongs to the instance
end
# another instance method which calls class and the first instance method
def bar
mystuff = Myclass.bar # if you want to call the class method you cannot use "self", you must directly call the class
mystuff2 = foo # self.foo is the same, because of the instance scope
return [mystuff, mystuff2]
end
end
You can call the last instance method like following
instance = MyClass.first
instance.bar
How do you define a method for an attribute of an instance in Ruby?
Let's say we've got a class called HtmlSnippet, which extends ActiveRecord::Base of Rails and has got an attribute content. And, I want to define a method replace_url_to_anchor_tag! for it and get it called in the following way;
html_snippet = HtmlSnippet.find(1)
html_snippet.content = "Link to http://stackoverflow.com"
html_snippet.content.replace_url_to_anchor_tag!
# => "Link to <a href='http://stackoverflow.com'>http://stackoverflow.com</a>"
# app/models/html_snippet.rb
class HtmlSnippet < ActiveRecord::Base
# I expected this bit to do what I want but not
class << #content
def replace_url_to_anchor_tag!
matching = self.match(/(https?:\/\/[\S]+)/)
"<a href='#{matching[0]}'/>#{matching[0]}</a>"
end
end
end
As content is an instance of String class, redefine String class is one option. But I don't feel like to going for it because it overwrites behaviour of all instances of String;
class HtmlSnippet < ActiveRecord::Base
class String
def replace_url_to_anchor_tag!
...
end
end
end
Any suggestions please?
The reason why your code is not working is simple - you are working with #content which is nil in the context of execution (the self is the class, not the instance). So you are basically modifying eigenclass of nil.
So you need to extend the instance of #content when it's set. There are few ways, there is one:
class HtmlSnippet < ActiveRecord::Base
# getter is overrided to extend behaviour of freshly loaded values
def content
value = read_attribute(:content)
decorate_it(value) unless value.respond_to?(:replace_url_to_anchor_tag)
value
end
def content=(value)
dup_value = value.dup
decorate_it(dup_value)
write_attribute(:content, dup_value)
end
private
def decorate_it(value)
class << value
def replace_url_to_anchor_tag
# ...
end
end
end
end
For the sake of simplicity I've ommited the "nil scenario" - you should handle nil values differently. But that's quite simple.
Another thing is that you might ask is why I use dup in the setter. If there is no dup in the code, the behaviour of the following code might be wrong (obviously it depends on your requirements):
x = "something"
s = HtmlSnippet.find(1)
s.content = x
s.content.replace_url_to_anchor_tag # that's ok
x.content.replace_url_to_anchor_tag # that's not ok
Wihtout dup you are extending not only x.content but also original string that you've assigned.