Rails 3: How to associate a new Topic with a Forum - ruby-on-rails-3

I am trying to write a forum with Ruby on Rails.
On model side, I finished association between Topic and Forum
# forum.rb
class Forum < ActiveRecord::Base
has_many :topics
attr_accessible :name, :description
end
# topic.rb
class Topic < ActiveRecord::Base
has_many :posts
belongs_to :forum
end
Controller for Forum
# forums_controller.rb
class ForumsController < ApplicationController
def new
#forum = Forum.new
end
def create
#forum = Forum.new(params[:forum])
if #forum.save
flash[:success] = "Success!"
redirect_to #forum
else
render 'new'
end
end
def index
#forums = Forum.all
end
def show
#forum = Forum.find(params[:id])
end
end
Controller for Topic
class TopicsController < ApplicationController
def new
#topic = current_forum???.topics.build
end
def create
#topic = Topic.new(params[:topic])
if #topic.save
flash[:success] = "Success!"
redirect_to #topic
else
render 'new'
end
end
def index
#topics = Topic.all
end
def show
#topic = Topic.find(params[:id])
end
end
How do I change new and create for topics_controller to make sure the topic is created for current forum rather than some other one?
So for example, if I create a new topic from a forum with id=1, how do I make sure that forum_id=1 for the new topic created?

Using nested resources
resources :forums do
resources :topics
end
you will have a path like
/forums/:forum_id/topics/new
then in your TopicsController
def new
#forum = Forum.find(params[:forum_id])
#topic = #forum.topics.build
end

class TopicsController < ApplicationController
def new
#forum = Forum.find(params[:id])
#topic = #forum.topics.build
end

Related

Rails: Setting Model Attributes to Attributes from Another Model

I am a little unsure of how to ask this so I apologize for the clunky explanation.
I have three models, User, Waterusage and Goals
class Goal < ApplicationRecord
belongs_to :user
end
class Waterusage < ApplicationRecord
belongs_to :user
end
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable,
has_one :waterusage, :dependent => :destroy
has_one :goals, :dependent => :destroy
end
Waterusage is filled out first by users and then goals. Goals is the exactly same schema as waterusage, but uses a portion of the waterusage form and copies the remaining attributes from waterusage.
class Goal < ApplicationRecord
belongs_to :user
# before_validation :get_from_waterusage
before_validation :calculate_totals
def get_from_waterusage
self.household_size = #waterusage.household_size
self.swimming_pool = #waterusage.swimming_pool
self.bathroom_sink_flow_rate = #waterusage.bathroom_sink_flow_rate
self.low_flow_toilet = #waterusage.low_flow_toilet
self.kitchen_sink_usage = #waterusage.kitchen_sink_usage
self.kitchen_sink_flow_rate = #waterusage.kitchen_sink_flow_rate
self.dishwasher_rate = #waterusage.dishwasher_rate
self.dishwasher_multiplier = #waterusage.dishwasher_multiplier
self.laundry_rate = #waterusage.laundry_rate
self.laundry_multiplier = #waterusage.laundry_multiplier
self.lawn_size = #waterusage.lawn_size
self.carwash_rate = #waterusage.carwash_rate
self.carwash_multiplier = #waterusage.carwash_multiplier
self.miles = #waterusage.miles
self.statewater = #waterusage.statewater
self.percent_statewater = #waterusage.percent_statewater
self.pet_cost = #waterusage.pet_cost
end
...
end
Here is the GoalsController
class GoalsController < ApplicationController
before_action :authenticate_user!
def new
#goal = goal.new
end
def create
##user = User.find(params[:user_id])
#goal = current_user.create_goal(goal_params)
redirect_to goal_result_path
end
def destroy
#user = User.find(params[:user_id])
#goal = #user.goal.find(params[:id])
#goal.destroy
redirect_to user_path(current_user)
end
def show
#goal = goal.find(params[:id])
end
def results
if current_user.goal.get_individual_total > 6000
#temp = 6000
else
#temp = current_user.goal.get_individual_total
end
#goal = current_user.goal
end
private
def goal_params
params.require(:goal).permit(:household_size, :average_shower,
:shower_flow_rate, :bath_rate, :bath_multiplier,
:bathroom_sink_usage,
:bathroom_sink_flow_rate, :mellow, :low_flow_toilet,
:kitchen_sink_usage,
:kitchen_sink_flow_rate, :dishwasher_rate,
:dishwasher_multiplier,
:dishwasher_method, :laundry_rate, :laundry_multiplier,
:laundry_method,
:greywater, :lawn_rate, :lawn_multiplier, :lawn_size,
:xeriscaping,
:swimming_pool, :swimming_months, :carwash_rate,
:carwash_multiplier,
:carwash_method, :miles, :statewater, :percent_statewater,
:shopping,
:paper_recycling, :plastic_recycling, :can_recycling,
:textile_recycling,
:diet, :pet_cost, :individual_total, :household_total,
:home_usage, :outdoor_usage,
:individualDifference, :householdDifference, :vehicle_usage,
:power_usage, :indirect_source_usage,
:individualDifference, :householdDifference)
end
end
I currently have the following error:
NameError in GoalsController#create
undefined local variable or method `current_user' for #
<Goal:0x007fbedde9a590>
It seems to be in the way I am retrieving the info from the waterusage model with
self.household_size = #waterusage.household_size
It there a join I could use?
The waterusage model works BTW.
Thanks
Don't know if it's the best way to do that, but I would use something like this:
In your goals model, you can check if its user have a waterusage already. If it has, you fill the values from that water usage
You can do it using after_initialize callback. In your goal model, would be something like
class Goal < ApplicationRecord
belongs_to :user
after_initialize :set_default_values
def set_default_values
waterusage = self.user.try(:waterusage)
if waterusage
self.attribute1 = waterusage.attribute1
self.attribute2 = waterusage.attribute2
self.attribute3 = waterusage.attribute3
#and it goes on...
end
end
end
so, like this when you do a Goal.new, it will check for a waterusage for that user and initialize those values on your goal. So you don't have to change anything on your controller and even if you do it on console, it will work. Guess it's a better practice to do that using models callbacks. Don't know if it solves your problem, but give it a try. Good luck!
Your error message is:
NameError in GoalsController#create
undefined local variable or methodcurrent_user' for #
Goal:0x007fbedde9a590`
The current_user object is automagically defined inside your controller by the Devise gem you're using. It will not be defined inside your models.
One of your comments includes the following snippet you say you're using from within your Goal model: current_user.waterusage.household_size. That is what your error message is referring to. (Note that this snippet from one of your comments disagrees with the code in your original post. This makes it harder to be certain about what is going wrong here.)

