Skip to main content

Mobomo webinars-now on demand! | learn more.

Rails’s named_scopes are great, and the ability to chain named_scopes together are even better. I am sure many of us have done something like this before:

   User.active.live_in('Virginia') 

That would give you the active users who live in the state of Virginia. You would define something like these in your User model:

   named_scope :active, lambda { |status| { :conditions => { :status => status } } }   named_scope :live_in lambda { |state| { :conditions => { :state => state } } } 

One recent client project has a model where the database table has 30-something columns. Doing these named_scopes became tedious and repetitive. That makes it not fun anymore.

So Dave Pranata and I wrote the searchable_attributes Rails plugin so we could stop worrying about explicitly defining these named_scopes once and for all.

To use it, you just install it like any other Rails plugin:

 script/plugin install git://github.com/rayvinly/searchable_attributes.git 

The plugin will automatically define a bunch of named_scopes for you if you include this in your model:

 class User < ActiveRecord::Base   searchable_attributes end 

You can then search on your models with something like this:

   def index     @users = User.with_first_name_equal('Gilbert').with_last_name_equal_if_not_null('Arenas').with_address_like('Main Street').with_age_greater_than(25).with_state_equal(['VA', 'MD', 'DC']).with_position_end_with('Guard').with_salary_within_inclusive((100..10000)).with_injured_equal(true).with_join_date_greater_than_or_equal_to(5.years.ago)   end 

Take a look in the plugin’s lib/searchable_attributes.rb for a list of automatically defined named_scopes.

Categories
Author

PayPal is an easy-to-use tool for making payments and ActiveMerchant is a great Ruby library for payment processing. A lot of us already have PayPal accounts for eBay and it works great in auctions, where you pay just once. However, ActiveMerchant currently does not support recurring billing through PayPal.

One requirement of a recent project was to charge for a monthly or yearly subscription plan, via PayPal. Therefore, I did some Googling and found that Jon Baker has already extended ActiveMerchant to add this functionality using PayPal’s Name-Value Pair (NVP) API. However, as Cody Fauser pointed out, the NVP API was taken out from ActiveMerchant, so I had to implement that with PayPal’s SOAP API.

First, download this file and put it in vendor/plugins/active_merchant/lib/active_merchant/billing/gateways/. Use the GitHub from now because maintaining a separate file download is too troublesome. Alternatively, there’s a fork on GitHub at http://github.com/rayvinly/active_merchant/.

In your controller, after a user selects one of your subscription plan, the form goes to the checkout action:

   def checkout     response = gateway.setup_agreement(:description => description, :return_url => return_url, :cancel_return_url => cancel_return_url)     redirect_to gateway.redirect_url_for(response.token)   end

This redirects the user to PayPal so he can login and read the description you provided. After he confirms, he is redirected back to your application’s return_url, which I set it to be the complete action below. If he cancels, he is redirected back to the cancel_return_url. You can set cancel_return_url to be the plan selection page where he can choose a different plan.

If he confirms, here’s the complete action:

   def complete     token = params[:token]     response = gateway.create_profile(token, :description => description, :start_date => start_date, :frequency => frequency_in_months, :amount => amount_in_dollars, :auto_bill_outstanding => true)   end      # Save this profile_id in your transactions table.  This is used to cancel/modify the plan in the future.     profile_id = response.params["profile_id"]      if response.success?       flash[:notice] = "Your PayPal account was successfully set up for the <strong>#{description}</strong> payment plan."       redirect_to login_path     else       flash.now[:notice] = "There was a problem setting up your PayPal account for the <strong>#{description}</strong> payment plan"       render cancel_url     end   end

I default the frequency to be in months and turn on auto_bill_outstanding because that is what I need, but you can look at the file you downloaded and the PayPal documentation to see what other options are available.

If the user wants to cancel the payment plan, a cancel action would look like:

   def cancel     response = gateway.cancel_profile(paypal_profile_id, :note => 'Payment plan was canceled by user')     flash[:notice] = 'You have successfully canceled your membership'   end

paypal_profile_id is the profile_id you saved in the complete action from above. Keeping this profile_id is very handy.

That’s it. Enjoy making money!

Categories
Author

Badger is a simple Rails plugin that creates photo badges. A site often allows its
users to upload a profile image. A profile image is just that, an image
resized to fit in a predefined space to show up in the user’s profile.

With Badger, you can have something prettier – a badge that shows the user-
uploaded image on top of another image that identifies the user as a part of
the community. We have company badges, security badges, so why not web
badges to have your users show off his/her affection for your site?

Badger works by accepting cropping parameters of the overlay image in a hash
(x1, y1, width, height), which is used to crop the overlay image. It then
resizes the cropped image to the size specified by composite_width and
composite_height in badger.yml. Finally, it places the resized image on top
of the background image at location specified by composite_x and composite_y
in badger.yml. The resulting image is saved back to either the filesystem or
Amazon S3, using attachment_fu.

