Shared posts

05 Oct 12:30

Finding All the Red M&Ms: A Story of Indexes and Full‑Table Scans

by Chris Saxon

In this guest post, Chris Saxon explains a very important topic using an analogy with chocolates: When does a database use an index and when is it better not using it. Although Chris explanation has the Oracle database in mind, the principles apply to other databases too.

A common question that comes up when people start tuning queries is “why doesn’t this query use the index I expect?”. There are a few myths surrounding when database optimizers will use an index. A common one I’ve heard is that an index will be used when accessing 5% or less of the rows in a table. This isn’t the case however - the basic decision on whether or not to use an index comes down to its cost.

How do databases determine the cost of an index?

Before getting into the details, let’s talk about chocolate! Imagine you have 100 packets of M&M’s. You also have a document listing the colour of each M&M and the bag it’s stored in. This is ordered by colour, so we have all the blue sweets first, then the brown, green and so on like so:

You’ve been asked to find all the red M&M’s. There’s a couple of basic ways you could approach this task:

Method 1

Get your document listing the colour and location of each M&M. Go to the top of the “red” section. Lookup the location of the first red M&M, pick up the bag it states, and get the sweet. Go back to your document and repeat the process for the next red chocolate. Keep going back-and-forth between your document and the bags until you’ve reached the end of the red section.

Method 2

Pick up a number of bags at a time (e.g. 10), empty their contents out, pick out the red chocolates and return the others (back to their original bag).

Which approach is quicker?

Intuitively the second approach appears to be faster. You only select each bag once and then do some filtering of the items inside. Whereas with the first approach you have done a lot of back-and-forth between the document and the bags. This means you have to look into each bag multiple times.

We can be a bit more rigorous than this though. Let’s calculate how many operations we need to do to get all the red chocolates in each case.

When going between the document and the bags (method 1), each time you lookup the location of a new sweet and fetch that bag that’s a new operation. You have 100 bags with around 55 sweets in each. This means you’re doing roughly 920 (100 bags x 55 sweets / 6 colours) operations (plus some work to find the red section in your document). So the “cost” of using the document is around 920.

With the second approach you collect 10 bags in one step. This means you do ( 100 bags / 10 bags per operation = ) 10 operations (plus some filtering of the chocolates in them), giving a “cost” of 10.

Comparing these costs (920 vs. 10), method 2 is the clear winner.

Let’s imagine another scenario. Mars have started doing a promotion where around 1 in 100 bags contain a silver M&M. If you get the silver sweet, you win a prize. You want to find the silver chocolate!

In this case, using method 1, you go to the document to find the location of the single sweet. Then you go to that bag and retrieve the sweet. One operation (well two, including going to the document to find location of the silver chocolate), so we have a cost of two.

With method 2, you still need to pick up every single bag (and do some filtering) just to find one sweet - the cost is fixed at 10. Clearly method 1 is far superior in this case.

What have M&M’s got to do with databases?

When Oracle stores a record to the database, it is placed in a block. Just like there are many M&Ms in a bag, (normally) there are many rows in a block. When accessing a particular row, Oracle fetches the whole block and retrieves the requested row from within it. This is analogous to us picking up a bag of M&Ms and then picking a single chocolate out.

When doing an index-range scan, Oracle will search the index (the document) to find the first value matching your where clause. It then goes back-and-forth between the index and the table blocks, fetching the records from the location pointed to by the index. This is similar to method 1, where you continually switch between the document and the M&M bags.

As the number of rows accessed by an index increases, the database has to do more work. Therefore the cost of using an index increases in line with the number of records it is expected to fetch.

When doing a full table scan (FTS), Oracle will fetch several blocks at once in a multi-block read. The data fetched is then filtered so that only rows matching your where clause are returned (in this case the red M&Ms) – the rest are discarded. Just like in method 2.

The expected number of rows returned has little impact on the work a FTS does. Its basic cost is fixed by the size of the table and how many blocks you fetch at once.

When fetching a “high” percentage of the rows from a table, it becomes far more efficient to get several blocks at once and do some filtering than it is to visit a single block multiple times.

When does an index scan become more efficient than a FTS?

In our M&M example above, the “full-table scan” method fetches all 100 bags in 10 operations. Whereas with “index” approach requires a separate operation for each sweet. So an index is more efficient when it points to 10 M&Ms or less.

Mars puts around 55 M&M’s in each bag, so as a percentage of the “table” that’s just under ( 10 M&M’s / (100 bags * 55 sweets) * 100 = ) 0.2%!

What if Mars releases some “giant” M&Ms with only 10 sweets in a bag? In this case there’s fewer sweets in total, so the denominator in the equation above decreases. Our FTS approach is still fixed at a “cost” of 10 for the 100 bags. This means the point at which an index is better is when accessing approximately ( 10/1000*100 = ) 1% of the “rows”. A higher percentage, but still small in real terms.

If they released “mini” M&Ms with 200 in a bag, the denominator would increase. This means that the index is more efficient when accessing a very small percentage of the table!

So as you increase the space required to store a row, an index becomes more effective than a FTS. The number of rows accessed by the index remains fixed. The number of blocks required to store the data increases however, making the FTS more expensive and leading to it having a higher cost.

There’s a big assumption made in the above reasoning however. It’s that there’s no correlation between the order M&M’s are listed in the document and which bag they are in. So, for example, the first red M&M (in the document) may be in bag 1, the second in bag 56, the third in bag 20, etc.

Let’s make a different assumption – that the order of red chocolates in the document corresponds to the order they appear in the bags. So the first 9 red sweets are in bag 1, the next 9 in bag 2 etc. While you still have to visit all 100 bags, you can keep the last bag accessed in your hand, only switching bags every 9 or so sweets. This reduces the number of operations you do, making the index approach more efficient.

We can take this further still. What if Mars changes the bagging process so that only one colour appears in each bag?

Now, instead of having to visit every single bag to get all the red sweets, you only have to visit around ( 100 bags / 6 colours) 16 bags. If the sweets are also placed in the bags in the same order they are listed in the document (so M&M’s 1-55 are all blue and in bag 1, bag 2 has the blue M&M’s 56-100, and so on until bag 100, which has yellow M&M’s 496-550) you get the benefits of not switching bags compounded with the effect of having fewer bags to fetch.

This principle – how closely the order of records in a table matches the order they’re listed in a corresponding index – is referred to as the clustering factor. This figure is lower when the rows appear in the same physical order in the table as they do in the index (all sweets in a bag are the same colour) and higher when there’s little or no correlation.

The closer the clustering factor is to the number of blocks in a table the more likely it is that the index will be used (it is assigned a lower cost). The closer it is to the number of rows in a table, the more likely it is a FTS will be chosen (the index access is given a higher cost).

Bringing it all together

To sum up, we can see the cost-based optimizer decides whether to use an index or FTS by:

  • Taking the number of blocks used to store the table and dividing this by the number of blocks read in a multi-block read to give the FTS cost.

  • For each index on the table available to the query:

    • Finding the percentage of the rows in the table it expects a query to return (the selectivity)

    • This is then used to determine the percentage of the index expected to be accessed

    • The selectivity is also multiplied by the clustering factor to estimate the number of table blocks it expects to access to fetch these rows via an index

    • Adding these numbers together to give the expected cost (of the index)

  • The cost of the FTS is then compared to each index inspected and the access method with the lowest cost used.

This is just an overview of how the (Oracle) cost-based optimizer works. If you want to see the formulas the optimizer uses have a read of Wolfgang Breitling’s “Fallacies of the Cost Based Optimizer” paper or Jonathan Lewis’ Cost-Based Oracle Fundamentals book. The blogs of Jonathan Lewis, Richard Foote and of course Markus’ articles on this site also contain many posts going into this subject in more detail.

08 Oct 00:25

Autenticação baseada em tokens com Silex e AngularJS

by Gonzalo Ayuso

Seguindo a linha do meu último artigo, hoje vamos criar um aplicativo AngularJS que utiliza o Silex Backend que criamos anteriormente. A ideia é usá-lo em um aplicativo PhoneGap/Cordova executado em um dispositivo móvel.

O aplicativo mostrará um formulário de login se o dispositivo não tiver um token correto.

token-1
E com o token correto:

token-2
Nada de outro mundo, né?

Nosso aplicativo front-end vai usar AngularJS e Topcoat:

<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org" lang="es" ng-app="G">
<head>
    <meta charset="utf-8"/>
    <meta name="format-detection" content="telephone=no"/>
    <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
    <meta name="viewport"
          content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi"/>
    <link rel="stylesheet" type="text/css" href="/bower_components/topcoat/css/topcoat-mobile-light.min.css">
    <title>Gonzalo Login Example</title>
</head>
<body ng-controller="MainController">
 
<div ng-view class="main-content"></div>
 
<script src="/bower_components/angular/angular.min.js"></script>
<script src="/bower_components/angular-route/angular-route.min.js"></script>
 
<script src="js/app.js"></script>
<script src="js/services.js"></script>
 
</body>
</html>

E o nosso aplicativo AngularJS:

'use strict';
var appControllers, G;
var host = 'http://localhost:8080'; // server API url
 
appControllers = angular.module('appControllers', []);
G = angular.module('G', ['ngRoute', 'appControllers']);
 
G.run(function (httpG) {
    httpG.setHost(host);
});
 
G.config(['$routeProvider', function ($routeProvider) {
    $routeProvider.
        when('/login', {templateUrl: 'partials/login.html', controller: 'LoginController'}).
        when('/home', {templateUrl: 'partials/home.html', controller: 'HomeController'});
}]);
 
appControllers.controller('HomeController', ['$scope', 'httpG', '$location', function ($scope, httpG, $location) {
    $scope.hello = function () {
        httpG.get('/api/info').success(function (data) {
            if (data.status) {
                alert("Hello " + data.info.name + " " + data.info.surname);
            }
        });
    };
 
    $scope.logOut = function () {
        alert("Good bye!");
        httpG.removeToken();
        $scope.isAuthenticated = false;
        $location.path('login');
    };
}]);
 
appControllers.controller('MainController', ['$scope', '$location', 'httpG', function ($scope, $location, httpG) {
    $scope.isAuthenticated = false;
 
    if (httpG.getToken()) {
        $scope.isAuthenticated = true;
        $location.path('home');
    } else {
        $location.path('login');
    }
}]);
 
 
appControllers.controller('LoginController', ['$scope', '$location', 'httpG', function ($scope, $location, httpG) {
    $scope.user = {};
 
    $scope.doLogIn = function () {
        httpG.get('/auth/validateCredentials', {user: $scope.user.username, pass: $scope.user.password}).success(function (data) {
            if (data.status) {
                httpG.setToken(data.info.token);
                $scope.isAuthenticated = true;
                $location.path('home');
            } else {
                alert("login error");
            }
        }).error(function (error) {
            alert("Login Error!");
        });
    };
 
    $scope.doLogOut = function () {
        httpG.removeToken();
    };
}]);

Nesse exemplo, estou usando angular-route para lidar com as rotas do aplicativo. Hoje, estou mudando para angular-ui-router, mas nesse exemplo ainda estou usando rotas “à moda antiga”. Definimos duas parciais:

partial/home.html

<div class="topcoat-button-bar full" style="position: fixed; bottom: 0px;">
    <label class="topcoat-button-bar__item">
        <button class="topcoat-button full" ng-click="logOut()">
            <span class="">Logout</span>
        </button>
    </label>
    <label class="topcoat-button-bar__item">
        <button class="topcoat-button--cta full" ng-click="hello()">
            <span class="">Hello</span>
        </button>
    </label>
</div>

partial/login.html

<div class="topcoat-navigation-bar">
    <div class="topcoat-navigation-bar__item center full">
        <h1 class="topcoat-navigation-bar__title">Login</h1>
    </div>
</div>
 
<ul class="topcoat-list__container">
    <li class="topcoat-list__item center">
        <input ng-model="user.username" class="topcoat-text-input--large" type="text" name="user"
               placeholder="Username"/>
    </li>
    <li class="topcoat-list__item center">
        <input ng-model="user.password" class="topcoat-text-input--large" type="password" name="pass"
               placeholder="Password"/>
    </li>
</ul>
 
<div class="topcoat-button-bar full" style="position: fixed; bottom: 0px;">
    <label class="topcoat-button-bar__item">
        <button class="topcoat-button--cta full" ng-click="doLogIn()">
            <span class="">Login</span>
        </button>
    </label>
</div>

Como podemos ver, estamos usando no aplicativo um serviço para manipular conexões HTTP com as informações do token.

'use strict';
 
G.factory('httpG', ['$http', '$window', function ($http, $window) {
    var serviceToken, serviceHost, tokenKey;
    tokenKey = 'token';
    if (localStorage.getItem(tokenKey)) {
        serviceToken = $window.localStorage.getItem(tokenKey);
    }
 
    $http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
 
    return {
        setHost: function (host) {
            serviceHost = host;
        },
 
        setToken: function (token) {
            serviceToken = token;
            $window.localStorage.setItem(tokenKey, token);
        },
 
        getToken: function () {
            return serviceToken;
        },
 
        removeToken: function() {
            serviceToken = undefined;
            $window.localStorage.removeItem(tokenKey);
        },
 
        get: function (uri, params) {
            params = params || {};
            params['_token'] = serviceToken;
            return $http.get(serviceHost + uri, {params: params});
        },
 
        post: function (uri, params) {
            params = params || {};
            params['_token'] = serviceToken;
 
            return $http.post(serviceHost + uri, params);
        }
    };
}]);

 

E isso é tudo, pessoal! Vocês podem checar a minha conta no GitHub para ver um exemplo completo.

***

