Tuesday, December 25, 2012

PHP : mod_rewrite services

Experience working with PHP is really good. But really missed the way rails for handling urls dynamically.
Really feeling nice when I found the way to add such method for dynamic urls.
Just need to use mod_rewrite in .htaccess file for the respective project.

In the beginning didn't work as per the requirement.

After some research found the post enable-mod_rewrite-in-a-ubuntu-server which helped a lot for starting the mod_rewrite services.

By default it showed that mod_rewrite services are running but need some changes in the files which are as below.

Just need to run
sudo gedit /etc/apache2/sites-enabled/000-default
and in <Directory /> and <Directory /var/www/> section change
AllowOverride None
to
AllowOverride All
And then just
sudo /etc/init.d/apache2 restart


Friday, November 23, 2012

Collection : PHP : Insert Data Function

In my previous post we added select_tag function for views as a helper function. Now we will add some function for inserting record. The names of functions might be similar to rails methods. This becomes easy for me to keep it simple to remember.

Lets consider we have some products table with id, name and description columns. As I have kept id auto generated we will not add if field in form.

In new.php
  1. <form id="product" method="POST" action="create.php" >
  2.  <input name="product[name]" type="text" value="" />
  3.  <textarea rows="6" name="product[description]"> </textarea>
  4.  <input id="product_submit" type="button" value="Save" onclick="submit();" />
  5. </form>
Check View.

Now when you click on save button it should redirect you to create.php. For now just add
  1. <?php print_r($_POST); ?>

and you can see (in view) Check View.

If you need to validate the records you can just add some code before the function we are going to add.
But I will just keep it simple, so directly going to add it to the database.

In create.php
  1. require_once 'includes/php/active_queries.php';
  2. insert_record($_POST['product'], "products");
  3. header('Location: index.php');

I have added active_queries.php, with this we will insert the data in table.
  1. /**
  2.  * For Inserting a single record
  3.  * @param array $record <p>Array of single record</p>
  4.  * @param string $model <p>Table name in which you want to make changes</p>
  5.  */
  6. function insert_record($record, $model) {
  7.   $fields = count($record);
  8.   if ($fields > 0) {
  9.     $i = 0;
  10.     $k = 0;
  11.     $insert = "INSERT INTO " . $model . " (";
  12.     foreach ($record as $key => $value) {
  13.       $k++;
  14.       $insert .= $key;
  15.       if ($fields > $k) {
  16.         $insert .= ", ";
  17.       }
  18.     }
  19.     $insert .= ") VALUES (";
  20.     foreach ($record as $key => $value) {
  21.       $i++;
  22.       if (gettype($value) == 'string') {
  23.         $insert .= " '" . pg_escape_string($value) . "'";
  24.       } else {
  25.         $insert .= $value;
  26.       }
  27.       if ($fields > $i) {
  28.         $insert .= ", ";
  29.       }
  30.     }
  31.     $insert .= ")";
  32.     $result = pg_query($insert);
  33.     return pg_affected_rows($result);
  34.   }
  35. }

This function will run loop of array twice. First to get the keys and then the values. And then it will add the data to the database. Once the query runs successfully it will return to the index page as we have mentioned header location as index.php. And you can see the inserted records.
Check Form View and Check Index View.

Now what if we want to add multiple records in one form/ click. This is not required for now but this can be used in nested_attributes.

So I added multiple_products.php in which we will add multiple fields.
  1. <form id="products" method="POST" action="create.php" >
  2.   <input name="multiple" type="hidden" value="true" /><br />
  3.   <?php for ($i=1; $i<=3; $i++) { ?>
  4.     <i><?php echo $i ?>. Name :</i><br />
  5.     <input name="products[<?php echo $i ?>][name]" type="text" value="" />    
  6.     <i>Description :</i><br />
  7.     <textarea rows="6" name="products[<?php echo $i ?>][description]"> </textarea><br />
  8.   <?php } ?>
  9.   <input id="product_submit" type="button" value="Save" onclick="submit();" />
  10. </form>
Check View

We are not creating another php file to submit these products to database. We will use the same create.php for which I added hidden field as multiple=true

