Render page and pass parameters - ruby-on-rails

I have three html pages.
In the first page, I view the list of data.
In the second page, I view the data of a particular element of the list.
In the third page, the user can edit data of a particular element of the list.
When the user submits the 'form', how can I redirect the user in the second page? I tried in this way:
render :action => "show_details",:id=>params[:id]
It works. The link is correct. But the page is not opened if I do not refresh the page.
UPDATE I
I write my code in this action in the reports controller:
def setFixed
rs=Report.find(params[:id])
rs.state ="1"
rs.save
render :action => "show_details",:id=>params[:id]
end
UPDATE II
Reports controller code:
class ReportsController < ApplicationController
before_filter :authenticate_user, :only => [:index,:show,:show_details,:new]
def stateDialog
render :stateDialog, :current_state=>params[:current_state]
end
def setFixed
rs=Report.find(params[:id])
rs.state ="1"
rs.save
render :action=>"show_details",:id=>params[:id]
end
def setNotFixed
rs=Report.find(params[:id])
rs.state ="0"
rs.save
render :action=>"show_details",:id=>params[:id]
end
def edit
#report=Report.find(params[:id])
end
def update
#report = Report.find(params[:id])
if #report.update_attributes(params[:report])
flash[:notice]=''
render :action=>"show_details",:id=>params[:id]
else
flash[:notice]=''
render :action=>"show_details",:id=>params[:id]
end
end
def deleteDialog
render "deleteDialog"
end
def focus_maps
render "focus_maps"
end
def delete
Report.find(params[:id]).destroy
render "show"
end
def index
#report=Report.new
end
def logged
render "new"
end
def show
render params[:page]
end
def new
#report=Report.new
end
def show_details
render "show_details"
end
def create
#report=Report.new( params[:report] )
if #report.save
flash[:notice]='Segnalazione avvenuta!'
else
flash[:notice]='Impossibile sottoporre la segnalazione!'
end
render "show"
end
end

I found advice that params must be filled before render calling, like this:
#id = params[:id]
render :action => 'show_details'
This solution is works for me, try it

If you want to redirect user somewhere you should use redirect_to
redirect_to action: 'show_details', id: params[:id]

Related

the foreign key is just passing to the new form not the create action (ruby on rails)

