Campo de formulário com separador decimal no Rails 4

660 views
Skip to first unread message

Moisés Jardim Pinheiro

unread,
Jul 1, 2016, 8:44:51 PM7/1/16
to rails-br
Olá pessoal, estou tendo problemas com um campo de formulário decimal.

No formulário o separador de decimais está com ".", mesmo a config do i18n estando ok.

Se entro com valor 20,12 por exemplo, a mensagem de erro é mostrada informando "Valor não é um número", entretano 20.12 aceita normalmente.

O que preciso fazer pra contornar esse comportamento?

No banco está assim:

t.decimal  "valor",               precision: 8, scale: 2,                 null: false

Model:

validates :valor, presence: true, numericality: { greater_than_or_equal_to: 0 }

Na view:

<%= f.text_field :valor%>

No application.rb:

config.i18n.default_locale = "pt-BR"

 E tenho o arquivo pt-BR.yml completo. Abaixo trecho da config:

  number:
    currency:
      format:
        delimiter: "."
        format: "%u %n"
        precision: 2
        separator: ","
        significant: false
        strip_insignificant_zeros: false
        unit: R$
    format:
      delimiter: "."
      precision: 3
      separator: ","
      significant: false
      strip_insignificant_zeros: false


Grato!

Alex Takitani

unread,
Jul 1, 2016, 9:13:59 PM7/1/16
to rail...@googlegroups.com
Nao use campo decimal para valores, use inteiro.

Essa gem ajuda bastante:


Não esqueça de configurar a moeda padrão antes de mais nada.

Na view vc pode usar mascaras em javascript

--
--
Você recebeu essa mensagem porquê está inscrito no Google
Groups "rails-br".
Para enviar uma mensagem para o grupo, mande um email para rail...@googlegroups.com
Para se descadastrar, mande um e-mail para
rails-br+u...@googlegroups.com
Visite o grupo em http://groups.google.com/group/rails-br?hl=pt-BR
Leia nossa política de uso: http://goo.gl/YGgt7

---
Você recebeu essa mensagem porque está inscrito no grupo "rails-br" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para rails-br+u...@googlegroups.com.
Para mais opções, acesse https://groups.google.com/d/optout.

Moisés Jardim Pinheiro

unread,
Jul 1, 2016, 9:43:34 PM7/1/16
to rail...@googlegroups.com
Porque inteiros?

Quando você diz inteiros, usar inteiros no banco? E no model o monetize?

Venho do Java, e isso era bem transparente para trabalhar, me parece um pouco estranha essa forma do rails de tratar isso.

Valeu!


Você recebeu essa mensagem porque está inscrito em um tópico no grupo "rails-br" dos Grupos do Google.
Para cancelar inscrição nesse tópico, acesse https://groups.google.com/d/topic/rails-br/oAQUsD_e8Lg/unsubscribe.
Para cancelar inscrição nesse grupo e todos os seus tópicos, envie um e-mail para rails-br+u...@googlegroups.com.

Para mais opções, acesse https://groups.google.com/d/optout.



--
Enviado por Moisés Jardim Pinheiro

Everaldo Gomes

unread,
Jul 1, 2016, 9:45:26 PM7/1/16
to rail...@googlegroups.com
Nem é coisa do Ruby e do Java. É mais um padrão mesmo:

Alex Takitani

unread,
Jul 1, 2016, 9:48:47 PM7/1/16
to rail...@googlegroups.com
Valeu Everaldo!

Vc vai guardar o valor no banco como centavos. O monetize vai fazer isso pra vc.

E na hora de usar ele faz conversões para "dollars".

O postgresql tem um tipo money, e o Rails 4.2 suporta. Dependendo do seu uso é o suficiente.

A gem money ajuda a formatar, converter etc, mas ela exige que vc salve os valores como inteiros. 

Moisés Jardim Pinheiro

unread,
Jul 1, 2016, 9:58:17 PM7/1/16
to rail...@googlegroups.com
Valeu!

Vou aplicar aqui.
Abraços.

Jonathan Calixto

