Tuesday, 6 August 2013

:: Rails Cropping Demystified ::

Design choice:
Solution1
Carrierwave, S3, Fog, Jquery File Upload, Jcrop + RMagick / MiniMagick

Advantage Server Side:

  1. Support for all browser versions
  2. Single stream file upload and can be coupled with resque for heavy loading.


OR

Solution2
S3,  Jquery File Upload, HTML5  cropping + CORS

Advantage client side and CORS Upload

  1. More compute optimized
  2. Server band-with as files are directly sent to S3
  3. Best suited to multiple file uploads.


Solution 1: Server side cropping
Setup:
Carrierwave has an excellent readme guide on how to install and setup these are the important bits:

GemFile:
gem 'carrierwave'
gem "fog"
gem "rmagick"

Initializer carrierwave.rb (Constants as defined in config.yml)
CarrierWave.configure do |config|
  config.fog_credentials = {
    :provider               => 'AWS',                        # required
    :aws_access_key_id      => APP_CONFIG['s3']['aws_access_key_id'],                        # required
    :aws_secret_access_key  => APP_CONFIG['s3']['aws_secret_key'],                        # required
    :region                 => 'us-east-1',                  # optional, defaults to 'us-east-1'
    # :host                   => 's3.domain.com',             # optional, defaults to nil
    # :endpoint               => 'https://s3.domain.com:8080' # optional, defaults to nil
  }
  config.fog_directory  = APP_CONFIG['s3']['bucket_name']                      # required
  config.fog_public     = false                                   # optional, defaults to true
  config.fog_attributes = {'Cache-Control'=>'max-age=315576000'}  # optional, defaults to {}
end
Uploader Model: 
  storage :fog setup for s3 done!
class UserAvatar < CarrierWave::Uploader::Base
  storage :fog
  include CarrierWave::RMagick

  version :thumb do
  process :manualcrop
  process :resize_to_fill => [200,200]

  end
  version :large do
    process :manualcrop
    process :resize_to_fit => [800, 800]
  end

  def extension_white_list
    %w(jpg jpeg gif png)
  end

 def manualcrop
 return unless model.cropping?
 manipulate! do |img|
   img = img.crop(model.crop_x.to_i,model.crop_y.to_i,model.crop_w.to_i,model.crop_h.to_i)
 end
 end

  def default_url
    ActionController::Base.helpers.asset_path([version_name, "default.png"].compact.join('_'))
  end
  
    def cache_dir
    "#{Rails.root}/tmp/uploads"
  end
end

Important Bits in the User Model
class User < ActiveRecord::Base
  
attr_accessor  :crop_x, :crop_y, :crop_h, :crop_w

   mount_uploader :avatar,UserAvatar

  after_update :reprocess_profile, :if => :cropping?

  def cropping?
    !crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank?
  end

  def profile_geometry
    img = Magick::Image::read(self.avatar.url).first
    {:width => img.columns, :height => img.rows }
  end

private

  def reprocess_profile
    self.avatar.recreate_versions!
  end

Controller:
  def crop_photo
    user = current_user
    user.crop_x = params[:settings]["crop_x"]
    user.crop_y = params[:settings]["crop_y"]
    user.crop_h = params[:settings]["crop_h"]
    user.crop_w = params[:settings]["crop_w"]
    user.save
    current_user.save
    redirect_to my_fav_path
  end
View Page:

Crop / Adjust your new picture

<%= image_tag(current_user.photo_url(:large), :id => "target", :alt => "Avatar") %>

Preview

<%= image_tag(current_user.photo_url(:large), :class => "jcrop-preview", :alt => "Preview") %>
<%= form_for :settings, :url => crop_photo_settings_path do |f| %> <% for attribute in [:crop_x, :crop_y, :crop_h, :crop_w] %> <%= f.hidden_field attribute, :id => attribute %> <% end %>
<%= button_tag "Crop", :class => 'submit' %>
<% end %>
Alternative Approach

As an alternate to using RMagick gem you can use MiniMagick gem and in that case the Uploader Model method to crop can change to
 def manualcrop
 return unless model.cropping?
    manipulate! do |img|
      size = model.crop_w << 'x' << model.crop_h
      offset = '+' << model.crop_x << '+' << model.crop_y
      img.crop("#{size}#{offset}") # Doesn't return an image...
      img = yield(img) if block_given?
      img #
    end
  end
And the image geometry can be fetched using
  def profile_geometry
    MiniMagick::Image.open(self.avatar.url)[:width]
  end



Solution 2: 
Client Side Cropping using HTML5 Canvas.

This is the alternative solution for re-sizing images and uploading them using Jquery File Uploader















File Uploader:
$('#fileupload').fileupload({
    url: '//jquery-file-upload.appspot.com/',
    dataType: 'json',
    // Enable image resizing, except for Android and Opera,
    // which actually support image resizing, but fail to
    // send Blob objects via XHR requests:
    disableImageResize: /Android(?!.*Chrome)|Opera/
        .test(window.navigator && navigator.userAgent),
    imageMaxWidth: 800,
    imageMaxHeight: 800,
    imageCrop: true // Force cropped images
})
https://github.com/blueimp/jQuery-File-Upload/wiki/Client-side-Image-Resizing


In order to upload directly to S3 you can use CORS - Cross Origin Resource Sharing Cross domain uploads A live Example and usage of this solution can be found here: Direct Upload S3 Jquery File Upload




Now that I have listed both approaches here are few design decisions worth considering..

Enjoy!!



References:
http://railscasts.com/episodes/182-cropping-images-revised
http://stackoverflow.com/carrierwave-crop-specific-version


