Thursday, March 25, 2010

JQuery and Unobtrusive Javascript on Rails 3

In the earlier post, JQuery on Rails 3. You will need a few files to kick start in using JQuery.
  1. rails.js from http://github.com/rails/jquery-ujs
  2. jquery.js from http://code.jquery.com/jquery-1.4.2.js
  3. application.js having your custom javascript command
  4. Load them in the layout file
Lets try adding an ajax link somewhere on a page. Notice the difference, we are no longer using link_to_remote anymore. We will instead be using link_to, passing in the :remote key.
index.html.erb

<span id=\"ajax_button\"><%= link_to \'ajax!\', \'/\', :remote => true %></span>

<div id =\'ajax\'>
</div>

What Rails 3 gives us in doing this. If you take a look at the html source file, we get a clean ajax request link in line with html5.

Then we will throw in some trivial JQuery codes into a generic application.js file. We will specify the span id of ajax_button to change the html content of a div id of ajax to have "lol" when the link is clicked.
application.js
$(document).ready(function(){
$("#ajax_button").click(function(){
$("#ajax").html("lol");
});
});


When done right, we should see an update on div id ajax.

In the next example, we are going to do some ajax together with Rails controller request.

For this example, a controller file, posts_controller.rb. We will add a format.js under the respond_to so the method knows that it should format a javascript file.

def create
@post = Post.new(params[:post])
respond_to do |format|
if @post.save
format.html { redirect_to(@post, :notice => 'Post was successfully created.') }
#format.xml { render :xml => @post, :status => :created, :location => @post }
format.js
else
format.html { render :action => "new" }
format.xml { render :xml => @post.errors, :status => :unprocessable_entity }
end
end
end


Then the same is true for forms in Rails 3, when we wanna send an ajax request, the remote_form_for has been scrapped and we will use form_for(@post, :remote => true), again passing in the remote key.
new.html.erb

<% form_for(@post, :remote => true) do |f| %>
<%= f.error_messages %>

<div class=\"field\">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class=\"actions\">
<%= f.submit %>
</div>
<% end %>

Then we will create another file in the View folder, create.js.erb which holds the JQuery logic that we want it to run when the create method is fired.
create.js.erb
$("#ajaxified").html(""); // updates the div id ajaxified
alert("Ajax works!"); // prints an alert message
It should print a message and update the ajaxified id with the post title when the form is submitted!

Monday, March 22, 2010

Sending Email on Rails 3

Rails 3 makes sending E-mail easy and intuitive. Thanks to the tutorial at
http://railscasts.com/episodes/206-action-mailer-in-rails-3

We will create a setup_mail.rb file under the config/initializers with this code (sending email from a gmail smtyp)


ActionMailer::Base.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:domain => "rails3mailer.com",
:user_name => "rails3mailer",
:password => "password",
:authentication => "plain",
:enable_starttls_auto => true
}


We will then generate a mailer


rails g mailer registration
create app/mailers/registration.rb
invoke erb
create app/views/registration
invoke test_unit
create test/functional/registration_test.rb


class Registration< ActionMailer::Base

def registration_confirmation(user)
@username = user.name
mail(:to => user.email, :subject => "Registered", :from => "rails3mailer@gmail.com")
end

end


As you can see, it works like a Controller now. The @username is the instance variable storing the user.name from the user object that we are going to pass in when the method is called.
Next line, would be the mailing method. We specify that we will want to send the email to user.email (just like accessing an object instance variable), specifying the subject and from who.

This would be the view aka. what will the user receive.
Putting in whatever text will be the mail that the user would receive. And since we have passed the user name through the instance variable. We can actually use that to put the user name here like,

app/views/registration.text.erb
Hey, @username
You are onboard!


To make sure the mail is delivered, we will have to do one more thing.
In Ryan Bates' example, this can reside in the User controller after the User is created.

Registration.registration_confirmation(@user).deliver

We basically call that method and then Deliver!

Tuesday, February 23, 2010

JQuery on Rails 3.0

Firstly, head over to http://github.com/rails/jquery-ujs and obtain the rails.js file and substitute with the one in our javascript folder.

We will then include the required javascript files,

<%= javascript_include_tag 'jquery','rails','application' %>

Get the jquery.js file from http://code.jquery.com/jquery-1.4.2.js

Now... let's do some trivial testing so that we know JQuery is actually working.


class EntriesController < ApplicationController
respond_to :html, :xml, :js

def index
@entries = Entry.all_entries
respond_with(@entries);
end

def new
@entry = Entry.new
end

def create
@entry = Entry.new(params[:entry])
if @entry.save
respond_with(@entry, :location => entries_path)
end

end

end


We will then create a create.js.erb file which contains the JQuery code that we want to run when then action is invoked.
Let's put something like,
alert("Thanks for visiting!");