In active_queries.php
  1. /**
  2.  * For Inserting multiple records
  3.  * @param array $records <p>Array of records</p>
  4.  * @param string $model <p>Table name in which you want to make changes</p>
  5.  */
  6. function insert_multiple_records($records, $model) {
  7.   foreach ($records as $record) {
  8.     insert_record($record, $model);
  9.   }
  10. }

And in create.php need to change some code. Instead of
  1. insert_record($_POST['product'], "products");

change code to
  1. if(isset($_POST['multiple'])){
  2.   insert_multiple_records($_POST['products'], "products");
  3. }else{
  4.   insert_record($_POST['product'], "products");
  5. }
Check View.

This will just get all data in and will run in loop which will call previous function(insert_record()) for each record array.

So after redirecting to index page we can view the inserted to reqords . Check View.

You can download whole source from github

Monday, November 19, 2012

Collection : PHP : select_tag

Learning PHP is fun to me but really miss the available helpers in Rails.
For example form helpers like form/ select_tag/ text_field and others.

I dont know whether these are already available in php or not.
So I added one file as common_helper.php and that can be directly required or just added separate (module_name)_helper.php and require common_helper.php in the new file. I have added module wise view helpers, so that I can keep the common helper functions and module wise helper function separate from each other.

This function creates a HTML select with options and selected option. Parameters required are :
  1. $name as string (Name for select node).
  2. $options as array (Array for generating options).
  3. $selected as string or integer - optional - (Selected value or blank string).
  4. $css_class as string - optional - (Css class name(s)).
  5. $onchange as string - optional - (onchange functionality)
This will append the given name and class name to select tag. Selected value will be selected in options by comapering the $selected.
Options send to the helper function needs to be array.
For example:
1. array(1, 2, 3, 4, 5, 6, 7);
Or
2. array(array(0,"0 Hours"), array(1,"1 Hour"), array(2,"2 Hours"), array(3, "3 Hours"), array(4, "4 Hours"));
Still for making work this code you will need to pass first 2 mandatory parameters which are name and array of options which will create the option tags for select_tag.

  1. <?php 
  2.   /**
  3.   * Creates a HTML select with options and selected option.
  4.   * @param string $name <p>Name for select node</p>
  5.   * @param array $options <p>Array for generating options</p>
  6.   * @param string $selected <p>Selected value or blank string. Can be string or integer.</p>
  7.   * @param string $css_class <p>Css class name(s) as a string</p>
  8.   * <p>
  9.   * This will append the given name and class name to select tag.
  10.   * Selected value will be selected in options by comapering the $selected.
  11.   * Options send to the helper function needs to be array
  12.   * For example:
  13.   * 1. array(1, 2, 3, 4, 5, 6, 7);
  14.   * Or
  15.   * 2. array(array(0,"0 Hours"), array(1,"1 Hour"), array(2,"2 Hours"), array(3, "3 Hours"), array(4, "4 Hours"));
  16.   * </p>
  17.   */
  18.   function select_tag($name, $options, $selected="", $css_class="", $onchange=""){
  19.     $select_tag = '<select class="' . $css_class . '" name="'.$name;
  20.     if($onchange!=""){
  21.       $select_tag .= '" onchange="'.$onchange;
  22.     }
  23.     $select_tag .= '">';
  24.     $select_options = "";
  25.     foreach($options as $a) {
  26.       if(is_array($a)){
  27.         $ky = $a[0];
  28.         $val = $a[1];
  29.       }else{
  30.         $ky = $a;
  31.         $val = $a;
  32.       }
  33.       $select_options .= '<option value="' . $ky . '"';
  34.       if($selected==$ky){
  35.         $select_options .= ' selected="selected"';
  36.       }
  37.       $select_options .= '>' . $val . '</option>';
  38.     } 
  39.     $select_tag .= $select_options . '</select>';   
  40.     return $select_tag;
  41.   } 
  42. ?>

So now in view we can directly add in php tag

  1. <?php echo select_tag("select_name", array(), $selected_value, "classname", "onchange_javascript_function(this.value);"); ?>

