wrong number of arguments (given 0, expected 1) passing parameter into helper - ruby-on-rails

module ProfilesHelper
def accept (invites)
invite=invites.find(id: '1')
invite.accept= '1'
invite.save
end
end
The user needs to click a button to accept or deny an invitation they have received. I created this helper method, it's suppose to find the user's invite and then set it's accept boolean to 1. But I get "wrong number of arguments (given 0, expected 1)"
Views:
<% #invites.each do |invite| %>
<%= link_to "Accept", accept(invite) %>
<% end %>
How do I pass the invite object into the accept helper?

I think you're misunderstanding the purpose of helpers. With the way you're using this code, the invite will be accepted when the page is viewed, not when the link is clicked.
Helpers are there to help you lay out the page. Either by abstracting logic away from the view, decorating things, formatting numbers into currency, or a million other things. They are not for handling requests.
You shouldn't be using standard GET requests (an anchor tag) to make changes to your data set. They should come via PUT, PATCH, POST, DELETE requests.
What you need here is to either use a button element, or change your link to submit the request using a PUT or PATCH verb. Then, using a suitable route to receive the request to accept the invite, add an action to your controller to handle this request.
Then in your controller you can do something like this:
def accept_invite
#invite = Invite.find(params[:id])
#invite.accept!
end
Which allows you to move the business logic of handling that acceptance into the model:
class Invite
def accept!
accept = true
save
# You might want to do other things here, like email them a confirmation etc.
end
end
This way, your controller can worry about just handling the request, and the model can worry about how to handle actually accepting an invite.

Related

Access old get parameters in URL after a post request

I have a form in RoR with a controller action that looks up a record via the get parameter.
def respond
if request.post?
# Submit logic here...
# cannot lookup this way to fill the form out again
# #current_message = Saved_message.find_by_id(params[:msg_id])
elsif request.get?
#current_message = Saved_message.find_by_id(params[:msg_id])
end
end
I can't use the params[:msg_id] to lookup the message again because it's a post request and I don't resend the get parameters. However, the get parameters remain in the url such as .../messages/respond?msg_id=2. I can get around this by passing in a hidden field with a different parameter name like <%= form.hidden_field :msg_id_2, value: params[:msg_id] %>. Then I can lookup the #current_message via the params[:msg_id_2]. However, I don't like this solution. Any advice to access the now inaccessible get parameter?
you should use RESTful routes so that you do not have to care about such issues.
since you are not posting much about the actual code or problem you are trying to solve, i can just assume what might be the issue here and how to solve it.

How to validate my controller action was called from within my Rails app

