Pages

Wednesday, September 28, 2011


How to change the data type for a given columns using Migrations


I have a users database table. There's an attribute, 'admin' which reflects if a given user is an administrator or not. It's now time to implement features in the application which would act based on whether a user is an administrator or not. While we could have got away with the admin attribute being an integer of either zero or one, setting it as a boolean data type would be better suited to the nature of the attribute being used.

1) generate a new migration

[rails3]$ rails generate migration change_data_type_for_admin_in_users_table
      invoke  active_record
      create    db/migrate/20110927235341_change_data_type_for_admin_in_users_table.rb


2) edit the migration file and use the change_column() method. This method does allow options to
set the default value of the attribute. In this case, I set it to false such that all new users are not administrators by default
Notice that I deleted the self.down method definition. This is because in the case of a db rollback, nothing needs to be done to this attribute.
I would expect the user database table to be dropped and that's it.


class ChangeDataTypeForAdminInUsersTable < ActiveRecord::Migration  def self.up        change_column(:users, :admin, :boolean, :default => false)  endend



3) run the migration


[rails3]$ rake db:migrate
==  ChangeDataTypeForAdminInUsersTable: migrating =============================
-- change_column(:users, :admin, :boolean, {:default=>false})
   -> 0.3049s
==  ChangeDataTypeForAdminInUsersTable: migrated (0.3052s) ====================
s_table.rb$ git add db/migrate/20110927235341_change_data_type_for_admin_in_user


4) done

Tuesday, September 20, 2011

How to include devise test helpers in your rails 3 controller specs (rspec2)
including devise test helpers in your controllers would mean that you could use devise's helpers to sign_in, confirm! and so forth. For more info on the test helpers in devise, look in the devise documentation page.

Two ways:

1) put the following contents in a file, spec/support/devise.rb

RSpec.configure do |config|
 config.include Devise::TestHelpers, :type => :controller
end


2) put them in spec/spec_helper.rb

RSpec.configure do |config|
 config.include Devise::TestHelpers, :type => :controller
 config.extend ControllerMacros, :type => :controller # I'm using factory girl here
 config.use_transactional_fixtures = true
end

Sunday, September 18, 2011

Good summary of collections

http://www.adp-gmbh.ch/ora/plsql/coll/index.html

associative arrays

  1. think of them as hashes (like in perl)
  2. the keys can be string or int which means they need to be unique. See http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14261/collections.htm#BABFACFA
nested tables
- no upper bound


varrays
-when declaring one, you'll need to define the upper bound
Triggers in pl/sql

Similar to :before, :after filters that are in ruby on rails, triggers in pl/sql are there to perform operations when certain events occur. This still sounds vague.

Take for example, we have a customer db table.

It would be helpful for us to have a trigger which runs when a deletion, insertion, update or  update on certain columns occurs to the customer db table. A typical action would be to log each action, timestamp and a comment into an audit dbtable for audit purposes to track the changes to the customer db table.

Format/syntax:

CREATE [OR REPLACE] TRIGGER <trigger name>
  {BEFORE|AFTER}
  {INSERT|UPDATE|DELETE| UPDATE OF <column list> ON <desired db table's name>
  [FOR EACH ROW]

  [WHEN  .... ] -- this is where we place more filters if we want the trigger to go to work when either an insertion/deletion/update or update on certain columns of a db table. For example, we only want to have the trigger work on Deletion statements issued on customer records for  customers who reside in 'state' = 'Melbourne, Victoria, Australia'
  [DECLARE]
      -- fit all the declaration statements here
  BEGIN

   [EXCEPTION]
 
END <trigger name>;
Packages in PL/SQL

Packages have objects (they own them as tables own their columns).
Objects can be (not limited to):

  1. packages
  2. functions
  3. variables
Think of a package in pl/sql as a logical grouping. For example, if there's a students package, it may have::
  • a package variable that reflects if the study term is open or not
  • procedures that process time table allocations ( does not need to return any value and runs after work hours in batch mode) 
  • functions that return statistics of students. For example, counts of local, interstate and overseas students in a given semester
Example:

CREATE OR REPLACE PACKAGE BODY students_pkg
IS 

   term_active  ....

   PROCEDURE allocate_timetables
   .....

   FUNCTION get_local_students_count
       ....
   RETURN count NUMBER;
       ....

   FUNCTION get_overseas_students_count
       ....
   RETURN count NUMBER;
       ....

   FUNCTION get_interstate_students_count
       ....
   RETURN count NUMBER;
       ....

  BEGIN 
     -- initialisation codes live here
     -- assign default values to objects here
     -- execute whatever logic/code here
  END students-pkg;


Outside the package, we use it...



DECLARE
    ...

BEGIN

    IF students_pkg.terms THEN
       DBMS_OUTPUT.PUT_LINE( "Number of local students in this term is " || students_pkg.get_local_students_count );
    END IF;

EXCEPTION
      ...
END;



Functions, procedures and their parameters - General concepts

The differences between functions and procedures are:



1) Functions need to be part of an expression (ie. assignment) whilst procedures can be called by themselves