Thursday, 31 January 2013

Junit Tests OpenJPA and Websphere EJB3.0 + DB2 9.0

Junit Tests OpenJPA and Websphere EJB3.0 DB2

For those of us with IBM and their beau software aka tools.

Heres what i had to do to configure the websphere Enterprise container to fire JUnits to persist EJB’s over OpenJPA 1.2.3

You will have to Override persistence.xml file settings to start using JDBC over JTA.

Here’s a snippet from my Abstract Test Case class
Most interesting one is the TransactionMode which is correlated to using

prop.getProperty("jdbc.password"));
  configOverrides.put("openjpa.TransactionMode", "local");

Result:
Allows you to reuse a single persistence unit as Websphere has a bug wherein it would not allow multiple persistence Units to load together. Even worse a separate one for test also fails

Just one more thing
Remember to commit or close connections in the end so:

 @After
 public void tearDown() throws Exception {
  entityTransaction.commit();
  super.tearDown();
 }
Complete Abrstract Template
public abstract class AbstractTestCase extends TestCase {

 private Logger logger = LoggerFactory.getLogger(AbstractTestCase.class);
 
 private EntityManagerFactory emf;

 protected EntityManager em;
  
 protected EntityTransaction entityTransaction = null;
 
 @BeforeClass
 public static void setUpBeforeClass() throws Exception {
 }

 /**
  * @throws java.lang.Exception
  */
 @AfterClass
 public static void tearDownAfterClass() throws Exception {
 }

 /**
  * @throws java.lang.Exception
  * Finally close connection
  * ensures always
  */
 @Before
 public void setUp() throws Exception{
  PropertyConfigurator.configure(AbstractTestCase.class.getResource("log4j.properties").getFile());
  logger.debug("Initialise Setup");
  Properties prop = new Properties();
   
     try {
               //load a properties file
      prop.load(AbstractTestCase.class.getResourceAsStream("TestSuite.properties"));
 
     } catch (IOException ex) {
      ex.printStackTrace();
        }

  logger.debug("Loading properties file from TestSuite.properties");

  Map configOverrides = new HashMap();
       
  configOverrides.put("openjpa.jdbc.Schema", prop.getProperty("jdbc.schemaname"));    
  configOverrides.put("openjpa.ConnectionURL", prop.getProperty("jdbc.connectionurl"));
  configOverrides.put("openjpa.ConnectionDriverName", prop.getProperty("jdbc.connectionDriverName"));
  configOverrides.put("openjpa.ConnectionUserName", prop.getProperty("jdbc.username"));
  configOverrides.put("openjpa.ConnectionPassword", prop.getProperty("jdbc.password"));
  configOverrides.put("openjpa.TransactionMode", "local");
  logger.debug("Finished load of properties file from TestSuite.properties");

     emf = Persistence.createEntityManagerFactory("Entity", configOverrides);
     
     em = emf.createEntityManager();
     entityTransaction = em.getTransaction();
     entityTransaction.begin();
  super.setUp();
 }
 
 /**
  * @throws java.lang.Exception
  */
 @After
 public void tearDown() throws Exception {
  logger.debug("Initialise tearDown");
  em.close();
  emf.close();
  logger.debug("All connections closed Successfully");
 }

Monday, 10 September 2012

Predicates Java Pattern to save Database Queries


The apache commons CollectionUtils has a collection of very interesting methods to allow using Ruby style collection filtering, rejecting,collecting injecting ...

I found the select method quite useful to avoid multiple DB Queries with a DB similar predicate and syntactically clean.

Problem:

I wish to append a List/Collection to my Bean based on some predicate (orgId="mcool").

Solution:


Use of Predicates:

Basic Example:

BeanPropertyValueEqualsPredicate  predicate = new BeanPropertyValueEqualsPredicate("orgId", value);


 BeanPropertyValueEqualsPredicate predicate = new BeanPropertyValueEqualsPredicate("orgId", value);  
 Collection<Organisation> organisationCool = CollectionUtils.select(allOrgansations, predicate);  
allOrganisations above was fetched with a single sql select.

bean.setOrganisationsList(organisationCool );

Caution: 

Needless to say but typically small datasets and only for cases where in memory selection is beneficial.



More about the Ruby style collection handling in this blog here
http://ytoh.wordpress.com/2008/07/17/ruby-like-collection-handling-in-verbose-java-collectionutils/


Wednesday, 27 June 2012

Loading Jquery with RichFaces 4.0 inbuilt File

There is a one line script at the head required to ensure the order of scripts and to allow you to use native Jquery

<h:outputScript name="jquery.js" />

ie6 Jquery Select Labels and IE6

Yes if you have to do it theres this sinppet that resolves an Ie6 bug wherein the selectLabels do not hold their values when clicked from their corresponding label.

More Here: http://support.microsoft.com/kb/314279

 function ie6(){  
  return jQuery.browser.msie && /msie 6\.0/i.test(navigator.userAgent);  
 }  
 function fixIe6SelectLabel(){  
  if(ie6()){  
   jQuery('select').bind({  
    focusin: function(event) {  
     try {  
     var eSrc = event.target;  
     if ( eSrc ) eSrc.tmpIndex = eSrc.selectedIndex;  
     } catch(e) {}  
    },  
    focus: function(event) {  
     try {  
    var eSrc = event.target;  
    if ( eSrc ) eSrc.selectedIndex = eSrc.tmpIndex;  
    } catch(e) {}  
    }  
   });  
  }  
 }  

Sunday, 17 June 2012

Start

Revamping my Tech Space.

Will be posting interesting tech bits here.

watch this space for ruby, rails, java and similar.