Artigo traduzido pela Redação iMasters com autorização do autor. Publicado originalmente em http://gonzalo123.com/2014/06/09/token-based-authentication-with-silex-and-angularjs/

10 Aug 00:22

Ask Toolbar no Java é um absurdo

by Marcio d\'Avila

[Atualizado em 18 de dezembro de 2014.]

Desde a versão 6 do Java, a Oracle começou a oferecer a instalação do software Ask de buscas patrocinadas na internet (ou seja, um adware), durante a instalação do runtime Java. E essa “oferta” insistente e vergonhosa aparece não só na instalação inicial do Java, mas em toda atualização on-line.

A página de ajuda (FAQ) do Java na Internet apenas define vagamente o seguinte:

“A Ask Toolbar é um add-on de browser gratuito que permite fazer pesquisa na Web usando o mecanismo de pesquisa Ask.com diretamente do browser.”

As ferramentas oferecidas para instalação junto com o Java são a Ask Toolbar, uma barra de ferramentas instalada como extensão nos navegadores internet, e a alteração da configuração dos navegadores instalados para que o mecanismo de busca padrão seja da Ask.com. Obviamente são buscas patrocinadas, ou sejam, que levam a resultados muitíssimo piores que os da busca do Google ou mesmo do Bing, e infestados de resultados patrocinados, onde o que mais interessa é gerar rendimento para a Ask, e não apresentar ao usuário resultados relevantes. Ou seja, o tipo de software — em geral indesejado — que é conhecido como adware.

Será realmente que um dos principais mecanismos de runtime de aplicações ricas na Internet como o Java, e uma hiper-mega-corporação global como a Oracle precisem de dinheiro extra pela revenda de patrocínio através da Ask para sustentar a distribuição e disseminação do Java? Será que realmente vale a pena associar o nome de uma empresa sólida como a Oracle e uma plataforma séria e tradicional (desde 1995!) como o Java, a um questionável adware??? Sinceramente, acredito que não!

Existe até uma petição pública (abaixo-assinado) na Internet para que a Oracle pare de distribuir o Ask na instalação do Java: Oracle Corporation: Stop bundling Ask Toolbar with the Java installer.

É fácil evitar a instalação do Ask, bastando desmarcar a opção na tela própria do assistente de instalação do Java as opções que vem marcadas por padrão:

Oferta para instalar o complemento de navegador da Ask

Mas como a maioria dos usuários não presta atenção e às vezes até não entende bem as opções, e simplesmente vai clicando “Avançar” até a conclusão da instalação, muitos acabam tendo instalada a extensão da Ask. Depois podem se assustar quando tentam fazer uma pesquisa na Internet e veem a página de resultados da Ask, sem nem se lembrarem de como isso foi parar ali!

Mesmo depois de instaladas as extensões da Ask, há opção de desinstalar o software pelo Painel de Controle do Windows, mas mais uma vez muitos usuários mais leigos ou desavisados sequer sabem disso ou procuram essa opção.

Lendo uma atualização de 12/12/2014 no site do abaixo-assinado contra o Ask ToolBar, descobri que a partir do Java 7 Update 65 (7u65) e Java 8 Update 11 (8u11) a Oracle passou a disponibilizar, no Painel de Controle Java, guia Avançado, opção que pode ser marcada para Suprimir ofertas de patrocinadores ao atualizar o Java.

Painel de Controle Java, guia Avançado, opção Suprimir ofertas do patrocinador ao instalar ou atualizar o Java.

O FAQ (Respostas a Perguntas Frequentes) da Oracle sobre “Como eu instalo o Java sem ofertas de terceiro patrocinador” ainda apresenta outra alternativa, mais técnica, para evitar a instalação de software patrocinado na instalação é executar o instalador pela linha de comando (Prompt/Console, cmd, ou Iniciar > Executar) acrescentando o parâmetro SPONSORS=0. Segundo o artigo, esta opção já existia antes do Java 7u65 e 8u11, mas não deixa claro desde quando.

Uma entrada no fórum SuperUser, How can I prevent Ask.com Toolbar from being installed every time Java is updated?, ensina que há um jeito de se adicionar entradas no registro do Windows que desabilitam em definitivo as ofertas de patrocinadores durante a instalação de Java: desativa_patrocinio_java.reg

Também quando se baixa a versão off-line do instalador do Java Runtime (JRE), disponível em Java.com e em Oracle TechNet (encontrado a partir da página inicial Java SE Downloads), a oferta de instalação da ferramenta patrocinada Ask não aparece.

Mas, mais uma vez, a maior parte dos usuários comuns não sabe disso e nunca chegará a estas alterativas, infelizmente!

No artigo Soluto’s data raises questions about how Oracle manages Java do blog da empresa Soluto, de soluções para suporte remoto para PCs e iOS, o instalador do Java é responsável por cerca de 40% das instalações indesejadas da Ask Toolbar em seus clientes. Além disso, pelo menos 60% dos clientes da Soluto que tiveram o Ask instalado providenciaram a desinstalação, mostrando o quanto a ferramenta é indesejada. E a empresa acredita que a maior parte dos que ainda não desinstalaram a Ask Toolbar têm a intenção de fazê-lo.

Se você é usuário do Java, recomendo evitar a instalação da Ask Toolbar ou desinstalar se você também acha um software indesejado. Se você é um representante da Oracle, por favor leve nosso protesto a quem de direito para retirar esse maldito e vergonhoso patrocínio de uma vez por todas!

Para saber mais:

31 Jul 15:39

7 motivos por que TDD falhou em ser mais utilizado

by Manuel Lemos

Recentemente, o criador do Ruby on Rails declarou que o TDD (Test Driven Development) está morto. Apesar de ter sido um grande apoiador do TDD como melhor método de desenvolvimento, ele finalmente decidiu anunciar que não concorda com isso.

Leia este artigo e entenda melhor o que aconteceu e o que isso quer dizer para desenvolvedores que utilizem ou não o TDD.

O que é TDD?

A comunidade de desenvolvimento de software é grande. Alguns desenvolvedores são bem mais experientes que outros. Portanto, eu acredito que deveria começar falando um pouco sobre o básico, para ajudar àqueles que não têm muita certeza do que seja TDD.

TDD é a sigla para Test Driven Development – Desenvolvimento Dirigido por Testes. É um processo de desenvolvimento de software que consiste em criar um código que teste se o código se o código real do projeto funciona como deveria.

Isso significa que primeiro você cria scripts de testes e depois o código em si do seu projeto. Então, você roda os testes para ver se algum deles apresenta erro. Nesse caso, você deve corrigir o código até que tudo esteja ok.

Kent Beck é o criador do TDD, ou ao menos quem redescobriu o processo em 2003 como forma de promover qualidade de software.

O debate

Na teoria, TDD é maravilhoso. Todo desenvolvedor quer escrever um software da melhor qualidade possível. Testes são uma forma de verificar que o software satisfaz os requerimentos do projeto. E isso não deveria ser algo ruim.

TDD é mais do que apenas escrever testes. É sobre escrever testes em primeiro lugar, antes mesmo de escrever qualquer componente do seu projeto. Isso leva a uma série de inconveniências que desencorajam muitos desenvolvedores a abraçar o TDD.

David Heinemeier Hansson, criador do framework Ruby On Rails, publicou um artigo admitindo que ele não usa TDD, apesar de no passado ter sido um dos defensores da metodologia como melhor forma de desenvolver software.

Após a publicação do texto, ele participou de um debate público com Kent Beck e Martin Fowler, promotores do Desenvolvimento Ágil/Agile. Esse debate aconteceu em diversos hangouts, que terminaram recentemente. Aqui está a playlist dos vídeos:

Então, por que o TDD falhou?

A argumentação contra o TDD é ampla. Diferentes oponentes ressaltam pontos distintos. Aqui, apresento apenas a minha opinião, com base no que eu acho ser mais relevante.

Algumas pessoas podem ter pontos de vista complementares, outras podem não concordar com os meus pontos. A ideia não é formar um consenso, mas oferecer a você uma perspectiva para refletir.

1. TDD é caro

Minha maior objeção ao TDD é que ele é caro. Se você precisa escrever testes para tudo antes de começar, obviamente o seu projeto vai levar mais tempo ou usar mais desenvolvedores.

Todo projeto de software pode falhar, especialmente se envolver dinheiro demais. Então, gastar ao escrever testes com antecedência pode fazer com que o seu projeto seja abortado antes de oferecer qualquer valor àqueles que estão pagando.

2. TDD vai atrasar o lançamento do seu projeto

Obviamente, escrever testes antes de começar vai fazer com que seu projeto demore mais para ser lançado. Alguns defensores dizem que o tempo gasto em fazer os testes será pago no futuro, se algum problema acontecer.

O fato é que você apenas vai quebrar algo se fizer uma mudança, ou mudar as dependências de forma significativa. Então, se você tem medo de quebrar algo e causar um problema no projeto devido a uma mudança, faça testes imediatamente após isso. Você não precisa escrever os testes do início.

Por outro lado, se você evita escrever testes do início, você ganha tempo, seu projeto será lançado mais cedo e o negócio pode ser validado mais rapidamente.

3. Você vai mudar os seus projetos, e os testes antigos serão ultrapassados

É bem frequente que projetos tenham seus objetivos alterados porque as empresas por trás deles percebem que algumas mudanças são necessárias, de forma que o negócio seja mais viável.

Quando os objetivos de um projeto mudam, ao menos uma parte do código se torna inútil, já que não será mais usado. Se você escreveu testes extensivos para o código que será deixado de lado, os testes também se tornarão inúteis. Acho que você já percebeu que o desperdício de tempo foi muito maior do que se você tivesse escrito apenas o código que fez o que deveria ter feito sem os testes.

4. Testar os meios dá mais trabalho que testar os resultados

Também é frequente a necessidade de mudar partes da implementação, sem alterar o resultado da sua aplicação. Por resultado, eu quero dizer por exemplo aquilo que precisa acontecer ou o que você tem que mostrar para o usuário.

O resultado é o principal a ser preservado. Se o software deixa de funcionar ou funciona de forma errada, os meios que foram usados para gerar o resultado também não estão funcionando corretamente. Para chegar a essa conclusão, você não tem que ter testado absolutamente todos os pequenos componentes de software que implementaram os meios.

No entanto, os componentes que implementaram os meios geralmente representam uma parte maior do seu projeto de software, então requerem muito mais testes, e você precisa gastar mais tempo nisso.

5. Testes extensivos são chatos

Testes não são funcionalidades. São apenas verficações. Para muitos desenvolvedores, gastar muito tempo escrevendo testes faz com que se sintam muito menos produtivos, porque eles precisam gastar muito mais tempo para ver o resultado final.

É por isso que muitos profissionais não gostam de escrever testes. Eles preferem que alguém faça isso para eles. Fazer com que um desenvolvedor escreva testes contra sua vontade é como uma tortura. E programar se torna uma obrigação dolorosa, ao invés de um prazer.

Com o tempo, muitos desenvolvedores passam a odiar ter que escrever testes, até mesmo os que são para verificar coisas cruciais que não podem falhar, como o resultado da aplicação.

Essa é provavelmente a razão pela qual muitos dos que eram entusiastas de TDD desistiram disso para sempre.

6. Muitos evangelistas de TDD não usam a metodologia na maior parte do tempo, mas não admitem isso

Muitos se tornaram tão entusiastas do TDD que pregavam isso como se fosse uma religião. Se alguém não praticava a metodologia, ganhava um sermão. Para esses evangelistas, não usar TDD era um pecado e você iria para o inferno quando morresse se não se convertesse à religião TDD.

Isso não seria tanto um problema se os praticantes do TDD não tivessem se arrependido e largado de mão. O problema é que, ao desistir, temeram admitir isso em público e apanhar dos pregadores, e seriam culpados de inconsistência nas suas crenças passadas.

Foi mais ou menos isso que aconteceu com David Heinemeier Hansson. Ao perceber que o TDD não era muito bom, ele evitou comentar sobre isso para não ser eventualmente julgado. Após 10 anos, ele finalmente resolver revelar tudo. Ele não usa TDD de forma alguma.

Ele também se sente mal por todos os outros profissionais que temem admitir que não usam o TDD apesar de terem sido entusiastas no passado. Alguns ainda usam a prática em parte dos seus projetos, especialmente em projetos Open Source, quando todos podem ver o que os outros fizeram. Mas, para projetos privados, eles raramente usam.

7. Muitos desenvolvedores com boa reputação não usam TDD       

Você não precisa ser super inteligente para perceber que TDD não é algo para ser usado em todos os seus projetos. É até mesmo estarrecedor que o David Heinemeier Hansson tenha levado 10 anos para declarar que TDD não é a melhor escolha para ele.

É ainda mais impressionante porque profissionais de renome e boa reputação já falaram contra o TDD no passado. É o caso dos fundadores do StackExchange, Joel Spolski e Jeff Atwood. Em 2009, eles declararam em um podcast do StackOverflow que não usavam TDD.

E eles não são os únicos a pensar assim. A questão é que eles não têm medo de falar o que pensam. Mas quando falaram, “Uncle Bob” (Robert Martin) discutiu com eles em um episódio seguinte do podcast. Então, outros desenvolvedores ficaram com medo de admitir isso publicamente.

Conclusões

Apesar disso, o TDD não está morto

Eu não acho que o TDD esteja realmente morto, como acredita o David Heinemeier Hansson. Acredito que ele está apenas com raiva de não ter falado tudo isso antes.

TDD ainda faz sentido em alguns casos, como por exemplo na implementação de protocolos padrão ou de especificações que não vão mudar ao longo do tempo.

