Unable to add string to an array - ruby-on-rails

I am trying to make a column in my Posts table which is an array. I want the array to store the current_user's email (or id/name etc...).
However, for some reason I cannot get the current_user's email to append to the array.
The array column is called claimed_users and the purpose of the column is to store the user_email of all users who have "claimed" the post. This way I can implement code to prevent them from claiming more than one post amongst other things.
I currently have this in my View:
<%= link_to "Claim This Post", { controller: :posts, action: :claim, post_id: #post.id, user_id: current_user.id },{ class: 'btn btn-default'} %>
And This for my action in PostsController:
def claim
#post = Post.find(params[:post_id])
#user = User.find(params[:user_id])
#post.increment!(:claim)
#post.claimed_users << current_user.id
if #post.claim < 99
redirect_to #post, notice: 'You have successfully claimed this post. You have 8 hours to post a response'
else
redirect_to #post, notice: 'Sorry, this post already has enough claims'
end
end
I added "serialize: claimed_users, Array" in my Posts model:
class Post < ActiveRecord::Base
belongs_to :user
has_many :responses
has_many :top_responses
has_many :claims
serialize :claimed_users, Array
end
However, I still have no luck. When I look in the database using the Rails console I still get "claimed_users: nil".
Before, I was getting "claimed_users: "--- []\n"" but that changed when I added some code though I'm not exactly sure where as I have tried a lot of things so far and read a lot of SO questions and google results to no avail.
I explored the idea of creating a new Model/Controller for Claims/Claimed_Users but it doesn't make much sense when I think about it.
Any help is greatly appreciated! I'm still fairly new to programming.

Related

getting Id from created record in rails

New web developer here, and I think I may be missing some very fundamental knowledge. Given the code
> def create
> #post = Post.new(post_params)
> if #post.save
> redirect_to #post
> else
> render "new"
> end
end
after saving the post, it redirects to show page, due to this "redirect_to #post", how can I do the same thing with "redirect_to: action => "show", :id => 5" I have to pass the ID now, how to retrieve the ID from #post object?
so only I can pass the Id to redirect page.
can I stop the compiler here, like debugger in js?
To answer your question of "I may be missing some very fundamental knowledge" yes, you might be. An object in Rails like #post is usually a database record. You can access any of it's columns in the DB by using the column name as a method:
#post.id
returns:
5 #or whatever the post id is.
If your post table has a column of "title" you can access it with
#post.title
returns:
"This is an awesome post"
I would highly recommend you view some Ruby and some Rails tutorials. Everything in Ruby is an object. Rails uses a lot of conventions so you can do things without having to write code for it, it's already there for you. When you get into Rails ActiveRecord Relations you'll see that relations expand this to give you related table information as methods. For Example:
Post.rb
...
belongs_to :user
User.rb
...
has_many :posts
Gives you methods like:
#post.user #returns the user object with all of its info
#post.user.username #returns the value of that column for that user
#post.user.posts #returns an array of Post objects that belong to the owner of that post.
Ruby has a pry-byebug gem for debugging. It's a combination REPL (Pry) and core debugger (byebug) that work very powerfully together.
Getting the id of a successfully saved ActiveRecord model is just #post.id, however the rails methods like redirect_to will take the object itself just fine, as #Beartech has mentioned, above. The documentation shows a variety of ways to use it, for convenience:
redirect_to action: "show", id: 5
redirect_to #post
redirect_to "http://www.rubyonrails.org"
redirect_to "/images/screenshot.jpg"
redirect_to posts_url
redirect_to proc { edit_post_url(#post) }

Edit author of an article

I have post which belongs_to users, and would like an option when editing to be able to change the user that post is associated with.
I've made a start, but I am very new to Rails and a bit stuck.
I started in my post_controller.rb.
def update
#post.user = associated_user
if #post.update(post_params)
flash[:notice] = "Post was successfully updated"
redirect_to edit_post_path(#post)
else
render 'edit'
end
end
I've defined the method associated_user in my application_controller.rb (I want to do this for a more than just articles.)
def associated_user
#associated_user ||= User.find(session[:user_id]) if session[:user_id]
end
I understand that this code is wrong - I don't want it to get the logged in user, I want to get it from the field I've set in my form view.
<%= f.text_field :associated_user, class: "form-control", placeholder: "Edit Author" %>
Ideally this form field would be a drop down listing all users with a certain status (a boolean I have already set in the users table.)
I'm not sure how far away I am, but if any one is able to offer some guidance, that would be greatly appreciated!
How are the relationships defined in the models. If your post belongs to users, then use a dropdown control in your form:
f.collection_select :user_id, User.whateverscope, :id, :text_method
and when the form is submitted rails will do the rest.
If you have something different set up, then post your models, your strong_params methods, etc.

Rails has_one build_association deletes record before save

So this has been asked previously, but with no satisfying answers.
Consider two models, User, and Subscription associated as such:
class User < ActiveRecord::Base
has_one :subscription, dependent: :destroy
end
class Subscription < ActiveRecord::Base
belongs_to :user
end
Inside of SubscriptionsController, I have a new action that looks like this
def new
user = User.find(params[:user_id])
#subscription = user.build_subscription
end
Given that a subscription already exists for a user record, I'm faced with the following problem:
user.build_subscription is destructive, meaning that simply visiting the new action actually destroys the association, thereby losing the current subscription record.
Now, I could simply check for the subscription's existence and redirect like this:
def new
user = User.find(params[:user_id])
if user.subscription.present?
redirect_to root_path
else
#subscription = user.build_subscription
end
end
But that doesn't seem all that elegant.
Here's my question
Shouldn't just building a tentative record for an association not be destructive?
Doesn't that violate RESTful routing, since new is accessed with a GET request, which should not modify the record?
Or perhaps I'm doing something wrong. Should I be building the record differently? Maybe via Subscription.new(user_id: user.id)? Doesn't seem to make much sense.
Would much appreciate an explanation as to why this is implemented this way and how you'd go about dealing with this.
Thanks!
It depends on what you want to do
Thoughts
From what you've posted, it seems the RESTful structure is still valid for you. You're calling the new action on the subscriptions controller, which, by definition, means you're making a new subscription (not loading a current subscription)?
You have to remember that Rails is basically just a group of Ruby classes, with instance methods. This means that you don't need to keep entirely to the RESTful structure if it doesn't suit
I think your issue is how you're handling the request / action:
def new
user = User.find(params[:user_id])
#subscription = user.build_subscription
end
#subscription is building a new ActiveRecord object, but doesn't need to be that way. You presumably want to change the subscription (if they have one), or create an association if they don't
Logic
Perhaps you could include some logic in an instance method:
#app/models/user.rb
Class User < ActiveRecord::Base
def build
if subscription
subscription
else
build_subscription
end
end
end
#app/controllers/subscriptions_controller.rb
def new
user = User.find(params[:user_id])
#subscription = user.build
end
This will give you a populated ActiveRecord, either with data from the subscription, or the new ActiveRecord object.
View
In the view, you can then use a select box like this:
#app/views/subscriptions/new.html.erb
<%= form_for #subscription do |f| %>
<%= "User #{params[:user_id]}'s subscription: %>
<%= f.collection_select :subscription_id, Subscription.all,:id , :name %>
<% end %>
They are my thoughts, but I think you want to do something else with your code. If you give me some comments on this answer, we can fix it accordingly!
I also always thought, that a user.build_foobar would only be written to the db, if afterwards a user.save is called. One question: After calling user.build_subscription, is the old subscription still in the database?
What is the output user.persisted? and user.subscription.persisted?, after calling user.build_subscription?
Your method to check if a subscription is present, is IMHO absolutely ok and valid.
I came across this today and agree that deleting something from the db when you call build is a very unexpected outcome (caused us to have bad data). As you suggested, you can work around if very easily by simply doing Subscription.new(user: user). I personally don't think that is much less readable then user.build_subscription.
As of 2018 Richard Peck's solution worked for me:
#app/models/user.rb
Class User < ActiveRecord::Base
def build_a_subscription
if subscription
subscription
else
build_subscription
end
end
end
My issue was that a user controller didn't have a new method, because users came from an api or from a seed file.
So mine looked like:
#app/controllers/subscriptions_controller.rb
def update
#user = User.find(params[:id])
#user.build_a_subscription
if #user.update_attributes(user_params)
redirect_to edit_user_path(#user), notice: 'User was successfully updated.'
else
render :edit
end
end
And I was finally able to have the correct singular version of subscriptions in my fields_for, so :subscription verses :subscriptions
#app/views
<%= f.fields_for :subscription do |sub| %>
<%= render 'subscription', f: sub %>
<% end %>
Before I could only get the fields_for to show in the view if I made subscriptions plural. And then it wouldn't save.
But now, everything works.

Rails 3 : How can I make auto grading system nested_form based online test system?

I'm still making online test program.
This is my model.
Survey
class Survey < ActiveRecord::Base
belongs_to :user
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda {|a| a[:content].blank?}, :allow_destroy => true
Question : there is is_correct column which indicates whether students get the right answer or not.
class Question < ActiveRecord::Base
belongs_to :survey
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
Answer : there is correct column which teacher checks making the survey(test), and there is user_answer column which students mark taking the test.
class Answer < ActviveRecord::Base
belongs_to :question
and I made taking exam interface on show.html.erb in survey views. So if students fill this check box and click the submit button, they can get their result page. but I can't show the result in the result page.
This is my survey controller.
def grading
#survey = Survey.new
#survey.user_id = current_user.id
if #survey.questions.answers.user_answer and #survey.questions.answers.correct
#survey.questions.is_correct = true
end
redirect_to results_surveys_path(#survey)
end
def results
end
The error message I saw is 'undefined method `answers' for []:ActiveRecord::Relation'. I thought that there were problem between question and answer table...
I thought auto grading part is easy, but I was wrong. I have no idea about this and I don't have any reference, except your help.
any idea welcome.
Thanks advanced.
Updated Question
Here is another question.
Now I can access to nested objects.(I think and I hope) but the result page(result.html.erb in survey views) can't show any result : "undefined method `name' for nil:NilClass".
result.html.erb
<h1><%= #survey.name %></h1>
<h3><%= #survey.user.username %></h3>
As I told in previous link, I have another form_tag in show.html.erb in survey views. then redirect to the result page with routes.rb.
resources :surveys do
collection do
get 'results'
end
end
I thought I can show the result page using is_correct column in question tables.
I didn't write anything in the result method in survey controller. Because when I redirect the page I wrote like this. Which means using #survey in result method, doesn't it?
redirect_to results_surveys_path(#survey)
Here is the result of rake routes.
seriousin#classcasts:~/ClassCasts$ rake routes | grep survey
results_surveys GET /surveys/results(.:format) surveys#results
surveys GET /surveys(.:format) surveys#index
POST /surveys(.:format) surveys#create
new_survey GET /surveys/new(.:format) surveys#new
edit_survey GET /surveys/:id/edit(.:format) surveys#edit
survey GET /surveys/:id(.:format) surveys#show
PUT /surveys/:id(.:format) surveys#update
DELETE /surveys/:id(.:format) surveys#destroy
surveys_grading POST /surveys/grading(.:format) surveys#grading
seriousin#classcasts:~/ClassCasts$
I think my basic idea caused all of my problem. Here is my survey controller.
class SurveysController < ApplicationController
def index
#surveys = Survey.all
end
def grading
#survey = Survey.new
#survey.user_id = current_user.id
#survey.questions.each do |question|
question.auto_check
end
redirect_to results_survey_path(#survey)
end
def results
#survey = Survey.where(params[:id])
end
def show
#survey = Survey.find(params[:id])
end
def new
#survey = Survey.new
end
def edit
#survey = Survey.find(params[:id])
end
def create
#survey = Survey.new(params[:survey])
#survey.user_id = current_user.id
end
def update
#survey = Survey.find(params[:id])
end
def destroy
#survey = Survey.find(params[:id])
#survey.destroy
end
end
as you can see, I'm using show page in survey views as another input form with grading method. I can use '#survey = Survey.new' in create method, it makes sense! but as I wrote in grading method, it generates another new survey, I think.
So I need to change that line. can you please help me?
Sending data
OK. when I submit in _form.html.erb in survey views, I can send data like this.
Parameters: {"id"=>"14", "survey"=>{"name"=>"The First Test!", "description"=>"This is the first test!", "questions_attributes"=>{"0"=>{"_destroy"=>"false", "id"=>"41", "content"=>"Question 2", "answers_attributes"=>{"0"=>{"_destroy"=>"false", "id"=>"66", "content"=>"Answer 2 of Question 2", "correct"=>"0"}, "1"=>{"_destroy"=>"false", "id"=>"67", "content"=>"Answer 1 of Question 2", "correct"=>"1"}}}, "1"=>{"_destroy"=>"false", "id"=>"42", "content"=>"Question 1", "answers_attributes"=>{"0"=>{"_destroy"=>"false", "id"=>"68", "content"=>"Answer 2 of Question 1", "correct"=>"0"}, "1"=>{"_destroy"=>"false", "id"=>"69", "content"=>"Answer 1 of Question 1", "correct"=>"1"}}}, "1376575795482"=>{"_destroy"=>"false", "content"=>"Question 3", "answers_attributes"=>{"1376575802879"=>{"_destroy"=>"false", "content"=>"Answer 1 of Question 3", "correct"=>"0"}}}}}, "commit"=>"Update Survey", "utf8"=>"✓", "authenticity_token"=>"/vNuB5Ck3QM5p+5ksL3tlmb+ti5nTA/qS96+vbPQkNw="}
This is OK. but because of form_tag in show.html.erb, show page contains another input form.
<%= form_tag({:controller => "surveys", :action => "grading"}) do %>
after submit again in show.html.erb I want to redirect to the results.html.erb with proper result. but there are errors like this.
Started POST "/surveys/grading" for 110.174.136.30 at Thu Aug 15 23:19:52 +0900 2013
Processing by SurveysController#grading as HTML
Parameters: {"utf8"=>"✓", "#<Answer:0x7fafd8c5f5a0>"=>"1", "#<Answer:0x7fafd95704a0>"=>"0", "#<Answer:0x7fafd9116a58>"=>"1", "authenticity_token"=>"/vNuB5Ck3QM5p+5ksL3tlmb+ti5nTA/qS96+vbPQkNw=", "#<Answer:0x7fafd8d03a38>"=>"0", "commit"=>"Submit", "#<Answer:0x7fafd8cfc580>"=>"1"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 12 LIMIT 1
Completed 404 Not Found in 3ms
ActionController::RoutingError (No route matches {:action=>"results", :id=>#<Survey id: nil, name: nil, description: nil, attempts: nil, user_id: 12, created_at: nil, updated_at: nil>, :controller=>"surveys"}):
app/controllers/surveys_controller.rb:31:in `grading'
Do you think that I need to change whloe answering mechanism?
Try like this, you are trying to call your method on an array. your have to iterate over it and then you can assign any value to it. you can do some re-factoring in your code as well to avoid loops.
in your grading method, do this:
def grading
#survey = Survey.new
#survey.user_id = current_user.id
#survey.questions.each do |question|
question.auto_check
end
redirect_to results_surveys_path(#survey)
end
In your Question model write a method auto_check like this:
def auto_check
answers.each do |answer|
is_correct = true if answer.user_answer and answer.correct
self.save!
end
end
I think it is a better approach.Thanks.
Updated Answer:
Whenever you try to pass an id in your path, that means that is a member function, if you are defining collection in your routes, that means you cannot pass an id in that(As you routes output shows. Have a keen look into it). change your routes like this:
resources :surveys do
member do
  get 'results'
end
end
Now you can access your url like this:
results_survey_path(#survey)
and in your results method :
def results
#survey = Survey.where(params[:id])
end
Hope, now it will work.

How to create and and edit nested objects in rails 3.2?

So I have two models, Reports and Receipts. Each report has many receipts. I used scaffolding to generate all my views and stuff but Im changing things around so that when a user creates a new report or edits one, they can create and edit receipts in the form.
My models are set up:
class Report < ActiveRecord::Base
has_many :receipts, :dependent => :destroy
accepts_nested_attributes_for :receipts, :allow_destroy => true
attr_protected :id
end
class Receipt < ActiveRecord::Base
belongs_to :report
attr_protected :id
validates_presence_of :vendor, :date, :description, :amount, :acctCode
end
I have the form set up to create a new receipt:
<%= form_for #report do |f| %>
....
<%= f.fields_for :receipts, Receipt.new do |receipt| %>
...
<% end %>
<% end %>
But every time I go to save a report, I get a routing error:
No route matches {:action=>"edit", :controller=>"receipts", :report_id=>#<Receipt id: nil, date: nil, vendor: "", description: "", amount: nil, companyCard: false, lobbyingExpense: false, acctCode: "", created_at: nil, updated_at: nil, report_id: 2>}
and my routes are set up as:
resources :reports do
resources :receipts
end
and my controller for receipts has
# GET /receipts/new
def new
#receipt = Receipt.new
respond_to do |format|
format.html # new.html.erb
end
end
# GET /receipts/1/edit
def edit
#receipt = Receipt.find(params[:id])
end
# POST /receipts
def create
#receipt = Receipt.new(params[:receipt])
respond_to do |format|
if #receipt.save
format.html { redirect_to #receipt.Report, notice: 'Receipt was successfully created.' }
else
format.html { render action: "new" }
end
end
end
I havent touched rails in a while so Im not sure what Im doing wrong. But in my older apps (3.1) when I added images to say, blog posts, I didnt even have a controller for images other than to delete them via ajax. The only reason I have a controller here for receipts is because I used scaffolds to generate the views and such.
edit - I should also point out, that if I go to the new receipt view, I get an error on the form tag:
<%= form_for(#receipt) do |receipt| %>
undefined method `receipts_path'
If you are using accepts_nested_attributes_for you don't need an extra controller to manage the records. Of course if you need specific pages like a "show view" for a receipt you need that controller.
To get accepts_nested_attributes_for you need:
A form for your report
use fields_for :receipts in that form
This way you can edit all created receipts for a given report. If you also want to create new receipts you can add a blank receipt with: #report.receipts.build. You can add this call to your new and edit actions.
Note that you edit the receipts in a form for the report. This means, that you should hit the ReportsController and not the ReceiptsController.
If things do not work here is some debugging advice:
execute rake routes to see if everything is defined correctly.
Inspect the generated HTML from form_for(#report). Especially the 'action=""' attribute of the form tag is relevant. It should point to "/reports/X"
EDIT: I created a Gist with all the relevant files to get a nested form working: https://gist.github.com/4420280
Do checkout cocoon gem for nested resources form. This gem has made a work lot easier dealing with nested resources. https://github.com/nathanvda/cocoon

Resources