Rails 3 nested attributes - association as choice - ruby

I am building a questionnaire for my website and I have models set up like so:
Question - has_many :answers
Answer - belongs_to :question
Response - belongs_to :answer, :user_application
UserApplication - has_many :responses
Answers are set answers, they could be Yes, No or a sentence, Responses are a nested resource of UserApplication so when I am hitting the new action in the UserApplicationsController I build responses, based on the number of questions, so I can use f.fields_for in the new form. When it renders I want the nested response to have radio buttons which have the id for the answer. So when the form is submitted the nested response receives the id for the selected answer. I just can not figure out the best way to do this. At the moment I am doing it like this:
UserApplicationController
def new
#user_application = UserApplication.new
#questions = Question.all
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user_application }
end
end
New Form
= form_for #user_application do |f|
- #questions.each_with_index do |question, idx|
= question.content
.control-group
.controls
= select_tag "user_application[responses_attributes][#{idx}][answer_id]", options_for_select(question.answers.map {|u| [u.content,u.id]})
This feels a little hacky because when the user edits the application the select boxes for the previous answers are not selected. I have tried to explain my problem as best as possible. If anyone can understand what I am trying to do then any help would be appreciated! If you need to know anything ask!
Thanks
Tony

Related

Do calculation using radio_button variable to nested form controller - Rails 3

I have a edit form which I have a radio_button that I would like to pass to a controller action and then use it to do a calculation. In the view I have:
<div class="field">
<%= radio_button_tag(:rating_select, "Up") %>
<%= label_tag(:rating_select, "Good.") %>
<%= radio_button_tag(:rating_select, "Down")%>
<%= label_tag(:rating_select, "Bad.")%>
</div>
In the controller I have:
def rating
#post = Post.find(params[:id])
.....
end
def update
#post = Post.find(params[:id])
##rating_select = params[:rating_select]
if #post.rating_select == "Up"
#post.score += 5
elsif #post.rating_select == "Down"
#post.score -= 5
end
......
end
Currently it is ignoring the if statement so the parameter isn't getting set properly. Ideally I would like to just use a temp variable from the view to use in the if statement to decide if I need to add or subtract in the update. But I also have a rating_select field in post if I need to use it also. Thanks.
UPDATE:
Thanks. That makes sense, I changed it to below but it still isn't incrementing or decrementing the score based on the radio box. So it seems it isn't getting the rating_select?:
def update
#post = Post.find(params[:id])
if params[:rating_select]=="Up"
#post.score += 5
elsif params[:rating_select]=="Down"
#post.score -= 5
end
respond_to do |format|
....
UPDATE2:
Finally figured it out, used another model Ratings to store association. I used the before_save in the Post model and it allowed me to do the calculation and save. What a headache.
before_save :set_rating
def set_rating
if self.rating.rating_select=="Up"
rating.score += 5
elsif self.rating.rating_select=="Down"
rating.score -= 5
end
end
Well, first off, in the code you're showing the post loaded in your update action is not receiving the params from your view.
Your code for an update action should typically look like this:
def update
#post = Post.find(params[:id])
if #post.update_attributes(params[:post])
... do stuff ...
else
render :edit, :alert => 'Unable to update post.'
end
end
Second, since you're using form_tag helper and not form_for, then you're not getting the params all setup for your model (ie. nested under params[:post]). So, in this case, your rating_select option is just a value by itself, which you can test for like this:
if params[:rating_select]=="Up"
...
else
...
end
The big thing to understand from your code is #post doesn't know anything about params[:rating_select], even if you used #post.update_attributes(params[:post]), because radio_button_tag as you have it set up is not building a hash of post attributes, it's just a standalone field.
I hope that makes sense, if you don't understand please leave comments and I'll try to explain more.

Troublesome Wice::WiceGridArgumentError

