I have only one controller and some actions in it to handle different functionalities related to IMAP. So my problem is I don't want to create a separate connection for every action. For example in an action I can do something like(it is not the actual code):
def index
#imap = Net::IMAP.new(server, 993, true)
#imap.login(user, password)
#imap.select("INBOX")
end
Again in another action inside the same controller, if I need to do something related to IMAP then I will have to create the #imap variable again.
I am working with IMAP first time so as per my understanding new method in each action will create another connection to the server and I have heard google has connection limit (15) for the number of IMAP connections.
I can not serialize this connection object or store it in any other service like Redis or Memcached or cache it, So how can I create this connection once and use it all other actions, at least actions inside the same controller if possible? If not possible then any other solutions to handle this problem?
And of course I can cache the data I need from the mailbox but that can't help much since there are some other actions which won't need the data, it will need to do so some operations in the mailbox like deleting mails, so that will need the connection instance.
How about you create a service object (singleton) that wraps you Net::IMAP. You can stick it in app/services/imap_service.rb or something like that. For an example on what that would look like:
require 'singleton' # This is part of the standard library
require 'connection_pool' # https://github.com/mperham/connection_pool
class IMAPService
include Singleton
def initialize
#imap = ConnectionPool.new(size: 15) { Net::IMAP.new(server, 993, true) }
end
def inbox(user, password)
#imap.with do |conn|
conn.login(user, password)
conn.select("INBOX")
end
end
end
You access this singleton like IMAPService.instance e.g. IMAPService.instance.inbox(user, password). I added in the connect_pool gem as per our discussion to make sure this is thread safe. There is no attr_reader :imap on IMAPService. However, you can add one so that you can directly access the connection pool in your code if you don't want to include all of the necessary methods here (although I recommend using the service object if possible). Then you can do IMAPService.instance.imap.with { |conn| conn.login(user, password) } and don't need to rely on methods in IMAPService.
It's worth noting that you don't have to use the Singleton mixin. There is a really good article on Implementing "the lovely" Singleton which will show you both ways to do it.
If you want the connection to stay open between requests you can not store it as an instance variable in your controller since each request will have its own instance of the controller.
One way to store the connection is to use a singleton.
Here is an example:
class ImapService
attr_accessor :imap
def initialize
#imap = Net::IMAP.new("imap.gmail.com", 993, true)
#imap.login("username#gmail.com", "password")
#imap.select("INBOX")
end
##instance = ImapService.new
private_class_method :new
def self.instance
return ##instance
end
end
This will open the connection the first time you access it, and if you access it again, it will use the old connection.
You would access the imap variable with ImapService.instance.imap anywhere in your application.
Related
Hello people. I'm creating a log process in my Rails 5 application, inside the application controller. I'm creating there because I want to call the log process inside many controllers with a before_save property. The log will save the changes that user performs in the form on edit view template. The problem is that I can't get the <ObjectController:> inside application controller. I've already got the instance variable from the controller, but I need the ObjectController too, because I have to get the strong parameters from controller object. The strong parameters holds all data that user inserted on input fields.
This is what I've done already:
app/controllers/application controller
def log
#controlr = instance_variable_get("##{controller_name.singularize}") #get the edited object
attribs = #controlr.attribute_names #get object table column names
edited_data = controlr_params #stuck here!
ctrlr = #controlr.attributes #retrive object data from db
...
##compare the edited_data with the actual data from db and check if something was changed
end
So, I need to obtain the Controller Object to access the strong parameters in order to compare if user edited any data. I'm not sure if this is the best way/practice to do this. If there is a better way, I'd like to know. But I need to call this process in a great number of controllers that require a data log.
Thanks for you time and sorry any bad english..
If params method won't help you to achieve your goal (but it's worth to try) you can always access current instance of controller object by calling self in context of any instance method or action.
To test you can put byebug in any action, call that action in browser with additional parameters and type self in console.
For example, in controller:
class UsersController < ApplicationController
def show
byebug
end
end
in browser:
localhost:3000/?some_param=1234&another_param=testing
There will be a lot of useful stuff in there, like self.instance_variables => [.... :#_request, ... :#_params].
Also request method contain all info about current request including parameters.
Hope that'll help.
Hi all a newbie question.
I am creating one rails application where after showing result to user i need to perform some other operations in background to update database.
(sorry i am poor at explaining things, see my example code for situation)
In my controller's action i am using third-party api to fetch data from remote server.
After showing fetched data to user i want to update database table with customized data on fetched data.
class MyController < ApplicationController
def SomeAction
#some logic
#result = FetchDataFromApi#Using third party api to fetch huge data
#show result to user
#after showing i need to do following operations elsewhere (controller helper)
#based on some fetched result i want to update my table
myId = #result.id
dataObj = MyModel.find(myId)
info = #result.information.gsub(',',' ') #some string operation
dataObj.update_attributes(:info, info)
end
end
I can use spawnling gem to perform extra operation after showing result to user. But i am just curious if i could do this with helper or other rails stuff.
While there are cases where it makes sense to use a background processing. This is not one of them.
I assume 'show result to user' means rendering some template based on the data fetched from the api.
Actually, it might be a good idea to move accessing the external API into background, but that would require changing the flow. As for the updating the db record, its generally not a good idea to move it to background.
that being said, I wouldn't do the update in the controller, I'd move it into the model or a 'mutation' class:
class Model
def self.update_from_api
res = API.fetch ...
object = find res.id
object.update_from_api! res
res
end
def update_from_api(api_data)
update_attributes! info: api_data.gsub(....)
end
end
I have array of objects. I can't store it in DB for performance reason. I tried store array in Global ($var) and Class (##var) variables (in controller), but it was the same array for all users. It should be unique for each user session and in each session it should be able to be modified quickly.
I understand that the session[] - is not the best solution. What is the best way?
I'm doing something like this $lines_rules << Processing::rule_creator(...) in every time, when run action in controller.
$lines_rules - it is my array of objects.
Why DB is not right for store $lines_rules? In $lines_rules I store objects with lambda function. When user press button I need call every lambda function with user input and keep result. Then I load new objects into $lines_rules. Thus every request change $lines_rules. I think work with memory is the better way for perfomance.
UPDATE
I use $global_hash [ session[:session_id] ] and this technique for resource saving:
def dead_sessions_killer
ActiveRecord::SessionStore::Session.where(["updated_at < ?", 30.minutes.ago]).each do |session|
$global_hash.delete_if {|key, value| key == session.session_id }
session.delete
end
end
Use a global, but a global hash, keyed by the user's session id.
Store whatever you want in there, although with proper caching, hard to say if this is a great idea.
You could declare the array as a class variable (only 1 # sign), and provide an accessor method.
That is:
class Foo
#some_array
def some_array
#some_array
end
end
From other classes, this lets you call foo_instance.some_array, which keeps the array specific to the user, but allows public access from any class.
You could store the user session in memory with memcache. That would be convenient and fast.
http://awesomerails.wordpress.com/2011/08/23/rails-3-memcached-session-store/
Note I am referring to one request, and not several requests and sessions.
I have several components that require Doctrine user object, some are located in layout, others are located in templates. Sometimes I need that Doctrine user object in action. Currently I have added a function to sfUser class that loads that object from database, which means every time I call that function I make a call to db. I'd like to know where to store this object so that I can access it without having to query db every time I need it. Again, we're talking about a single request, not several requests or something that would require session.
Can I save it in sfContext somehow? Any other places so that it can be available everywhere?
You can store it in your model's Table class, because tables are always accessed as singletones.
class sfGuardUserTable extends PluginsfGuardUserTable
{
protected $specialUser = null;
public function getSpecialUser()
{
if (null === $this->specialUser)
{
$this->specialUser = $this->findOneById(1);
}
return $this->specialUser;
}
}
Now, you can use this in actions and components like this:
$u = sfGuardUserTable::getInstance()->getSpecialUser();
And you will always end up with one query.
you can configure Doctrine cache so that the result of this specific request is always cached. What if so good about it is that if you use, say, the APC backend, you will have it cached across requests. You also get query caching as a bonus (this is not result caching, read the link I provided carefully)!
In asp.net there is something called Request.Items that I can add an object to, and then I can check to see if an object is present in the .Items collection from another part of my code (like in another class).
Does Rails have something like this?
One of the most popular options is to use the request_store gem, which allows you to access a global store that you from any part of your code. It uses Thread.current to store your data, and takes care of cleaning up the data after each request.
RequestStore[:items] = []
Be aware though, since it uses Thread.current, it won't work properly in a multi-threaded environment where you have more than one thread per request.
To circumvent this problem, I have implemented a store that can be shared between threads for the same request. It's called request_store_rails, and the usage is very similar:
RequestLocals[:items] = []
You may define a class method (in any class you want) to store your data:
class Xyzzy
def self.items
#items ||= {}
end
end
# ....
if Xyzzy.items.include? :fubar
Xyzzy.items[:asd] = 1
end
But you should decide when you want this 'cache' to be cleared. If you want it only for one request, define a before_filter which will call Xyzzy.items.clear
The class objects are global. You may also use a standard global variable, but that would be less elegant. :)