Rails inheritance_column migration - ruby-on-rails

I'm changing the inheritance_column value of a base model, which is extended using STI, in an existing app. How can I write a migration in order to make the existing columns conform with the new inheritance_column?
Here's my first attempt:
class MigrateStoryTypes < ActiveRecord::Migration
def self.up
Story.all.each { |story|
new_story_type = story.story_type.camelize + 'Story'
puts "changing #{story.id}'s story_type from #{story.story_type} to #{new_story_type}"
story.update_column :story_type, new_story_type
}
end
def self.down
Story.all.each { |story|
new_story_type = story.story_type.underscore.gsub /_story/, ''
puts "changing #{story.id}'s story_type from #{story.story_type} to #{new_story_type}"
story.update_column :story_type, new_story_type
}
end
end
However, this fails with:
ActiveRecord::SubclassNotFound: The single-table inheritance mechanism
failed to locate the subclass: 'clean_slate'. This error is raised
because the column ' story_type' is reserved for storing the class in
case of inheritance. Please rename this column if you didn't intend it
to be used for storing the inheritance class or overwrite
Story.inheritance_column to use another column for that information.
Is there a straight-forward way of doing this through ActiveRecord or do I need to use a temporary column, SQL, etc.?

Using models inside migrations is generally a bad idea as the model classes assume they know what the database structure is but migrations are intended to manipulate the database structure. Your error message is just one case of the model classes not be in sync with the database. As soon as Story.all tries to instantiate a model, you get your ActiveRecord::SubclassNotFound STI exception because ActiveRecord expects to find the class name in story_type but you still have your old string types in story_type: you can't fix your database using models until your database is fixed.
I'd recommend that you pretend that your models don't exist at all in migrations, you'll have a better time if you work with the database directly. You only have two story_type values so the SQL is pretty straightforward:
def up
connection.execute(%q{
update stories
set story_type = case story_type
when 'whatever1' then 'Whatever1Story'
when 'whatever2' then 'Whatever2Story'
end
})
end
There's only two values and you know what they are so don't waste time trying to be clever.

Related

Rails application with legacy database

I'm building an API that will look for data in a "non-rails-database". It already exists and was not created with the rails project. What is the best approach to use in this case ? Let's say I have a table called User in this database, I would like to be able to do something like this in the controller: User.last or User.find(id). It would be good, because I have to send back to the frontend as a JSON.
Should I use PORO Models ? How would it be ?
Create your models then set table name and primary key explicitly, this helps you call active record methods in a controller
class User < ApplicationRecord
self.primary_key = 'user table primary key'
self.table_name = 'user table name'
end
ref http://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/PrimaryKey/ClassMethods.html
http://api.rubyonrails.org/classes/ActiveRecord/ModelSchema/ClassMethods.html#method-i-table_name-3D
I would personally prefer to have them in PORO. For me, it makes things clear and gives more flexibility.
For an example, I would have a seperate folder called api_models in the app folder and have the classes there.
First, it may feel like you are duplicating the code, but it will give you an object to work with when API needs changes.
E.g
ApiModel::User
...
end
also,
if you wanting be bit more advance and thinking about versioning (if not you should ;) etc, I would recommend a gem like grape, because that handles lot of api related things and all you need to focus is the code.
I would create custom methods that contain custom DB queries that override ActiveRecord methods. Because model classes inherit from ActiveRecord, if you define a method in the model class with the same method name as the ActiveRecord method, then when you call it, the model class will be called. You can still call the ActiveRecord method if you call super
So, assuming you already setup the connection to the DB, you can do something like:
class User < ApplicationRecord
def self.find(id)
sql = "Select * from ... where id = ..."
records_array = ActiveRecord::Base.connection.execute(sql)
end
end
So when you call find it will call the User find, and not the ActiveRecord find. You can also define a custom method so that you don't override the ActiveRecord find:
class User < ApplicationRecord
def self.new_find(id)
sql = "Select * from ... where id = ..."
records_array = ActiveRecord::Base.connection.execute(sql)
end
end
Not sure if its the best approach but its an approach :)

Storing class names in database to mimic a design pattern - Ruby on Rails

