automatically generate instances of a model when a different one is created? - ruby-on-rails

I really didn't know how to phrase the question better but here goes:
Lets say i have 2 models
class Record < ActiveRecord::Base
has_many :roles, :dependent => :destroy
end
class Role < ActiveRecord::Base
belongs_to :record
end
I want a way to make sure that every time a new record instance is created 2 new roles are created automatically by the system for that record.
each role will have a name, record_id, Boolean called edit and Boolean called review.
so if i create a record called Hello and it has an ID 1 then the system should create these 2 new roles at the same time:
Role 1: name: Hello edit, record_id: 1, edit: true, review: false
Role 2: name: Hello review, record_id: 1, edit: false, review: true

ActiveRecord callbacks to the rescue:
class Record < ActiveRecord::Base
after_create :create_roles
def create_roles
Role.create :name => "Hello edit", :record_id => self.id, :edit => true, :review => false
Role.create :name => "Hello review", :record_id => self.id, :edit => false, :review => true
end
end

Try this:
class Record < ActiveRecord::Base
has_many :roles
after_create :add_roles
def add_roles
roles.create [
{:name => "Hello edit", :edit => true, :review => false},
{:name => "Hello review", :edit => false, :review => true}]
end
end

Related

after_save error in ruby on rails

I have two models linked to each other and I am trying to do an after_save, create in the model and code is as follows.
class CustomerBill < ActiveRecord::Base
after_save :updating_and_creating_ledger_items
has_many :customer_ledgers, :dependent => :destroy
def updating_and_creating_ledger_items
CustomerLedger.create(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)
end
end
customer ledger model
class CustomerLedger < ActiveRecord::Base
belongs_to :customer_bill, :foreign_key => "customer_bill_id"
end
Now the problem is the program executes perfectly but the value are not been put in the database. If I check Customer ledger it is still empty.
The values are not getting stored. what seems to be the problem? Guidance towards this matter will be helpful.
Thanks in advance. :)
Add
validates_associated :customer_ledgers
In customer_bill.rb
Try
ledger = customer_ledgers.build(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)
ledger.save
EDITED for to avoid Validations, use
ledger = customer_ledgers.build(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)
ledger.save(:validate => false)
The CustomerLedger may be failing a validation check. Replace:
def updating_and_creating_ledger_items
CustomerLedger.create(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)
end
with:
def updating_and_creating_ledger_items
ledger = customer_ledgers.build(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)
ledger.save(:validate => false)
end
You're not setting the customer_bill_id in the create, change create to build as so:
customer_ledger.build(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)

ruby on rails rake db:seed undefined method for

