Passing Form Params to Model in Rails3 - ruby

I'm trying to pass some parameters (count) from a form to a model.
The model basically creates a user X times.
In our form, we have this:
= f.text_field :count
And I can see this is submitted properly in the dev. log:
user"=>{"count"=>"2"}
I've tried this in my controller:
#user = User.generate(params[:count])
Generate is then supposed to call this in my user model:
def self.generate(count=10)
count.times do
.....
end
I basically need the '10' replaced with the count from form.
How can I go about this?
-- UPDATE 1 --
I've edited as below but get had to change from count to usercount because I think that's reserved in rails..
I have this in my controller:
#user = User.generate(params[:user][:usercount])
This in my user.rb:
attr_accessor :usercount
def self.generate
usercount.times do
...
end
When I submit, I get this:
ArgumentError (wrong number of arguments (1 for 0)):
app/models/user.rb:22:in `generate'
app/controllers/users_controller.rb:38:in `new_users_create'
Really frustrating...

class User
attr_accessor :count
end
#user = User.generate(params[:user][:count])
should work fine.

Related

undefined method `save' for 1:Fixnum [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I made some variables that is related to models and I want to save the new variable relating to the control structure, but I can't. It said "NoMethodError - undefined method `save' for 1:Fixnum:".
What I want to make is that the function if This program get 4 people, It will show the member name for that members. 5th member will be in the next group.
Anyone can solve this?
or if you need more information, please let me know.
Thanks
This is Waitinglists_controller
class WaitinglistsController < ApplicationController
before_action :authenticate
def new
#waitinglist = current_user.created_waitinglists.build
end
def create
#waitinglist = current_user.created_waitinglists.build(waitinglist_params)
if #waitinglist.save
redirect_to waitinglist_waiting_path(#waitinglist, #owner)
else
render :new
end
end
def waiting
#group_number = Waitinglist.select(:count_number).last
#already_group_people = Waitinglist.where(count_number: #group_number).count
#current_person_group_number = current_user.created_waitinglists.select(:count_number)
#current_group_people = Waitinglist.where(count_number: #current_person_group_number).count
case #already_group_people
when 0
#current_person_group_number = 1
#current_person_group_number.save
when 1..2
#current_person_group_number = #group_number
#current_person_group_number.save
when 3
#current_person_group_number = #group_number
#current_person_group_number.save 
redirect_to show_waitinglist_path
when 4
group_number += 1
#current_person_group_number = #group_number
#current_person_group_number.save
end
end
def show
#current_person_group_number = current_user.created_waitinglist.select(:count_number)
#matched_people = Waitinglist.find(count_number: #current_person_group_number)
#matched_people == 0 if #matched_people = nil
end
private
def created_by?(user)
return false unless user
owner_id == user.id
end
def waitinglist_params
params.require(:waitinglist).permit(:look_like, :id)
end
end
This is Sessions controller for User loggin
class SessionsController < ApplicationController
def create
user = User.find_or_create_from_auth_hash(request.env['omniauth.auth'])
session[:user_id] = user.id
redirect_to root_path
end
def destroy
reset_session
redirect_to root_path
end
end
Application contrtoller is this
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :current_user, :logged_in?
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def logged_in?
!!session[:user_id]
end
def authenticate
return if logged_in?
redirect_to root_path
end
end
Under codes are for models
class Waitinglist < ActiveRecord::Base
belongs_to :waiting_person, class_name: 'User'
after_initialize :init
def init
self.count_number ||= 1 #will set the default value only if it's nil
end
end
class User < ActiveRecord::Base
has_many :created_waitinglists, class_name: 'Waitinglist', foreign_key: :owner_id
def self.find_or_create_from_auth_hash(auth_hash)
provider = auth_hash[:provider]
uid = auth_hash[:uid]
name = auth_hash[:info][:name]
image_url = auth_hash[:info][:image]
User.find_or_create_by(provider: provider, uid: uid) do |user|
user.nickname = name
user.image_url = image_url
end
end
end
It's quite clear to me. You call save on #current_person_group_number, which is instance of Fixnum, so it doesn't have save method defined.
Unfortunately the code you wrote makes very little sense and it is pretty hard to understand what you're trying to do here.
Firstly, you overuse instance variables. If you're not gona use them in another methods (it is hard to say as you haven't post rest of your class)
Secondly, you overuse select method.
#group_number = Waitinglist.select(:count_number).last
All it does is changing the SELECT statement when querying the database for models, but it still returns the model, not a number or field value. So #group_number is not a number - it is a WaitingList instance. If you want a number do:
group_number = WaitingList.last.count_number
(posting now as question may be closed in a second. Will update later if that won't happen)
You can only save the ActiveRecord objects, which means you need to have it somewhere. Apparantly you want to update #current_person_group_number, however you can't reassign this variable to do the trick. You have to get the whole model, change its attribute and then save the model. It would look sth like:
current_waiting_list = current_user.created_waitinglists.last # This seems to be a collection, you need to tell here which waiting list you want to get from this collection
current_waiting_list.count_number += 1
current_waitin_list.save
My last point is - please look into act_as_list gem. Since you're creating waiting list it is a must have gem for you.

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.

How can I test the setting of a composed object's property using RSpec?

Say for example I have two classes within my Rails application - Customer class and a Card class. The Customer class is composed of a Card class i.e. the customer has a card.
I then have a Rails controller with a 'do_something' action defined, which will initialise a new instance of Customer (which in-turn will internally create a new instance of Card) using the params passed in on the POST.
The number of the card is then set as follows:
class ShopController < ApplicationController
def do_something
customer = Customer.new params
customer.card.number = params[:card_number]
...
end
end
How is this assignment of the card number tested in an RSpec test? Ideally, if 'should_receive_chain' existed we could write:
describe MyController do
describe "POST 'do_something'" do
it "should set card number"
params = { :card_number => '1234' }
card_mock.should_receive_chain(:card, :number).with '1234'
post :do_something
end
end
end
Any ideas? Perhaps the fact that it can't be tested easily is a code smell, and maybe I should create a setter method on the Customer class?
I do think you are testing this at the wrong level. If you are wed to setting the card number in a separate statement then it might be better to create a function to help with this.
class Customer
def self.new_with_card_number(params, number)
customer = new(params)
customer.card.number = number
customer
end
end
describe Customer do
it 'creates a card with a number' do
customer = described_class.new_with_card_number({}, '1234')
customer.card.number.should == '1234'
end
end
You could then change your controller to:
class ShopController < ApplicationController
def do_something
customer = Customer.new_with_card_number(params, params[:card_number])
end
end
A simpler solution might be to simply name the parameter in your form so that setting the card number is done automatically:
params[:customer][:card_attributes][:card_number]
You could then just change the call to customer = Customer.new(params).
I would change the Customer model to add accepted_nested_attributes_for :card and change the controller action to
class ShopController < ApplicationController
def do_something
customer = Customer.create params[:customer]
...
end
end
then your spec can look like
describe ShopController do
describe 'POST /do_something' do
it "sets the card's card number" do
post :do_something, :customer =>
{
:card_attributes => {:card_number => '1234'}
}
Customer.last.card.number.should == '1234'
end
end
end

in UsersController#create, User.new(params[:user]) return an empty User (params looks good)

I'm kind of new to Rails 3.1. and I'm facing an issue only in my production env with my Signup form (actually, it's more about the controller).
Here is the code in User
class UsersController < ApplicationController
[...]
def create
#user = User.new(params[:user])
logger.info "value of login in param : #{params[:user][:login]}" #-> log the actual login
logger.info "value of login : #{#user.login}" #-> log empty
#user.admin = false
if #user.save
flash[:notice] = t('flash.notice.user.create.valid')
redirect_back_or_default root_path
else
flash[:notice] = t('flash.notice.user.create.invalid')
render :action => :new
end
end
end
Also, the controller logs show that the params hash is good
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"QwOqmp0CT/d4mmC1yiLT4uZjP9bNDhbUXHanCQy5ZrA=",
"user"=>{"login"=>"myLogin",
"email"=>"t.r#gmail.com",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"}}
My login form works as expected (already created users are able to sign in)
Again, this only happens in production.
EDIT: Here is my User Model
class User < ActiveRecord::Base
acts_as_authentic
#== Callbacks
before_create :set_defaults
attr_accessible :avatar ##### EDIT: TO FIX THE ISSUE, ADD THE OTHER FIELDS AS WELL
protected
def set_defaults
self.total_1 = self.total_2 = self.total_3 = 0
end
end
Just to memorialize the answer from the comments above:
Normally you can use mass assignment to set fields on a model, but when you use attr_accessible, you are then limited to only mass assigning those fields. So stuff like User.new(params[:user]) won't work; instead, you'd have to do:
#user = User.new
#user.login = params[:user][:login]
# ...etc.
#user.save
Simple add your fields to the attr_accessible list and you can go back to mass assignment.

Testing before_create method in rspec and rails 3

I've looked into some tutes and all I saw were old posts on how to test before_create. Also it seems like they're all just testing that before_create was called i.e.:
#user = User.new
#user.should_receive(:method_name_called_by_before_create)
#user.send(:before_create) (sometimes they just do #user.save)
I want to actually test that my method worked and that it had assigned(and saved the variables) after creating the record.
Here are my models:
user.rb
class User < ActiveRecord::Base
has_one :character, :dependent => :destroy
after_create :generate_character
private
def generate_character
self.create_character(:name => "#{email}'s avatar")
end
end
and character.rb
class Character < ActiveRecord::Base
belongs_to :user
before_create :generate_character
private
def generate_character
response = api_call
#API CALL HERE
#set object attributes here
self.stat1 = calculate_stat1(response) + 5
self.stat2 = calculate_stat2(response) + 5
self.stat3 = calculate_stat3(response) + 5
end
def api_call
return api_call_response
end
end
I want to test that generate character indeed set the attributes without going online and calling the API call. Is this possible with rspec? I have a fixture of a json response so I was hoping I can stub out generate character and then use the fake response for testing.
Here's my character.spec:
describe Character do
before(:each) do
Character.any_instance.stub!(:api_call).and_return(fake_response.read)
#user = Factory(:user)
#character = #user.character
puts #character.inspect
end
def fake_response
File.open("spec/fixtures/api_response.json")
end
It prints out only 5 for each of the character's stats. Also I did a puts response in the generate_character method in character.rb and it still prints out the "real" api call.
I managed to do a puts in fake_response and it does goes through there but it also goes through the "real" api_call after, which makes the stub obsolete. How do I get through this?
A good approach here is extracting your api call into a self contained method. Something like this:
class Character < ActiveRecord::Base
belongs_to :user
before_create :generate_character
private
def generate_character
data = api_call
#set object attributes from data
end
def api_call
# returns a data structure
# resulting from the call
end
end
Then use RSpec's any_instance to stub the api_call method to return a fixed data structure
Character.any_instance.stub!(:api_call).and_return { {:id => 1, :attribute_one => "foo"} }
#user = User.create
#user.character.attribute_one.should == "foo"
for more info on any_instance check this commit

Resources