sábado, 21 de fevereiro de 2015

Override Backbone toJSON

Salve galera,

Outro dia precisei sobrescrever o toJSON do Backbone Model.. fica ai a dica de como fazer

   // method override
    toJSON: function () {

        if (this.beforePost)
            this.beforePost();

        // call the "super" method
        var json = Backbone.Model.prototype.toJSON.call(this);

        // here manipule the json objeto ;)

        return json;
    }
Eu precisava alterar o comportamento para que, quando fosse criado um objeto para o post, alguns atributos mudassem de nome no json, assim:


    // mapper properties to swap values
    properiesMap: [
        { from: 'TaskGroup', to: 'TaskGroupId' },
        { from: 'TaskType', to: 'TaskTypeId' },
        { from: 'UserProfile', to: 'UserProfileId' },
        { from: 'Sprint', to: 'SprintId' },
        { from: 'BacklogItem', to: 'BacklogItemId' },
        { from: 'Status', to: 'Status' },
    ],

    // call before json creation
    beforePost: function() {
        this.attributes['Content'] = $(".form-task").find("[name='Content']").val();
        this.attributes['BacklogItem'].Id = parseInt($("#backlogItemId").val());
    }, 
 // call to create json to post
    toPostJson: function () {

        if (this.beforePost)
            this.beforePost();

        // the original toJSON
        var json = Backbone.Model.prototype.toJSON.call(this);

        for (var i = 0; i < this.properiesMap.length; i++) {
            var it = this.properiesMap[i];
            json[it.to] = this.get(it.from).Id;

            if (it.from != it.to)
                delete json[it.from];
        }

        return json;
    }

Post original: http://www.4minds.com.br/artigo/Override_Backbone_toJSON

quarta-feira, 11 de fevereiro de 2015

Grails 2.3.4 + Spring Security Custon Access Denied Page

Usando o Grails Versão 2.3.4 tive algumas dificuldades até conseguir configurar uma página personalizada de acesso negado. Logo descobri que existia uma configuração para que o mapeamento no UrlMappings surtisse efeito.

Adicione a seguinte linha no Config.groovy

grails.plugin.springsecurity.adh.errorPage = null


E o mapeamento é normal (minhas páginas de erro estão na raiz, junto do index):

         "/"(view:"/index")
        "500"(view:'/500')
        "404"(view:'/404')       
        "403"(view:'/403')                    
        "401"(view:'/403')  

Agora deve funcionar.

sexta-feira, 19 de setembro de 2014

DJango redirect query

Salve galera,

Como enviar parametros em um redirect no django? Aqui vai uma solução que encontrei:

from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect

url_name = reverse('app:url_mapper_name')
return HttpResponseRedirect(url_name + '?id=3')


e é só.

quinta-feira, 18 de setembro de 2014

Jade + Underscore

Salve galera,

Aqui vou mostrar como usar o template javascript underscore dentro de templates jade.

 Primeiro vamos criar os templates jade


layouts/main.jade


doctype html
html
  head
    meta(http-equiv='http-equiv', content-type='Content-type', content='text/html; charset=utf-8')
    link(rel='stylesheet', href='/stylesheets/foundation.css') 
    link(rel='stylesheet', href='/stylesheets/foundation/icons/foundation-icons.css')
    link(rel='stylesheet', href='/stylesheets/style.css')
    title Mobile Mind
      block title 

  body
    header    
      .row
        .large-12.columns
          nav.top-bar(data-topbar)
            ul.title-area
              li.name
                h1
                  a Delicious Food
              li.toggle-topbar.menu-icon
                a
                  span menu

            section.top-bar-section

              ul.left
                li.divider
                li.has-form
                  .row.collapse
                    .large-8.small-9.columns
                      input(type='text', placeholder="Find Stuff...")
                    .large-4.small-3.columns
                      a.alert.button.expand.btn-search(href="#") Search
              ul.right
                li.divider
                li.has-dropdown
                  a(href="#") Categorias
                  ul.dropdown
                    each it in ['A', 'B', 'C', 'D', 'E']
                      li 
                        a(href="#") Categoria #{it}

      
    block content

    footer.row
      .large-12.columns
        hr
        .row
          .large-6.columns
            p 
              @ Copyright Mobile Mind
          .large-6.small-12.columns
            ul.inline-list.right
              li 
                a(href="http://www.mobilemind.com.br", target="_blank") www.mobilemind.com.br

  

  script(src="js/script.js")


index.jade 

extends layouts/main

block title
  |  Store
