segunda-feira, 12 de setembro de 2011

Codeigniter 2 + STI + PHPActiveRecord (parte 3)



Para validar o funcionamento do ORM em conjunto com o Codeigniter e o CIUnit vamos criar uma  simples aplicação acadêmica como prova de conceito guiada por testes e aplicando alguns design paterns quando necessário.

Requisitos:
  1. Um Curso é composto por uma ou mais Disciplinas
  2. Uma Disciplina pode fazer parte de nenhum ou muitos Cursos
  3. Cada Disciplina deve ser Ministrada por um único Professor Responsável
  4. Cada Disciplina pode pode ter outros Professores Adjuntos
  5. Um Professor pode Ministrar uma ou muitas Disciplinas diferentes
  6. Cada Turma de Estudantes deve ter uma Sala de aula
  7. Um Estudante pode se Matricular em um ou vários Cursos ou ainda em Disciplinas Isoladas
As palavras destacadas sugerem possíveis classes que compõe o sistema e são expressas no diagrama inicial abaixo.  O diagrama ainda deve crescer e ser alterado durante o desenvolvimento do sistema.

Diagrama de classes de exemplo

Iniciando com TDD

Iniciar criando a hierarquia de classes que representa a herança entre Teachers (Professores) e Students (Estudantes)

Criar um testes para validar a classe Person (Pessoas) que é a classe pai da herança.  Para simplificar vamos mapear a herança para nosso modelo relaciona em uma tabela simples (STI-Single Table Inheritance) incluindo um campo type na tabela para definir o tipo de objeto a que se refere a linha.

Criar a primeira classe de teste. Somente para prova de conceito vamos testar a herança na própria classe Person, mais tarde este testes devem ser refatorado.

Rodar os testes e ver falhar o primeiro teste que valida as classes que fazem parte da herança STI e representam os papéis Teachers e Students
$  phpunit --testdox --color models/PersonTest.php
$  phpunit --testdox --group models => só as classes com anotações


Tabelas para comportar o STI

Criar a tabela do topo da herança (peoples) e definir um campo (type) que vai receber o tipo de classe ao qual o registro se refere.
CREATE TABLE  `academico_test`.`people` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `type` varchar(150) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

Criar uma view para mapear cada subclasse pelo tipo definido

CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` 
SQL SECURITY DEFINER VIEW  `academico_test`.`students` AS 
select `people`.`id` AS `id`,`people`.`name` AS `name`,`people`.`type` AS `type` 
from `people` 
where (`people`.`type` = 'Student')

CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` 
SQL SECURITY DEFINER VIEW  `academico_test`.`teachers` AS 
select `people`.`id` AS `id`,`people`.`name` AS `name`,`people`.`type` AS `type`,
`people`.`created_at` AS `created_at`,`people`.`updated_at` AS `updated_at` 
from `people` 
where (`people`.`type` = 'teacher')


Ajustando as classes de modelo
Sobreescrever o construtor das classes filhas para passar o tipo de cada uma delas. Também é necessário declarar a variáel stática que define o campo da chave primária da tabela.
<?php
class Student extends Person {
    static $primary_key = 'id';
    public function  __construct(array $attributes = array()) {
        parent::__construct(array_merge($attributes,array('type'=> __CLASS__ )));
    }
}
<?php
class Teacher extends Person {
   static $primary_key = 'id';
    public function  __construct(array $attributes = array()) {
        parent::__construct(array_merge($attributes,array('type'=> __CLASS__ )));
    }
}

Agora é só rodar novamente os testes e ver a barra verde, indicando que a implementação da herança com tabela simples está funcionando.
$  phpunit --testdox --color models/PersonTest.php
$  phpunit --testdox --group models => só as classes com anotações

quarta-feira, 7 de setembro de 2011

Codeigniter 2 + PHPActiveRecord (parte 2)

Existem várias formas para instalar o pacote do PHPActiveRecord, vamos instalar usando o gerenciador de pacotes GetSpark.
$ cd my-cia-app/
$ php -r "$(curl -fsSL http://getsparks.org/go-sparks)”
Agora é só instalar o phpactiverecord
$ php tools/spark install php-activerecord
Em alguns ambientes o firewall bloqueia o download de arquivos binarios. Um simples hack pode resolver isso. Acrescente a linha abaixo no final do construtor da classe my-ci-app/tools/lib/spark/spark_types/gt_spark.php
 # TODO: hack para o protocolo git bloqueado no firewall
 $this->base_location = str_replace("git://","https://",$this->base_location);

