Setup script to get Ruby and Rails running on Ubuntu with one command using RailsReady

Posted by Bhushan Ahire | Posted in git, Rails, ruby, Subversion | Posted on 05-12-2011

0

How would you like to get a full Ruby on Rails stack up on Ubuntu with one command?

Now you can by running Rails Ready. Rails Ready is a setup script that gets Ruby and Rails running on a fresh install of Ubuntu with one command (Tested on Ubuntu server 10.04 LTS (Long-term Support)).

This is a brand new project by Josh Frye that he uses all the time to setup VMs, but there’s always testing to be done and improvements to be made.

Running the Script

Check out railsready.sh to see everything Rails Ready is doing.

  sudo wget --no-check-certificate https://github.com/joshfng/railsready/raw/master/railsready.sh && bash railsready.sh

The script will then ask if you want to build Ruby from source or install RVM. If you want to watch the magic happen just run tail -f ~/railsready/install.log.

What gets installed?

  • An updated system (Linux only)
  • Homebrew (OSX only)
  • Ruby 1.9.3 latest patch level (installed to /usr/local/bin/ruby) or RVM running 1.9.3 latest patch level
  • Imagemagick
  • libs needed to run Rails (sqlite, mysql, etc)
  • Bundler, Passenger, and Rails gems
  • Git

All you need to do is install NGINX or Apache, run passenger-install-nginx-module or passenger-install-apache-module, upload your app, point your vhost config to your apps public directory and go!

A note about RVM+passenger+nginx: Passenger installed via RVM can’t locate the OpenSSL package installed on Ubuntu. A user contributed fix is as follows:

rvm remove 1.9.2
rvm package install openssl
rvm install 1.9.2 --with-openssl-dir=$HOME/.rvm/usr
rvmsudo passenger-install-nginx-module

Hope this guide will be helpful to you.

Install different version of nginx with Phusion Passenger

Posted by Bhushan G Ahire | Posted in Featured, General | Posted on 18-05-2011

0

Phusion Passenger aka mod_rails + Apache2 or + Nginx is the easiest way to deploy Rails 3 app on the market. Thanks to the great team at Phusion, you could install standalone version and test out your app like you run test out with WEBrick and it’s just easy as 1,2,3.

Phusion Passenger standalone version install with nginx 0.8.5.4 by default. Now what if you’re running different nginx version on your production and you want to make sure you match that version in your development? Here’s how

passenger start --nginx-version VERSION

where VERSION is the desired version. For example, if you want to try out the shiny and hot nginx 1.0, simply type in your rails3 folder in terminal

passenger start --nginx-version 1.0.0

Enjoy!

Attachment_fu image saving from Base64 string.

Posted by Bhushan G Ahire | Posted in iphone, JRuby, Rails, ruby | Posted on 04-01-2011

0

Just the other day, I was struggling with the problem that I needed to create an attachment from a base64 encoded image passed by a Flash object. My first idea was to encapsulate the data in an UploadedFile object, but I couldn’t get it to work without writing some pretty ugly code. I could also use the LocalFile model as described here, which you can use to pass a local file to attachment_fu. I figured that wouldn’t do the job properly either; you would write the data to the filesystem, pass the file, and attachment_fu would load it again.

Looking in the attachment_fu code, I found that it expects either a File or a StringIO. Moreover, it expects that it contains a original_filename method and a content_type method. My solution was therefore to create a small wrapper for the imagedata, which is based on StringIO:

# Usage example:
#
# model = Model.new()
# model.uploaded_data = VirtualImage.new(image_data, filename, mime_type)
# model.save
class VirtualImage < StringIO
 # Image mime types
 MIME_TYPES = [ "image/gif", "image/ief", "image/jpeg", "image/jpeg", "image/jpeg", "image/x-portable-bitmap", "image/x-portable-graymap", "image/png", "image/x-portable-anymap", "image/x-portable-pixmap", "image/cmu-raster", "image/x-rgb", "image/tiff", "image/tiff", "image/x-xbitmap", "image/x-xpixmap", "image/x-xwindowdump" ]

 # The filename of the image to be stored
 attr_reader : original_filename
 # The content type of the image_data
 attr_reader :content_type

 def initialize(image_data, filename, mime_type)
  raise "Unrecognized MIME type '#{mime_type}'" if !MIME_TYPES.include?(mime_type)
  @content_type = mime_type
  @original_filename = filename
  super(image_data)
 end

