Check username availability using jquery and Ajax in rails - ruby-on-rails

I am using rails with jquery and ajax to check the availability of username. I am using
the following plugin for jquery validation purpose.
https://github.com/posabsolute/jQuery-Validation-Engine
In my controller i am using the following method to check the username availability.
def check_name
name = params[:name]
if name.strip == ""
render :json => { :available => false }
return
end
user = User.find(:first, :conditions => [ "name = ?", name])
if user
render :json => ["name", false , "This name is already taken"]
else
render :json => ["name", true , ""]
end
end
Is this the correct way to write the method? I checked many of the username availability
posts in this forum, but nothing worked out.

I am adding the answer. Sorry for the delay guys.
First credit to the plugin:https://github.com/posabsolute/jQuery-Validation-Engine .
Used the plugin for validations in the application.
In the view, i had
<%= f.username_field :username, :id => 'free-user', :placeholder=>'User Name', :class => "validate[required, ajax[ajaxUserCall]]", "data-prompt-position" => "topLeft:0,9"%>
In the same view, in java script:
<script type="text/javascript">
$(document).ready(function(){
$("#free-user").bind("jqv.field.result", function(event, field, errorFound, prompText){
if(errorFound){
$(".continue").attr("disabled", false); // .continue is a button
} else{
$(".continue").attr("disabled", true);
}
})
});
</script>
In routes.rb i have the following route.
match '/check-user' =>"users#check_user" // creating route for ajax call
In jquery.validationEngine-en.js file i have following:
"ajaxUserCall": {
"url": "/check-user",
// you may want to pass extra data on the ajax call
"alertText": "* This user is already taken",
"alertTextLoad": "* Validating, please wait"
},
In users controller, i have the following method
def check_user
user = params[:fieldValue]
user = User.where("username = ?", username).first
if user.present?
render :json => ["free-user", false , "This User is already taken"]
else
render :json => ["free-user", true , ""]
end
end