Testar o funcionamento
Criar a tabela users no database
DROP TABLE IF EXISTS users;
CREATE TABLE users (
    id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    email_address VARCHAR(200) NOT NULL DEFAULT ''
) ENGINE=InnoDB;
Criar um novo modelo User
<?php
class User extends ActiveRecord\Model {
}
Criar ou editar o controlador Welcome

Para fazer o autoload dos pacotes gerenciados pelo Spark inclua a linha abaixo no arquivo config/autoload.php
$autoload['sparks'] = array('php-activerecord/0.0.1');

Hack para o CIUnit funcionar com o Spark
Alterar o arquivo application/core/MY_Loader.php
define('SPARKPATH', 'sparks/'); 

para
define('SPARKPATH', BASEPATH . '../sparks/');

Referências:
https://bitbucket.org/kenjis/my-ciunit/wiki/Home http://beau.frusetta.com/2011/07/07/codeigniter-php-activerecord/http://getsparks.org
http://www.phpactiverecord.org/ https://github.com/kla/php-activerecord

terça-feira, 6 de setembro de 2011

Codeigniter 2 + CIUnit (parte 1)

Baixar  a versão mais recente do Codeigniter no repositório do projeto
$ wget https://github.com/EllisLab/CodeIgniter
Instalar a versão mais recente do phpunit
$ pear channel-discover pear.phpunit.de
$ pear channel-discover components.ez.no
$ pear channel-discover pear.symfony-project.com
$ pear install phpunit/PHPUnit

Integrando o phpunit ao CI 2
A equipe de desenvolvimento do Codeigniter está trabalhando para integrar o PHPUnit com o core do framework. Um novo branch foi criado e pode ser acompanhado no github do projeto.
https://github.com/EllisLab/CodeIgniter/tree/feature/unit-tests

