Documente Academic
Documente Profesional
Documente Cultură
para Desenvolvedores
Tabela de conteúdos
Sobre o livro 0
Ambiente de Desenvolvimento 1
IDEs 1.1
Ruby 1.2
Rails 1.3
Redmine 1.4
Fluxo de Dados 2
Estrutura das Pastas 3
Subpastas importantes 3.1
Plugins 4
Quando devo desenvolver um plugin? 4.1
Como criar um plugin 4.2
Criando um modelo 4.3
Criando um controller 4.4
Adicionando uma rota 4.5
Adicionando um link ao menu 4.6
Internacionalização 4.7
Criando uma view 4.8
Assets 4.9
Permissões 4.10
Módulos 4.11
Hooks 4.12
Hooks nas Views 4.12.1
Hooks nos Controllers 4.12.2
Fazendo um plugin ser configurável 4.13
Sobrescrever o código do Redmine 4.14
Sobrescrever um Modelo 4.14.1
Sobrescrever um Controller 4.14.2
Sobrescrever uma view 4.14.3
Fonte de Estudo 5
2
Redmine para Desenvolvedores
Futuro 6
Colaboradores 7
Leitores 8
3
Redmine para Desenvolvedores
Sobre o livro
Neste livro veremos como desenvolver plugins para Redmine 3.1. Com foco na teoria e
técnicas de programação da framework Rails e como se aplica ao Redmine.
O livro é aberto e colaborativo, quem quiser contribuir pode mandar issues e pull requests
para: https://github.com/victorlcampos/curso-redmine
Sobre o livro 4
Redmine para Desenvolvedores
Ambiente de Desenvolvimento
Neste capítulo iremos verificar como instalar o ambiente de desenvolvimento necessário
para programar para o Redmine
Ambiente de Desenvolvimento 5
Redmine para Desenvolvedores
IDEs
Sublime
Atom
Atom é sem dúvida a minha recomendação para quem quer seguir com desenvolvimento
ruby. Um editor estável, open source e desenvolvido/mantido pelo próprio GitHub.
Poderia passar horas falando o porque eu gosto do atom, mas não é o foco do curso.
RubyMine
IDEs 6
Redmine para Desenvolvedores
Como escrito no próprio site da JetBrain, a IDE de ruby mais inteligente. Porque não uso?
Primeiro, porque acredito que IDE traga mais distrações do que o benefícios, segundo
porque demora mais tempo para iniciar que o Atom, terceiro porque custa 199 obamas no
primeiro ano e 99 para renovar a licença. E não acho que esse valor se pague.
Mas se você é desenvolvedor Java e não consegue viver sem autocomplete, vale a pena
testar os 30 dias de trial.
PS: Não citei o eclipse porque era bem ruim quando usei para ruby(a uns 2 anos atrás) e
nunca mais voltei, mas se você quiser testar, fique a vontade.
IDEs 7
Redmine para Desenvolvedores
Ruby
Introdução
Ruby é uma linguagem de programação de 1995 onde quase tudo é um objeto. Uma
linguagem moderna, possuindo tipos dinâmicos, lambda function e altamente influenciada
por programação funcional.
Diferente do Java onde o tipo é explícito, em Ruby a tipagem é conhecida como Duck
Typing, se um argumento tem todos os métodos que o método precisa para usá-lo, o
método vai aceita-lo como argumento. O que não significa que a variável não tenha tipo,
todo objeto tem o método .class, que retorna a classe que ele pertence.
Outra diferença com o Java é que as classes em Ruby são abertas, mas o que isso
significa? Significa que após declarar uma classe, você pode abri-la novamente e altera-la.
Continuou sem entender? Vamos para o Exemplo:
Ruby 8
Redmine para Desenvolvedores
class A
def a
print 'a'
end
end
obj = A.new
obj.a
=> a
class A
def b
print 'b'
end
end
obj = A.new
obj.a
=> a
obj.b
=> b
Depois de declarar a classe A pela segunda vez, quando iniciei um novo objeto dessa
classe, ele passou a ter ambos os métodos. Mas o que ocorreria se eu tivesse declarado o
mesmo método novamente?
class A
def a
print 'novo a'
end
end
obj = A.new
obj.a
=> novo a
obj.b
=> b
Se o mesmo método for declarado duas vezes, a última declaração passa a valer. Essa
característica da linguagem, evita as milhões de classe Utils que criamos no Java e facilita a
criação de plugins.
E como ruby é altamente influenciada por programação funcinal, toda função tem um
retorno, não existe função void em Ruby.
Instalação
Ruby 9
Redmine para Desenvolvedores
Para instalar o Ruby no Linux (Usem LINUX), vamos utilizar um gerenciador de versão do
Ruby para conseguirmos ter mais de uma versão rodando na mesma máquina.
Os dois gerenciadores mais famosos são o rbenv e o rvm. Para esse curso vamos utilizar o
rvm.
Escolha baseada em gosto pessoal, se quiserem se aventurar no rbenv ele também é muito
bom.
Ruby 10
Redmine para Desenvolvedores
$ apt-get update
$ apt-get install -y subversion git telnet
$ apt-get install -y libmysqlclient-dev freetds-dev imagemagick libmagickcore-dev libmagickwand-dev l
$ rvm requirements
$ rvm install 2.2
$ rvm use 2.2 --default
Ruby 11
Redmine para Desenvolvedores
Rails
Introdução
O Rails é uma framework MVC baseado em dois princípios básicos que você deve
SEMPRE seguir, Convenção sobre Configuração e o Dry(Don't repeat yourself).
Instalação
Podemos instalar o rails utilizando o RubyGem, uma gem seria o equivalente do jar no
Java. O RubyGem é um instalador de gems que já é instalado junto com o ruby pelo rvm,
ele funciona parecido com o apt-get do ububtu.
Rails 12
Redmine para Desenvolvedores
Redmine
Introdução
Instalação
Para instalar o Redmine, vamos utilizar a última versão estável do redmine 2.6, que no
momento de criação desse curso era a 2.6.6
Redmine 13
Redmine para Desenvolvedores
$ cd redmine-2.6.6
$ cp ./config/database.yml{.example,}
$ cp ./config/configuration.yml{.example,}
Assim como no Java temos o Maven para baixar as dependências, no Ruby temos o
Bundle, para utiliza-lo basta fazer:
$ bundle install
Ele irá olhar o arquivo Gemfile, na pasta raiz do projeto e instalar todas as dependências
que lá estiver.
NÃO, ele, diferente do Maven, se foca em fazer bem uma única coisa: gerenciar
dependências.
Para automatizar tarefas, temos o rake, vamos utilizar para gerar o token de segurança de
sessão.
$ rake generate_secret_token
Também precisamos gerar as tabelas do banco de dados que o redmine usa. O Rails por
padrão possui migrações, arquivos em ruby(.rb) que descreve as operações que devemos
realizar no banco.
Podemos com o rake rodar todas essas migrações e o Rails se encarrega de transformar
no sql certo para o banco descrito no database.yml
$ rake db:create
$ rake db:migrate
$ rails server
Redmine 14
Redmine para Desenvolvedores
Fluxo de Dados
A primeira coisa para se entender como programar utilizando Ruby on Rails é entender o
fluxo de dados.
Tenta encaixar a url do request em algum partner cadastrado nos arquivos de rotas.
Podemos verificar todas as rotas cadastradas rodando o comando "rake routes" no
terminal
O arquivo de rotas dirá para o Rails qual o controller ele deve chamar e qual action ele
deve executar.
Uma action é o nome de um método de um controller
Uma action pode redirectionar para outra action ou renderizar uma view.
Por padrão o Rails renderiza a view com o mesmo nome da action dentro da pasta
com o mesmo nome do controller. (Convenção sobre Configuração)
A view enxerga as variáveis de instâncias(@) do controller
Entender e praticar esse fluxo é de extrema importância para saber encontrar o que você
deseja modificar no Redmine e saber em qual parte do código está dando erro. Com o
tempo você perceberá que as coisas estão onde devem estar.
Fluxo de Dados 15
Redmine para Desenvolvedores
app
Pasta onde ficam os arquivos da aplicação como models, controllers, views, etc
config
Pasta onde ficam os arquivos de configuração de banco, ambiente, rotas, locales
db
Pasta onde se encontra as migrações do banco
doc
Pasta para guardar as documentações da aplicação
extra
Pasta com exemplo de plugin
files
Pasta onde o Redmine guarda os arquivos anexados
lib
Pasta onde ficam as bibliotecas de código desenvolvida, como rake taskes,
patches, etc
plugins
Pasta onde ficam guardados os plugins desenvolvidos para o Redmine, é aqui
onde faremos 90% do desenvolvimento
public
Pasta onde ficam os arquivos estáticos servidos pelo webserver
script
Contêm o script para inicialização do rails
test
Pasta com os testes automatizados do Redmine
Subpastas importantes
App
Como mencionado a pasta app contêm os controllers, models e views, mas em qual
subpasta eles ficam? Bom, vou deixar você, caro leitor, descobrir sozinho.
Descobriu? Ótimo, agora vamos olhar para as views. As views possuem também
subpastas, cada uma delas com o mesmo nome do controller. Assim o rails sabe qual view
renderizar quando uma action for chamada. Ele vai dentro da pasta view >
nome_controller > nome_action.{html, js, xml, etcc}.erb.
Caso ele não encontre o arquivo correspondentes, ele vai buscar na pasta com o mesmo
nome da superclasse do controller e assim sucessivamente. Caso ele por fim não encontre,
a página 404 do public é renderizada.
Public
Subpastas importantes 18
Redmine para Desenvolvedores
Na pasta public ficam as imagens, javascript e css(pasta stylesheets), nela também ficam
os temas do redmine.
O Redmine possui uma lista de themas feitos pela comunidade para você não partir do
zero.
Subpastas importantes 19
Redmine para Desenvolvedores
Plugins
Plugins 20
Redmine para Desenvolvedores
Se existir um plugin open source que faça parecido, faça um fork do plugin e contribua com
ele. Assim todos ganham =).
Reparem, ele criou uma estrutura muito parecida com a estrutura do próprio redmine, de
diferente temos:
assets
Nessa pasta ficarão as imagens, javascript e css do plugin. O Redmine ao iniciar
irá pegar esses arquivos e colocar na pasta plugin_assets dentro da pasta public
Redmine::Plugin.register :polls do
name 'Polls plugin'
author 'Author name'
description 'This is a plugin for Redmine'
version '0.0.1'
url 'http://example.com/path/to/plugin'
author_url 'http://example.com/about'
end
O init.rb é o arquivo que o redmine chama ao carregar o plugin, iremos utilizar mais ele no
futuro. Mas por enquanto podemos somente modificar as informações do plugin.
Criando um modelo
Novamente, para criar um modelo dentro de um plugin, podemos chamar o generator do
próprio redmine
Podendo reparar que é muito parecido com o generator de modelo do rails. Então vamos
criar um modelo poll dentro do plugins polls, que vai ter uma question do tipo string, uma
contagem de sim e uma contagem de não, ambos do tipo numérico.
Reparem, ele criou um modelo na pasta models e uma migração na pasta migrations
Vamos entender a migração, ela chama uma função create_table que recebe como
argumento um símbolo (:polls) e um bloco de código que recebe também um argumento(t).
Um simbolo é parecido com a String do Java. A diferença entre "polls" e :polls é que o
primeiro é mutável, se você fizer:
Criando um modelo 24
Redmine para Desenvolvedores
a = "a"
a += "b"
print a
=> "ab"
a = nil
a = :a
print a
=> "a"
a = nil
O símbolo :a, não seria arrancado da memória. Isso é perigoso quando criamos símbolos
dinamicamente e é uma vunerabilidade conhecida do Rails.
Mas porque então usamos ele ao invés de String, a resposta é simples: desempenho.
Se você cria um símbolo de maneira controlada, sempre que você for acessa-lo, não
precisará realocar memória e não importa quantas variáveis apontem para ele, elas vão
estar consumindo a mesma quantidade de memória, pois estarão apontando sempre para a
mesma posição.
Agora que já entendi o que é um símbolo, o que diabos é passar um bloco de código
como argumento?
Bom, em ruby uma função pode receber um bloco de código e executa-lo dentro dela. O
equivalente em Java 7 seria instanciar uma interface e preencher os métodos, muito usado
nos handlers da vida.
No caso da função create_table ela executa o que tem que executar, instância um objeto e
passa para a nossa função anônima. Como o ruby é Duck Typing, se o objeto t recebido
pela nossa função anônima tiver todos os métodos necessários para o bloco de código ser
executado, então o bloco será executado sem problema.
Esse bloco irá chamar os métodos do objeto t que criam as colunas, o método string cria
uma coluna do tipo string, o integer do tipo integer, etc... O método create_table cria sozinho
a coluna id e os timestamps.
Criando um modelo 25
Redmine para Desenvolvedores
$ rake redmine:plugins:migrate
Migrating polls (Polls plugin)...
== CreatePolls: migrating ====================================================
-- create_table(:polls)
-> 0.0323s
== CreatePolls: migrated (0.0324s) ===========================================
O rails irá criar a table a as colunas para você, "indendente" do banco que esteja
configurado no seu database.yml
Utilizando convenção sobre configuração, ele irá atribuir todas as colunas da tabela
polls(plural) ao modelo poll(sigular) como métodos, já com getters & setters
poll = Poll.new
poll.question = "Question 1"
print poll.question
=> "Question 1"
print poll.yes
=> nil
Criando um modelo 26
Redmine para Desenvolvedores
Criando um controller
Você já deve ter imaginado que o Redmine possui um generator para o controller
É uma boa prática seguir o padrão rest no rails que consiste nas actions:
Por exemplo, se quisermos criar uma action index para o controller devemos editar o
controller para:
def index
@polls = Poll.all # busca todas as enquetes do banco
end
end
Criando um controller 27
Redmine para Desenvolvedores
Sem @, escopo local, somente dentro do bloco de código que se encontra e bloco de
códigos filhos
@, escopo instância, a variável é vista dentro de qualquer método do objeto
@@, escopo classe, no Java seriam as variáveis estáticas.
Criando um controller 28
Redmine para Desenvolvedores
Bom, o rails possui o método resources(controller_name, opts) que define todas as rotas do
rest por padrão, então podemos definir:
resources :polls
O rails possui por padrão uma rake task que lista todas as rotas criadas, vamos roda-la e
usar o grep para filtrar somente as rotas do Controller polls
Podemos reparar que todas as rotas necessárias para um serviço rest foram criadas. Se
você parar para analisar um segundo, verá que a saída tem uma primeira coluna, o que
será ela?
Para você não sair escrevendo urls de forma hardcode, o rails cria por padrão dois métodos
para cada rota, uma com sufixo path e uma com sufixo url, no nosso caso temos a
polls_path e polls_url que tem como retorno /polls e http://localhost:3000/polls
respectivamente.
Ok, achei super legal, mas eu somente queria uma rota para index e ele criou várias
que eu nem preciso usar.
É verdade, por isso uma das opções que o método aceita é only. Podemos passar da
seguinte maneira:
As rotas no rails são MUITO poderosas e poderia escrever um artigo somente sobre elas,
então aconselho a dar uma olhada na guia oficial da linguagem para conhecer essa
ferramenta incrível: http://guides.rubyonrails.org/routing.html
Para adicionar um menu, é necessário editar o arquivo init.rb e dentro do registro do plugin
usar o método menu que tem a sintaxe:
:param - the parameter key that is used for the project id (default is :id)
:if - a Proc that is called before rendering the item, the item is displayed only if it returns
true
:caption - the menu caption that can be:
a localized string Symbol
a String
a Proc that can take the project as argument
:before, :after - specify where the menu item should be inserted (eg. :after => :activity)
:first, :last - if set to true, the item will stay at the beginning/end of the menu (eg. :last =>
true)
:html - a hash of html options that are passed to link_to when rendering the menu item
Com isso, criaremos um link para a nossa action no menu principal fora dos projetos
Internacionalização
A última coisa que falta para o menu, é que nossos clientes são Brasileiros e falam
português, eles não querem polls e sim enquetes escrito no menu. Para fazer essa
alteração, iremos criar um arquivo chamado pt-BR.yml dentro do config/locates do plugin e
preencher com o seguinte conteúdo:
pt-BR:
label_polls: Enquetes
Caso a opção caption não seja passada, e ela não deve ser, o redmine usa o menu_name
préfixado com label para fazer a internacionalização do menu.
Internacionalização 32
Redmine para Desenvolvedores
<h1>Polls</h1>
<ul>
<% @polls.each do |polls| %>
<li><%= polls.question %></li>
<% end %>
</ul>
erb ou Embedded Ruby ou mesmo eRuby é o template padrão do Rails, existem diversos
outros e a comunidade é bem dividida nesse ponto. O rails permite adicionar qualquar
preprocessador de arquivo estático, sempre lendo as extensões da direita para esquerda.
Se tivéssemos um javascript que quiséssemos que antes dele ser enviado para o cliente,
rodasse código ruby, poderíamos criar o arquivo file.js.erb.
Para entender mais sobre views, que é um dos pontos mais completos do Rails. Ele aceita
partials, layouts, content, e várias outras features que facilitam o dry, leia o
http://guides.rubyonrails.org/layouts_and_rendering.html
Assets
Vale ressaltar que view feia não agrada cliente e muitas vezes precisamos escrever css e
javascript específicos para um plugin. Para isso o redmine permite adicionar assets da
seguinte forma:
Se você leu o guia sobre layout como sugerido, saberá que este content_for rodará o bloco
de código passado por ele na posição onde tiver um yield(:header_tags), que no nosso caso
fica dentro da tag
<header></header>
O método stylesheet_link_tag, adiciona um link para o css com nome css_name que se
encontra dentro do plugin com nome igual plugin_name, assim como o
javascript_include_tag fará para o javascript.
Com isso você não precisará modificar o código direto dos assets do redmine para fazer
uma modificação específica da sua view.
Assets 34
Redmine para Desenvolvedores
Permissões
Muitas vezes queremos que só um determinado grupo de pessoas dentro do redmine possa
executar uma determinada tarefa. Para esse controle de acesso o redmine um mecanismo
de permissões.
Esse mecanismo permite que os plugins definem novas permissões dentro do seu init.rb. As
permissões funcionam da seguinte forma:
Public (true/false): onde setar como true implica em dar essa permissão específica para
todos os usuários
Require (:loggedin/:member): retringe para quem você pode dar a permissão, somente
usuários logados ou somente membros do projeto
Vamos dizer que na nossa enquete, a gente divida em dois grupos: Usuários que podem
votar na enquete e usuários que podem ver os resultados. Para isso teremos que editar o
init.rb do nosso plugin para criar essas duas permissões.
Redmine::Plugin.register :polls do
name 'Polls plugin'
author 'Author name'
description 'This is a plugin for Redmine'
version '0.0.1'
url 'http://example.com/path/to/plugin'
author_url 'http://example.com/about'
Permissões 35
Redmine para Desenvolvedores
Para as permissões valerem para o redmine, é necessário fazer um pequeno ajuste nos
controller. Antes de qualquer action é necessário setar um @project para o projeto que o
usuário está acessando e chamar o método authorize do redmine.
def index
@project = Project.find(params[:project_id])
authorize
@polls = Poll.all
end
end
Porém o Rails fornece um mecanimso de filtro, onde você pode setar métodos para
rodarem antes, ao redor ou depois de cada action, essa é uma maneira melhor de resolver
o problema pois permite reuzar código para todas as action mantendo o princípio dry.
No nosso caso iremos adcionar um before_filter e dizer para ele chamar o nosso método
find_project que vai buscar um projeto
def index
@polls = Poll.all
end
protected
def find_project
@project = Project.find(params[:project_id])
end
end
Permissões 36
Redmine para Desenvolvedores
Com isso, qualquer action nova no controller, verificará se o usuário tem permissão para
acessa-la e o código do método da action terá a responsabilidade de executar somente o
código necessário para ela, sem se preocupar com permissão.
Internacionalização
Se vocês repararem na imagem de edição dos papéis, verão que o redmine por padrão
quebra o _ em espaço e coloca a primeira letra maiúscula, mas ele também permite
internacionalizar esse nome, criando uma chave no arquivo de tradução com o nome da
permissão preficada com a palavra permission.
No nosso caso:
pt-BR:
permission_view_polls: Ver enquetes
permission_vote_polls: Votar nas enquetes
Permissões 37
Redmine para Desenvolvedores
Módulos
Módulos 38
Redmine para Desenvolvedores
Hooks
Hooks 39
Redmine para Desenvolvedores
Fonte de Estudo
1. http://guides.rubyonrails.org/
2. http://www.redmine.org/projects/redmine/wiki/Plugin_Tutorial
3. http://www.redmine.org/projects/redmine/wiki/Hooks
4. http://www.redmine.org/projects/redmine/wiki/Plugin_Internals
Fonte de Estudo 44
Redmine para Desenvolvedores
Futuro
A ideia de desenvolver o curso no github é deixar ele colaborativo e expansível, assim como
o Redmine. Gerando assim uma apostila completa sobre o assunto, que se mantenha
sempre atualizada.
Futuro 45
Redmine para Desenvolvedores
Colaboradores
Quem contribuir com esse material, peço que mande um pull request adicionado o seu
nome na lista abaixo.
Colaboradores 46
Redmine para Desenvolvedores
Leitores
Quem utilizar esse material para estudo, peço que mande um pull request adicionado o seu
nome na lista abaixo.
Leitores 47