Model association that spans across parent app and engine - ruby-on-rails

I'm making a Rails engine that makes reference to the current_user in a controller like so:
require_dependency "lesson_notes/application_controller"
module LessonNotes
class NotesController < ApplicationController
def index
if current_user
#notes = Note.where(student: current_user) if current_user.user_type.name == "student"
#notes = Note.where(teacher: current_user) if current_user.user_type.name == "teacher"
end
end
end
end
This is quite verbose and it would seem I could do something like this instead:
require_dependency "lesson_notes/application_controller"
module LessonNotes
class NotesController < ApplicationController
def index
#notes = current_user.notes if current_user
end
end
end
However, the user model exists in the parent app, not the engine.
In the Note model I have this to define the belongs_to association:
module LessonNotes
class Note < ActiveRecord::Base
belongs_to :student, class_name: "::User"
belongs_to :teacher, class_name: "::User"
end
end
How would I define the other side of the association - the has_many - in the User model?

This is how I ended up doing it...
In the parent app:
class User < ActiveRecord::Base
has_many :notes, class_name: LessonNotes::Engine::Note, foreign_key: "teacher_id"
end

Related

Rails Has many in a belongs_to relationship

In my rails app I have the following models
class Member < ActiveRecord::Base
has_many :trainings
end
class Student < ActiveRecord::Base
belongs_to :member
has_many :trainings #maybe a through relationship here
end
class Teacher < ActiveRecord::Base
belongs_to :member
end
######edited#################
class Training < ActiveRecord::Base
belongs_to :member #only member not student nor teacher
end
#############################
Now, how can I build the trainings in my student controller
class StudentsController < ApplicationController
def new
#student = Student.new
#student.trainings.build #### This is not working
end
end
Thanks
You have to write accepts_nested_attributes_for in the model and add them in strong parameters if you are using rails 4. Like this :
class Student < ActiveRecord::Base
belongs_to :member
has_many :trainings
accepts_nested_attributes_for :trainings
end
class StudentsController < ApplicationController
def new
#student = Student.new
#student.trainings.build
end
def create
#student = Student.create(student_params)
#student.trainings.build(params[:student][:trainings])
redirect_to student_path
end
#For rails 4
def student_params
params.require(:student).permit(:id, :name, trainings_attributes: [ :id, :your fields here ])
end
end
Here is a link that will help you:
Rails 4: accepts_nested_attributes_for and mass assignment
If you've properly defined your associations, then the code in your new controller action will work (I tested it). Check and make sure your Training model exists, or that you've used the correct association name (perhaps you meant :teachers?).
app/models/student.rb
class Student < ActiveRecord::Base
has_many :trainings
end
app/models/training.rb
class Training < ActiveRecord::Base
belongs_to :student
end
app/controllers/students_controller.rb
class StudentsController < ApplicationController
def new
#student = Student.new
#student.trainings.build
end
end
Update:
Assuming these are how your associations are defined, you could build a scoped instance of Training like so:
app/models/member.rb
class Member < ActiveRecord::Base
has_many :trainings
end
app/models/student.rb
class Student < ActiveRecord::Base
delegate :trainings, to: :member
belongs_to :member
end
app/models/training.rb
class Training < ActiveRecord::Base
belongs_to :member
end
app/controllers/students_controller.rb
class StudentsController < ApplicationController
def new
#student = Student.new
#student.build_member
#student.trainings.build
end
end
Hope that helps.

How to associate ActiveRecord model and plain class model

class User < ActiveRecord::Base
has_one :report
has_many :invoices
end
class Report
include ActiveModel::Model
belongs_to :user
def self.monthly_sales
user.invoices.group_by { |i| i.date.beginning_of_month }
end
end
Unfortunately the code above is not working. I want to access my report methods like #user.report.monthly_sales. I feel like I am so close to it. Please show me the way how to associate these two models.
Instead of association, you could just do like below:
class User < ActiveRecord::Base
has_many :invoices
def report
#report ||= Report.new(self)
#report
end
end
class Report
include ActiveModel::Model
def initialize(user)
#user = user
end
def monthly_sales
user.invoices.group_by { |i| i.date.beginning_of_month }
end
end

