I want to add a has_many through association to a activerecord model class for each symbol in an array. for example
PeopleOrganisation::ROLES.each do |role|
has_many role.to_s.pluralize.to_sym, :through => :people_organisations, :source => :person,
:conditions => "people_organisations.role = '#{role.to_s}'" do
def << (object)
PeopleOrganisation.send(:with_scope, :create => {:role => **role**}) { self.concat object }
end
end
end
everything works fine except for the reference to the role variable inside the method def. This is because the method def is not a closure. Is there a way of achieving what I want?
Try this:
PeopleOrganisation::ROLES.each do |role|
has_many(role.to_s.pluralize.to_sym,
:through => :people_organisations, :source => :person,
:conditions => ["people_organisations.role = ?", role]
) do
define_method("<<") do |object|
PeopleOrganisation.send(:with_scope, :create => {:role => role}) {
self.concat object
}
end
end
end
Instead of defining method using def you can try define_method method:
PeopleOrganisation::ROLES.each do |role|
has_many role.to_s.pluralize.to_sym, :through => :people_organisations, :source => :person,
:conditions => "people_organisations.role = '#{role.to_s}'" do
define_method(:<<) do |object|
PeopleOrganisation.send(:with_scope, :create => {:role => role}) { self.concat object }
end
end
end
Related
Beginner programmer, probably getting over my head with multiple models, but your help would be greatly appreciated. Subjects have many pages, pages have many sections, sections have section edits. Been searching for hours for solution, just haven't found anything that works. Why am I getting this error? I've clearly overlooked something... Thanks!
Error:
NoMethodError in SectionsController#create
NoMethodError (undefined method `subject_id' for #<Section:0x007fe2efe24a80>
Did you mean? object_id):
app/controllers/sections_controller.rb:24:in `create'
sections_controller.rb
class SectionsController < ApplicationController
layout 'admin'
before_action :confirm_logged_in
before_action :find_page
before_action :find_pages, :only => [:new, :create, :edit, :update]
before_action :set_section_count, :only => [:new, :create, :edit, :update]
...
def create
#section = Section.new(section_params)
#section.page = #page
if #section.save
flash[:notice] = "Section created successfully."
redirect_to(sections_path(:page_id => #page.id))
else
render('new')
end
end
...
private
def section_params
params.require(:section).permit(:name, :position, :visible, :content_type, :content)
end
def find_page
#page = Page.find(params[:page_id])
end
def set_section_count
#section_count = #page.sections.count
if params[:action] == 'new' || params[:action] == 'create'
#section_count += 1
end
end
with error originating from line in 'create':
if #section.save
routes.rb
Rails.application.routes.draw do
root :to => 'public#index'
get 'show/:permalink', :to => 'public#show', :as => 'public_show'
get 'admin', :to => 'access#menu'
get 'access/menu'
get 'access/login'
post 'access/attempt_login'
get 'access/logout'
resources :admin_users, :except => [:show] do
member do
get :delete
end
end
resources :subjects do
member do
get :delete
end
end
resources :pages do
member do
get :delete
end
end
resources :sections do
member do
get :delete
end
end
section.rb
class Section < ApplicationRecord
acts_as_list :scope => :subject
belongs_to :page
has_many :section_edits
has_many :admin_users, :through => :section_edits
scope :visible, lambda { where(:visible => true) }
scope :invisible, lambda { where(:visible => false) }
scope :sorted, lambda { order("position ASC") }
scope :newest_first, lambda { order("created_at DESC") }
CONTENT_TYPES = ['text', 'HTML']
validates_presence_of :name
validates_length_of :name, :maximum => 255
validates_inclusion_of :content_type, :in => CONTENT_TYPES,
:message => "must be one of: #{CONTENT_TYPES.join(', ')}"
validates_presence_of :content
end
page.rb
class Page < ApplicationRecord
acts_as_list :scope => :subject
belongs_to :subject, { :optional => false }
has_many :sections
has_and_belongs_to_many :admin_users
scope :visible, lambda { where(:visible => true) }
scope :invisible, lambda { where(:visible => false) }
scope :sorted, lambda { order("position ASC") }
scope :newest_first, lambda { order("created_at DESC") }
validates_presence_of :name
validates_length_of :name, :maximum => 255
validates_presence_of :permalink
validates_length_of :permalink, :within => 3..255
validates_uniqueness_of :permalink
end
subject.rb
class Subject < ApplicationRecord
acts_as_list
has_many :pages
scope :visible, lambda { where(:visible => true) }
scope :invisible, lambda { where(:visible => false) }
scope :sorted, lambda { order("position ASC") }
scope :newest_first, lambda { order("created_at DESC") }
scope :search, lambda {|query| where(["name LIKE ?", "%#{query}%"]) }
validates_presence_of :name
validates_length_of :name, :maximum => 255
end
Rest of my code
https://github.com/danwernstrom13/simple_cms
Let me know if I can provide any more context or code... Your assistance greatly appreciated!
You should remove this line from Section model:
acts_as_list :scope => :subject
because Section does not belong to Subject.
This would be correct:
acts_as_list :scope => :page
I have a polymorphic association that looks like this:
class Event < ActiveRecord::Base
belongs_to :eventable, :polymorphic => true
end
With a bunch of types:
class Nap < ActiveRecord::Base
include Eventable
end
class Meal < ActiveRecord::Base
include Eventable
end
module Eventable
def self.included(base)
base.class_eval do
has_one :event, :as => :eventable, :dependent => :destroy
accepts_nested_attributes_for :event, :allow_destroy => true
scope :happened_at, -> (date) {
where("events.happened_at >= ? AND events.happened_at <= ?",
date.beginning_of_day, date.end_of_day).order("events.happened_at ASC")
}
base.extend(ClassMethods)
end
end
module ClassMethods
define_method(:today) do
self.happened_at(Date.today)
end
end
end
And so on.
Here's the other end of the relationship:
class Person < ActiveRecord::Base
has_many :events
has_many :meals, {
:through => :events,
:source => :eventable,
:source_type => "Meal"
}
has_many :naps, {
:through => :events,
:source => :eventable,
:source_type => "Nap"
}
has_many :moods, {
:through => :events,
:source => :eventable,
:source_type => "Mood"
}
has_many :notes, {
:through => :events,
:source => :eventable,
:source_type => "Note"
}
...
end
I want to grab all the events of all types that belong to a person for display in a single view. Here's what I'm doing:
def show
#events = Event.by_person(#person).happened_at(date)
#meals, #naps, #moods, #notes = [], [], [], [], []
#events.each do |e|
#meals << e.eventable if e.eventable_type == 'Meal'
#naps << e.eventable if e.eventable_type == 'Nap'
#moods << e.eventable if e.eventable_type == 'Mood'
#notes << e.eventable if e.eventable_type == 'Note'
end
end
I need to filter by type because the view is going to be displaying type-specific attributes in each section of the view.
Question: Should this logic of filtering out the collection of events by type into their own type-specific arrays exist in the controller? Or elsewhere? Perhaps the model?
I was reluctant to just pass #events to the view and have the type test happen in the view itself. That seemed wrong.
You can use the #events query to create a subquery without having to iterate (I'm assuming you have the inverse has_many :events, as: :eventable in each of your other models):
#events = Event.by_person(#person).happened_at(date)
#meals = Meal.joins(:event).where events: { id: #events }
#naps = Nap.joins(:event).where events: { id: #events }
# etc.
I currently have this scope:
class User
has_many :inbox_messages, :through => :message_users do
def unread
published.where(:message_users => { :sender => false, :trash => false , :deleted => false}).where(MessageUser.arel_table[:read_at].not_eq(nil))
end
end
end
And it's working. But I was wondering if there is way to merge the second where into the first one.
If I understand your code correctly, you should be able to do that with
published.where(:message_users => { :sender => false, :trash => false , :deleted => false, :read_at => nil })
Using rails 3 and acts_as_paranoid. I want to make sure that every task has a comment.
How can I bypass this one validation (check_if_notes) if mark_completed_and_msg is called?
EDIT - Is this the right way?
task.rb
has_one :comment, :as => :commentable
attr_accessor :force_task
# original was - before_update, :check_if_notes
validate :check_if_notes, :on => :update, :unless => proc { |a| a.force_task }
def mark_completed_and_msg(user_id, msg)
Comment.create!(:commentable_type => self.class, :commentable_id => self.id, :content => msg )
self.update_attributes(:completed_by => user_id, :deleted_at => Time.now, :force_task => true)
end
def check_if_notes
if self.comment.blank? || self.comment.content.blank? || self.comment.content.scan(/[\w-]+/).size <= 2
saved = false
self.comment.errors[:content] << "You must have a comment and more than 3 words."
raise ActiveRecord::Rollback
end
end
I think you meant using == in your Proc.
:unless => proc { |a| a[:force_task] == true }
You can also use
:unless => proc { |a| a.force_task? }
My setup: Rails 2.3.10, Ruby 1.8.7
I have a rather complicated set of relationships between several models.
class A
has_many :classB
has_many :classD
end
class B
belongs_to :classA
has_many :classC
end
class C
belongs_to :classB
belongs_to :classE
end
class D
belongs_to :classA
belongs_to :classE
end
class E
has_many :classD
has_many :classC
end
I'm having an issue with the JSON syntax to get all the related information starting with classA. Here's what I have working so far.
classA.to_json(:include => {:classB => {:include => [:classC, :classE]}})
I can't get the syntax working to also include classD and related classE records.
UPDATE Actually something like this might work except that I can't mix hashes and arrays
classA.to_json(:include => [ :classB => { :include => { :classC => { :include => :classE } } },
:classD, :classE ])
Note that I didn't use singular/plural in my example code above but in my real code, I am. Any insights will be much appreciated.
Thanks,
Bob
This should work:
classA.to_json(:include => {
:classB => {:include => {:classC => {:include => :classE}}},
:classD => {},
:classE => {},
})
Try this, you should only need one :include =>:
classA.to_json(:include => {:classB => [:classC, { :classE => :classD }] })
I don't know if you want classE included through both class C and classD but this should work:
classA.to_json(:include => { :classB => { :include => { :classC => { :include => :classE } } },
:classD => { :include => :classE } })
EDIT:
class A
has_many :bs
has_many :ds
end
class B
belongs_to :a
has_many :cs
end
class C
belongs_to :b
belongs_to :e
end
class D
belongs_to :a
belongs_to :e
end
class E
has_many :ds
has_many :cs
end
#class_a = A.first
#class_a.to_json(:include => { :bs => { :include => { :cs => { :include => :e } } },
:ds => { :include => :e } })