viernes, 21 de diciembre de 2012

MongoDB en rails

Hay casos en los que una base de datos relacional no es la mejor opción. Si vamos a acceder siempre por una clave, y el objeto es bastante grande, es más conveniente usar una BBDD noSql.
En este caso, para Ruby on Rails, lo más extendido es usar MongoDB.

Configurar la aplicación es bastante sencillo. En el Gemfile metemos las gemas necearias. La principal es mongomapper: Si vamos a necesitar serializar objetos, también incluimos bson_ext:

# mongo db
gem "mongo_mapper"
gem "bson_ext"

Ahora tenemos que crear un fichero de configuración, y un initializer. En el de configuración (mongodb.yml, por ejemplo) meteremos la dirección, el nombre, ... de la base de datos MongoDB:

development:
  host: localhost
  port: 27017
  database: myapp-development

test:
  host: localhost
  port: 27017
  database: myapp-test

production:
  host: remote-host # (for me amazon ec2)
  port: 27017
  database: myapp-production

En el initializer (mongo.rb) creamos la conexión:

database_yaml = YAML::load(File.read("#{Rails.root}/config/mongodb.yml"))
puts "Initializing mongodb"

mongo_database = database_yaml[Rails.env]

MongoMapper.connection = Mongo::Connection.new(mongo_database['host'], mongo_database['port'])
MongoMapper.database = mongo_database['database']

if defined?(PhusionPassenger)
  PhusionPassenger.on_event(:starting_worker_process) do |forked|
    MongoMapper.connection.connect if forked
  end
end

Si sólo usamos modelos en mongoDB, debemos eliminar las referencias a ActiveRecord de la aplicación, ya que si no nos daría errores. En el fichero config/application.rb comentamos la línea donde pone "require 'rails/all'" y en su lugar ponemos:

require "action_controller/railtie"
require "action_mailer/railtie"
require "active_resource/railtie"
require "rails/test_unit/railtie"
require "sprockets/railtie"

Así evitamos que se requiera el AvtiveResource.

Ahora, creamos el modelo, mapeado por mongomapper:

class MyClase
  include MongoMapper::Document

  key :num_cosas, Integer, :default => 0
  key :fecha_cambio, Date

  key :nombre, String
end

Y con esto, al levantar la aplicación, podremos acceder a los objetos vía mongo-db

jueves, 20 de diciembre de 2012

Paralelización de bucles en Ruby

En ocasiones, tenemos que realizar un proceso Batch en Ruby. Por ejemplo, una tarea rake que tenga que hacer una carga de datos.
Si el volumen de datos es elevado, el proceso puede llegar a tardar demasiado. Por esto, hay veces que , dependiendo del tipo de proceso, se puede considerar la paralelización de alguno de los bucles de la tarea.

La forma más simple de hacerlo es la siguiente:

Incluimos la librería de hilos de Ruby:

require 'thread'

namespace :nstyarea do
  task :tarea_pesada => :environment do

#...


end

Ahora, vamos al bucle que queremos correr en paralelo. Por ejemplo, el procesado de un fichero con muchas líneas. Preparamos un array donde iremos guardando los hilos, y partiremos las líneas del fichero en tantos trozos como hilos queramos generar.

    NUM_HILOS = 4
    my_threads = []
    lineas_fichero = (fichero_entrada.readlines)
    lineas_fichero.in_groups_of(lineas_fichero.count / NUM_HILOS, false) do |lineas|

        my_threads << Thread.new do
            lineas.each do |line|
               begin

               # cuerpo del bucle


               rescue Exception => ex
                 logger.error ex
                 logger.error ex.backtrace
                 logger.error "Error en #{line}"
               end
            end
        end
    end

Con esto, el bucle se ejecutará en paralelo con 4 hilos. Para finalizar, los hilos deben esperarse para continuar con el proceso:

    my_threads.each do |t|
      t.join
    end

Y con esto se realiza la paralelización. Es posible que si manejamos objetos dentro, en algún caso nos de un error, diciendo que no encuentra el tipo de dato o la clase. Para ello, podemos incluir directamente el fichero al principio. Ejemplo:

