Pass Gmail Connection to sidekiq - ruby-on-rails

require gmail
gmail = Gmail.connect("testemail#gmail.com", "password")
SidekiqWorker.perform_async(gmail, item.user_id)
gmail.logout()
I want to pass a object to sidekiq,It is a gmail client object,And I don't want to create that object in perform method ,so I am passing and its going in string format , I am taking the help of open struct to pass it,
But its going in string format.
#OpenStruct gmail=#Gmail::Client0xbe18230 (abcd#gmail.com) connected>>

There are a couple of issues. I do not think the code above will work as you intend. The SidekiqWork is run asynchronously with the rest of the code. It looks like your code might be subject to a race condition. You cannot control if the worker code runs before or after the gmail.logout() call.
In addition, it's generally not considered a best practice to pass an object as an argument to a Sidekiq job. See the Sidekiq Best Practices. Instead, I might consider making the gmail connection from within the Sidekiq job.
As an alternative, you might consider using a class method in a lib class that stores an instance of the gmail client. You can memoize this connection like the following:
class GmailClient
def self.connection
#client ||= Gmail.connect("testemail#gmail.com", "password")
end
end

Related

Keep an instance of an object for the whole Rails server?

Essentially, I have a simple class that interacts with a local RPC server. Call it RPC_Class.
Every user the website will host will make requests that require the rails server to make calls to the RPC client.
This class needs to be instantiated as such
rpc = RPC_Class.new('localhost:4325')
And then I make calls to it
puts rpc.getbalance
Now, there are three options:
Create a new instance for every request needed (obviously a bad solution)
Create a new instance for every user's session
Create one instance for the whole server
I think option three is the most performative option. Where do I initialize this class such that I can use it throughout the server code?
I think you should go with Rails caching for handling this use case. You can cache the response when you make the API call the first time and use the cached response for all the calls you receive after that.
You can write a method like this:
def fetch_rpc_balance
Rails.cache.fetch("rpc_balance", expires_in: 24.hours)
rpc = RPC_Class.new('localhost:4325')
puts rpc.getbalance
end
end
Now, if you use the fetch_rpc_balance method in your logic, it will make the API call only the first time and return the cached response from second call onwards.

How to cleanly stub out REST client when in test environment

I have a basic model like the following
class MyModel
def initialize(attrs)
#attrs = attrs
#rest_client = Some::REST::Client.new
end
def do_a_rest_call(some_str)
#rest_client.create_thing(some_str)
end
end
For testing purposes, I don't want #rest_client to make remote calls. Instead, in a test environment, I just want to make sure that #rest_client gets called with a specific some_str when it goes through certain branches of code.
In an ideal world, I'd have an assertion similar to:
expect(my_model_instance).to.receive(do_a_rest_call).with(some_str) where in the test I will pass some_str to make sure it's the right one.
What's the best way to do this using RSpec 3.8 and Rails 5.2.2?
A solution that should work without any additional gems:
let(:rest_client_double) { instance_double(Some::REST::Client, create_thing: response) }
it 'sends get request to the RestClient' do
allow(Some::REST::Client).to receive(:new).and_return(rest_client_double)
MyModel.new(attrs).do_a_rest_call(some_str)
expect(rest_client_duble).to have_received(:create_thing).with(some_str).once
end
Basically, you are creating a double for REST client.
Then, you make sure that when calling Some::REST::Client.new the double will be used (instead of real REST client instance).
Finally, you call a method on your model and check if double received given message.

Is it safe to use Thread.current with Unicorn in Rails?

I need to get current login user name in model code, but I dont want to add a new additional parameter that will require many changes. So I am thinking whether it works to put the login user name in Thread.current, and then access it in model code.
It works in a simple try, but I have a concern whether it can work properly with unicorn multi workers, for example
- the login request is handled by worker 1, and the 2nd request is handled by worker 2. My basic understanding is that it should be ok because I set it from session into Thread.current in ApplicationController before filter that should be executed in the beginning of each request.
- if a unicorn worker is killed and restarted for whatever reason, is the request will be re-initiated, and still have the session data?
I dont have enough knowledge on unicorn... so probably it is a naive question...
And any other possible issue to use Thread.current?
Thanks in advance for your help!
You don't have to use Threads directly, you can use this gem https://github.com/steveklabnik/request_store
Your User model code can look something like this:
def self.current_user
RequestStore.store[:current_user]
end
def self.current_user=(logged_in_user)
RequestStore.store[:current_user] = logged_in_user
end
And in your controller, after login you can set User.current_user = current_user

Creating smart model in Ruby