Load associations to one level while conditionally sideloading associations in Active model serializers

AMS version 0.8.3,
I created a base_serializer.rb like this and extended the same.
class BaseSerializer < ActiveModel::Serializer
def include_associations!
if #options[:embed]
embed = #options[:embed].split(',').map{|item| item.strip.to_sym}
embed.each do |assoc|
include! assoc if _associations.keys.include?(assoc)
end
end
end
end
class EventSerializer < BaseSerializer
attributes :id, :name
has_many :organizers, serializer: OrganizerSerializer
has_many :participants, serializer: ParticipantSerializer
end
class OrganizerSerializer < BaseSerializer
attributes :id, :name
has_many :related, serializer: RelatedSerializer
end
class ParticipantSerializer < BaseSerializer
attributes :id, :name
has_many :related, serializer: RelatedSerializer
end
class RelatedSerializer < BaseSerializer
attributes :id, :name
has_many :something, serializer: SomethingSerializer
end
and the index method in EventsController is written as
# GET /events?embed=organizers,participants
def index
#events = Event.all
render json: #events, embed: params[:embed]
end
With this I can get the :id and :name of events, organizers and participants. But, I want the attributes of related association as well. I don't need details of something serializer. I want to go till this level for each association. How can I achieve that?
I ended up doing this to achieve the same.
class BaseSerializer < ActiveModel::Serializer
def include_associations!
#options[:embed_level] ||= 2
return unless #options.key?(:embed) && #options[:embed_level] != 0
embed = #options[:embed].split(',').map{|item| item.strip.to_sym}
embed.each do |assoc|
next unless _associations.key?(assoc)
assoc_serializer = serializer_for(assoc)
embed = #options[:embed]
embed_level = #options[:embed_level]
#options[:embed_level] = #options[:embed_level] - 1
#options[:embed] = assoc_serializer._associations.keys.join(",")
include! assoc
#options[:embed_level] = embed_level
end
end
def serializer_for(assoc)
serializer = _associations[assoc].options[:serializer]
return serializer if serializer
assoc.to_s.classify.concat("Serializer").constantize
end
end
Ref: Github Issue Link
Special Thanks to Yohan Robert!!!

Undefined method permit

I am practicing the posts in rails guide. In comments controller I write like this but it comes to error
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:commenter, :body))
redirect_to post_path(#post)
end
end
I'd recommend to follow up this guide
This should work:
class CommentsController < ApplicationController
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.create(comment_params)
redirect_to article_path(#article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end

Ruby on Rails, what's the correct way to relate two instances?

I'm new to learning Rails 3 and working through a Q&A app tutorial. I'm just wondering why I can't do this (I get an error) in relating a particular answer to a question. It works for the current user...
class AnswersController < ApplicationController
before_filter :auth, only: [:create]
def create
#question = Question.find(params[:question_id])
**#answer = Answer.new(params[:answer])
#answer.question = #question
#answer.user = current_user**
if #answer.save
flash[:success] = 'Your answer has been posted!'
redirect_to #question
else
#question = Question.find(params[:question_id])
render 'questions/show'
end
end
end
The tutorial says that this is the correct way:
class AnswersController < ApplicationController
before_filter :auth, only: [:create]
def create
#question = Question.find(params[:question_id])
**#answer = #question.answers.build(params[:answer])**
#answer.user = current_user
if #answer.save
flash[:success] = 'Your answer has been posted!'
redirect_to #question
else
#question = Question.find(params[:question_id])
render 'questions/show'
end
end
end
Doing the following
#answer = #question.answers.build(params[:answer)
Is the same as doing this
#answer = Answer.new(params[:answer])
#answer.question_id = #question.id
Doing a build adds the relation attributes to the new answer, in this case question_id
As for the error, can you provide the type of error you receive?

How to create nice forms with active_scaffold gem on Rails 3

How to override Active Scaffold form fields for date or time?
(datepicker and calendar_date_select didn't work for me, probably
because I'm using the activescaffold gem)
How to override Active Scaffold form to select from a list of resources in the
database?
Thanks.
I was struggling this question until I figured it out. Here's an example:
class Player < ActiveRecord::Base
belongs_to :game
attr_accessible :name
end
class Game < ActiveRecord::Base
has_many :players
attr_accessible :thedate, :thetime, :winnername
end
class GamesController < ApplicationController
active_scaffold :game do |conf|
# do nothing in this example
end
end
module GamesHelper
# date select
def game_thedate_form_column (record, options)
date_select :record, :thedate, options
end
# time select
def game_thetime_form_column (record, options)
time_select :record, :thetime, options
end
# select from database resources
def game_winnername_form_column (record, options)
select_tag :winnername, options_for_select(get_players_names_arr(record)), options
end
def get_players_names_arr(game)
names = []
game.players.each do |player|
names << player.name
end
names
end
end