What data (if any) persists across web-requests in Ruby on Rails? - ruby-on-rails

I decided to use the singleton design pattern while creating a view helper class. This got me thinking; will the singleton instance survive across requests? This led to another question, Which variables (if any) survive across web requests and does that change depending on deployment? (Fastcgi, Mongrel, Passenger, ...)
I know that Controller instance variables aren't persisted. I know Constants are persisted (or reloaded?). But I don't know about class variables, instance variables on a class, Eigenclasses, ...

The simple answer is none. Each request is treated as an independent event and no state information is carried over apart from what is stored in the user session and any external databases, caches, or file stores. It is best that you design your application with this in mind and not expect things to persist just because you've set them.
The more complicated story is that some things do persist. For example, you can create a class variable on a controller and this will be carried from one request to the next as you might expect. The catch is that this only applies to the singular instance of that controller, as contained within that process, and will not apply to requests served by other processes. If you need caching, make use of the Rails.cache infrastructure and avoid hacking in your own.
A typical production environment is a complicated, ever-changing thing, where processes are created and destroyed constantly and there is no way to determine in advance which process will ultimately end up serving a particular request. As many deployments involve not only multiple processes on a single machine, but multiple machines, there really is no practical way to create application-wide singleton objects.
The best thing you can do is build a layer on top of the caching engine where your singleton object is merely a wrapper to functions that fetch and write from the cache. This gives you the appearance of a singleton object while maintaining inter-process consistency.

