I have this Post model:
class Post < ActiveRecord::Base
attr_accessible :title, :content, :tag_names
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
attr_writer :tag_names
after_save :assign_tags
before_create :init_sort_column
def tag_names
#tag_names || tags.map(&:name).join(" ")
end
private
def assign_tags
self.tags = []
return if #tag_names.blank?
#tag_names.split(" ").each do |name|
tag = Tag.find_or_create_by_name(name)
self.tags << tag unless tags.include?(tag)
end
end
def init_sort_column
self.content_changed_at = self.created_at || Time.now
end
end
and Tag model:
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :posts, :through => :taggings
has_many :subscriptions
has_many :subscribed_users, :source => :user, :through => :subscriptions
def tag_posts_count
"#{self.name} (#{self.posts.count})"
end
end
I would like to turn attr_writer :tag_names into an actual column in the database so I can do this: Post.find_by_tag_names("drinks").
How can I achieve this?
What do you think of turning around the associations?
Tag.find_by_name('drinks').posts
You should archive the same.
Related
I have a model Job, which has_many Applications, and has_many Questions. An Answer belongs to both an Application and a Question.
I'm trying to make a factory method an admin can use to create applications, without users having to write anything.
To do this, I wrote --
def self.make_by_admin(params)
app = Application.new
app.user_id = params[:user_id]
app.job_id = params[:job_id]
app.questions.each do |question|
app.answers.new(question_id: question.id, content: 'N/A')
end
app
end
But, I get the error
#<ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection: Cannot modify association 'Application#questions' because the source reflection class 'Question' is associated to 'Job' via :has_many.>
What's weird though is that I'm not actually modifying questions. All I want to do is to generate blank answers for each question.
How would I go about doing that?
Full Models
class Job < ActiveRecord::Base
default_scope { order('jobs.created_at DESC') }
has_many :bullets, dependent: :destroy, inverse_of: :job
has_many :roles, dependent: :destroy, inverse_of: :job
has_many :questions, dependent: :destroy, inverse_of: :job
has_many :job_city_relations, inverse_of: :job, dependent: :destroy
has_many :cities, through: :job_city_relations
has_many :job_industry_relations, inverse_of: :job, dependent: :destroy
has_many :industries, through: :job_industry_relations
has_many :applications, inverse_of: :job, dependent: :destroy
has_many :users, through: :applications
validates :cities,
:job_title,
:job_summary,
:qualifications,
:industries,
:bullets,
:roles,
:questions,
presence: true
accepts_nested_attributes_for :bullets,
reject_if: :all_blank,
allow_destroy: true
accepts_nested_attributes_for :roles,
reject_if: :all_blank,
allow_destroy: true
accepts_nested_attributes_for :questions,
reject_if: :all_blank,
allow_destroy: true
scope :with_cities, ->(city) do
job = Job
.includes(:industries)
.includes(:cities)
.includes(:questions)
.includes(:bullets)
.includes(:roles)
job = job.where(cities: { id: city }) if city
job
end
scope :with_search, ->(search) do
job = Job.includes(:industries)
.includes(:cities)
.includes(:bullets)
.includes(:roles)
.includes(:questions)
if search
job = job.where('jobs.job_title LIKE ?', "%#{search}%")
end
job
end
scope :with_info, -> do
Job.includes(:industries)
.includes(:cities)
.includes(:bullets)
.includes(:roles)
.includes(:questions)
end
def self.build
job = Job.new
2.times {
job.bullets.build
job.roles.build
}
job.questions.build
job
end
def potentials
good_fits = User.includes(:source, :heat, :applications, common_app: [:cities, :industries])
.where('cities.id IN (?)', self.city_ids)
.where('industries.id IN (?)', self.industry_ids)
.where('users.id NOT IN (?)', self.users.map(&:id))
end
end
class Application < ActiveRecord::Base
STATUS_OPTIONS = ["Application Complete",
"Materials Submitted",
"Pending Interview",
"Second Interview"]
belongs_to :job, counter_cache: true
belongs_to :user, counter_cache: true
has_many :questions, through: :job
has_many :answers, inverse_of: :application, dependent: :destroy
validates :job_id, presence: true
validates :user_id, presence: true
validates :answers, presence: true
accepts_nested_attributes_for :answers,
allow_destroy: true, reject_if: :all_blank
scope :with_dependents, -> do
Application
.includes(:job)
.includes(:questions)
.includes(:answers)
end
scope :for_job, ->(job_id) do
Application
.includes(user: [:source, :heat, common_app: [:cities, :industries]])
.includes(questions: :answer)
.where('applications.job_id = ?', job_id)
end
def self.build(job, appl = Application.new)
job.questions.each do |question|
appl.answers.build(question_id: question.id)
end
appl
end
def self.make_by_admin(params)
app = Application.new
app.user_id = params[:user_id]
app.job_id = params[:job_id]
app.questions.each do |question|
app.answers.new(question_id: question.id, content: 'N/A')
end
fail
app
end
end
class Question < ActiveRecord::Base
belongs_to :job
has_one :answer
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :application
end
I ended up writing
def self.make_by_admin(params)
app = Application.new
app.user_id = params[:user_id]
app.job_id = params[:job_id]
app.questions.pluck(:id).each do |id|
app.answers.new(question_id: id, content: 'N/A')
end
app
end
I have a Post model:
class Post < ActiveRecord::Base
attr_accessible :title, :content, :tag_names
has_many :votes, :as => :votable, :dependent => :destroy
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
attr_writer :tag_names
after_save :assign_tags
def tag_names
#tag_names || tags.map(&:name).join(" ")
end
private
def assign_tags
self.tags = []
return if #tag_names.blank?
#tag_names.split(" ").each do |name|
tag = Tag.find_or_create_by_name(name)
self.tags << tag unless tags.include?(tag)
end
end
end
a Tag model:
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :posts, :through => :taggings
has_many :subscriptions
has_many :subscribed_users, :source => :user, :through => :subscriptions
def tag_posts_count
"#{self.name} (#{self.posts.count})"
end
end
and a Vote model:
class Vote < ActiveRecord::Base
belongs_to :votable, :polymorphic => true
belongs_to :user
before_create :update_total
protected
def update_total
total_average = self.votable.total_votes
self.votable.update_attribute(:total_votes, total_average + self.polarity)
end
end
As you can see in this last model, it updates the :total_votes attribute of a post each time a new instance of Vote is created.
For some reason, this action is triggering the after_save :assign_tags action in the post model. So each time I create a vote for a post this part is called:
def assign_tags
self.tags = [] // I added this so that the previous array of tags are removed before the new ones are added.
and all the tags are removed. I want the assign_tags to only be triggered when a post is being edited. Any suggestions to fix this?
EDIT:
votes_controller:
class VotesController < ApplicationController
def vote_up
#votable = params[:votable_type].constantize.find(params[:id])
if #votable.votes.exists?(:user_id => current_user.id)
#notice = 'You already voted'
else
#vote = #votable.votes.create(:user_id => current_user.id, :polarity => 1)
#votable.reload
end
respond_to do |format|
format.js
end
end
update post without callback inside vote model -
def update_total
self.votable.total_votes += self.polarity
self.votable.send(:update_without_callbacks)
end
OR
you can use `update_column(name, value) it skips validations & callbacks -
def update_total
self.votable.update_column(:total_votes, votable.total_votes + polarity)
end
Here is revised vote model
class Vote < ActiveRecord::Base
belongs_to :votable, :polymorphic => true
belongs_to :user
after_create :update_total
protected
def update_total
if votable && votable.is_a?(Post)
self.votable.update_column(:total_votes, votable.total_votes + self.polarity)
end
end
end
Use after_commit as the callback
I have a Post model:
class Post < ActiveRecord::Base
belongs_to :user
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
attr_writer :tag_names
after_save :assign_tags
before_create :init_sort_column
def tag_names
#tag_names || tags.map(&:name).join(" ")
end
private
def assign_tags
self.tags = []
return if #tag_names.blank?
#tag_names.split(" ").each do |name|
tag = Tag.find_or_create_by_name(name)
self.tags << tag unless tags.include?(tag)
end
end
end
a Tag model:
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :posts, :through => :taggings
has_many :subscriptions
#has_many :subscribed_users, :source => :user, :through => :subscriptions
end
and a User model:
class User < ActiveRecord::Base
(Code related to Devise)
has_many :posts, :dependent => :destroy
has_many :subscriptions
has_many :subscribed_tags, :source => :tag, :through => :subscriptions
has_many :subscribed_posts, :source => :posts, :through => :subscribed_tags
attr_writer :subscribed_tag_names
after_save :assign_subscribed_tags
def subscribed_tag_names
#subscribed_tag_names || subscribed_tags.map(&:name).join(' ')
end
private
def assign_subscribed_tags
#self.subscribed_tags = []
return if #subscribed_tag_names.blank?
#subscribed_tag_names.split(" ").each do |name|
subscribed_tag = Tag.find_or_create_by_name(name)
self.subscribed_tags << subscribed_tag unless subscribed_tags.include?(subscribed_tag)
end
end
end
In the index page users only see posts with tags they have subscribed to:
posts_controller.rb:
#posts = current_user.subscribed_posts.paginate(:page => params[:page],
:per_page => 5,
:order => params[:order_by])
Now say there is a post with the tags food and drinks, and the user has subscribed to these two tags. He will see the post twice; it seems like it is appearing once as a post tagged as food and then as a post tagged as drinks.
Is there a way of preventing posts like this from appearing twice?
Add :uniq => true as a parameter to the has_many in the User model:
has_many :subscribed_posts, :source => :posts, :through => :subscribed_tags, :uniq => true
The docs at http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many-label-Options says:
:uniq
If true, duplicates will be omitted from the collection. Useful
in conjunction with :through.
I'm trying to make conditional validation based on Project status in Payment model. For example status can be "Talks" or "Active". What's the best way to do it considering the structure below?
class Project < ActiveRecord::Base
has_many :costs, :dependent => :destroy
has_many :payments, :through => :costs, :dependent => :destroy
accepts_nested_attributes_for :costs, :allow_destroy => true
end
class Cost < ActiveRecord::Base
has_many :payments, :dependent => :destroy
accepts_nested_attributes_for :payments, :allow_destroy => true
belongs_to :project
end
class Payment < ActiveRecord::Base
belongs_to :cost
validates_presence_of :value1, :if => :new?
validates_presence_of :value1, :if => :talks?
validates_presence_of :value2, :if => :active?
def new?
# if controller action is new
end
def talks?
# if project status is "Talks" (edit action)
end
def active?
# if project status is "Active" (edit action)
end
end
class Payment < ActiveRecord::Base
belongs_to :cost
has_one :project, :through => :cost
validates_presence_of :value1, :if => :new?
validates_presence_of :value1, :if => :talks?
validates_presence_of :value2, :if => :active?
def new?
self.new_record?
end
def talks?
project.status == "talks"
end
def active?
project.status == "active"
end
end
I have developed an application and I seem to be having some problems with my associations. I have the following:
class User < ActiveRecord::Base
acts_as_authentic
has_many :questions, :dependent => :destroy
has_many :sites , :dependent => :destroy
end
Questions
class Question < ActiveRecord::Base
has_many :sites, :dependent => :destroy
has_many :notes, :through => :sites
belongs_to :user
end
Sites (think of this as answers to questions)
class Site < ActiveRecord::Base
acts_as_voteable :vote_counter => true
belongs_to :question
belongs_to :user
has_many :notes, :dependent => :destroy
has_many :likes, :dependent => :destroy
has_attached_file :photo, :styles => { :small => "250x250>" }
validates_presence_of :name, :description
end
When a Site (answer) is created I am successfully passing the question_id to the Sites table but I can't figure out how to also pass the user_id. Here is my SitesController#create
def create
#question = Question.find(params[:question_id])
#site = #question.sites.create!(params[:site])
respond_to do |format|
format.html { redirect_to(#question) }
format.js
end
end
I'd think this would do the job
#question = current_user.questions.find params[:question_id]
if not, then just assign mannualy.
#site = #question.sites.build(params[:site])
#site.user = current_user
#site.save