Category: Javascript

Image Upload for Redactor with Ruby on Rails

Redactor is an awesome wysiwyg editor, and one of its features is image uploading. To do that, according to their (also awesome) documentation, you need to setup a service to receive a post request with the image to upload, and respond with a specific json.

The implementation of that service on your existent Ruby on Rails application is fairly simple. I’ll assume you have basic Rails knowledge, and you’re familiar with Carrierwave, a Ruby library to handle file uploads. Also, I’ll be presenting an example code.

So, first step, you may need to generate a controller to handle that service.

rails g controller images

Next, setup a route for it:

post 'images/upload', to: 'images#upload_image', as: 'upload_image'

A sample code for the controller may be as such:

class ImagesController < ApplicationController
  skip_before_filter :verify_authenticity_token, only: [:upload]
  def upload
    uploader = TemporaryImageUploader.new #your uploader
    uploader.store!(params[:file])
    file_url = request.protocol + request.host_with_port + uploader.url
    render json: [{ filelink: file_url }]
  end
end

A few notes on the code above: please note the skip_before_filter part – without it, your app may lose the authentication session after the request from Redactor.

For the javascript, just add the imageUpload parameter and set it to your image_upload_url path, defined on the routes.

And that’s it.

Best Movies Watchlist

Chrome Web Store, para quem não conhece, é uma loja de aplicativos que rodam dentro do Google Chrome (webapps). Com as novas tecnologias (principalmente HTML5) já é possível desenvolver webapps com uma ótima qualidade e que chegam cada vez mais próximas, principalmente na performance e no “look and feel”, dos aplicativos desktop.

Desde seu lançamento estive pensando em desenvolver uma webapp, principalmente para ter uma experiência prática com esse novo paradigma. Queria fazer algo que fosse útil para outras pessoas, e não só um aplicativo experimental descartável. Como bom cinéfilo, estou sempre de olho nas listas de “top movies” em busca de bons filmes que ainda não assisti. Daí veio a idéia da Best Movies Watchlist: uma webapp que traz sempre a lista atualizada de “top movies”, e me permite marcar os que já assisti. Simples e objetiva, e resolve em cheio o meu “problema”.

Neste post irei relatar como foi o processo de desenvolvimento dessa webapp, que você pode conferir já na webstore.

1. Documentação

2. Estrutura Inicial

Primeiro, criei um diretório chamado best-movies-watchlist no meu Desktop, estruturado da seguinte forma:

best-movies-watchlist/
    css/
        style.css
    img/
    js/
    icon_16.png
    icon_32.png
    icon_128.png
    index.html

Depois disso, conforme indicado pela documentação, criei um arquivo chamado manifest.json dentro da pasta best-movies-watchlist com o seguinte conteúdo:

{
    "name": "Best Movies Watchlist",
    "version" : "0.1",
    "description": "Watchlist from the best movies of all time!",
    "icons": {
        "16": "icon_16.png",
        "32": "icon_32.png",
        "128": "icon_128.png"
    },
    "app": {
        "launch": {
            "local_path": "index.html"
        }
    }
}

O manifest é um json que indica, basicamente,  os dados gerais da aplicação (incluíndo os ícones) e o arquivo a ser “chamado” quando a webapp for lançada (index.html).  Até agora, bem simples.

3. HTML (5)

Como boa fonte de pesquisa, me guiei pelo ótimo artigo da Smashing Magazine sobre HTML5: Coding a HTML5 Layout From the Scratch. Utilizei, basicamente, as novas tags header, section, article, aside e footer para melhor descrever a marcação da página. O resultado final do HTML você pode visualizar aqui.

4. Javascript

Toda a “mágica” da webapp vai ser realizada pelo Javascript. Carreguei o jQuery no HTML e agora é colocar as mãos na massa.

Para pegar a lista de filmes de outros sites, vou “esbarrar” em um problema: tenho que respeitar a Same Origin Policy, que previne cross-site scripting. Utilizarei então um serviço do Yahoo chamado YQL, que permite realizar o cross-site-scripting e, de quebra, ainda me permite fazer buscas no conteúdo de páginas web de uma maneira mais simples. O que tenho que fazer, então, é uma chamada ajax para o serviço de YQL do yahoo com os parâmetros da minha consulta, exibir os resultados em uma tabela e voilá.

Porém, de onde irei puxar a lista de filmes? Meu primeiro pensamento é o IMDb, cuja lista de Top 250 é a, talvez, mais confiável. Só que, infelizmente, não é permitido usar “robôs” e semelhantes para extrair informações das páginas web do IMDb. Próximo da lista então. Descobri dois IMDB-alikes: OMDB e TMDB, que permitem extração de informações, ao contrário do IMDb.