Function:



DECLARE

    v_student_name students.name%TYPE

BEGIN

   
      v_student_name :=  get_student_name_function( 'student_id' => 1020283 );

END



Procedure:



   remove_graduated_students( :current_year => 2011, :current_campus => 'Clayton' );



2) Functions MUST return value(s) whilst procedures WILL NEVER EVER return anything



Format:



FUNCTION <name>

   RETURN return_datatype

IS

   ---declare all variables here

BEGIN

[EXCEPTION]

END [<name>];



3) format

FUNCTION:

FUNCTION <name>

 [

    (args)

 ]

   RETURN return_datatype

IS

   ---declare all variables here

BEGIN

[EXCEPTION]

END [<name>];



PROCEDURE:

PROCEDURE <name> 

 [

    (args)

 ]

IS

   ---declare all variables here

BEGIN

[EXCEPTION]

END [<name>];

Parameters:

 In the world of pl/sql, there are 2 types of parameters:
a) formal - this is the name of the parameter that is known to callers of the function/procedure. b) actual - the actual values that are represented by the formal parameters
Thinking in terms of a perl hash, formal parameters are keys whilst the actual parameters are the values.
Example:
FUNCTION get_total_sales 

     (   
   
          company_id IN company.id%TYPE,
     
         status          IN company.status%TYPE 
 
    ) 

  RETURN NUMBER

IS   v_total_sales NUMBER;

data type    ….  

 RETURN v_total_sales;

END 
DBMS_OUTPUT.PUT_LINE "The total sales is " || get_total_sales( :company_id => 21232, :status => 'trading' )

actual parameters: 21232 and 'trading'
formal parameters: company_id and status


Modes for parameters:

 The following modes are used when you define a procedure/function's parameters in its header section.

IN         - can only be read. Think of them as constants that cannot be mutated. This is the default mode pl/sql will assume when none is specified in your functions/procedures
OUT      - can only be written to (ie. assigned)
IN OUT  - can be read and written to. This is useful in the especially for procedures which would do processing and alter the values of certain variables as it does not return any values.

Advice: best to be clear in your parameters definition in the headers of your procedures/functions


PL/SQL Cursors



There are a few types of cursors but over here, I'm going to cover 2 main types of cursors:

a) implicit cursors

- oracle creates implicit cursors each time you run a select INTO , delete, insert or update statement. Note that they are all single row operations
- you are using an implicit cursor if the sql you are running is either select, insert or update
- the sql is being run immediately and OPEN,FETCH,CLOSE are not needed (and must not be done)
- Since implicit cursors deal with only exactly one row, error handling is simplifier to two conditions:
a) when there's no rows returned
 -- WHEN NO_DATA_FOUND THEN (syntax) 
b) when there's more than 1 row returned
 -- WHEN TOO_MANY_ROWS THEN (syntax)

-cursor attributes can also be used:
a) %FOUND,
b) %NOTFOUND
c) %ROWCOUNT
d) %ISOPEN


 Example:

DECLARE

    v_title book.title%TYPE

BEGIN

  SELECT title
  INTO v_title
  FROM book
  WHERE id = 1000;

END


b) explicit cursors