end

Removing “out of sync” error in acts_as_solr

Posted by Bhushan G Ahire | Posted in JRuby, Rails, ruby | Posted on 05-10-2010

0

Solr is an open source enterprise
search server based on the Lucene Java search library, with XML/HTTP and JSON APIs, hit highlighting, faceted search, caching, replication, a web administration interface and many more features. It runs in a Java servlet container such as Tomcat.  -Apache Solr

Solr can be used in different containers and different wrappers. Our application runs on Ruby on Rails, and we used acts_as_solr. Though solr is a powerful, already stable and yet flexible third party solution that we could rely on, we were still not able to maximize its full capacity. We used the bare minimum features of solr for our search modules.

As of now, we’ve used a couple of acts_as_solr enhancments and add ons, some of which we learned from different online resources. We were able to use db_free_solr and explored on the highlighting and faceting capabilities of solr. Its been pretty helpful, but of course nothing is almost always seamless. We encounter few problems with syncing records from the database and onto solr. For sure, you’ve come across this trouble before, if you’ve been using solr:

Out of sync! Found N items in index, but only n were found in database!

It sure was putting down every page wherein there was this glitch in the count of the records retrieved. It therefore gave the negative impression that our site was frequently unstable. Removing a certain indexed element from the solr index is easy as:

ActsAsSolr:: Post.execute(Solr::Request:: Delete.new(:query => %{type_s:Model AND id:"Model:110809"}))
ActsAsSolr:: Post.execute(Solr::Request::Commit.new)

It could’ve been pretty straightforward removing this concerned item from the solr index and then everything would be well.. but its a lot harder than that if you’re looking at over a thousand indexed elements vs their ‘existing’ counterparts in the database! Finding the exact data to remove was really the hardest part! I never knew this until I took the liberty of helping out our kind Infra Team to resolve the problem. I decided to tweak the solr parser method returning the “out of sync” error. I thought that it would actually be brilliant to just display the concerned element’s id so that they could delete it from the index itself. And so, I had something like this: (in acts_solr/lib/parser_methods.rb)

raise "Out of sync! Found #{ids.size} items in index, but only #{things.size} were found in database! Remove #{(ids - (things.collect{|x| x.id})).to_sentence}." unless things.size == ids.size

And yes, viola! I can now see the faulty ids that were causing the “out of sync” problem. I presented this not-so-brilliant solution to our Infra Team, and they came up with a better idea. My colleague thought that it would be nicer if I could just do away with the “out of sync” error altogether. Since I can already pinpoint the cause of the trouble, then why not remove it for good? I came up with half the solution. It was the quicker one to implement and didn’t require much from their end either.

Distinguishing the faulty id from the list of objects from solr vs those that were from the db, it paved the way for me to simply remove these ids from the checking. It was half the solution because (hint, hint.. I may be doing this next time when I have time) I could actually delete the certain indexed element from solr instead of simply removing it from solr’s items on hand. This “full” solution could actually bring forth other complications since you’d have to deal with what models were concerned and what fields will solr need to look at, etc.

And so.. the half solution that I did was to clean up the elements on hand for solr. This snippet is found in acts_as_solr/lib/parser_methods.rb.

 def reorder(things, ids)
    ordered_things = Array.new(things.size)

    unless things.size == ids.size
      (ids - (things.collect{|x| x.id})).collect{|missing| ids[ids.index(missing)] = nil}
      ids = ids.compact
    end

    raise "Out of sync! Found #{ids.size} items in index, but only #{things.size} were found in database! Remove #{(ids - (things.collect{|x| x.id})).to_sentence}." unless things.size == ids.size

    things.each do |thing|
      position = ids.index(thing.id)
      ordered_things[position] = thing
    end

    ordered_things
  end

The first four lines above the “out of sync” message is what is critical. It will attempt to remove the missing object from the items that solr will return. If all else fails, then it will be displaying the “out of sync” error, but would still be displaying the ids that were causing the problem.

Its quick, but not dirty. It works, but will not really guarantee that your problem will go away permanently. I suggest you do a complete reindex of your whole data. Or better yet, whatever was causing it, just make sure that there are no direct database deletion of any data so that solr will always remain in sync with your database.

Also there is an alternative option if you dont like the above.
Why just don’t MySQL decide your ordering.

A small snippet you need to change in