Não obstante, a declaração de David Heinemeier Hansson foi útil porque ajudou a libertar vários outros desenvolvedores, que puderam admitir que não usam TDD. David é um grande influenciador na comunidade de desenvolvimento de software, então muitas pessoas prestam atenção no que ele fala. Pena que ele levou tanto tempo para se expressar.

Outras manias de desenvolvimento de software para você repensar

Eu acho que o TDD seja apenas mais uma mania, modinha, à qual muitos desenvolvedores têm se apegado por muito tempo. Mas não é a única. Aqui estão algumas outras manias similares com as quais muitos ainda estão obcecados porque acreditam serem dogmas de religião. Eu não vou falar muito sobre essa lista porque não é o foco deste artigo, apenas reflita sobre elas:

  • Tudo tem que ser um objeto
  • Forçar-se a usar quantos Design Patterns forem possíveis
  • Discutir estilo de formatação de códigos
  • Programar tem que ser feito de forma bonita
  • Contrate mais desenvolvedores para fazer programação em par (pair programming)

Sua opinião

Um pensamento importante que você deveria ter a partir deste artigo é que seu ponto de vista importa acima de todos os outros. Muitos seguem os “evangelistas de TDD” para, mais tarde, perceberem que não serve/não é bom para eles. Ainda assim, têm medo em admitir e nunca contestam abertamente.

Você não deveria tomar a opinião dos outros como leis a serem seguidas. Não vá cegamente pelo caminho e pelas ideias de outros. E isso inclui a mim. Não siga o que eu digo sem antes pensar no que é bom e no que é ruim para você. Se concorda comigo, ótimo. Se discorda, ótimo também. O que importa é que você reflita sobre outras opiniões, mas tire as suas próprias conclusões.

Se você apenas seguir as opiniões de outros e os seus projetos de software seguirem na direção errada, isso será culpa sua. Não vai adiantar dizer para o seu chefe e seus clientes que você seguiu a opinião de alguém – isso só vai fazer com que você pareça um bobo.

Então, se você discorda de mim e quer colocar o seu ponto de vista, comente neste artigo.

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://www.phpclasses.org/blog/post/237-7-Reasons-Why-TDD-Failed-to-become-Mainstream.html

Mensagem do anunciante:

Torne-se um Parceiro de Software Intel®. Filie-se ao Intel® Developer Zone. Intel®Developer Zone

31 Jul 13:55

Certificações em teste e qualidade de software

by Marcio d\'Avila

Ao fazer um levantamento sobre certificações disponíveis no mercado para atestar os conhecimentos teóricos de profissionais de teste e garantia da qualidade de software, descobri que existem mais de 20 certificações de pelo menos sete instituições certificadoras no mundo! Isso sem falar nas certificações em ferramentas e produtos específicos para teste de software (IBM, Borland etc.).

Cada instituição costuma dividir seu caminho de certificações em níveis, que podem estar entre os quatro seguintes: Fundamental, Profissional ou Avançado, Especialista, Gerencial.

Cada instituição certificadora propõe um corpo de conhecimento (body of knowledge – BOK) em teste de software, com a ementa de referência (syllabus) de cada certificação e, em geral, provê também um glossário sistematizado de termos.

Obviamente, como estão todos abordando a mesma disciplina de teste de software, existem muitas similaridades, mas há bastante variação de tópicos e abordagens.

As entidades certificadoras mais populares no Brasil parecem ser a International Software Testing Qualifications Board (ISTQB) de origem européia, através de seu braço nacional Brazilian Software Testing Qualifications Board (BSTQB); a Associação Latino Americana de Teste de Software (ALATS); e o Quality Assurance Global Institute (QAI), de origem americana.

O quadro resumo das certificações e detalhamento das informações está na seção Teste e Garantia da Qualidade de Software da página Hyperlink: Qualidade do Produto, Métricas e Teste de Software.

Mas são tantas siglas de instituições e certificações que você pode se confundir facilmente com essa sopa de letrinhas. Nesse caso, a variedade e diversidade mais complica do que ajuda. Seria muito bom se surgisse um esforço internacional de unificação ou consolidação destas ementas de referência…

30 Jul 03:49

Dormindo com o “inimigo”: por que o Google contratou um dos maiores hackers do mundo?

by Kaluan Bernardo
O Google contratou o hacker GeoHot para trabalhar no Project Zero, projeto que quer transformar a internet em um ambiente mais seguro. GeoHot, conhecido entre os advogados como George Hotz, responsável pelo primeiro jailbreak do iPhone e o único a desbloquear o PlayStation 3 até hoje, é um dos maiores especialistas do mundo em encontrar falhas de segurança. Após encontrar uma dessas brechas no Chrome, ele recebeu US$ 150 mil do Google e foi convidado ao Project Zero. Sobre o projeto, falamos...

Icone_Startupi Leia [+] no Startupi.com.br
28 Jul 16:33

Hospedagem de aplicações em máquinas virtuais Linux em cloud computing

by Augusto Campos

Enviado por Fernando Mariano (blogΘmariano·eng·br):

“Se você está procurando um VPS (virtual private server) pode ser por algumas razões. Como por exemplo: o provedor de webhosting compartilhado não atende mais a demanda de seu blog/site ou sua aplicação requer a instalação de uma dependência ou módulo que necessita de acesso à determinadas bibliotecas ou acesso root do sistema operacional.

Independente de qual for o motivo é preciso ter a consciência de que você terá que realizar a configuração de diversos aplicativos e serviços para que a sua aplicação atenda uma série de requisitos como segurança, serviço de email, esteja acessível remotamente para manutenção, garantia de backup e alta disponibilidade.

Os conceitos apresentados vão guiar na configuração e setup de uma VPS em provedores de cloud computing para disponibilizar uma aplicação na internet. O artigo não explica de forma detalhada todos os conceitos, mas apresenta boas referências de livros, sites e tutoriais de onde o conhecimento pode ser adquirido.” [referência: mariano.eng.br]

O artigo "Hospedagem de aplicações em máquinas virtuais Linux em cloud computing" foi originalmente publicado no site BR-Linux.org, de Augusto Campos.

25 Jul 18:26

Michael Paquier: Postgres 9.5 feature highlight: Display failed queries in psql

Postgres 9.5 is coming up with a new ECHO mode for psql that has been introduced by this commit:

commit: 5b214c5dd1de37764797b3fb9164af3c885a7b86
author: Fujii Masao <fujii@postgresql.org>
date: Thu, 10 Jul 2014 14:27:54 +0900
Add new ECHO mode 'errors' that displays only failed commands in psql.

When the psql variable ECHO is set to 'errors', only failed SQL commands
are printed to standard error output. Also this patch adds -b option
into psql. This is equivalent to setting the variable ECHO to 'errors'.

Pavel Stehule, reviewed by Fabrízio de Royes Mello, Samrat Revagade,
Kumar Rajeev Rastogi, Abhijit Menon-Sen, and me.

Up to now, there have been two ECHO modes:

  • "all", to print to the standard output all the queries before they are parsed or executed. This can be set when starting psql with option -a.
  • "queries", to have psql print all the queries sent to server. This can be set additionally with option -e of psql.

The new mode is called "errors" and can be either set with the option -b when starting psql or with "set" command in a psql client like that:

=# \set ECHO errors

The feature added is simple: have psql print all the failed queries in the standard error output. The failed query is printed in an additional field prefixed with STATEMENT:

=# CREATE TABLES po ();
ERROR:  42601: syntax error at or near "TABLES"
LINE 1: CREATE TABLES po ();
           ^
LOCATION:  scanner_yyerror, scan.l:1053
STATEMENT:  CREATE TABLES po ();

If multiple queries are specified within a single input only the query that failed is displayed:

=# CREATE TABLE aa (a int); CREATE FOO po; CREATE TABLE bb (a int);
CREATE TABLE
ERROR:  42601: syntax error at or near "FOO"
LINE 1: CREATE FOO po;
               ^
LOCATION:  scanner_yyerror, scan.l:1053
STATEMENT:  CREATE FOO po;
CREATE TABLE

Also, queries that are typed in multiple lines are showed as they are, spaces included:

=# SELECT
      col1_not_here,
      col2_not_here
   FROM
      table_not_here;
ERROR:  42P01: relation "table_not_here" does not exist
LINE 5:     table_not_here;
            ^
LOCATION:  parserOpenTable, parse_relation.c:986
STATEMENT:  SELECT
    col1_not_here,
    col2_not_here
FROM
    table_not_here;
10 Jul 18:43

Fraude dos boletos pode ter desviado mais de R$ 8 bilhões

by Ronaldo Gogoni

boleto

Sabe aquele e-mail pra lá de suspeito que você recebe de tempos em tempos, dizendo que existe uma cobrança em seu nome ou aquela mensagem estranha que alguém te enviou via rede social, ambos com um link para você clicar? Se você tem mais de 15 minutos de internet sabe que isso é fria, em quase 100% dos casos um malware ou uma página falsa de um banco procurando arranjar seu dinheiro. Só que esse golpe tem evoluído nos últimos anos, e agora uma investigação conjunta da empresa de segurança RSA e FBI descobriu que uma quadrilha internacional já pode ter causado um prejuízo bilionário com boletos falsos.

 

Esse tipo de golpe é uma particularidade nossa, já que boletos são uma forma de pagamento exclusivamente brasileira. Não é de hoje que falsários mandam cobranças para diversas pessoas na esperança de que algum incauto pague uma taxa inexistente. Como trabalhei muitos anos no CPD/financeiro de um comércio, boletos suspeitos enviados por “associações” eram uma constante. Hoje o golpe migrou para a internet, na esperança de pegar o internauta que não entende que não pode sair clicando em tudo que aparece. Entretanto, o novo malware chamado Bolware (Boleto + malware) trabalha de forma um tanto diferente.

A investigação da RSA envolve suas equipes no Brasil, Estados Unidos e Israel, bem como a Polícia Federal e o FBI, todos empenhados em identificar os falsários, já que parte das operações foram rastreadas até o território norte-americano. E ele já virou uma verdadeira praga: detectado em 2012, ele já possui 19 variações e infectou 192.227 computadores Windows em solo brasileiro, causando um prejuízo de mais de R$ 8,5 bilhões.

Eis como o Bolware opera: quando o usuário clica em um link suspeito o malware é instalado na máquina, que passa a monitorar as ações do navegador, seja IE, Firefox ou Chrome. Quando o usuário inicia uma transação ele entra em ação: quando o banco ou estabelecimento emitem um boleto para pagamento, ele intercepta a comunicação e exibe para o usuário um boleto falso, com informações trocas mas mantendo nome do cedente, valor do pagamento e outras facilmente identificáveis, visando dificultar a detecção de fraude. Ao realizar o pagamento, a grana vai obviamente para o falsário.

Pior: boletos impressos também são afetados. Ao realizar a digitação do número do código de barras no navegador, o malware identifica e troca o número, alterando a conta-corrente. Entretanto a exibição do número alterado só é feita após a realização do pagamento.

bolware-modus-operandi-001

Segundo a RSA, boletos emitidos pelo governo e operações realizadas via mobile não estão na mira dos hackers, a operação se concentra em desktops Windows. A Febraban alegou que foi informada do caso, mas se recusou a comentar. Entretanto, afirmou que as denúncias “parecem tecnicamente inconsistentes”, alegando que o sistema atual dos bancos é seguro e que o montante de pagamentos em boletos é irrisório, cerca de 4,5% do total. De qualquer forma, é bom ficar de olho aberto e não sair clicando em qualquer coisa.

Fonte: RSAFSP e G1.

The post Fraude dos boletos pode ter desviado mais de R$ 8 bilhões appeared first on Meio Bit.








10 Jul 18:31

É fantástico: grupo cria supercondutor e quebra recorde de dez anos

by Carlos Cardoso
oleonegro

Supercondutor em levitação magnética sendo atingido por um jato de ferrofluído.

A supercondutividade é um daqueles fenômenos físicos que a gente sabe como gerar, usa direto, depende dele diariamente em equipamentos como máquinas de Ressonância Magnética, mas os detalhes ainda fogem de nossa compreensão.

As propriedades de algumas substâncias quando muito resfriadas são… estranhas. Em alguns casos se tornam supercondutores, apresentando resistência elétrica zero. Transmitem energia sem perda nenhuma. Também apresentam o chamado Efeito Meissner, que ocorre quando o objeto supercondutor passa a repelir campos magnéticos. Como resultado temos… levitação.

O Santo Graal da supercondutividade é manter o efeito em temperatura ambiente. Quem conseguir isso tem um Nobel garantido, além da gratidão de todas as indústrias do planeta. SE for possível será uma revolução nunca vista. Para começar todas as molas serão substituídas por supercondutores, trens abandonarão as rodas e serão todos MagLevs, sem o custo imenso de energia dos trens levitadores de hoje. Você terá placas na parede e pendurará seus gadgets levitando, como um Post-It 3D.

O mais complicado de entender é que essa levitação magnética não é um simples imã, o supercondutor não está repelindo um polo. Não é um barco chato deslizando na lama. É uma pedra DENTRO da lama, que seria o campo magnético em volta. Veja o vídeo abaixo faz sentido se você imaginar um tubo de lama invisível e o supercondutor dentro dele:

Esse sonho ainda é distante, claro, mas aos poucos estamos avançando. Foi o que conseguiu um grupo da Universidade de Cambridge. Utilizando uma liga de óxido de cobre bário e gadolínio, produziram um supercondutor da categoria de alta temperatura, acima do ponto de ebulição do nitrogênio, – 196 °C. Isso por si só não é novidade, mas o condutor criado encapsulou um campo magnético de 17,6 tesla. Os ímãs do LHC tem potência de 8 T.