- this type of cursor is created by defining the cursor (based on the format below)
DECLARE CURSOR <cursor name>  [ (parameters) ]
   [ RETURN argument(s) ]

IS
  --sql stmt
BEGIN
  -- this is when you do your for-loop OR manual open, fetch, check for exceptions, processing and close
END

END

An example of return would be :

CURSOR get_current_employee
   RETURN employee%ROWTYPE
IS
   SELECT
      *
   FROM
      employee
   WHERE
      status = 'current'
BEGIN
   …
END


Observe that in comparison with implicit cursors, the sql is being defined in the "IS" section and it needs to be managed manually (ie. use for loop OR  fetch, open , check and close).


------


Basic way of working with a cursor - declare , open, fetch data and close it.

   OPEN <cursor_name>
       [( <parameter_value_1, <parameter_value_2>,... <parameter_value_N> )]; 

   LOOP
     -- loop until you manually EXIT;
    END LOOP;

    FETCH <cursor_name> INTO <variable_name_1>, <variable_name_2>,... <variable_name_N>;
    CLOSE <cursor_name>; 
    EXIT;


Better way (optimised) - use a for loop:


   DECLARE

    CURSOR <cursor name> ( <arg1> IN <existing column>%TYPE )
       --put sql here. It can use <arg1>

     -- put all variable declarations here

    BEGIN

        FOR item IN <cursor name> LOOP
             <cursor name>(arg1);
       END LOOP;

    END
With the FOR-LOOP, you will not need to worry about doing the required operations: OPEN, FETCH and CLOSE as it's handled by the for-loop.

It is also good practice to use a loop label which you would sandwich between '<<' and '>>'.
Example:
<< get_student_cohorts >>

Explicit cursor attributes:

1) %NOTFOUND-Within an explicit cursor, we can check if it's returned any data or not by using the %NOTFOUND method.
2)  %FOUND - returns true if the current cursor's fetch does bring back a valid row (not empty)
3) %ROWCOUNT - number of rows being fetched to date. This is useful when you're fetching rows out using a for loop and want to find out what iteration it is. After each fetch, the value of this variable will be incremented
4) %ISOPEN - checks if the current cursor is open or not
5) %BULK_ROWCOUNT, %BULK_EXCEPTIONS  - for use with FORALL statements


To use it, append it to the cursor name after it's been opened.
Example:

DECLARE CURSOR get_employee_id ( employee_name AS varchar2 )
IS
   SELECT id
   FROM employee
    WHERE name = employee_name

   v_employee_name varchar2 := 'Gordon Yeong';
   v_employee_id employee.ID%TYPE;

begin
   open get_employee_id(v_employee_name);

   fetch  get_employee_id
      into v_employee_id;

   if get_employee_id%notfound then
     v_employee_id := 92; -- this is how I set a default value in case the cursor does not return anything
   end if

   close get_employee_id;

    exception
      when OTHERS then
         raise_application_error( -20001, SQLERRM || ' on select of employee id' )
    end;

end


Saturday, September 17, 2011

Snippets on my rails 3 ujs study.

Once I get the hang of things, I should look to publishing a proper tutorial/article on how UJS works with Rails 3.

For the moment, these are just notes to self.


For javascript responses (ie. use of ajax), rails will render whatever is inside the 
<controller>/<action>.js.erb file.
This corresponds with the explicit respond_to :js {} block within each controller method.


If the desired action.js.erb file is to be rendered,
1) the action in the controller method must call it explicitly by having a .js block.

Example:

  # DELETE /parts/1
  # DELETE /parts/1.xml
  def destroy
    @part = Part.find(params[:id])
    @part.destroy
    respond_to do |format|
      format.html { redirect_to(parts_url) }
      format.xml  { head :ok }
    format.js   {
      # it could be blank but we explicitly tell rails to render the destroy.js.erb file's contents
    }
    end
  end

2) the javascript codes that does the fading out of a deleted object which used to reside in application.js will be moved to action.js.erb (after all, we want to put action specific javascript code into their own js.erb file)

$('.delete_part').bind('ajax:success', function() {
$(this).closest('tr').fadeOut();
});


