I have he following code in my update action for a Controller. The code works when in the create, but doesn't seem to kick in under update:
def update
#contact = Contact.find(params[:id])
# bug, why isn't this working?
unless #contact.fax.empty?
#contact.fax = "1" + Phony.normalize(#contact.fax)
end
unless #contact.phone.empty?
#contact.phone = "1" + Phony.normalize(#contact.phone)
end
if #contact.update_attributes(params[:contact])
flash[:notice] = "Successfully updated contact."
redirect_to #contact
else
render :action => 'edit'
end
end
these should be in your model. FAT model, SKINNY controller:
# contact.rb
...
# may need require 'phony' and include Phony
before_save :prep
def prep
self.fax = 1+Phony.normalize(self.fax) unless self.fax.empty? || (self.fax.length == 11 && self.fax[0] == 1)
self.phone = 1+Phony.normalize(self.phone) unless self.phone.empty? || (self.phone.length == 11 && self.phone[0] == 1)
end
...
Edit:
As I mentioned in my comment, it's better in terms of storage and efficiency and indexing to store as a bigint unsigned in your database and add the prettiness to the numbers in a method. This way, your site is always normalized (no two phone numbers will ever look different because they are formatted 'on the fly').
# sample methods
def phony
str = self.phone.to_s
"#{str[0..2]}-#{str[3..5]}-#{str[6..10]}"
end
# use a similar method for faxing, but I'll write
# this one differently just to show flexibility
def faxy
str = self.fax.to_s
"+1 (#{str[0..2]}) #{str[3..5]}-#{str[6..10]}"
end
you never call save on #contact in your unless blocks, so your call to #contact.update_attributes(params[:contact]) undoes any change you made in those blocks (because those keys in the params hash correspond to empty values).
def update
#contact = Contact.find(params[:id])
if #contact.update_attributes(params[:contact])
#contact.update_attributes(:fax => "1" + Phony.normalize(#contact.fax)) unless #contact.fax.empty?
#contact.update_attributes(:phone => "1" + Phony.normalize(#contact.phone)) unless #contact.phone.empty?
flash[:notice] = "Successfully updated contact."
redirect_to #contact
else
render :action => 'edit'
end
end
You could use update_attribute but that bypasses validation.
You could also use a before_save callback in the Contact class, but you would have to check if the phone or fax are already "normalized."
Related
I'm fairly new to rails and struggling on changing database values after the user successfully paid via stripe. Additionally after paying, it somehow redirects me everytime to '/subscriberjobs/1' which doesn't exist. Instead it should direct to the root_path of the application.
Here is what I've got:
Routes
resources :subscriberjobs
resources :jobs
Jobs Controller
def new
if current_user
#job = current_user.jobs.build
else
redirect_to new_user_session_path
end
end
def create
#job = current_user.jobs.build(job_params)
if #job.save
redirect_to '/subscriberjobs/new'
else
render 'new'
end
end
Subscriberjobs Controller (Here is what doesn't work!)
class SubscriberjobsController < ApplicationController
before_filter :authenticate_user!
def new
end
def update
token = params[stripeToken]
customer = Stripe::Customer.create(
card: token,
plan: 1004,
email: current_user.email
)
Job.is_active = true # doesn't work
Job.is_featured = false # doesn't work
Job.stripe_id = customer.id # doesn't work
Job.save # doesn't work
redirect_to root_path # doesn't work
end
end
Please tell me if you need additional information. Every answer is very appreciated. Thanks!
Send saved job id to subscriberjobs/new as a param. You can keep hidden field which will have value job_id in subscriberjobs/new html form, which will call your SubscriberjobsController#update method. There access it using params.
In JobController #create
redirect_to "/subscriberjobs/new?job_id=#{#job.id}"
In your SubScribeJob form
hidden_field_tag 'job_id', params[:job_id]
In your SubScribeJobCotroller
#job = Job.find(params[:job_id])
User has a page where he can edit his personal information(name, date of birth,...).
But the user can delete the value from the Name field, and then go to some link with the sidebar, and the information is saved as an empty string('').
Question: how do I make such a check, the user can not go to anywhere, if at least one field is empty? Or if it is empty, then fill it with the previous data?
Controller:
def editMain
unless #user = User.find_by_id(session[:user_id])
render_404
return
end
#user.update_attributes(params[:user])
if params[:user][:first_name] == ""
#user.errors.add(:first_name, "ERROR!")
end
if params[:user][:last_name] == ""
#user.errors.add(:last_name, "ERROR!")
end
if params[:user][:birtday] == ""
#user.errors.add(:birthday, "ERROR!")
end
#countries = Country.all
#cities = City.all #where(country_id: #user.city.country.id)
if #user.errors.empty?
flash[:notice] = "Succssesful!"
else
render "editMain"
end
end
Please, help me!
Use what framework gives you. You don't have to rewrite existing things.
This code =>
unless #user = User.find_by_id(session[:user_id])
render_404
return
end
Equals to =>
#user = User.find(session[:user_id])
Dont try to validate objects in controller.
This code =>
#user.update_attributes(params[:user])
if params[:user][:first_name] == ""
#user.errors.add(:first_name, "ERROR!")
end
if params[:user][:last_name] == ""
#user.errors.add(:last_name, "ERROR!")
end
if params[:user][:birtday] == ""
#user.errors.add(:birthday, "ERROR!")
end
Equals to this =>
if #user.update_attributes(params[:user])
flash[:notice] = "Succesful!"
redirect_to some_path
else
render "editMain"
end
and use activerecord validations.
models/user.rb
validates_presence_of :first_name, :last_name, :birthday
Frankly, this piece of code is quite a mess:
1) 404 is returned when you can't find a page (vs you can't find a resource (user) with some id)
2) 404 is returned automatically by rails, if page (route) isn't found
3) Model should do validation (vs controller)
4) Rendering could be done for your automatically in most cases
I would really recommend to read through Getting tarted with Rails:
http://guides.rubyonrails.org/getting_started.html
In my create action, method new acts like create.
def create
#page = Page.new(params[:page].merge(:user_id => current_user.id ))
if #page.save
flash[:notice] = t("success")
redirect_to pages_path
else
render :new
end
end
ActiveRecord creates new object in database while I'm using new with params. Page.new works fine in new action in my controller. What can be the reason? There is no overridden method new and no callbacks (before_save, before_create etc) in my model. Any help would be appreciated.
UPDATE - code from debugger
.../app/controllers/pages_controller.rb:48
#page = Page.new(params[:page].merge(:user_id => current_user.id ))
(rdb:25) #page
nil
(rdb:25) n
.../app/controllers/pages_controller.rb:49
if #page.save
(rdb:25) #page
#<Page id: 80 ... >
(rdb:25) Page.last
#<Page id: 80 ... >
(rdb:25) #page.save
false
Check my inline comments..
def create
#page = Page.new(params[:page].merge(:user_id => current_user.id )) # you trigger new thats fine..
if #page.save # Notice here.. This line is triggering query on database.
flash[:notice] = t("success")
redirect_to pages_path
else
render :new
end
end
Reason (method in model which can change status in workflow):
def status=(state_name)
states = [self.current_state.to_sym]
possible_states.each {|t| states<< t[1]}
unless state_name.blank?
if states.include? state_name
process_event! state_name
end
end
end
Ugly fix
def create
#page = Page.new
if #page.update_attributes(params[:page].merge(:user_id => current_user.id )) && #page.save
flash[:notice] = t("success")
redirect_to pages_path
else
render :new
end
end
Mistake was quite silly and I'm not proud of my solution. Anyway, thanks for help:)
With an ActiveRecord class, create = new + save
https://github.com/rails/rails/blob/7edade337e968fb028b2b6abfa579120eb424039/activerecord/lib/active_record/persistence.rb#L40
Your controller code is correct. This is how a 'create' controller method should work. The problem is not there.
Are you certain you're having two models created?
The .new method you're calling with the attributes creates an activerecord object in memory that's unsaved. The .save method saves it. At the end (assuming the data is valid) you should have a single object in memory.
If you have two objects created, then there is a problem. If you have only one, then it's as it should be.
Are you having a second object created by this controller method?
The process should be:
# when GET /student/new is called, this returns an empty object to display in the form
# for the user to see.
def new
#page = Page.new
end
# When POST /page is called, the form params are passed in here.
def create
# First, generate a new page object with the params passed in.
#page = Page.new(params[:page].merge(:user_id => current_user.id ))
# Now try save the object to persist it in the database.
if #page.save
flash[:notice] = t("success")
redirect_to pages_path
else
render :new
end
end
I'm trying to build a game where you guess numbers.
The problem is if you make a mistake it redirects you to a leaderboard(mvc) form where you enter your name plus it`s pre populated with sessions data from a different controller(game) and submits both into the DB.
#round & #points are the two variables I want to access and store as score and level.
class ApplicationController < ActionController::Base
before_filter :set_current_account
def set_current_account
# set #current_account from session data here
Game.current = #round
end
protect_from_forgery
end
-
class Leaderboard < ActiveRecord::Base
cattr_accessor :current
end
# == Schema Information
#
# Table name: leaderboards
#
# id :integer not null, primary key
# name :string(255)
# score :string(255)
# level :string(255)
# created_at :datetime
# updated_at :datetime
#
-
class GameController < ApplicationController
def index
#games = Game.all
respond_to do |format|
format.html
end
end
def start_game
session[:round] ||= 1
session[:points] ||= 0
#round = session[:round]
#points = session[:points]
end
def generate_round
numbers = Array.new(6){rand(9)}
#addition = []
#display = numbers
numbers.inject do |s, i|
#addition << s + i
#addition.last
end
end
def next_round
session[:round] += 1
session[:points] += 1200
#round = session[:round]
#points = session[:points]
end
def destroy_sessions
session[:round] = nil
session[:points] = nil
session[:addition] = nil
#round = session[:round]
#points = session[:points]
#addition = session[:addition]
start_game
end
def submit_name
#game = Game.new(params[:game])
respond_to do |format|
if #game.save
format.html { redirect_to(leaderboard_path, :notice => 'Score was added successfully.') }
else
format.html { render :action => "new" }
end
end
end
def game_over
redirect_to :controller => 'leaderboards', :action => 'new' and return
end
I haven't read the whole thing, but if you just want to access those variables, you can just pass them as parameters.
Pass those values into game_over as params
Use this to redirect
redirect_to :controller => 'leaderboards', :action => 'new' and return, :round => params[:round], :points => params[:points]
Alternatively, you can just keep the session until a new game is started or score is recorded to the leaderboard.
I think you've taken an approach here that shouldn't even work. The Rails MVC framework is structured around the principle that each request is serviced independently, and that, in theory, there is no state transfer from one request to the next except through params passed in, records stored in the database and the persistent user session.
To design a web-based application like you might a single-process, single-user, single-session program is a mistake. Using singletons, like your cattr_accessor called current will be problematic since it is both shared between requests, and not shared between different instances of Rails, of which there are typically many.
Mapping more closely to the REST standard of index, new, create, show, edit, update and destroy would help. For instance start_game should be create and destroy_sessions should probably be destroy.
It's not clear from your design if each game is shared amongst several users, or if it they are created for each user individually, so it's hard to say more about how to solve your problem.
So I have a url like the following
localhost/users/:id/posts
which gives the posts of that particular user. Now this id can be either his login (which is a string) or the id (user.id) which is technically an Integer but params[:id] is always a string. So how do I implement this an action.
#user = params[:id].is_a?(String) ? User.find_by_login(params[:id]) : User.find(params[:id])
The above code miserably fails since params[:id] is always a string. Any thoughts? Thanks.
When I've done this, I've actually had two separate controller actions-- show and show_by_login. I feel like it's less unpredictable that way, and I have more control.
Be sure to enforce uniqueness of your logins, index them, and if show_by_login can't find the record you have to raise ActiveRecord::RecordNotFound yourself.
def show
#user = User.find(params[:id])
respond_to do |format|
format.html
format.xml { render :xml => #user.to_xml }
end
end
def show_by_login
#user = User.find_by_login(params[:login])
unless #user
raise ActiveRecord::RecordNotFound
end
render :action => 'show'
end
You could use a regular expression:
#user = params[:id] =~ /^\d+$/ ? User.find(params[:id]) : User.find_by_login(params[:id])
So long as you don't allow any logins to consist purely of digits, you could write your own finder/named_scope.
class User < ActiveRecord::Base
named_scope :find_by_id_or_login, lambda {|id_or_login|
{ :conditions => ["id = ? OR login = ?", id_or_login, id_or_login] }
}
end
#user = User.find_by_id_or_login(params[:id])