require_relative "../../app/models/empresa"

Así evitaremos este error.

miércoles, 19 de diciembre de 2012

Conectar Rails con Facebook, Twitter (I)

Para poder conectar nuestra aplicación con Facebook, Twitter, ... usaremos la gema devise, ya que contiene OmniAuth, lo que nos permitirá usar un conector para casi cualquier sitio que queramos asociar.
En el Gemfile, teendremos:


gem 'devise'
gem 'omniauth'
gem 'omniauth-facebook'

La configuración del modelo de usuario se hará como indicamos en el post: "Integrar decvise con Rails"

Posteriormente, debemos hacer una migración para añadir los campos necesarios para la conexión con otras redes:
provider:string uid:string token:string

Y añadir a User la extención omniauthable

Ahora tenemor que configurar los parámetros. Eso lo haremos en el fichero config/initializers/devise.rb:
Devise.setup do |config|
  config.omniauth :facebook, "APP_ID", "APP_SECRET", {:scope => 'email, offline_access', :client_options => {:ssl => {:ca_file => '/usr/lib/ssl/certs/ca-certificates.crt'}}}

end


En este caso, estamso ocnfigurando el login mediante Facebook. Las claves (APP_ID y APP_SECRET) las obtenemos en Facebook, entrando en el programa para desarrolladores.
Los parámetros que vemos en client_options, es debido a que nuestra aplicación estará alojada en Heroku, por lo que son necesarios.

Para hacer la llamada a este login, tendremos que poner en la vista:

<%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %>

Los helpers user_omniauth los asigna la gema omniaouth, por lo que sólo tendremos que ocuparnos de pasarle el provider (facebook en nuestro caso)

Hasta aquí, tenemos lista la llamada a Facebook. Ahora falta implementar el callback para recibir los datos una vez que el usuario esté logado. Esto lo veremos en el próximo artículo

martes, 18 de diciembre de 2012

Librerías y Framework en iOs

Cuando estamos programando una aplicación iOs, y necesitamos importar una librería o framework, hay una forma bastante fácil de hacerlo, y es usando CocoaPods.

La instalación es sencilla, ya que se instala como una gema de ruby:

gem install cocoapods
pod setup

Luego crearemos, dentro de una carpeta, un fichero llamado Podfile. Dentro de ese fichero declararemos el sistema operativo en el que vamos a desarrollar (iOs en nuestro caso) y las librerías que necesitamos:

platform :ios
pod 'ZBarSDK'

En el ejemplo, importamos ZBar, una librería de lectura de códigos QR. Para saber el nombre de la librería, basta con buscar en la página, y nos dará el nombre del pod y la versión. Una vez incluidas las librerías, ejecutamos:

pod install

Se creará un proyecto y un workspace. Debemos abrir el workspace con el XCode, y ya lo tenemos todo. Para añadir nuevas librerías posteriormente, basta con modificar el Podfile y ejecutar de nuevo el comando de instalación.

lunes, 17 de diciembre de 2012

Deshacer un commit con git

Estamso trabajando en un proyecto con GIT, hacemos commit y, entonces, nos damos cuenta de que la hemos liado parda.
No pasa nada, tiene solución. Para ello usaremos el comando reset de Git.

Pongamos que hemos hecho lo siguiente:
git add .
git commit -m "commit con errores"

Simplemente, para deshacer el commit, haremos:
git reset --soft HEAD^

Y tendremos de nuevo el proyecto en el estado anterior, pudiendo editar el commit

domingo, 16 de diciembre de 2012

iOS: crear un nuevo UIViewController

Cuando queramos crear un nuevo UIViewController en XCode para un proyecto de iOS, hay que seguir los siguientes pasos:

Nuevo fichero: clase de Objetive-C


Luego, elegimos que sea subclase de UIViewController.

Si ahora queremos asociarle un fichero NIB, haremos lo siguiente:

Nuevo fichero: Empty User Interface