I have a simple user registration form.
When the user fills out the form and clicks "Submit" I have a JavaScript event that intercepts the submit and uses AJAX to call the validate method below to check the form data before submitting. If the form data is ok it continues with the submit, but if it's not it cancels the Submit and displays a warning on the page. It's a simple way of doing real-time client side validations.
# app/controllers/users_controller.rb
# Validates the form data. Returns an error message response in the format
# { response: "Invalid email format!" }
#
# A blank response means success
#
def validate
if request.xhr? || <request came from another method in this controller>
# Run various validations on input data and determine a response
# response_text = ...
render json: { response: response_text }
else
redirect_to root_path
end
end
def create
if JSON.parse(validate)["response"].blank?
User.create(...)
# Other stuff
end
end
However when the submit does eventually pass and go through, it sends a POST to the create action to create a new User. That method calls validate again (the cost of re-validating is minimal) to ensure that no one bypassed the form and submitted a malicious request directly to the server.
So my validate method has to respond to both (a) AJAX calls and (b) "internal" calls from within the app. All other calls to the validate action should just redirect to the root path.
I can tell whether the call was an a AJAX call pretty simple using request.xhr?.
How do I check whether the action was called internally and not by a user?
Taking a step back, is this a good general approach for validation? Any thoughts to improve on it?
Thanks!
Rails generates an authenticity token whenever a user views a form and stores it as a random string in the HTML. That token is also stored in the session and is therefore invisible to the user. Upon receiving a request, your application will compare the tokens to verify whether the request was generated from your form.
TL;DR: Don't worry, you're already protected.
I don't have a specific answer, but do have some information which could help. I'll gladly delete this if required...
AJAX calls and "internal" calls
You have to understand that XHR requests can only come from your app -- CORS (cross origin resource sharing).
Ajax calls can only come from your own domain, so don't think a malicious hacker could run some scraper or whatever -- you choose which domains are permitted to send XHR requests.
So when you're calling...
if request.xhr?
... as part of a validation, you need to scope when XHR will be used.
On the same note, what validation are you performing?
If you're validating input data, Rails handles this at the model layer. We've done something similar (click login/register at top):
The base structure of Rails is MVC, which means that your controller is only responsible for taking a request & building the appropriate data objects out of it.
Thus, if you're "validating" an input, what is there to validate apart from the data itself (handled by the model)?
As #MarsAtomic specified, too, Rails uses a CSRF token to provide some level of authentication for a form submission.
--
You could easily use the following:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
respond_to :js, :json, :html
def create
#user = User.new user_params
respond_with #user.save
end
end
If you then sent the request from your front-end as json, you'd get back the created user OR the errors:
#app/views/users/new.html.erb
<%= form_for #user, remote: true, format: :json do |f| %>
<%= f.text_field .... %>
<%= f.submit %>
<% end %>
#app/assets/javascripts/application.js
$(document).on("ajax:error", "#new_user", function(xhr, status, error) {
// do stuff with response
});
In your create method, you want to return a #user if it's not valid (if they skip the ajax validate).
If that's the case, you don't want to do User.create, instead, create an instance and try to save it. If you do all this, you're doing the normal controller action for create and there is no need to call validate internally.
def create
#user = User.new(user_params)
if #user.save
# Other stuff
end
end
And, it's not a good practice to create a json response and parse it to see if an object is valid. Instead, either test with valid? or just try to save and get back a true or false.
In other words - keep your validate method separate from your create method. They'll both rely on valid? eventually.

Best way to reset ActiveRecord Attribute with a link

I have a counter in my model that I want to give the user the ability to
reset it, I'm wondering what's the best way to achieve this. I can think of
two ways:
By a custom controller action.
Simple and easy but I can't decide which HTTP verb to use. I can make the
case that it should be a GET because the user clicks a link that reset
the counter and the result are always the same, i.e. counter
becomes 0. But it could also be a POST/PATCH since we are modifying
something on the server but POST/PATCH requires a form which leads to
the other way.
By a link that submits an edit form with the counter reset to 0 without
the user seeing the form.
I like this solution because it can be done with RESTful controller
methods. But I have no idea how to do that with Rails, or even if it's
possible.
So which is "Rails Way" to do this? and how do I do it?
Rather than creating a custom action, another approach is to create a well-named controller and stick to the RESTful controller method names.
config/routes.rb
resource :counter_reset, only: [:create]
app/controllers/counter_reset_controller.rb
class CounterResetController < ApplicationController
def create
# reset your counter
end
end
Then POST to counter_reset_path in your view
Personally, I would use button_to — this generates a single button that submits to the URL; it performs a POST operation by default. If you don't like the button style, you can switch to using link_to; however, keep in mind that if a user has JavaScript disabled, the request will fallback to using GET.
<%= button_to "Reset counter!", counter_reset_path %>
<%= link_to "Reset counter!", counter_reset_path, method: :post %>
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
Update:
If you prefer not to create a new controller, you can create a new route that maps to a custom action in your existing controller:
config/routes.rb
resources :counters do
post :reset, to: "counters#reset"
end
app/controllers/counters_controller.rb
class CountersController < ApplicationController
def reset
# reset your counter
end
end
In your view:
<%= button_to "Reset counter!", counter_reset_path %>
Actually you don't need a form, for me i would add a new action, it would look something like this ( of course depends on how your current routing looks like )
/user/:id/counter/reset # with action = post
And the link is very simple, you just create a link_to and add a method: :post which will add a data-method: :post in the html, the rest will be handled by the unobtrusive js.
The reason I don't recommend the form method, is users might use it to update different attributes that you might not want to update, or at least even change the counter to whatever number they want, I prefer the specific number to be defined in the controller not the view/form.