acts_as_solr/lib/parser_methods.rb

 def find_objects(ids, options, configuration)
      result = if configuration[:lazy] && configuration[:format] != :ids
        ids.collect {|id| ActsAsSolr::LazyDocument.new(id, self)}
      elsif configuration[:format] == : objects
        conditions = [ "#{self.table_name}.#{primary_key} in (?)", ids ]
        find_options = {:conditions => conditions}
        find_options[:include] = options[:include] if options[:include]
        if self.connection.adapter_name =~ /mysql/i
          find_options[:order] = "FIELD(#{self.table_name}.#{primary_key}, #{ids.join(',')})"
          result = self.find(:all, find_options)
        else
          result = reorder(self.find(:all, find_options), ids)
        end
      else
        ids
      end

      result
    end

In the above method it will check if the adapter is mysql then it will fetch the latest result list from the DB which will not cause the “Out Of Sync” issue.

Hope this helps.

Run acts_as_solr in JRuby in background mode

Posted by Bhushan G Ahire | Posted in JRuby, Rails, ruby | Posted on 12-07-2010

0

When you try and run rake solr:start or rake solr:stop it uses Kernel.fork to spawn of a child process. However in JRuby this is disabled by default due to concurrency issues. And this prevents your solr rake tasks from running under a JRuby environment…

The Problem

When you try and run rake solr:start or rake solr:stop it uses Kernel.fork to spawn of a child process.
However in JRuby this is disabled by default due to concurrency issues. There is an option to enabling fork (jruby -J-Djruby.fork.enabled=true)
within JRuby but it is experimental and as the warning says, “WARNING: fork is highly unlikely to be safe or stable on the JVM.” as it can cause all sorts of weird and wonderful side-effects.

The Solution

The solution therefore is to simply alter the solr:start and solr:stop tasks to use Kernel.exec instead.
To do this find the solr rake tasks usually in {RAILS_ROOT}/vendor/acts_as_solr/lib/tasks/solr.rake. In the start task all that is required is to comment out the start of the fork block. so you only have the exec method call,
This will not start the solr process in the background. To do so you just have to add “&” at the end of the exec command, which causes the jar file to run in background mode.

task :start do
      require "#{File.dirname(__FILE__)}/../../config/solr_environment.rb"
      begin
        n = Net::HTTP.new('127.0.0.1', SOLR_PORT)
        n.request_head('/').value

      rescue Net::HTTPServerException #responding
        puts "Port #{SOLR_PORT} in use" and return

      rescue Errno::ECONNREFUSED #not responding
        Dir.chdir(SOLR_PATH) do
            exec "java #{SOLR_JVM_OPTIONS}
-Dsolr.data.dir=#{SOLR_DATA_PATH} -Djetty.logs=#{SOLR_LOGS_PATH}
-Djetty.port=#{SOLR_PORT} -jar start.jar &"
          sleep(5)
          File.open("#{SOLR_PIDS_PATH}/#{ENV['RAILS_ENV']}_pid", "w"){ |f| f << pid}
          puts "#{ENV['RAILS_ENV']} Solr started successfully on #{SOLR_PORT}, pid: #{pid}."
        end
      end
    end

and in the end task just comment out the begining of the fork block so you are left with

  task :stop do
    require "#{File.dirname(__FILE__)}/../../config/solr_environment.rb"
      file_path = "#{SOLR_PIDS_PATH}/#{ENV['RAILS_ENV']}_pid"
      if File.exists?(file_path)
        File.open(file_path, "r") do |f|
          pid = f.readline
          Process.kill('TERM', pid.to_i)
        end
        File.unlink(file_path)
        Rake::Task["solr:destroy_index"].invoke if ENV['RAILS_ENV'] == 'test'
        puts "Solr shutdown successfully."
      else
        puts "PID file not found at #{file_path}. Either Solr is not running or no PID file was written."
      end
  end

you should now be able to run your solr rake tasks under jruby with out any problems..

Paginating multiple models using will_paginate on the same page

Posted by Bhushan G Ahire | Posted in Rails, ruby | Posted on 13-04-2010

0

The will_paginate plugin makes pagination for your models in Ruby on Rails ridiculously simple. However sometimes you’ll find yourself wanting to paginate over two or more models on a single page. For instance, you might want to display a list of users and administrators on a single page along with a pager for each model (assuming that users and administrators are stored in separate tables).

Controller code

The code is pretty simple, except that I am specifying the page to show to be equal to params[:user_page] and params[:administrator_page] respectively. Since we are allowing the ability to page over two models, we need two separate parameters to determine which page of users or administrators to show.