In the project I am working on needed the onchange javascript function.
Its not mandatory. So you can just avoid or remove that from the code.

This code is available on github : php_common_helpers.git.

Friday, November 16, 2012

Collection : Javascript : Validating max length

There are some scripts/ functionalities which are commonly and mostly used in many web based applications. I would like to keep some in the collection with us. These might be simple for the readers but are very important to make a note of it.

In this post I will keep note of 1 javascript function which restricts the user by adding more than the specific limit for the input. Or validating the input for certain limit to enter.

In Javascript file
/* check_the_max_length(input, maxLen) 
  input  : need a id or class of input
  maxLen : IE do not accept maxlength so passing it as a parameter.
*/
function check_the_max_length(input, maxLen){
  var val = $(input).val();
  var left = maxLen - val.length;
  var remaining_points = left<=0 ? 0 : left
  $('#counter').html(remaining_points + '/' + maxLen);
  if (val.length >= maxLen) {
      $(input).val(val.slice(0, maxLen));
  }
}
Considering in view
<%= f.input :description, :as => :text, :input_html => {:style => "width:450px;"}
I added some javascript in views, as application's most of the views are on popups. In main view, where popups will get displayed, add following lines at bottom in script tag.
$('#description').live('keyup keydpress', function(e) {
  check_the_max_length('#description', 220);
});
As we read, for IE, maxlength was not working, there was another bug. while entering paragraphs it do no calculate properly. For example: if I enter 4 paras the count will show I still have 4 values to enter. But max length attribute actually do not let you enter the value. So removed the maxlength attribute from views for inputs and added slice method which will actually cut down the text to original required count. SO the above function is the solution for both bugs.

Monday, November 5, 2012

Postgres : Server Installation and settings

Postgres is one of the popular database server for Ruby on Rails Applications. This post takes us step by step, installation and some important settings.

Postgres 9.1.x installation steps
sudo add-apt-repository ppa:pitti/postgresql
sudo apt-get update
sudo apt-get install postgresql
This will install the postgresql to the server/ your local machine. If you just want to use it locally then these steps are enough. But if you also want to use this database available for other machines then you have to add the ip to pg_hba.conf for making availablity for that ip machine only.
There is also other option for making database available if your ip is not stable. which is as given below. But also suggested use this only if server is only for internal use. For this we need to make following changes in

/etc/postgresql/9.1/main/pg_hba.conf(find following and make changes as shown)
# TYPE  DATABASE     USER  ADDRESS        METHOD
  local all          all                  trust
  host  all          all   127.0.0.1/32   trust
And another to the same file
# IPv4 local connections:

# host  all          all   127.0.0.1/32   md5
  host  all          all   0.0.0.0/0      trust
other file to make changes is /etc/postgresql/9.1/main/postgresql.conf
port = 5432
listen_addresses = '*'
And then just restart the server.
sudo /etc/init.d/postgresql restart
Install pgAdmin3
sudo apt-add-repository ppa:flexiondotorg/postgres
sudo apt-get update
sudo apt-get install pgadmin3

Wednesday, October 24, 2012

Phonegap : adb command not found

As many people must have known that Phonegap is really nice for cross platform mobile app development. I installed android development environment via eclipse and downloaded the latest plugin of phonegap.

Tried out the sample application given in the plugin. It worked well in emulator but while running it on my sony ericsson x10i I faced some problems as I will say I am new to android development.

First my phone was not recognized by Android device chooser in eclipse. So I got that I will have to add rules file to users/udev/something folder. So I followed the dimitar.me. Also the information is given on developer.android.com.

Even for sony need to add sudo gedit /etc/udev/rules.d/51-android.rules and the required information in the file.

Now when I completed most of the steps command
adb devices
was not getting the output. But instead throwing the error as below.

Changed the permissions for the same file. I again run the adb command, but no positive result still the same in terminal and in eclipse. Finally restarted the pc and checked the command in terminal. Still the same error. But when I checked it in eclipse it showed the device chooser.

Thats it and the device detected and apk installed successfully.

Thursday, September 13, 2012

