tipos - listado de funciones en sql server
Cómo hacer que el setter de atributos envíe valor a través de la función de SQL (1)
Estoy tratando de hacer que un setter de atributos en un modelo ActiveRecord ajuste su valor en la función text2ltree () postgres antes de que rails genere su consulta sql.
Por ejemplo,
post.path = "1.2.3"
post.save
Debería generar algo así como
UPDATE posts SET PATH=text2ltree(''1.2.3'') WHERE id = 123 # or whatever
¿Cuál es la mejor manera de hacer esto?
EDITAR: para lograr exactamente lo que estás buscando arriba, lo usarías para anular el setter predeterminado en tu archivo de modelo:
def path=(value)
self[:path] = connection.execute("SELECT text2ltree(''#{value}'');")[0][0]
end
Entonces el código que tienes arriba funciona.
Estoy interesado en aprender más sobre el funcionamiento interno de ActiveRecord y sus bases de metaprogramación impenetrables, así que como ejercicio intenté lograr lo que describiste en tus comentarios a continuación. Aquí hay un ejemplo que funcionó para mí (esto es todo en post.rb):
module DatabaseTransformation
extend ActiveSupport::Concern
module ClassMethods
def transformed_by_database(transformed_attributes = {})
transformed_attributes.each do |attr_name, transformation|
define_method("#{attr_name}=") do |argument|
transformed_value = connection.execute("SELECT #{transformation}(''#{argument}'');")[0][0]
write_attribute(attr_name, transformed_value)
end
end
end
end
end
class Post < ActiveRecord::Base
attr_accessible :name, :path, :version
include DatabaseTransformation
transformed_by_database :name => "length"
end
Salida de la consola:
1.9.3p194 :001 > p = Post.new(:name => "foo")
(0.3ms) SELECT length(''foo'');
=> #<Post id: nil, name: 3, path: nil, version: nil, created_at: nil, updated_at: nil>
En la vida real, supongo que querrá include
el módulo en ActiveRecord :: Base, en un archivo en algún lugar anterior de la ruta de carga. También tendría que manejar adecuadamente el tipo de argumento que está pasando a la función de base de datos. Finalmente, me enteré de que connection.execute
está implementado por cada adaptador de base de datos, por lo que la forma de acceder al resultado puede ser diferente en Postgres (este ejemplo es SQLite3, donde el conjunto de resultados se devuelve como una matriz de hashes y la clave de la primera el registro de datos es 0].
Esta publicación del blog fue increíblemente útil:
http://www.fakingfantastic.com/2010/09/20/concerning-yourself-with-active-support-concern/
como era la guía de Rails para la creación de plugins:
http://guides.rubyonrails.org/plugins.html
Además, por lo que vale, creo que en Postgres aún lo haría utilizando una migración para crear una regla de reescritura de consultas, pero esto resultó en una gran experiencia de aprendizaje. Espero que funcione y pueda dejar de pensar en cómo hacerlo ahora.