Rails 3 model mapping certain columns to different model attributes - sql

I have the old legacy table called "DXFTACCTS", and I created Rails model "Account".
class Account < ActiveRecord::Base
set_table_name "DXFTACCTS"
end
The problem is that DXFTACCTS has fields like "XORFNAME" which I want to be "first_name" in the model, and so on. How do I "map" specific table columns to model attributes?
Thanks!

You can use the method alias_attribute like this:
class Account < ActiveRecord::Base
set_table_name "DXFTACCTS"
alias_attribute :first_name, :XORFNAME
end
alias_attribute creates the methods first_name, first_name= and first_name? which will map to the XORFNAME column in your table. However, you will NOT be able to use it in conditions like regular columns. For example:
Account.all(:conditions => { :first_name => "Foo" })
That will fail...

I think something like definition of getter and setter methods should do the trick:
class Account < ActiveRecord::Base
...
def firts_name
self[:XORFNAME]
end
def first_name= value
self[:XORFNAME] = value
end
...
end

Related

Rails concatenate sql columns and create one to many relationship

I've got two models: Building and BuildingInfo. I want to relate the two tables using two columns townhall_level and name.
Ideally it will work like the following: Building.first.building_info For instance Building.first.townhall_level => 5 and Building.first.name => cannon, Building.first.building_info would access BuildingInfo.where(townhall_level: 5, name:"cannon".
What's the best way to do this? Can I create a third column which concatenates name and townhall_level? Could I also use that column to create the belongs_to and has_many relationship?
Simple and straightforward:
class Building < ActiveRecord::Base
def building_info
BuildingInfo.find_by(townhall_level: townhall_level, name: name)
end
end
It will be nil if nothing is found, and will return only the first record even if multiples are found. I also highly suggest that you add an index to the two columns through a migration:
add_index :building_infos, [:townhall_level, :name], name: 'building_infos_level_and_name'
Which will speed up searching, if you were concerned about performance.
mmm...I'm not sure this will work but you can do something like
class Building < ActiveRecord::Base
def self.bulding_info
BuildingInfo.find_by(townhall_level: townhall_level, name: name)
end
end
but I would really suggest you to put a building_info_id in the Building model and have a
class Building < ActiveRecord::Base
belongs_to :bulding_info
end

Returning Columns from Associated Table in Model.all

Summary:
I have a model with a few "belongs_to" associations and when I call Model.all (or another custom method if need be) I want to not only return all columns of Model, but also one column from each of the associated Models. Basically instead of just returning the ID's of the associated Models I want to get a more friendly attribute like "name".
Using Rails 3.2.x
Model Details:
I have five models, basically for data normalization.
class ActionItem < ActiveRecord::Base
belongs_to :action_item_status
belongs_to :prod_ops_acceptance
belongs_to :action_item_priority
belongs_to :incident_ticket
<truncated the rest>
end
class IncidentTicket < ActiveRecord::Base
attr_accessible :number
has_many :action_items
validates_presence_of :number
end
class ActionItemPriority < ActiveRecord::Base
attr_accessible :name
has_many :action_items
validates_presence_of :name
end
class ActionItemStatus < ActiveRecord::Base
attr_accessible :name
has_many :action_items
validates_presence_of :name
end
class ProdOpsAcceptance < ActiveRecord::Base
attr_accessible :name
has_many :action_items
validates_presence_of :name
end
Attempted Solutions:
I've tried many combinations of things including using ActionItem.includes and ActionItem.joins to no avail. The latest thing I tried is (trying only for the 'number' attribute of the IncidentTicket model to start with...)
ActionItem.all(
select: 'action_items.title, incident_tickets.number',
joins: 'INNER JOIN incident_tickets
ON action_items.incident_ticket_id = incident_tickets.id')
The above only returns the 'title' attribute from the ActionItem model and not the 'number' attribute from the IncidentTicket model despite the SQL looking correct. It seems like the SELECT on the joined table is completely ignored no matter what I try.
Obviously I am seriously missing something here or doing this completely wrong. I feel like there is some ActiveRecord magic that I'm missing out on that makes this trivial. Any help would be much appreciated! Please let me know if you need more details, I feel like this is kind of difficult to explain...
This ought to work for you:
action_items =
ActionItem.joins(:incident_ticket, :action_item_priority, ...)
.select(%[ action_items.title,
incident_tickets.number AS incident_ticket_number,
action_item_priorities.name AS action_item_priority_name,
... ]
)
.all
logger.info(action_items.first.incident_ticket_number)
What I ended up doing for now is creating a method that returns an array containing the results of ActionItem.all with the additional attributes I want injected in. This can probably be optimized, but I haven't spent any more time focusing on that just yet:
def self.all_with_extras
action_items_with_extras = []
action_items = ActionItem.all.to_json
JSON.parse(action_items).each do |ai|
extras = {
'incident_ticket_number' => IncidentTicket.find(ai['incident_ticket_id']).number,
'status' => ActionItemStatus.find(ai['action_item_status_id']).name,
'priority' => ActionItemPriority.find(ai['action_item_priority_id']).name,
'acceptance' => ProdOpsAcceptance.find(ai['prod_ops_acceptance_id']).name
}
with_extras = ai.merge(extras)
action_items_with_extras.append(with_extras)
end # each
action_items_with_extras
end # def

Polymorphic has_one association and multiple inheritance with Rails 3

I've seen some posts dealing with this, and am trying to determine the best solution.
Semantically, I want a Client model with a one-to-one relationship with a Survey. There are different kinds of surveys that have different fields but I want to share a significant amount of code between them. Because of the different fields I want different database tables for the surveys. There is no need to search across different types of surveys. It feels like I want the foreign key in the Client table for fast retrieval and potential eager-loading of the Survey.
So theoretically I think I want polymorphic has_one and multiple inheritance something like this:
class Client < ActiveRecord::Base
has_one :survey, :polymorphic => true
end
class Survey
# base class of shared code, does not correspond to a db table
def utility_method
end
end
class Type1Survey < ActiveRecord::Base, Survey
belongs_to :client, :as => :survey
end
class Type2Survey < ActiveRecord::Base, Survey
belongs_to :client, :as => :survey
end
# create new entry in type1_surveys table, set survey_id in client table
#client.survey = Type1Survey.create()
#client.survey.nil? # did client fill out a survey?
#client.survey.utility_method # access method of base class Survey
#client.survey.type1field # access a field unique to Type1Survey
#client2.survey = Type2Survey.create()
#client2.survey.type2field # access a field unique to Type2Survey
#client2.survey.utility_method
Now, I know Ruby does not support multiple inheritance, nor does :has_one support :polymorphic. So is there a clean Ruby way to achieve what I'm getting at? I feel like it's right there almost...
Here's how I would do this:
class Client < ActiveRecord::Base
belongs_to :survey, :polymorphic => true
end
module Survey
def self.included(base)
base.has_one :client, :as => :survey
end
def utility_method
self.do_some_stuff
end
end
Type1Survey < ActiveRecord::Base
include Survey
def method_only_applicable_to_this_type
# do stuff
end
end
Type2Survey < ActiveRecord::Base
include Survey
end

How to "merge" more than one 'ActiveRecord::Associations' and one or more scope methods?

I am using Ruby on Rails 3.2.2 and MySQL. I would like to "merge" the result of more than one ActiveRecord::Associations and one or more scope methods. That is, I have:
class User < ActiveRecord::Base
has_many :a_user_relationships, :foreign_key => 'a_key'
has_many :b_user_relationships, :foreign_key => 'b_key'
has_many :a_articles, :through => :a_user_article_associations # Returns objects kind of 'Article'
has_many :b_articles, :through => :b_user_article_associations # Returns objects kind of 'Article'
end
class Article < ActiveRecord::Base
# Note: This is a scope method.
def self.public
where(:status => 'public')
end
end
Given the above code I would like to run some method (as-like the following) so to retieve all public "a" and "b" user articles by executing as few as possible database queries:
#user.all_articles
class User < ActiveRecord::Base
# Note: Code in the following method is incorrect, but maybe it helps
# understanding what I mean.
def all_articles
self.(a_articles & b_articles).public
(self.a_articles.public & self.b_articles.public)
end
end
Is it possible?
Since you have two separate associations, you'll need at least two queries.
If you always want articles from both here, is there anything stopping you from having a simple "articles" association in addition to or instead of these two separate ones? That way, you could retrieve your articles with self.articles.public.

How to work with associaion ids like with dirty attributes?

I have some Model that has_many another models. And I want to do something before save, if structure of associated models changes. Something like this:
class Foo < AR:Base
has_many :bars
before_save :do_smth, :if => bar_ids_changed? # like ActiveModel:Dirty
def do_smth
...
end
end
What is the better way to implement this?