unread,
Jul 3, 2016, 10:48:26 AM7/3/16
to Grupos Rails BR
Eu resolvi este problema de forma diferente, no banco eu savo o registro como decimal (estou usando postgres), e ao receber o valor no model eu converto de string para decimal usando um parser, e para exibir no formulário, eu uso um decorator/preseter para converter o valor decimal em currency.
Como otmizei estes conversores, não preciso escrever muito codigo.


Atenciosamente,

Jonathan Celestino Calixto
Desenvolvedor Web Ruby on Rails
email:     jonathan...@gmail.com
msn:       jonathan...@gmail.com
github:    https://github.com/jonathanccalixto
skype:    jonathanccalixto
twitter:    jonathanccalixt

Moisés Jardim Pinheiro

unread,
Jul 4, 2016, 8:20:04 PM7/4/16
to rail...@googlegroups.com
Valeu por compartilhar essa outra alternativa Jonathan 😉

A quem interessar possa:

Utilizei a seguinte tradução para a chave invalid_currency no pt_BR.yml:

activerecord:
    errors:
      messages:
        invalid_currency: deve ser uma moeda válida (ex. '100', '5%{decimal}24', ou '123%{thousands}456%{decimal}78'). Obteve %{currency}

Abraços.
Moisés.

. /\/\ a r c o s . /\ u g u s t o

unread,
Jul 5, 2016, 2:23:04 AM7/5/16
to rail...@googlegroups.com
Vocês devem ler novamente o que o Alex disse.

Jonathan não resolveu um problema,  arrumou outros.

E Moisés piorou a vida do usuário p/ não fazer do jeito certo.

Jeito certo == mascara js + money gem
NAO USE FLOAT/DECIMAL p dinheiro, não importa se é ruby, java ou js!



. /\/\ a r c o s    /\ u g u s t o  ;

.eu ando de passo leve que é pra num acordar o dia.

Laércio Crestani

unread,
Jul 5, 2016, 7:11:34 AM7/5/16
to rail...@googlegroups.com
Bom dia, amigo usa isso no teu MODEL


def valor=(valor)
    write_attribute(:valor, valor.tr(',.', '').to_f/100)
  end

No teu input vc tem uma máscara 1.234,56 porém no banco salva (1234.56)

Sendo que o campo no banco seja “valor” você também pode usar decimal (8,2)

Abraço.

Jonathan Calixto

unread,
Jul 5, 2016, 7:23:54 AM7/5/16
to Grupos Rails BR
Bom dia Marcos,

Qual problema que arrumei com a minha alternativa? No meu ver eu facilitei a minha vida, e deixei de adicionar uma dependencia(gem) a mais em meu projeto. Não falei que a minha solução é uma "bala de prata", dei alternativa utilizando uma experiencia que para mim dá muito certo. A sua solução é muito boa, mas não é perfeita. Eu já tentei usar a gem money, e algumas alternativas e achei um trabalho gigante, para uma solução simples, posso não ter entendido a gem direito. Não criei uma gem melhor, porque não tive tempo, mas o farei para me ajudar e melhorar meu trabalho. Não estou falando que vocês estão errados em usar a gem money, mas para mim não serviu.


Atenciosamente,

Jonathan Celestino Calixto
Desenvolvedor Web Ruby on Rails
email:     jonathan...@gmail.com
msn:       jonathan...@gmail.com
github:    https://github.com/jonathanccalixto
skype:    jonathanccalixto
twitter:    jonathanccalixt

Jonathan Calixto

unread,
Jul 5, 2016, 7:24:59 AM7/5/16
to Grupos Rails BR
Outra coisa eu já utilizo mascaras em meu input, por isso a necessidade de usar um decorator/presenter. 


Atenciosamente,

Jonathan Celestino Calixto
Desenvolvedor Web Ruby on Rails
email:     jonathan...@gmail.com
msn:       jonathan...@gmail.com
github:    https://github.com/jonathanccalixto
skype:    jonathanccalixto
twitter:    jonathanccalixt

Alex Takitani

unread,
Jul 5, 2016, 7:28:36 AM7/5/16
to rail...@googlegroups.com
A questão não é usar gem ou máscara a questão é usar inteiros para representar $.

Os problemas possíveis estão no link que o Everaldo indicou.






Jonathan Calixto

