Is dynamically changing Schema in Rails possible? - ruby-on-rails

I would like to create new tables, add/delete columns from within my app. Is this possible?

Yes, you can do whatever the application database user can do to the database with ActiveRecord::Base.connection.execute. For example:
ActiveRecord::Base.connection.execute('ALTER TABLE people ADD name VARCHAR(60);')
But, if you add a column to a table, the corresponding attribute for the column will not be available in the ActiveRecord class until you restart the application.

No. It is not possible. Rails has to run migrations to get the tables in to the database. This requires that the server has be stopped and restarted after the migrations. You don't want this scenario in production.
Dynamic forms would accomplish what I believe you are after.
In short you make fields of a model a separate model. For instance
class Car
has_many :car_fields
end
class CarFields
belongs_to :car
end
Then you can make a form where the users can add and remove fields when the add a car to the database.
This is great explained by Ryan Bates here http://railscasts.com/episodes/403-dynamic-forms
I think you need a subscription to watch it. I you don't have one, get one. Railscasts is great!

Related

Rails Cookies to manipulate database entries

I am trying to create a Rails app and I have a database consisting of author and a quotation by that author.
Now different users can choose to destroy or kill quotations from the database however it must only be deleted for that particular user i.e other users should still be able to see quotes that they didn't delete even if another user did.
I know that I would need to implement cookies but other than that I am unsure how to proceed. Can anyone point me to a tutorial or give me some pointers to get started on this complex task?
You surely have a User model in your application - one 'Rails-like' way to go about this would be to add a has_and_belongs_to_many relationship between User and Quotation.
This creates a relationship between each individual user and 'their' quotations. This relationship can be deleted without actually deleting a quotation, so all quotations would still be available to other users. If you want each user to be able to see all quotations by default, you would need to set up the relationship in advance.
Assuming you are using Devise to log your users in, all you'd need to do then is to replace Quotation.all with current_user.quotations in whichever controller you are using to display quotations.
The Rails guide linked above is quite helpful but basically you just need to add something like the following:
class User
has_and_belongs_to_many :quotations
before_create :add_quotations
def add_quotations
self.quotations << Quotation.all
end
#etc...
end
class Quotation
has_and_belongs_to_many :users
#etc...
end
and then run a migration adding a new table called users_quotations with the columns user_id and quotation_id.
EDIT
As #Yule pointed out this wouldn't let users see any quotations that were created after they were, and it would be quite annoying to have to set up the join tables in advance, so a more efficient way would be to have an excluded_quotations join table instead. So users can see all quotations except the ones that they have excluded.

Rails: has_many association with a table in another database and without foreign key

Here is my situation. I have model called Account. An account can have one or more contracts. The problem is that i'm dealing with a legacy application and each account's contracts are stored in a different database.
Example:
Account 1's contract are in account1_db.contracts.
Account 2's contract are in account2_db.contracts.
The database name is a field stored in accounts table.
How can i make rails association work with this?
This is a legacy PHP application and i simply can't change it to store everything in one table. I need to make it work somehow.
I tried this, but it didn't worked:
has_many :contracts, :conditions => [lambda{ Contract.set_table_name(self.database + '.contracts'); return '1' }]
Any ideas?
Why isn't database migration an option?
You're approaching this the wrong way. You want the two systems in your integration to be loosely coupled. By trying to get the two associated, you're creating an array of interdependencies that will later come around to backstab you. The approach you are trying creates tight coupling and reduces cohesion.
But, to directly answer your question, see below. Once again, I don't recommend implementing what I say below, but it would technically be a solution.
The first thing is that rails associations work only with foreign key. In fact, all database associations work this way. There isn't a ActiveRecord method of association without foreign keys as it defies what it means to associate two objects.
So you're not going to get it done with a has_many association. Instead, I would just manually create a function on your Contract model that simulates a has_many association.
class Account
memoize :contracts
def contracts
# Load from other database in here
end
def contracts=
# Push to other database in here
end
end

Modifying models to create an association, what is the right way to do this?

Say I have two models of an existing Rails 3 application that I want to create an association for:
User
AccountType
I want to create an association such that I can do:
User.account_type
AccountType.users
So a user has a single account_type, and a account type has 0 or many users.
At the db level, the user will have a account_type_id and the AccountType table will not have any association related columns for User.
So my first step would be to write a test right? So in both of my user_spec.rb and account_type_spec.rb I should create a simple test to see if it has the proxy class .account_type an account_type.users exist right? anything else?
Modify the User.rb model and add belongs_to :AccountType, and in AccountType.rb add a has_many right?
Create a migration script, do I just add account_type_id or do I use a special way to reference the AccountType?
Regarding testing: i'd agree with Dave Newton's comment that you should test whatever behaviour you intend to create with this association (so write tests for the ability to create an association, to look up a user or account by this association, etc) but that testing for the existence of the methods as you describe is maybe going farther than necessary.
Your steps 2 and 3 look right to me.
rails g migration add_account_type_id_to_users account_type_id:integer
should generate the migration you need.

Rails Model Property Location

I'm working through the Ruby on Rails tutorial and just made a Comment model with three properties.
rails generate model Comment commenter:string body:text post:references
It generated an ActiveRecord class with post but not commenter and body.
class Comment < ActiveRecord::Base
belongs_to :post
end
Why doesn't rails formally define the non-reference properties anywhere other than the DB migration scripts?
Rails dynamically loads attributes - specifically, the names of the columns and their types - based on the database schema. There is no need to define or declare them in your models. For apps running in production, it does this once, at load time. For development, it will reload them as often as every request, but only loads them when each model is used.
Rails does not infer other things from your database, though. For instance, if you were to place a unique index on a name column, it would not automatically add a validates_uniqueness_of :name to your model. Of course, the database would still enforce this constraint when you save the record, causing an exception to be raised should the name field contain a duplicate value. The recommendation, in this case, is to do both.
Why doesn't rails formally define the non-reference properties anywhere other than the DB migration scripts?
Well, where do you need them "defined" anyways? Migrations are the only place where these attributes matter coz its responsibility is to create database tables with those attributes.
If you do a scaffold on comments with similar parameters, it would also generate the views and it would be using the attributes. They don't need to be "defined" as such anywhere else.
The short answer to your question is "no". Even the migration is not a definitive place to look as there might be many migrations related to a model.
However, you may have a look at the generated "db/schema.rb" which is an aggregation of all migrations. It contains the schema definition of all activerecord models. This maybe your best bet.
Additionally, you may want to use the https://github.com/ctran/annotate_models plugin that inserts a comment in your model to help you keep track of all your model's attributes.

Ruby on Rails working with pre-existing database. Rails makes everything plural

What is the deal with this? I'm working with a pre-existing that I did not do myself. Everything in the database is labeled in singular form. user, security, spec, etc. I guess the right way would be users, securities, specs. At least that's what ruby on rails try's to lookup when I generate a scaffold .
How do I specifically state to use user instead of users in the sql. I don't see anywhere in my project where it is looking up the sql. I mean if my model is user you would think it would try to lookup user. Instead of users.
Thanks for any help.
You need set_table_name :name_of_the_table in your model (source).
So:
class User < ActiveRecord::Base
set_table_name :user
end
The reason they use plural for the table and singular for the model is because an instance of the model represents one user, whereas the table contains all the users. It's just to make it more readable and logical.
You can specifiy the table name:
How do I explicitly specify a Model's table-name mapping in Rails?

Resources