Esse recorde superou o anterior, de 17,2 tesla; estabelecido em 2003 por cientistas japoneses. Esperemos que não levem tanto tempo para quebrar o recorde atual.

O trabalho foi publicado na edição atual do periódico Superconductor Science and Technology.

Fonte: GG.

The post É fantástico: grupo cria supercondutor e quebra recorde de dez anos appeared first on Meio Bit.








09 Jul 15:58

Devrim GÜNDÜZ: Installing and configuring PostgreSQL 9.3 and 9.4 on RHEL 7

Red Hat Enterprise Linux 7 was released recently. The day after the release, we announced PostgreSQL 9.4 RPMs for RHEL 7, and last week we pushed PostgreSQL 9.3 RPMs for RHEL 7.

Since Red Hat switched to systemd as of RHEL 7, there are some changes in the packaging as compared to RHEL 5 and RHEL 6. So, how do you install and configura PostgreSQL on RHEL 7?
Continue reading "Installing and configuring PostgreSQL 9.3 and 9.4 on RHEL 7"
09 Jul 03:52

Validação de formulários com HTML5

by diego@tableless.com.br (Tableless.com.br)

O HTML5 trouxe diversas possibilidades e, principalmente facilidades para os desenvolvedores. Uma delas é relacionada a validação de formulários. O que antes era feito com algum tempo e JavaScript, hoje pode ser feito diretamente no HTML e em um tempo muito menor. Por isso, navegando pela internet achei no SitePoint esse artigo do Agbonghama Collins, um desenvolvedor nigeriano, que escreveu de forma rápida e direta alguns pontos da validação de formulários utilizando HTML5 e, resolvi traduzi-lo para nós.

Quando contruímos aplicações web, é importante levarmos a segurança a sério, especialmente quando essa precisa coletar dados dos usuários.

Não confiar em ninguém, é uma norma máxima de segurança, portanto, nunca confie que o usuário vá inserir valores corretos e válidos no formulário. Por exemplo, em um campo de e-mail, em vez de inserir um endereço de e-mail válido, o usuário pode digitar um e-mail inválido ou dados mal-intencionados, obviamente, ignorando a indicação da requisição do campo.

Quando se trata de validar valores de campos de formulários, isso pode ser feito no lado do cliente (navegador) e no lado do servidor (usando a linguagem que preferir).

No passado, validações no client-side só podiam ser feitas usando JavaScript ou algumas bibliotecas de frameworks (como o plugin jQuery validation). Mas isso está mudando, ou melhor, já mudou, porque a validação agora pode ser feita usando HTML5, sem a necessidade de escrever um código complexo de JavaScript para isso.

Validação de formulário com HTML5

HTML5 inclui um mecanismo bastante sólido na validação de formulários com base nos atributos da tag input:  type, pattern e require. Graças a esses novos atributos, você pode delegar algumas funções de verificação de dados para o navegador.

Vamos examinar esse atributos para ver como eles podem nos ajudar na validação de um formulário.

O atributo type

Esse atributo indica o tipo de controle de entrada de dados como o popular <input type="text"> para manipulação de dados de texto simples.

Alguns controles de formulários herdam sistemas de validação sem a necessidade de escrever qualquer código. Por exemplo, <input type="email"> valida o campo para garantir que o dado digitado seja de fato um endereço de e-mail válido. Se o campo tiver um dado inválido, o formulário não vai poder ser submetido até que esse erro seja corrigido.

Imagem mostrando a validação client-side em um campo de formulário

Teste o exemplo nesse link digitando um endereço de e-mail válido (Link do CodePen original).

Há também o <input type="number">, <input type="url"> e <input type="tel"> para validar números, URLs e telefones respectivamente.

Nota: Os formatos de números de telefone variam de país para país devido à inconsistência nos tamanhos e formatos. Como resultado, a especificação não define um algoritmo para validá-los, portanto não é suportado nos navegadores web no momento da escrita.

Lembre-se, a validação pode ser feita para o campo telefone em conjunto com o atributo pattern que aceita uma expressão regular, e que veremos a seguir.

O atributo pattern

O atributo pattern vai deixar os desenvolvedores felizes, principalmente aqueles que trabalham com front-end. Este atributo especifica um formato (na forma de expressão regular do JavaScript) em que o valor do campo é testado.

Expressões regulares são uma linguagem usada para analisar e manipular texto. Elas são frequentemente utilizadas para executar operações complexas de search-and-replace, e para garantir que os dados de texto estão corretos.

Hoje em dia, as expressões regulares estão incluídas na maioria das linguagens de programação, assim como em muitas linguagens de script, editores, aplicações, bancos de dados e ferramentas de linha de comando.

Expressões regulares (RegEX) oferecem um poderoso, conciso e flexível meio para encontrar string ou textos com caracteres particulares, palavras ou padrões de caracteres.

Ao passarmos uma string RegEX como valor para o atributo pattern, conseguimos definir qual valor é aceitável pelo campo do formulário e também informar ao usuário de possíveis erros.

Vamos ver alguns exemplos de como usar expressões regulares para validação de dados em campos de formulário.

Números de telefone

Como mencionado, o input tel não é totalmente suportado pelos navegadores web devido à inconsistência no formato dos números de telefone em diferentes países.

Por exemplo, no meu país, a Nigéria, o formato do telefone é xxxx-xxx-xxxx, que seria algo como 0803-555-8205.

A RegEX ^\d{4}-\d{3}-\d{4}$ corresponde ao formato, portanto, o HTML ficaria assim:

<label for="phonenum">Número de telefone:</label>
<input pattern="^\d{4}-\d{3}-\d{4}$" type="tel">

Veja aqui um exemplo. (Link do CodePen original).

Valores alfanuméricos

O exemplo a seguir corresponde a caracteres alfanuméricos (combinações de letras do alfabeto e números).

<input pattern="[a-zA-Z0-9]+" type="text">

Veja aqui um exemplo. (Link do CodePen original).

Usuário do twitter

Essa expressão regular corresponde a um usuário do Twitter com o símbolo @. Por exemplo: @tech3sky

<input pattern="^@[A-Za-z0-9_]{1,15}$" type="text">

Veja aqui um exemplo. (Link do CodePen original).

Modo de cor HEX

Esse corresponde a cores hexadecimais. Por exemplo #3b5998 ou #000.

<input pattern="^#+([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$" type="text">

Veja aqui um exemplo. (Link do CodePen original).

Dando algumas dicas

Para fornecer ao usuário uma descrição da pattern, ou um erro reportando que o valor inserido está inválido, você pode utilizar o atributo title, dessa maneira:

Imagem mostra o atributo title de um campo de formulário ao usuário deixar o mouse sob ele

Imagem mostra erro devido à entrada de dados inválidos em um campo de formuláriop

Veja aqui um exemplo. (Link para o CodePen original).

Se você é novo com expressões regulares, você pode consultar esse documento no WebPlatform para lhe dar uma ajuda inicial. No entanto, na maioria dos casos, você pode usar o Google para ajudar a procurar a expressão regular que você quer, ou até mesmo utilizar uma ferramenta para ajudá-lo.

O atributo required

Esse é um atributo booleano usado para indicar que um determinando campo de formulário é obrigatório para o envio do mesmo. Ao adicionar esse atributo a um campo de formulário, o navegador obriga o usuário a inserir dados naquele campo antes de enviar o formulário.

Essa validação substitui a validação básica de formulário implementada com JavaScript, tornando as coisas um pouco mais úteis e nos poupando algum tempo de desenvolvimento.

Exemplo: <input name="my_name" required="" type="text"> ou <input name="my_name" required="required" type="text"> para compatibilidade XHTML.

Imagem mostra erro da validação HTML5 em um campo de formulário que é obrigatório.

Todos os links de exemplos acima utilizam o atributo required, assim você pode testá-los tentando submetê-los sem digitar nada nos campos.

Resumo

O suporte dos navegadores para as features de validação de formulários é bem grande, e você pode utilizar polyfills quando necessário.

Vale a pena lembrar que confiar apenas no navegador (client-side) para a validação pode ser perigoso, pois isso pode ser contornado por um usuário mal-intencionado ou por bots.

Nem todos os navegadores suportam HTML5 e nem toda entrada de texto enviada para seu script virá do formulário. Isso significa que validação do lado do servidor também deve estar antes do envio dos dados para o processamento do servidor.

Texto traduzido e adaptado do artigo escrito pelo Agbonghama Collins em 06 de junho de 2014. Tradução autorizada pelo autor.

Dei um fork em todos os exemplos do CodePen colocando o texto em português, mas mantive os links para os originais também.

Qualquer erro ou sugestão de melhoria na tradução, é bem vinda! :)

---
Este artigo foi escrito por Raphael Fabeni.

Visite o nosso site para mais posts sobre desenvolvimento web! Tableless.

08 Jul 19:12

Android continua a não ser seguro! 86% equipamentos afectados

by Pedro Pinto
Sistema permite que se obtenham credenciais e outra informação sensível Quando se fala em vulnerabilidades/malware no segmento dos dispositivos móveis, o Android é o primeiro a quem se aponta o dedo. As notícias dos últimos anos tentam “vender”, de certa forma, que esta plataforma é uma das mais vulneráveis, mas também há quem defenda que […]
03 Jul 14:29

15 fatos sobre programação que você provavelmente não sabia

by pichiliani

0_Capa

A tarefa de programar está se tornando cada vez mais comum, porém ainda existem muitos fatos que as pessoas não conhecem sobre programadores e a programação em si. Acompanhe comigo o conteúdo deste post que apresenta 15 fatos pouco conhecidos sobre a programação.

Assim como outras atividades intelectuais, a tarefa de programar e de como as pessoas aprendem a programar computadores é muito estudada. De fato, com cada vez mais pessoas aprendendo a programar, independente da linguagem, ferramenta ou plataforma utilizada, é natural que poucas pessoas realmente saibam de certos fatos importantes já bem conhecidos sobre programação e desenvolvimento de software.

Do ponto de vista acadêmico, as áreas de engenharia de software e educação apresentam diversos estudos muito interessantes obtidos por meio de experimentos cujos resultados são apresentados em teses de mestrado e doutorado ou papers da área. E com base nestes resultados que escolhi os fatos a serem citados neste post junto com as devidas referências. A propósito, se você leitor ficou com vontade de saber mais sobre cada um dos pontos discutidos aqui recomendo fortemente procurar a referência completa para mais informações.

Com base neste contexto, vou apresentar 15 fatos importantes e que, infelizmente, são pouco conhecidos por quem programa. Mas antes faço um aviso: estes fatos apresentam resultados de pesquisas experimentais e empíricas que possuem contextos específicos. O que quero dizer é que existe certa margem para discussão da aplicabilidade e generalização, porém conhecer o que já foi estudado e descoberto é importante e, no mínimo, pode instigar a discussão e o quão perto da realidade do leitor são estas informações.

1) Programadores demoram para pedir ajuda quando tem problemas

1_PedirAjuda

Este é um fato relacionado à maneira como as pessoas aprendem a programar, pois basicamente o ensino segue a linha de aprendizado da matemática: um pouco de teoria, um ou dois exemplos e muitos exercícios. Este formato leva os aprendizes a tentar muito nos exercícios e, muitas vezes, resolver tudo sozinhos sem pedir ajuda.

Esta atitude não é ruim e é até recomendada, porém é preciso saber até que ponto deve-se deixar de tentar e pedir alguma forma de ajuda.

Referência: Andrew Begel e Beth Simon. Novice software developers, all over again.  ICER ’08 Proceedings of the 4thinternational Workshop on Computing Education Research, 2008.

2) Programadores possuem uma tendência a reportar de forma incompleta seus problemas

2_Erro

Este fato é relacionado com pesquisas da área da psicologia. Os resultados indicam que quando uma pessoa tem algum problema ela não comunica todas as informações completas sobre os problemas, especialmente quando ela é o responsável de forma direta ou indireta.

Este resultado já foi comprovado experimentalmente com programadores e uma das principais justificativas é a seguinte: reportar de forma completa um problema é visto como sinal de fraqueza que pode levar a algum tipo de julgamento da habilidade e proficiência por quem está ouvindo o relato. Esta situação é muito mais comum quando se trata de um erro básico de principiante.

Referência: Shrauger, J.S. and T.M. Osberg. The Relative Accuracy of Self-Predictions and Judgments by Others in Psychological Assessment. Psychological Bulletin, 1981. 90(2): p. 322-351.

3) Programadores procuram outras formas de ajuda antes de falar com colegas de trabalho

3_StatckOverflow2O fato da comunicação com outras pessoas não assumir a prioridade quando um programador precisa de ajuda está relacionado novamente à sensação de julgamento que outras pessoas fazem ao saber da dificuldade.

Não obstante, sites como o StackOverflow e outros floresceram explorando este tipo de comportamento através da agregação da ajuda com diversos aspectos de comunidades para desenvolvedores.

Referência: LaToza, T.D., Venolia, G., and Deline. R. Maintaining Mental Models: A Study of Developer Work Habits. in Proc. ICSE. 2006: IEEE.

4) O progresso na programação pode ser classificado em quatro fases

4_Progresso4
A classificação do progresso de um programador é importante para auxiliar diversas métricas envolvidas no desenvolvimento de software, além de ajudar gerentes de projeto e outras pessoas a avaliar como anda o projeto como um todo.

Além disso, também é importante saber em qual fase de progresso o desenvolvedor está para, entre outras atitudes, oferecer algum tipo de ajuda para que ele não fique muito tempo emperrado em um local específico ao ponto de atrasar alguma entrega. Uma classificação interessante identificou (de forma automática) quatro possíveis estados de progresso:

a)    Programação complexa
b)    Fazendo progresso
c)    Progresso lento
d)    Emperrado (stuck)