Rials:When we use fields_for, how to initiate association_build if we are not sure how many association should be created?

In such case:
class User < ActiveRecord::Base
has_many :companies
accepts_nested_attributes_for :companies
end
class Company < ActiveRecord::Base
belongs_to :users
end
This is nomal case, we're sure how many association we should create:
# user_controller
def new
#user = User.new
2.times { #user.companies.build }
end
But what if we don't know how many association should be created?

Any shortcut for updating join table when creating one of the models

For example, let us say we have
class User < ActiveRecord::Base
has_many :networks, through: user_networks
has_many :user_networks
end
class Network< ActiveRecord::Base
has_many :users, through: user_networks
has_many :user_networks
end
class UserNetwork < ActiveRecord::Base
belongs_to :user
belongs_to :network
end
Is there a shortcut for doing the following in a controller:
#network = Network.create(params[:network])
UserNetwork.create(user_id: current_user.id, network_id: #network.id)
Just curious and I doubt it.
This should work:
current_user.networks.create(params[:network])
But your code implies you are not using strong_parameters, or checking the validation of your objects. Your controller should contain:
def create
#network = current_user.networks.build(network_params)
if #network.save
# good response
else
# bad response
end
end
private
def network_params
params.require(:network).permit(:list, :of, :safe, :attributes)
end

Automatically create associations through many existing associations

I'm working on an engine where any model can have a has_many association with Permit as Permissible:
class Permit < ActiveRecord::Base
belongs_to :permissible, polymorphic: true
end
module Permissible
def self.included(base)
base.class_eval do
has_many :permits, as: :permissible
end
end
class Group < ActiveRecord::Base
include Permissible
end
class GroupAllocation < ActiveRecord::Base
belongs_to :person
belongs_to :group
end
class Person < ActiveRecord::Base
include Permissible
has_many :group_allocations
has_many :groups, through: :group_allocations
end
class User < ActiveRecord::Base
belongs_to :person
end
So, Group has_many :permits and Person has_many :permits. What I am trying to do is dynamically create associations on User that uses the permits association as a source, and chain associations on other models down to User by doing the same. This can be done manually (in rails 3.1+) with:
class Person
has_many :group_permits, through: :person, source: :permits
end
class User
has_many :person_permits, through: :person, source: :permits, class_name: Permit
has_many :person_group_permits, through: :person, source: :group_permits, class_name: Permit
end
However, in practice, Permissible will be included on many models, so I'm trying to write a class method on User (actually within another module, but no need to confuse things more) that can traverse User.reflect_on_all_associations and create an array of new associations, which may be many associations deep each.
Looking for input on how to do this cleanly in rails 3.2.8.
Here is how I did it (implementation code varies slightly from the details given in the question):
module Authorisable
def self.included(base)
base.class_eval do
base.extend ClassMethods
end
end
module ClassMethods
class PermissionAssociationBuilder
def build_permissions_associations(klass)
chains = build_chains_from(klass)
chains.select! {|c| c.last.klass.included_modules.include? DistributedAuthorisation::Permissible}
permissions_associations = []
chains.each do |chain|
source_name = :permissions
chain.reverse.each do |r|
assoc_name = :"#{r.name}_#{source_name}"
r.active_record.has_many assoc_name, through: r.name.to_sym, source: source_name, class_name: DistributedAuthorisation::Permission
source_name = assoc_name
end
permissions_associations << source_name
end
return permissions_associations
end
private
def build_chains_from(klass)
chains = reflections_to_follow(klass).map {|r| [r]}
chains.each do |chain|
models = chain.map {|r| r.klass}.unshift klass
reflections_to_follow(models.last).each do |r|
chains << (chain.clone << r) unless models.include? r.klass
end
end
end
def reflections_to_follow(klass)
refs = klass.reflect_on_all_associations
refs.reject {|r| r.options[:polymorphic] or r.is_a? ActiveRecord::Reflection::ThroughReflection}
end
end
def permissions_associations
#permissions_associations ||= PermissionAssociationBuilder.new.build_permissions_associations(self)
end
end
Probably not the most efficient method, but it adds the chains I'm after with Klass.permissions_associations, and stores their symbols in class instance variable.
I'd be happy to hear suggestions on how to improve it.

Resources