En MSIP es posible tener vistas automáticas para un modelo y su controlador, con buen grado de personalización.
Esto requiere que el modelo incluya el módulo Msip::Modelo y que el
controlador sea descendiente de Msip::ModelosController
Lo ilustramos a continuación con un ejemplo, supongamos que necesitamos una
tabla histórica con tasas de cambio a diversos tipos de moneda (como se
tiene en cor1440_cinep). La tabla será tasacambio, con modelo Tasacambio sus campos serán:
- id
- Fecha localizada (i.e dia/Mes/año)
- Tipo de moneda, la cual será llave foránea de la tabla básica
Tipomoneda(es decir tendrá campos id y nombre). Sera un decimal localizado - Valor en pesos de la moneda a la fecha dada
Supongamos que creamos uno inicial con:
rails g migration crea_tasacambioY que en la migración que se crea (digamos
db/migrate/20170810040012_crea_tasacambio.rb):
class CreaTasacambio < ActiveRecord::Migration[5.1]
def change
create_table :tasacambio do |t|
t.date :fecha
t.integer :tipomoneda_id
t.decimal :enpesos
t.string :observaciones, limit: 5000
end
add_foreign_key :tasacambio, :tipomoneda, column: :tipomoneda_id
end
endSe podrá crear la tabla en la base de datos con:
rails db:migrateEl modelo en app/models/tasacambio es:
class Tasacambio < ActiveRecord::Base
include Msip::Modelo
include Msip::Localizacion
campofecha_localizado :fecha
flotante_localizado :enpesos
belongs_to :tipomoneda, class_name: '::Tipomoneda',
foreign_key: 'tipomoneda_id'
validates :fecha, presence: true
validates :tipomoneda_id, presence: true
validates :enpesos, numericality: { greater_than: 0}, presence: true
validates :tipomoneda_id, uniqueness: {
scope: :fecha,
message: 'no puede haber dos tasas para una moneda en una misma fecha'
}
endNote que es un modelo típico pero incluye Msip::Modelo para facilitar la creación de vistas genéricas y Msip::Localizacion para facilitar localización de fechas.
Administre los permisos para gestionar según el rol en app/models/ability.rb:
can :manage, TasacambioEn este caso como la forma plural de tasa de cambio es tasas de cambio en
config/initializers/inflections.rb agregamos:
inflect.irregular 'tasacambio', 'tasascambio'Y el contenido de app/controllers/tasascabio_controller es:
class TasascambioController < Msip::ModelosController
helper ::ApplicationHelper
before_action :set_tasacambio,
only: [:show, :edit, :update, :destroy]
load_and_authorize_resource class: ::Tasacambio
def clase
"::Tasacambio"
end
def atributos_index
[ "id",
"fecha_localizada",
"tipomoneda_id",
"enpesos_localizado",
"observaciones"
]
end
def index_reordenar(registros)
return registros.reorder(fecha: :desc, tipomoneda_id: :asc)
end
def new_modelo_path(o)
return new_tasacambio_path()
end
def genclase
return 'F'
end
private
def set_tasacambio
@registro = @basica = @tasacambio = ::Tasacambio.find(
Tasacambio.connection.quote_string(params[:id]).to_i
)
end
# No confiar parametros a Internet, sólo permitir lista blanca
def tasacambio_params
params.require(:tasacambio).permit([
:enpesos_localizado,
:fecha_localizada,
:observaciones,
:tipomoneda_id
])
end
endEn este caso supondremos que las rutas serán las típicas de rails
(tasascambio, tasascambio/1) aunque preferimos en español las rutas
para editar y nuevas. Se crean definiendo en config/routes.rb:
resources :tasascambio,
path_names: { new: 'nueva', edit: 'edita' }En caso que prefiera usar singular tanto para el modelo como para el
controlador, especifique la misma cadena singular y plural en
config/initializers/inflections.rb y cree una ruta post para el modelo
en singular con alias crea_modelo_path y que diriga al método create
del controlador. Ver por ejemplo como se hace con plantillahcr en
https://gitlab.com/pasosdeJesus/heb412_gen/blob/main/config/initializers/inflections.rb y
https://gitlab.com/pasosdeJesus/heb412_gen/blob/main/config/routes.rb.
Notará que tiene varios métodos típicos de un controlador de rails:
tasacambio_params, set_tasacambio y al comienzo las declaraciones:
before_action :set_tasacambio,
only: [:show, :edit, :update, :destroy]
load_and_authorize_resource class: ::TasacambioLa primera para establecer la variable @tasacambio antes de llamar
métodos show, edit, update y destroy con la función set_tasacambio.
Notará que esa función establece @ŧasacambio como es típico en rails,
pero también @registros que es requerida por las vistas automáticas.
Por su parte load_and_authorize... asegura que se cumplan las reglas de
autorización que se definan sobre ::TasaCambio. Sin embargo el
controladore y las vistas automáticas lo obligan como se explica en
Autorización por omisión con Msip::ModelosController
El método clase retorna una cadena con el módelo de la clase que
principalmente modificaran las vistas automáticas.
El método atributos_index retorna un vector con los campos que deben
presentarse en la vista index (también sería posible especificar atributos
por presentar en el formulario y en la vista show, pero si no se especifican
mediante los métodos atributos_form y atributos_show por omisión serán
los mismos de atributos_index.)
El método index_reordenar reordena los registros que se presentarán en la
vista index.
El método new_modelo_path retorna la ruta para crear un nuevo registro de
la tabla tasacambio.
El método genclase retorna el genero en español del módelo, por ejemplo
como decimos "la tasa de cambio" (femenino) se retorna 'F', (si fueses
"el proyecto" sería 'M').
Notará que las vistas automáticas usan bootstrap para el diseño y tom-select para los campos de texto
Con esto basta para usar las vistas creadas automáticamente. Inicie el servidor de prueba
rails sY en el navegador puede dirigirse a la ruta /tasascambio
Verá el listado con diseño estilo bootstrap y listo para agregar nuevos registros. Cuando agregue suficientes verá el paginado.
Notará un primer filtro en la columna id, podrá definir otros personalizando un poco más el modelo y el controlador o creando vistas parciales.
Con el botón Nueva ingresará al formulario de edición, la cual tendrá como
campos de selección (agradables con tom-select) los campos que en el modelo
sean belongs_to y has_many.
Puede usar sus propias vistas creando los archivos
app/views/tiposcambio/index.html.erb, app/views/tiposcambio/show.html.erb,
app/views/tiposcambio/edit.html.erb y app/views/tiposcambio/new.html.erb.
Como haría en una aplicación rails típica, sin embargo la infraestructura de
Msip permite hacer pequeñas modificaciones a las vistas genéricas que genera
para Msip::Modelo, como se explica en esta sección.
El título se presenta en la parte superior del listado y los nombres de
campos son los títulos de las columnas (así como los nombres de campo en el
formulario) se modifican en config/locale/es.yml allí dentro de
es.attributes.activerecord.attributes cree una sección como:
tasacambio:
Tasacambio: Tasa de cambio
Tasascambio: Tasas de cambio
tipomoneda: Tipo de monedaSi necesita que el título de una columna (digamos con atributo pmindicador)
sea diferente sólo en el listado, puede crear una vista parcial de nombre
_index_titulo_tabla_pmindicador.
Usted puede modificar por completo el filtro estándar que se presenta
definiendo en el directorio de la vista el archivo _index_filtro.html.erb
que recibe el formulario en la variable f.
Si prefiere también puede personalizar el filtro por omisión de una vista
index, el cual inicialmente sólo tiene la identificación (para buscar
por esta). Para agregar otros criterios basta agregar scopes en el
modelo cuyo nombre sea de la forma :filtro_campo donde campo es alguno
de los campos de atributos_index.
Por ejemplo para filtrar por tipo de moneda,
scope :filtro_tipomoneda_id, lambda { |t|
where(tipomoneda_id: t)
}Para campos de fecha, enteros y flotantes es fácil que el filtro maneje
un rango usando dos scope, uno con posfijo ini y otro con posfijo fin.
Por ejemplo para filtrar en un rango de fechas:
scope :filtro_fechaini, lambda { |f|
where('fecha >= ?', f)
}
scope :filtro_fechafin, lambda { |f|
where('fecha <= ?', f)
}Para campos tipo cadena recomendamos algo como:
scope :filtro_observaciones, lambda {|o|
where("unaccent(observaciones) ILIKE '%' || unaccent(?) || '%'", o)
} Que permite encontrar parte de la cadena, sin tener en cuenta capitalización, ni acentos.
Note que en el caso de fecha, a menos que cambie el control para el filtro,
por omisión el control que Msip pone es un campo HTML estándar para fechas.
Este control depende del navegador y siempre retorna la fecha en formato
aaaa-mm-dd, por eso el filtro no emplea fecha_localizada (sino fecha)
aún cuando la columna especificada en el controlador sea esa.
Al igual que en formulario se generará un control para el filtro con base
en el tipo del campo y su relación con el modelo que se presenta. Si
prefiere especificar un control particular para un campo cree una plantilla
app/views/tasascambio/_filtro_tipomoneda_id.html.erb con:
<%= f.input :bustipomonedad_id,
collection: ::Tipomoneda.where("nombre!='PESO'"),
include_blank: true,
label_method: :presenta_nombre,
value_method: :id,
label: false,
selected: Msip::ModeloHelper.poromision(params, :bustipomoneda_id),
input_html: {
class: 'tom-select',
'data-enviarautomatico' => ''
}
%>También puede emplear otros nombres en los campos de filtro (no incluido en atributos_index del controlador), en
tal caso puede detectar y filtrar por estos en un scop de nombre filtrar_alterno en el modelo.
Puede ver un ejemplo de campos de filtro no definidos en atributos_index en https://gitlab.com/pasosdeJesus/sivel2_gen/blob/main/app/views/sivel2_gen/victimas/_filtro_pconsolidado.html.erb y
su scope filtrar_alterno en https://gitlab.com/pasosdeJesus/sivel2_gen/blob/main/lib/sivel2_gen/concerns/models/victima.rb
A la derecha del filtro debajo de la columna acciones por omisión se
presentará el botón "Filtrar".
Si necesita agregar otro o modificarlo defina en el directorio de la
vista el archivo _index_filtro_controles.html.erb que recibe el
formulario en la variable f.
Es posible configurar la presentación de un campo de dos maneras (1) con una
vista parcial de nombre _index_campo_nombrecampo.html.erb o (2) mediante
la función presenta del modelo.
Un ejemplo puede verse en cor1440_cinep, donde los anexos de un efecto
se presentan agregando el archivo app/views/efectos/_index_campo_anexo_efecto.html.erb
con un contenido que utiliza la variable registro:
<% if registro %>
<% registro.msip_anexo.each do |i| %>
<%= link_to(
image_tag("cor1440_gen/clip.svg", size: "16"),
msip.descarga_anexo_path(i.id),
{"data-turbo" => false }
) %>
<% end %>
<% end %>Un ejemplo está en las fuentes de msipen el modelo usuario que tiene un
campo entero rol, cuya representación en cadena no está en tabla alguna
sino en la aplicación (se trata de un vector Ability::ROLES):
def presenta(atr)
case atr.to_s
when 'rol'
a = Ability::ROLES.select { |v| v[1] = rol }
a.first[0]
else
presenta_gen(atr)
end
end Después de la tabla es posible configurar un epilogo definiendo en el
directorio de la vista el archivo
_index_post_tabla que recibe el formulario en la variable f.
Si no define uno diferente el epilogo por omisión permite paginar y presenta un botón "Nuevo" que permite añadir registros.
Si prefiere tener un control no estándar para algún campo del formulario
basta que cree un directorio para las vistas y un archivo con nombre
de la forma _campo_nombre.html.erb.
Suponiendo que en tasacambio quisiera emplear un campo diferente para el
tipo de moneda que no incluya PESOS, lo que debe hacer es crear el
archivo app/views/tasascambio/_campo_tipomoneda_id.html.erb con contenido:
<% tm = ::Tipomoneda.where("nombre!='PESO'") %>
<%= f.association :tipomoneda,
collection: tm,
label_method: :presenta_nombre,
value_method: :id,
input_html: { class: 'tom-select' }
%>El contenido de un campo puede presentarse de forma personalizada bien
con la función presenta del modelo o bien con una vista parcial de
nombre _show_campo_nombrecampo. Ambas situaciones son análogas en la
vista index por lo que lo referenciamos a ver la sección 4.1.
Los botones de esta vista puede configurarse por completo para un modelo con
una vista parcial _show_acciones o pueden agregarse botones antes del
botón Eliminar en _show_mas_acciones. Estos parciales deben ubicarse en
una ruta de la forma app/views/modelo/_show_acciones