Pass a PORO from view to controller - ruby-on-rails

What would be the best way to pass a Plain Old Ruby Object that I have in a view to a controller method?
It is not an object that is persisted in the DB. I would rather not refactor things and just want some ideas on best way to pass this object.
view
link_to "activate", activate_apis_path(my_ip_instance: #my_ip), class: "btn btn-primary btn-xs"
controller
#my_ip = params[:my_ip_instance]
#my_ip is just a string... want the whole object
(Rails 4.2)

Usually the best way is through a form. Consider creating a form with hidden fields for all of your #my_ip attributes.
<%= form_tag activate_apis_path do %>
<%= hidden_field_tag "my_ip_instance[foo]", #my_ip.foo %>
<%= hidden_field_tag "my_ip_instance[tomato]", #my_ip.tomato %>
<%= submit_tag "Activate", class: "btn btn-primary btn-xs" %>
<% end %>
(Extra credit: you could also loop over #my_ip's attributes to generate the hidden fields.)
Another way is to serialize #my_ip as JSON and then deserialize it in the controller. I think that is much messier though.
link_to "activate", activate_apis_path(my_ip_instance: #my_ip.to_json)
To make this work for a more complex object, you would need to write your own serializer/deserializer logic into the class as described in this post.
require "json"
class A
def initialize(string, number)
#string = string
#number = number
end
def to_json(*a)
{
"json_class" => self.class.name,
"data" => {"string" => #string, "number" => #number }
}.to_json(*a)
end
def self.json_create(o)
new(o["data"]["string"], o["data"]["number"])
end
end

What you basically need to do is to serialize the object. Rails provides built in options to serialize objects and Object.to_query which can be used to convert to query parameters.
But, passing around a bunch of state like that violates rest and is indicative of a poor application design. If it's a non-persisted object it should be passed as form parameters in a POST/PATCH request. In a GET request you should strive to initialize all objects needed from the headers, params from the request url and session.

Related

how to pass a static string into a link_to as an argument in ruby on rails view

I have a modified string. Here is the code where I do the changes:
<% device=#devices.find(1) %>
<% #string ="" %>
<% device.attributes.keys.each do |attribute| %>
<% next if attribute == 'id' || attribute== 'token' || attribute =='carrier' || attribute =='segment' || attribute =='created_at' || attribute =='updated_at' %>
<% x=attribute.to_s %>
<%#string = #string + x +":device."+x +"," %>
<% end %>
<% #string %>
<% #arguments= #string.gsub(/\,$/, '') %>
<%= #arguments %>
It works and it is in the right format to put it in the link_to helper.
This is how I first wrote the link_to helper, and it worked.
<td><%= link_to 'Send notification', controller: "home", action: "send_notification", token: device.token, first_name: device.first_name, last_name: device.last_name %></td>
I tried to change it like this:
<td><%= link_to 'Send notification', controller: "home", action: "send_notification", token: device.token, #arguments %></td>
or #{arguments}
But it doesn't work. I even created another variable without # but it didn't work either.
How can I paste my arguments?
This is my arguments string btw:
"first_name:device.first_name,last_name:device.last_name,nickname:device.nickname"
What should I change?
Another simple newbie question; I feel like I am doing most of the coding in the wrong place. Is it right thatI write so many things in view?
What is the best approach in Ruby on Rails programming?
Thanks in advance
I'll answer your code question first and leave the string question for last.
First, some of your code is in the wrong places. Rails expects you to retrieve the database record in the controller and then pass it into the view. Something like:
devices_controller.rb
class DevicesController < InheritedResources::Base
def send_notification
#device = Device.find(id)
end
....
Then in your show view (app/views/devices/send_notification.html.erb) you can use the #device object and access its attributes like #device.first_name and #device.last_name and print them out or whatever.
Second, the link_to method needs a Hash of arguments, not a string. But either way, there is no use case in Rails that I can think of for passing the entire set of object attributes into the link_to method. It's just generating a link. You probably don't really want it to be littering your html elements with every one one of your record attributes.
All you need to do if you want access to that data when the user clicks the link is to pass the id in as a url element and then have the controller at the other end of the link (in your case Home?) catch the id and create an object out of it there.
I'd suggest taking a good look at: http://guides.rubyonrails.org/

working with custom hash array in Rails

I already posted a similar question but i got told to try and solve it myself and post here what i have so far.
What i'm doing is calling some xmlrpc methods to extract some data from an external app.
what i can do so far is display contents from a simple array as per below.
<% #attachment.each do |att| %>
<div class="item">
<%= image_tag att %>
</div>
What i want is to be able to pass a hash array similar to this:
{id:"1", content:"somedataaa", imgurl:"someurl.com/image.jpg"}
what i want is when the user clicks on an image to pass the id to a controller method so i can then go and get more data with the id provided and provide another view.
<% #hasharray.each do |att| %>
<div class="item">
<%= att.id %> //this could be hidden an only used to pass the id to a method
<%= image_tag att.url %> //add a link_to here somehow to route to a method on the controller.
</div>
I know how to do this with rails model data, but since this data is coming from an external xmlrpc i have to pass it as a hash array.
Could you please guide me to how i need to do this, is this the right way, or can i parse the hash array and somehow save it to a Rails model so i can then use the view as normal and have access to the routes.
attachments = [{ :id => 1, :url => 'images/1.jpg' }, { :id => 2, :url => 'images/2.jpg' }]
attachments.each do |attachment|
link_to image_tag(attachment[:url]), "/path/to/controller/#{attachment[:id]}/show"
end
Side note: Please follow proper naming conventions. Don't name variables "hasharray" or "hash" or "array" or anything similar. Make them meaningful. Please.
Reading this will help: http://www.ruby-doc.org/core-1.9.3/Hash.html
You can just pass the hash instead of a model, and us att[:id] instead of att.id.
If you really need it to be a model instead of a hash, you can look at Hashie, which provides several methods to convert a hash to a model:
https://github.com/intridea/hashie

Pass Parameters in Link_to

I would like to pass a parameter through a link_to method in Rails. I know there is a way to do it via the URL but I really don't want to do that. Is there any way to pass a param via a link without adding it to the link itself?
I know in PHP you can post and then retrieve that value by using the post variable. Is there something similar in Rails?
link_to signature looks as follows:
link_to(body, url_options = {}, html_options = {})
So, POST would look like (lets say you want to POST user's data):
link_to "Link text", some_path(:foo => "bar", :baz => "quux"), user: #user, :method => :post
User's data can be retrieved in the controller using params[:user]
Here's how you can pass a parameter around via the link_to method in order to, say, create a new object with the passed parameter. This strategy would allow you to pass variables among actions in your controller and create objects with predefined attributes:
Say in your show view, you have a variable called #foo that you want to pass to your new controller action. In which case, in your show view, you can have
<%= link_to "Link Text", new_widget_path(:foo => #foo) %>
which would store #foo in params[:foo], allowing you to use params[:foo] in your controller. Which controller action you get directed to depends upon *new_widget_path*. In this case, you get directed to the new action in WidgetController.
Clicking on Link Text will direct Rails to the new action of your WidgetController. You can have
def new
#widget = Widget.new(:foo => params[:foo])
end
Then, in your new.html.erb view file, you can allow the user to create a new Widget object with this pre-defined foo attribute already filled out via a hidden form field:
<%= form_for(#widget) do |f| %>
<%= f.label :other_attribute %><br />
<%= f.text_field :other_attribute %>
<%= f.hidden_field :foo %>
<%= f.submit %>
<% end %>
Allowing the user to create a new widget with the foo attribute already filled out!
Passing information through a web request can be done either by the URL: http://example.com/foo?bar=blah in a GET request which is what link_to does, or through a POST operation which usually requires a form. The form could have hidden elements if you just want a submit button:
<form method="POST" action="http://example.com/foo">
<input type="hidden" name="bar" value="blah">
<input type="submit">
</form>
There are various rails helpers to help build the form if needed.
Lastly, if you really want a link, you could either CSS style that button, or you could use javascript to observe a link and then POST the info. (the method Simon Bagreev posted does this with javascript)
What sort of parameter? If it's a key for a GET request, convention would dictate using the url (e.g. params[:id] or a an active record path variable). If you want to POST something, you should be using a form. Otherwise, you could write a helper method to set a session variable or something, but think about your architecture and what you're semantically trying to do, and I'm sure someone here can help you out.

Rails: textfield list to array of strings

I want to take input from a textfield and turn it into an array of strings. After having submitted the post request, I want to display again the textfield, but including the values of the textfield in an array.
I have a view that would look like:
<% form_tag "/list2array" do -%>
<%= text_area_tag "mylist" %>
<div><%= submit_tag 'save' %></div>
<% end -%>
<% #myArray.each do |item| %>
<%= item %>
<% end %>
And as a start for the controller:
class List2ArrayController < ApplicationController
def index
end
def save
#myArray = params[:mylist].split("\r\n")
end
end
However, after the post, I only get an empty textfield without values in the array from the previous POST.
Do I need to use the model layer for my experiment? How? Or do I need to modify my controller?
Sort answer: Yes. You need to use some form of data store, either models or you can store it in the session. The are no continuations of state.
If you have a model you can add an attribute called mylist and mylist_array (you can use serialize for the array). Then either with a setter, or a before_validations callback set the value of mylist_array as you do in your example.
On a slightly contradictory note: adding the following to the end of your save method, will make your experiment sort of work, but you will need to fix your form post url first or add in a route for it manually.
render :index

How can you pass an object from the form_for helper to a method?

So let's say I have a form which is being sent somewhere strange (and by strange we mean, NOT the default route:
<% form_for #form_object, :url => {:controller => 'application',
:action => 'form_action_thing'} do |f| %>
<%= f.text_field :email %>
<%= submit_tag 'Login' %>
<% end %>
Now let's say that we have the method that accepts it.
def form_action_thing
User.find(????? :email ?????)
end
My questions are thus:
How can I make the object #form_object available to the receiving method (in this case, form_action_tag)?
I've tried params[:form_object], and I've scoured this site and the API, which I have to post below because SO doesn't believe I'm not a spammer (I'm a new member), as well as Googled as many permutations of this idea as I could think of. Nothing. Sorry if I missed something, i'm really trying.
How do I address the object, once I've made it accessible to the method? Not params[:form_object], I'm guessing.
EDIT
Thanks so much for the responses, guys! I really appreciate it. I learned my lesson, which is that you shouldn't deep-copy an object from a form, and that the parameters of a form are actually included when you submit it.
I will admit it's sort of disheartening to not know stuff that seems so obvious though...
you need to pass the "id" of your "#form_object" in the url and then lookup that object (assuming you have a model and using ActiveRecord)
It depends on how do you set up your routes. If you're using the default /:controller/:action/:id route, you can pass it as a parameter in the URL. Note that not the whole #form_object can/should be passed, but it's id or some other attribute to identify it instead. In this case, you should make your URL:
<% form_for #form_object, :url => {:controller => 'application',
:action => 'form_action_thing', :email => some_email} do |f| %>
<%= f.text_field :email %>
<%= submit_tag 'Login' %>
<% end %>
And in your controller
def form_action_thing
#user = User.find_by_email(params[:email])
end
You can pass parameters through the url, but when submitting a form the only thing that should (probably) be passed through the url is the record id for a RESTful record.
And it appears you didn't find out yet where your form data can be found in the params.
So
All the data from your form should end up in params[:form_object]. The actual value for :form_object is selected by Rails, it's probably coming from the object's class (too lazy to look that up right now)
In any case, you can easily find out where your form values are submitted by looking at your console/log output. All the params for each requests are dumped there.
The form fields will be inside the params like params[:form_object][:email] - each field that is submitted has an entry corresponding to the field name.
The params hash not contain all the original values from your #form_object. There will be only those values that you included in the form.
If you need to pass non-editable values to the controller with your form, use hidden_field(s) These will be submitted with the form, but are not visible to the user.

Resources