@users = User.paginate(:page => params[:user_page], :per_page => 10)<br/>
@administrators = Administrator.paginate(:page => params[:administrator_page], :per_page => 10)

View code

In the view all we need to do is make sure to set the param_value to the correct value to indicate to the plugin that we want to use a different parameter name for the page. The default is simply called ‘page’, but we need to make sure to use ‘user_page’ and ‘administrator_page’ instead for the two different models.

<%= will_paginate @users, :param_name => 'user_page' %><br/>
<%= will_paginate @administrators, :param_name => 'administrator_page' %>

That’s it, you should now be able to page through your users and administrators on the same page.

List of useful rake tasks for Rails…

Posted by Bhushan G Ahire | Posted in Rails, ruby | Posted on 25-03-2010

0

rake cache:clear
# Clears all cached pages
rake db:bootstrap
# Loads a schema.rb file into the database and then loads the initial database fixtures.
rake db:bootstrap:copy_default_theme
# Copy default theme to site theme
rake db:migrate
# Migrate the database through scripts in db/migrate. Target specific version with VERSION=x
rake db:schema:dump
# Create a db/schema.rb file that can be portably used against any DB supported by AR
rake db:schema:load
# Load a schema.rb file into the database
rake db:bootstrap:load
# Load initial database fixtures (in db/bootstrap/*.yml) into the current environment's database.  Load specific fixtures using FIXTURES=x,y
rake db:fixtures:load
# Load fixtures into the current environment's database.  Load specific fixtures using FIXTURES=x,y
rake db:sessions:clear
# Clear the sessions table
rake db:sessions:create
# Creates a sessions table for use with CGI::Session::ActiveRecordStore
rake db:structure:dump
# Dump the database structure to a SQL file
rake db:test:clone
# Recreate the test database from the current environment's database schema
rake db:test:clone_structure
# Recreate the test databases from the development structure
rake db:test:prepare
# Prepare the test database and load the schema
rake db:test:purge
# Empty the test database
rake deploy
# Push the latest revision into production using the release manager
rake diff_from_last_deploy
# Describe the differences between HEAD and the last production release
rake doc:app
# Build the app HTML Files
rake doc:clobber_app
# Remove rdoc products
rake doc:clobber_plugins
# Remove plugin documentation
rake doc:clobber_rails
# Remove rdoc products
rake doc:plugins
# Generate documation for all installed plugins
rake doc:rails
# Build the rails HTML Files
rake doc:reapp
# Force a rebuild of the RDOC files
rake doc:rerails
# Force a rebuild of the RDOC files
rake edge
# freeze rails edge
rake log:clear
# Truncates all *.log files in log/ to zero bytes
rake rails:freeze:edge
# Lock to latest Edge Rails or a specific revision with REVISION=X (ex: REVISION=4021) or a tag with TAG=Y (ex: TAG=rel_1-1-0)
rake rails:freeze:gems
# Lock this application to the current gems (by unpacking them into vendor/rails)
rake rails:unfreeze
# Unlock this application from freeze of gems or edge and return to a fluid use of system gems
rake rails:update
# Update both configs, scripts and public/javascripts from Rails
rake rails:update:configs
# Update config/boot.rb from your current rails install
rake rails:update:javascripts
# Update your javascripts from your current rails install
rake rails:update:scripts
# Add new scripts to the application script/ directory
rake remote_exec
# Execute a specific action using the release manager
rake rollback
# Rollback to the release before the current release in production
rake show_deploy_tasks
# Enumerate all available deployment tasks
rake stats
# Report code statistics (KLOCs, etc) from the application
rake test
# Test all units and functionals
rake test:functionals                 
# Run tests for functionalsdb:test:prepare
rake test:integration
# Run tests for integrationdb:test:prepare
rake test:plugins                     
# Run tests for pluginsenvironment
rake test:recent
# Run tests for recentdb:test:prepare
rake test:uncommitted                 
# Run tests for uncommitteddb:test:prepare
rake test:units
# Run tests for unitsdb:test:prepare
rake tmp:cache:clear
# Clears all files and directories in tmp/cache
rake tmp:clear                        
# Clear session, cache, and socket files from tmp/
rake tmp:create                       
# Creates tmp directories for sessions, cache, and sockets
rake tmp:pids:clear                   
# Clears all files in tmp/pids
rake tmp:sessions:clear               
# Clears all files in tmp/sessions
rake tmp:sockets:clear
# Clears all files in tmp/sockets
rake update_dialog_helper
# Copies the latest dialog.js to the application's public directory