I am trying to set up a basic user authentication - I have the user login stuff sorted and am up to adding roles for the user.
Essentially I want my Users to have many Roles, which gives them access to Rights.
I wrote some seed data but keep getting the error:
rake aborted!
undefined method `roles' for #<Array:0x007f8c0581ba80>
My seed data looks like:
#user
user = User.create!([{ email: 'admin#admin.com', first_name: 'Admin', last_name: 'Test', password: 'admin', password_confirmation: 'admin'}])
user.roles << admins = Role.create!(:name => "Admin")
#user roles
create = Right.create!(:resource => "users", :operation => "CREATE")
read = Right.create!(:resource => "users", :operation => "READ")
update = Right.create!(:resource => "users", :operation => "UPDATE")
delete = Right.create!(:resource => "users", :operation => "DELETE")
#add the roles to the admin
admins.rights << read
admins.rights << create
admins.rights << update
admins.rights << delete
rake db:migrate works fine and all table columns are as I expect them to. Just when I run rake db:seed it aborts with the above error. I understand what the error is saying - I just can't see where I am not defining the has_many to roles.
I have gone through the models very closely but can't seem to find what I have missed.
and my model files looks like this:
class User < ActiveRecord::Base
has_secure_password
has_many :assignments
has_many :roles, :through => :assignments
attr_accessible :email, :first_name, :last_name, :password, :password_confirmation
validates_presence_of :email, :on => :create
validates :password, :confirmation => true
validates :password_confirmation, :presence => true
validates_uniqueness_of :email
#will be using this later to check if the user has access to resources/actions
# def can?(action, resource)
# roles.includes(:rights).for(action, resource).any?
# end
end
class Role < ActiveRecord::Base
has_many :grants
has_many :assignments
has_many :users, :through => :assignments
has_many :rights, :through => :grants
scope :for, lambda{|action, resource|
where("rights.operation = ? AND rights.resource = ?",
Right::OPERATION_MAPPINGS[action], resource
)
}
end
class Right < ActiveRecord::Base
attr_accessible :operation, :resource
has_many :grants
has_many :roles, :through => :grants
OPERATION_MAPPINGS = {
"new" => "CREATE",
"create" => "CREATE",
"edit" => "UPDATE",
"update" => "UPDATE",
"destroy" => "DELETE",
"show" => "READ",
"index" => "READ"
}
end
class Grant < ActiveRecord::Base
attr_accessible :right_id, :role_id
belongs_to :role
belongs_to :right
end
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :role
attr_accessible :role_id, :user_id
end
any help would be greatly appreciated.
You should not create one user as array of users. Try delete the square brackets in User.create!()
user = User.create!({email: 'admin#admin.com', first_name: 'Admin', last_name: 'Test', password: 'admin', password_confirmation: 'admin'})
Just ditch the []'s and {}'s in the first line, eg:
user = User.create!(email: 'admin#admin.com', first_name: 'Admin', last_name: 'Test', password: 'admin', password_confirmation: 'admin')

"Accepts nested attributes" not actually accepting attributes in model

I'm working on a project where there are tasks that make up a scavenger hunt. When a user creates a new hunt, I'd like the hunts/show.html.erb file to show the hunt as well as the tasks associated with that hunt. But the models are giving me trouble. I've got the hunt model setup to that it accepts nested attributes for the tasks model. So when the user creates a new hunt, she also creates three tasks automatically. I can get the new hunt to save, but I can't get those new tasks to save. Here are my models.
What's missing? Do I need an "attr accessible" statement in the HunTasks.rb file?
class Hunt < ActiveRecord::Base
has_many :hunt_tasks
has_many :tasks, :through => :hunt_tasks
accepts_nested_attributes_for :tasks, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
attr_accessible :name
validates :name, :presence => true,
:length => { :maximum => 50 } ,
:uniqueness => { :case_sensitive => false }
end
class Task < ActiveRecord::Base
has_many :hunt_tasks
has_many :hunts, :through => :hunt_tasks
attr_accessible :name
validates :name, :presence => true,
:length => { :maximum => 50 } ,
:uniqueness => { :case_sensitive => false }
end
class HuntTask < ActiveRecord::Base
belongs_to :hunt # the id for the association is in this table
belongs_to :task
end
Here's what my Hunt controller looks like:
class HuntsController < ApplicationController
def index
#title = "All Hunts"
#hunts = Hunt.paginate(:page => params[:page])
end
def show
#hunt = Hunt.find(params[:id])
#title = #hunt.name
#tasks = #hunt.tasks.paginate(:page => params[:page])
end
def new
if current_user?(nil) then
redirect_to signin_path
else
#hunt = Hunt.new
#title = "New Hunt"
3.times do
hunt = #hunt.tasks.build
end
end
end
def create
#hunt = Hunt.new(params[:hunt])
if #hunt.save
flash[:success] = "Hunt created!"
redirect_to hunts_path
else
#title = "New Hunt"
render 'new'
end
end
....
end
The major difference between your example and the railscast is that you are doing many-to-many instead of one to many (I think his was Survey had many Questions). Based on what you described, I wonder if the HuntTask model is necessary. Are the tasks for one hunt ever going to be resused in another hunt? Assuming they are, then looks like your answer is here:
Rails nested form with has_many :through, how to edit attributes of join model?
You'll have to modify your new action in the controller to do this:
hunt = #hunt.hunt_tasks.build.build_task
Then, you'll need to change your Hunt model to include:
accepts_nested_attributes_for :hunt_tasks
And modify your HuntTask model to include:
accepts_nested_attribues_for :hunt

has_many with two has_one's

I have a User model that has_many parents.
I want that user model to have one father and one mother.
So my class Parent belongs_to user
Currently I have
class User < ActiveRecord::Base
has_many :parents
has_one :father, :class_name => 'Parent', :foreign_key => 'user_id', :conditions => {:type => 'male'}
has_one :mother, :class_name => 'Parent', :foreign_key => 'user_id', :conditions => {:type => 'female'}
end
class Parent < ActiveRecord::Base
belongs_to :user
end
The problem is in my controller.
...
def edit
#user = User.find(params[:id])
#user.mother = Parent.new(:type => 'female')
#user.father = Parent.new(:type => 'male')
...
When I go into the edit, it creates and throws the 2 parents into the database without even having changed anything in the form. For example, when I click edit on a user, I go to the edit page. When I look into the database, they're already created.
My form looks like so:
= form_for #user do |f|
= f.fields_for :father do |father_form|
etc...
= f.fields_for :mother do |mother_form|
etc...
I've tried doing something alone the lines of this in my controller:
...
#user.parents.build(:type => 'male')
#user.parents.build(:type => 'female')
...
But the form doesn't show up.
Any help would be greatly appreciated.
Try to use
#user.build_father(:type => 'male')
#user.build_mother(:type => 'female')
instead of
#user.mother = Parent.new(:type => 'female')
#user.father = Parent.new(:type => 'male')
in your action

Getting value from associated model

My setup: Rails 2.3.10, Ruby 1.8.7
Here are my models
class User
has_many :user_projects
end
class Project
has_many :user_projects
#has a column named "project_type"
end
class UserProject
belongs_to :user
belongs_to :project
#fields: project_id, user_id
end
When I return a JSON string of a user and his related user_projects records, I also want to include in the user_project record the project.project_type column. Note: I don't want to also include the entire project record in the results. A possible solution is dup the project_type field in user_projects but I prefer not to do that if possible, is there another way to accomplish this during the find/read action?
Just to be clear, here's the JSON output I'm looking for
{
"user": {
"username": "bob",
"id": 1,
"email": "bob#blah.com"
"user_projects": [
{
"id": 15,
"user_id": 1,
"project_id": 10,
"project_type": "marketing"
}
{
"id": 22,
"user_id": 1,
"project_id": 11,
"project_type": "sales"
}
]
}
class UserProject
belongs_to :user
belongs_to :project
#fields: project_id, user_id
attr_reader :type
def type
self.project.type
end
end
class MyController < AC
def action
#model = whatever
respond_to do |format|
format.json { render :json => #model.to_json(:methods => :type)}
end
end
end
Hope this helps.
You could try using the :only key in a nested include:
user.to_json(:include => {:user_projects => {:include => {:project => {:only => :type}}}})
But I would add has_many :projects, :through => :user_projects to User so you can do the simpler:
user.to_json(:include => {:projects => {:only => [:id, :type]}})
Also, an off-topic cautionary note: never use 'type' as a column name in Rails unless you are using STI (ie. the project types are ruby subclasses of Project).
-
Edit
Here's a way to add project_type to UserProject like you want
class UserProject
belongs_to :user
belongs_to :project
delegate :type, :to => :project, :prefix => true
end
user.to_json(:include => {:user_projects => {:methods => :project_type}})

Resources