📜 ⬆️ ⬇️

Legacy Databases in Rails 3

Based on real events that happened (occurring) with real people.

If you work with legacy databases, you don’t always have the ability to change field names when fields start to conflict with Ruby on Rails. The simplest example is a field named 'class' in one of your tables. Rails really don't like it. It's like a mother-in-law who doesn't like your new hairstyle, and she pays attention to it at every opportunity.
In the absence of dramatic talent, the translator failed to produce a brighter metaphor, creating a general association, as opposed to a private


#      ruby-1.9.2-p0 > u = User.new :class => '1995' NoMethodError: undefined method `columns_hash' for nil:NilClass 

 #   ,    ,       . ruby-1.9.2-p0 > u = User.new :name NoMethodError: undefined method `has_key?' for nil:NilClass 

 #     ruby-1.9.2-p0 > u = User.new => #<User id: nil, name: nil, class: nil, created_at: nil, updated_at: nil> ruby-1.9.2-p0 > u.class = '1995' NoMethodError: undefined method `private_method_defined?' for nil:NilClass 

As with the aforementioned mother-in-law, your problems are inevitable until the hairstyle is corrected.
Fortunately, Brian Jones solved this problem for us with his gem safe_attributes . Rails automatically creates acessors (getters and setters) for each attribute in the ActiveRecord model table. Attempting to redefine important methods such as “class” with Rails is what gives us problems. Safe_attributes excludes the creation of any attributes with dangerous names.

It is enough to do the following:
 # app/models/user.rb class User < ActiveRecord::Base bad_attribute_names :class end 

After adding the gem in the bundle, pass to bad_attribute_names a list of the names of the offending fields, and this will free Rails from trying to generate acessor methods for them. Now everything works, but without these acessors. Let's try to get / assign our attribute: class:
 ruby-1.9.2-p0 > u = User.new => #<User id: nil, name: nil, class: nil, created_at: nil, updated_at: nil> ruby-1.9.2-p0 > u.class = '1995' => "1995" ruby-1.9.2-p0 > u => #<User id: nil, name: nil, class: "1995", created_at: nil, updated_at: nil> ruby-1.9.2-p0 > u.class => User(id: integer, name: string, class: string, created_at: datetime, updated_at: datetime) 

The setter works (I assume that it was created also because there was no previously existing method 'class' =), and we can make sure that the attribute value is correctly assigned. But the default getter call causes ... well, the default behavior.

The fact is that you can always use the attribute in the context of hash (associated array, further hash, comment of the translator). You can pass a key / value attribute hash to the object, and this will work. This means that when you create and update your controller, you do not have to change it.
Methods such as new, create, update_attribute, update_attributes, etc. will work fine.
')
If you only want to assign a value (to avoid immediate saving, for example), proceed as follows.
 ruby-1.9.2-p0 > u[:class] = '1996' => "1996" ruby-1.9.2-p0 > u => #<User id: nil, name: nil, class: "1996", created_at: nil, updated_at: nil> 


In general, you can still set the value of attributes directly, instead of using rail-generated acessors. But we are still one step away from the final decision. We want to refer to this attribute as the rest, and this requires us to organize a normal set of access methods (getters and setters). One of the reasons to do this is that we will have standard validations for this attribute.

You can add acessors as in this example:
 # add to app/models/user.rb def class_name= value self[:class] = value end def class_name self[:class] end 

We declare acessor 'class_name', and now we can use it anywhere in the place of the original attribute name. We can use it in forms:
 <%= f.text_field :class_name %> 

Or in validators:
 validates_presence_of :class_name 

Or when we create a new object:
 User.create :class_name => 'class of 1995' 

If you downloaded the code, then these additions are test-driven, meaning that I wrote tests for these methods before writing the methods themselves, to make sure that these methods work in an appropriate way. I urge you to do the same.

Good luck!

Original available

Source: https://habr.com/ru/post/155215/


All Articles