block content
  
  .row    
    .large-4.small-12.columns
      img(src="http://lorempixel.com/500/500/food")
      .custon-panel-alert
        h3 Delicious Food
        h5.subheader
          | Come eat here!!

      a(href="#")
        .custon-panel-main          
          h6 99 items in your cart      
    
    .large-8.columns
      a(href="#storeItems/50/10") load
    .large-8.columns
      .row.items-main                            

    .large-8.columns
      hr
      .pagination-centered
        ul.pagination
          li.arrow.unavailable
            a(href="#") «
          li.current
            a(href="#") 1
          each it in [2, 3, 4, 5]
            li
              a(href="#") #{it}
          li.unavailable
            a(href="#") …
          each it in [12, 13, 14, 15]
            li
              a(href="#") #{it}
          li.arrow
            a(href="#") »
  
  include extra/item.jade

extra/item.jade 

#item_template.hidden  
  .large-4.small-6.columns
    .item-row
      .panel.item-header
        h5 
          | {{ it.name }}
      .item-body
      img(src="http://lorempixel.com/500/500/food")
      .panel.item-footer
        .large-5.columns
          h6.subheader 
            | {{ it.price }}
        .large-7.columns
          a.button.alert.tiny(href="#") 
            i.fi-shopping-cart.small


Explicando:

Criamos um arquivo de layout principal, que é o main.jade. Depois um index, que usa o layout principal.. e depois o item.jade.. que contém nosso template que será usada no handlebars. Agora, no script.js vamos criar o código de carregamento da listagem de items, via ajax:

script.coffee 

       _.templateSettings = {
      interpolate: /\{\{(.+?)\}\}/g
        }
        template = _.template $('#item_template').html()           
        el = '.items-main'
        $.ajax 
   url: ContextPath + "/store/items?" + $.param {offset: 0, max: 10}
   dataType: 'json'
   success: (data, textStatus, jqXHR) ->     
     for it in data
              $(el).append template({it: it})
   error: (jqXHR, textStatus, errorThrown) ->
            console.log textStatus


Atenlção! 

Veja a alteração na sintaxe do undercode definida na váriavel templateSettings. Fazendo essa definição podemos usar as váriaveis no jade com {{ variavel }} (igual ao mustache). Também veja que no template definimos uma div com class hidden, pois como é um template não ficará visivel na página.

Para isso fucionar você deve adicionar as fererências para jquery e underscore em seu projeto bem assim como complilar o coffee e jade de algum modo.. Em outro artigo aqui no blog mostro como fazer toda essa integração usando grails e gulp, ou só gulp e html. 
Isso é um exemplo estrutural.. então para funcionar tem que modificar algumas coisas.

Até a próxima

Grails + sass + jade + handlebars + coffe

Fala galera,

A pouco tempo comecei a usar outras tecnologias otimizadoras no processo de desenvolvimento junto do Grails. São elas:

- sass
- jade
- handlebars
- coffee

Para tal necessidade eu fui atrás de alguns plugins do grails, mas não fiquei satisfeito com alguns deles. Primeiro por que alguns encontram-se desatualizados e com pouca documentação, e ainda tem alguns bugs. Ora, se pararmos para pensar, essas ferramentas são complementares ao desenvolvimento, apenas voltadas para a produtividade, então não se deve perder muito tempo com complexidades na configuração do ambiente. Para resolver o problema eu acabei usando uma estrutura separada do grails para o controle, uma que eu postei aqui no blog a poucos dias.. usando o gulp para realizar a tarefa.

Então foi simples.. Eu criei a seguinte estrutura no projeto grails (dentro de assets):


Para esse projeto uso a versão do grails 2.3.4, e essa versão ainda não trabalha com o plugin assets por padrão.

Crieu meu gulpfile com o seguinte script:



gulp = require 'gulp'
sass = require 'gulp-sass'
jade = require 'gulp-jade'
coffee = require 'gulp-coffee'
handlebars = require 'gulp-handlebars'
concat = require 'gulp-concat'
declare = require 'gulp-declare'
wrap = require 'gulp-wrap'

errorHandler = (err) ->
 console.log err

sources = {}
destinations = {}

sources.sass = 'grails-app/assets/stylesheets/**/*.scss'
sources.coffee = 'grails-app/assets/coffeescripts/**/*.coffee'
sources.jade = 'grails-app/assets/jade/**/*.jade'
sources.handlebars = 'grails-app/assets/templates/**/*.html'
 
destinations.css = 'web-app/css'
destinations.js = 'web-app/js'
destinations.templates = 'grails-app/assets/templates'
destinations.handlebars = 'web-app/js/bi/templates'


watcher = (task) ->
  (evt) ->
   console.log 'run ' + evt.path
   gulp.start task
   
gulp.task 'compile:sass', ->
 gulp.src(sources.sass)
 .pipe(sass({sourceComments: 'normal'}).on('error', errorHandler))
 .pipe(gulp.dest(destinations.css))