Best way to allow admin to build objects for Admin

My objective is to allow Admins the right to sign Users up for a Project.
Currently, Users can sign themselves up for Projects.
So I was thinking in order to allow Admin to do this.. do something like this :
haml
= link_to "Project Signup", card_signups_path + "?user=#{user.id}", :class => "button"
And pass the params[:user] so I can replace this controller with this :
if params[:user]
#card_signup = User.find(params[:user]).build_card_signup
else
#card_signup = current_user.build_card_signup
end
The trouble is though.. this is a 3 part signup process, and its loaded VIA AJAX, so I can't pass the ?user=#{user.id} in any of the steps after the first.. ( at least not by the same convention that I already did, or know how to )
What kind of strategy would you employ in this?
One possible way of accomplishing this, would be to add a hidden field to your form, that mirrors the parameter your passing in, if its found.
So if the parameter your passing in is user, in your view you want to add a hidden field something like:
<input type="hidden" name="user" and value="<%= params[:user] %>" />
or with a rails form helper:
hidden_field_tag 'user', params[:user]
This way the code in your controller can check for this parameter at each step on the receiving end and know who to save the object for. Something like this:
def create
#card_signup = CardSignup.new(params[:card_signup])
if params[:user] && params[:user].to_i > 0
##
##some logic here to make sure current_user is admin, as no one else is allowed to do this
##
#card_signup.user_id = params[:user]
else
#card_signup.user_id = current_user.id
end
##onto validating model and saving / redirecting / etc
end
But the ultimate goal here is to keep the user param around, whether its an initial GET parameter to the page, or a Put/Post from ajax/etc to submit the form, this parameter will be around.
One other security angle to check would also be in the 'new' action of this controller, and check that if the user param is present then the current_user is an administrator, otherwise redirect or display an error message. This combined with re-validating this on the create should provide a decent way of making sure no one else can make these requests. You could also put this in a before_filter and call it for only new and create to keep things clean.

Question about a/bingo basics (a/b testing)

from:
http://www.bingocardcreator.com/abingo/usage
#A view example with a block passed to ab_test:
<% ab_test("call_to_action", %w{button1.jpg button2.jpg}) do |button| >
<%= image_tag(button, :alt => "Call to action!" %>
<% end %>
Does whatever "choice" that gets passed in the block have to be some sort of link? How does a/bingo know when different choices have been converted?
The way Abingo works is to issue different options to different "identities" in a consistent manner so that the results can later be aggregated together again. There are several ways to do this, such as by IP address, by session_id, or by registered account, all of which are valid and can be used in conjunction. In effect, a particular identity will always get the same random selection of options.
An example from the documentation on assigning the identity is as a handler in ApplicationController:
before_filter :set_abingo_identity
def set_abingo_identity
if #user
# Assign identity based on user
Abingo.identity = #user.abingo_identity
else
# Assign identity for anonymous user
session[:abingo_identity] ||= rand(10 ** 10).to_i.to_s
Abingo.identity = session[:abingo_identity]
end
end
When you want to track action based on which A/B option was used, you need to inject calls in your controllers. Another example:
def show
# Track conversion for active Abingo identity
bingo!("show_info_page")
end
The mechanism by which the user navigates to that particular page is entirely arbitrary and can be by link, by form submission, by JavaScript redirect, or by clicking on an email. The only thing that matters is that the display of the A/B option and the later controller action that tracks the activity both have the same Abingo identity assigned.

Resources