PHP : phpmyadmin not found

Recently I installed php on my pc. But got some trouble when I installed phpmyadmin and tried to view it in browser. It displayed as Not Found.



But after some research found that I will have to move the phpmyadmin folder from usr/share location to var/www
So I run the below command from terminal
sudo mv /usr/share/phpmyadmin /var/www/phpmyadmin
And phpmyadmin was running as required in browser.

Sunday, August 12, 2012

Rails 3 : APN on Rails

I got very excited when one of the colleague told me that he wants to send notifications to his apple devises(itouch/ iphone) through rails application I am working on.

So I got PRX/apn_on_rails and yekmer.posterous.com tutorial. And I started working after reading the required information. Wow..

But there was error as below in console while running devise creation command
ActiveRecord::StatementInvalid: PG::Error: ERROR:  relation "apn_bases" does not exist
LINE 4:              WHERE a.attrelid = '"apn_bases"'::regclass
                                        ^
:             SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
              FROM pg_attribute a LEFT JOIN pg_attrdef d
                ON a.attrelid = d.adrelid AND a.attnum = d.adnum
             WHERE a.attrelid = '"apn_bases"'::regclass
               AND a.attnum > 0 AND NOT a.attisdropped
             ORDER BY a.attnum

Well, this was not working.
Tried to find some solution, and got issue 38 for github.com/PRX/apn_on_rails.

After reading added
self.abstract_class = true
to apn/base.rb and restarted the server. It started working as required. It successfully created a devise information row in the table.

