Copy fixed data into model - ruby-on-rails

I'm developing an application in which a Course belongs to a user. I would like to predefine a number of courses, such that a Course's template details are then copied into the users course details. From this initial point each user has a one-to-one mapping with a course.
I'd like to know the best place for the static attributes for building a user's course.
Thanks,
Adam

You could use a before_create or after_create filter on your user model, something like this:
before_create :add_default_courses
def add_default_courses
self.courses << Course.new({:foo => 'bar'});
end

you can initialize courses after a user is created.
User.rb #you user file
def after_initialize
self.courses << add_courses
end
private
def add_courses
#add_courses = Courses.find(:all, conditions => [])
end

Related

How to access a model from another model?

I have two models in ROR, one which is Note and another one which is Access. Each Access has a Note field and a user field. In my index action of the notes controller I want to filter notes owned by the user (done) as well as notes accessible to the user, which I named #accessible_notes.
The following code gives me the correct notes owned by the user, however I cannot get the notes accessible to the user.
Basically, I need to find all the Accesses in which the user is involved and then fetch the corresponding notes. How can I do that?
def index
#notes = Note.where(user: current_user)
#personal_access = Access.where("user_id = ?",current_user.id)
#accessible_notes = []
#personal_access.each do |accessible|
tnote = Note.find(accessible.note_id)
#accessible_notes += tnote if tnote
end
end
class User < ActiveRecord::Base
has_many :accessible_notes, :through => :accesses, :source => :notes
end
#accessible_notes = current_user.accessible_notes
How about
#personal_access.each do |accessible|
#accessible_notes << accessible.note
end
#accessible_notes.flatten!
There might be a faster way using Active Record queries.
And that faster way is in depa's answer.

Scoping a class method to current_user

I'm working on implementing a tagging system and I'm having problem querying for tagged objects with a scope.
For example, I would like to find all the user's items with a certain tag. With a class method I can currently find all the objects:
def self.tagged_with(name)
Tag.find_by_name(name).items
end
However, this has a problem. If I were to do something like: current_user.items.tagged_with(name) won't this existing method return ALL the items and not just items owned by the current_user? I suppose this is a simply querying issue but I can't figure out how to change a class method into something called on a collection. I have tried going the opposite way, to get a the collection through the tags, something like... tag.items.where(:user_id => current_user.id) but in this case, it's a many-to-many relationship and I haven't been able to get on thumb on this either.
What's the proper way to restrict a query like this?
Create an association on your User class that points to your Tag class.
class User < ActiveRecord::Base
has_many :tags
end
Then you can do:
current_user.tags.where(...)
If you don't already have an association in place, you'll need to create a migration to have the tags table reference your users table with a foreign key.
I think this will help you:
class Account < ActiveRecord::Base
has_many :people do
def find_or_create_by_name(name)
first_name, last_name = name.split(" ", 2)
find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
end
person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name # => "Heinemeier Hansson"
So, basically you can define your method tagged_with directly into the association!
This example is took from the documentations ActiveRecord::Associations

create a record in a different class(table)

I have a class People and class User (from Devise).
When someone signs up a user row(object) gets created in the User class(table).
I would also like the user.rb model to create a row(object) in the People class(table).
(The user.rb also has "has_one :person" in it.)
I tried the following without success:
after_create :create_person
protected
def create_person
self.create_person email: self.email
end
How could I code this?
after_create :create_person
protected
def create_person
Person.create(self.attributes)
end
But take care, if you want to update the person record when the corresponding user record is updated use after_save and Person.find_or_create_by_email(self.email)
The code you show should work,
the only reason why it might not work would be validations.
Doing create_person will do a save not a save!.
If you have validation on the Person model, it may be failing.

Validate using an existing value

I have a Topic model:
class Topic < ActiveRecord::Base
belongs_to :user
def validate_on_update
errors.add(:user, "Only the topic creator can update the topc") if
self.user != user;
end
end
I would like to check before every update that the existing topic.user is the same with the user that is trying to update the model.
I think that
self.user != user
is not working but I do not know how to fix that!
You need to find the record in the controller before doing that, so you can do this in your controller action:
#topic = current_user.topics.find(params[:id])
This will trigger an exception that you can easily catch or leave it.
This is the best method to ensure data integrity, unless you're tinkering in other places of the app and you need to create Topics not in controllers.
If you have such need, it's not bad to have a validation rule in the model to ensure major data integrity, but the model does need to know the user, that's only accessible from the controller.
My recommendation is that you assign the user controller-side or just use scopes like:
current_user.topics.create(params[:topic])
This way you are sure that the user is the same in question, and this invalidates the need to do another validation if it's the only place you're calling topic creation.
If you are unsure and wants to game on with a validate_on_update I suggest creating a virtual attribute like so:
attr_accessor :this_user
But in any case you'd pass this via controller, since your model should know nothing about the current logged in user:
#topic = Topic.new(params[:topic])
#topic.this_user = current_user # or #topic.user_id and check for a attr_changed?
Update: adding example as requested
# #topic = Topic.new(params[:topic])
# #topic.this_user = current_user
class Topic < ActiveRecord::Base
belongs_to :user
attr_accessor :this_user
def validate_on_update
# Make sure that this_user is an instance of User, otherwise just use the id
errors.add(:user, "Only the topic creator can update the topic") if user_id != this_user.id;
end
end
Update:
another suggestion is to use:
attr_readonly :user_id

nested form & habtm

I am trying to save to a join table in a habtm relationship, but I am having problems.
From my view, I pass in a group id with:
<%= link_to "Create New User", new_user_url(:group => 1) %>
# User model (user.rb)
class User < ActiveRecord::Base
has_and_belongs_to_many :user_groups
accepts_nested_attributes_for :user_groups
end
# UserGroups model (user_groups.rb)
class UserGroup < ActiveRecord::Base
has_and_belongs_to_many :users
end
# users_controller.rb
def new
#user = User.new(:user_group_ids => params[:group])
end
in the new user view, i have access to the User.user_groups object, however when i submit the form, not only does it not save into my join table (user_groups_users), but the object is no longer there. all the other objects & attributes of my User object are persistent except for the user group.
i just started learning rails, so maybe i am missing something conceptually here, but i have been really struggling with this.
Instead of using accepts_nested_attributes_for, have you considered just adding the user to the group in your controller? That way you don't need to pass user_group_id back and forth.
In users_controller.rb:
def create
#user = User.new params[:user]
#user.user_groups << UserGroup.find(group_id_you_wanted)
end
This way you'll also stop people from doctoring the form and adding themselves to whichever group they wanted.
What does your create method look like in users_controller.rb?
If you're using the fields_for construct in your view, for example:
<% user_form.fields_for :user_groups do |user_groups_form| %>
You should be able to just pass the params[:user] (or whatever it is) to User.new() and it will handle the nested attributes.
Expanding on #jimworm 's answer:
groups_hash = params[:user].delete(:groups_attributes)
group_ids = groups_hash.values.select{|h|h["_destroy"]=="false"}.collect{|h|h["group_id"]}
That way, you've yanked the hash out of the params hash and collected the ids only. Now you can save the user separately, like:
#user.update_attributes(params[:user])
and add/remove his group ids separately in one line:
# The next line will add or remove items associated with those IDs as needed
# (part of the habtm parcel)
#user.group_ids = group_ids

Resources