Get Controller Object and Controller Strong Params in Application Controller - ruby-on-rails

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.

Related

how to create an action controller helper class and how to access that in controller action?

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

An instance variable defined in New method not accessible in Create method

I have tried and tried to find the answer elsewhere, but I just cannot resolve this problem.
I have a model call BidSignal that is related to a model called BuySignal. A BuySignal has many BidSignals and a BidSignal belongs to a BuySignal.
When I create a new BidSignal using the new method in the BidSignal controller I want to capture the BuySignal that it relates to, so I can save the relation in the BidSignal create method. I'm currently doing this by passing the buysignal_id to BidSignal controller on a call to the new method. But when I come to create the BidSignal record and try to recall the buysignal_id to find the BuySignal record, I get an error - Couldn't find Buysignal without an ID
Bid Signal New Method
def new
#buy_signal_idx = params[:buysignal_id]
#bid_signal = BidSignal.new
end
Bid Signal Create Method
def create
#a_buysignal = Buysignal.all
#a_buysignal = #a_buysignal.find(#buy_signal_idx)
#a_bid_signal = #a_buysignal.bid_signals.build(bid_signal_params)
if #a_bid_signal.save
flash[:success] = "Bid Signal Successfully Created!"
redirect_to #a_bid_signal
else
render action: 'new'
end
end
However, if I replace:
#a_buysignal = #a_buysignal.find(#buy_signal_idx)
with:
#a_buysignal = #a_buysignal.find(300)
It all works fine ( except of course all my bid signals are being related to buy signal with id 300 ).
It's as if the #buy_signal_idx is not accessible within the create method. I have tried abstracting this out so that new calls a class to set a variable for the bid signal through it's own new method and then use a getter to return the buy_signal_idx in the bid signal create method. But i get the same result.
I think I get that if I initiate an instance variable in a method that variable should be accessible by other methods in that object instance - but it's not working.
I know i'm being stupid and missing something fundamental - but what is it ?
Every time new request comes in, rails instantiate new instance of the controller class - hence the instance variables set in previous request are no longer available (controller object holding this variable is destroyed by now). This is a result of web being stateless - the only ways to preserve any form of data between two separate requests is to store it within the session or to send it with every request (not really storing the variable, but you know what I mean)
The best choice depends really on your routes here. If you are using nested resources, than params[:buysignal_id] is passed as a part of url (not the query string) and hence it is also accessible within your create action. So you can simply do:
# create action
#a_buysignal = Buysignal.find(params[:buysignal_id])
If it is part of a query string, then I would do:
# new action
#buy_signal_idx = session[:byusignal_id] = params[:buysignal_id]
# create action
#a_buysignal = Buysignal.find(session[:buysignal_id])
Another solution is to add a hidden field with a params[:buysignal_id] value to your form and use it in your create action, but i think it is a little bit cumbersome.

Can't access one model's instance variables from another model) (self.ruby)

I have two models, Draft and Pick. Draft creates an array of available Players in an instance variable named 'available_players'. This is done using the 'before_save' callback. The callback runs the instance method 'start' which in turn runs 'set_active_players'. I've tested all of this in my Draft_spec and I have no problems loading players and having them returned in the available_players array. All my draft specs pass.
The problem is that when I try to access the 'available_players' instance variable from Pick.rb, it returns nil. If I call 'draft.start' (the instance method that should run before Draft.rb saves), I can suddenly access the 'available_players' array... it's like the Draft object is not creating the available_players array even though I have the before_save method in place.
Here is the code that fails inside of Pick.rb:
def available_players_returns_nil
#draft_object.available_players
end
Here is the code that works inside of Pick.rb:
def available_players_working
#draft_object.start
#draft_object.available_players
end
I don't want to have to call start every time I call the method because available_players should not need to reload ALL Players. Please help me access available_players!
Links: failing Pick specs, Pick.rb
EDIT:
I should add that #draft_object is found using
#draft_object = Draft.find(self.draft_id)
For a start, this is wrong:
#draft_object = Draft.find(self.draft_id)
You have an association set up, so use it. You can simply use draft within your Pick object to access the Draft it belongs to. No need to assign it to an instance variable called #draft_object.
Same story with player.
Incidentally, your set_available_players method in Draft is just looping through all of the players and adding them to an instance variable. Why are you doing this? Why don't you simply grab the players directly if you need them in Pick? Like this:
#players = Player.all
Also ... I'm somewhat concerned that pretty much all of your tests are commented out. I hope that's not by design?

I'd like #report_for to stick around for an entire session

I created a method called "report4" in my application_controller which is used to set my "#report_for" variable
in another controller, "report4" is called with a before_filter for a view
In the view it displays as part of the heading
I thought that it would still be present when data is sent back to the controller for an update, but "#report_for" is coming up nil.
Can, and should, a variable like "#report_for" be set to exist for the entire session like "current_user"?
If not, is the best way to send it back in a hidden field?
Thanks.
you can just use the session hash (ActionDispatch::Session::AbstractStore::SessionHash):
session[:report_for] = 'whatever'
if session[:report_for].present?
#do your thing
else
#carry on
end

Passing messages from the application layer to the View

Let's say I have an AddProductToCartTask with a method Execute().
Now, the task is being called from a Controller. In the Execute method , there
is a check that if not met - the action is not performed.
Let's say the message returned would be: "You do not have enough bonus to buy
this product".
I thought about launching an event when the domain validation fails - but that would mean that in the Controller I have to have all kinds of class variables that need checking in the action (to determine if I need to set an error message, or i need to redirect .. etc)
Instead of this I could have a method on the task : GetErrorMessages(). If empty
return the JSON object if not empty return the Message. Or the method could return an enum that would tell if i need to redirect or set a message or return the object ....
I'm not sure which road to take. Any input would be appreciated. How do you bubble up messages from your domain layer ?
Edit: this is mainly in an AJAX context. Even though I'm not sure it matters as it's an action that it's getting called from somewhere .
I might be misunderstanding your request, but it seems to me like you want a central messages functionality, rather than something specific to the task object. If you leave it in your task, then the task must be kept in scope and "alive" until the AJAX request.
I do something similar, though directly from the Controller.
I have a static class called Messages. It has AddMessage(), GetLastMessage(), and GetAllMessages() methods. Each one, when first called, will check the user's session variable and, if nothing is found, creates and saves a Queue<string>() object. The methods are basically just an interface to the Queue. The Queue is nice because it handles push/pop which automatically removed "viewed" messages.
My controller does:
Messages.AddMessage("Product Saved");
You could potentially do:
Messages.AddMessage(task...GetErrorMessages());
Then, from my View, I have an html helper that checks how many error messages there are and, if any, creates a <ul> with each message as a <li>.
You could just as easily have a GetMessages() controller that returns any messages as a JSON object.
James

Resources