I thought the work is done(not really) and just need to run command
rake apn:notifications:deliver --trace
But it started throwing the error as below
** Invoke apn:notifications:deliver (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute apn:notifications:deliver
rake aborted!
SSL_connect returned=1 errno=0 state=SSLv3 read finished A: sslv3 alert certificate unknown
/home/supriya/.rvm/gems/ruby-1.9.3-p125/gems/apn_on_rails-0.3.1rails3/lib/apn_on_rails/libs/connection.rb:58:in `connect'
/home/supriya/.rvm/gems/ruby-1.9.3-p125/gems/apn_on_rails-0.3.1rails3/lib/apn_on_rails/libs/connection.rb:58:in `open'
/home/supriya/.rvm/gems/ruby-1.9.3-p125/gems/apn_on_rails-0.3.1rails3/lib/apn_on_rails/libs/connection.rb:23:in `open_for_delivery'
/home/supriya/.rvm/gems/ruby-1.9.3-p125/gems/apn_on_rails-0.3.1rails3/lib/apn_on_rails/app/models/apn/notification.rb:104:in `send_notifications'
/home/supriya/.rvm/gems/ruby-1.9.3-p125/gems/apn_on_rails-0.3.1rails3/lib/apn_on_rails/tasks/apn.rake:7:in `block (3 levels) in '
/home/supriya/.rvm/gems/ruby-1.9.3-p125/gems/rake-0.9.2.2/lib/rake/task.rb:205:in `call'
Again searched and got to know that there might be something to do with certificate generated. Ok, I and colleague discussed on this and he solved the same with generating new .pem file for this task. The certificate generation steps can be viewed at maniacdev.com/.

Replaced the same in my project and wow, that worked like a charm.

Tuesday, August 7, 2012

Rails 3 : WARN Could not determine content-length

One of the project I am working on, gets the call from iPhone application. Suddenly iPhone application stopped receiving the data. After checking the log I found the following error.


I searched and found the solution on stackoverflow.com : content-length-of-response-body and stackoverflow.com : unknown-warning-in-development-mode which was very informative. I added
gem 'thin'
and then
bundle install
After the whole process restarted the server. Error was vanished and iPhone application was running as per the requirement.

Wednesday, July 11, 2012

Ruby : Rails : Mixins and Refactoring

I really love coding and to try out new things whenever possible. Though it's not always possible to do what I want but, one should fully utilize the opportunity when it presents itself.

Refactoring, it's one of the best things you can do for your code. After refactoring code becomes more readable, more secure and if everything work out good then refactoring also results in better performance.

And I got the opportunity I have been looking for, the Job was "To do refactoring" for one of the projects I've been working on.

Considering the project is for stores, there were 2 models rewards and promotions for store. The only difference between two was rewards do have points and not for promotions.

In reward.rb
class Reward < ActiveRecord::Base
  belongs_to :store

  validates_presence_of :store_id
  validates_presence_of :points
  validates_presence_of :description
  
  validates_numericality_of :points, :greater_than => 0
  
  validates :valid_from, :date => {}
  validates :valid_to, :date => { :after_or_equal_to => :valid_from }, :allow_blank => true

  scope :first_scope, lambda{ # some code }
  scope :second_scope, lambda { # some code }
  scope :third_scope, lambda { # some code }
end
In promotion.rb
class Promotion < ActiveRecord::Base
  belongs_to :store
  
  validates_presence_of :store_id
  validates_presence_of :description
  
  validates :valid_from, :date => {}
  validates :valid_to, :date => { :after_or_equal_to => :valid_from }, :allow_blank => true
  
  scope :first_scope,  lambda{ # some code }
  scope :second_scope, lambda { # some code }
end
Except that every thing from validations to associations were same.
Got irritated for a while, but then was happy that I was going to change the code.

So I added a module in lib as rewards_promotions.rb and added following code to it.
module RewardPromotion
  def self.included(base)
#    base.extend(ClassMethods)
    base.class_eval do
      belongs_to :store

      validates_presence_of :store_id
      validates_presence_of :description

      scope :first_scope, lambda{ # some code }
      scope :second_scope, lambda { # some code }
    end
  end
end
Now I just removed the similar code from respective models(reward.rb and promotion.rb) and just added following line
  include RewardPromotion
Thats it. My work was completed and I just restarted the server and everything worked as before. Just that the code was at one place and I dont have to repeat the same code for both models.
This was done for other models too and the project is now really great.

For refactoring I also avoided using if !true conditions to unless true.

Also the major calculations were called directly in controller from model. Which was converted to private method and then called from other method.
Need to understand the important functionality needs to go in private methods and be called from other method rather that directly making it available for controllers.
Refactoring was fun for me to get the things out of controllers and shifting to model with a great security and performance.

Sunday, July 1, 2012

Revised: Sending mail through ruby on rails

I have previously added post Sending mail through ruby on rails for adding mail configuration to Ruby on Rails application. But as we know in rails 3 there are separate mailers and we dont have to add mailers in models folder any more.
So I though just to add revised code.

First I created a setup_mail.rb in config/initializers and added below code which is for my heroku app (sendgrid)
 ActionMailer::Base.delivery_method = :smtp
 ActionMailer::Base.smtp_settings = {
  :user_name => "send grid user name",
  :password => "sendgrid password",
  :domain => "your domain name",
  :address => "smtp.sendgrid.net",
  :port => 25,
  :authentication => :plain,
  :enable_starttls_auto => true
 }
Now just created user_mailer.rb in app/mailers and added a below code
=begin
  File Name : user_mailer.rb (UserMailer)
  Description : File added to hold mailer template actions for users.
  Date : 13/04/2012
  Dependencies : ActionMailer::Base
  Note : If file not in use please remove the method calls from the other controllers. Information for other controllers and actions is given above the respective actions.
=end

class UserMailer < ActionMailer::Base
  default :from => "info@testapp.com"
  # registration_confirmation(new_user_information) : 13/04/2012
  # This action is called from users_controller.rb in create action after successful user registration.
  # (Autoresponder Email on new Registration)Task
  def registration_confirmation(user)
    @user = user
    mail(:to => @user.email, :subject => "Registered with Test App") do |format|
      format.html
    end  
  end
end
Well, lots of comments. But I prefer to keep those for other programmers to know where the code is used or for what purpose. Thats really useful for me too to find the code quickly and reminding what the action does.

After setup files done, added 1 single line as below, in users_controller.rb in create action after the user is successfully stored.
UserMailer.registration_confirmation(@user).deliver
And at last created a user_mailer folder in app/views. Again in that created registration_confirmation.html.erb
  Hi <%= @user.name %>, 
  < br />< br />
  == Some Demo Text ==
  < br />< br />
  Thanks For Signing Up ==
  Test App Team
Thats it. We are done with mailer configuration and now just restart your server. For more information go through Action Mailer Basics from Guides Ruby On Rails.

Thursday, June 28, 2012

Rails 3 : Devise : Omniauth : Facebook authentication

Today most of the clients prefer to have option for authentication through social sites as facebook/ twitter. As working of one of the project I was also adding facebook authentication for client project. Now the first choice was ofcourse to see if Ryan added any video for facebook authentication.

And I was lucky to get #360 Facebook Authentication just added, when I was looking for it. But I also found that there were some issues with devise, as I was not able to login.

I mean the simple project authentication was working great as shown. But when devise comes in it was having some problems. So I searched for devise and omniauth. And I found that I ignored the devise was ominauthable. So now I followed the step suggested in devise : omniauth :overview
Made some changes in my project as given in the example.

GemFile
gem "omniauth-facebook"
And run bundle command.
In devise.rb at top
require "omniauth-facebook"
and just uncommented
config.omniauth :facebook, "APP_ID", "APP_SECRET"
and changed the app_id and secret as per my facebook app generated.
I faced OpenSSL error but also the solution is available in the same example. Just changed the above config line to
config.omniauth :facebook, "APP_ID", "APP_SECRET",
      {:scope => 'email, offline_access', :client_options => {:ssl => {:ca_file => '/usr/lib/ssl/certs/ca-certificates.crt'}}} 
Ofcourse app is running on heroku. Then added
:omniauthable
to the devise settings in user.rb (devise model)
In routes.rb I changed the previous single line
devise :users
to

devise_for :users, 
           :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }

added a migration file
class AddAuthFieldsToUser < ActiveRecord::Migration
  def change
    add_column :users, :provider, :string
    add_column :users, :uid, :string
    add_column :users, :oauth_token, :string
    add_column :users, :oauth_expires_at, :datetime
  end
end
run rake db:migrate to add the columns in users table and add same columns to attr_accessible in user.rb. After that created new folder in controllers as users where added omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def passthru
  render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
    # Or alternatively,
    # raise ActionController::RoutingError.new('Not Found')
  end

  def facebook
    # You need to implement the method below in your model (e.g. app/models/user.rb)
    @user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)

    if @user.persisted?
      flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
      redirect_to stores_path, :event => :authentication, :current_user => @user
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end
end
In user.rb just removed name as its not in my users table.
  def self.from_omniauth(auth)
    where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.oauth_token = auth.credentials.token
      user.oauth_expires_at = Time.at(auth.credentials.expires_at)
      user.save!
    end
  end
  
  def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
    user = User.where(:provider => auth.provider, :uid => auth.uid).first
    unless user
      user = User.create(  provider:auth.provider,
                           uid:auth.uid,
                           email:auth.info.email,
                           password:Devise.friendly_token[0,20]
                           )
      user.ensure_authentication_token!
      # added extra to create authentication token for user
    end
    user
  end
The main steps are done. Just need to restart the server and way to go with facebook authentication for my project.
The given wiki is also contains the example for OpenID and Google. Check the link given at the start. Hope that will solve your problem, as it solved mine.

Sunday, June 24, 2012

Javascript : Debugging Object

Debugging JavaScript with firebug and other available tools is pretty easy. But without it this may seem little complex. But while checking out for solution I really found very helpful function at breakingpar.com.

The solution they provided was really great to know what the object is containing. function dumpProps uses confirm prompts for debugging inner objects or main objects with our requirement.

Once we found what we want we can just click cancel for coming out the loop or keep continuing debugging.

Tuesday, May 22, 2012

Rails 3 : Timezone

While working on some project I really found one helpful post for timezone integration in my rails application.
Very easy and step by step explanation with code snippets at www.spilth.org, which helped me for quick integration.
Loved the post.

Saturday, April 21, 2012

Javascript : From - To Date Validation

Working with javascript(jQuery) and rails really makes me happy.
This time I wanted to add validation messages for from and to dates for nested attributes.

As per my previous post we have already seen the link_to_remove_association for adding confirmation message. But this time the situation is bit different. I want to add validation messages next to the fields of the nested attributes. So those validations run for each field.

Adding function and comparing 2 dates might not be much difficult but becomes tricky, because I am using simple_form_for which wraps in divs. So need to work around the after build html source for exact tree structure.

Well, the view code was something like below(haml file) and check the valid_from and valid_to date fields on which the javascript function needs to be added. So added a function call in onfocus and passed as a parameter input_from to the simple_form_for input.
.nested-fields
  %table.table-condensed
    %tbody
      %tr
        %td
          = f.input :name, :label => false, :input_html => {:class => 'name'}
        %td
          = f.input :description, :label => false, :input_html => {:class => 'description'}
        %td
          = f.input :valid_from, :as => :string, :label => false, :input_html => {:class => 'valid_from', :onfocus => "createDatePickerWithValidation(this, 'from', '#{t :selected_date_prior_to_today}', '#{t :selected_both_dates_prior_to_today}');"}
        %td
          = f.input :valid_to, :as => :string, :label => false, :input_html => { :class => 'valid_to', :onfocus => "createDatePickerWithValidation(this, 'to', '#{t :selected_date_prior_to_today}', '#{t :selected_both_dates_prior_to_today}');"}
        %td
          = link_to_remove_association "X", f, { :onClick => "confirm_for_remove_record(this, '#{t :confirm_for_remove_record}', 10)" }
        %td{:valign => "top"}
          %span{:style=>"display: none; color: #B94A48"}

In javascript file added below function (I usually create common.js).
/* createDatePickerWithValidation(field, type, field_message, combined_message)
*    Datepicker with warning messages for valid from and through dates 
*    warning_div :   is the div in which the warning message is displayed. 
*                    This needs to be searched dynamically for each record based 
*                    on the position of the clicked field.
*    other_field :   is the field seareched based on the clicked field. 
*                    If the clicked field is valid_from then other_field will be valid_to and vice versa.
*                    This is based on each records fields so that it will not conflict to other records.
*    field_message : is the combination of values of both the fields of each record.
*                    First value will always be the valid_from and other will be valid_to.
*/
function createDatePickerWithValidation(f, typ, message, both_fields_message) {
  $(f).datepicker({ dateFormat: 'yy-mm-dd',
    onSelect: function(date) {
      var todays_date = new Date();
      var input_date = new Date( date );
      var warning_div, other_field, field_message;

      if(typ=='from'){
        warning_div = $(f).closest("td").next().next().next().children();
        other_field = $(f).closest("td").next().children().children().children();
        field_message = date + " and " + other_field.val()
      }else{
        warning_div = $(f).closest("td").next().next().children();
        other_field = $(f).closest("td").prev().children().children().children();
        field_message = other_field.val() + " and " + date
      }
      /* Default warning message set */
      var msg = ""
      var other_field_date = new Date( other_field.val() );

      /* If both input dates are prior to today's date */
      if( todays_date > input_date && todays_date > other_field_date ){
        msg = field_message + both_fields_message
      }

      /* If clicked input is prior to todays date but other field is blank or after todays date */
      if( todays_date > input_date && ( todays_date <= other_field_date || other_field.val()=="" ) ){
        msg = date + message
      }

      /* If other field is prior to todays date but clicked input is blank or after todays date */
      if( todays_date > other_field_date && ( todays_date <= input_date || date=="" ) ){
        msg = other_field.val() + message
      }

      /* If message blank then remove the text and hide the div else add text and show div */
      if(msg==""){
        warning_div.html("").hide();
      }else{        
        warning_div.html(msg).show();
      }
    }
  });
  $(f).datepicker("show");
}
and in en.yml
  selected_date_prior_to_today: " date is prior to todays Date"
  selected_both_dates_prior_to_today: " dates are prior to todays Date"

Tuesday, April 17, 2012

Cocoon : link_to_remove_association : confirm box

Really happy when used the nathanvda/cocoon for adding nested model records.
This makes life easy. But only thing I missed was confirmation message.

We can actually add html_options to the method, but because of "remove_fields" class, this actually removes/ hides the fields on-click of the link.
So adding confirmation from html option was no use.

So for overriding, I added the following method in application_helper.rb. But you can also add it in new view helper class in lib and include it in application helper(just to keep it separate).
# Adding confirm message for association remove links.
# Original : f.hidden_field(:_destroy) + link_to(name, '#', :class => "remove_fields #{is_dynamic ? 'dynamic' : 'existing'}")
# Cocoon : ViewHelpers : link_to_remove_association
# link_to_remove_association(name, f, args={})
# name : to be displayed for link.
# f : form object
# args : defaults set to empty hash.
#        this holds the html options like onClick.
#        e.g : link_to_remove_association "name", f, { :onClick => "onclick();", onBlur => "onblur();" }
# extra_class : If no html options are passed from the view it will append 
#               the class to the class attribute of the link.
#               This will call the original functionality
# option_class : If class key added in view for styling purpose, this will add it to the args
# is_dynamic : is as per the original functionality. Lets us know if the records is new or existing.
def link_to_remove_association(name, f, args={})
  extra_class = (args.has_key?(:onclick) || args.has_key?(:onClick)) ? nil : "remove_fields"
  is_dynamic = f.object.new_record? ? 'dynamic' : 'existing'
  option_class = args.has_key?(:class) ? args[:class] : nil

  args.merge!({:class => "#{extra_class} #{is_dynamic} #{option_class}"})
  f.hidden_field(:_destroy) + link_to(name, '#this', args)
end
This worked out smoothly.
Now if I dont pass any option it will actually append the remove_fields class to the class option of the link.
This will run the original functionality of cocoon.
This is working for me as required functionality.

Now in javascript I have added function for confirming the removal.
So added
/* confirm_for_remove_record(field, confirm_message)
*  record : The whole row of the current clicked field.
*  record_input : method delete input.
*  class_name : added div class name for appending dialog.
*/
function confirm_for_remove_record(f, message, max_records){
  var record = $(f).closest(".nested-fields");  
  var record_input = $(f).prev();
  var class_name = "." + record_input.attr("id")

  /* Appending extra div for dialog box holder */
  $(f).parent().append( "<div class=" + record_input.attr('id') + ">" + message + "</div>" )
  
  /* Adding dialog box functionality to newly added div 
  *  If field is dynamic it will delete the entire div 
  *  else it will just hide and remove the class name ".nested_fields" from the div class attribute.
  *  If less than max_length then enable add_a_record link by removing the class "disable" from the anchor tag.
  */
  $( class_name ).dialog({
    draggable : false,
    resizable : false,
    modal : true,
    buttons : {
      "Remove" : function() {
        $( class_name ).dialog( "close" );
        $(record_input).val(1);
        
        if($(f).hasClass("dynamic")){
          $(record).remove();  
        }else{
          $(record).hide();
          $(record).removeClass("nested-fields");
        }

        if($(".nested-fields").length < 15 ){
          $("#add_a_record").removeClass("disabled");
        }        
        
      },
      "Don't Remove" : function() {
        $( class_name ).dialog( "close" );
      }
    }
  }).dialog( "open" );
}
And in view
link_to_remove_association "X", f, { :onclick => "confirm_for_remove_record(this, '#{t :confirm_for_remove_record}', 10)" }

Thursday, March 29, 2012

QA : 3 : Rails Resources

Question from Apoorv Parijat.

Right now, I'm learning rails which is so damn slow. I've read the theoretical part but creating a new rails app is a drag. Can you point me to any interesting resource?

Wednesday, March 21, 2012

QA : 2 : How to Unmount / Remove Engine?

Question from Pratik Jadhav.

In Rails 3 we have concept called as "Engine". As in Rails 4.0 vender/plugin are going to be removed, we have to use Engine's instead.
I have gone through many sites where they explain how we can create/mount new engine OR convert existing Rails App to engine.
But didn't found any link where they have explained about how to unmount Or remove engine.
can any one help me out with this??