Rails - Partial View form to execute SQL stored procedure - ruby-on-rails

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.

Related

rails form post action without changing path

I've got a view that renders a contact form. This contact form is rendered through javascript. There is also a javascript filter that the user can set viewing options in. (Depending on the settings, different markers are shown on a google map. The user can then click on the markers and in the viewbox click on a view button that renders some info and the contact form below the map)
If I were to make a normal form and use the post method with a #contact and contact routes, I would have to rerender the entire page after the #contact#create method was called. Which would mean all of the current users filter options would be unset. I could obviously save the settings, but feel like this is a hassle.
What I would like is for the contact form to call a method upon submit without actually changing paths, but I have no idea if this is even possible. (i'm using simple form so an answer for that would be preferable)
Since your question is quite broad, I'll have to answer as such:
if this is even possible
Yes it's possible.
You'll have to use ajax to send an asynchronous request to your server.
Ajax (Asynchronous Javascript And Xml) sends requests out of scope of typical HTTP; you can send/receive "hidden" data without reloading (this is what you want):
Don't worry - ajax is really simple once you understand it.
There's a great Railscast about it here:
Implementation
For you, you will just have to get your form to submit over ajax (javascript). There are two ways to do this:
Standard JS (JQuery)
Rails UJS (unobtrusive Javascript)
Basically, javascript acts as a mini browser, opening a url on your server, handling the returned data & doing what you tell it on the path:
<% form_tag contact_path, remote: true %>
<%= text_field_tag "name %>
<%= email_field_tag "email" %>
<%= submit_tag %>
<% end %>
You'll then be able to back this up with the corresponding controller action on your server:
#app/controllers/contact_forms_controller.rb
class ContactFormsController < ApplicationController
def create
... #-> your own logic here
respond_to do |format|
format.js #-> when receiving a pure xml request, this will fire
format.html
end
end
end
#app/views/contact_forms/create.js.erb
$("body").append("<%=j #variable %>");
Without going into too much detail, this is the most efficient way to achieve what you want. I can elaborate if required.

Why a ruby command runs even if a user don't activate the script yet?

I'm a Ruby user, trying to make a web service that receives user's active request. I made a button, of which class is a "btn-send-alert". Then after the html code, I put a script function.
<div class="page-title">
<button class="btn-send-alert" style="background-color: transparent;">Help Request</button>
<p>Hello</p><br>
</div>
........
<script>
$(".btn-send-alert").click(function(){
alert('hello!');
<% Smalier.class_alert(#lesson,current_user).deliver_now %>
});
</script>
The problem is, the ruby code just start on its own even before I click this button.
And if I click this button, no email is delivered any longer.
Maybe in some point, I think I'm seriously wrong but I can't find where it is. Is there way that I can make this function work correctly?
Looking forward to seeing the response!
Best
Thanks to Rich, I am now able to write a code that works fine! The below code is that code.
<%= content_tag :div, class: "page-title" do %>
<%= button_to "Help Request", support_path, method: :get, remote: true, class:"btn btn-danger", params: { lesson_id: #lesson.id, user_id: current_user.id} %>
<%= content_tag :i, "wow!" %>
////
def support
#lesson = Lesson.find_by(:id => params[:lesson_id])
current_user = User.find_by(:id => params[:user_id])
mailer.class_alert(#lesson,current_user).deliver_now
end
Above code runs well!
I'm a Ruby user
Welcome to Rails!!
Stateless
Firstly, you need to understand that Rails applications - by virtue of running through HTTP - are stateless, meaning that "state" such as User or Account have to be re-established with each new action.
In short, this means that invoking actions/commands on your system have to be done through ajax or another form of server-connectivity.
Many native developers (native apps are stateful) don't understand how Rails / web apps are able to retain "state", and thus make a bunch of mistakes with their code.
receives user's active request
Even if you understand how to set up authentication inside a Rails app, it's important to understand the virtues of it being stateless... EG the above line means you have to have a user signed in and authenticated before you can send the request.
This forms one part of your problem (I'll explain in a second)
ERB
Secondly, the other problem you have is with the ERB nature of Rails.
the ruby code just start on its own even before I click this button.
This happens because you're including pure Ruby code in your front-end scripts. This means that whenever these scripts are loaded (triggered), they will fire.
The bottom line here is you need to put this script on your server. Otherwise it will just run...
Fixes
1. ERB
<%= content_tag :div, class: "page-title" do %>
<%= button_tag "Help Request", class:"btn-send-alert" %>
<%= content_tag :p, "Hello %>
<% end %>
You'll thank me in 1+ months.
Convention over Configuration means you use as many of the Rails helpers as you can. You don't need to go stupid with it, but the more "conventional" your code is, the better it will be for future developers to improve it.
Another tip - only use HTML for formatting; CSS for styling. Don't use <br> unless you actually want to break a line.
Another tip - never use inline styling - Rails has an adequate asset pipeline into which you should put all your CSS
--
2. Ajax
Secondly, your use of Javascript is incorrect.
More specifically, you're calling a server-based function inside front-end views. To explain this a little more, I'll show you the famed MVC image I post on here a lot:
This is how Rails works (MVC - Model View Controller) - this means that whenever you deal with your application, you have to accommodate a layer of abstraction between the user & your app -- the browser.
By its nature, the browser is not stateful - it stores session cookies which you have to authenticate on the server. You cannot call "server" code in the front-end HTML/JS.
This is why your Ruby code is firing without any interaction, although I'm not sure how it's able to fire in the asset pipeline.
If you want to make it work properly, you'll need to create a controller action to invoke the mailer send function, which you'll be able to do using the following setup:
#config/routes.rb
get :support, to: "application#support", as: :support -> url.com/support
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
respond_to :js, only: :support
def support
Smalier.class_alert(#lesson.current_user).deliver_now
end
end
#app/views/controller/view.html.erb
<%= content_tag :div, class: "page-title" do %>
<%= button_to "Help Request", support_path, method: :get, class:"btn-send-alert" %>
<%= content_tag :p, "Hello" %>
<% end %>
#app/views/application/support.js.erb
alert ("Hello");
Each and every ruby code snippet embedded in ERB runs on server, in order to assemble a valid HTML or Javascript script for browsers to render.
Browsers don't understand ruby script at all, all they can understand is HTML and Javascript.
In your case (I'm supposing you're using rails since you tagged your question with ruby-on-rails), emails are delivered when rails engine is assembling HTML's.
If you want the emails being sent after the users click that button, the correct way is:
Define an action method in some controller, give it an URL (i.e. add a route in config/routes.rb), send email in that action.
When the button on the page is clicked, send an AJAX request to that URL.

undefined method `pushes_path' for #<#<Class:0x007f85a15c6c90>

i'v been trying to resolve this error for the past 5 hours and I'm gonna burn my computer if I can't solve this.
undefined method `pushes_path' for #<#:0x007f859d605250> this is the error code I'm getting but i don't understand why.
this is my index.html.erb file inside of the interaction
<%= simple_form_for #push do |f| %>
<%= f.input :payload, as: :text %>
<%= f.input :segment, as: :radio_buttons %>
<%= submit_tag "start the campaign" %>
<% end %>
and this is my interaction controller
class InteractionController < ApplicationController
def index
#push =Push.new
end
end
Push is my table in the database and i'll get the inputs and write them in the database to use them for later one.
and this is my routes file
devise_for :partners
get 'home/index'
get 'segmentation/index'
get 'interaction/index'
root to: "home#index"
i really don't know why its looking for pushes_path, what am i doing wrong?
form_for
The problem you have is that your form_for method is going to try and generate a route based off your #path object. And as such, if you don't have a path created for it, you'll receive the error you're getting:
:url- The URL the form is to be submitted to. This may be represented
in the same way as values passed to url_for or link_to. So for example
you may use a named route directly. When the model is represented by a
string or symbol, as in the example above, if the :url option is not
specified, by default the form will be sent back to the current url
(We will describe below an alternative resource-oriented usage of
form_for in which the URL does not need to be specified explicitly).
The bottom line is that as Rails is object orientated, its built around the assumption that you'll have routes set up to handle the creation of individual objects.
Every time you use form_for, Rails will attempt to construct your routes from your object -- so if you're trying to do the following, it will treat the routes as photo_path etc:
#app/views/pushes/new.html.erb
<%= form_for #push do |f| %>
...
<% end %>
--
Fixes
As #mandeep suggested, there are several fixes you can employ to get this to work:
Firstly, you can just create a route for your push objects:
#config/routes.rb
resources :pushes
Secondly, as you're using a different controller, you'll want to do the following:
#config/routes.rb
resources :interactions
#app/views/pushes/new.html.erb
<%= form_for #push, url: interaction_path do |f| %>
...
<% end %>
This will route your form submission to the interactions controller, rather than the pushes controller that you'll get by default!
Objects
Something to consider when creating Rails-based backends is the object-orientated nature of the framework.
By virtue of being built on Ruby, Rails is centered on objects - a term for a variable, which basically encompasses much more than just a piece of data. Objects, in the case of Rails, are designed to give the application:
Once you understand this, the entire spectrum of Rails functionality becomes apparent. The trick is to realize that everything you do in Rails should be tied to an object. This goes for the controllers too:
--
Ever wondered why you call resources directive in your routes, for a controller? It's because you're creating a set of resourceful routes based for it:
Do you see how it's all object orientated?
This gives you the ability to define the routes for specific controllers etc. The most important thing to note is how this will give you the ability to determine which routes / controller actions your requests should go
--
There's nothing wrong in using the controller setup as you have - the most important thing is to ensure you're able to define the custom URL argument, as to accommodate the non-object based structure
In your index action you have
def index
#push =Push.new
end
and your form has
<%= simple_form_for #push do |f| %>
so your form is looking for /pushes with post verb or pushes_path and you don't have that route in your routes.rb file so to fix this you need to add this in routes.rb:
resources :pushes
Update:
when you add resources :push rails basically creates seven different routes for you. One of which is
POST /pushes pushes#create create a new push
and if you look at the html generated by your form it would be something like:
<form action="/pushes" class="new_push" id="new_push" method="post">
// your fields
</form>
notice the action and verb so when you submit your form your routes are checked for them and since you didn't define them in your routes you were getting this error
And how will i be able to use the params i m getting from this form with this new resource addition?
Your form will take you to pushes_controller create action so first of all you'll have to define them. You can access them simply by params[:pushes] in your controller action but since you want to create a new record so you'll have to permit those attributes, checkout strong parameters
If you are using rails >= 4 then you can do
class PushesController < ApplicationController
def create
#push =Push.new(push_params)
if #push.save
redirect_to #push
else
render 'interaction/index'
end
end
private
def push_params
params.require(:push).permit(:attributes)
end
end
If you are using rails < 4 then instead of permitting these attributes(because strong parameters feature came from rails 4) you'll have to tell rails that these attributes are accessible by writing this in your pushes.rb
attr_accessible :attribute_name
Why it is assuming that its pushes controller?Because of the Push.new creation?
That's because if you look at your index action #push = Push.new so #push contains a push object with nil values(as you have just initialized it) so this is where rails magic comes, rails automatically tries to figure out url of your form and since your #push is only an initialized variable so rails takes you to create action for it. For details you should checkout rails polymorphic urls If you want your form to go to interaction_controller or some other url then you'll have to specify the url option for it
<%= form_for #push, url: "your_url_for_custom_method" %>
// other fields
<% end %>
And in the end you should really read docs

Rendering a Rails partial within a single-page app after button-click

How can I render a rails partial within a single-page app after a button-click (assuming there are at least two controllers at play)?
It is unclear to me, regardless of how much material I read:
How many controllers I need
How many views I need
How many actions I need
To which action do I assign resources to?
This is precisely what I am trying to do:
I would like for a user to visit the index page of my application. At that page, they should be able to click a button to "get party event results." The results returned to them should come from the database. This button-click request should be AJAX.
From what I understand, that means
I need two controllers: 1) StaticPagesController and 2) PartyEventsController. It means that I should have a partial for party events app/views/layouts/_part_events_results.html.erb.
I should have an app/views/static_pages/index.html.erb view.
It means that I will need a js.erb file somewhere outside of the assets/javascript path.
Currently I have:
app/controllers/static_pages_controller.rb
def index
end
app/controllers/party_events_controller.erb
def food_event
end
app/views/static_pages/index.html.erb
<%= button_to "get events", 'layouts/party_events/', remote: true %>
I have 'remote: true' set because this should indicate that the link will be submitted via AJAX.
app/views/static_pages/_party_events.html.erb
<% food_events.each do |event| %>
<li><%= event.text %></li>
<li><%= event.location %></li>
---------------------
<% end %>
Other info, if helpful:
Rails 4.0.0
There will eventually be many types of events, e.g. "food", "music". Do I need view partials for each one?
Open to any design solution as long as it's a single-page application. I know how to achieve this in PHP/JS, but am struggling to understand how with MVC.
In your static page, you should have a DOM element available for the ajax to replace or modify.
In your button_to, you should specify the controller and action you would like to hit
In your party_event_controller#action, you can respond with a js and have it render a template, #action.js.erb which will perform the DOM manipulation to replace or update the page.
Similar question asked: Rails - updating div w/ Ajax and :remote => true
An tutorial showing ajax crud with unobstructive javascript: http://stjhimy.com/posts/07-creating-a-100-ajax-crud-using-rails-3-and-unobtrusive-javascript

Use a params[:value] to reference a controller method in Rails

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.

Resources