Setup Capistrano to deploy Rails application on Amazon EC2 with Git

Posted by Bhushan G Ahire | Posted in capistrano, git, Rails, ruby, Security, Subversion | Posted on 17-02-2010

0

1: Create a new Rails app – we’ll call is ‘deploytest’

$ rails deploytest
$ cd deploytest

2: Create a local Git repository for it

$ git init
$ git add *
$ git commit -a -m 'initial commit'
$ git status

3: Create a couple of Capistrano files

$ capify .

4: Edit config/deploy.rb

# The name of your app
set :application, "deploytest"
# The directory on the EC2 node that will be deployed to
set :deploy_to, "/var/www/apps/#{application}"
# The type of Source Code Management system you are using
set :scm, :git
# The location of the LOCAL repository relative to the current app
set :repository,  "."
# The way in which files will be transferred from repository to remote host
# If you were using a hosted github repository this would be slightly different
set :deploy_via, :copy

# The address of the remote host on EC2 (the Public DNS address)
set :location, "ec2-xxx-xxx-xxx-xxx.compute-1.amazonaws.com"
# setup some Capistrano roles
role :app, location
role :web, location
role :db,  location, :primary => true

# Set up SSH so it can connect to the EC2 node - assumes your SSH key is in ~/.ssh/id_rsa
set :user, "root"
ssh_options[:keys] = [File.join(ENV["HOME"], ".ssh", "id_rsa")]

The only account on a default EC2 instance is root. You probably want to create a second user that is responsible for your application.

5: Copy your SSH public key to your EC2 node

$ scp -i ~/my-ec2-keypair ~/.ssh/id_rsa.pub root@ec2-xxx-xxx-xxx-xxx.compute-1.amazonaws.com:/root/.ssh/authorized_keys2

NOTE the filename authorized_keys2 – not authorized_keys!!

6: Setup the EC2 node for Capistrano deployment.
From your LOCAL machine, not the EC2 node:

$ cap deploy:setup

7: Finally, deploy your application

$ cap deploy

You will see lots of output and with this dummy application some of those will report errors/warnings. Don’t worry about that for now.

8: Check that the Deployment was successful
Connect to the EC2 node with SSH the regular way, cd to the app directory and check that everything is there. If that is all working then you are ready to deploy a real application and add custom tasks for managing the database, restarting the server etc.

Bear in mind that Capistrano add new ‘releases’ of your software in separate directories and symlinks the ‘current’ directory to the latest. So the root of your deployed application is the ‘current’ subdirectory.

Hope this will help you setting up your ec2 instance with capistrano.

Send SMS from Ruby On Rails application using web service, SOAP API

Posted by Bhushan G Ahire | Posted in Rails, ruby | Posted on 15-01-2010

0

SOAP4R is a Ruby library for accessing Web Services via SOAP. Recently I had a chance to explore SOAP4R. Here’s how to get started with it.

Installation

Although Ruby 1.8.x comes with SOAP4R in its standard library, it is an old, buggy version. I highly recommend using the latest gem (1.5.8 as of the this update). It has one dependency, httpclient.

gem install soap4r --include-dependencies

Service

There are many services available to send SMS but I prefer to use,

MailServe-SMS

MailServe-SMS, text messaging service, is everything you need for fast, no-frills, no-fuss text messaging. A quick and easy way to send SMS.

Let’s explore these further.

Method 1: Read the WSDL at run-time

 

require "soap/wsdlDriver"
wsdl = "http://sms.qlc.co.in/smsapi.wsdl"
driver = SOAP::WSDLDriverFactory.new(wsdl).create_rpc_drive

 

A single call to a driver factory reads the WSDL file, and creates a driver class for you to use, complete with the methods defined by the service. What if your service requires authentication? The driver inherits methods from httpclient, so you can specify its options as you would for httpclient:

Once driver is get initialised you need to call SMS sending API i.e. SendSMSRequest.

driver.SendSMSRequest("username", "password", "sender_no", "from_no", "message")

 

Once This will return you response 200 SMS sent successfully on success else if the information submitted was wrong then 500 Information submitted was incomplete.

 

Method 2: Generate classes from WSDL

SOAP4R installs a command-line utility called ‘wsdl2ruby’ which can generate a client or server.

