Como usar Ruby on Rails con Oracle PL/SQL


Enrique Vargas

@kike77

linkedin

23 de Diciembre de 2015

Una pequeña guía con los pasos básicos necesario para hacer una instalación y pruebas de objetos Oracle con Ruby on Rails en un servidor Linux.

Al ver el título se estarán preguntando ¿en qué estaba pensando este sujeto al utilizar Oracle con Ruby on Rails teniendo base de datos de código abierto como PostgreSQL o MySQL?, supongo es como pasar al lado oscuro de la fuerza ¿no creen?, pero la verdad es que todos en algún momentos hemos trabajado con tecnologías con licencias pagas. Antes de enamorarme de Rails trabajé durante años con tecnología Oracle, por lo tanto mientras más trabajaba con Rails más crecía mi curiosidad de cómo integrarlo con Oracle y sus distintos objetos PL/SQL (que fue lo que más utilicé) y obviamente probar su desempeño.

 

Finalmente, luego de varios proyectos, he tenido la oportunidad de integrarlo gracias al requerimiento de un cliente. Sabemos que estas licencias son caras por lo tanto antes de este proyecto solo había podido probarlo en versiones Express gratuitas (Oracle XE), por lo que ésta se convirtió en la oportunidad perfecta para medir desempeño en una base de datos Oracle 11g.

 

Lo que nos llevó a realizar esta implementación fuera de un entorno experimental fue la necesidad del cliente de contar con  un portal web capaz de conectarse con objetos Oracle llamados paquetes (Packages), dentro de estos se encuentran definidos los procedimientos y funciones que contiene lógica de su negocio, los cuáles han ido trabajando durante años y se ajustan a las necesidades de su aplicación principal la cual fue desarrollada en Oracle Forms (capa de presentación) y PL/SQL (capa de negocios), ésta es utilizada en su operativa diaria pero sólo para uso interno y trabajando directamente dentro de su intranet.

 

Aquí la palabra clave es la “reutilización”. Todos aquellos procesos que existen se podrían reutilizar cambiando básicamente la forma de cómo invocarlos y los tipos de parámetros a utilizar, el propósito es ejecutar procesos complejos con datos simples y/o obtener colecciones de datos (Objetos Cursor) que son realmente lo más importante para visualizar multi-registros.  Ahora se estarán preguntando ¿por qué no hacerlo entonces con un Framework JS, por ejemplo AngularJS ó Backbone?,  la respuesta es simple: además de que nos encanta Ruby on Rails (como lo habrán notado) también tenemos pensado utilizar ActiveRecord para procesos que no sean específicamente de negocios sino de aplicativo, por ejemplo autenticación, sesiones de usuarios, roles, menús dinámicos, configuración de sistema, parametrización, etc., convirtiendo nuestro proyecto en una aplicación híbrida.

 

También es posible dejar nuestra aplicación Rails sólo para manejar visualización de componentes HTML, mejor dicho solo utilizar la vista y el controlador, pero ya en ese punto en mi opinión cuestionaría el uso de Ruby on Rails, pero supongo es cuestión de gustos.

 

Partiendo de esta explicación configuraremos nuestra aplicación para poder trabajar con dos casos: conexión de procesos de negocios en lenguaje procedimental (PL/SQL) y nuestra capa de modelo (ActiveRecord) conectado directamente a Oracle.

 

Para esta prueba se utilizará un servidor con Linux Centos 7 64bits, con la versión 2.1.2 de Ruby y Ruby on Rails versión 4.2.4, no se instalará Oracle en el servidor ya que este se encuentra en otro servidor físico, la versión de Oracle es 11g pero en teoría esta misma configuración podría funcionar con un Oracle XE ejecutándose localmente.

 

La instalación de Ruby y RoR se les dejo a ustedes, ahora lo primero que tenemos que hacer es hacer una instalación del cliente Oracle, para esto tendremos que bajar los siguientes archivos de la página web de Oracle:

 

instantclient-basic-linux.x64-11.2.0.4.0.zip

instantclient-sdk-linux.x64-11.2.0.4.0.zip

instantclient-sqlplus-linux.x64-11.2.0.4.0.zip

 

Aquí solo debemos descomprimir los archivos en el lugar que nosotros lo necesitamos, en nuestro caso lo haremos en: /opt/oracle

 


mkdir /opt
mkdir /opt/oracle
cd /opt/oracle

 

Al hacer unzip de los archivos, nos generará una carpeta llamada “instantclient_11_2/” 

 

 
unzip instantclient-basic-linux.x64-11.2.0.4.0.zip 
unzip instantclient-sdk-linux.x64-11.2.0.4.0.zip 
unzip instantclient-sqlplus-linux.x64-11.2.0.4.0.zip 
cd /opt/oracle/instantclient_11_2

 

Creamos un link simbólico

 


cd instantclient_11_2
ln -s libclntsh.so.11.1 libclntsh.so

 

Cargamos la librería en nuestra variable de ambiente:

 


LD_LIBRARY_PATH=/opt/oracle/instantclient_11_2
export LD_LIBRARY_PATH

 

Para cargarla en cada sesión lo colocamos en el bash profile

 


vi ~/.bash_profile
LD_LIBRARY_PATH=/opt/oracle/instantclient_11_2
export LD_LIBRARY_PATH

 

Luego, para conectar Ruby con Oracle necesitamos una interfaz que nos haga las cosas fáciles. Para esto tenemos la gema ruby-oci8, asi q solo tenemos que ejecutar :

 


gem install ruby-oci8

y el resultado debe ser:

Fetching: ruby-oci8-2.2.1.gem (100%)
Building native extensions.  This could take a while...
Successfully installed ruby-oci8-2.2.1
1 gem installed 

 

