I know I will get answers that I shouldn't do this, but due to specific way to solve problem I am facing, I will have to use session in my /lib/example.rb file. (or at least I think I will have to use it)
I am calling an action, which will first run (seudo code):
module ApplicationHelper
def funcion(value)
MyClass.use_this(value)
end
end
And then I will use it in my lib/example.rb
module MyClass
# include SessionsHelper # this is not working
def self.use_this(value)
# I want to be able to use session here. What I need to do that in order to make it work.
session[:my_value] = value
end
end
What should I do in order to use session inside MyClass (I can pass variable to MyClass.use_this(value,session), but I wouldn't want to do that
Edit:
What I want to achieve with this session thing is that I would like to preserve a value during multiple requests. I am making a call to the web application multiple times, and I want to preserve some value on the next call. I am calling the app via API and I shouldn't use database to save values. So I have left with sessions, or text files, or even maybe cookies to make this happen - to preserve the same value on multiple calls.
Why not include the module in your controller, and then call the use_this function directly from there?
module MyClass #should probably rename this anyway
def use_this(value)
session[:my_value] = value
end
end
class SomeController < ApplicationController
include MyClass
def some_action
...
use_this(the_value)
...
end
end
In order to use session inside MyClass may be you could use instance variable #session:
module MyClass
extend SessionsHelper
def self.use_this(value)
#session[:my_value] = value
end
end
module SessionsHelper
def some_method
#session = ...
end
end
self.include(module) method makes the instance methods (and instance variables) of the included module into instance methods of the including module.
Edit: include SessionsHelper changed to extend SessionsHelper
self.extend(module) -- methods of receiver become class methods of that class and instance variables will work between this methods.
Related
TL;DR;
I need to make some ##vars of a static method (extends) in one module visible to a instance method in another module(includes).
How to accomplish that once only setting ##var=value was not enough to make it visible?
Maybe you can just read my capitalized comment bellow and jump to question 4.
Hi, I would like to add an method to my models to index some data in a mysql table with some full text search fields.
In order to accomplish that, I created the following module:
module ElasticFakeIndexing
module IndexingTarget
#instance method to be called on model to get data to save
def build_index_data
{
entity_id: self.id,
entity_type: self.class.name,
#UNABLE TO ACCESS IF SET ONLY WITH ##var=value. Why?
#AND ALMOST SURE THAT USING class_variable_set IS THE CAUSE OF CONFIGURATION OF ONE MODULE MESSING UP WITH ANOTHER'S
title: ##title_fields.collect{|prop| self.send(prop.to_sym)}.join(" || "),
description: ##description_fields.collect{|prop| self.send(prop.to_sym)}.join(" || "),
}
end
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
#class method to declare/call at a given model
def elastic_fake(options = {})
#Make sure we always get an array so we can use 'join'
title_arg = Array(options[:title])
ElasticFakeIndexing::IndexingTarget.class_variable_set(:##title_fields, title_arg)
description_arg = Array(options[:description])
ElasticFakeIndexing::IndexingTarget.class_variable_set(:##description_fields, description_arg)
extra_arg = Array(options[:extra])
ElasticFakeIndexing::IndexingTarget.class_variable_set(:##extra_args, extra_arg)
end
end
end
end
And I use it this way at my models:
class SomeModel < ApplicationRecord
#includes the module
include ElasticFakeIndexing::IndexingTarget
...
# 'static' method call to configure to all classes of this model
elastic_fake(title: "prop_a", description: ["prop_b", "prop_c", "prop_d"])
end
And at some point of my code something like this will be called:
index_data = some_model_instance.build_index_data
save_on_mysql_text_search_fields(index_data)
But I got some problems. And have some questions:
when I use/include my module in a second model, looks like the configuration of one model is being visible to the other. And I got 'invalid fields' like errors. I guess it happens because of this, for example:
ElasticFakeIndexing::IndexingTarget.class_variable_set(:##title_fields, title_arg)
But I got to this this because only set ##title_fields wasn't enough to make title_fields visible at build_index_data instance method. Why?
Why using only #title_fields isn't enough too to make it visible at build_index_data?
How to design it in a way that the set of fields are set in a 'static' variable for each model, and visible inside the instance method build_index_data? Or as a possible solution, the fields could live in a instance variable and be visible. But I think it should live in a 'static' variable because the fields will not change from one instance of the model to another...
Any thoughts? What am I missing about the variables scopes/visibility?
Thank you
Read the following articles on Ruby variables:
Ruby Variable Scope
Understanding Scope in Ruby
quick reminder: ##title_fields, class variable, must be initialized at creation time, while #title_fields, instance variable, hasn't such requirement.
Instead of relying on class variables I recommend using class side instance variables. Class variables will easily be overwritten between individual models including the module. Class side instance variables however are save.
Using some of the syntactic sugar (namely concern and class_attribute) rails offers you could write something like
module ElasticFakeIndexing
extend ActiveSupport::Concern
included do
class_attribute :title_fields,
:description_fields,
:extra_args
end
class_methods do
def elastic_fake(options = {})
...
self.title_fields = Array(options[:title])
...
end
end
def build_index_data
...
title: self.class.title_fields ...
...
end
end
My model, Widget.rb, has include ApplicationHelper and my instance methods have no trouble using any method defined in application_helper.rb
However, when I try to use one of the helper methods in any of my class methods such as
def self.send_broadcast(guid)
track_guids(guid) # defined in application_helper.rb
end
I get No Method error.
Is there some secret handshake to permit use of a ApplicationHelper method inside a class method?
ApplicationHelper is just a module:
module ApplicationHelper
def track_guids(something)
end
end
class Widget
extend ApplicationHelper
def self.send_broadcast(guid)
track_guids(guid)
end
end
Now you should have access to the module methods from a class method. I'm not sure if you can both extend and include the same module though... not really sure what that'd do.
Edit to add:
I'm not sure what will happen if you try both extending and including the same module into the class. With extend you get the module included at the class-level, with include it is included at the instance-level. It might give you the methods at both class and instance if you do both... or it might die horribly. Give it a try?
I don't think you can access instance methods unless self is an instance. You could make an instance of Widget and call a class method from that, or you could try to call the methods from the module directly.
I have a gem I'm developing that is based around using filters on ApplicationController. It's basically for logging, and one of the modules defines an around filter like so:
module LogExceptionFilter
self.included(base)
base.around_filter :do_a_bunch_of_logging_stuff
end
def do_a_bunch_of_logging_stuff
...
end
end
It happens to be an around filter where I deal with exception logging, but my question would apply for any filter.
So it's supposed to be used like this
class ApplicationController
include LogExceptionFilter
end
So what I'm worried about is if someone does:
class ApplicationController
include LogExceptionFilter
include LogExceptionFilter
end
I don't want to execute do_a_bunch_of_logging_stuff twice. So first
1)If do_a_bunch_of_logging_stuff is included twice, will rails apply the filter twice?
2)Is it my responsibility to protect the user from doing this? I could do so with a class variable, something like:
module LogExceptionFilter
class << self
cattr_accessor :filter_loaded
end
self.included(base)
unless filter_loaded
base.around_filter :do_a_bunch_of_logging_stuff
filter_loaded = true
end
end
def do_a_bunch_of_logging_stuff
...
end
end
This variable is not thread safe so it's something that I'd want to be careful about putting in. But I don't want to write a library that can be easily broken. Thanks.
Here are some relevant links:
http://www.ruby-forum.com/topic/95269
http://www.ruby-forum.com/topic/164588
Basically, a module will only be included once, but the included callback may be called multiple times.
I'm slightly new to Rails (i.e. stupid and need some teachin').
I have a controller (call it ControllerFoo) that performs a particular task (theMethod) which could be useful in other controllers (say, from within ControllerBar). So, of course, the method is defined as self.theMethod in ControllerFoo (which means it's a class method, right?), and access in ControllerBar as ControllerFoo.theMethod. Confused yet?
Here's the problem: the ControllerFoo.theMethod uses session data, and when called from ControllerBar, session is nil. In fact, it seems that session is also nil when being called from itself. I guess what I'm saying is class methods can't access session data?
<rant>I hate how session data can't simply be accessed anywhere like in PHP</rant>
So for now, since I'm not smart enough to know how to do this correctly, I've just duplicated the logic in several places throughout my app. But this is not DRY at all, and I hate it.
So how can I create a method in a controller that's accessible to other controllers and can also access session data?
class ControllerFoo < ApplicationController
def self.theMethod (greeting)
p "#{greeting} #{session[:user]}!"
end
end
class ControllerBar < ApplicationController
def show
ControllerFoo.theMethod("Hello,")
end
end
Couple of options...
Put the shared method in the shared parent ApplicationController
Create a module that both ControllerFoo and ControllerBar will include
e.g.
module SharedModule
def theMethod (greeting)
p "#{greeting} #{session[:user]}!"
end
end
class ControllerFoo < ApplicationController
include SharedModule
end
class ControllerBar < ApplicationController
include SharedModule
def show
theMethod("Hello,")
end
end
The way you would do this is Ruby would be to create a module containing the class (or instance) methods you wish to share and include it in the classes you need to have those methods defined in.
I'm trying to extend a specific model in my app using railtie. Adding class methods works, but neither does instance methods. I have the following code:
class Railtie
def self.insert
return unless defined?(::ActiveRecord)
::MyApp::MyModel.extend(ModelMethods)
end
end
module ModelMethods
def hello
puts "hello"
end
end
Now, I'm able to call MyModel.hello. But what should I do if i want to add some instance methods? When I try to add them through ::MyApp::MyModel.include(InstanceMethods) it fails with something saying calling a private methods.
include is a private method and cannot have an explicit receiver. You can get around this limitation by using send:
MyModel.send(:include, InstanceMethods)