How to setter setter attribute via SQL function

I'm trying to make an attribute installer in an ActiveRecord model wrap its value in the postgres text2ltree () function before the rails create their SQL query.

For example,

post.path = "1.2.3" post.save 

It should generate something like

 UPDATE posts SET PATH=text2ltree('1.2.3') WHERE id = 123 # or whatever 

What is the best way to do this?

+8
sql ruby-on-rails activerecord postgresql arel
source share
1 answer

EDIT: To achieve exactly what you are looking for above, you should use this to override the default installer in your model file:

 def path=(value) self[:path] = connection.execute("SELECT text2ltree('#{value}');")[0][0] end 

Then the code that you have above works.

I'm interested in learning more about the internal components of ActiveRecord and its impenetrable basics of metaprogramming, as I tried to complete the exercise that you described in your comments below. Here is an example that worked for me (that's all in 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 

Console output:

 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> 

In real life, I assume that you want to include module in ActiveRecord :: Base in a file somewhere earlier in the download path. You also need to properly handle the type of argument that you pass to the database functions. Finally, I found out that connection.execute is implemented by each database adapter, so the way to access the result may be different in Postgres (this example is SQLite3, where the result set is returned as an array of hashes and the key to the first data record is 0].

This blog post was incredibly helpful:

http://www.fakingfantastic.com/2010/09/20/concerning-yourself-with-active-support-concern/

as well as the Rails tutorial for creating plugins:

http://guides.rubyonrails.org/plugins.html

Also, for what it's worth, I think in Postgres I still do this through migration to create a request rewrite rule, but it did for great learning. Hope it works, and I can stop thinking about how to do it now.

+2
source share

All Articles