unread,
Jul 5, 2016, 7:32:04 AM7/5/16
to Grupos Rails BR
eu criei um arquivo em config/initializers, chamado activerecorde_core_ext.rb e coloquei o seguinte:

module ActiveRecord
  class Base
    def self.decimal_attribute(*methods)
      separator = I18n.t(:'number.format.separator')
      delimiter = I18n.t(:'number.format.delimiter')

      methods.each do |method|
        define_method(:"#{method}=") do |value|
          if value.to_s =~ %r(\A\d{1,3}(#{"\\#{delimiter}"}?\d{3})*#{"\\#{separator}"}\d+\z)
            value = BigDecimal.new value.to_s.gsub(delimiter, '').gsub(separator, '.')
          end

          super(value)
        end
      end
    end

    def previous_attribute_changes?(attribute)
      previous_changes.fetch(attribute.to_s, []).second.present?
    end
  end
end

nos meus models que usão valores decimais, não só valores de moeda. 

em meus models uso o seguinte:

class Plan < ActiveRecord::Base
  has_paper_trail

  decimal_attribute :value, :activation_value, :board, :direct_indication
  decimal_attribute :residual_value, :value_profitability
...
end

em meus inputs fica assim:

<%= f.text_field :value, :class => 'money', :value => decore(f.object).value %>

onde o decore é um helper que usa um objeto de modelo para acionar um decorator meu, onde value é exibido humanizado e formatado.


Atenciosamente,

Jonathan Celestino Calixto
Desenvolvedor Web Ruby on Rails
email:     jonathan...@gmail.com
msn:       jonathan...@gmail.com
github:    https://github.com/jonathanccalixto
skype:    jonathanccalixto
twitter:    jonathanccalixt

Em 5 de julho de 2016 08:24, Jonathan Calixto <jonathan...@gmail.com> escreveu:

Jonathan Calixto

unread,
Jul 5, 2016, 7:41:28 AM7/5/16
to Grupos Rails BR
Olá Alex,

quando eu adotei esta forma de trabalhar com dinheiro, eu estudei e vi sobre converter para centavos e salvar em inteiro. Mas eu uso BigDecimal do ruby, e salvo em decimal no postgres, pelo que estudei, estes dois tipos, BigDecimal e decimal do postfgres, não possuem a perda de valor que o Float e o real possuem, e nas minhas conversões e arredondamentos também não possuem perdas. Para mim não foi problema. E pelo que vi do post que me passou, o Martin Fowler não sugeri de usarmos inteiros, e sim criar uma classe que faz os tratamentos corretos. Eu entendi os questinamentos, e não falo que converter para inteiro é errado, é uma alternativa boa.


Atenciosamente,

Jonathan Celestino Calixto
Desenvolvedor Web Ruby on Rails
email:     jonathan...@gmail.com
msn:       jonathan...@gmail.com
github:    https://github.com/jonathanccalixto
skype:    jonathanccalixto
twitter:    jonathanccalixt

Alex Takitani

unread,
Jul 5, 2016, 8:09:20 AM7/5/16
to rail...@googlegroups.com
Vc está certo Jonathan, seria um problema se fosse float ou algo do tipo, decimais não tem problema de arredondamento.

. /\/\ a r c o s . /\ u g u s t o

unread,
Jul 5, 2016, 2:41:53 PM7/5/16
to rail...@googlegroups.com
Alou Jonathan,

O caso maior é qql tipo de aritmética. Pode ficar complicado...
Texto -> BigDecimal -> Texto tudo bem p/ exibição.
Mas pra aritmética, integer... com 2, 3 ou 4 casas de precisão.


Integração com money (AR) já ateh tem, e se resume a:

def valor=(v)
  self[:valor] = Money.new(v)
end

Ou talvez #to_i hehe...

Abraço,

Jonathan Calixto

unread,
Jul 5, 2016, 3:57:05 PM7/5/16
to Grupos Rails BR
Saquei (^_^)


Atenciosamente,

Jonathan Celestino Calixto
Desenvolvedor Web Ruby on Rails
email:     jonathan...@gmail.com
msn:       jonathan...@gmail.com
github:    https://github.com/jonathanccalixto
skype:    jonathanccalixto
twitter:    jonathanccalixt

Reply all
Reply to author
Forward
0 new messages