Referência: Jason Carter,  Prasun Dewan. Design, Implementation, and Evaluation of an Approach for Determining When Programmers are Having Difficulty. ACM Group 2010.

5) Programadores encontram barreiras superáveis e insuperáveis

5_BArreira2 Este fato pode parecer óbvio, mas ele é muito importante de ser detectado, uma vez que uma barreira de programação pode levar a sérios problemas de prazo, moral da equipe e confiança.

Uma das principais dificuldades de se detectar barreiras e classifica-las está no fato que esta informação pode ser subjetiva. Ou seja, perguntar diretamente para o programador se ele está com alguma barreira superável ou insuperável já afeta o resultado, pois ele nem sempre pode ser sincero. Há também algumas implicações em termos de ego e moral apenas pelo fato de identificar este tipo de barreira na programação,

Referência: Andrew J. Ko et al. Six Learning Barriers in End-User Programming Systems. 2004 IEEE Symposium on Visual Languages – Human Centric Computing.

6) São 6 os tipos de barreiras relacionadas à programação

6_Barreira

Além da classificação de barreiras de programação em superáveis e insuperáveis há um estudo muito interessante que caracterizou cada uma das possíveis barreiras de programação.

Para ajudar a entender as barreiras os pesquisadores evidenciaram frases comuns em cada uma das classificações abaixo:

a) Design: “Eu não sei o que o computador deve fazer”.
b) Seleção: “Eu sei o que fazer, mas não sei o que usar”.
c) Coordenação: “Sei o que usar, mas não sei como combinar o que preciso”.
d) Uso: “Eu sei o que usar, mas não sei como usar”.
e) Compreensão: “Pensei que sabia usar X, mas ele não faz o que eu esperava”.
f) Informação: “Compreendo o que aconteceu, mas não consigo checar”.

Referência: Andrew J. Ko et al. Six Learning Barriers in End-User Programming Systems. 2004 IEEE Symposium on Visual Languages – Human Centric Computing.

7)  Programadores passam aproximadamente 30% do tempo navegando no código fonte
7_Navegando

Quem programa sabe que a maior parte do tempo é gasta dentro de uma ferramenta de edição de código fonte. Contudo, como o tempo é divido entre as tarefas de edição do texto representado pelo código fonte ainda não está claro do ponto de vista científico.

De acordo com um estudo muito importante, descobriu-se que aproximadamente 30% do tempo de trabalho de um programador não é gasto editando o texto (incluindo, alterando o excluindo), mas sim navegando entre diversos arquivos com códigos fontes. Esta navegação envolve pesquisa, observação, coleta de informações, memorização e outras atividades. Ou seja, dá para dizer que a programação é uma atividade cuja terça parte é apenas contemplativa.

Referência: Andrew J. Ko et al. An Exploratory Study of How Developers Seek, Relate, and Collect Relevant Information during Software Maintenance Tasks. Journal IEEE Transactions on Software Engineering archive Volume 32 Issue 12, December 2006 pp. 971-987.

8) Produtividade de programadores remotos é menor que a produtividade de programadores locais

8_FastEsta afirmação sobre produtividade é polêmica, especialmente quando se fala cada vez mais em home office, trabalho remoto e projetos globais de desenvolvimento de software. De qualquer maneira, existem evidências concretas baseadas em diversas métricas de software que, de fato, programadores remotos não produzem tanto quanto programadores que estão no mesmo local.

Faz sentido pensar desta maneira se analisarmos os outros fatos desta lista como, por exemplo, a preferência pela falta de comunicação com outras pessoas. De fato, a comunicação informal é um dos principais fatores que influenciaram o resultado desta pesquisa, pois pedir aquela dica no encontro durante uma pausa para o café é muito importante de acordo com que foi apurado.

Referência: Herbsleb, J.D., et al. Distance, dependencies, and delay in a global collaboration. In: Proceedings of the 2000 ACM Computer-Supported Cooperative Work conference.

9) A maioria dos programadores é masculina, branca e jovem
9_computer-programmer

Esta afirmação sobre a diversidade de programadores não veio exatamente de uma pesquisa acadêmica, mais sim de Adda Birnir que é a fundadora do site de recrutamento e seleção Skillcrush. Esta declaração foi apresentada no vídeo “Is CODE the most important language in the world?”.

Atualmente é muito comum destacar minorias na programação, espacialmente a baixa quantidade de mulheres. Contudo, como certos dados mostram este não é o único perfil que possui baixa representatividade na programação e isso pode ter uma implicação séria quando falamos em código de aplicações que precisam lidar de forma adequada com certos grupos de usuários.

Referência: declaração da Adda Birnir sobre diversidade de codificadores no vídeo “Is CODE the most important language in the world?”.

10) As principais mensagens de erro, erro em tempos de execução e erros de compilação e o tempo médio para resolvê-los

10_compile-time-error2

Mensagens de erro, problemas de tempo de execução e erros de compilação são muito específicos para cada linguagem. Para destacar alguns casos cito a tese de mestrado da pesquisadora Suzanne Marie Thompson, pois ela analisou uma grande quantidade de programadores Java em diversos cenários e coletou muitos dados interessantes sobre isso. As tabelas abaixo contam um pouco da história sobre erros e tempo médio para corrigi-los.

Apesar do estudo se concentrar em um contexto muito específico (aprendizado da linguagem Java) é possível fazer um paralelo com outros cenários e situações e comprovar que realmente boa parte dos erros mais comuns acontece em diferentes contextos.

TempoMedioResolverProblemasTabela 6.2. Erros mais frequentes (Java) e tempo médio de execução

TabelaErrosDeExecucaoTabela 6.12. Frequência de erros de execução em Java (runtime)

TabelaErrosCompilacao Tabela D.1 Principais erros de compilação (Java) e tempo médio para resolvê-los

Referência: Thompson, Suzanne Marie. An exploratory Study of Novice Programming Experiences and Erros. Tese de mestrado defendida em 2004 na inversidade de Victoria, Canadá.

11) A manutenção de software consome mais de 50% do esforço

11_legacy-code

A manutenção de um software envolve a manipulação de código legado, assunto que já abordei antes. Porém desta vez cito um estudo que fala sobre esforço e que mostra como resultado que a divisão não é igual entre a criação e manutenção.

No estudo que citou este valor de mais de 50% de esforço há uma ótima discussão sobre evolução do software no sentido da sua manutenção e das tarefas necessárias para tanto. Com certeza vale a pena dar uma olhada nesta referência antes de tomar aquela decisão sobre começar a desenvolver a solução do zero ou trabalhar com uma base de código existente.

Referência: Kemerer C.F. and Slaughter S. An Empirical Approach to Studying Software Evolution, IEEE Transactions on Software Engineering, 25(4), pp. 493-509, 1999.

12) A manutenção de software consome entre 40 e 90% dos custos

12_Sem título

Uma das principais regras de quem trabalha com negócios diz que é muito mais caro conseguir um cliente novo do que manter um cliente já existente. Contudo, de acordo com pesquisas da área de engenharia de software a realidade é um pouco diferente quando se fala de código: manter um código em funcionamento através de tarefas de manutenção pode chegar a custar até 90% de todos os custos do projeto.

Estas estatísticas são bem genéricas e foram obtidas em um contexto muito particular das 487 organizações estudadas nesta pesquisa, sem contar que a data do estudo é de 1980. Certamente existem diversos fatores a serem considerados, mas ao menos existe um ponto de partida para analisar cursos e discutir sobre este aspecto quando se fala de manutenção de software.

Referência: Lientz, B.; Swanson, E.B. Software maintenance management: a study of the maintenance of computer application software in 487 data processing organizations. Addison Wesley, 1980.

13) O trabalho de manutenção de software é dividido em 4 tarefas básicas

13_Sem título

Ainda falando sobre manutenção de código fonte, um estudo que influenciou muito a comunidade de engenharia de software classificou por meio da análise de resultados de questionários as principais práticas da manutenção de software. Quatro práticas foram identificadas:

a)    Melhoria: envolvem mudanças de funcionalidades
b)    Adaptativa: são mudanças no ambiente para adaptação a requisitos
c)    Corretiva: atividades para a correção de erros
d)    Preventiva: melhorias para evitar problemas futuros

A classificação das práticas de manutenção é muito importante para auxiliar medições, organizar e rastrear bugs, agrupar funcionalidades em novas versões e gerenciar o trabalho de programadores.

Referências: Lientz, B.; Swanson, E.B. Software maintenance management: a study of the maintenance of computer application software in 487 data processing organisations. Addison Wesley, 1980.

Lientz, B.; Swanson, E.B.; Tompkins, G.E. Characteristics of applications software maintenance, Communications of the ACM, Vol. 21, pp.466-471, 1978.

14) Custos da correção de defeitos após a implantação são 10x maiores do que na fase de construção e 100x maiores do que na fase de design
14_059.TRONSS.32_905

Este fato é um clássico da área e motivou a evolução dos processos tradicionais de desenvolvimento de software até chegarmos ao que temos hoje. O principal ponto aqui é a identificação de custos elevados quando não se dá a devida atenção à construção e design do software.

Referências: Barry W. Boehm: Software Process Management: Lessons Learned from History. ICSE 1987: 296-298.

15) Revisão de código pelos pares consegue descobrir até 60% dos defeitos

15_PairProgramming

A revisão de código feita por outas pessoas, seja na modalidade de pair programming ou não, é realmente efetiva. Existem diversos estudos sobre isso, mas um dos principais indica que até 60% dos defeitos podem ser descobertos (mas não necessariamente corrigidos) quando mais de uma pessoa revisa o código fonte.

Este estudo é relativamente antigo e pode-se dizer que ele é um dos principais influenciadores de técnicas envolvendo processos ágeis (Agile) e outras formas de desenvolver software cujo principal foco é nas atividades, etapas, organização e outras habilidades não tão técnicas quanto a programação.

Referência: Barry W. Boehm: Improving Software Productivity. IEEE Computer 20(9): 43-57, 1987.



01 Jul 22:19

Criando uma aplicação Single Page com AngularJS

by diego@tableless.com.br (Tableless.com.br)

angularjs

Um dos melhores conceitos que o AngularJS oferece é o de “Single Page”, onde os recursos apropriados são dinamicamente carregados e adicionados à página, conforme necessário, geralmente em resposta a ações do usuário.

Para isto acontecer o framework oferece módulos que te possibilitam ter apenas uma página index, com outras páginas de conteúdo (views) sendo carregadas de acordo com uma específica rota (route).

Deste modo, você consegue separar bem as responsabilidades do seu projeto, e ficando de fácil manutenção e codificação dos elementos.

Vamos parar de enrolação e ver como isso funciona em um rápido exemplo…

Estrutura de pastas

Primeiro criamos uma estrutura de pastas simples para o exemplo:

app
    controllers
        controllers.js
    views
        home.html
        sobre.html
        contato.html
    app.js
index.html

Configurando a aplicação

É aqui que o show acontece, no arquivo app.js, primeiro, carregamos o módulo ngRoute que é usado para deep-linking URLs para controllers e views. Este módulo observa qual é url ($location.url()) e tenta mapear o caminho de acordo com alguma rota pré-definida, assim ele consegue executar o controller e a view correspondente para cada url.

Para setarmos uma configuração no AngularJS, precisamos usar a função config, que pode receber diversas propriedades já existentes do angular.

var app = angular.module('app',['ngRoute']);

app.config(function($routeProvider, $locationProvider)
{
   // remove o # da url
   $locationProvider.html5Mode(true);

   $routeProvider

   // para a rota '/', carregaremos o template home.html e o controller 'HomeCtrl'
   .when('/', {
      templateUrl : 'app/views/home.html',
      controller     : 'HomeCtrl',
   })

   // para a rota '/sobre', carregaremos o template sobre.html e o controller 'SobreCtrl'
   .when('/sobre', {
      templateUrl : 'app/views/sobre.html',
      controller  : 'SobreCtrl',
   })

   // para a rota '/contato', carregaremos o template contato.html e o controller 'ContatoCtrl'
   .when('/contato', {
      templateUrl : 'app/views/contato.html',
      controller  : 'ContatoCtrl',
   })

   // caso não seja nenhum desses, redirecione para a rota '/'
   .otherwise ({ redirectTo: '/' });
});

Neste exemplo recebemos duas propriedades, $locationProvider e $routeProvider.

O $routeProvider é usado para configurar as rotas, que é exatamente o que estamos fazendo, quando usamos o .when(), definimos a rota (url) e depois um objeto com o templateUrl (url da view) e qual nome do controller correspondente.

O $locationProvider é usado para configurar como a aplicação com os chamados “deep-linking” irão ser armazenados, no nosso caso nós usamos a propriedade para definir true o modo de html5 (html5Mode), isso faz com que não fique com # nas URLs.

Definindo os controllers