BUT if we wanted to have the javascript codes (that do the fading) on an application javascript level (application.js.erb), 
1) the action method in the controller DOES NOT NEED to explicitly return via :js 
(ie. no need for the :js block)
2) no action.js.erb file required

Thursday, September 08, 2011

Yesterday, a good friend of mine approached me for some opinion on new tyres for his RX8 with a profile of 225/45/18 all round.


It seems like I'm always looking for tyres every year be it for myself or someone else. So, I decided to document my findings are countless of after work hours looking at forums, contacting my contacts in the tyre trade and looking at my old notes of my previous tyres.


The RX8 is not going to see any track days so the requirements would be

  • good wear ( I would say on average, 280 )
  • good grip in the wet
  • low noise
  • traction rated at least 'A" ( looking at AA)
  • temperature at 'A'

Shipping from the US

As tirerack.com is the best place to get tyres, the problem is that it takes time to have the tyres shipped from the US to Australia. It will benefit people who are buying:
  1. spares, or
  2. in advance, or
  3. them for a very low price (well, most Australian tyre shops have high mark ups except for my contacts')

Options considered

  •  Hankook Evo S1 K107
        Tyre plus in Glen Waverly, Vic wants $380 for this. My contact's getting them in for $300 per corner ;) 


           Review from
  1. Tyretest.com - pretty good
  2. EvolutionOz.net - by an ex Evo IX owner quoting, "I am using this tyre pretty good.My tyre shop says customer who ran eagle F1's on his porsche (regularly tracked) tried a set and claims the handling was comparable, except it lasts longer due to the compound"
  •  Hankook Evo S1 K110 (winner)
           Tyre plus in Glen Waverly, Vic wants $330 for this. With tirerack.com quoting "280 AA A" for the required profile, it looks good.


Got pretty good reviews on
http://www.tirerack.com/tires/tires.jsp?tireMake=Hankook&tireModel=Ventus+V12+evo+K110
http://www.modified.com/tech/modp-0904-hankook-ventus-v12-evo-k110-tire-review/index.html
http://www.1010tires.com/tires/reviews/Hankook/Ventus+V12+Evo+(K110)

From http://www.hankooktyre.com.au/iDrive/ (look under "Street"), they have a Road Hazard replacement guarantee for 1 year


  • Kumho KU31
    Price: $1020 for all 4 from OTR motorsports 
    This thing has a high tread wear (380) which is close to my friend's current tyres. Higher thread wear mean that it will last longer but will give way to grip earlier than a tyre of lower thread wear
  • Bridgestone RE002
    Price: $379 per tyre (before my contact puts in the discount)
    I have the RE001 on both the Evo, Lexus and my friend’s Mazda 3. Excellent street tyre which has no noise and great grip. I would think that the RE002 which superscedes the RE001 to be as good if not better
              The concern I have here are:
              1. the 17" RE001 for my Evo were made in Japan
              2. the 16" RE001 for our Lexus IS250 and my friend's mazda 323 were made out of Japan ( I think it was korea)
             With the recent earthquakes in Japan, tyres made in Japan are lesser and of course, with less supply, comes more demand. As Bridgestone's website is pretty much a promo site, they do not disclose any threadwear rating NOR would they disclose where they were made   :(

Options dismissed
  1. Advan AD08 - too aggressive thread pattern although I could get them for $376.18
  2. Michelin Pilot Sport 3 - a little overkill for a street car in capabilities and price ($500+)
  3. Bridgestone S001 - priced at $429 per tyre (before I ask for discount). This is one class higher than the RE002 and it's a bit over kill for my friend.

References

So I can get Michelin PS3s 235/40/18 for $279 each.


The Bridgestone Re001 Adrenalin is around $369 per tyre.


So I'm thinking the Michelin PS3. Reviews have been good.


Any recommendations on tyre deals here in Brisbane (northside)?



"I went away from the Adrenalins based on price and went for the Falken 452's. I am now on my second set and each time have got them for around $240 each for the standard 225/45/18.
I have found them to have great wet and dry grip and produce a lot less noise than the Bridgstone's.
Got around 40K out of the first set, but wheel alihnment was poor and I scrubbed out one side. Anyway 40K from RX8 tyres is better than what most people told me I would get."