How to query intermediate table? - ruby-on-rails

I have two classes in ruby:-
class Role < ActiveRecord::Base
# attr_accessible :title, :body
acts_as_authorization_role :subject_class_name => 'User', :join_table_name => "roles_users"
end
and
class User < ActiveRecord::Base
acts_as_authentic
acts_as_authorization_object :role_class_name => 'Role', :subject_class_name => 'User'
acts_as_authorization_subject :association_name => :roles , :join_table_name => 'roles_users'
has_one:employee_detail ,:foreign_key => "User_id"
end
and migration files are:-
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :username
t.string :email
t.string :crypted_password
t.string :password_salt
t.string :persistence_token
t.timestamps
end
end
end
class CreateRoles < ActiveRecord::Migration
def change
create_table "roles" , :force => true do |t|
t.string :name , :limit => 40
t.string :authorizable_type, :limit => 40
t.integer :authorizable_id
t.timestamps
end
end
end
class RolesUsers < ActiveRecord::Migration
def change
create_table "roles_users", :id => false, :force => true do |t|
t.references :user
t.references :role
end
end
end
i have user id and want to retrieve role id, but not able to query intermediate table. can anyone provide some solution for this. thnks

It's clear that your user is going to have multiple roles
You can define an habtm relation in user for for role and get retrive roles roles like following
class User < ActiveRecord::Base
acts_as_authentic
acts_as_authorization_object :role_class_name => 'Role', :subject_class_name => 'User'
acts_as_authorization_subject :association_name => :roles , :join_table_name => 'roles_users'
has_one:employee_detail ,:foreign_key => "User_id"
has_and_belongs_to_many :roles
end
And retrieve roles for a user like below
user = User.find user_id
user.roles # this will give you roles array
role_ids = user.roles.map { |r| r.id } # if you only want array of ids

Related

How to save class as one of the attributes in ROR model

