I currently have a form (using form_tag). One of the fields is a dropdown list of options. Each option value matches the name of a method in my controller. What I want to do is when the form submit button is clicked, it runs the controller method corresponding directly to the value selected in the dropdown field.
I've built a work-around right now, but it feels too verbose:
def run_reports
case params[:report_name]
when 'method_1' then method_1
when 'method_2' then method_2
when 'method_3' then method_3
when 'method_4' then method_4
else method_1
end
# each method matches a method already defined in the controller
# (i.e. method_1 is an existing method)
I had thought that it may work to use the dropdown option value to run the corresponding method in my controller through the form_tag action (i.e. :action => params[:report_name]), but this doesn't work because the action in the form needs to be set before the params value is set. I don't want to use javascript for this functionality.
Here is my form:
<%= form_tag("../reports/run_reports", :method => "get") do %>
<%= select_tag :report_name, options_for_select([['-- Please Select --',nil],['Option 1','method_1'], ['Option 2','method_2'], ['Option 3','method_3'], ['Option 4','method_4']]) %>
<%= submit_tag "Run Report" %>
<% end %>
Any suggestions?
Can I change my controller method to look something like this - but to actually call the controller method to run? I'm guessing this won't run because the params value is returned as a string...
def run_reports
params[:report_name]
end
WARNING: this is a terrible idea
You could call the method via a snippet of code like this in the controller:
send(params[:report_name].to_sym)
The reason this is a terrible idea is that anyone accessing the page could manually construct a request to call any method at all by injecting a request to call something hazardous. You really, really do not want to do this. You're better off setting up something to dynamically call known, trusted methods in your form.
I think you should rethink the design of your application (based on the little I know about it). You have a controller responsible for running reports, which it really shouldn't be. The controllers are to manage the connection between the web server and the rest of your app.
One solution would be to write a new class called ReportGenerator that would run the report and hand the result back to the controller, which would run any of the possible reports through a single action (for instance, show). If you need variable views you can use partials corresponding to the different kinds of reports.
As for the ReportGenerator, you'll need to be a little creative. It's entirely possible the best solution will be to have an individual class to generate each report type.
Related
I've got a partial view:
<%= form_with url: admin_command_path() do |f| %>
[form stuff]
<%= f.submit :onclick(?) button_to? link_to? %>
<% end %>
This view/form doesn't need to create/update the model referenced, but should instead ONLY execute a sql() that I currently have stashed in ApplicationHelper AND in the referenced ^Command module, just to see where I can call it from. Little bit of pasta, meet wall situation :/
def command_string(id)
execute = ActiveRecord::Base.connection.exec_query("
exec [dbo].[FunctionName] #{id}")
end
I've tried just about all manner of form action, url routing, etc no no avail.
The ideal outcome is just calling the dang sql function from the form's submit button, or in a in the view itself, but either the function executes on load (rather than on click) or doesn't execute at all.
Yall don't get too hung up on the missing params, code for rough context. Just looking for an onclick -> sql exec path forward. Thx
You're thinking about this completely wrong.
Helpers are mixins/junk drawers where you can place code that you intend to resuse in your controllers and views. They are not called directly from your routes and "I want method X in my helper to be called when a button is clicked" isn't a very good way of going about it.
If you want to something to happen server side when the user clicks a link/form/button etc its done by sending a HTTP request from the client to the server. This can be a syncronous (normal) or asyncronous (AJAX) request.
You make Rails respond to HTTP requests by creating a route which matches the request and a controller action which sends a response.
# routes.rb
post 'admin_command', as: :admin_command,
to: "admin#do_the_thing"
class AdminController < ApplicationController
def do_the_thing
# This code is vulnerable to a SQL injection attack if the
# id originates from user! Use a parameterized query!
result = ActiveRecord::Base.connection.exec_query("
exec [dbo].[FunctionName] #{id}")
render text: "Okelidokeli duderino"
end
end
<%= form_with url: admin_command_path, local: true do |f| %>
<%= f.submit %>
<% end %>
You follow the same basic structure in Rails applications even when there is no model or view involved. Don't think in terms of functions - think in terms of the API your Rails application provides to the client and how you're going to respond.
Its only later if you need to resuse the code which performs the SQL query across controllers or in your view that you would place it in a helper when refactoring - it has no merit in itself.
If you then want to make this asyncronous (so that the page doesn't reload) you can do so by using Rails UJS or Turbo depending on your rails version. Or you can do from scratch by attaching an event handler to the form and sending an ajax request with the Fetch API.
But you should probally figure out the basics of the syncronous request / response cycle first.
I'm trying to make an app in Rails 4 and using Statesman for states.
In my project model, I want to display the :current_state of an object. However, I don't want the name of the attribute to appear. Instead, I want to write a human friendly state that can be rendered if the object is in the corresponding state.
For example, I have defined a state called :request_approval.
In my projects show page, I can write:
<%= #project.current_state %>
and the output is request_approval.
How can I write something that says if project is in current_state :request_approval, render: Awaiting a response to your request for approval? Can I make some kind of method in my model to do that?
In your projects_helper you can write a method:
def text_for_state(state)
case state
when 'request_approval'
'Awaiting Response'
when 'something_else'
'Then Something Else'
end
end
And in your view just write:
<%= text_for_state(#project.current_state) %>
This is a option there may be better solutions to do this.
I have a method in one of my models that, when called, fetches a tweet using the twitter gem and stores some parts of it. I'd like to be able to trigger that action from the web interface to my app. What is the Rails Way to accomplish this? I've seen some references to not calling model methods from views, so should I be doing this from within a controller somehow instead?
My method (the relevant models are Sponsor and Sponsortweet (so my model name wouldn't conflict with Tweet, from the gem):
def create_tweet
tweet = Twitter.user_timeline(self.twitter).first
self.sponsortweets.create!(content: tweet.text,
tweet_id: tweet.id,
tweet_created_at: tweet.created_at,
profile_image_url: tweet.user.profile_image_url,
from_user: tweet.from_user,)
end
EDIT:
I created a tweet method in my sponsors controller:
def tweet
#sponsor = Sponsor.find(params[:id])
#sponsor.create_tweet
end
and added the following to my config/routes.rb: match 'tweet', to: 'sponsors#tweet', via: :post.
As well as the following code in my view (I'm using haml):
= button_to :tweet, tweet_path(#sponsor)
However, clicking the button results in the following error:
Couldn't find Sponsor without an ID
Your view should have a button that posts to a specific route in your controller. That controller would then call the method in your model. Having no idea what your app actually looks like, here's an example:
EDIT includes better example
View (assuming it's a Sponsor view):
<%= button_to :submit, tweet_path %>
Controller:
def tweet
Sponsor.create_tweet
end
And your model would stay the same, except you'd change your method to a class method like so:
def self.create_tweet
...your code here...
end
Since it seems this isn't tied to any particular sponsor, you'll use a class method and thus don't need an instance of the class to call your method. That said, it seems like you would want an instance of your class at some point...
I'd be curious to hear other people's answers, as I'm now wondering if there is such a way to bypass the controller all-together.
However, my take on this is that, since Rails is an MVC (Model View Controller) framework, I think the Rails way of accomplishing what you're considering is probably to simply handle the action normally; through the controller to the model.
If I am correct in assuming you have a button or link, or perhaps some AJAX, which is initiating the server-side Twitter processing, then I would set up your routing for that URL to point to a controller action method, which would then call your model method myModel.create_tweet.
Here's my code for a form that contains a drop down list -
<div class="field">
<%= f.label :type, "Select profile type"%>
<%=
f.select :type, Profile::TYPES,
:prompt => "Select a profile type"
%>
</div>
The drop down menu looks fine. But, how would I check which option is selected? I want to route to a different view based on this selection.
Thanks in advance!
The logic of routing to a different view should occur in your controller. When the user submits this form, check the value of the params, and perform your logic to route to a view:
class ExampleController
def routing
case params[:example][:type]
when 'foo'
redirect_to foo_path
when 'bar'
redirect_to bar_path
end
end
You can create a custom action name, since this routing isn't one of the CRUD operations. You will need to place this route into the config/routes.rb file if it is a custom name.
Optionally, you can bind to the select's onChange event, as mentioned by others to auto-submit the form when the user changes the value. This would still send the data to the controller and perform a redirect. The advantage to this approach is that you can keep your route information out of Javascript, and in the Rail's controller.
More on Rails routing can be found here: http://guides.rubyonrails.org/routing.html
More on Javascript binding to onChange can be found here: http://www.w3schools.com/jsref/event_onchange.asp
You should be able to use jQuery or any other popular Javascript framework to achieve this -- either attach an onChange listener or set the value somewhere and check it. Events, yay, etc.
I am new to Rails and don't quite understand what I'm supposed to do. Let's say, for example, I want a textbox containing a string to be passed into another controller (another page?) when the user clicks a button. How would I go about doing that?
Functions of controllers are pages, correct? Can a function take parameters just like a normal method? (E.g. sum(x,y))
For complete information, check out Rails Form helpers. Basically, you give the form_tag method a path which points to the controller and the action that you want to handle the form submission. For example,
<%= form_tag(search_path, :method => "get") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
Here, the action and controller that search_path points to (defined in your routes) will receive the form submission and the value from the text field.
Your action in the controller IS a function, but it will not receive the value from the form submission as a parameter to the function. Instead, you will access it through the params hash. In the example above, you can access the value from the text field as
params[:q]
What are you doing with the string? Storing it? Using it as a parameter on another page?
I suggest you take a look at the Getting Started Guide, go through it, and pay particular attention to the What is Rails? section, where it explains MVC architecture and REST (Representational State Transfer.)
There are dozens of other Rails tutuorials out there, I'm sure if you searched this site you'd find many questions like this one:
https://stackoverflow.com/questions/2794297/how-to-learn-ruby-on-rails-as-a-complete-programming-beginner
Functions of controllers are pages, correct? Can a function take parameters just like a normal method?
Functions of controllers are pages if that's the route you've set up in your routes.rb configuration file. I suggest you run through some tutorials to understand what Rails is for and how it works.