I was looking for implementing strategy design pattern in one of my project which is on rails. Basically I have to call methods on objects that are differentiated by a single field in the database, and this difference will ensure different method behavior.
After some study, I found that ruby rarely requires design patterns and that it uses the duck-typing. Now to achieve the same affect, I am thinking about storing class names in the database and call the methods of that class when required. Like this,
duckOne = Duck.find(id)
duckOne[:className].fly(duckOne) #This scenario is same as Class.fly,but class is coming from db
duckTwo = Duck.find(id)
duckTwo[:className].fly(duckTwo) #Similar to above, just a different class from database
Is there a better way to do it? And is this approach correct?
Basically, from what you've described, I think you're looking for Single Table Inheritance (STI) (you may have to scroll to find the info).
With STI you use one database table to represent multiple classes that derive from the same superclass. Rails uses a column called type to know which class to interpret the db row as.
You can still use the find method on the superclass to find all kinds of subclassing models.
For example:
class Bird < ActiveRecord::Base
def quack
"quack"
end
end
class Duck < Bird
def quack
"foo"
end
end
class Goose < Bird
def quack
"bar"
end
end
duck = Duck.create
goose = Goose.create
Bird.find(duck.id).quack
# => "foo"
Bird.find(goose.id).quack
# => "bar"

Field name in model versus field name in database

I have a model named end. It works fine in my development environment where I use SQLite.
But in production I get an error because of PostgreSQL where end is a reserved word.
I don't want to rename the field in the model, because there are too many files to edit.
Instead, I want to declare a mapping rule so that field name in model stay "end" but name of this field in database became end_date.
How I can do it?
Your best bet, long-term, is almost certainly to suck it up and change all your Ruby code to use end_date. Obviously, that's going to be tedious because end is a Ruby keyword too, meaning Search & Replace won't Just Work; so if you really can't face it try this.
Change the name in the database, then add the following two methods to your model:
class Widget < ActiveRecord::Base
def end
end_date
end
def end=(val)
self.end_date = val
end
end

Using existing tables and its relations in rails 3.2 application?

I have an existing application with lot of data and about 20 tables, how can I use them directly.
My database.yml file points to the MysQL database. Something like magic model generator.
you can do the following
connection = ActiveRecord::Base.connection()
results = connection.execute("#{your_sql_query_here}")
results.each do |row|
puts row[0]
end
However I'd recommend you to associate them in a more coherent fashion.
You would create a model for each one of them which is not much when you only have about 20.
In the model you would associate the table to them using the
set_table_name :name_of_your_table
Keep in mind the model name just have to be relavent enough to the table, because of the explicit set_table_name method they do not have to follow strict convention.
And to set relationships you would use the class_name just like this
has_many :fruits, :class_name => "CrazyFruit"
It might sound tedious but its the right way

Model Inheritance in Ruby on Rails 3

Please help a newbie to choose the best way to implement inheritance in RoR3. I have:
-Person (address fields, birthdate, etc.)
-Player, inherits from Person (position, shoe_size, etc.)
-Goalkeeper, inherits from Player (other specific fields related to this role)
I think that Single Table Inheritance is a bad solution, because there will be a lot of null fields in the table created. What is the best way to do this? Use polymorphic associations (with has_one?)? Use belongs_to/has_one (but then how to show in the Player views the fields of Person too?)? Don't implement inheritance? Other solutions?
While I think STI is probably the approach I would use for this, one other possibility, if you want to avoid a lot of NULL attributes, is to add a column other_attributes to your Person model that will store a Hash of attributes. To do this, add a text column to the people table:
def self.up
add_column :people, :other_attributes, :text
end
Then make sure the attribute is serialized in the model. And you may want to write a wrapper to make sure it's initialized as an empty Hash when you use it:
class Person < ActiveRecord::Base
serialize :other_attributes
...
def other_attributes
write_attribute(:other_attributes, {}) unless read_attribute(:other_attributes)
read_attribute(:other_attributes)
end
end
Then you can use the attribute as follows:
p = Person.new(...)
p.other_attributes #=> {}
pl = Player.new(...)
pl.other_attributes["position"] = "forward"
pl.other_attributes #=> {"position" => "forward"}
One caveat with this approach is that you should use strings as keys when retrieving data from other_attributes, as the keys will always be strings when the Hash is retrieved from the database.
I suggest STI. An alternative solution is to use a document store like mongodb, or use the activerecord store http://api.rubyonrails.org/classes/ActiveRecord/Store.html. If you have a postgress database look at his HStore column http://rubygems.org/gems/activerecord-postgres-hstore.
Another option is PostgreSQL table inheritance. http://www.postgresql.org/docs/8.1/static/ddl-inherit.html

Resources