I am trying to create a model in rails named chat, where I have two columns user1 and user2, and I want to store the user object in these. In grails, I do this simply as
class Chat {
User user1
User user2
Date chatStartedOn
}
and I am done. I did somewhat the same for rails
rails generate model Chat user1:User user2:User chatStartedOn:date
but I run db:migrate it showing me the error
undefined method `User' for #<ActiveRecord::ConnectionAdapters::TableDefinition
my user migrate file
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :username
t.string :email
t.string :encrypted_password
t.string :salt
t.timestamps
end
end
end
Please guide me on how I save user's object in chat table.
Try this
rails generate migration CreateChats
You can then add this code in your Chat migration file
class CreateChats < ActiveRecord::Migration
def change
create_table :chats do |t|
t.integer :user1_id, :references => [:users, :id]
t.integer :user2_id, :references => [:users, :id]
t.date :chatStartedOn
t.timestamps
end
end
end
you would then need to add the associations
Model user.rb
has_one :chat
Model chat.rb
belongs_to :user1, :class_name => "User"
belongs_to :user2, :class_name => "User"

Derived Activerecord Ruby Class

I am totally new to ruby. I am trying to make a RESTful service for task tracking application. I researched and found Sinatra better for the job than rails. So I am using Sinatra and ActiveRecord. I am following Up and Running With Sinatra and ActiveRecord. I will be creating the client application in .NET using Restsharp. But this is all about server side.
This is the migration I have created
class CreateTasksPeopleDocumentsAndComments < ActiveRecord::Migration
def self.up
create_table :tasks do |t|
t.string :name
t.string :task_status
t.string :task_type
t.text :description
t.text :analysis
t.text :investigation
t.integer :developer_id
t.integer :reviewer_id
t.date :open_date
t.date :analysis_date
t.date :promotion_date
t.date :review_date
t.date :correction_date
t.date :collection_date
t.date :closed_date
t.date :modified_date
t.date :target_date
end
create_table :people do |t|
t.string :name
t.string :trigram
t.string :state
t.string :level
end
create_table :documents do |t|
t.string :name
t.binary :data
t.string :path
t.integer :task_id
t.integer :developer_id
end
create_table :comments do |t|
t.text :comment
t.datetime :comment_timestamp
t.integer :person_id
t.integer :task_id
t.integer :comment_id
end
end
def self.down
drop_tables :tasks
drop_tables :people
drop_tables :documents
drop_tables :comments
end
end
And the Main App.rb
class Person < ActiveRecord::Base
end
class Developer < Person
has_many :tasks
end
class Reviewer < Person
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :developer
belongs_to :reviewer
has_many :documents
end
class Document < ActiveRecord::Base
belongs_to :task
belongs_to :developer
end
class Comment < ActiveRecord::Base
belongs_to :task
has_many :comments
end
get '/' do
"Hola World!"
end
get '/tasks' do
Task.all.to_json
end
get '/people' do
Person.all.to_json
end
get '/person/:id' do
Person.where(["id = ?", params[:id]]).to_json
end
get '/task/:id' do
Task.where(["id = ?", params[:id]]).to_json
end
get '/document/:id' do
Document.where(["id = ?", params[:id]]).to_json
end
get '/task/:id/documents' do
# Task.where(["id = ?", params[:id]]).document.all.to_json
# Document.where(["task_id = ?", params[:id]]).all.to_json
Task.find(params[:id]).documents.all.to_json
end
get '/make' do
person1 = Person.create(
:name => "AEonAX",
:trigram =>"anx",
:state => "Active",
:level => "Master"
)
person2 = Person.create(
:name => "XEonAX",
:trigram =>"xnx",
:state => "Inactive",
:level => "User"
)
person3 = Person.create(
:name => "ZEonAX",
:trigram =>"znx",
:state => "Active",
:level => "User"
)
person4 = Person.create(
:name => "LEonAX",
:trigram =>"lnx",
:state => "Inactive",
:level => "Master"
)
task1 = Task.create(
:name => "IR-000001V0R2000",
:description => "The Very First Incident Report",
:task_status => "Opened",
:task_type => "Internal",
:developer_id => person2.id,
:reviewer_id => person1.id
)
task2 = Task.create(
:name => "IR-000002V0R2000",
:description => "Second Incident Report",
:task_status => "Tech. Anal.",
:task_type => "External",
:developer_id => person2.id,
:reviewer_id => person1.id
)
task3 = Task.create(
:name => "IR-000003V0R2000",
:description => "Another Incident Report",
:task_status => "Under Corr.",
:task_type => "External",
:developer_id => person3.id,
:reviewer_id => person1.id
)
document1 = Document.create(
:name => "FirstDoku",
:path => "\\XEON-NB\Test\FiddlerRoot.cer",
:task_id => task1.id,
:developer_id => task1.developer.id,
:data => Task.all.to_json #SomeBinaryData
)
end
Currently this code only reads data. I have not started writing of data.
Basically the relations are like for a task there will be a developer and reviewer. It will have documents attached to it. It will also have comments.
Comments can be on the task or in reply to a comment.
As you can see I have declared a Person class and derived Developer and Reviewer from it. Is it the right way? Any other suggestions are welcome. Even suggestions to use other frameworks are accepted.

How to compare activerecord object to new object and update if nto equal in Rails 3?

I'm trying to figure out if there is a way to do this in Rails more efficiently.
There is kind of a long setup for the question, so please bear with me.
Let's say I have models Customer, Phone, Address
Here are sample migrations to give you an idea:
class CreatePhones < ActiveRecord::Migration
def self.up
create_table :phones do |t|
t.integer :country_prefix, :limit => 3
t.integer :area_prefix, :limit => 5
t.integer :number, :limit => 7
t.integer :category_id
t.references :phonable, :polymorphic => true
t.timestamps
end
end
end
class CreateAddress < ActiveRecord::Migration
def self.up
create_table :addresses do |t|
t.string :address_line_1
t.string :address_line_2
t.string :address_line_3
t.string :city
t.string :state
t.string :zip
t.string :country
t.string :attn
t.integer :category_id
t.references :addressable, :polymorphic => true
t.timestamps
end
end
end
class CreateCategories < ActiveRecord::Migration
def self.up
create_table :categories do |t|
t.string :name
t.string :code
t.integer :category_id # Every subcategory has a category: i.e. phone has work, fax,mobile
t.timestamps
end
end
end
class CreateCustomers < ActiveRecord::Migration
def self.up
create_table :customers do |t|
t.string :code , :limit => 20 , :null => false
t.string :name , :null => false
t.string :billing_name
t.integer :preferred_shipping_method_id
end
end
Here are models and relations:
class Customer < ActiveRecord::Base
belongs_to :preferred_shipping_method , :class_name => "Category", :foreign_key => :preferred_shipping_method_id
has_many :addresses, :as => :addressable, :include => :category, :dependent => :destroy
has_many :phones, :as => :phonable, :include => :category, :dependent => :destroy
end
class Category < ActiveRecord::Base
has_many :addresses
has_many :phones
has_many :customer_by_shipping_methods, :class_name => "Customer", :foreign_key => :preferred_shipping_method_id
has_many :subcategories, :class_name => "Category", :foreign_key => :category_id
belongs_to :category, :class_name => "Category"
end
class Address < ActiveRecord::Base
belongs_to :category
belongs_to :addressable, :polymorphic => true
end
class Phone < ActiveRecord::Base
belongs_to :category
belongs_to :phonable, :polymorphic => true
end
Here is a question.
Let's say I have a customer record with a bunch of phone (mobile, work) and addresses (billing, shipping).
old = Customer.where(:code => "ABC").first
Then I'm creating or importing (from a legacy DB) another customer object
new = Customer.new
new.code = "ABC"
new.phones.build(:number => "12345567")
etc.
Then I want to compare old customer info to the new customer info and based on that update old customer info.
Like so :
if old.eql?(new) # this should compare not only, name & code and such but also polymorphic associations
old.update_with(new) # this should update old info attributes with new if there is new info, or if update / add to one of the associations
old.save #
else
new.save
end
So the question is is there any CONVENTIONAL way in Rails 3 to do what I describe in comments?
Right now I'm overriding hash & eql? methods which is fine for comparison. But to update each attribute and each associated object and its attributes, is getting kind of involved. I was wondering if there is an easier way to do this then my way:
class Customer < ActiveRecord::Base
def hash
%{#{ name }#{ code }}.hash # There is a lot more here of course
end
def eql?(other)
hash == other.hash
end
def update_with(other)
name = other.name
code = other.code
etc ....
end
end
Ok it doesn't look like there is a standard solution so here is something I came up if anybody else is looking for it.
You can rename methods anyway you like, just put this in you lib folder in some .rb file. ( don't forget to mention it in environment.rb like so require 'custom_comparisons'
/lib/custom_comparisons.rb
module ActiveRecord
class Base
def comparison_hash
h = ""
self.attributes.each_pair do |key, value|
unless ["id", "updated_at", "created_at"].include?(key)
h << "#{value}"
end
end
h.hash
end
def eql_to?(other)
comparison_hash == other.comparison_hash
end
def differences_from?(other)
h = {}
self.attributes.each_pair do |key, value|
unless self.method_missing(key) == other.method_missing(key)
h[key.to_sym] = [self.method_missing(key), other.method_missing(key)]
end
end
h
end
end
end
This can be cleaned up a bit and I need to add association drill down but the solution is there.
This does the comparison and the shows the differences between objects. Now I can update attributes that need to be updated. Will add update method and drill down tomorrow.

Retactoring advanced has_many example

My user model has three relations for the same message model, and is using raw SQL :/ Is there a better more rails way to achieve the same result?
Could the foreign key be changed dynamically? e.g User.messages.sent (foreign key = author_id) and User.messages.received (foreign key = recipient ) I have been trying to move some of the logic into scopes in the message model, but the user.id is not available from the message model...
Any thoughts?
Table layout:
create_table "messages", :force => true do |t|
t.string "subject"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "author_id"
t.integer "recipient_id"
t.boolean "author_deleted", :default => false
t.boolean "recipient_deleted", :default => false
end
This is my relations for my user model:
has_many :messages_received, :foreign_key => "recipient_id", :class_name => "Message", :conditions => ['recipient_deleted = ?', false]
has_many :messages_sent, :foreign_key => "author_id", :class_name => "Message", :conditions => ['author_deleted = ?', false]
has_many :messages_deleted, :class_name => "Message", :finder_sql => 'SELECT * FROM Messages WHERE
author_id = #{self.id} AND author_deleted = true OR
recipient_id = #{self.id} AND recipient_deleted = true'
Best regards.
Asbjørn Morell
Yes, use a named_scope for sorting between deleted and not deleted messages.
class User < ActiveRecord::Base
has_many :messages_received, :foreign_key => 'recipient_id'
has_many :messages_sent, :foreign_key => 'author_id'
end
class Messages < ActiveRecord::Base
named_scope :deleted, :conditions => 'author_deleted = TRUE OR recipient_deleted = TRUE'
named_scope :not_deleted, :conditions => 'author_deleted = FALSE OR recipient_deleted = FALSE'
end
# Example user
user = User.first
user.messages_received.deleted
user.messages_received.not_deleted
user.messages_sent.deleted
user.messages_sent.not_deleted
Alternatively, you could go one step further and simplfy the association by using the user_id as the foreign key and specifying the message type.
create_table "messages", :force => true do |t|
t.string "subject"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
t.string "message_type"
t.integer "user_id"
t.boolean "deleted", :default => false
end
class User < ActiveRecord::Base
has_many :messages
end
class Messages < ActiveRecord::Base
MESSAGE_TYPES = %w[Recipient Author]
belongs_to :user
named_scope :recipient, :conditions => {:message_type => 'Recipient'}
named_scope :author, :conditions => {:message_type => 'Author'}
named_scope :deleted, :conditions => {:deleted => true}
named_scope :not_deleted, :conditions => {:deleted => false}
# Convenience class methods
def self.sent
author.not_deleted
end
def self.received
recipient.not_deleted
end
end
# Example usage
user = User.first
user.messages.sent
user.messages.received
user.messages.deleted
This approach is advantagoues because:
One less column.
Extendable. Adding an additional message type in the future is trivial (Eg: Drafts).

STI and has_many association with "type" column as Key

I am using Single Table Inheritance for managing different types of projects.
I decided to store some information associated with each project type. So i created new table "project_types" with "model_type" field as primary key. Primary key values are values of "type" field of "projects" table. Problem: When i trying to get associated with Project object ProjectTypes object it always returns null.
>> p = Project.find(:first)
=> #<SiteDesign id: 1, type: "SiteDesign", name: "1", description: "dddd", concept: "d", client_id: 40, created_at: "2009-10-15 08:17:45", updated_at: "2009-10-15 08:17:45">
>> p.project_type
=> nil
Getting projects associated with ProjectTypes project is OK. Is there way to make it works properly?
Models:
class Project < ActiveRecord::Base
belongs_to :project_type, :class_name => "ProjectTypes", :foreign_key => "model_name"
end
class SiteDesign < Project
end
class TechDesign < Project
end
class ProjectTypes < ActiveRecord::Base
self.primary_key = "model_name"
has_many :projects, :class_name => "Project", :foreign_key => "type"
end
Migrations:
class CreateProjectTypes < ActiveRecord::Migration
def self.up
create_table :project_types, :id => false do |t|
t.string :model_name , :null => false
t.string :name, :null => false
t.text :description
t.timestamps
end
add_index :project_types, :model_name, :unique => true
#all project types that are used.
models_names = {"SiteDesign" => "Site design",
"TechDesign" => "Tech design"}
#key for model_name and value for name
models_names.each do |key,value|
p = ProjectTypes.new();
p.model_name = key
p.name = value
p.save
end
end
def self.down
drop_table :project_types
end
end
class CreateProjects < ActiveRecord::Migration
def self.up
create_table :projects do |t|
t.string :type
t.string :name
t.text :description
t.text :concept
t.integer :client_id
t.timestamps
end
end
def self.down
drop_table :projects
end
end
Not surprising you're getting problems. By moving from a pure STI system to your current system you are horribly breaking the patterns you are using by intermingling parts of one with parts of another.
I'd personally go for something like:
class Project < ActiveRecord::Base
attr_readonly(:project_type)
belongs_to :project_type
before_create :set_project_type
def set_project_type()
project_type = ProjectType.find_by_model_name(this.class)
end
end
class SiteProject < Project
end
class TechProject < Project
end
class ProjectType < ActiveRecord::Base
has_many :projects
end
with migrations:
class CreateProjectTypes < ActiveRecord::Migration
def self.up
create_table :project_types do |t|
t.string :model_name , :null => false
t.string :name, :null => false
t.text :description
t.timestamps
end
add_index :project_types, :model_name, :unique => true
#all project types that are used.
models_names = {"SiteDesign" => "Site design",
"TechDesign" => "Tech design"}
#key for model_name and value for name
models_names.each do |key,value|
p = ProjectTypes.new();
p.model_name = key
p.name = value
p.save
end
end
def self.down
drop_table :project_types
end
end
class CreateProjects < ActiveRecord::Migration
def self.up
create_table :projects do |t|
t.string :type
t.references :project_type, :null => false
t.text :description
t.text :concept
t.integer :client_id
t.timestamps
end
end
def self.down
drop_table :projects
end
end
It just cleans things up and it also helps clarify what you're doing. Your 'ProjectType' table is purely for extra data, your inheritance tree still exists. I've also thrown in some checks to make sure your project type is always set (and correctly, based on the model name) and stops you from changing project type once it's been saved by making the attribute read only.

Resources