Keep an instance of an object for the whole Rails server? - ruby-on-rails

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.

Related

How can I reuse the same watir object in the next controller action

I am using watir with headless browser. I would need to perform three steps add location, add vehicle and fetch product from the another site , for the information which I want from a another website.
I am submitting these three details from my server and performing these all three step in one HTTP request with the help of watir and headless.
I just want to breakdown one http request in to three http request on my server. The request will be:
1)add_location: Fire a http request which will open headless browser and select the location.
2)add_vehicle: Fire a http request which will reuse headless browser in which location added and we will select the vehicle.
3)Fetch product: Fire a http request which will reuse headless browser in which location and vehcile added, will fetch the product list.
I am not getting any way to reuse watir and headless session which is already open in the next http request at rails side.
Code Sample:
class TestsController < ApplicationController
def add_location
#headless = Headless.new
#headless.start
#watir = Watir::Browser.new
#watir.goto('www.google.com')
#watir.text_field(id: 'findstore-input')
.wait_until(&:present?).set(params[:zip_code])
#watir.a(id: 'findstore-button').click
#watir.div(class: 'notifier').wait_while(&:present?)
end
def add_vehicle
#need to resuse above #watir object in this action
end
end
The design change from 1 request to three has a big impact on your API, as even this simple part is now stateful, i.e. you need to keep the state between each of the three request.
Once you understand that, you have different possibilities.
Build your information request after request, and only when it is complete, use watir to get the information you need.
This is basically just changing the API and you store the data in a session, cookie, database or whatever.
It doesn't have a big impact on the changes you have to make, but does not bring any advantage.
Already forget this point, but you could pass around a global reference to your object in a session, but it has a HUGE memory impact and you could run into race condition.
NEVER do this, please
In case you really want to split the watir request into three different step (e.g. because it is too slow), you can use a background job to which you can transmit the user's data when it arrives (using dedicated databases, websocket, or whatever), then wait for your job to end (i.e. get a result), e.g. by trying to access it until it's available.
This solution requires a lot more work, but it keeps your HTTP requests with your client lightweight and allow you to do any kind of complex task in the background, which would otherwise probably timeout.
You can make use of the hooks file, to initiate the browser in headless mode and assign to the variable to call within separate def to pass url to the browser.
For example:
in hooks, you can add it as below
#browser = Watir::Browser.new :chrome, options: {args: ['--headless']}
So you can reuse the #browser.goto('www.google.com') in one def and can use the same instance some other call as well.
def example1:
#browser.goto('www.google.com')
end
def example2:
#browser.goto('www.facebook.com')
end
.
.
.
etc
Hope this helps.

How to make a rails server wide object?

I am using the RedditKit gem and in order to access certain elements, I need to send a request to reddit api to create a "client" object. Below is my current logic:
## application_controller
before_action :redditkit_login
private
def redditkit_login
#client = RedditKit::Client.new ENV["reddit_username"], ENV["reddit_password"]
end
As you can see in my logic here, before EVERY REQUEST, I am subsequently making a new client object and then using that everywhere.
My question is, how do I only make one client object which can be used to serve ALL requests from anywhere?
My motive behind this is speed. For every request to server, I am making a new request to reddit and then responding to the original request. I want to have the client object readily available at all times.
You have a lot of options. A simple one would be to create a config/initializers/reddit_client.rb file and put in there:
RedditClient = RedditKit::Client.new ENV.fetch("reddit_username"), ENV("reddit_password")
(note I switched to ENV.fetch because it will error if the key is not found, which can be helpful).
You could also rename the file as app/models/reddit_client.rb. Although it's not really a model, that folder is also autoloaded so it should work as well.

Pass Gmail Connection to sidekiq

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

ruby on rails model method loop

The title is probably not the best, but here is my question. I have a rails model with this method
def stats(event_id)
while true do
sleep 1
response = HTTParty.get (xxx)
response
end
end
I continuously stream the response to the client and then I have a client script that calls this method whenever a user click on another event.
in my head, I think there is an issue. whenever the client script calls this method again, a new instance is created (in the controller) which means i'm streaming 2x to the client (the previous one and the new instance).
How can I get this to work? I need this instance to exist once for each user so no duplicate data is streamed and previous instances for the user should stop whenever a new request is made.
I don't mind moving this to the controller if it helps. Also, you access the streaming page only if you are logged in.
Thanks.

Make Rails return response from other local URL

I want a /plan method to return a json object that's itself returned by another local (but belonging to another web app in java) URL (let's call it /plan2 for the sake of this question).
What I want is not a redirect but really to have /plan return the data as it is returned by /plan2, to which I'm appending a bunch of other keys. Since the request is local, would using Net::HTTP be overkill? What are my options, considering I'd also like to send an HTTP Accept header along.
Shooting in the dark here, but I am assuming that /plan belongs to public Rails app and the /plan2 is the url from another web app (maybe not even Rails) on the same server accessible by Rails app but not publicly available. If this is the case, then yes, you can get the response, but I would suggest rather OpenURI or Mechanize than Net::HTTP. If you put it in respond_to JSON format block then everything should be fine with Accept header also.
are you speaking of re-using functionality of another controller-method?
there is no standard way of doing this in rails. you could either put the common functionality into a module and include it this way, or use inheritance if the functionality is in another controller. if it's the same controller class, you could just call the method.
if it's a "private" url, how are you going to call it via http?!
I would suggest encapsulating whatever functionality /plan2 uses and simply re-use that in /plan1
But if you just want to get it to work...
class PlanController < ApplicationController
def plan1
plan2(extra_parameter_1: true, extra_parameter_2: 'hello')
end
def plan2(extra = {})
params.merge!(extra)
# Whatever your code was before...
end
end

Resources