I am working on a Rails 3.1.1 app that is using WICE_GRID and I am stuck on this error.
I want to show a grid of Roles on the User show page. I am setting up the data in the controller like this.
User and Role are related by has_many through user_role.
def show
#user = User.find(params[:id])
#roles = initialize_grid(#user.roles)
end
When I run the site I get this error
Wice::WiceGridArgumentError in UsersController#show
WiceGrid: ActiveRecord model class (second argument) must be a Class derived from ActiveRecord::Base
The error is pointing to #roles = init.... line. initialize_grid does take a record arguent but that is a hash of options, not an activerecord model collection.
When I run the code in the console I see that #user.roles is
[#<Role id: 1, title: "Role1>, #<Role id: 2, title: "Role2">]
Looks like an ActiveRecord collection to me.
Any help gratefully accepted!
initialize_grid takes a class. You're passing in an array of objects. It appears you want to display a user's roles in the grid. You want something like this:
def show
#user = User.find(params[:id])
#roles = initialize_grid(Role, :conditions => ['user_id = ?', #user.id])
end
However, I'm guessing your roles table doesn't have user_id in it. You likely have a mapping table called user_roles. In which case, you will want to refactor the code above. Try just running this code instead to make sure you can view Roles in a grid (unscoped).
def show
#user = User.find(params[:id])
#roles = initialize_grid(Role)
end

Using Padrino form helpers and formbuilder - getting started

I've jumped into learning Ruby by going straight to Padrino with Haml.
Most of the Padrino documentation assumes a high-level of knowledge of Ruby/Sinatra etc...
I am looking for samples that I can browse to see how things work. One specific scenario is doing a simple form. On my main (index) page I want a "sign up" edit box with button.
#app.rb
...
get :index, :map => "/" do
#user = "test"
haml: index
end
get :signup, :map => "/signup" do
render :haml, "%p email:" + params[:email]
end
...
In my view:
#index.haml
...
#signup
-form_for #user, '/signup', :id => 'signup' do |f|
= f.text_field_block :email
= f.submit_block "Sign up!", :class => 'button'
...
This does not work. The render in (/signup) never does anything.
Note, I know that I need to define my model etc...; but I'm building to to that in my learning.
Instead of just telling me what I'm doing wrong here, what I'd really like is a fairly complete Padrino sample app that uses forms (the blog sample only covers a small part of Padrino's surface area).
Where can I find tons of great Padrino samples? :-)
EDIT
The answer below was helpful in pointing me at more samples. But I'm still not finding any joy with what's wrong with my code above.
I've changed this slightly in my hacking and I'm still not getting the :email param passed correctly:
#index.haml
...
#signup
- form_for :User, url(:signup, :create), :method => 'post' do |f|
= f.text_field_block :email
= f.submit_block "Sign up!"
...
#signup.rb
...
post :create do
#user = User.new(params[:email])
...
end
EDIT Added Model:
#user.rb
class User
include DataMapper::Resource
property :id, Serial
property :name, String
property :email, String
...
end
When this runs, params[:email] is always nil. I've compared this to bunches of other samples and I can't see what the heck I'm doing wrong. Help!
You can browse some example sites here: https://github.com/padrino/padrino-framework/wiki/Projects-using-Padrino
Or you can browse sources of padrinorb.com here: https://github.com/padrino/padrino-web
The best way also is to generate admin: padrino g admin where you should see how forms works.
The tag form perform by default post actions unless you specify :method => :get|:put|:delete so in your controller you must change :get into :post
post :signup, :map => "/signup" do ...
Since you are using form_for :user params are in params[:user] so to get email you need to puts params[:user][:email]

Eager-loading in Rails 3.1 controller action which responds_to json

Here's what I have so far:
class Show < ActiveRecord::Base
belongs_to :event
# use default_scope so shows are ordered by date by default
default_scope order("date ASC")
end
class Event < ActiveRecord::Base
has_many :shows, :dependent => :destroy
has_and_belongs_to_many :users
scope :future, lambda { includes(:shows).joins(:shows).where("shows.date > ?", Date.today).group("event_id") }
def start_date
shows.first.date
end
def end_date
shows.last.date
end
def ends_in_future?
end_date > Date.today
end
end
I would like to create a controller action to use with jqGrid. So far using Event.includes(:shows).all.to_a returns all the shows in the JSON string, but I can't get hold of start_date and end_date, which is kinda understandable. Is it possible to have derived/calculated properties rendered in JSON?
I also notice the shows for each event are not in the JSON string. Is there any way I can get all the events, complete with child shows entities, rendered in the JSON string?
Many thanks,
Dany.
EDIT: Partially solved this by using as_json(:include => :shows) in the controller action. This returns the event and all the associated shows for each event. The only thing remaining is to figure out how I can include start_date and end_date in the json string...
EDIT: Here was my original controller action code - it may not be the best code since I'm still feeling my way around Rails:
matches = Event.includes(:shows).all
respond_to do |format|
format.json {render :json => matches.as_json(:include => :shows)}
end
As it turned out I don't have to run the query first - it can just be part of responding to the json request. I should've read the as_json specs a lot closer first! Here's the solution in my controller action:
respond_to do |format|
format.json {render :json => Event.all.as_json(:include => :shows, :methods => [:start_date, :end_date])}
end
That renders everything, including the "derived" method. Unfortunately that seems to generate a lot of queries. This provides the json string I want, but is it the best way? Would love to hear any improved methods.
You can get those columns selected into the Event record like so:
Event.includes(:shows).joins(:shows).select("events.*, MIN(shows.date) as start_date, MAX(shows.date) as end_date").all
You may need to alias those columns by a different name (e.g. 'show_start_date', 'show_end_date') because the Event records will already have start_date and end_date methods that you've defined. You'll have to work around that method name collision.

How to update a join model with a has_many :through association?

I have a question regarding many-to-many relationships, specifically has_many :through associations.
All the tutorials I found just set you up with the models and migrations but leave you hanging when it comes to controllers.
What I want to do is to update the timestamps of the join table when an article that is already present is added again, so that it moves to the top of the "list". How do I do that?
This is what my create action looks like:
def create
#article = Article.find_or_create_by_url(params[:article])
if current_user.articles.find(#article)
# update the timestamps on the join table
# ie moved the old article to the top
flash[:notice] = "Successfully added article (again)."
redirect_to #article
else
#article.users << current_user
if #article.save
flash[:notice] = "Successfully added article."
redirect_to #article
else
render :action => 'new'
end
end
end
Thanks in advance!
Update:
#Ben Lee
Thanks for your answer, as I have a has_many through association my article model looks like this:
has_many :readinglist_items, :dependent => :destroy
has_many :users, :through => :readinglist_items
So I don't know if I can add a :touch => true to has_many as I just want to specific entry in the join table.
The point of the update in the create action is to move the article to the top (instead of adding it again) if a user adds an article it have already added in the past.
If I'm understanding you correctly, this isn't a controller issue, but a model issue. You can specify :touch => true on a belongs_to relationship. This will make it so that whenever the child is updated, the association's update_at is updated well.
So put something like this in your Article model:
belongs_to :whatever, :touch => true
Also, tangentially related: it's not clear from the code exactly what your code is doing, but it seems like maybe you are putting create and update functionality both in in the create method instead of splitting them up appropriately.
I solved it! (readinglist_item is the name of the join table):
def create
#article = Article.find_or_create_by_url(params[:article])
if current_user.articles.find(#article)
#article.readinglist_items.find_by_user_id(current_user.id).touch
flash[:notice] = "Successfully added article (again)."
redirect_to #article
else
#article.users << current_user
if #article.save
flash[:notice] = "Successfully added article."
redirect_to #article
else
render :action => 'new'
end
end
end

Resources