Ahora, desde el Interface Builder, pinchamos en File's Owner, y en Identity Inspector, le asignamos la clase que acabamos de crear


Así, ya tenemos el XIB relacionado con nuestro nuevo controlador

sábado, 15 de diciembre de 2012

Rails: Modificar módulos de devise

Si ya tenemos instalado devise con nuestro modelo User, pero posteriormente queremos añadir alguna de las extensiones, tendremos que realizar una migración sobre el modelo para añadir los campos que requiere el módulo.

Por ejemplo, para añadir comfirmable a devise:

En el módulo comfirmable, vemos que los campos necesarios son:

required_methods = [:confirmation_token, :confirmed_at, :confirmation_sent_at]

Por tanto, generamos la migración

$ rails g migration AddConfirmableToUser confirmation_token:string:uniq confirmed_at:datetime confirmation_sent_at:datetime unconfirmed_email:string
Que nos quedará así:

class AddConfirmableToUser < ActiveRecord::Migration
  def change
    add_column :users, :confirmation_token, :string
    add_index :users, :confirmation_token, :unique => true
    add_column :users, :confirmed_at, :datetime
    add_column :users, :confirmation_sent_at, :datetime
    add_column :users, :unconfirmed_email, :string
  end
end


Ahora, en User, descomentamos :confirmable.

Ya solo queda configurar la confirmación a nuestro gusto en condig/initializers/devise.rb:
Por ejemplo...

config.allow_unconfirmed_access_for = 1.days #esperar 1 dia antes de bloquear
config.reconfirmable = true # confirmar el cambio de email
Y con esto tenemos incluido el nuevo módulo

viernes, 14 de diciembre de 2012

iPhone y Rails con devise

Cuando queremos conectar una aplicación de iPhone con una aplicación de Ruby on Rails que tenga la gestión de usuarios con Devise, esto es lo que debemos hacer:

Primero, debemos tener el modelo User de devise con la extensión token_authenticatable. Si no lo tenemos, le añadimos el módulo :token_authenticatable en la clase User.

Realizamos una migración sobre User para los campos requeridos y la corremos sobre la tabla de usuarios:


rails g migration AddTokenToUser authentication_token:string:uniq

class AddTokenToUser < ActiveRecord::Migration
  def change
    add_column :users, :authentication_token, :string
    add_index :users, :authentication_token, :unique => true
  end
end

Ahora configuramos las rutas de la aplicación, para tener apartado el acceso  para aplicaciones móviles (formato JSON) del resto de aplicación Web.
....routes.draw do

devise_for :users
[...]

namespace :api do
  resources :tokens,:only => [:create, :destroy]
end

Necesitamos crear un controlador para manejar los token de acceso:

 rails g controller api::token create destroy

Que quedará más o menos así:

class Api::TokensController  < ApplicationController 
  skip_before_filter :verify_authenticity_token
  respond_to :json
  
  def create
    identificador = params[:login] || params[:email]
    password = params[:password]

    if request.format != :json
      render :status=>406, :json=>{:message=>I18n.t('errors.invalid_nojson')}
      return
    end

    if identificador.blank? or password.blank?
      render :status=>400, :json=>{:message=>I18n.t('errors.invalid_nouserpwd')}
      return
    end

    @user=User.find_by_email(identificador.downcase) || User.find_by_username(identificador.downcase)

    if @user.blank?
      render :status=>401, :json=>{:message=>I18n.t('errors.invalid_user')}
      return
    end

    @user.ensure_authentication_token
    @user.save!

    if @user.valid_password?(password)
      render :status=>200, :json=>{:token=>@user.authentication_token}
    else
      render :status=>401, :json=>{:message=>I18n.t('errors.invalid_user')}
    end

  end
  
  def destroy
    @user=User.find_by_authentication_token(params[:id])
    if @user.blank?
      render :status=>404, :json=>{:message=>I18n.t('errors.invalid_token')}
    else
      @user.reset_authentication_token!
      render :status=>200, :json=>{:token=>params[:id]}
    end
  end  

end  