Coming soon…..

Paypal Instant Payment Notification in Rails with Active Merchant

Posted by Bhushan Ahire | Posted in Amazon EC2, paypal, Rails, ruby | Posted on 05-08-2009

3

Active Merchant makes it extremely simple to use Paypal IPN. Here is a simple guide for getting IPN up and running.

Sign up for a Paypal sandbox account

Paypal provides a sandbox environment that mimics their production environment, with the exception that it doesn’t actually process the transactions. This is extremely useful for development and testing. It allows you to create multiple fake accounts and generate bank accounts and credit cards. More information can be found on Paypal’s Testing Instant Payment Notification page.

Unfortunately, I’ve signed up for two different developer accounts and I’ve had trouble logging in with both of them. I’ve tried resetting my password, but I still can’t log in. Fortunately, I already have my sandbox accounts set up and don’t really have a need for it (except to write this guide).

Create a Personal account and add a credit card

After you sign up for your developer account, create a personal sandbox account and add a credit card.

Create a Business account and add a checking

Next, create a business sandbox account and add a checking account.

Install the money gem

sudo gem install money

Install the Active Merchant plugin

script/plugin install http://activemerchant.googlecode.com/svn/trunk/active_merchant

Create a form that submits to Paypal

Include ActiveMerchant::Billing::Integrations in a controller to add Active Merchant’s helpers.

class PaymentsController < ApplicationController
  include ActiveMerchant::Billing::Integrations

  def create
    @enrollment = current_user.enrollments.find(params[:id])
  end
end

In the view, use payment_service_for to create a form that submits to Paypal to process the payment.

<% payment_service_for @enrollment.id, PAYPAL_ACCOUNT,
        :amount => @enrollment.course.deposit, :currency => 'USD',
        :service => :paypal do |service|

    service.customer :first_name => @enrollment.student.first_name,
        :last_name => @enrollment.student.last_name,
        :phone => @enrollment.student.phone,
        :email => @enrollment.student.email
    service.billing_address :city => @enrollment.student.city,
        :address1 => @enrollment.student.street,
        :state => @enrollment.student.state,
        :country => 'USA',
        :zip => @enrollment.student.zip
    service.item_name "#{@enrollment.course.program} Deposit"
    service.invoice @enrollment.invoice.id
    service.tax '0.00'

    service.notify_url url_for( :o nly_path => false, :action => 'notify')
    service.return_url url_for( :o nly_path => false,
        :controller => 'account', :action => 'show')
    service.cancel_return_url url_for( :o nly_path => false,
        :controller => 'account', :action => 'show') %>

    <!-- display payment summary here -->

    <%= submit_tag 'Make Payment' %>
<% end %>

The code above refers to the constant PAYPAL_ACCOUNT, which I define in environment.rb. I also set Active Merchant to use test mode, which directs it to use Paypal’s sandbox:

unless RAILS_ENV == 'production'
  PAYPAL_ACCOUNT = 'sandboxaccount@example.com'
  ActiveMerchant::Billing::Base.mode = :test
else
  PAYPAL_ACCOUNT = 'paypalaccount@example.com'
end

Create an action that processes the IPN

After the above form submits to Paypal and the user makes a payment, Paypal will post data about the transaction to your server. Set up an action to receive the post:

  def notify
    notify = Paypal::Notification.new(request.raw_post)
    enrollment = Enrollment.find(notify.item_id)

    if notify.acknowledge
      @payment = Payment.find_by_confirmation(notify.transaction_id) ||
        enrollment.invoice.payments.create(:amount => notify.amount,
          :payment_method => 'paypal', :confirmation => notify.transaction_id,
          :description => notify.params['item_name'], :status => notify.status,
          :test => notify.test?)
      begin
        if notify.complete?
          @payment.status = notify.status
        else
          logger.error("Failed to verify Paypal's notification, please investigate")
        end
      rescue => e
        @payment.status = 'Error'
        raise
      ensure
        @payment.save
      end
    end
    render :nothing => true
  end

Depending on the model for your application, this action will obviously look different. The important part is that you pass the raw post data from the request to Paypal::Notification.new, and call notify.acknowledge to connect back to Paypal to verify the data.

Enable IPN

Lastly, log into the business account that you created above, go to “Instant Payment Notification Preferences” in your profile, and set the URL that Paypal should post back to after payments. (Note: this needs to be a publicly accessible URL.)