How to associate ActiveRecord model and plain class model - ruby-on-rails

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

Related

How to validate the state of a join model before creation

As I got downvoted the first time, this time I try to be as clear as possible about my goals. If they're not clear, please let me know what's missing.
I have course and students which have a has_many through: relationship. When I create a record for a newCourseParticipation, I would like to check if the course is already full (via the full? method).
What is the best way to do that? My first impulse was to introduce a conditional check in the Create action of the controller, now I'm doing the validation in the Course model. But I think it would best be a "before_create" validation in the CourseParticipation model. Not sure how to do this though.
My Course model
class Course < ActiveRecord::Base
has_many :students, through: course_participations
has_many :course_participations
end
And my Student model
class Student < ActiveRecord::Base
has_many :courses, through: course_participations
end
The join model
class CourseParticipation < ActiveRecord::Base
belongs_to :student
belongs_to :course
end
In the UsersController:
def create
#course = Course.find(params[:course_id])
#student = Student.find_or_create_by(user_params)
if #student
#course.participate(#student)
end
end
In the Course model:
def full?
self.students.count >= self.max_students
end
def participate(student)
if !self.full?
course_booking = CourseParticipation.new(course_id: self.id, student_id: student.id)
course_booking.save
else
self.errors.add(:course_full, "course is full")
end
end
Goal:
Best place to validate that course is not full and create an instance of CourseParticipation
Try this:
class CourseParticipation < ActiveRecord::Base
belongs_to :student
belongs_to :course
before_create :check_class_size
private
def check_class_size
!self.course.full?
end
end

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.

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?

How to use delegate with try in Rails

I have a relation within my application where one Client has Address which belongs to one Province.
Now I want to put the province name near the client data, so I created a method in client.rb:
def province_name
try(:address).try(:province).try(:name)
end
The problem is that not every client has joined address. This method works but it looks ugly. Is it possible to write in a better way, maybe using delegate method?
We've achieved something similar before:
#app/models/client.rb
class Client < ActiveRecord::Base
has_one :address
end
#app/models/address.rb
class Address < ActiveRecord::Base
belongs_to :province
delegate :name, to: :province
end
#app/models/province.rb
class Province < ActiveRecord::Base
has_many :addresses
def name
self[:name] ||= province
end
def province
self[:province] ||= address
end
end
You may wish to refactor the above code - but it works perfectly for us:
#app/models/user.rb
class User < ActiveRecord::Base
has_one :profile, inverse_of :user
delegate :name, to: :profile
end
#app/models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user, inverse_of :profile
def name
self[:name] ||= user.email
end
end

Model association that spans across parent app and engine

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

Resources