This is my controllers and routes
I have a albums controller and a bands controler with their models, and I want to access the foreign key to pass it, but it told me bands is blank
def show
#album = Album.find_by(:id => params[:id])
render :show
end
def new
#band = Band.find_by(:id => params[:band_id])
#albums = Album.new(:band_id => params[:band_id])
render :new
end
def create
#albums = Album.new(albums_params)
if #albums.save
flash[:success] = "Album created successfully"
redirect_to album_path(#albums.id)
else
#band = #albums.band
flash[:error] = #albums.errors.full_messages
render :new
end
end
def update
render :edit
end
def edit
end
def destroy
end
private
def albums_params
params.require(:albums).permit(:name, :band_id, :live, :year)
end
end```
resources :bands do
resources :albums, :only => :new
end
Try to pass Band relation like below.
def new
#band = Band.find_by(:id => params[:band_id])
#albums = Album.new(:band => #band)
render :new
end
OR check your code. Can you find Band with correct id?
#band = Band.find_by(:id => params[:band_id])
AND check your Views
You must put someting like below
<%=form.hidden_field :band_id, value: #albums.band_id%>
OR
<%=form.hidden_field :band_id, value: #band.id %>

Web scraping information from different websites

Im using a gem called MetaInspector to scrape data from different websites. Im building a site where i can collect data from different sites but am having trouble setting up. I have a model called site with a title and a url both strings. When i create a new "site" the name will come out as example.com/"sitename" and in there i would like to have the data just from that site. I kinda have an idea to this by adding page = MetaInspector.new to the new method but cant see how i can set a url in there.
I can show my controller and other info if needed.
Controller
class Admin::SitesController < Admin::ApplicationController
def index
#sites = Site.all
end
def show
#site = Site.friendly.find(params[:id])
end
def edit
#site = Site.friendly.find(params[:id])
end
def update
#site = Site.friendly.find(params[:id])
if #site.update(site_params)
redirect_to admin_path
else
render :edit
end
end
def destroy
#site = Site.friendly.find(params[:id])
#site.destroy
if #site.destroy
redirect_to admin_path
end
end
def new
#site = Site.new
end
def create
#site = Site.new(site_params)
if #site.save
redirect_to admin_path
else
render :new
end
end
private
def site_params
params.require(:site).permit(:title, :url)
end
end
If I understand correct you want to show the metainfo for a Site you have added. You could put that code in the show action of the controller:
def show
#site = Site.friendly.find(params[:id])
#page = MetaInspector.new(#site.url)
end
And update the show.html.erb template to display info about #page, ie:
<%= #page.title %>

Ruby on rails controller code, needs refactor best way to approach for more dry?

I have a welcome wizzard that builds a user profile when first login.Problem is it is quite messy implemented but I have tried to refactor it several times and rewrite it but cannot comeup with something better then below.
In ideal world it would be all inside welcome_controller.rb but this have caused much headache so Now i rewrote the update method for profile_controller instead.
Any thoughts on how to improve this make it more dry and clean? Would love to recieve some good input on this and thoughts perhaps to move all update stuff to welcome controller instead?
WelcomeController:
class WelcomeController < ApplicationController
before_filter :authenticate_user!
before_filter :load_step
layout "welcome"
def sub_layout
"center"
end
def edit
# form updates post to edit since
# profile is non existant yet
params[:step] = "photos" unless params[:step]
#photos = Photo.where(:attachable_id => current_user.id)
#profile = Profile.where(:user_id => current_user.id).first
#photo = Photo.new
if ["photos", "basics", "details", "test"].member?(params[:step])
# force rendering the correct step
case current_user.profile.step
when 1
render :template => "/profiles/edit/edit_photos", :layout => "welcome"
when 2
render :template => "/profiles/edit/edit_basics", :layout => "welcome"
when 3
render :template => "/profiles/edit/edit_details", :layout => "welcome"
when 4
render :template => "/profiles/edit/edit_test", :layout => "welcome"
end
else
render :action => "/profiles/edit/edit_photos"
end
end
def load_step
redirect_to root_path if current_user.profile.complete
case current_user.profile.step
when 1
redirect_to "/welcome" unless params[:controller] == "welcome"
when 2
redirect_to "/welcome/basics" unless params[:controller] == "welcome" && params[:action] == "edit" && params[:step] == "basics"
when 3
redirect_to "/welcome/details" unless params[:controller] == "welcome" && params[:action] == "edit" && params[:step] == "details"
when 4
redirect_to "/welcome/test" unless params[:controller] == "welcome" && params[:action] == "edit" && params[:step] == "test"
end
end
end
ProfileController:
class ProfileController < ApplicationController
...
def update
#profile = Profile.find(params[:id])
#tags = Session.tag_counts_on(:tags)
#profile.form = params[:form]
#match = Match.where(:user_id => current_user.id).first
authorize! :update, #profile
respond_to do |format|
if #profile.update_attributes(params[:profile])
if current_user.profile.complete
format.html { redirect_to "/profiles/#{ current_user.username }/edit/#{ #profile.form }", notice: t('notice.saved') }
else
case current_user.profile.step
when 1
current_user.profile.update_attributes(:step => 2)
format.html { redirect_to "/welcome/basics", notice: t('notice.saved') }
when 2
current_user.profile.update_attributes(:step => 3)
format.html { redirect_to "/welcome/details", notice: t('notice.saved') }
when 3
current_user.profile.update_attributes(:step => 4)
format.html { redirect_to "/welcome/test", notice: t('notice.saved') }
end
end
else
if current_user.profile.complete
format.html { render action: "/edit/edit_" + params[:profile][:form], :what => #profile.form }
else
case current_user.profile.step
when 1
current_user.profile.update_attributes(:step => 2)
format.html { redirect_to "/welcome/basics", notice: t('notice.saved') }
when 2
current_user.profile.update_attributes(:step => 3)
format.html { redirect_to "/welcome/details", notice: t('notice.saved') }
when 3
current_user.profile.update_attributes(:complete => 1)
format.html { redirect_to root_path }
end
end
end
end
end
...
end
Views are in /profiles/edit/*
Wizards are notoriously difficult to get right and I've never seen an implementation that fully satisfied me. I usually go with so called "form objects" and create a restful controller for each step.
There is an excellent (but paid) Railscast on the subject.
The gist is this: You make an object that quacks just like a regular ActiveRecord model, by using ActiveModel.
For instance:
class Welcome::BasicInformation
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
def persisted?
false
end
def initialize(user)
#user = user
end
attr_reader :user
delegate :some_field, :some_other_field, to: :user
validates_presence_of :some_field
def save(params)
user.some_field = params[:some_field]
user.some_other_field = params[:some_other_field]
if valid?
user.step = 2
user.save
end
end
def photo
#photo ||= Photo.new
end
def profile
#profile ||= user.profiles.first
end
end
You'd basically create a model like this for every step.
Then you can create controllers for each step, with a specialized ApplicationController for all the steps:
class Welcome::ApplicationController < ::ApplicationController
layout "welcome"
before_filter :authentice_user!
end
And for each step:
class Welcome::BasicInformationsControlller < Welcome::ApplicationController
def new
#step = Welcome::BasicInformation.new(current_user)
end
def create
#step = Welcome::BasicInformation.new(current_user)
if #step.save(params[:welcome_basic_information])
redirect_to welcome_some_other_step_path, notice: "Yay"
else
render :new
end
end
end
And create a route for each step:
namespace :welcome do
resource :basic_information, only: [:new, :create]
resource :some_other_step, only: [:new, :create]
end
This only leaves some automatic redirects to do, like prohibiting users from going to steps that they're not yet allowed to visit. This might not be as important now that you're using separate URLs for each step.
You can store information about which step to visit in the form objects:
class Welcome::BasicInformation
# ...
def allowed?
user.profile.step == 1
end
end
And then refactor the controllers a bit:
class Welcome::BasicInformationsController < Welcome::ApplicationController
before_filter :allowed?
def new
end
def create
if step.save(params[:welcome_basic_information])
redirect_to welcome_some_other_step_path, notice: "Yay"
else
render :new
end
end
private
def step
#step ||= Welcome::BasicInformation.new(current_user)
end
helper_method :step
def allowed?
redirect_to previous_step_path unless step.allowed?
end
end
This might not be shorter, but I do like how flexible it is, how focussed each step is, how you can do different validations on each step and so on. Each controller/model combination is very easy to follow and will be understandable for others.
There are a couple of things I'd do, but first some thoughts.
Sometimes you have to break restfullness a little to make code more readable. That's the case
It's not a good manner to redirect between controllers as you do in here
So, what I'd do.
Put all the code concerning those steps in a single controller (profile preferably) and adjust url with routing.
Create a single show and single save action
If I understand properly the step that will be shown to user depends ONLY on what User#step is set on current_user. Threfore I think there's really no need to pass any url variables, you can get current/next step from current_user.
Code refactored (may be some errors, didn't test that)
All in ProfileController
def edit
#profile = Profile.find(current_user.id)
#next_step = current_user.step.to_i + 1 # I imply that there's just single permissable next step
render :template => "/profiles/edit/#{#next_step}", :layout => "welcome"
end
def update
#profile = Profile.find(params[:id])
authorize! :update, #profile
if #profile.update_attributes(params[:profile])
# you should pass step number in params so I get's updated by default.
redirect_to "/welcome/basics", notice: t('notice.saved')
else
end
end

How can I detect if it's submit from new or edit?

This before_filter validates before update or create record if captcha is correct.
When it's incorrect, it takes me back to previous page but all of the input data will be gone....
How can I remain the input data that was typed in at previous page?
I'd like to use before_filter and apply these 2 actions 'update' and 'create'.
It should detect where the submit is come from and switches where to re-render 'new' or 'edit'
before_filter :simple_captcha_check, :only => [:update, :create]
def simple_captcha_check
if !simple_captcha_valid?
flash[:error] = 'Wrong Captcha!'
redirect_to :back
end
end
Assuming you're creating/updating an User model, your code can look like this:
def simple_captcha_check
if !simple_captcha_valid?
flash[:error] = 'Wrong Captcha!'
if request.put? # We came from an edit request
#user = User.find(params[:id])
#user.attributes = params[:user]
render :action => :edit
elsif request.post? # We came from a new request
#user = User.new params[:user]
render :action => :new
end
end
end

Rails says class is nil despite console showing database

I have a database of objects (tools) in my Ruby on Rails project. When I use "rails dbconsole" and
select * from tools;
it returns a whole list of tool objects. But when I try to view the following page, it gives me an error.
Page:
<!DOCTYPE html>
<html lang="en">
<%= stylesheet_link_tag "tools", :media => "all" %>
<body>
<%= #tools.each do |tool| %>
<%= link_to(tool(image_tag.image_url)) %>
<% end %>
</body>
</html>
Error:
undefined method `each' for nil:NilClass
When I change the code to add an if statement against nil objects, the page works (without displaying any tools).
<% if #tools.nil? %>
<% else %>
<%= #tools.each do |tool| %>
<%= link_to(tool(image_tag.image_url)) %>
<% end %>
<% end %>
So it seems like #tools doesn't have any values in it, but when I look at it in the dbconsole, there are tools there. I can't figure this out, and I've spent the past few days googling for answers, so any and all ideas would be welcome!
EDIT: Added tools_controller.rb
class ToolsController < ApplicationController
before_filter :check_authentication
def check_authentication
unless session[:user_id]
session[:intended_action] = action_name
session[:intended_controller] = controller_name
redirect_to new_session_url
end
end
def new
#tool = Tool.new
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #tool }
end
end
def show
end
def index
#tools = Tool.all
end
# GET /tools/1/edit
def edit
#tool = Tool.find(params[:id])
end
# POST /tools
# POST /tools.json
def create
#tool = Tool.new(params[:tool])
respond_to do |format|
if #tool.save
format.html { redirect_to #tool, :notice => 'tool was successfully created.' }
format.json { render :json => #tool, :status => :created, :location => #tool }
else
format.html { render :action => "new" }
format.json { render :json => #tool.errors, :status => :unprocessable_entity }
end
end
end
end
Loading #tools in a before_filter for every action as #nbarraille has suggested is a bad idea, because there are many (probably most) actions where you will definitely not need the full set of tools (e.g. create and destroy). The line #tools = Tool.all hits your database so you should minimize the number of times you use it.
For the case you have here, you only need to change your show action to get this to work:
def show
#tools = Tool.all
end
However, note that normally the show action is for displaying a single resource (tool), not the whole list of resources (which is normally done in the index action). It looks like you're deviating from the normal way of doing things, is there any particular reason why?
In order for the #tools variable to be accessible from your view, you need to declare it in your controller, like this:
#tools = Tool.all
If you want it to be only accessible from one page, just declare it in the according method.
Here is an example, assuming you want to make the variable available for your home/index page:
class HomeController < ApplicationController
def index
#tools = Tool.all
respond_to do |format|
format.html # index.html.erb
end
end
end
If you want it to be accessible in all your pages, you can declare it in the before_filter method of your ApplicationController.
Here is how to do this:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :load_variables
# Load variables to be used everywhere
def load_variables
#tools = Tool.all
end
end

Resources