Enquanto a integração oficial não é finalizada uma boa alternativa é o uso do CIUnit.
Baixar o fork para o CI 2.0 do CIUnit no repositório. (https://bitbucket.org/kenjis/my-ciunit ou https://github.com/fukata/CIUnit-for-CI2) e seguir as instruções de instalação e uso do arquivo README.
cp ciunit/application/third_party/CIUnit   my-ci-app//application/third_party/CIUnit
cp ciunit/tests   my-ci-app/tests


Configurar as conexões
Alterar o arquivo de configuração de conexões da aplicação para trabalhar com o database de teste:
$ vi /my-ci-app/application/config/database.php
Criar os databases:
$ mysqladmin -u root -p create exercise academico
$ mysqladmin -u root -p create exercise academico_test

Verificar se tudo funciona
O CIUnit vem com alguns arquivos de teste de exemplo que podemos utilizar para verificar se tudo está corretamente instalado.

Criar a tabela do modelo
Testar a aplicação no melhor estilo TDD, baby-steps. Red-Green
$ cd my-ci-app/tests
$ phpunit
Barra vermelha (red)!  Criar o modelo em my-ci-app/application/models/phone_carrier_model.phpe rodar os testes mais uma vez.
$ phpunit
Agora é só implementar a funcionalidade do modelo e ver a barra verde
Código da aplicação em https://github.com/fcm/codeigniter-academic

Referências:
https://github.com/EllisLab/CodeIgniter/tree/feature/unit-testsasdasd
https://bitbucket.org/kenjis/my-ciunit
https://github.com/fukata/CIUnit-for-CI2
https://github.com/EllisLab/CodeIgniter

sexta-feira, 26 de agosto de 2011

Rails 3.1 + Mountable Engines

Engines do Rails 3.1 permitem dividir a aplicação em módulos funcionais, incluindo uma aplicação dentro da outra.

Verificar a versao do Rails que deve ser a 3.1 ou superior:
$ rvm use 1.9.2@rails3.1
$ rails -v
Criar uma nova engine:
$ rails plugin new uhoh --mountable
$ cd uhoh
$ git init
$ echo "rvm use 1.9.2@rails3.1" > .rvmrc
A estrutura de diretório criada é igual a de uma aplicação rails comum com a diferença dos diretórios app/controllers, assets, helpers e views que ficam no namespace da aplicação uhoh:
create  
create  README.rdoc
create  Rakefile
create  uhoh.gemspec
create  MIT-LICENSE
create  .gitignore
create  Gemfile
create  app
create  app/controllers/uhoh/application_controller.rb
create  app/helpers/uhoh/application_helper.rb
create  app/models
create  app/views/layouts/application.html.erb
create  app/views/layouts/uhoh/application.html.erb
create  app/assets/images/uhoh
create  app/assets/images/uhoh/.gitkeep
create  config/routes.rb
create  lib/uhoh.rb
create  lib/tasks/uhoh_tasks.rake
create  lib/uhoh/engine.rb
create  app/assets/stylesheets/uhoh/application.css
create  app/assets/javascripts/uhoh/application.js
create  script
create  script/rails
create  test/test_helper.rb
create  test/uhoh_test.rb
append  Rakefile
create  test/integration/navigation_test.rb
vendor_app  test/dummy
run  bundle install
O arquivo de configuração principal está em /lib/uhoh/engine.rb e já possui uma
chamada a isolate_namespace, que torna a engine isolada da aplicação onde será montada.
module Uhoh  
  class Engine < Rails::Engine  
    isolate_namespace Uhoh  
  end  
end  

Engine de Noitificações 1

Criar um controller para a engine:
$ rails generate controller failures index
Criar uma rota padrao para a action do controller:
Uhoh::Engine.routes.draw do  
  root :to => "failures#index"  
end 
Criar um model para armazenar as mensagens de erro:
$ rails generate model failure message:text
As migrations podem ser executadas diretamente na engine
$ rake db:migrate
$ rails c
> Uhoh::Failure.create!(:message => 'Ohoh Failure!')
Mas quando incluidas em outra aplicação Rails elas precisam ser copiadas para as migrations da aplicação. Esta informação deve ser colocada no arquivo README para orientar o usuário da engine.
$ rake uhoh:install:migrations
Modificar o controller /app/controllers/uhoh/failures_controller.rb
module Uhoh  
  class FailuresController < ApplicationController  
    def index  
      @failures = Failure.all  
    end  
  end  
end 
Modificar a view /app/views/uhoh/failures/index.html.erb:
<h1>Failures</h1>  
<ul>  
  <% for failure in @failures %>  
  <li><%= failure.message %></li>  
  <% end %>  
</ul>  

Configurar um Initializer

Notifier que vai ficar escutando quando a aplicação lança uma exception. Criar um initializer em /uhoh/config/initializers/exception_handler.rb
ActiveSupport::Notifications.subscribe \  
  "process_action.action_controller" do \  
  |name, start, finish, id, payload|  
  if payload[:exception]  
    name, message = *payload[:exception]  
    Uhoh::Failure.create!(:message => message)  
  end  
end

Testar a Engine na aplicação Dummy

É possível testar a engine na aplicação ficticia de teste criada em test/dummy:
Informar para aplicação qual url será usada para referênciar a Engine no arquivo /test/dummy/config/routes.rb:
Rails.application.routes.draw do  
  mount Uhoh::Engine => "/uhoh"  
end
Criar um controle na aplicação fictícia para simular uma exception:
$ cd test/dummy
$ rails g controller simulate failure 
Lançar uma exception de teste:
class SimulateController < ApplicationController  
  def failure  
    raise "Simulating an exception"  
  end  
end

Testado tudo numa nova aplicação

Criar a nova aplicação que vai utilizar a engine a partir do path
$ rails new uhoh-parent
$ vi uhoh-pai/Gemfile
#=>incluir esta linha utilizar a engine criada localmente
gem 'uhoh',  :path => '../uhoh/'
Ou criar uma gem e instalar no sistema. Editar o arquivo de propriedades uhoh.gemspec e contruir a gem
$ gem build uhoh.gemspec
Vai gerar o arquivo uhoh-0.0.1.gem que pode ser colocado num repositório ou instalado direto no sistema
$ gem install uhoh
Verificar se a gem foi instalada, a partir da aplicação pai abrir a engine
$ export BUNDLER_EDITOR=gmate
$ bundle open uhoh
Instalar as migrations da engine na aplicação
$ bundle exec rake uhoh:install:migrations
$ bundle exec rake db:migrate
$ rails s
Acessar http://0.0.0.0:3000/uhoh

Referências:
1 http://railscasts.com/episodes/277-mountable-engines
http://www.builtfromsource.com/2010/12/13/mountable-engines-in-rails-3-1-beta-getting-started/
http://railscasts.com/episodes/249-notifications-in-rails-3

sexta-feira, 19 de agosto de 2011

Rails 3.1 + Ubuntu

Criar um novo ambiente para as gems do Rails 3.1:
$ rvm ruby-1.9.2@rails3.1 --create --default
Atualizar o rake 0.8.7 para 0.9.2:
$ gem update rake
$ rake --version
Instalar a última versão do Rails:
$ gem install rails --pre --no-ri --no-rdoc
$ rails -v
Nova aplicação de teste:
$ rails new testeapp
$ cd testeapp
Configurar o gemset que o projeto vai utlizar:
$ echo 'rvm 1.9.2@rails3.1' > .rvmrc
Adicionar um motor javascript ao Gemfile (necessário no Ubuntu)
$ vi Gemfile
  gem 'therubyracer', '>= 0.8.2'
Testar se tudo funciona:
$ bundle exec rake -T
Gems que pode auxiliar a criar aplicações Rails 3.1:
$ gem install i18n thor bundler --no-ri --no-rdoc
$ gem install rails_apps_composer --no-ri --no-rdoc
A gem rails_app_composer ajuda a criar novas aplicações rails:
$ rails_apps_composer list
$ rails_apps_composer new APP_NAME -r jquery haml rspec cucumber devise


Referencias:
http://railsapps.github.com/installing-rails-3-1.html
https://github.com/sstephenson/execjs
https://github.com/RailsApps/rails_apps_composer

quarta-feira, 17 de agosto de 2011

Django + Virtualenv + Pip + Ubuntu

Instalar os pacotes necessários:
$ sudo apt-get install python-setuptools python-dev build-essential
Instalar o Pip:
$ sudo easy_install -U pip
$ pip help
$ pip zip --list
Instalar a Virtualenv:
$ sudo pip install -U virtualenv
Criar um novo ambiente virtual:
$ mkdir -p ~/.virtualenvs
$ cd ~/.virtualenvs
$ virtualenv --no-site-packages --distribute ~/.virtualenvs/default
Instalar Yolk (ferramenta para listar pacotes) no novo ambiente virtual default criado:
$ pip install -E ~/.virtualenvs/default yolk
Ativar o ambiente para uso:
$ source ~/.virtualenvs/default/bin/activate
$ yolk -l
Para desativar o ambiente:
$ deactivate
$ yolk -l
Instalar o Django no ambiente
$ pip install -E ~/.virtualenvs/default Django
Criar um arquivo com que especifica as versão utilizadas:
$ pip freeze -E ~/.virtualenvs/default &gt; requirements.txt
É possivel usar este arquivo para instalar os mesmos pacotes:
$ pip install -E ~/.virtualenvs/default -r ~/.virtualenvs/default/requirements.txt
Instalar o console IPhyton:
$ pip install -E ~/.virtualenvs/default ipython
Ativar o suporte ao tab-complete dos comandos do pip:
$ pip completion --bash &gt;&gt; ~/.bash_completion
$ source ~/.bashrc
$ pip search "mysql"
Testar se todo o ambiente está funcionando:
$ ipython =&gt; CTRL+D para sair do console
$ cd ~/projetos-python
$ django-admin.py startproject demoapp
$ python manage.py runserver 8000  #=&gt; acessar http://127.0.0.1:8000/


Referências:
http://tumblr.intranation.com/post/766290325/python-virtualenv-quickstart-django
http://pypi.python.org/pypi/virtualenv
http://pypi.python.org/pypi/yolk
http://alexandrebm.com/pip-instalador-de-pacotes-no-python
http://www.pip-installer.org/en/latest/#using-pip-with-virtualenv

quarta-feira, 10 de agosto de 2011

Deploy com Capistrano

Configurar a variavel de ambiente do servidor
$ sudo nano /etc/environment
#inserir esta linha
export RAILS_ENV=production
Instalar a gem Capistrano
$ gem install capistrano --no-ri --no-rdoc
Criar uma nova aplicação Rails, versionar e adicionar o repositório remoto.
$ rails new app.demo
$ cd app.demo 
$ bundle install #=> é necessário criar o Gemfile.look
$ git init
$ git add .
$ git commit -am 'Initial commit'
$ git remote add origin git@pavilion:app.demo
$ git push origin master
Criar os arquivos do Capistrano
$ capify .
$ git status # -> para mostrar os diretórios incluidos
Substituir o conteúdo do arquivo que o comando anterior criou em config/deploy.rb
Configurar o deploy na aplicação
$ git add .
$ git commit -am 'Initial commit'
$ git push
Fazer o deploy da aplicação
$ cap deploy:setup
$ cap deploy  
$ cap deploy:migrations
Configurar a página de manutenção (é possível customizar o layout da pagina maintenance.htm)
$ sudo a2enmod rewrite #=> habilita o modulo de reescrita de url
$ cap deploy:web:disable \
      REASON="Atualizações de Segurança" \
      UNTIL="21h de hoje"
$ cap deploy:web:enable

Referências:
http://beginrescueend.com/integration/capistrano
http://gembundler.com/deploying.html
http://blog.josephholsten.com/2010/09/deploying-with-bundler-and-capistrano/
http://andkrup.wordpress.com/2011/05/10/ubuntu-10-10-apache2-passenger-rvm-capistrano-and-git-the-whole-shebang/
http://shiftcommathree.com/articles/make-your-rails-maintenance-page-respond-with-a-503