Badger requires the attachment_fu plugin, ImageMagick, and MiniMagick. Also,
the JavaScript Image Cropper UI can be used to obtain the cropping parameters
from the users.

Configuration

When this plugin is installed , the badger.yml will be copied to the config
directory. You need to specify the following:

  1. background : filename of the background image, searching from public/images
  2. composite_x : top left corner of the overlay image location in x
  3. composite_y : top left corner of the overlay image location in y
  4. composite_width : width of the overlay image in pixels
  5. composite_height : height of the overlay image in pixels

For example, I want to overlay an image on top of a background image
(badge.jpg). The box for the overlay image should be 30 pixels in width and
20 pixels in height, and it should appear at (x, y) = (60, 80) of the
background image. My badger.yml then looks like:

  development:     background: badge.jpg     composite_x: 60     composite_y: 80     composite_width: 30     composite_height: 20

Example

In the model that you use to store attachments:

  class Photo < ActiveRecord::Base     has_attachment :content_type => :image,                    :storage => :s3,                    :max_size => 1.megabytes,                    :resize_to => '320x200>',                    :thumbnails => { :thumb => '100x100>' },                    :processor => :MiniMagick     validates_as_attachment     has_badge :storage => :s3   end

In the controller:

  def create_my_awesome_badge     @photo = Photo.find(params[:id])     # params[:crop_coord] is a hash with indexes x1, y1, width, height     @photo.create_badge(params[:crop_coord])   end

Improvements Needed

Please feel free to submit patches for bug fixes and improvements.
Specifically, I would like to:

1. Use something nicer than system(“convert blah…”), but couldn’t get it
to work. I don’t think Minimagick supports compositing images, so
RMagick may have to be used, but is it worth the heavy memory
consumption?

2. Make it more flexible (i.e. accept background image and composite
params dynamically instead of in badger.yml). Maybe pass them in the
call to create_badge?

Categories
Author

There have been several methods to deploy an Ruby on Rails application. Until recently, the most popular is to run Apache and proxy balance to multiple Mongrel instances that are running simultaneously.

Passenger, developed by Phusion, is the new kid entering the Rails deployment market. Everyone has been using the Apache PHP module for years and deploying a PHP applications is a snap. This has not been possible with Rails until Passenger. It is extremely easy, and you can still use Capistrano to automate deployment. I will show you how I get it to work on Ubuntu.

sudo gem install passenger passenger-install-apache2-module 

Update: Phusion just released Passenger 2.0 RC 1. You can download this version and do gem install passenger-1.9.0.gem instead. But I had an error compiling it on Mac OS X Leopard. hongli pointed me to use the version from GitHub that has the fix and it works like a charm. Thanks Phusion guys.

To get it from GitHub:

git clone git://github.com/FooBarWidget/passenger.git 

I created a separate /etc/apache2/mods-available/passenger.load and it contains the following:

For 1.0.5:

LoadModule passenger_module /usr/local/lib/ruby/gems/1.8/gems/passenger-1.0.5/ext/apache2/mod_passenger.so RailsSpawnServer /usr/local/lib/ruby/gems/1.8/gems/passenger-1.0.5/bin/passenger-spawn-server RailsRuby /usr/local/bin/ruby 

For the GitHub version (Of course the path will look different depending on where your git clone is):

LoadModule passenger_module /home/rlaw/downloads/passenger/ext/apache2/mod_passenger.so PassengerRoot /home/rlaw/downloads/passenger PassengerRuby /usr/local/bin/ruby 

I then tell Apache to load the Passenger module:

a2enmod passenger 

Now, I create a virtual host configuration for one of my Rails app in /etc/apache2/sites-available/myapp:

<VirtualHost *:80>   ServerAdmin webmaster@myapp.com   ServerName myapp.com    DocumentRoot /home/deploy/apps/myapp/current/public    <Directory /home/deploy/apps/myapp/current/public>     Options FollowSymLinks     AllowOverride None     Order allow,deny     Allow from all   </Directory>    LogLevel warn   ErrorLog /var/log/apache2/myapp/error.log   CustomLog /var/log/apache2/myapp/access.log combined </VirtualHost> 

I then restart Apache:

sudo /etc/init.d/apache2 reload 

When you need to restart your application because you have changed some code that Rails does not reload in production, just do:

touch /home/deploy/apps/myapp/current/tmp/restart.txt 

I have not tried their Ruby Enterprise Edition yet. They claim substantial memory and speed improvement at RailsConf 2008, so it will be interesting to see how that develops.

Categories
Author
1
Subscribe to Rubyonrails