Abaixo um pedaço de código que ilustra uma chamada ajax para o YQL em busca da lista de top movies do OMDB:

function loadDataFromOmdb() {
    var movieList = new Array();
    // Fetching Data from http://www.omdb.org/movie/top
    var url = "http://www.omdb.org/movie/top";
    var sanitizedUrl = sanitizeUrl(url);
    $.getJSON("http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url%3D%22" + sanitizedUrl + "%22%20and%20xpath%3D'%2F%2Ftable%5B%40id%3D%22filmography%22%5D%2F%2Ftr'&format=json&diagnostics=true&callback=?", function(data){
        if (!data.query || !data.query.results) {
            showInitError();
        } else {
            $.each(data.query.results.tr, function(i,item){
                if (item.td) {
                    var movie = {
                         ranking: ++i,
                         title: item.td[1].a.content,
                         url: "http://www.omdb.org" + item.td[1].a.href,
                         rating: item.td[2].p,
                         year: ''
                    }
                    movieList.push(movie)
                }
            });
            setMovieList(movieList);
            setSource('www.omdb.org');
            renderContent(movieList);
        }
     });
}

A query YQL utilizada neste exemplo, para fins de melhor leitura, foi a seguinte:

select * from html where url="http://www.omdb.org/movie/top" and xpath='//table[@id="filmography"]//tr'

Estou pegando todas as TR (table row) dentro da tabela de id “filmography” do site www.omdb.org/movie/top. Bem intuitiva, né? Depois disso eu as coloco em um array e o passo para a função renderContent, que irá renderizá-lo na tabela definida no HTML.

Agora a webapp irá exibir a lista dos filmes, com a opção do usuário marcar os que ele já viu. Próximo passo agora é fazer com que essa definição dos filmes já vistos seja persistida, para quando o usuario abrir e fechar o browser, ou até reiniciar o computador, sua lista continue indicando os filmes que ele já viu. Para atingirmos isso iremos utilizar mais um recurso do HTML5: localStorage, que nos permite gravar dados (simples) em um banco de dados local, implementado pelo browser conforme as definições da W3. Sempre que o usuario marcar ou desmarcar um filme, a database será atualizada com o valor daquele filme. Abaixo um pedaço do código necessário para isso:

function setMovieStatus(movieId, status) {
    localStorage["best-movies-watchlist." + movieId] = status;
}

O banco de dados é acessado da maneira mais simples possível: localStorage[nomeDoCampo] = valor. Para recuperar um valor salvo, a sintaxe é semelhante: valorSalvo = localStorage[nomeDoCampo].

Para que a webapp funcione offline, precisarei ter uma cópia da lista salva localmente. Irei tentar atualizá-la com a fonte escolhida 1x por dia e, caso consiga, irei converter o array movieList para JSON e salvá-lo localmente, já que o localStorage só salva “texto”. Nas próximas utilizações do usuário nesse dia, a webapp irá trazer a lista salva localmente, convertê-la de JSON para um array novamente e plotá-la na tela — dessa forma temos a webapp funcionando em um cenário pós-apocalíptico offline.

Além desses recursos, também implementei alguns outros para deixar a webapp mais robusta, como:

  • Opção de trocar a fonte da lista de filmes
  • Sugerir filmes com base nos filmes já assistidos
  • Opção de exibir somente filmes não assistidos
  • Exibir quantos filmes foram assistidos e quantos não foram
  • Botão para “resetar” a webapp ao seu estágio inicial
  • Seleção de tema para a webapp, e persistindo a escolha no localStorage

Mais detalhes da implementação de cada um desses recursos podem ser vistos na página do projeto no github.

5. Carregando a Webapp no Google Chrome

Para carregar uma webapp em desenvolvimento no Chrome, abra o mesmo e vá para a url chrome://extensions. Lá habilite o modo Developer Mode, clique no botão Load unpacked extension e escolha a pasta raiz da webapp. Pronto, é isso. :)

6. Publicando na Webstore

Para publicar a webapp na Web Store é interessante ler a documentação, que já basicamente especifica tudo que precisa ser feito. Vale a pena lembrar, porém, que é necessário pagar uma taxa de 5$ para poder enviar suas webapps para a store.

A Best Movies Watchlist, exemplo deste post, está disponível na Web Store e todo o seu código fonte está disponível no GitHub. Quaisquer sugestões e/ou críticas serão bem-vindas! :)