To check the Username/Email Availability do the following:
Using the https://github.com/posabsolute/jQuery-Validation-Engine
edit the validationsEngines-en.js file for the AJAX calls, one for the email will look like the following:
"ajaxEmailAvailable": {
"url": "/route_to_send_the_parameters",
// you may want to pass extra data on the ajax call
"alertTextOk": "* This email is available",
"alertText": "* This email is already taken",
"alertTextLoad": "* Validating, please wait"
},
Make sure you configure your routes.rb file to match the route you want to use, the default action with the call is a GET HTTP Request.
Next set up the proper action in your controller to handle the request (I included a helper in the Application Controller so that the input value can be sanitized before queried in the database:
CONTROLLER HANDLING THE REQUEST
def username_availability
scrubb = help.sanitize(params[:fieldValue], :tags => '')
user = User.find_by_email(scrubb)
if user.blank?
render :json => ["INPUT_ID", true , ""]
else
render :json => ["INPUT_ID", false , "This email is already taken"]
end
end
If you are unsure of the proper INPUT ID, watch the server logs for the parameters passed during the call and do a simple copy-paste. By default the request passes the INPUT ID and INPUT VALUE.
To gain access to this helper add the following:
APPLICATION CONTROLLER
def help
Helper.instance
end
class Helper
include Singleton
include ActionView::Helpers::TextHelper
end
Now on the form itself, your input should read as the following:
<%= c.text_field :email, "data-validation-engine"=>"validate[required, custom[email], ajax[ajaxEmailAvailable]]" %>
As per the proper functionality always place the AJAX call as the last validation.
Don't forget to include jquery.js, jquery.validationEngine-en.js, jquery.validationEngine.js and validationEngine.jquery.css in the head of the document [in that order of js] and to call a
<script type="text/javascript">$(function() {$("#FORM_ID").validationEngine();});</script>
If you want to do this for username, just edit the above appropriately.

Related

New Stripe SCA checkout flow in Rails

I'm struggling with switching my Rails app to the new Stripe checkout flow to accommodate the new SCA regulation.
I want to implement the simple dynamic product routine found in this link: https://stripe.com/docs/payments/checkout/migration#api-products-after
I can't figure out where to put the different pieces of code. What should go in:
- controller -> in which methods
- views -> the event show view for example. The form/button the user will click
- javascript -> how to pass the right session id
- controller again -> implementing the success and error use cases
The Stripe tech support just sent me to the documentation link above so I would really appreciate some help here.
The Rails workflow for the new Stripe Checkout is:
Create a Stripe Checkout Session and retrieve the session.id (.rb)
Pass the session.id to a js initializer to redirect to Stripe Checkout
STRIPE CHECKOUT SESSION
This is a sample client/server Stripe Checkout implementation that I'm using for a Subscription service. Your steps would essentially be the same except you would be referencing a Stripe Product rather than a Plan:
subscriptions_controller.rb
STRIPE_API_KEY = Rails.application.credential.stripe[:secret_key]
skip_before_action :user_logged_in?, only: :stripe_webhook
protect_from_forgery except: :stripe_webhook
def stripe_webhook
stripe_response = StripeWebhooks.subscription_events(request)
end
def index
end
def new
session = StripeSession.new_session(STRIPE_API_KEY, current_user.email, params[:plan])
#stripe_session = session
end
In my case, my index.html.erb template has a link to "Get more info..." about a particular subscription. That link goes to the controller's :new action, passing the relevant Stripe Plan (or Product) info as params. In your case, you might pass whatever Product params necessary for your Stripe Checkout session:
subscriptions/index.html.erb
<%= link_to 'Get more info...', new_subscription_path(plan: 'plan_xxx' %>
The :new controller action will return your Stripe CHECKOUT_SESSION_ID for use in your template. (Also, note that this controller is bypassing logged_in? and forgery protection to allow for the Stripe Webhook POST response to your Checkout Session. You'll need to address your particular authorization scheme here)
Now, you need to call the Stripe API. I'm doing this in a Stripe service like so:
app/services/stripe_session.rb
class StripeSession
require 'stripe' ### make sure gem 'stripe' is in your Gemfile ###
def self.new_session(key, user_email, plan)
new(key, customer_email: user_email, plan: plan).new_checkout_session
end
def initialize(key, options={})
#key = key
#customer_email = options[:customer_email]
#plan = options[:plan]
end
def new_checkout_session
Stripe.api_key = key
session = Stripe::Checkout::Session.create(
customer_email: customer_email,
payment_method_types: ['card'],
subscription_data: {
items: [{
plan: plan,
}],
},
success_url: 'https://yourapp.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://yourapp.com/cancel'
)
end
private
attr_reader :key, :customer_email, :plan
end
If your call to Stripe was successful the session object in your controller :new action will now contain your session data:
def new
session = StripeSession.new_session(STRIPE_API_KEY, current_user.email, params[:plan])
#stripe_session = session
end
JS SCRIPT LOADING
You'll be using the session.id in your link to redirect to the Stripe Checkout page:
subscriptions/new.html.erb
<%= content_for :header do %>
<script src="https://js.stripe.com/v3/" data-turbolinks-eval="false"></script>
<% end %>
<div data-stripe="<%= #stripe_session.id %>">
<%= link_to 'Subscribe', '', class: 'subscribe-btn', remote: true %>
</div>
<script>
const subscribeBtn = document.querySelector('.subscribe-btn')
subscribeBtn.addEventListener('click', e => {
e.preventDefault()
const CHECKOUT_SESSION_ID = subscribeBtn.parentElement.dataset.stripe
stripe.redirectToCheckout({
sessionId: CHECKOUT_SESSION_ID
}).then((result) => {
// handle any result data you might need
console.log(result.error.message)
})
}
</script>
The above template is doing several important things:
Load the stripe v3 js script (it's up to you how/where you load this script. If using content_for then your layout.html file would have a corresponding block:
<% if content_for? :add_to_head %> <%= yield :add_to_head %> <% end %>
Pass the #stripe_session.id from the controller :new action to the data-stripe-id attribute of your <div> element.
Add the EventListener for the subscribe-btn to redirect to Stripe Checkout, passing in the #stripe_session.id
ALTERNATE APPROACH FOR JS SCRIPTS
There are other ways to load the js scripts. Personally, I love using Stimulus for this sort of thing. For example, rather than loading js with content_for and using <script> tags I have a subscription_controller.js Stimulus Controller doing the work:
subscriptions/new.html.erb (now becomes)
<div data-controller="subscription" data-session="<%= #stripe_session.id %>">
<%= link_to 'Subscribe', '', class: 'btn', remote: true,
data: {action: 'subscription#redirectToCheckout', target: 'subscription.sessionID'}
%>
</div>
---
(The Stimulus controller)
app/javascript/controllers/subscription_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ 'sessionID' ]
get sessionID() {
return this.sessionIDTarget.parentElement.dataset.session
}
initialize() {
const script = document.createElement('script')
script.src = "https://js.stripe.com/v3/"
document.head.appendChild(script)
}
redirectToCheckout(e) {
e.preventDefault()
// grab your key securely in whichever way works for you
const stripe = Stripe('pk_test_xxx')
const CHECKOUT_SESSION_ID = this.sessionID
stripe.redirectToCheckout({
sessionId: CHECKOUT_SESSION_ID
}).then((result) => {
console.log(result.error.message)
})
}
}
You would need to add/initialize Stimulus to your Rails app for the above to work...
STRIPE WEBHOOKS
Stripe will POST to your webhook endpoints (if you configure them to). If listening for them, you configure some routes (see below) to handle them. You can also do this in a service of your choosing. For example, create another file in your app/services folder:
app/services/stripe_webhooks.rb
class StripeWebhooks
require 'stripe'
STRIPE_API_KEY = Rails.application.credentials.stripe[:secret_key]
def self.subscription_events(request)
new(request).subscription_lifecycle_events
end
def initialize(request)
#webhook_request = request
end
def subscription_lifecycle_events
authorize_webhook
case event.type
when 'customer.created'
handle_customer_created
when 'checkout.session.completed'
handle_checkout_session_completed
when # etc.
end
end
private
attr_reader :webhook_request, :event
def handle_customer_created(event)
## your work here
end
def handle_checkout_session_completed(event)
## your work here
end
def authorize_webhook
Stripe.api_key = STRIPE_API_KEY
endpoint_secret = Rails.application.credentials.stripe[:webhooks][:subscription]
payload = webhook_request.body.read
sig_header = webhook_request.env['HTTP_STRIPE_SIGNATURE']
#event = nil
begin
#event = Stripe::Webhook.construct_event(
payload, sig_header, endpoint_secret
)
rescue JSON::ParserError => e
puts e.message
rescue Stripe::SignatureVerificationError => e
puts e.message
end
end
end
This file will receive and authorize the incoming Stripe webhook that you configured in your Stripe Dashboard. If successful, event attribute will contain the JSON response of whichever webhook you're ingesting at the moment.
That allows you to call various methods based on the event.type which will be the name of the webhook. event.data.object will get you into specific response data.
RAILS ROUTES
None of the above will work without the proper routes!
routes.rb
get 'success', to: 'subscriptions#success'
get 'cancel', to: 'subscriptions#cancel'
resources :subscriptions
post '/stripe-webhooks', to: 'subscriptions#stripe_webhook'
I had to place the get 'success' & 'cancel' routes above the subscription resources for them to resolve properly.
And, finally, add the success and cancel callbacks to your controller and do whatever you need with them. For example:
subscriptions_controller.rb
...
def success
### the Stripe {CHECKOUT_SESSION_ID} will be available in params[:session_id]
if params[:session_id]
flash.now[:success] = "Thanks for your Subscribing/Purchasing/Whatever..."
else
flash[:error] = "Session expired error...your implementation will vary"
redirect_to subscriptions_path
end
end
def cancel
redirect_to subscriptions_path
end
...
Note: you'll need a corresponding success.html.erb file. The cancel action can redirect or create an html.erb file for that too if you'd like.
So, it was kind of a bear to get it all setup. However, with the plumbing out of the way there are lots of cool possibilities to handle all sorts of lifecycle events/webhooks. Currently, I'm listening for about 15 of them to keep my subscription system running smoothly.
Good luck!
I'm not using ruby but in the case to pass the session ID when Success checkout is Done when creating the session just add "?session_id={CHECKOUT_SESSION_ID}" after the * _url,
Don't know if this is your case but glad to help
mode : "subscription",
customer : customerid,
success_url: 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://example.com/cancel?session_id={CHECKOUT_SESSION_ID}',
also, I suggest watching this https://youtube.com/watch?v=8TNQL9x6Ntg

Rails grab template file as string variable in controller

I'm creating a custom ActionMailer which will also send Direct Messages on Twitter, and Text Message through Twilio. I need each to receive a parameter of the body of the message. I need to use the current template as a string variable.
For example:
# calling send_invite's mail method will load the send_invite template
def send_invite(to)
mail(to: to) # body parameter is automatically rendered from template
end
Now if I modify it to send through other services:
def send_invite(to, option)
if option == :email
mail(to: to) # body parameter is automatically rendered from template
elsif option == :twitter
twitter.create_direct_message(to, NEED_TEMPLATE_AS_STRING_HERE)
elsif option == :sms
twilio.sms(to, NEED_TEMPLATE_AS_STRING_HERE)
end
end
How might I grab the template for each of these service calls? Would there be something like:
message(to, render layout: false)
# or
message(to, IO.read template_path_helper("template") )
# or
message(to, template_helper.to_s)
How might I get the template as a string for my message body in these other methods? What's the Rails Way to do it? The templates are erb files and need to be able to render the appropriate variable as they would from a render.
I need to do it this way for translations of templates to be available.
I think you can create the template string from the controller, and send it to your mailer, like this:
#parsed_template = render_to_string(:partial => 'my_partial', :layout => false, :locals => {:my_object => my_value})
SomeMailer.send_invite(to, option, #parsed_template).deliver

Add generic fields to rails JSON response

I need to render JSON response for the REST api in which I need to include additional JSON fields that are not part of the model being rendered. I did read this link about ActiveModel where it suggests to use the ":methods" option to call another method where I can additional generic fields.
def add_fields
{ "field1" => "true" }
end
if #user.save
render :json => #user.as_json(:only => [:username, :org], :methods => [:add_fields])
endif
However, when the JSON response is received only the username & org fields are returned in the JSON. The additional parameters defined in the method "add_fields" are not added. What is it that I am missing due to which additional fields are not being added to JSON response. Or is there a better way to add generic fields (not part of model) in JSON response?
May be you forgot to implement the #add_fields method in User.
I just tested this on my own User model and it worked just fine.
2.0.0p195 :002 > user.as_json(only: [:first_name, :last_name], methods: [:full_name])
{
"first_name" => "Cody",
"last_name" => "Russell",
:full_name => "Cody Russell"
}
Are you sure you have the method defined, and it's a public method?

How to change the response format depending on the data sent?

I'm doing some controllers to render reports and here is my problem:
The user open a page with a form which let it change the download format and the date of the report.
The download format is set trough a select input.
When the user press the button I want to response depending on the selected format.
The problem is that it's specified trough the url. So trying to do something like:
case format
when "xlsx" then format.xlsx{...}
when "html" then format.html{...}
...
end
doesn't work because rails or the browser (I'm not sure) expects an html response.
I've though of two options:
Change the url of the form onsubmit which makes the application more dependent on javascript. Or.
redirect_to url + ".#{params[:download_format]}"
The second way looks better to me but I need to pass the :report_date in the url and I can't find the way to do it.
I've tried this:
url = my_custom_url_path
redirect_to url + ".#{params[:download_format]}", :date_format => params[:date_format]
But it's not working.
In the form:
<%= f.select :download_format, { "xlsx" => "xlsx, "Online" => "html" } %>
In the controller:
def action
if download_format = params[:download_format].delete
redirect_to my_action_path(options.merge( :format => download_format ) ) and return
end
# do some logic here
respond_to do |format|
format.xlsx{...}
format.html{...}
end
end

Rails - When calling NEW, create a record before displaying a form?

Here's what I'm trying to do.
when the user clicks new note.. I want the user to be taken to a page when they can start typing a note, and save it to the server all with AJAX..
Problem is, every time the page saves, it's making a new note.
This leads me to believe that when Rails gets the DEF NEW controller, some how I need rails to first make a NEW NOTE record and then redirect to the edit controller of that new note, where the user can create/edit the note all with AJAX.
Thoughts? Thanks.
I had the same problem once, creating the note first is probably a good idea.
Another way would be to send the user to the new action. When the first save occurs you send the new object back as a JSON object, and replace the form's action with the update url for that record as well as setting the form's method to put.
This way you don't end up with empty records in the database (with your use-case, you might want exactly that, so a User can continue a note later.)
Just my two cents.
Ok a way of implementing this could look like this:
Form
<%= form_for resource,
:remote => true,
:html => { 'id' => 'autosave' },
:url => resources_path(:format => :json) do |f| %>
...
<% end %>
Application JS
var $form = $('#autosave');
// bind to the first success event from the form
$form.one('ajax:success', function(data, status, xhr) {
// data contains our json object (your note as json)
// now we update the form
$form.attr('action', '/notes/' + data.id);
$form.attr('method', 'put');
$form.attr('data-method', 'put');
});
Controller
class ExampleController
...
def create
#
# respond with the json object if format = json,
# see the form above I'm passing the format with :url parameter in form_for
#
respond_with(resource) do |format|
format.json { render :json => resource }
end
end
end
If you really want use to use #new to create a note and save it, then you can simply do
def new
#note = Note.create # instead of Note.new
end
Rails will then display this note just like the #edit action, so the note id will be in a hidden field. Then when you send the Ajax calls, you'll be calling #edit. If you want to preserve the behavior of #new for when javascript is turned off, then you might want to create a different action.
def new
#note = Note.new
end
def new_js
#note = Note.create
end
When you load the page that has the link to new_note, include some javascript that changes the link to new_js_note. So when JS is off, you get the standard #new form. When JS is on, you get a form that is basically editing a preexisting blank note.

Resources