I want to create an ActiveRecord-like interface for Salesforce, such that I can call
class Account < Salesforce::Model
end
and be able to call methods like Account.find_by_FirstName() using my own method_missing function.
However, connecting to Salesforce can be done in two ways: username and password, and oauth. If a username/password is used, I can have it defined in a salesforce.yml file and load automatically. But with oauth, I can't do that since each user will have this defined. I don't want to initialize a class with Account.new('oauth', oauth_parmas) or Account.new('username','password','sec_token'), but have the model determine which to use based off of rules and by seeing if one or the other is present.
Is there a way to implement this? In other words, is there a way for the model to know if the current user has a current oauth token or if a username/password defined?
Additionally, if I were to use this in a Rails app, the user would be logging in after the app was started, so the oauth token would be defined after the application started, and would be different for each of the multiple users. For example, let's say I call Account.find_by_FirstName('John') in AccountController#Show. I want the Account model to use the oauth token or usename/password without having to be asked. I also don't want to establish connection directly in my show method in the controller. I have two questions:
How would I implement this? Should I use a before_filter in the controller, or is there a way to implement this application-wide?
If I have multiple users connecting to Salesforce, would this cause issues in my application? In other words, would I have to worry about a connection being used by another user since the connection is dynamic?
Your needing is not different from ActiveRecord::Base connection establishment: you establish the connection using ActiveRecord::Base.establish_connection and every model you use after the connection establishment know which connection to use, because you memorized the connection at superclass level.
For Salesforce you can use the same concept:
class Salesforce::Model
def self.oauth_params
#oauth_params
end
def self.establish_connection(oauth_params)
#oauth_params = oauth_params
end
def self.find(id)
# use oauth_params here
end
end
class Account < Salesforce::Model
end
Now you can do something like
Salesforce::Model.establish_connection ['username', 'password']
Account.find 2 # without specifying authentication params
Since you know authentication params after knowing the logged user, you can establish the connection after the user is logged:
def sign_user
# user = ...
oauth_params = get_oauth_params(user)
Salesforce::Model.establish_connection(oauth_params)
end
Concurrency (threads)
If I have multiple users connecting to Salesforce, would this cause issues in my application? In other words, would I have to worry about a connection being used by another user since the connection is dynamic?
Legitimate question. If you run the Rails application in a threaded environment (threaded application server - f.e. Puma, multi-threaded architecture - JRuby, Rubinius...) AND Rails is configured as threadsafe (config.threadsafe!), you could have concurrency problems (the explanation is not trivial - check out this).
If this is your case you can scope the #oauth_params variable accessor to Thread.current:
class Salesforce::Model
#oauth_params = { Thread.current => nil }
def self.oauth_params
#oauth_params[Thread.current]
end
def self.establish_connection(oauth_params)
#oauth_params[Thread.current] = oauth_params
end
Would it be possible that the thread for the current user changes?
It is possible, if some code you execute runs inside a new thread. F.e.:
Salesforce::Model.establish_connection(oauth_params)
Thread.new{ p Salesforce::Model.oauth_params }.join #=> puts nil
In this case you have to reestablish the connection in the new thread (I can do it just if you need it).
I could request something on thread 1 and complete that request, but afterwards, someone else uses thread 1 and I have to use thread 2. Is this possible?
Thinking about it, you need to reset the variable at the beginning of the call in order to avoid that the next request uses the params set in any previous request:
before_action :reset_connection, :sign_user
def reset_connection
Salesforce::Model.establish_connection(nil)
end
def sign_user
# ...

How to deal with authentication for a Ruby API wrapper?

I'm working on an API wrapper for Viddler, which will eventually be made public, and I'm trying to figure out the best way to deal with authentication/API keys, specifically with usage within Rails applications in mind.
The easiest way to write the wrapper would be to just have the code create a new client each time, and the developer could store the API key in a constant for future use:
#client = Viddler::Client.new(VIDDLER_API_KEY)
The problem with this is, it's kind of clunky to have to keep creating client objects and passing in the API key. This gets even more complicated when you throw user authentication into the mix.
I'm thinking some sort of solution where I all the the API key to be set in the environment file and then the authentication would be done in a before_filter.
Viddler::Client.api_key = 'abc123'
Viddler::Client.authenticate! 'username', 'password'
Viddler::Client would then store this in a class variable, and you could call Viddler::Client.new without any parameters and make authenticated calls. One thing I'd be concerned about is that this means the developer would have to be sure to clear out the authentication before or after each request, since the class variables would persist between requests.
Any thoughts?
Storing the API key globally would for sure be pretty useful and certainly is the way to go for that kind of information. User authentication on the other hand I think shouldn't be stored globally, never ever, especially for a high level API, because telling your users to "ensure to add an after_filter :reset_viddler_auth" might lead to some unexpected security risks.
# in a config/initializer/*.rb file or something
Viddler::Client.api_key = "abc123"
# in the controller/action/model/wherever
#client = Viddler::Client.new # anonymous
#client.authenticate!("username", "password") # authenticate anon client
#client_auth = Viddler::Client.new("username", "password") # authenticated client
Guess like that you've best of both worlds :) Maybe even provide a way to create a new client with another API key like,
#client_other = Viddler::Client.new("username", "password", :api_key => "xyz890")
So... just my 2 cents.
PS: not sure how up-to-date it is, but there's already a ruby viddler wrapper, just FYI, http://viddler.rubyforge.org/rdoc/

Resources