Rails is ignoring my redirect.
Use Case:
user navigates to his account and selects the option to display his subscription status
app lists his current subscription and the option to cancel it
user cancels the subscription
app updates the subscription record and should redirect the user back to the subscriptions action
-> but rails ignores this last step... any ideas what I am doing wrong?
Routes
map.resources :users, ..., :member => { ..., :subscriptions => :get, :subscribe => :post, :unsubscribe => :put}
Controllers:
def subscriptions
#tradesman = User.find_by_id(params[:id])
#subscription = #tradesman.current_subscription || Subscription.new
#all_subscriptions = Subscription.find(:all)
end
def subscribe
#tradesman = User.find_by_id(params[:id])
#subscription = current_user.subscriptions.build(params[:subscription])
#subscription.update_attributes(:started_at => Time.zone.now)
#subscription.save
redirect_to :action => 'subscriptions', :id => #tradesman.id
end
def unsubscribe
#tradesman = User.find_by_id(params[:id])
#subscription = #tradesman.current_subscription
#subscription.update_attributes(:ended_at => Time.zone.now)
#subscription.save
redirect_to :action => 'subscriptions', :id => #tradesman.id
end
View:
<div class = 'wrapper'>
<%= render :partial => "my_account_leftbar" %>
<% form_for #subscription, :url => subscribe_user_path(current_user) do |f| %>
<% #all_subscriptions.each do |subscription| %>
<div class="field">Start: <%= subscription.started_at %></div><br><br>
<% end %>
<% if #subscription.new_record? %>
<div class="field">
<%= f.check_box :subscription_type %>
<div class="actions">
<%= f.submit "Subscribe", :class => "button mr8" %>
</div>
</div>
<% else -%>
<%= f.check_box :subscription_type, :value => #subscription.subscription_type, :disabled => true %>
<% form_for #subscription, :url => unsubscribe_user_path(current_user) do |f| %>
<div class="actions">
<%= f.submit "Unsubscribe", :class => "button mr8" %>
</div>
<% end %>
<% end %>
<% end %>
</div>
Use redirect_to instead of render (last method call in your unsubscribe action).
Found the bug in the view where I nested the 'unsubscribe' form within the 'subscribe' form - what didn't make sense.
I rearranged my view (separated those two forms, one in the 'if' part and the other in the 'else' part. Works fine now:
<div class = 'wrapper'>
<%= render :partial => "my_account_leftbar" %>
<% #all_subscriptions.each do |subscription| %>
<div class="field">Start: <%= subscription.started_at %></div><br><br>
<% end %>
<% if #subscription.new_record? %>
<% form_for #subscription, :url => subscribe_user_path(current_user) do |f| %>
<div class="field">
<%= f.check_box :subscription_type %>
<div class="actions">
<%= f.submit "Subscribe", :class => "button mr8" %>
</div>
</div>
<% end %>
<% else -%>
<% form_for #subscription, :url => unsubscribe_user_path(current_user) do |f| %>
<%= f.check_box :subscription_type, :value => #subscription.subscription_type, :disabled => true %>
<div class="actions">
<%= f.submit "Unsubscribe", :class => "button mr8" %>
</div>
<% end %>
<% end %>
</div>
Related
Hey guys im developing a rails application that stores quotes in a database and then allows you to search through the quotes using a simple search. I have implemented the search form but the results do not appear and I cant figure out why.
controller:
class BasicsController < ApplicationController
def quotations
#quotations = Quotation.all
if params[:search]
#quotations = Quotation.search(params[:search]).order("created_at DESC")
else
#quotations = Quotation.all.order("created_at DESC")
end
if params[:quotation]
#quotation = Quotation.new( params[:quotation] )
if #quotation.save
flash[:notice] = 'Quotation was successfully created.'
#quotation = Quotation.new
end
elsif
#quotation = Quotation.new
end
if params[:sort_by] == "date"
#quotations = Quotation.order(:created_at)
else
#quotations = Quotation.order(:category)
end
end
end
model:
class Quotation < ApplicationRecord
def self.search(search)
where("author_name LIKE ? OR quote LIKE ?", "%#{search}", "%#{search}")
end
end
view:
<%= form_tag basics_quotations_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search], placeholder: "Search Quotations" %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<h3>Quotations</h3>
<ul>
<% for quotation in #quotations %>
<li><%= h quotation.author_name %>: <%= h quotation.quote %></li>
<% end %>
</ul>
<br/>
<% if params[:sort_by] == "date" %>
<%= link_to "Sort by category", :action => :quotations, :sort_by => :category %>
<% else %>
<%= link_to "Sort by date", :action => :quotations, :sort_by => :date %>
<% end %>
<hr/>
<h3>New quotation</h3>
<%= form_for #quotation, :url => { :action => :quotations } do |form| %>
<fieldset>
<legend>Enter details</legend>
<div class="form_row">
<%= form.label :author_name %>
<%= form.text_field :author_name, :size => 20, :maxlength => 40 %>
</div>
<div class="form_row">
<%= form.label :category %>
<% #cats = [] %>
<% Quotation.select('DISTINCT category').map(&:category).each do |element| %>
<% #cats << element %>
<% end %>
<%= form.select(:category,options_for_select([[#cats[0],1],[#cats[1], 2], [#cats[2],3]])) %>
</div>
<div class="form_row">
<%= form.label :new_category%>
<%= form.text_field :category , :size =>20 , :maxlength => 40 %>
</div>
<div class="form_row">
<%= form.label :quote %>
<%= form.text_area :quote, :rows => 2, :cols => 40, :maxlength => 500 %>
</div>
</fieldset>
<p>
<div class="form_row">
<%= form.submit 'Create' %>
</div>
</p>
<% end %>
routes:
Rails.application.routes.draw do
get 'basics/quotations'
resources :quotation, :quotations
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
You've got EVERYTHING in one action, which isn't great. You might want to review some of the simple rails tutorials available.
On the subject of search in particular, note the last four lines of your method...
if params[:sort_by] == "date"
#quotations = Quotation.order(:created_at)
else
#quotations = Quotation.order(:category)
end
So regardless of the results of your seach, you'll replace #quotations at that point with all quotations in either created_at or category order.
It is not wise to place all the stuffs into a single action,your action should be very clear and well defined.But you can achieve what you wanted to do like this.
class BasicsController < ApplicationController
before_action :new_quotation, only:[:search_quotations,:index,:quotations]
def search_quotations
respond_to do |format|
if params[:search]
#quotations = Quotation.search(params[:search]).order("created_at DESC")
else
#quotations = Quotation.all.order("created_at DESC")
end
if params[:sort_by] == "date" && quotations.present?
#quotations = #quotations.order(:created_at)
else
#quotations = #quotations.order(:category)
end
format.js{}
end
end
def quotations
if params[:quotation]
#quotation = Quotation.new( quotation_params)
if #quotation.save
flash[:notice] = 'Quotation was successfully created.'
end
redirect_to root_path
end
end
def index
#quotations = Quotation.all
end
private:
def new_quotation
#quotation = Quotation.new
end
//If you are using rails4 for later version then go for this line.
def quotation_params
params.require(:quotation).permit(:author_name, :quote,:category)
end
end
You need to actually separate the logic into above actions and where 'quotation' action is meant for creating a new quotation and 'seach_quotation' is for searching all the quotations and it should returns js response cause we are going to need this while rendering a partial '_list.html.erb'.
Your view(index.htm.erb) will be looking like this.
<div>
<%= form_tag basics_search_quotations_path, :method => 'get', remote: true do %>
<p>
<%= text_field_tag :search, params[:search], placeholder: "Search Quotations" %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
</div>
#This partial will be used for refreshing the quotations list via remote true feature for searching and sorting.
<div id="quotation_list">
<%= render 'basics/shared/list',{quotations: #quotations} %>
</div>
br/>
<% if params[:sort_by] == "date" %>
<%= link_to "Sort by category", :action => :search_quotations, :sort_by => :category, :remote => true %>
<% else %>
<%= link_to "Sort by date", :action => :search_quotations, :sort_by => :date, :remote => true %>
<% end %>
<hr/>
<h3>New quotation</h3>
<%= form_for #quotation, :url => { :action => "quotations", :controller => "basics" } do |form| %>
<fieldset>
<legend>Enter details</legend>
<div class="form_row">
<%= form.label :author_name %>
<%= form.text_field :author_name, :size => 20, :maxlength => 40 %>
</div>
<div class="form_row">
<%= form.label :category %>
<%= form.select(:category,options_for_select(['Love','Romance','Sadness'])) %>
</div>
<div class="form_row">
<%= form.label :category%>
<%= form.text_field :category , :size =>20 , :maxlength => 40 %>
</div>
<div class="form_row">
<%= form.label :quote %>
<%= form.text_area :quote, :rows => 2, :cols => 40, :maxlength => 500 %>
</div>
</fieldset>
<p>
<div class="form_row">
<%= form.submit 'Create' %>
</div>
</p>
<% end %>
Here is the partial that display the list of Quotations /basics/shared/_list.html.erb
<h3>Quotations</h3>
<ul>
<% for quotation in #quotations %>
<li><%= h quotation.author_name %>: <%= h quotation.quote %></li>
<% end %>
</ul>
Here is the routes you need to add routes.rb
resources :quotation, :quotations
get "basics/search_quotations" => "basics#search_quotations"
post "basics/quotations" => "basics#quotations"
root 'basics#index'
Instead of performing any calculation in views its better to perform it in controller/model if needed.
so instead of this following line in your view
<% #cats = [] %>
<% Quotation.select('DISTINCT category').map(&:category).each do |element| %>
<% #cats << element %>
<% end %>
<%= form.select(:category,options_for_select([[#cats[0],1],[#cats[1], 2], [#cats[2],3]])) %>
You can create an instance variable let's say #categories or something and use it like this
<%= form.select(:category,options_for_select(#categories)) %>
And last but not the least we have to have a search_quotations.js.erb because we are sending ajax request for fetching the search result and returning 'js' response.
$("#quotation_list").html("<%= escape_javascript(render('basics/shared/list', {quotations: #quotations })) %>")
This is the erb template:
<div id='recipe-form'>
<% if #recipe.errors %>
<div id='errors'>
<% #recipe.errors.messages.each do |field, messages| %>
<div class='error'>
<div class=field'><%= field %></div>
<div class='messages'>
<ul>
<% messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
</div>
<% end %>
<%= form_for #recipe, :html => {:multipart => true}, :url => '/recipes' do |f| %>
<%= f.label :title, 'title' %>
<%= f.text_field :title %>
<div id="photo-upload">
<%= file_field :photo0, :image, :id => 0 %>
</div>
<div id='existing-photos'>
<% recipe.photos.each do |photo| %>
<div id='<%= photo.id %>'>
<img src='<%= photo.image.url(:thumb) %>' />
<ul>
<li>
<%= link_to 'delete',
recipe_photo_url(
:recipe_id => #recipe.slug,
:id => photo.id
),
:method => :delete,
:remote => true
%>
</li>
</ul>
</div>
<% end %>
</div>
<%= f.label :body, 'body' %>
<%= f.cktext_area :body, :ckeditor => {:width => "500"} %>
<%= f.label :tags, 'tags (comma separated)' %>
<%= text_field_tag :tags %>
<%= submit_tag 'submit' %>
<% end %>
</div>
This is the create action:
def create
#recipe = Recipe.new(params[:recipe])
photo_keys = params.keys.select{|k|k.match(/^photo/)}
#photos = []
photo_keys.each do |photo_key|
#photos << Photo.new(params[photo_key])
end
#recipe.tags = Tag.parse(params[:tags])
#recipe.author = current_user
if #recipe.save &&
#photos.all?{|photo|photo.save}
#photos.each do |photo|
photo.recipe_id = #recipe.id
photo.save
end
flash[:notice] = 'Recipe was successfully created.'
redirect_to recipe_url(#recipe.slug)
else
flash[:error] = 'Could not create recipe. '
flash[:error] += 'Please correct any mistakes below.'
render :action => :new
end
end
And this is the new action:
def new
#recipe = Recipe.new
end
I read that if I use form_for as I am using above, the fields will be re-populated automatically.
When I inspect #recipe.errors from within the erb template, I can see that the errors generated by create are also available when the new action is rendered, but the fields do not re-populate.
I'm actually not sure about what render action: does but what I do and works is: Instead of rendering the action just render the template using render :new.
You need to set the same instance variables (those with #), which you already in your create action.
My comments model is pretty simple and works polymorphically, but I am now adding the ability to hide a comment by the author of a given record across those polymorphic associations.
class Comment < ActiveRecord::Base
attr_accessible :content, :show
belongs_to :commentable, :polymorphic => true
belongs_to :user
end
So, requests, questions, posts, submissions etc... all have comments and are accessing the comments template without issue, but I want to allow the author of the content in those models to show or hide comments (as opposed to flagging, for example) when the application identifies them as the author of the content that is being commented on.
class Request < ActiveRecord::Base
has_many :comments, :as => :commentable, :dependent => :destroy
end
So, I have everything working when there is only one model, by calling the author: #request.user,
but I'm wondering how to call an author using metaprogramming, so the comment view (with help) can determine what model is currently using the comment view.
I've done some research into metaprogramming, but have not found the answer.
Here is the code that calls the author (#request.user):
<% if #comments %>
<h1 class="mtop20">Comments</h1>
<% for comment in #comments %>
<% if signed_in? %>
<% if comment.show == true %>
<div class="well comment mtop10">
<% if current_user == #request.user or current_user.has_role? :admin %>
<%= simple_form_for [#commentable, comment] do |f| %>
<div class ="">
<%= f.input :show, :as => :hidden, :input_html => { :value => false } %>
<%= f.submit "Hide Comment", :class => 'btn btn-mini pull-right' %>
</div>
<% end %>
<% end %>
<span>
<%= image_tag comment.user.image.source(:header) %>
<%= link_to comment.user.name, comment.user %></span>
Posted <%= time_ago_in_words(comment.created_at) %> ago
</span>
<p class="mleft20 mtop10"><%= comment.content %></p>
<% if signed_in? %>
<% if current_user.id == comment.user_id or current_user.has_role? :admin %>
<%= link_to 'Edit', polymorphic_path([ comment.commentable, comment], :action => :edit),
:class => 'btn btn-mini mtop5 mleft10' %>
<%= link_to 'Delete', [comment.commentable, comment],
:confirm => 'Are you sure?',
method: :delete,
:class => 'btn btn-mini mtop5' %>
<% end %>
<% end %>
</div>
<% end %>
<% if comment.show == false %>
<p>A comment by <%= comment.user.name %> has been hidden by <%= #request.user.name %></p>
<% if current_user == #request.user or current_user.has_role? :admin %>
<%= simple_form_for [#commentable, comment] do |f| %>
<div class ="">
<%= f.input :show, :as => :hidden, :input_html => { :value => true } %>
<%= f.submit "Show Comment", :class => 'btn btn-mini btn-success' %>
</div>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<%= render "comments/form" %>
use comment.commentable.user to access the author of your post.
Everything after #posts should persist throughout the application (Like the left sidebar you find on Facebook).
def index
#title = "Posts"
default_order = "content_changed_at DESC"
params[:order_by] ||= default_order
#posts = current_user.subscribed_posts.paginate(:page => params[:page],
:per_page => 5,
:order => params[:order_by])
#subscribed_tags = current_user.subscribed_tags
#recent_posts = current_user.posts.limit(5).order("created_at DESC")
#tags= Tag.limit(20).order("ID asc")
#user = current_user
end
views/layout/_sidebar.html.erb:
<div class="user-profile">
<% avatar = image_tag(current_user.avatar.url(:thumb), :class => "authenticated-avatar") %>
<%= link_to avatar, "/users/#{current_user.id}" %>
<%= link_to "#{current_user.username}", "/users/#{current_user.id}", :class => "authenticated-username" %>
</div>
<%= form_for(#user, :remote => true) do |f| %>
<div class="field">
<%= f.label :subscribed_tag_names %><br />
<%= f.autocomplete_field :subscribed_tag_names, autocomplete_tag_name_posts_path, :"data-delimiter" => ' ', :class => "autocomplete_field" %>
</div>
<div class="actions">
<%= f.submit :class => "user_subscribed_tag_names_submit" %>
</div>
<% end %>
<div class="user-subscribed_tags">
<% #subscribed_tags.each do |subscribed_tag| %>
<%= link_to "#{subscribed_tag.name}(#{subscribed_tag.posts.count})", unsubscribe_tags_path(:unsubscribed_tag_name => subscribed_tag.name) %>
<% end %>
</div>
<div class="user-recent-posts">
<h4>Recent Posts</h4>
<ul>
<% #recent_posts.each do |recent_post| %>
<li><%= link_to recent_post.title, recent_post %></li>
<% end %>
</ul>
</div>
<div class="top-tags">
<% #tags.each do |tag| %>
<span class="tag-name"><%= tag.name %></span>
<span class="tag-count"><%= tag.posts.count %></span>
<% end %>
</div>
<% end %>
Where should I place the code in the controller if I want them to persist throughout the application? (I would like to see some example code if possible).
If it persists throughout the application, i.e. for every actions of all controllers, you might place it in a before_filter in the application controller.
Add a before_filter to the application controller as :
before_filter :find_recent_posts_and_tags
And define it (as private) :
private
def find_recent_posts_and_tags
# define your instance variables
end
More about filters here and here.
I have three partials that I'd like to consolidate into one. They share the same collection, but each gets passed its own :local variable. Those variables are used for specific Models, so as a result, I have three different calls to the partial and three different partials.
Here's the repetitive code:
<% for email in campaign.emails %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<% #contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->
<!-- render the information for each contact -->
<%= render :partial => "contact_email",
:collection => #contacts,
:locals => {:email => email} %>
<% end %>
Calls in this Campaign:
<% for call in campaign.calls %>
<h4><%= link_to call.title, call %> <%= call.days %> days</h4>
<% #contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->
<!-- render the information for each contact -->
<%= render :partial => "contact_call",
:collection => #contacts,
:locals => {:call => call} %>
<% end %>
Letters in this Campaign:
<% for letter in campaign.letters %>
<h4><%= link_to letter.title, letter %> <%= letter.days %> days</h4>
<% #contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->
<!-- render the information for each contact -->
<%= render :partial => "contact_letter",
:collection => #contacts,
:locals => {:letter => letter} %>
<% end %>
An example of one of the partials is as follows:
<
div id="contact_email_partial">
<% if from_today(contact_email, email.days) < 0 %>
<% if show_status(contact_email, email) == 'no status'%>
<p> <%= full_name(contact_email) %>
<% unless contact_email.statuses.empty?%>
(<%= contact_email.statuses.find(:last).status%>)
<% end %>
is <%= from_today(contact_email,email.days).abs%> days overdue:
<%= do_event(contact_email, email) %>
<%= link_to_remote "Skip Email Remote",
:url => skip_contact_email_url(contact_email,email),
:update => "update-area-#{contact_email.id}-#{email.id}" %>
<span id='update-area-<%="#{contact_email.id}-#{email.id}"%>'> </span>
<% end %>
<% end %>
</div>
And here is the other partial...similar, eh? Need help making it DRY!
<% if (from_today(contact_call, call.days) < 0) %>
<% if show_status(contact_call, call) == 'no status'%>
<p> <%= full_name(contact_call) %>
<% unless contact_call.statuses.empty?%>
(<%= contact_call.statuses.find(:last).status%>)
<% end %>
is <%= from_today(contact_call,call.days).abs%> days overdue:
<%= do_event(contact_call, call) %>
<%= contact_call.phone %>
</p>
<% end %>
<% end %>
For the local variables, the key you select when passing the locals hash is the name the variable will have in the partial. So you could do this in your repetitive code:
<% for email in campaign.emails %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<% #contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->
<!-- render the information for each contact -->
<%= render :partial => "contact",
:collection => #contacts,
:locals => {:objt => email,
:url_method => "skip_#{class}_url".to_sym } %>
<% end %>
Calls in this Campaign:
<% for call in campaign.calls %>
<h4><%= link_to call.title, call %> <%= call.days %> days</h4>
<% #contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->
<!-- render the information for each contact -->
<%= render :partial => "contact",
:collection => #contacts,
:locals => {:objt => call,
:url_method => "skip_#{class}_url".to_sym } %>
<% end %>
Letters in this Campaign:
<% for letter in campaign.letters %>
<h4><%= link_to letter.title, letter %> <%= letter.days %> days</h4>
<% #contacts= campaign.contacts.find(:all, :order => "date_entered ASC" )%> <!--contacts collection-->
<!-- render the information for each contact -->
<%= render :partial => "contact",
:collection => #contacts,
:locals => {:objt => letter,
:url_method => "skip_#{class}_url".to_sym } %>
<% end %>
Since in the partial we don't really know the class of the object we need to render, I'm creating a symbol representing the method you need to call to build the URL. We'll invoke this in the partial using a bit of send magic.
Notice the partial has been renamed simply "contact" (_contact.html.erb), and I'm assigning email, call and letter in turn to a variable called objt, that will be accessible from the partial.
For the partial (_contact.html.erb):
<div id="contact_partial">
<% if from_today(contact, objt.days) < 0 %>
<% if show_status(contact, objt) == 'no status'%>
<p> <%= full_name(contact) %>
<% unless contact.statuses.empty?%>
(<%= contact.statuses.find(:last).status%>)
<% end %>
is <%= from_today(contact,objt.days).abs%> days overdue:
<%= do_event(contact, objt) %>
<%= link_to_remote "Skip Email Remote",
:url => send(url_method,contact,objt),
:update => "update-area-#{contact.id}-#{objt.id}" %>
<span id='update-area-<%="#{contact.id}-#{objt.id}"%>'> </span>
<% end %>
<% end %>
</div>
Notice how, instead of calling the skip_email_url method directly, we use "send" to invoke a method named in the first parameter (in this case, url_method, a local passed from the invoking view), and further, we pass this method the additional parameters (in this case, contact and objt). Just make sure the method named in url_method does effectively take two parameters.