nested resources with form_for losing first id - ruby-on-rails-3

This is a strange error that has really got me bugged. First, some background.
I have the following nested resources in my config/routes.rb:
scope :requirements => { :protocol => 'https' } do
resource :user
resources :orgs do
resources :members
resources :events
resources :levels
resources :attendances
end
resources :sessions, :only => [:new, :create, :destroy]
end
Then, in app/controllers/levels_controller.rb I have:
def edit
#org = Org.find(params[:org_id])
#level = OrgLevel.find(params[:id])
end
def update
#level = OrgLevel.find(params[:id])
if #level.update_attributes(params[:level])
flash[:success] = "Level details updated"
redirect_to #level
else
render 'edit'
end
end
Finally, in app/views/levels/edit.html.erb, I have:
<% provide(:title, "Edit #{#level.name} for #{#org.name}") %>
<div class="hero-unit">
<h2>Edit "<%= #level.name %>" membership level for <%= #org.name %></h2>
<div class="row">
<div class="span6 offset3">
<%= form_for [#org, #level], :url => org_level_path do |f| %>
<%= render 'shared/error_messages' %>
<table class="editor">
<tr>
<td class="label_x">
<%= f.label :name %>
</td>
<td colspan="3">
<%= f.text_field :name %>
</td>
</tr>
</table>
<% end %>
</div>
</div>
</div>
The result of calling https://spot-macbook.local/orgs/55/levels/162/edit is normal, but clicking "Save Changes" results in a redirection to https://spot-macbook.local/orgs/162/levels/162 and the following error:
ActiveRecord::RecordNotFound in LevelsController#show
Couldn't find Org with id=162
Rails.root: /Users/ogod/Projects/rails_projects/nom_de_joye_app
Application Trace | Framework Trace | Full Trace
app/controllers/levels_controller.rb:71:in `correct_user'
Request
Parameters:
{"requirements"=>{"protocol"=>"https"},
"org_id"=>"162",
"id"=>"162"}
Note that the org_id has changed to "162" instead of "55". What am I doing wrong?

Doh!
Five seconds after I post this question, I realised there error and corrected it.
The original has the update method with the following:
redirect_to #level
This should be:
redirect_to org_level_path(#org, #level)
Such a simple error, but I was looking in the wrong place!

Related

undefined method `model_name' for Mailer - Rails 5

I am trying to make a contact me form in Rials 5 with the built in Mail features.
I put it together a lot like a regular controller and views with a Mailer as the model. But when I have it all written out I get the error:
undefined method `model_name' for #ContactMailer
Contact is the name of the Mailer.
The error comes up when I try to view the new.html.erb page with rails server running. It looks like this:
Screen Shot
Here is the code I am working with:
contacts_controller.rb
class ContactsController < ApplicationController
def new
#contact = ContactMailer.new
end
def create
#contact = ContactMailer.new(params[:name, :email, :message])
if #contact.deliver
flash[:success] = "Your message has been sent."
else
flash[:error] = "There was a problem sending your message."
render 'new'
end
end
And
new.html.erb
<%= form_for #contact do |f| %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control', required: true %></div>
<div class="form-group">
<%= f.label :message %>
<%= f.text_area :message, class: 'form-control' %>
</div>
<%= f.submit "Send", class: 'btn btn-default' %>
<% end %>
There is also the generated Mailer and views, but I have not changed anything with those.
From what I have been able to research the error could be that I have not initialized the #contact variable, but I believe that I have. And now I am lost. Any help would be appreciated.
As additional information:
My plan is for this contact form to send an email using send grid on Heroku.
You are trying to build a mailer.Thats not how they are used. They are not models.
Instead you would do something like:
ContactMailer.some_method_you_added_to_your_mailer(params[:name, :email, :message]).deliver_now
You don't use new on them.
Suggest you read over http://guides.rubyonrails.org/action_mailer_basics.html .

ActiveAdmin - Pass locals to form

In my photos.rb file I have:
ActiveAdmin.register Photo do
form :partial => 'admin/forms/photo', :locals => { :events => Event.all }
end
In my _photo.html.erb file I want to be able to access the value of events, but it doesn't seem to be detectable. How can I do this?
As requested in a comment, here is my form:
<% if events.any? %>
<%= form_for [:admin, #photo], :validate => true do |f| %>
<!-- insert a bunch of standard field divs here -->
<div class="actions" style="margin-top: 20px">
<% if #photo.new_record? %>
<%= f.submit 'Create photo' %>
<% else %>
<%= f.submit 'Update photo' %>
<% end %>
</div>
<% end %>
<% else %>
<div style="font-size: 14px">
You cannot add photos until you have added at least one event.
</div>
<% end %>
The error message I am getting is around the events.any? line:
Completed 500 Internal Server Error in 108ms
ActionView::Template::Error (undefined local variable or method `events' for #<#<Class:0x007fdb8e841b80>:0x007fdb8a93e7a8>)
form do |f|
f.render partial: 'admin/forms/photo', locals: { f: f, events: Event.all }
end
Note that you will need to remove form_for, semantic_form_for, active_admin_form_for etc from your partial, since it will be covered by the form do part in the admin/photos.rb, otherwise there will be two nested forms.
Example partial:
# app/views/admin/forms/_photo.html.arb
if events.any?
f.inputs do
f.input :title, label: 'Etc'
f.input :file
end
f.actions
else
f.div 'You cannot add photos until you have added at least one event.',
style: 'font-size: 14px'
end
form do |f|
render partial: 'admin/forms/photo', locals: { value: 'random_data' }
end
You can use f.render or just render.
Note that partial: is required to send locals to the form
Example Partial
#app/views/admin/forms/_photo.html.arb
active_admin_form_for [:admin, resource] do |f|
f.semantic_errors *f.object.errors.keys
f.inputs do
f.input :name
f.input :random if (value == 'random_data')
end
f.actions
end
another thought on this, if you want to check only if there are any Events in db you can make any call directly on Class:
<% if Event.any? %>
do this
<% else %>
do that
<% end %>
without sending variables to partial, the above code result in:
2.0.0p0 :004 > Event.any?
(0.6ms) SELECT COUNT(*) FROM "events"
=> true
and leave the ActiveAdmin partial without locals:
ActiveAdmin.register Photo do
form :partial => 'admin/forms/photo'
end

Create a select from existing or create new in a form Rails 3

So I'm trying to set a name attribute of organizations and create a new organization or choose from a previously existing organization from the same form.
I've tried to follow Ryan Bates' railscast on the topic here: http://railscasts.com/episodes/57-create-model-through-text-field
I have also tried numerous solutions from stack. However, I can't quite seem to get it to run (that and I have a validation that does not recognize the virtual attribute I'm using)
so my organizations model:
class Organization < ActiveRecord::Base
has_many :materials
has_many :users
has_and_belongs_to_many :causes
has_and_belongs_to_many :schools, :join_table => 'organizations_schools'
####The following line has been edited ####
attr_accessible :name, :unlogged_books_num, :id, :new_organization_name
attr_accessor :new_organization_name
before_validation :create_org_from_name
validates_presence_of :name
def self.assign_school_to_organization(org, school)
orgschool = OrganizationsSchool.create(:organization_id=> org.id, :school_id=> school[0])
end
def create_org_from_name
create_organization(:name=>new_organization_name) unless new_organization_name.blank?
end
end
I have also tried the create_org_from_name as the following:
def create_org_from_name
self.name = new_organization_name
end
And this does not change the name to the organization name before validating or saving the instance.
I have also tried to change the before_save to before_validation, and that has not worked
My controller for organization (I also tried to change this in create)
def create
respond_to do |format|
#organization = Organization.new(params[:organization])
#organization.name = #organization.new_organization_name unless #organization.new_organization_name.blank?
if #organization.save
#school = params[:school]
Organization.assign_school_to_organization(#organization, #school)
format.html { redirect_to #organization, notice: 'Organization was successfully created.' }
format.json { render json: #organization, status: :created, location: #organization }
else
format.html { render action: "new" }
format.json { render json: #organization.errors, status: :unprocessable_entity }
end
end
end
And finally, I have what my form is doing currently:
<%= form_for(#organization) do |f| %>
<% if #organization.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#organization.errors.count, "error") %> prohibited this organization from being saved:</h2>
<ul>
<% #organization.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% #schools = School.all %>
<% #organizations = Organization.all %>
<div class="field">
<%= f.label 'Organization Name' %><br />
<%= f.collection_select(:name, #organizations, :name, :name, :prompt=>"Existing Organization") %>
Or Create New
<%= f.text_field :new_organization_name %>
</div>
<div class="field">
<%= f.label :unlogged_books_num %><br />
<%= f.number_field :unlogged_books_num %>
</div>
<div class="field">
<%= f.label 'School' %><br />
<% school_id = nil %>
<%= collection_select(:school, school_id, #schools, :id, :name) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
==================================EDIT============================================
So currently, when I try to make an organization with something only written in the virtual text field, My log tells me the following:
Processing by OrganizationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"igoefz8Rwm/RHrHLTXQnG48ygTGLydZrzP4gEJOPbF0=", "organization"=> {"name"=>"", "new_organization_name"=>"Virtual Organization", "unlogged_books_num"=>""}, "school"=>["1"], "commit"=>"Create Organization"}
Rendered organizations/_form.html.erb (7.1ms)
Rendered organizations/new.html.erb within layouts/application (8.0ms)
Completed 200 OK in 17ms (Views: 12.2ms | ActiveRecord: 1.0ms)
================================EDIT 2============================================
So this is what I get from the rails console if I try to create a new organization running this command: Organization.create(:new_organization_name=>"Virtual Organization", :unlogged_books_num=>"3")
irb(main):001:0> Organization.create(:new_organization_name=>"Virtual Organization", :unlogged_books_num=>"3")
(0.1ms) BEGIN
(0.1ms) ROLLBACK
=> #<Organization id: nil, name: nil, unlogged_books_num: 3, created_at: nil, updated_at: nil>
If the function of create_org_from_name is self.name = new_organization_name, then the result of the same command from the console is blank:
irb(main):002:1> Organization.create(:new_organization_name=>"Virtual Organization", :unlogged_books_num=>"3")
irb(main):003:1>
You need:
before_validation :create_org_from_name
and
def create_org_from_name
self.name = new_organization_name if not new_organization_name.blank?
end
You don't want to do a create in your before_validation method.

Rails - Attaching image through paperclip on nested fields causes unwanted/unavoidable redirect_to

I have a form in my project for a model Artworks where I am using fields_for for a resource Photo where Artworks has_many :photos.
The Photo model uses paperclip to attach images.
All my forms are AJAX based, meaning the page should never be reloaded, and I shouldn't be using redirect_to anywhere in my controllers (and I'm not).
When I include the fields_for in my form with the paperclip file_field, if I create or update the photo when I submit the form, I get the following error:
Template is missing
Missing template artworks/show, application/show with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :coffee]}. Searched in: * "/home/david/rails/piml_artwork/app/views" * "/home/david/.rvm/gems/ruby-1.9.3-p194/gems/devise-2.1.2/app/views"
The destroy image checkbox functions as expected. Updating the rest of the details on the form and submitting it functions as expected, just returning an AJAX response and filling out the page.
My artworks controller is as follows:
class ArtworksController < ApplicationController
before_filter :load
authorize_resource
respond_to :html, :json, :js
def load
end
def show
#artwork = Artwork.find(params[:id])
end
def index
#artworks = Artwork.all
end
def new
#artwork = Artwork.new
#artwork.photos.build unless #artwork.photos.count > 0
end
def create
#artwork = Artwork.new(params[:artwork])
if #artwork.save
flash[:notice] = "Artwork saved"
#artworks = Artwork.all
respond_with #artwork
end
end
def edit
#artwork = Artwork.find(params[:id])
#artwork.photos.build unless #artwork.photos.count > 0
end
def update
#artwork = Artwork.find(params[:id])
if #artwork.update_attributes(params[:artwork])
flash[:notice] = "Artwork updated"
#artworks = Artwork.all
respond_with #artwork
end
end
def destroy
#artwork = Artwork.find(params[:id])
#artwork.destroy
flash[:notice] = "Artwork destroyed"
#artworks = Artwork.all
end
end
The form causing the strife is this:
<%= form_for( #artwork, remote: true, mutlipart: true ) do |f| %>
<%= render 'layouts/error_messages', object: f.object %>
<div id="artwork-title">TITLE: <%= f.text_field :title %></div>
<div id="artwork-title-underline"></div>
<div id="artwork-show-picture"><%= image_tag #artwork.photos.first.photo.url(:medium) unless #artwork.photos.first.new_record? %>
<div id="artwork-picture-upload">
<%= f.fields_for :photos, remote: true, multipart: true do |p| %>
<% if p.object.new_record? %>
UPLOAD PHOTO
<%= p.file_field :photo, value: 'Upload Image' %>
<% else %>
REPLACE PHOTO
<%= p.file_field :photo, value: 'Replace Image' %>
<br />
<br />
<%= p.check_box :_destroy %>
<%= p.label :_destroy, 'Delete Image' %>
<% end %>
<% end %>
</div>
</div>
<div id="artwork-public">
ARTIST<br />
<%= f.select :artist_id, Artist.select_list, include_blank: "SELECT ARTIST" %><br />
<br />
DATE OF WORK <br />
<%= f.text_field :date_of_work %><br />
<br />
<div id="artwork-modify-buttons">
<%= f.submit %>
</div>
<% end %>
My JS file for update is:
<% if !#artwork.errors.any? %>
$("#facebox .content").html("<%= escape_javascript(render(partial: "/artworks/show")) %>");
$("#artworks-col").html(" <%= escape_javascript(render(partial: "/admin/artworks_list" )) %>")
<% else %>
$("#facebox .content").html("<%= escape_javascript(render(partial: "/artworks/form")) %>");
<% end %>
As it turns out, the issue I was having is that by design, you cannot upload files through AJAX.
After a few days of toiling with this, I found a gem that fixes it all incredibly easily; Remotipart
The entirety of what I had to do to fix it was:
Add remotipart to my gem file gem 'remotipart'
Add //= require jquery.remotipart to my app/assets/javascripts/application.js file
bundle install
That fixed it. Images are now uploading with what 'looks like' AJAX, but is actually using an iframe to do the work.

No Method Error for "comments", can't find and correct the nil value to make the comment post

I'm relatively new to rails and am trying to pull off my first polymorphic association with comments.
I am running rails 3.2.3
Edit - When I try to post a comment, my log is returning this error:
Started POST "/comments" for 127.0.0.1 at 2012-05-20 13:17:38 -0700
Processing by CommentsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"SOLcF71+WpfNLtpBFpz2qOZVaqcVCHL2AVZWwM2w0C4=", "comment"=>{"text"=>"Test this comment"}, "commit"=>"Create Comment"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 101 LIMIT 1
Completed 500 Internal Server Error in 126ms
NoMethodError (undefined method `Comment' for nil:NilClass):
app/controllers/comments_controller.rb:13:in `create'
I have tried out many different solutions offered on SO and elsewhere, including the answer from Jordan below, due, I'm sure, to my own inexperience, but have been unable to resolve the error.
The trace calls out line 13 in the Comments Controller and I commented after that line below to mark the error:
class CommentsController < ApplicationController
def index
#commentable = find_commentable
#comments = #commentable.comments
end
def new
#post = Post.find(params[:post_id])
end
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment]) #<<<<LINE 13
if #comment.save
flash[:notice] = "Successfully created comment."
redirect_to :id => nil
else
render :action => 'new'
end
end
private
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
end
Posts Controller:
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #post }
end
end
Comment template (in post show)
<ul id="comments">
<% if #comments %>
<h2>Comments</h2>
<% #comments.each do |comment| %>
<li><%= comment.text %></li>
<% end %>
<% else %>
<h2>Comment:</h2>
<% end %>
</ul>
<%= simple_form_for [#commentable,Comment.new], :html => { :class => 'form-horizontal', :multipart => true } do |f| %>
<fieldset>
<%= f.input :text %>
Upload Photo <%= f.file_field :photo %>
</fieldset>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
</div>
<% end %>
Post show:
<p id="notice"><%= notice %></p>
<div class="row">
<div class="span2 offset1">
<%= image_tag #post.photo.url(:show) %>
</div>
<div class="span5">
<h1><%= #post.title %></h1>
<p><%= #post.index_text.html_safe %></p>
<p><%= #post.show_text.html_safe %></p>
<%= render "comments/comment" %>
<%= render "comments/form" %>
<% if can? :update, #course %>
<%= link_to 'Edit Post', edit_post_path(#post), :class => 'btn btn-mini' %>
<%= link_to 'Delete Post', #post,
confirm: 'Are you sure?',
method: :delete,
:class => 'btn btn-mini' %>
<%= link_to 'New Post', new_post_path, :class => 'btn btn-mini' %>
<% end %>
</div>
<nav class="span2 offset1">
<ul class="well">
<li>Category 1</li>
<li>Category 2</li>
</ul>
</nav>
</div>
<div class="row offset2">
<%= link_to 'Back to Posts', posts_path, :class => 'btn btn-mini' %>
</div>
Routes:
resources :posts, :has_many => :comments
resources :comments
It is probably something obvious that someone with more experience can resolve. Let me know if anything comes to mind. Brian
The problem is that #commentable is nil, which means that CommentsController#find_commentable is returning nil. I think your regular expression is sound, so that means one of two things is happening in find_commentable:
There aren't any keys in params that match your regex.
Your regex is matching but there aren't any records in the resulting table with the id in value.
Debug this as usual by inspecting params and the records in your database to make sure they look like you expect them to look.
The problem is your find_commentable method.
Here are the params passed to your CommentsController#create:
Started POST "/comments" for 127.0.0.1 at 2012-05-20 13:17:38 -0700
Processing by CommentsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"SOLcF71+WpfNLtpBFpz2qOZVaqcVCHL2AVZWwM2w0C4=", "comment"=>{"text"=>"Test this comment"}, "commit"=>"Create Comment"}
Here is your CommentsController#create:
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment]) #<<<<LINE 13
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
As you can see, find_commentable expects a param like xx_id (for example, comments_id) which it uses to search for an appropriate class (in case of comments_id, it will be Comment), otherwise it returns nil. Refer classify and constantize here.
Your params do not contain any such param. So, you always get a nil object.
Your find_commentable needs some rework. I think in case of nested_fields, it should be an expression like
/(.+)_attributes$/
instead of
/(.+)_id$/.
And you need to have
:accepts_nested_attributes_for :commentable
in your Comment model class.
I tried both of the above answers, but the problem continued.
I ended up consulting with a friend who suggested the following solution, which I like because it's more elegant than my original attempt and easier to read (for later, when I or someone else need to return to the code):
def find_commentable
if params[:post_id]
Post.find(params[:post_id])
#elsif params[:other_id]
# Other.find(params[:other_id])
else
# error out?
end
end
The commented out section will refer to other associations once I get them up and running.