What's the absolute path to current request object in Rails environment? - ruby-on-rails

I'm searching for the current request of class Rack::Request to find the params. Suppose I've spawned a debugger in my model, I don't want to send a new request, but still find my params.
I couldn't find any class attributes, that would store current request, which is reasonable.
I don't know how to find any instances of ApplicationController or Rack::Server, which might contain the info.
Also, peaking into the log is considered too much effort, so I'd like the effort to be concentrated on finding the request object, not telling me to grep/search through log.
In hopes of being able to be lazy,
Love Dzhon.

It's possible I'm misunderstanding your question, but from within a controller you can simply access a request object to get its details, and params to get the params.
ItemController
def show
#page_variable = request.inspect + params.inspect
end
end
If you want to make the request object available to your models you can create a class accessor and store it at the beginning of any action (via a before_filter in the application controller) for example. More details why here.

Related

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.

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

Rails - Show changes after PATCH

I would like to show the users which fields have been modified following his PUT/PATCH request
For example, I have a big "project" form, with several fields, but my user decided to only update the deadline and the project name. After he clicks the "save" button, I would like to show some message saying "You have successfully updated : name, deadline"
If it's possible, I would like some generic code that would detect the update action and infer the variable name. By generic I mean, I want to implement this in my ApplicationController, so I don't have to add code in every controller#update action
Let's look at this sample code from controllers/entreprise_controller.rb
def update
if #entreprise.update_attributes(entreprise_params)
redirect_to #entreprise, notice: "Entreprise éditée"
else
render 'edit'
end
end
Here's an idea of steps to reach my goal. Could you help me with each of these ? Or suggest a better approach ?
Detect that we are doing a CRUD update action, for example from the action name in the code, that should always be update (how can I read, from the code, the name of the action being executed ?)
Guess the variable name : here #entreprise, it can be inferred from the file for example (or maybe calling self.class and doing some regex ?)
Save the list of variables that are going to be updated (maybe some tricks involving before_action and after_action and dirty_tracking ? See my edit.)
Provide this list as a GET argument for the redirect to #entreprise (should be pretty straightforward)
Show this list to the user (this part is OK for me)
EDIT concerning Dirty Tracking
Mongoid already implements this. However the main problem is getting the intermediate variable before it is saved. Each controller instanciates the variable like #entreprise during a before_action callback. And if I add a before_action in my ApplicationController, it will fire before, so no variable is available yet. And as regards a possible after_action in ApplicationController, the doc says "Any persistence operation clears the changes." so it's dead already. I probably cannot get away without rewriting every controller ?
Dirty Tracking & controller in a nutshell :
prepend_before_action
before_action of ApplicationController
before_action of EntrepriseController (which includes set_entreprise, where the variable #entreprise is defined so as to proceed with update)
If we can get a callback to HERE, it would let us inspect the object for dirty tracking information, as the object exist in memory, we can use #entreprise.attributes=entreprise_params and look at the dirty info (where entreprise_params is the strong parameters for #entreprise)
action : on success, it will store the info in the DB, and we lose dirty tracking info
after_action

How to set variables across models for a single request in rails?

The scenario: I need to give models access to API tokens stored in the session.
Background: I have an API-driven rails 3 application utilizing DataMapper(DM) and a DM adapter to interface with the API. Each DM model has a corresponding REST-ish API endpoint much like you get with rails scaffolding. The API requires various headers for requests, including API tokens, keys, ids etc. The headers have nothing to do with the requested data, they exist for authorization and tracking purposes only. A number of these tokens are stored in the session. I want a clean way to make these API headers available to any model during a request.
Possible solutions:
1. Passing session variables from the controller to the models
The obvious answer is passing the tokens in a hash or other object from the controller to the models. A controller action might have the following: #user = User.find(params[:id], api_headers).
The problem is needing to override any model method to accept the additional api_headers object. Not counting methods defined by Rails and DataMapper, there are hundreds of methods already defined in the application models that would need to be rewritten. So I'm ruling out a rewrite, and this also doesn't seem like a good solution since it would require overriding a ridiculous number of DM methods like the User#find example above.
2. Some metaprogramming hack
I could catch any ArgumentError's on DM's base class and check if the last argument is the api_headers object, then set the values as instance variables and invoke the requested method. This thought exercise already has me cringing at dealing with optional arguments etc. If given long enough I could probably create a functional Frankenstein that should get me fired but probably wouldn't.
3. Use a singleton (current preferred solution)
In the application controller set a before_filter to dump the session-stored API headers into a singleton ApiHeaders object. Then any model making an API request can get that singleton with the required API headers.
An additional after_filter* on the application controller would set all attributes to nil on the ApiHeaders singleton at the end of the request to prevent leaking headers between requests.
This is currently my preferred solution but I don't like that the API header values could potentially carry over into other requests if the after_filter doesn't get invoked. I don't know in which scenarios this might happen (in an application error perhaps?) which raises concerns. All I know is the values don't necessarily die with the request.
4. Custom code
Drop support of DataMapper and the custom API adapter and manually make all API calls, passing through all required API headers. Besides the fact I don't have time for this level of rewrite, why use a framework at all if you have to throw a huge chunk out to support a custom auth scheme?
Summary
What's the cleanest way to get these pesky API tokens from the session into the bowels of the application where they can be sent with each API request? I'm hoping for a better solution than those listed above.
* An alias for after_action
I set the current user and the request information on my User model using the request_store gem which is just a tiny shim over thread local storage with a bit of clean-up.
This makes the information available from any of my models via the User class. I have User.current, User.request and User.location available wherever I need it.
Your controller just has to set User.current and User.request once it has authenticated the user.
Example User model:
# models/user.rb
require 'request_store'
class User
def self.current
RequestStore.store[:current_user]
end
def self.current=(user)
RequestStore.store[:current_user] = user
end
def self.request
RequestStore.store[:current_request]
end
def self.request=(request)
# stash the request so things like IP address and GEO-IP based location is available to other models
RequestStore.store[:current_request] = request
end
def self.location
# resolve the location just once per request
RequestStore.store[:current_location] ||= self.request.try(:location)
end
end
Use Thread.current, which is passed in from request to model (note, this breaks if, inside your request, you use sub-threads). You can store the attribute you want to share in a cattr_accessor or in rails cache:
in a cattr_accessor
class YourClass
cattr_accessor :my_var_hash
...
# and in your controller
# set the var
YourClass.my_var_hash = {} if YourClass.my_var_hash.nil?
YourClass.my_var_hash[Thread.current.object_id] = {}
YourClass.my_var_hash[Thread.current.object_id][your_var] = 100
... and in your model
lvalue = YourClass.my_var_hash[Thread.current.object_id][your_var]
Note, if you use this method, you will also want to make one of the hash values a timestamp, and do some housekeeping on getting, by deleting old keys, b/c you'll eventually use up all your system memory if you don't do the housekeeping
with cache:
# in your controller
#var = Rails.cache.fetch("#{Thread.current.object_id}_var_name") do
return 100 # do your work here to create the var value and return it
end
# in your model
lvalue = Rails.cache.fetch(("#{Thread.current.object_id}_var_name")
You can then set the cache expiration to 5 minutes, or you can wildcard clear your cache at the end of your request.

Encrypting Parameters across controllers

I need to pass a parameter from one method in a controller to another.
From my understanding I have to pass the parameters as a GET exposing it in the url string.
What is the best way to encrypt the data so no one can see what is actually getting passed in the string? Also, is there a way to pass it via POST or is my original understanding correct?
I haven't used RoR, but in the web world, this problem is solved with sessions. Using sessions you can store the parameters on the server and avoid sending sensitive data with GET or POST (both are insecure).
The Ruby on Rails Security Guide looks like a great read related to this.
I suggest you abstract your code into lib/ so that you don't have to call additional methods. Instead of making a new HTTP request, just put the code in a central place and call it from there.
class MyController < ApplicationController
def index
MyLibrary::Thing.do_stuff
end
def show
MyLibrary::Thing.do_stuff
end
end
# lib/my_library/thing.rb
module MyLibrary
module Thing
def self.do_stuff
# do stuff!
end
end
end
That way you can access the same code in multiple actions, without doing extra HTTP requests.

Resources