Si ahora accedemos a http://localhost:3000/api/token.json vía POST con los parámetros adecuados, tendremos el token de sesión para el usuario

jueves, 13 de diciembre de 2012

Cambiar proyecto iOS a ARC

El nuevo cambio de la programación en las plataformas iOS es el ARC (Automatic Reference Counting). Esto evita tener que llevar la gestión manual de la memoria en nuestros programas, y no será necesario hacer las llamadas a release y dealloc.

De hecho, más que no ser necesarias, no están permitidas, y en las que tengamos, el compilador nos dará mensajes del tipo: "ARC forbids explicits message send of 'dealloc'". El problema viene cuando queremos integrar una clase o trozo de código de otra aplicación que hubiéramos desarrollado previamente, ya que toda esta gestión manual ya no se puede realizar, y nos darán errores de compilación.

Para transformar un proyecto a la nueva versión, con ARC, lo realizaremos con el XCode, en "Edición --> Refractor":



Él solo se encargará de transformar el código para tenerlo actualizado.

Una explicación bastante detallada de lo que implica este cambio la podemos ver aquí.

martes, 11 de diciembre de 2012

Integrar Devise en Rails

Dentro del desarrollo de aplicaciones en Rails, si tenemos que elegir una forma de gestionar los usuarios del sistema, yo (y muchos más) recomiendo la gema Devise de plataformatec.

Es sencilla de usar y te ahorra todo el tiempo que requiere el gestionar la creación, auntentificación, bloqueos y demás acciones que requiere un sistema con usuarios.

Pasos para integrarla en una aplicación:

  • Incluimos la gema en nuestro Gemfile:
  • gem 'devise'

  • Preparamos la aplicación y generamos nuestro modelo. Para mayor facilidad, recomiendo llamar al modelo User, ya que todos los ejemplos y tutoriales suelen asumir ese nombre.
    rails generate devise:install
    rails generate devise User
  • Antes de realizar la migración, configuramos las extensiones
  • Para obligar a que un usuario tenga que logarse al entrar en la aplicación (o una parte), debemos incluir el filtro:
    before_filter :authenticate_user!
    Una vez que corramos las migraciones, ya tendremos lista nuestra gestión de usuarios. Podemos modificar config/initializers/devise.rb para configurarlo. Por ejemplo:
  • config.authentication_keys = [ :username ], si queremos login por email o nombre de usuario

Subir cambios a un nuevo repositorio en Github


Cuando hemos empezado un proyecto, y queremos subirlos a un nuevo repositorio en GitHub, estos son los pasos que debemos llevar a cabo:


  1. Entramos en nuestra cuenta de github.com y creamos un repositorio vacío.
  2. Desde la línea de comandos, ejecutamos los siguientes comandos:
    
    
    git init
    git add .
    git commit  -m "Subida inicial"
    git remote add origin https://github.com/user/nombrerepo.git
    git push origin master

De esta forma, asignamos el origin al proyecto, diciéndole que el repositorio remoto esta en esa dirección de github, y subimos todo a la rama master

miércoles, 5 de diciembre de 2012

Tutorial de GIT : lo básico

Vamos a ver los comandos básicos de GIT, para evitar errores que puedan provocar conflictos e incluso perder parte del tranajo.


  • Crear una rama:
    git checkout -b nueva-rama-local
    
  • Commit de Cambios:
    git add .
    git commit -m "comentario sobre los cambios"
    
  • Preparar commit para subir a origen:
    git checkout master
    git pull origin master
    git checkout nueva-rama-local
    git rebase master
    
    Con esto, los cambios que se hubieran subido al master de origin se incorporan a la rama local. En caso de que hubiera algún fichero con conflictos, git avisaría (CONFLICT), y haciendo
    git status
    
    Veremos los ficheros que hay que revisar: los que se indican con "both modified". Una vez corregidos, haremos:
    git add .
    git rebase --continue
    
    Y listo
  • Para subir los cambios a origin, una vez hecho el rebase:
    git checkout master 
    git merge nueva-rama-local
    git puch origin master