I know that this post is old, but for who is looking a solution, it's possible to use Rails.Cache, like this:
class TestEventsController < ApplicationController
require 'httparty'
##cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
before_action :get_data, only: [:get]
before_action :get_response, only: [:set]
def get
uri = "https://hooks.zapier.com/hooks/catch/zap_id/"
event_id = event_id_generate()
##cache.write(event_id, "")
result = HTTParty.post(uri.to_str,
:body => {id: event_id, data: #data}.to_json,
:headers => {'content-Type' => 'application/json'})
sleep 2
render json: { 'value': ##cache.read(event_id) }, status: 200
end
def set
##cache.write(#id, #value)
render json: { 'value': ##cache.read(#id) }, status: 200
end
def get_data
#data = params["data"]
end
def get_response
#id = params["id"]
#value = params["value"]
end
def event_id_generate
token = SecureRandom.urlsafe_base64(10, false)
end
end
What I'm doing is receive a request in a route, sending a GET to Zapier, and waiting for the answer in another route. Rails opens a new Thread for each request, so I write in the RAM my data in format 'key: value'
Rails.cache in different threads not caching?

The web is a stateless medium. Unless you purposely save data in a session or pass it in a get or post, each web request starts with a blank slate. Any objects created with the current request are destroyed once the page is delivered to the web browser.

Related

Rails 5.2 Service objects with non current user

I am trying to forward an action to another controller through the service object methodology in rails 5.2.
The create action should pass the user id for the create action but I am failing at passing that param appropriately.
Business logic is the following: a user rents out an asset, the potential renter makes a request, when the asset owner agrees to a visit, the rent user is created as a client in another controller to organise a visit.
I am trying to address the create action in the client controller as follows:
In the rent controller :
private
def visit(room, rent)
#newclient = NewclientService.create(params)
if #newclient.save
rent.Approved!
...
else
rent.Declined!
...
end
and then in the app/service/newclient_service.rb
module NewclientService
class << self
def create(params)
#rent = Rent.find_by(id: params[:id])
user = #rent.user_id
name = #rent.user.fullname
email = #rent.user.email
Client.create(user_id: user, name: name, email: email)
end
end
end
This code does the job. The db is filled up, validations and strong params seem to work and it seems to me robust/secure enough.
Question: is the service object (my way ?) route the most preferred way for forwarding that action ?
Thanks for your help,
I like the pattern in principle and it has really cleaned up the apps which I produce. There are a couple of nice gems that I typically use to get the job done and keep the controllers clean.
I use the mutations gem and simple_command. These two together give you a nice (almost completely) consistent API. The mutations gem in particular is what I use for digesting and resolving JSON input data from params which can then handle processes for me.
This is a good pattern in the sense that it encapsulates the logic of discrete functionality very well. For example, if you have a RegisterUser mutation, you can use that in a controller or you can use it to digest a whole list of objects etc. You can even use the builder option for attributes to process deeply nested json.
I would recommend checking it out.
https://github.com/cypriss/mutations
For those times where I am not processing JSON from an API and want to create discrete encapsulated functionality I generally use simple_command https://github.com/nebulab/simple_command. This approach is also great because it allows you to use the same components from any context. For example, a command called GetLatestTweets.call() could be used equally well from a controller as it could from the REPL.
Both of these libraries provide you with a result object which you can then process as appropriate
def create
outcome = NewClientMutation.run(params.require(:resource).permit!)
if outcome.success?
render json: outcome.result, status: :created
else
render json: {resource: outcome.result, errors: outcome.errors}, status: :unprocessable_entity
end
end
In my particular case I use 'permit!' since the mutations library ignores any parameters that aren't explicitly specified which means that strong parameters aren't necessary if you use this library as it filters parameters as well.

Rails: Passing API JSON Response to View, without Model

Disclaimer: I'm doing something which may qualify for Code Smell of 2015 Award. Using rails 4.2, no javascript or anything like that.
I have a form into which users input their data. With this data I call a third-party API which will remain nameless. :)
I have no model, I'm not persisting anything. (Part of a larger app, not a one-pager.) Thus when faced with presenting the user with the response, I find myself stuck on how to render the data properly into a view. The response contains an array of hashes which I obviously intend to present the user.
I render the form into widgets/new, etc, create and process the request, etc, but then what?
I thought maybe I could make use of decorators to do my dirty work but not sure how to actually get the user off to the respective view. I don't care which view. Call it a widget_path.
WidgetsController < ApplicationController
def new
render :new
end
def create
# preparing request
...
# data = response, each_serializer, WidgetSerializer, root: false
# data = WidgetDecorator.new(render_serialized(response, WidgetSerializer))
# #data = WidgetDecorator.new(JSON.parse(response))
# redirect_to ??_path ... and take your #data with you
end
end
What do I do?
Your idea of Model is unfortunately corrupted by Rails itself (sorry).
A model is business logic not an ActiveRecord::Base (not necessarily). Controller methods shouldn't be big, ~5 lines long is probably the maximum with a ~100 lines max per controller file. Try to stick with this and it will automatically correct good chunck of code smells.
Anyway, you may handle this with a Model, as a PORO (plain old ruby object).
class MyApiResponse
attr_reader :myapikey
attr_reader :whatever
def initialize(myapikey, whatever)
#myapikey = myapikey
#whatever = whatever
end
def get
#_response ||= JSON.parse(run_api_stuff(myapikey))
end
end
So in controller you would do something like
def create
myapiresponse = MyApiResonse.new(myapikey, whatever)
#response = myapiresponse.get
end
Last but not least, you can't pass what you obtained through the API in the redirect. You are subject to HTTP limits so, you have a limit on GET params size, a limit on session and you can't redirect to a POST. You have 3 options
Best is store last api request for given user in the database and fetch it back through an ID (which will travel through the redirect)
Store it in session if request is really small (and you must ensure it is small!)
Perform the API request again after the redirect, horrible. Otherwise perform the API request only after redirect, not sure if this is an option though

Save requests for a certain action

I want to save information about requests to a certain action in a model named Impression.
I assume it's benificial for the visitor's response time to save this info in an after_filter, e.g:
after_filter :save_impression
private
def save_impression
Impression.create!(ip_address: request.remote_ip, controller_name: params[:controller], action_name: params[:action], referer: request.referer)
end
Can this code be optimized or am I doing it right?
A good solution for that would typically involve using a worker. Anything that is not mission critical to the request and that involves complex computing can be deferred and run later by a background job.
Two common implementations of workers are delayed_job and resque.
For example, with resque, you would have a job class in app/jobs/impression_creation_job.rb, containing something like that :
class ImpressionJob
#queue = :impression
def self.perform( attrs )
Impression.create!( attrs )
end
end
And you can call it in your controller like that :
after_filter :save_impression
private
def save_impression
Resque.enqueue( ImpressionJob, ip_address: request.remote_ip, controller_name: params[:controller], action_name: params[:action], referer: request.referer)
end
This will ensure a fast handling on the request part (it just loads data in redis) and will then be processed by a background process (see resque documentation for how to set it up and start workers).
Please note that this will be useful in your case in only two cases :
Your app is always under heavy load or need especially good response time
You do big computations in Impression#before_create or other callbacks
If not matching one of those conditions, it's probably more effective to just let your impression creation in a controller filter : accessing database has a cost, but not that much that a user will feel when you make a single insertion in database.
This will still run before render. To run after the render/redirect, you need to spawn a separate thread.
See this question

Alternative to using Thread.current in API wrapper for Rails

I've developed an application that allows our customers to create their own membership protected websites. My application then connects to an outside API service (customer specific api_key/api_url) to sync/update/add data to this other service. Well, I've had an API wrapper written for this other service that has worked up to this point. However, I'm now seeing very random drops where the connection is nil. Here is how I'm currently using the connection:
I have a xml/rpc connection class
class ApiConnection
attr_accessor :api_url, :api_key, :retry_count
def initialize(url, key)
#api_url = url
#api_key = key
#retry_count = 1
end
def api_perform(class_type, method, *args)
server = XMLRPC::Client.new3({'host' => #api_url, 'path' => "/api/xmlrpc", 'port' => 443, 'use_ssl' => true})
result = server.call("#{class_type}.#{method}", #api_key, *args)
return result
end
end
I also have a module that I can include in my models to access and call the api methods
module ApiService
# Set account specific ApiConnection obj
def self.set_account_api_conn(url, key)
if ac = Thread.current[:api_conn]
ac.api_url, ac.api_key = url, key
else
Thread.current[:api_conn] = ApiConnection.new(url, key)
end
end
########################
### Email Service ###
########################
def api_email_optin(email, reason)
# Enables you to opt contacts in
Thread.current[:api_conn].api_perform('APIEmailService', 'optIn', email, reason)
end
### more methods here ###
end
Then in the application controller I create a new ApIConnection object on every request using a before filter which sets the Thread.current[:api_conn]. This is because I have hundreds of customers each with their own api_key and api_url, using the application at the same time.
# In before_filter of application controller
def set_api_connection
Thread.current[:api_conn] = ApiService.set_account_api_conn(url, key)
end
Well my question is that I've read that using Thread.current is not the most ideal way of handling this, and I'm wondering if this is the cause for the ApiConnection to be nil on random requests. So I would like to know how I could better setup this wrapper.
Answer 1
I'd expect that the problem is the next request coming before the connection has finished, and then the before_filter overwrites the connection for the still ongoing connection. I'd try to stay away from threads. It's easier to fork_off, but there's certain caveats to that as well, especially regarding performance.
I try to move logic like this over to a background job of some sort. A common solution is delayed job https://github.com/collectiveidea/delayed_job that way you don't have to mess with threads and it's more robust and easy to debug. You can then start background jobs to asynchronously sync the service whenever somebody logs in.
#account.delay.optin_via_email(email,user)
This will serialize the account, save it to the job queue, where it will be picked up by delayed job unserialized and the method after delay will be called. You can have any number of background jobs, and even some job queues dedicated to certain types of actions (via using job priorities - let's say two bj for high prio jobs and one dedicated to low prio jobs)
Answer 2
Just make it as an object instead
def before_filter
#api_connection = ApiConnection.new(url, key)
end
then you can use that connection in your controller methods
def show
#just use it straight off
#api_connection.api_perform('APIEmailService', 'optIn', email, reason)
# or send the connection as a parameter to some other class
ApiService.do_stuff(#api_connection)
end
Answer 3
The easiest solution might just be to create the api connection whenever you need it
class User < ActiveRecord::Base
def api_connection
# added caching of the the connection in object
# doing this makes taking a block a little pointless but making methods take blocks
# makes the scope of incoming variables more explicit and looks better imho
# might be just as good to not keep #conn as an instance variable
#conn = ApiConnection.new(url, key) unless #conn
if block_given?
yield(#conn)
else
#conn
end
end
end
that way you can easily just forget about the creation of the connection and have a fresh one handy. There might be performance penalities with this but I suspect that they are insignificant unless there's an extra login request
#user.api_connection do { |conn| conn.optin_via_email(email,user) }

How do I store an instance variable across multiple actions in a controller?

Say I want to store some variable in my controller. I want to initialize it in one action, increment it in another, and read it in yet another. Just declaring this variable with #foo doesn't work because #foo dies after the action that created it is rendered.
I do not want this variable to be stored in a model.
Is there a way to preserve this variable besides storing it in a session?
It seems like I've run into this simple problem a few times, and I want to know the best way to go about solving it.
Not really. Each call to a controller action is stateless. Nothing is available after the controller action finishes. A new controller instance is created for each request, and then discarded at the end of the request.
If you don't want to store it in the session, or database model, you don't have many options if you're wanting that variable to be specific to a particular session.
If it is global across all sessions, you could put it in a ##class_variable rather than an #instance_variable, but that can get messy once you start having multiple Rails processes (each which will have their own copy of it), or if you're running in threadsafe mode, you can end up with nasty concurrency bugs.
I guess you could look at something like memcached, but you'd still need to key that to some user_id or other session marker (unless it's global)
I too am wondering why you are against using session? If you don't like working with session directly in your actions, you could emulate a surviving #foo instance variable with filters. Something like this maybe?
class FooController < ApplicationController
before_filter :load_foo
after_filter :save_foo
private
def load_foo
#foo = session[:foo] || 0
end
def save_foo
session[:foo] = #foo
end
end
Your actions will the be able to manipulate the value through the #count instance variable and this will be automatically persisted to session.
You could make use of the built in Rails.cache mechanism to store the value but as mentioned in the first answer you'd have to key it off something like the user_id. This is a nice way to go since you can back it with different storage mechanisms.
Rails.cache.write(:foo)
# in later action
Rails.cache.read(:foo)
One other thing you could look at is the flash hash, which provides a keep method to make the flash value last more than one subsequent request.
So in action 1 you could create the value:
flash[:foo] = some_value
flash.keep(:foo)
In action 2 you can access it, and call keep again if you want it to stay alive for more subsequent actions.
flash[:foo] #use it for something
flash.keep(:foo) # keep it for another request
It's a bit of a tricky thing to do cleanly within the context of http requests.
If it's a simple count or string, I think the best solution is to store it in the session. That way it will be there if you are using multiple web servers.
Why are you against using a session for this?
Don't worry, sessions won't bite.
Also, the session is probably the best way to do this.

Resources