gulp.task 'compile:coffee', ->
 gulp.src(sources.coffee)
 .pipe(coffee({bare: true, sourcemap: true}).on('error', errorHandler))
 .pipe(gulp.dest(destinations.js))

gulp.task 'compile:jade', ->
 gulp.src(sources.jade)
 .pipe(jade().on('error', errorHandler))
 .pipe(gulp.dest(destinations.templates))

gulp.task 'compile:handlebars', ->
 gulp.src(sources.handlebars)
 .pipe(handlebars())
 .pipe(wrap('Handlebars.template(<%= contents %>)'))
 .pipe(declare({
  namespace: 'View.templates',
  noRedeclare: true
 }))
 .pipe(concat('templates.js'))
 .pipe(gulp.dest(destinations.handlebars))


gulp.task 'default', ->
 gulp.start 'compile:sass', 'compile:coffee', 'compile:jade', 'compile:handlebars'


gulp.task 'watch:sass', ->
 gulp.watch sources.sass, watcher 'compile:sass'

gulp.task 'watch:coffee', ->
 gulp.watch sources.coffee, watcher 'compile:coffee'


gulp.task 'watch:jade', ->
 gulp.watch sources.jade, watcher 'compile:jade'

gulp.task 'watch:handlebars', ->
 gulp.watch sources.handlebars, watcher 'compile:handlebars'

gulp.task 'watch', ->
 gulp.start 'watch:sass', 'watch:coffee', 'watch:jade', 'watch:handlebars'
  
 

E logo depois (ou antes) de subir meu projeto grails é só executar:

gulp watch

E então ficar atentos com o console do gulp quanto a erros de complilação. E não se esqueça de instalar as dependências usadas no gulp via npm.

Espero que ajude! Abraço

segunda-feira, 1 de setembro de 2014

Classes com CoffeScript

Salve galera,


Vou dar uma help de como criar uma classe javascript com costrutor, atributos e metodos usando coffeescript.

Para o exemplo, vamos criar uma classe Spinner. Vamos crar uma imagem de carregamento nesse site: 





No site selecione a opção PNG sprite, crie seu spinner e faça o download da imagem. Na documentação tem os exemplos de css e também um link para o  javascript, que deve ser algo mais ou menos assim:



(function() {
    SpriteSpinner = function(el, options){
        var self = this,
            img = el.children[0];
        this.interval = options.interval || 10;
        this.diameter = options.diameter || img.width;
        this.count = 0;
        this.el = el;
        img.setAttribute("style", "position:absolute");
        el.style.width = this.diameter+"px";
        el.style.height = this.diameter+"px";        

        return this;
    };
    SpriteSpinner.prototype.start = function(){
        var self = this,
            count = 0,
            img = this.el.children[0];
        this.el.style.display = "";
        self.loop = setInterval(function(){
            if(count == 19){
                count = 0;
            }
            img.style.top = (-self.diameter*count)+"px";
            count++;
        }, this.interval);

        $(this.el).parent().css('display', 'block')
    };
    SpriteSpinner.prototype.stop = function(){
        clearInterval(this.loop);
        this.el.style.display = "none";
    };
    document.SpriteSpinner = SpriteSpinner;
})(); 


E com base nesse código vamos criar nossa elegante classe usando coffee:

    class SpriteSpinner

        constructor: (options) ->            
            @el = $(options.el)[0]
            img = @el.children[0]
            @interval = options.interval || 10
            @diameter = options.diameter || img.width
            @count = 0            
            img.setAttribute "style", "position:absolute"
            @el.style.width = @diameter + "px";
            @el.style.height = @diameter + "px";       


        start: ->                     
            count = 0
            self = @
            img = @el.children[0]   
            @el.style.display = ""
            @loop = setInterval (  ->           
                count = 0 if  count == 19
                img.style.top = (-self.diameter * count) + "px"
                count++   
                return
            ), @interval

            $(@.el).parent().css 'display', 'block'
        
        stop: -> 
            clearInterval @loop 
            @el.style.display = "none"   

Detalhes:

No constructor você pode definir os atributos da classe usando o  @, que serão automágicamente criados na complicação para js.

Para usar

    spinner = new SpriteSpinner {interval: 50, el: '#spinnerWrapper'}

    spinner.start()

    setInterval( ->
       spinner.stop()
    , 10000)




Até a próxima!

MVIRAL - SMS em massa

Fala galera,


Acabamos de lançar uma ferramenta para envio de SMS em massa chamada MVIRAL.

Com o MVIRAL você envia SMS em massa para usuários ou grupo de usuários, permitindo ainda a criação de agendamentos e criação de modelos de mensagens. 

Também criamos uma API de integração para softwares de terceiros. É fácil e simples de usar. Acesse e confira http://www.mviral.com.br/