Para probar que funcione con una conexión remota ejecutamos el siguiente comando:

 


ruby -r oci8 -e "OCI8.new('usuario', 'password', '101.0.0.178:1521/xe').exec('select \'test\' from dual') do |r| puts r.join(','); end"

 

IP Servidor remoto: 101.0.0.178

Puerto: 1521

SID: xe

 

Si la respuesta nos muestra  “test” significa que vamos por buen camino.

El siguiente paso es crear una aplicación Ruby on Rails para iniciar nuestras pruebas

 


rails new app_oracle
cd app_oracle

 

Dentro de nuestro Gemfile incluimos nuestros Gems necesarios para utilizar objetos Oracle.

 


#oracle
gem 'ruby-oci8'
gem 'ruby-plsql'
gem 'activerecord-oracle_enhanced-adapter'

 

El primero ya lo hemos instalado y sabemos para qué funciona, el segundo sirve para conectar con PL/SQL y demás objetos Oracle y el tercero es un adaptador ActiveRecord para conectarse a Oracle.

En config/initializers creamos el archivo oracle.rb y le incluimos lo siguiente:

 


plsql.activerecord_class = ActiveRecord::Base

 

Configuramos la base de datos, aquí es importante utilizar distintos usuarios para desarrollo y pruebas básicamente por que dentro de ellos se crean todos los objetos.  Esto se hace en caso de que no quieras perder tus datos de prueba.

 

 


config/database.yml

default: &default
  adapter: oracle_enhanced
  pool: 5
  timeout: 5000

development:
  <<: *default  

  database: //172.16.247.152:1521/xe   
  username: dev
  password: test

test:
  <<: *default
  database: //172.16.247.152:1521/xe   
  username: test
  password: test

production:
  <<: *default
  database: //172.16.247.154:1521/xe   
  username: prod
  password: test

 

Ahora probemos lo más básico, un scaffold para el mantenimiento de un modelo, en este caso HighScore será nuestro modelo

 

 


rails generate scaffold HighScore game:string score:integer
rake db:migrate 
rails s

 

Ponemos en funcionamiento el CRUD del modelo High Score, si todo sale bien deberías poder probarlo sin dificultad

http://localhost:3000/high_scores

 

Es tiempo de conectarnos con PL/SQL, para esto utilizaremos la gema ruby-plsq la cual nos da una serie de métodos para hacer conexiones a paquetes, procedimientos y funciones.  En la primera prueba definiremos un procedimiento  dentro un Package al cual le enviaremos una variable y devolveremos la misma variable con una concatenación de texto, algo bastante sencillo.

 

Primero se define el package specification:

 


create or replace package test_oracle is 
  procedure return_same_value(name IN varchar2, result OUT varchar2);
end test_oracle; 

 

Luego definimos el package body

 


create or replace package body test_oracle is

procedure return_same_value(name IN varchar2, result OUT varchar2) is
begin
  result := name|| ': Success' ;
end;

end test_oracle;

 

Ya devuelta en Rails, se crea una acción dentro del controlador HighScoresController

 


def get_info_from_oracle    
   @param_value = plsql.test_oracle.return_same_value(params[:name])[:result]  
end

Y se edita routes.rb

resources :high_scores do
   collection do
     get 'get_info_from_oracle'
   end  
end  

 

Creamos la vista views/high_scores/ get_info_from_oracle.html.erb

 


Hello parameter: <%=@param_value%> 

 

En el index se crea un enlace para la prueba views/ high_scores/index.html.erb

 


<%= link_to 'Test Oracle', get_info_from_oracle_high_scores_path(name:"Calling Oracle Procedure")%>

 

Si vemos el mensaje de “Success” significa que lo hemos logrado

 

 

Ahora para la siguiente prueba creamos en el mismo Package una función (Function) que nos devuelva un cursor que luego iteraremos en la vista. Aquí tenemos que usar un tipo de dato especifico (ref_cursor) en nuestro código PL/SQL.

 

 


Specification:

  function get_collection(name IN varchar2) return sys_refcursor;


Body:

  function get_collection(name IN varchar2) return sys_refcursor is
    l_cursor sys_refcursor;
  BEGIN
  
    open l_cursor for
      SELECT LEVEL, 'John' || LEVEL name, 'Domme' || LEVEL lastname
        FROM DUAL
      CONNECT BY LEVEL <= 5;
  
    return l_cursor;
  
  END;


 

Modificamos un poco la acción creada para incluir el llamado a la función PL/SQL:

 

 


def get_info_from_oracle    
    @param_value = plsql.test_oracle.return_same_value(params[:name])[:result]  

    plsql.test_oracle.get_collection(params[:name]) do |cursor|
       @values = cursor.fetch_all
    end 
end

 

También modificamos la vista para mostrar los datos provenientes de la función en views/high_scores/get_info_from_oracle.html.erb

 

 


<h1>Hello parameter: <%=@param_value%> </h1>
<table border="1">
	<tr>
		<th>
			ID
		</th>
		<th>
			Name
		</th>
		<th>
			Lastname
		</th>
	</tr>
	<%@values.each do |value|%>
		<tr>
			<td>
				<%=value[0]%>
			</td>
			<td>
				<%=value[1]%>
			</td>
			<td>
				<%=value[2]%>
			</td>
			
		</tr>
	<%end%>
</table>

 

Refrescamos el navegador y debemos ver nuestra tabla con valores provenientes del Cursor

 

Estas son las pruebas más básicas de conexión de objetos Oracle. Obviamente existirán procesos que por su complejidad deberán ser cambiados o tratados de una forma diferente, ya sea en Oracle y/o en Rails.

 

Espero que esta pequeña guía les haya ayudado, si tienen alguna duda o sugerencia no duden en escribirme.