The new syntax in Rails 3 is that we will not use remote_form_for no more, but instead

<% form_for @entry, :remote => true do |f| -%>
<%= f.error_messages %>
<%= f.label :title, "Title" %>
<%= f.text_field :title %>


Same is true if it's a link,

<%= link_to 'Ajaxified', :remote => true %>

If done right, the browser should fire an Alert box when the form is submitted.

Tuesday, December 22, 2009

Basic PayPal

RyanB's Railscasts provided the perfect example to adding PayPal functionality to your application, http://railscasts.com/episodes/141-paypal-basics.

But what if let say you don't wanna do a cart system but instead you want the site to take you directly to the payment section in one click. With that of course, you can't have the user selecting items into the cart but that would do if you are charging all users the same price for the same item.

In the view, we will need a link_to taking us to the payment site on PayPal. We can also use a form to have it sent as a POST method if we are concern about the details in the url.


Based on RyanB's tutorial, we will need a sandbox account to play with this kinda stuff before firing up the real deal.

models/user.rb

def paypal_url(return_url)
values = {
:business => 'sell_1261499871_biz@live.com',
:cmd => '_xclick',
:return => return_url,
:invoice => id
}
values.merge!({
"amount" => 500,
"item_name" => "Registration"
})
"https://www.sandbox.paypal.com/cgi-bin/webscr?" + values.to_query
end


So now when we go ahead and click the "Pay Up Now!" link, we will be redirected to the PayPal site where we are gonna pay for a registration fee of $500...

You may like to refer to the documentation and HTML variables for more info and features.

Tuesday, December 8, 2009

Fooling Around With Sessions

A very trivial try,


def session_string
if session[:storage].nil?
session[:storage] = "Hi! i reside in the session storage."
else
session[:storage]
end

This action checks whether the session[:storage] is nil? If it is, then just put a string into it. If it isn't then the action will just return the session.


puts session[:storage]

It will display the string stored in the session.

A lil part i picked, from the Agile Development Rails book,


def find_cart
unless(session[:cart])
session[:cart] = Cart.new
end
session[:cart]
end


This action will check if a session[:cart] is nil or contains a cart. If it doesn't then create a new Cart object and store it into the session. If there is, then it will just return the session[:cart] which contains a former Cart object in it.

Session is stored in a cookie by default in Rails. So each browser will have its own session storage and will interact differently between different clients.

Thursday, September 3, 2009

JQuery on Rails

I am starting to test out JQuery on Rails and see if i can reproduce the same result of what i did back then with the Rails bundled Prototype helpers.

in rjs, (js.rjs)

page.replace_html "widget-count-#{@post.id}", :partial => 'widget_count', :locals => {:post => @post}
page.replace "widget-button-#{@post.id}", image_tag('wiggy.png')

in jquery, (js.erb)

$("#widget-count-<%= @post.id %>").replaceWith
('<%= escape_javascript(render(:partial => 'widget_count', :locals => {:post => @post} ))%>');
$("#widget-button-<%= @post.id %>").replaceWith
('<%= image_tag('wiggy.png') %>');


in addition to that, if you are using JQuery, do not forget to import that JQuery javascript file.

However, if you are dealing with ajax request... there is something extra you need to do. Based on RyanB's railscast at http://railscasts.com/episodes/136-jquery,

in application.js

#to request for js format by default instead of html if you have both respond_to |format|

jQuery.ajaxSetup({
'beforeSend' : function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
})


#Changing the behavior of the form, note the form id "new_comment" to submit an ajax request

$(document).ready(function(){
$("#new_comment").submit(function(){
$.post($(this).attr("action"), $(this).serialize(), null, "script");
return false
})
})


JQuery.js has to be loaded first before application.js in order to get it work.

Then for the appearance of the comment in an ajax manner,

in rjs, (js.rjs)

if @comment.save
page.insert_html :top,"comments", :partial => 'comment'


in jquery, (js.erb)

<% if @comment.save %>
$("#comments").prepend("<%= escape_javascript(render :partial => 'comment') %>")
<% end %>


Finally if you intend to use JQuery with other Javascript libraries, Prototype for example, bear in mind the need to declare noconflict. http://docs.jquery.com/Using_jQuery_with_Other_Libraries

One of the example to prevent conflict between JQuery and Prototype is by,

Add this right after the libraries are loaded. Perhaps in application.js

jQuery.noConflict();

Then anytime you intend to use JQuery libraries wrap the JQuery codes inside the jQuery(document).ready(function($){ #jquery codes }) as such,

jQuery(document).ready(function($){
<% if @comment.save %>
$("#comments").prepend("<%= escape_javascript(render :partial => 'comment') %>");
<% else %>
alert('NO!');
<% end %>
})


The $(element) codes without wrapping into jQuery will be treated as Prototype and the score is settled.