Gracias por vuestras respuestas.
En realidad el problema no está en el scaffold, sino más adentro: en el
propio BigDecimal. Mi opinión es que un BigDecimal deberÃa tener un
campo de escala para saber cómo imprimirse correctamente cuando le hagas
un to_s.
He hecho algunas modificaciones en el código base de Rails, y al parecer
he dado con la tecla. Si queréis probarlo vosotros, a continuación os
indico los cambios que hay que hacer:
- en
/activesupport/lib/active_support/core_ext/bigdecimal/formatting.rb,
sustituir el contenido por el siguiente:
class BigDecimal #:nodoc:
alias :_original_to_s :to_s
attr_accessor :scale
@scale = 0
def to_s(format=“F”)
s = _original_to_s(format)
a = s.split(".")
if scale and scale != 0
a[0] + “.” + a[1] + “0” * (scale - a[1].length)
elsif scale and scale == 0
a[0] + “.0”
else
a[0]
end
end
end
- en /activerecord/lib/active_record/base.rb, cambiar la definición del
método read_attribute_before_type_cast por el siguiente:
def read_attribute_before_type_cast(attr_name)
r = @attributes[attr_name]
column = column_for_attribute(attr_name.to_s)
if column.type == :decimal and r.respond_to?(:scale=)
r.scale = column.scale
end
r
end
- en
/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb,
cambiar la definición de los métodos type_cast, type_cast_code y
self.value_to_decimal por los siguientes:
def type_cast(value)
return nil if value.nil?
case type
when :string then value
when :text then value
when :integer then value.to_i rescue value ? 1 : 0
when :float then value.to_f
when :decimal then self.class.value_to_decimal(value, scale)
when :datetime then self.class.string_to_time(value)
when :timestamp then self.class.string_to_time(value)
when :time then self.class.string_to_dummy_time(value)
when :date then self.class.string_to_date(value)
when :binary then self.class.binary_to_string(value)
when :boolean then self.class.value_to_boolean(value)
else value
end
end
def type_cast_code(var_name)
case type
when :string then nil
when :text then nil
when :integer then “(#{var_name}.to_i rescue #{var_name} ? 1 : 0)”
when :float then “#{var_name}.to_f”
when :decimal then
“#{self.class.name}.value_to_decimal(#{var_name}, #{scale})”
when :datetime then
“#{self.class.name}.string_to_time(#{var_name})”
when :timestamp then
“#{self.class.name}.string_to_time(#{var_name})”
when :time then
“#{self.class.name}.string_to_dummy_time(#{var_name})”
when :date then
“#{self.class.name}.string_to_date(#{var_name})”
when :binary then
“#{self.class.name}.binary_to_string(#{var_name})”
when :boolean then
“#{self.class.name}.value_to_boolean(#{var_name})”
else nil
end
end
def self.value_to_decimal(value, scale=nil)
if value.is_a?(BigDecimal)
r = value
elsif value.respond_to?(:to_d)
r = value.to_d
else
r = value.to_s.to_d
end
r.scale = scale
r
end
Todos estos cambios consiguen que un BigDecimal proveniente de una
columna decimal(x,y) contenga dentro un atributo llamado “scale” con el
valor “y”. Y cuando se le pida que se imprima con el to_s, éste tiene en
cuenta el valor de escala.
Lo he probado con el scaffold y con el script/console, y parece que
funciona decentemente (a falta de hacer más pruebas). ¿Qué os parece?