Para criar um controller no angular, podemos usar a instância do módulo, que definimos anteriormente como “app”, deste modo definimos um controller mais modular (é como prefiro :]) ou de forma tradicional como sempre criamos funções no JavaScript (function HomeCtrl($rootScope, $location){ //do stuff… }).

Por ser apenas um exemplo, coloquei todos meus controllers no mesmo arquivo, mas se você quiser pode colocar em arquivos diferentes, só não esqueça de linka-los à index.

Criaremos o primeiro controller com o nome de ‘HomeCtrl’, igual ao que definimos nas rotas mais acima, estamos passando para ele dois parâmetros, $rootScope e $location, já definidos no Angular.

app.controller('HomeCtrl', function($rootScope, $location)
{
   $rootScope.activetab = $location.path();
});

app.controller('SobreCtrl', function($rootScope, $location)
{
   $rootScope.activetab = $location.path();
});

app.controller('ContatoCtrl', function($rootScope, $location)
{
   $rootScope.activetab = $location.path();
});

Note que para cada controller, realizamos a mesma funcionalidade, para saber em qual página estou, criei uma variável no $rootScope, o que a torna global na página, podendo ser acessada de qualquer local.
Para a variável activetab atribuímos o caminho da localização que estamos, no caso, seria atribuído ‘/’ logo quando acessamos o exemplo.

Eis a Single Page!

Aqui está nossa index, ela servirá para carregar os nossos templates definidos previamente nas rotas.

Nesta página contemos algumas diretivas (directives) do próprio angular, mas o que é uma diretiva?
Basicamente, diretivas em angular são atributos de tags HTMLs que definem o comportamento do mesmo. O próprio angular já oferece muitas diretivas, mas você pode criar a sua própria se desejar.

O recomendado quando se usa AngularJS e você deseja fazer alterações no DOM, é criar uma diretiva personalizada para tal…

<!doctype html>
<html ng-app="app">
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <meta http-equiv="content-language" content="pt-br" />
      <title>AngularJS: Single Page com ngView e ngRoute</title>
      <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
   </head>

   <body>
      <ul class="nav nav-tabs">
         <li ng-class="{active: activetab == '/'}"><a href="#/home">Home</a></li>
         <li ng-class="{active: activetab == '/sobre'}"><a href="#/sobre">Sobre</a></li>
         <li ng-class="{active: activetab == '/contato'}"><a href="#/contato">Contato</a></li>
      </ul>

      <div ng-view></div>

      <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular.min.js"></script>
      <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular-route.min.js"></script>

      <script src="app/app.js"></script>
      <script src="app/controllers/controllers.js"></script>
   </body>
</html>

Note que a linha mais importante de código aqui é a que contém a diretiva ng-view, é ali que seus templates irão carregar, aquilo que você definiu previamente nas rotas, irão ser abertas dentro desta div.

Para usarmos o conceito de Single Page no angular, é necessário usarmos a diretiva ng-view. Ela é a responsável por renderizar suas devidas ‘views’ à sua index principal.

Para cada rota, definimos uma view anteriormente, certo?
Àquelas views serão carregadas dentro desta div, dependendo da rota que o usuário estiver.

Mesmo não sendo o foco do artigo, coloquei uma diretiva ng-class para adicionar a classe active ao elemento <li> se a condição for verdadeira. Ou seja, quando o controller é ativado pelo angular, ele atribui na variável activetab a sua localização atual.

Se estivermos na rota ‘/’ a classe active irá ser adicionada ao primeiro elemento <li>.
Se estivermos na rota ‘/sobre’ a classe active irá ser adicionada ao segundo elemento <li>.
Se estivermos na rota ‘/contato’ a classe active irá ser adicionada ao terceiro elemento <li>.

Criando as views

Como é apenas um exemplo, procurei agilidade, então, os templates são praticamente iguais.

<!-- home.html -->
<div class="page-header">
   <h1>Página Home!</h1>
</div>
<!-- sobre.html -->
<div class="page-header">
   <h1>Página Sobre!</h1>
</div>
<!-- contato.html -->
<div class="page-header">
   <h1>Página Contato!</h1>
</div>

Finalizamos por aqui pessoal, a ideia era de passar exatamente como criamos uma aplicação Single Page com AngularJS, espero que todos consigam!

---
Este artigo foi escrito por Lucas Caprio.

Visite o nosso site para mais posts sobre desenvolvimento web! Tableless.

25 Jun 22:30

Deus

by Francisco Nunes

Ateísmo: Deus é uma ilusão.
Deísmo: Deus é uma idéia.
Panteísmo: Deus é um objeto.
Misticismo: Deus é uma experiência.
Cristianismo: Deus é uma Pessoa.

(Matt Smethurst)

Send to Kindle
23 Jun 15:06

idade

by Francisco Nunes

Não tenho mais idade pra ficar velho.

(Francisco Nunes)

Send to Kindle
23 Jun 14:09

Hackers ameaçam sites de empresas durante a Copa

by Diego Remus

A agência de notícias Reuters divulgou que hackers associados ao grupo Anonymous declararam ter derrubado sites relacionados à Copa do Mundo da Fifa, incluindo o site oficial do governo sobre a Copa, o da patrocinadora Hyundai (ou este), o do Estado do Mato Grosso e o da Agência Brasileira de Inteligência (ABIN). Quando chequei, todos esses sites, incluindo o da FIFA (ou este), estavam funcionando normalmente.

 

EMPRESAS

Esta semana, hackers divulgaram uma lista com 27 sites que dizem ter afetado. Também alegam terem conseguido logins de 450 emails gov e divulgaram uma relação de sites de empresas patrocinadoras que seriam afetadas: Adidas, Sony, Coca-Cola, Garoto, Visa, Liberty Seguros, Emirates, Centauro, Itaú, Wise Up, Mc Donald's, Oi, Budweiser, entre outras.

Segundo a Reuters, um hacker chamado Che Commodore declarou: "estivemos ocupados nos últimos dias e ainda vem mais pela frente". "Companhias e instituições que trabalham com um governo que nega os direitos básicos do seu povo para promover um evento esportivo privado, exclusivo e corrupto serão nosso alvo". Hackers divulgaram até uma relação de sites de empresas patrocinadoras que seriam afetadas.

A Reuters disse que o hacker declarou que atacaram esses sites com DDOS (ataque distribuído de negação de serviço, causado por excesso de acessos) e também fizeram "deface" (desconfiguração visual) em outros sites governamentais - o que pode ser mais preocupante, pois significa que conseguiram acessar os sistemas.

Daniele Cunha falou à Reuters em nome do governo de Mato Grosso confirmando que foram hackeados e levaram 30 minutos para restabelecer a normalidade. Um representante do Ministério de Defesa alegou que detectaram um aumento de ativismo hacker mas nenhum ataque a site governamental foi oficializado. Um representante da ABIN disse que o site andou passando por manutenção, inclusive ficando fora do ar, e negou terem sofrido ataque. A Hyundai também negou que fosse ataque.

The post Hackers ameaçam sites de empresas durante a Copa appeared first on Startupi.com.br.

22 Jun 23:07

Globo e FIFA dão rasteira em paraplégico

by Carlos Cardoso

globomente5

Essa imagem que você está vendo foi o ápice da Abertura da Copa. Mostrando que não só o Brasil é capaz de organizar eventos de nível mundial, mas também acreditamos e investimos em ciência e tecnologia. Nem de longe é uma solução, mas uma prova de conceito, uma demonstração da pesquisa feita pelo neurocientista Miguel Nicolelis e sua equipe da Duke University.

Eles conseguiram desenvolver sistemas de biofeedback que permitiam a macacos controlar com o cérebro membros robóticos, e recentemente chegaram a recuperar o sentido do tato. A pesquisa apresentada, fruto de uma promessa de quatro anos, foi cumprida: um paraplégico, vestindo um exoesqueleto deu o chute inicial da Copa do Mundo.

Isso, claro, se você acreditar nas palavras de Nicolelis, pois todo mundo que viu a abertura da Copa, cortesia da Tia Mariângeles e a turma de Arte & Artesanato da 5ª Série da Escola Municipal Tancredo Neves, achou que o trabalho de Nicolelis estava mais para Flash do que Homem de Ferro.

Vendido (e comprado) como ponto-alto da abertura da Copa, o Exoesqueleto se resumiu a isto:

Bp86il8IAAEk4j9

Isso mesmo. Tela dividida. Galvão Bueno narrava a IMPORTANTÍSSIMA chegada do ônibus com a Seleção Brasileira enquanto ignorava a demonstração tecnológica de um dos raros brasileiros que não se destaca com os pés ou com a bunda. Não é exagero. Em tela dividida foram TRÊS SEGUNDOS, sem nenhuma menção, para em seguida cortar para o ônibus.

A Ciência foi colocada literalmente para escanteio. A apresentação ficou restrita ao canto do gramado. A justificativa da FIFA? O Exoesqueleto era muito pesado e iria danificar o gramado delicado criado pela vó. Claro, com certeza um exoesqueleto pesa muito mais do que um palco-bola com motores. Pombas, a bunda da Jennifer Lopez pesa mais que o exoesqueleto e não foi proibida de acompanhar a dona pelo gramado.

No Twitter comentaram que a apresentação fez sentido, mostrou a exata atenção que o Brasil dá a Ciência e Tecnologia.

Por isso, meninos e meninas, que nosso programa espacial tem datilógrafos. Quase nada fazemos e quando fazemos, não valorizamos. O piloto de provas do exoesqueleto estava usando um lenço cedido pela família de Santos Dumont, um gesto simbólico unindo invenções e inventores separados por mais de 100 anos. Mas isso não interessa, melhor ver uma bosta de um ônibus.

Isso me faz ver o quanto estamos atrasados. Nas Olimpíadas nos EUA, em 1984, o mundo se deslumbrou ao ver um homem voar.

30 anos depois não conseguimos ver um homem andar.

Leia também aqui no MB:

Leia no Contraditorium:

The post Globo e FIFA dão rasteira em paraplégico appeared first on Meio Bit.








18 Jun 23:19

Coca-Cola cria ideias para reutilização das garrafas PET

by administrador@bytequeeugosto.com.br (Marcel Dias)

O artigo Coca-Cola cria ideias para reutilização das garrafas PET faz parte do conteúdo do Byte Que Eu Gosto! - Nerd, Geek, Dicas, Cinema, Games e mais!.

Esse comercial é muito bacana. Nele, a Coca-Cola mostra como reaproveitar as garrafas PET de uma forma criativa e útil. Foram criadas diversas tampas especiais, onde você pode transformar a garrafa num borrifador para regar plantas e até mesmo num apontador de lápis.

E se houvesse vida para a garrafa após a Coca-Cola, é a pergunta. Muito legal!

O artigo Coca-Cola cria ideias para reutilização das garrafas PET faz parte do conteúdo do Byte Que Eu Gosto! - Nerd, Geek, Dicas, Cinema, Games e mais!.

18 Jun 21:21

gabrielle roth: autovacuum: long naps aren’t better

There’s that saying about “the first time’s an accident, the second’s a coincidence, the third is a pattern”. It’s probably because I’ve been studying Postgres’s autovacuum feature so much lately and these things stand out to me now, but I’ve noticed a really intriguing pattern (n>5) over the past month or so: folks with their […]
08 Jun 14:15

Hack no Windows XP permite updates por mais 5 anos

by Pedro Pinto
Tenha suporte no seu Windows XP até 2019 Lançado em Outubro de 2001, o Windows XP chegou ao mercado quando o nível de propagação da banda larga e o número de equipamentos ligados à Internet eram ainda muito baixos, assim como a sofisticação e ocorrência de ataques cibernéticos. Passados 12 anos, a Microsoft anunciou oficialmente […]
02 Jun 21:55

Robert Treat: Postgres 9.4 - A First Look

Today I gave a talk at pgcon about the upcoming features in 9.4. As the beta was just last week, I think it's a fairly accurate representation of what should ultimately end up in 9.4. Of course, in the course of a talk I couldn't cover everything, but I think it should give a good primer for anyone looking to upgrade.

I want to give a big thanks to Magnus Hagander and Dave Page, who did talk on earlier versions of 9.4, which was invaluable in helping me put together my own slide deck. Also thank to Michael Paquier and Hiekki Linnikagas who provided supplemental materials. Also no one could do these talks without the work of depesz; I would strongly encourage those looking for more information on 9.4 to check out his blog. Finally, I'd like to thank all of the Postgres Developers who have worked on the 9.4 release, without whom we wouldn't have a release.

01 Jun 16:37

Processamento paralelo com Python

by Alan Raphael

O processamento paralelo, ou assíncrono, permite o uso de threads paralelas para distribuição do processamento. A utilização deste método de processamento possibilita que uma tarefa não tenha que correr de forma sequencial, porém a quebra do processamento em threads deve ocorrer quando as elas não geram dependência entre si, caso contrário elas ficarão bloqueadas até esta dependência ser satisfeita.

O que são threads?

Threads são linhas de execuções que compartilham a área de memória com outras linhas, diferente do processo tradicional, que conta com uma área de memória própria.

As linhas de execução (threads) são criadas dentro de processos. Cada processo conta com ao menos uma linha de execução, sendo possível criar outras linhas dentro do mesmo processo. Quando um processo é finalizado, todas as threads correspondentes a ele são encerradas.

Processamento paralelo

O processamento paralelo utiliza várias linhas de execução para a resolução de um mesmo problema, atacando cada uma delas em uma parte do trabalho e se comunicando para a troca de resultados intermediários ou no mínimo para a divisão inicial do trabalho e para a junção final dos resultados.

ar01

Uma das vantagens de utilizar o processamento paralelo está em explorar com mais eficiência a capacidade da CPU, deste modo é possível aumentar a performance quando se trabalha com processamento de grandes cargas de dados ou quando necessitamos realizar tarefas em background.

Observação: Quando estamos lidando com otimizações e alto desempenho, existe uma quantidade de linhas de execução aceitáveis. Criar o maior número de threads possíveis pode causar um efeito inverso, fazendo o desempenho cair consideravelmente. Então, deve-se tomar cuidado! Realize testes e monitore os processos.

O Python no cenário multithread

O Python dispõe de um módulo nativo chamado thread, que é interpretado por sistemas que suportam o posix threads (pthreads). Também há o módulo threading, que é uma interface de alto nível que provê uma maneira mais fácil de lidar com threads, porém o módulo thread é muito simples de usar.

Para este exemplo vou utilizar a versão 3.3 do CPython.

Para utilizar o módulo _thread, antes de tudo, devemos importá-lo:

import _thread

Vamos agora criar uma tarefa a ser executada:

def task(task_name):
	print(“Thread : %s” % (task_name))

O módulo thread provê uma śerie de funções para manipular as tarefas criadas, porém a mais importante (e que nos interessa) é a função start_new_thread. Ela é a responsável por criar a thread. Para criá-la você deve passar como primeiro parâmetro o nome da função que pretende executar na thread, o segundo parâmetro deverá ser uma tupla com os argumentos que serão passados para a função que será executada.

_thread.start_new_thread(task, (“Tarefa 1”,))
_thread.start_new_thread(task, (“Tarefa 2”,))

Ao executar este código você não verá nada, pois o processo é encerrado rapidamente. Para isso temos que manter o processo aberto, então vamos adicionar o seguinte código:

while True:
            pass

O código deve estar assim:

import _thread

def task(task_name, delay):
	print(“Thread : %s” % (task_name))

_thread.start_new_thread(task, (“Tarefa 1”,))
_thread.start_new_thread(task, (“Tarefa 2”,))

while True:
	pass

Ao executar o código no terminal, não se pode notar se realmente as threads estão sendo criadas. Para que possamos ver as threads em ação vamos criar um delay entre as saídas dos processos. Para ficar mais interessante o nosso código, utilizaremos um loop onde a cada N tempo, uma das threads irá escrever algo na tela.

Para retardar o loop vamos utilizar o módulo time e executar a função sleep.

import _thread
import time

def task(task_name, delay):
	ct = 0
	while ct < 5:
		time.sleep(delay)
		print(“Thread : %s” % (task_name))
		ct += 1

_thread.start_new_thread(task, (“Tarefa 1”, 2))
_thread.start_new_thread(task, (“Tarefa 2”, 4))

while True:
	pass

Agora vamos fazer um incremento ao código. Vamos utilizar uma variável global para definir o número de loops.

import _thread
import time

max_loop = 5

def task(task_name, delay):
	global max_loop
	ct = 0
	while ct < max_loop:
		time.sleep(delay)
		print(“Thread : %s” % (task_name))
		ct += 1

_thread.start_new_thread(task, (“Tarefa 1”, 2))
_thread.start_new_thread(task, (“Tarefa 2”, 4))

while True:
	pass

Vale ressaltar que, diferente dos processos, as threads compartilham a área de memória, então é preciso tomar cuidado ao compartilhar variáveis globais, pois podem haver problemas de concorrência.

É muito simples lidar com processamento paralelo em Python, porém temos um pequeno problema em nosso código: quando que o processo irá morrer?

Manter o processo aberto sem necessidade pode ser péssimo! Por outro lado, se não deixarmos o processo aberto, ele será finalizado quando o código chegar ao fim, e todas as threads abertas serão encerradas. Então, o que fazer? Vamos aproveitar a memória compartilhada e criar um contador para as threads. A ideia é simples: quando a thread for criada, será incrementado 1 ao contador e quando for encerrada vamos decrementar 1. O loop while, que mantém o processo aberto, também será alterado.

import _thread
import time

num_thread = 0
max_loop = 5

def task(task_name, delay):
	global num_thread, max_loop
	num_thread += 1
	ct = 0
	while ct < max_loop:
		time.sleep(delay)
		print(“Thread : %s” % (task_name))
		ct += 1
	num_thread -= 1

_thread.start_new_thread(task, (“Tarefa 1”, 2))
_thread.start_new_thread(task, (“Tarefa 2”, 4))

while num_thread > 0:
	pass

Porém, agora temos outro problema: antes que a num_thread seja incrementada, a nova thread vai para a fila de processamento, então antes que ela seja capaz de incrementar o contador, o processo corrente será finalizado. Para resolver este problema, criaremos uma flag para sinalizar quando a primeira thread começar a ser processada, também teremos que retardar a execução do loop, que contará as threads, criando outro laço que irá aguardar a mudança de estado.

import _thread
import time

num_thread = 0
max_loop = 5
thread_started = False

def task(task_name, delay):
	global num_thread, max_loop, thread_started
	thread_started = True
	num_thread += 1
	ct = 0
	while ct < max_loop:
		time.sleep(delay)
		print(“Thread : %s” % (task_name))
		ct += 1
	num_thread -= 1

_thread.start_new_thread(task, (“Tarefa 1”, 2))
_thread.start_new_thread(task, (“Tarefa 2”, 4))

while not thread_started;
	pass
while num_thread > 0:
	pass

Agora temos o controle do processo. Ao serem encerradas as threads o processo também será finalizado.

29 May 06:42

Como hackear, resetar e recuperar a senha root Ubuntu

by Maudy T. Pedrão

Aprenda a como hackear, resetar ou recuperar a senha root Ubuntu

Pode ser um choque para alguns, pois a fama do Linux é justamente ser seguro. Ele é seguro. Mas se perdermos a senha de um sistema usando Ubuntu, como podemos recuperá-la ou simplesmente criar outra? Enfim, use com moderação e saiba que o blog não se responsabiliza por atos ilícitos, ok? Vamos lá!

Como recuperar a senha root Ubuntu?

PASSO 1

No menu GRUB, ao ligar o sistema, entre no menu de modo de recuperação. Na versão 2.0 do GRUB, escolha “Opções avançadas…”

senha root ubuntu

Irá aparecer uma tela preta com várias linhas de comando. Apenas espere.

PASSO 2

Aqui você escolhe a opção ROOT como na imagem abaixo:

senha no grub

PASSO 3

Use o comando abaixo para listar todos os usuários do sistema:

ls /home

Escolha o nome do perfil que queira alterar a senha:

passwd username

Aparecerá os comandos abaixo onde você deverá digitar a nova senha e confirmá-la:

Enter new UNIX password:
Retype new UNIX password:

Se aparecer uma mensagem de erro tipo esta:

passwd: Authentication token manipulation error

Quer dizer que a partição esta montada com acesso apenas de leitura. Para mudar isto, digite:

mount -rw -o remount /

E tente mudar a senha novamente.

fonte

Espero que tenha conseguido a recuperar a senha do root Ubuntu.

Como hackear, resetar e recuperar a senha root Ubuntu is a post from: Ubuntu Dicas

O post Como hackear, resetar e recuperar a senha root Ubuntu apareceu primeiro em Ubuntu Dicas.

25 May 15:22

How to use ngMessages in AngularJS

How to use ngMessages in AngularJS

ngMessages is a new feature in AngularJS 1.3 for rending error messages in forms

Forms in AngularJS are a delight to work with since they naturally work off of the basics of how forms work in HTML. The ngModel directive peacefully works with form controls and the state of a form can easily be examined using the name of the form and the name of each input control. But what about displaying error messages? Since there is no set way of displaying error messages in forms, there are many inconsistent ways to do this in AngularJS. While showing and hiding a message is easy using ngIf or ngSwitch, making use of multiple messages across and entire application is where things get messy. Do we really have to use ngIf over 20 times for a form just to show a handful of messages? How can we reuse a template of messages in other parts of the application? Are forms really this complicated?

The new ngMessages module introduced in AngularJS 1.3-beta.8 is designed to render error messages in a reusable and maintainable way. Instead of having to butcher up your template code by placing ngIf statements everywhere, ngMessages listens on the `model.$error` object and then decides which messages to display based on what is present in the template.

Interested in learning more? let's explore the ngMessages and ngMessage directives in detail.

Read More
19 May 22:07

Jim Mlodgenski: Trigger Overhead

I recently had discussions with some folks about triggers in PostgreSQL. They had two main questions.

  1. What is the overhead of putting a trigger on a table?
  2. Should a trigger function be generic with IF statements to do different things for INSERT, UPDATE and DELETE?

So I created a simple test to verify some assumptions.

First, I created a simple table and made it UNLOGGED. I didn’t want the overhead of the WAL to possibly dwarf the timings of the triggers.

CREATE UNLOGGED TABLE trigger_test (
 key serial primary key, 
 value varchar, 
 insert_ts timestamp, 
 update_ts timestamp
);

I then create two scripts to push through pgbench and get some timings.

INSERTS.pgbench

INSERT INTO trigger_test (value) VALUES (‘hello’);
UPDATES.pgbench

\set keys :scale
\setrandom key 1 :keys
UPDATE trigger_test SET value = 'HELLO' WHERE key = :key;

I ran these with the following pgbench commands:
pgbench -n -t 100000 -f INSERTS.pgbench postgres
pgbench -n -s 100000 -t 10000 -f UPDATES.pgbench postgres

The result is that I created 100,000 rows in the test table and then randomly updated 10,000 of them. I ran these commands several times with dropping and recreating the test table between each iteration and the average tps values I was seeing were:
Inserts: 4510 tps
Updates: 4349 tps
Then to get the overhead of a trigger, I created a trigger function that just returns. I then repeated the process of running the pgbench commands as before.

CREATE FUNCTION empty_trigger() RETURNS trigger AS $$
BEGIN
 RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER empty_trigger BEFORE INSERT OR UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE empty_trigger();

The result with the empty trigger were:
Inserts: 4296 tps
Updates: 3988 tps

That results in a 4.8% overhead for inserts and an 8.3% overhead for updates. I didn’t dig further as to why is appears that the overhead for a trigger on an update is almost twice as high as on an insert. I’ll leave that to a follow-up when I have some more time. A 4%-8% overhead of placing a trigger on a table will likely not be noticed in most real-world applications, the overhead of what is executed inside the trigger function can be noticed, which led to the next topic.
I then wanted to see the overhead of having a single trigger function versus having separate trigger functions for inserts and updates.

For a single trigger function, I used the following:

CREATE FUNCTION single_trigger() RETURNS trigger AS $$
BEGIN
 IF (TG_OP = 'INSERT') THEN
 NEW.insert_ts = CURRENT_TIMESTAMP;
 RETURN NEW;
 ELSIF (TG_OP = 'UPDATE') THEN
 NEW.update_ts = CURRENT_TIMESTAMP;
 RETURN NEW;
 END IF;
 RETURN NULL; 
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER single_trigger BEFORE INSERT OR UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE single_trigger();

And for separate trigger functions, I used:

CREATE FUNCTION insert_trigger() RETURNS trigger AS $$
BEGIN
 NEW.insert_ts = CURRENT_TIMESTAMP;
 RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION update_trigger() RETURNS trigger AS $$
BEGIN
 NEW.update_ts = CURRENT_TIMESTAMP;
 RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER insert_trigger BEFORE INSERT ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE insert_trigger();
CREATE TRIGGER update_trigger BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE update_trigger();

I then reran the same process as before to see the overhead.

Single Trigger Inserts: 3569 tps
Single Trigger Updates: 3450 tps

Separate Triggers Inserts: 3623 tps
Separate Triggers Updates: 3870 tps

It turns out that splitting the trigger function into separate functions does make a difference. For the insert trigger, keeping things as a single trigger only added 1.5% to the overhead, but for the update trigger, a single trigger function added nearly 11% overhead. The is most likely due to the update case being handle second in the trigger function. That’s another thing to dig into when there is time.

 

19 May 03:02

The Grumpy Programmer's Guide To Software Company Culture

My co-worker Jarvis wrote a blog post about how he doesn’t care about your company culture.

I don’t know what prompted this blog post, but I disagree with many fundamental parts of it. Jarvis seems like a decent guy (have only met him online and not yet in person) but I think Jarvis is making a mistake that many people (not just software developers) make: thinking that the rest of the world should think like you when it clearly doesn’t.

Initially I was going to make a point-by-point takedown of his blog post, but halfway through it I realized I was being an asshole. So instead I want to discuss where I think he’s wrong.

I mentioned on Twitter to Jarvis that it’s just as important to work on your co-workers as all the other things you do. He said two interesting things in response:

  • “This is what I don’t like about big companies”
  • “I work on computers, not people”

First, I’ve worked at places both big and small. There is nothing inherently better about either type. Do not kid yourself thinking only great things happen at small companies. Great things happen when groups of smart people get together and are given permission to go make it happen.

I’ve seen it work at large places where managers “ran interference” so their employees could make stuff happen. I’ve seen small places stifle creativity in their employees out of fear of change.

It’s not the size of the company that matters, it’s how they choose to work together that matters.

The second point is the one where I think Jarvis has made the biggest mistake in his approach.

We work FOR people USING computers. The computer is a tool to achieve a goal. Those goals are achieved by working with others, whether you wish to believe that or not.

As someone who has worked from home for a long time, I understand and sympathize with many of the things Jarvis indicates that bug him. Noise from co-workers, drive-by meetings, feeling pressure to participate in social activities.

But at the end of the day, to accomplish anything of substance requires me to interact with others in REAL TIME. I have proven that I can both get work done AND not be such an asshole that others choose to ignore the remote worker instead of working hard to integrate me into the team.

I find that socializing with my co-workers is a good way to learn about the people I work with, so that when I need something from them later they are less likely to say “why the fuck should I help that guy?” I need their help as much as they need mine.

Of course, I don’t think this bit at the end helps much:

I am extremely productive in this setting. For five years I have cranked out more code in this room than many people have written in their entire careers.

I am exceedingly good at my job and I am sick of being told that my lack of cultural involvement makes me a bad employee.

mic drop

Disrespecting co-workers isn’t always the smartest move. Failing to understand why others think that being aloof and distant and insisting that results matter more than how you treat people isn’t always the smartest move either.

Tech is not a meritocracy, despite our wishes for it to be. Jarvis, you’re not alone in wanting people to just leave you the hell alone so you can get some work done. I am that way many times, but I make sure to create those bonds with my co-workers so that the amount of time that I spend with them is enjoyable instead of resenting the time there.

Nothing creates bonds of friendship like a shitty job, but being friendly with your co-workers costs you nothing but a little time, time you’re probably spending doing horrible things like reading the comments on Hacker News.

18 May 21:37

Hubert 'depesz' Lubaczewski: Waiting for 9.4 – Add support for wrapping to psql’s “extended” mode.

On 28th of April, Greg Stark committed patch: Add support for wrapping to psql's "extended" mode. This makes it very   feasible to display tables that have both many columns and some large data in some columns (such as pg_stats).   Emre Hasegeli with review and rewriting from Sergey Muraviov and reviewed by Greg Stark […]
11 May 01:24

Zend Framework 2 – Hydrators, Models e o Padrão TableGateway

by Rafael Jaques

O post Zend Framework 2 – Hydrators, Models e o Padrão TableGateway apareceu primeiro em PHPit.

hydrator3

Introdução

O Zend Framework 2 vem embalado com uma variedade de novos recursos e funcionalidades que agilizam o desenvolvimento em alguns cenários comuns, tais como a interação com bancos de dados, sistema de template, cache, etc.

Não importa se estamos falando dos novos elementos de formulário HTML5 e seus view helpers, a nova implementação do Zend\Http, o Service Manager, o Event Manager ou de tantos outros módulos – é possível perceber que o ZF2 anda arrebentando a boca do balão!

Mas existem alguns recursos que se sobressaem perante aos outros e, recentemente, têm me feito sorrir! Estou falando de: Hydrators, Models e Table Gateways. Se você é novo no ZF2 ou nunca utilizou interação de frameworks com bancos de dados, este artigo foi escrito pra vocês, pois dá uma boa introdução à utilização desses dois juntos.

Alguma vez você já se perguntou para que servem Hydrators ou Table Gateways? Hoje veremos esses componentes mais de perto!

Nós iremos trabalhar com um código de exemplo que ensinará como criar models desacoplados da lógica do recebimento de dados através de uma configuração simples no ServiceManager. Veremos também um não-tão-complexo hydrator que será capaz de extrair informações de uma consulta no banco de dados e preencher o model automaticamente.

Por que essa abordagem?

No Zend Framework 1, quando você precisava ter uma camada de modelo independente da fonte de dados, nem sempre era simples de implementar. Embora se fale muito em PHP e MySQL, todos sabemos que existe uma infinidade de opções, tais como: MongoDB, CouchDB, PostgreSQL, Cassandra, Redis e muito, muito, muito mais.

Algumas aplicações que desenvolvemos, podem começar com necessidades simples, modestas. No começo, talvez um SGBD básico seja suficiente. Mas, conforme suas necessidades mudam e crescem, é bom saber que, sem muita refatoração de código, será possível adaptar seu sistema.

O que veremos hoje é isso! Permitiremos uma fonte de dados quase transparente, pois a camada de modelo não saberá nada sobre essa fonte. Isso vai nos ajudar a assegurar que, independentemente de qual local venha a informação, ela será transformada de uma maneira que o model seja capaz de utilizá-la.

Como funciona?

hydrator_uml
Vou tentar explicar em poucas palavras como funciona. Primeiro, a classe TableGateway realiza a interação específica com a base de dados, como buscar, adicionar, atualizar ou remover dados. Nesse caso, vamos trabalhar com um banco MySQL 5. Então, a classe Hydrator irá mapear (e transformar, quando necessário) a informação recuperada da TableGateway para classe Model.

No método getServiceConfig na nossa classe Module, as duas são unidas e o Hydrator é configurado. Por fim, em uma action do controller poderemos acessar fonte de dados, consultar registros, auto-popular nossa Model e então trabalhar sobre os registros retornados. Ótima ideia, não?

Então vamos lá!

A classe TableGateway

Aqui nós temos a classe TableGateway. Eu a chamei de UserTable, pois ela é responsável por gerenciar informações em uma tabela de usuários. Mantive simples, focando apenas em retornar os registros, deixando de lado as outras operações de CRUD.

No construtor, passamos um objeto TableGateway que será disponibilizado na configuração do ServiceManager. Isso nos fornece um modo simples de acessar o banco de dados. No método fetchAll(), recuperamos um result set chamado o método select() no objeto TableGateway.

As chamadas de buffer() e next() não são exatamente necessárias, mas precisamos incluí-las já que, na ação da listagem, um objeto Paginator é retornado. Erros serão lançados se esses dois métodos não forem chamados.

namespace Phpit\Model;

use Zend\Db\TableGateway\TableGateway;

class UserTable
{
    protected $tableGateway;

    public function __construct(TableGateway $tableGateway)
    {
        $this->tableGateway = $tableGateway;
    }

    public function fetchAll()
    {
        $resultSet = $this->tableGateway->select();
        $resultSet->buffer();
        $resultSet->next();
        return $resultSet;
    }
}

A classe Modelo

Agora temos a classe User. Como você pode ver, esta classe não tem nenhum código referenciando qualquer tipo de fonte ou banco de dados. Aqui estamos preocupados apenas com os dados que desejamos trabalhar, que neste caso são:

  • userId: id do usuário
  • userName: nome
  • userActive: flag de ativo/inativo
  • createdDate: data de criação do registro

O método exchangeArray é comumente utilizado no ZF2 para passar um dataset, nesse caso um array para auto-popular o objeto. É possível ver que eu estabeleci todas as propriedades relacionadas com as chaves respectivas do array. Fácil, limpo e simples.

namespace Phpit\Model;

class User
{
    public $userId;
    public $userName;
    public $userActive;
    public $createdDate;

    public function exchangeArray($data)
    {
        if (isset($data['userName'])) {
            $this->userName = $data['userName'];
        } else {
            $this->userName = null;
        }

        if (isset($data['userActive'])) {
            $this->userActive = $data['userActive'];
        } else {
            $this->userActive = null;
        }

        if (isset($data['userId'])) {
            $this->userId = $data['userId'];
        } else {
            $this->userId = null;
        }

        if (isset($data['createdDate'])) {
            $this->createdDate = $data['createdDate'];
        } else {
            $this->createdDate = null;
        }
    }

    public function getArrayCopy()
    {
        return get_object_vars($this);
    }
}

O Hydrator

O Hydrator é o aspecto-chave da configuração. É o que mapeia e faz a ligação entre os nomes dos campos no banco de dados e as propriedades da entidade no outro. No entanto, ele não armazena essas informações internamente (como você verá). Tentei ir para uma abordagem mais genérica, uma classe que possa ser aplicada em qualquer tabela.

Com isso nós podemos passar um array de Nomes de Colunas -> Propriedades da Entidade e, quando o método de hydrate for chamado, transferir a informação respectiva da fonte de dados para o objeto de modelo.

namespace Phpit\Hydrator;

use ReflectionMethod;
use Traversable;
use Zend\Stdlib\Exception;
use Zend\Stdlib\Hydrator\AbstractHydrator;
use Zend\Stdlib\Hydrator\HydratorOptionsInterface;

class TableEntityMapper
    extends AbstractHydrator
    implements HydratorOptionsInterface
{
    protected $_dataMap = true;

    public function __construct($map)
    {
        parent::__construct();
        $this->_dataMap = $map;
    }

    public function extract($object) {}

No método de hydrate, passamos a fonte de dados e o modelo. Se o modelo não for um objeto, nós lançamos uma excessão do tipo BadMethodCallException. Se for, prosseguimos e começamos a iterar sobre os dados disponíveis.

Se não houver um mapeamento disponível, vamos determinar qual campo no modelo corresponde a determinado campo na fonte de dados. Se isso não precisaremos realizar essas determinações (se nenhum mapeamento for necessário), vamos definir a propriedade diretamente. Quaisquer propriedades desconhecidas ou ausentes são silenciosamente ignoradas.

    public function hydrate(array $data, $object)
    {
        if (!is_object($object)) {
            throw new Exception\BadMethodCallException(sprintf(
                '%s expects the provided $object to be a PHP object)',
                __METHOD__
            ));
        }

        foreach ($data as $property => $value) {
            if (!property_exists($this, $property)) {
                if (in_array($property, array_keys($this->_dataMap))) {
                    $_prop = $this->_dataMap[$property];
                    $object->$_prop = $value;
                } else {
                    // unknown properties are skipped
                }
            } else {
                $object->$property = $value;
            }
        }

        return $object;
    }
}

A configuração do Module Service

Na configuração do Módulo nós iremos juntar tudo. Sem este componente, não seria possível fazer o resto. Em primeiro lugar, vamos registrar um objeto na lista de factories, que inicializará UserTable e passará o objeto para o TableGateway.

Em seguida, temos a configuração do objeto TableGateway. A abordagem que vamos utilizar é baseada no tutorial de hydration do Evan Coury. O que temos aqui é o seguinte: durante a execução o adapter do banco é retornado, nós inicializamos uma instância do hydrator e a alimentamos com o array de mapeamento; os nomes das colunas da tabela à esquerda e as propriedades do modelo à direita. Em seguida, fornecemos o User model como o protótipo a ser usado com o objeto HydratingResultSet. Se você não estiver familiarizado com ele, o HydratingResultSet é uma parte surpreendente do Zend Framework 2. Não vou tentar reinventar a roda, então vou apenas traduzir o que está escrito no manual:

Zend\Db\ResultSet\HydratingResultSet é uma versão mais flexível do objeto ResultSet que permite aos desenvolvedores escolherem uma “estratégia de hydration” (hydration strategy) para pegar dados de uma linha em um objeto-alvo. Enquanto estiver iterado sobre os resultados, HydratingResultSet pegará o protótipo de um objeto-alvo e o clonará para cada linha. O HydratingResultSet então irá preencher (hydrate) esse objeto clone com os dados da linha.

Basicamente, nós fornecemos o modelo e os dados e o hydrator cuida do resto. Depois disso, retornamos um novo objeto TableGateway especificando a tabela referente, tbluser, que será a fonte de dados, o adapter de banco de dados e o objeto de resultset que acabamos de inicializar. Agora, nós temos os dois lados da equação bem unidos. Caso ocorra mudança no nome da tabela, nas propriedades ou nos nomes das colunas, só precisamos fazer pequenos ajustes aqui ou ali. Não há necessidade de tocar em outras classes ou trechos de código.

public function getServiceConfig()
{
    return array(
        'factories' => array(
            'PhpitAdmin\Model\UserTable' =>  function($sm) {
                $tableGateway = $sm->get('UserTableGateway');
                $table = new UserTable($tableGateway);
                return $table;
            },
            'UserTableGateway' => function ($sm) {
                $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                $hydrator = new \Phpit\Hydrator\TableEntityMapper(
                    array(
                        'UserID' => 'userId',
                        'UserName' => 'userName',
                        'StatusID' => 'userActive',
                        'CreatedOn' => 'createdDate'
                    ));
                $rowObjectPrototype = new \Phpit\Model\User;
                $resultSet = new \Zend\Db\ResultSet\HydratingResultSet(
                    $hydrator, $rowObjectPrototype
                );
                return new TableGateway(
                    'tbluser', $dbAdapter, null, $resultSet
                );
            }
        )
    );
}

O Controller

Maravilha! Agora está na hora de aprender como usar. No nosso controller nós possuímos uma action de listagem. Ela irá retornar os registros e nós iremos iterar sobre eles.

namespace PhpitManagementAdmin\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Phpit\Model\User;
use Phpit\Form\AddUserForm;
use Phpit\Form\EditUserForm;

class userController extends AbstractActionController
{
    protected $userTable;
    protected $_createUserForm;

    public function listAction()
    {

Em primeiro lugar, recuperamos uma cópia do objeto userTable através do Service Locator. Em seguida, chamamos o método fetchAll(), e recuperamos todas as linhas da tabela. Eu usei um filter iterator e um paginator para deixar o exemplo mais completo.

O filter iterator só retornará registros nos quais o campo userActive estiver definido como “ativo”. Todos os outros serão ignorados. Este iterator é então passado para o objeto Zend\Paginator, algumas propriedades são definidas e então é devolvido no ViewModel, pronto para ser iterado na nossa view.

        $sm = $this->getServiceLocator();
        $userTable = new \Phpit\Model\UserTable(
            $sm->get('userTableGateway')
        );

        $filterIterator = new StatusFilterIterator(
            $userTable->fetchAll(), "active"
        );
        $paginator = new Paginator(new Iterator($filterIterator));

        $paginator->setCurrentPageNumber(
            $this->params()->fromRoute('page')
        );

        $paginator->setItemCountPerPage(
            $this->params()->fromRoute('perPage', 10)
        );

        return new ViewModel(array(
            'paginator' => $paginator,
            'status' => $this->params()->fromRoute('status')
        ));
    }
}

Considerações finais

E, finalmente, estamos prontos. Com apenas um pouquinho de código, criamos um model que é capaz de interagir com uma variedade de fontes de dados e, ao mesmo tempo, conseguimos evitar um acoplamento forte. Se formos sair do MySQL para o PostgreSQL ou o Redis, então poderemos fazer algumas pequenas alterações e tudo estará funcionando novamente.

Eu não sou tão experiente com hydrators, data mappers e table gateway pattern como os outros, portanto adoraria que vocês contassem e dessem suas sugestões para melhorar essa abordagem. Aproveito e deixo a dica de um artigo sobre Strategies, escrito por Jurian Sluiman.

Espero que tenham gostado e que possa ser útil no desenvolvimento.

Um abraço a todos e fiquem com Deus!
Texto adaptado do original: http://www.maltblue.com/tutorial/zendframework2-hydrators-models-tablegateway-pattern

O post Zend Framework 2 – Hydrators, Models e o Padrão TableGateway apareceu primeiro em PHPit.