Parsing a file after upload in Rails - ruby-on-rails

I want to be able to upload an xml file to my rails app and have it then parse through the file and generate ActiveRecord entries based off the contents.
I've put together a parser using nokogiri that works fine in vanilla Ruby, so no problem there, but for some reason Rails seems to want to run through it before I've even hit the upload button to supply it with the file. As soon as I try to go to the upload page, I receive this
TypeError in FoobarsController#create
no implicit conversion of nil into String
what I've got looks like this:
app/models/foobar.rb
class Foobar < ActiveRecord::Base
attr_accessible :xml_file
has_attached_file :xml_file
before_save :parse_file
def parse_file
require 'xml_parser'
myFile = xml_file.queued_for_write[:original]
fooParser = Nokogiri::XML::SAX::Parser.new(FoobarParser.new)
fooParser.parse(File.open(myFile,mode='r'))
end
end
app/controllers/foobars_controller.rb
class FoobarsController < ApplicationController
def new
#foobar = Foobar.new
end
def create
#foobar = Foobar.new(params[:foobar])
if #foobar.save
redirect_to #foobar
else
render 'new'
end
end
end
app/views/foobars/new.html.erb
<h1>Upload foobar</h1>
<div class="row">
<div class="span6 offset3 center">
<%= form_for(#foobar) do |f| %>
<%= f.file_field :xml_file %>
<%= f.submit %>
<% end %>
</div>
</div>
I guess it's a problem with the controller's create method, but I was basing my code on this so I thought it should do what I want. Any help would be appreciated.

Related

redirect_to an instance variable does not work in Rails 4

I'm completely new to Rails. Just started from *http://guides.rubyonrails.org/getting_started.html
While trying I'm stuck with redirect_to problem like below error -
NoMethodError in SwversionsController#create
undefined method `sw_versions_url' for #<SwversionsController:0xa38d158>
Rails.root: C:/Sites/edisonprojectmanagement
Application Trace | Framework Trace | Full Trace
app/controllers/swversions_controller.rb:19:in `create'*
This error occurs after clicking submit button. I found the model is working fine and data is saved in my postgresql database.
I think problem is with my redirect_to method.
My SwVersionsController code looks like -
class SwversionsController < ApplicationController
def index
end
def show
#swversion = SwVersion.find(params[:id])
end
def new
end
def create
#swversion = SwVersion.new(swversion_params)
#swversion.save
redirect_to #swversion
end
private
def swversion_params
params.require(:swversion).permit(:sw_version, :description)
end
end
And the new.html.erb code is -
<h1> New SW versions </h1>
<%= form_for :swversion, url: swversions_path do |f| %>
<p>
<%= f.label :sw_version, "SW Version" %> <br>
<%= f.text_field :sw_version %>
</p>
<p>
<%= f.label :description, "Description" %> <br>
<%= f.text_area :description %>
</p>
<p>
<%= f.submit "Submit" %>
</p>
<% end %>
My routes.rb is pretty simple
Rails.application.routes.draw do
resources :swversions
get 'welcome/index
Someone please help me to banish this problem
undefined method `sw_versions_url' for SwversionsController:0xa38d158
Naming Conventions:
Rails is very strict in naming and for a good reason. Rails expects the class names to be CamelCase and the methods/variables to be snake_case.
Your model class name is SwVersion, so the method/variable name should be sw_version not swversion. So you should change swversion to sw_version in your entire code
You should also change the controller class name to SwVersionsController.
And also if you have resources :swversions in routes.rb, then you should change it to resources :sw_versions
More about naming conventions here
Try this code:
redirect_to sw_version_path(#swversion)

Interpolating data in a rails view

I'm a beginner at rails and thus far interplating data in views has been pretty straight forward. I've been introduced to something slightly new as far as how the controllers are setup and as a result I'm not sure how one would go about getting the data to present in the view.
First controller
class PagesController < ApplicationController
def index
#guestbook_entry = GuestbookEntry.new
render "welcome"
end
end
Second controller
class GuestbookEntriesController < ApplicationController
def create
GuestbookEntry.create(guestbook_entry_params)
redirect_to root_path, notice: "Thank you for your entry."
end
private
def guestbook_entry_params
params.require(:guestbook_entry).permit(:body)
end
end
And here is the welcome.html.erb
<h1>Welcome to My Guestbook</h1>
<br>
<%= image_tag("under_construction.gif") %>
<div id="guestbook-entries">
<p>Guestbook Entries:</p>
<ul>
</ul>
</div>
<%= form_for #guestbook_entry do |f| %>
<%= f.label :body, "Guestbook Entry:" %>
<%= f.text_area :body %>
<%= f.submit "Submit" %>
<% end %>
So it wants me to iterate through all the entries and display them on a welcome page that's located in view/pages/welcome.html.erb.
Up to this point I guess I've only been doing basic simple rails applications where the view corresponded with the controller, and followed the typical CRUD setup, where index would hold the #xxx = Xxxx.all and new/create would handle #xxx = Xxxx.new/create/build. I thought I could simply move the PageController's index action to create/new and do
def index
#guestbook_entry = GuestbookEntry.all
render "welcome"
end
To satisfy the test (it looks for render welcome in the index action)
This seems weird but again I admit, I'm a beginner.
If you want to list all the guest book entries on your root page you would do something like:
class PagesController < ApplicationController
def index
#guestbook_entry = GuestbookEntry.new
#guestbook_entries = GuestbookEntry.limit(10).all
render "welcome"
end
end
And in your view you would list them like:
<% if #guestbook_entries.any? %>
<div id="guestbook-entries">
<p>Guestbook Entries:</p>
<% #guestbook_entries.each do |entry| %>
<ul>
<li class="entry"><%= h(entry.body) %></li>
</ul>
<% end %>
</div>
<% end %>
The rest of you application is correct - you should be creating entries in GuestbookEntriesController#create. In many real life applications then the functionality of the standard new and edit actions can actually be a totally different controller.

Rails: Why isn't the 'create' action saving the newly created Quiz instance?

My form gets passed a 'new' Quiz (not saved to the database). My form partial looks like this:
<%= form_for(#quiz) do |f| %>
<p>
<%= f.check_box(:answer1) %>
<%= f.check_box(:answer2) %>
<%= f.check_box(:answer3) %>
<%= f.check_box(:answer4) %>
<%= f.check_box(:answer5) %>
<%= f.check_box(:answer6) %>
<%= f.check_box(:answer7) %>
<%= f.check_box(:answer8) %>
</p>
<p>
<%= f.submit("Get my results!") %>
</p>
<% end %>
Here is my QuizzesController#create action:
def create
#results = Quiz.create(post_params) #from private method
if #results.save
redirect_to results_path
else
#error handle here
end
end
...which gets triggered when the user clicks 'get my results' on my quiz form. And the post_params method looks like this:
def post_params
params.require(:quiz).permit(:id, :user_id, :answer1, :answer2, :answer3, :answer4, :answer5, :answer6, :answer7, :answer8) #add other attributes here
end
My results/index.html.erb looks like this:
<div class="container">
<!-- Example row of columns -->
<div class="row">
<h1>Results</h1>
<p><%= #results.inspect %></p>
</div>
</div>
But that 'inspected' Quiz instance returns 'nil' for all the answers1, answers2 etc attributes. Any idea why that would be? Is there something I'm NOT doing to save the user's answers to the database?
The reason it shows nil is because you are not setting the variable.
After creating and saving, you redirect to results_path and the variable #results does not persist during a redirect. Without seeing the full code, I'll have to guess at your naming conventions but there are two ways to do this.
1) If you want to redirect to the index then in the code for your index action, you can set the variable:
#results = Quiz.last
This is easy to work with in development because you are the only user and this will always return the last quiz you created. Not so great in production.
2) The alternative is to redirect to the show action for that quiz.
def create
#results = Quiz.new(post_params)
if #results.save
redirect_to result_path(#results)
else
# error handle here
end
end
Again, I have had to guess that result_path is the correct path. Without seeing the full routes file, I cannot be sure but you can rename accordingly if necessary.

Rails: form_for active record object complaining about no index path

I have a survey model that has no index page. I only want an edit view for this model, but it seems like rails wont let me do that. It complains undefined method surveys_path when I try to use form_for(#survey). Is there anyway around doing this without creating an empty index route/view.
Here is my survey controller so far
class SurveysController < ApplicationController
def show
#survey = Survey.find(params[:id])
end
def edit
#survey = Survey.new
job = Job.find(params[:id])
#survey.job_id = job.id
authorized_user = job.user
unless !is_runner?(current_login) && current_login.id == authorized_user.id
redirect_to jobs_path
end
end
def update
#survey = Survey.new(params[:survey])
end
end
And here is the form partial being rendered in the edit.html.erb
<%= form_for(#survey) do |f| %>
<div class="field">
<%= f.label :speed %><br />
<%= f.text_field :speed %>
</div>
<div class="field">
<%= f.label :service %><br />
<%= f.text_field :service %>
</div>
<div class="field">
<%= f.label :suggestion %><br />
<%= f.text_area :suggestion %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
It shouldn't be asking about surveys_path just for defining a form, but there are some oddities in your controller code that could be causing you grief.
Your edit action should, in the surveys controller of a RESTful app, be using that params[:id] to find an existing survey, why is it finding a job? It should be doing the same as your show action.
Your survey object in the survey edit method is new and unsaved, therefore the form builder will generate a form pointing to a create action rather than an update action. Form builder will generate an edit form only for records that are persisted?
Have you defined the routes for this model? You should have something like the following in your routes file:
resources :surveys, :except => [:index] # will create all rest routes for survey model except for an index route.
You get this error if you mistakenly run your model/table as plural instead of singular. You probably ran something like this:
rails generate model surveys
Instead of this:
rails generate model survey
So, if you run:
rake routes
You'll probably see that all of your routes have an 's' ie. surveys_new.
So, rails is confused. You need to rename your table in a migration.
class ChangePluralNametoSingularforSurveys< ActiveRecord:Migration
def change
rename_table :surveys, :survey
end
end
Then you'll have to rename your files (surveys.rb, etc) manually.
For me, the issue was nested routes. If your object has a parent and its routes are nested, you'll need to do something like:
<%= form_for([#category, #survey]) do |f| %>
...
<% end %>

Rails, STI and 'becomes' - f.object.errors not showing in view

My question is: why doesn't .becomes pass errors over to the new object? Isn't this the expected behaviour?
I have the following single table inheritance classes in a rails app:
class Document < ActiveRecord::Base
validates :title, :presence => true
end
class LegalDocument < Document
end
class MarketingDocument < Document
end
I want to use the same controller and set of views to edit both LegalDocuments and MarketingDocuments, so I am using DocumentsController < ApplicationController with the following edit and update actions:
def edit
#document = Document.find(params[:id])
end
def update
#document = Document.find(params[:id])
if #document.update_attributes(params[:document])
redirect_to documents_path, :notice => "#{t(:Document)} was successfully updated."
else
render :action => "edit"
end
end
and the following in my edit view:
<%= form_for #document.becomes(Document) do |f| %>
<% if f.object.errors.present? %>
<div class="error_message">
<h4><%= pluralize(f.object.errors.count, 'error') %> occurred</h4>
</div>
<% end %>
<div>
<%= f.label :title %>
<%= f.text_field :title, :class => "inputText" %>
</div>
<%= f.submit %>
<% end %>
If title is filled in, the documents update correctly.
If title is left blank, I am returned to the edit view BUT the error is not shown.
From debugging, I know it's not showing because f.object.errors is nil. However, from debugging, I also know #document.errors is NOT nil, as expected.
My question is: why doesn't .becomes pass errors over to the new object? Isn't this the expected behaviour?
Yes, I noticed that too.
Just change f.object.errors.present? by #document.errors.any? ( or #document.errors.present?).
If you really want to use f.object.errors.present?, write becomes in the controller (both edit and update actions) instead of in the view:
def edit
#document = Document.find(params[:id]).becomes(Document)
end
def update
#document = Document.find(params[:id]).becomes(Document)
# ....
end
And then in the view:
<%= form_for #document do |f| %>
<% if f.object.errors.present? %>
<p>Errrorsss....</p>
<% end %>
#.....
It happens because the url of the form is build according to #document.becomes(Document) (=> PUT document/:id) but #document is created according to its "true" class (a subclass of Document).
If you have pry (highly recommended), write:
def update
#document = Document.find(params[:id])
binding.pry
# ...
end
And then inspect #document. You will see that #document is an instance of LegalDocument or the other subclass even though you called #document.becomes(Document) in your form.
So in final f.object and #document are not the same.
This explains why you can't see f.object.errors when validation fails.
Edit
The 'best way' to deal with STI and form is NOT to use becomes:
<= form_for #document, url: { controller: 'documents', action: 'update' }, as: :document do |f| %>
<% if #document.errors.any? %>
# or if f.object.errors.any?
# handle validation errors
<% end %>
# your form...
<% end %>
This enables you:
to have only one controller (documents_controller)
to have only one resource (resources :documents)
it keeps trace of your subclasses: a LegalDocument will be store as a LegalDocument. No conversion: You don't have to store its class before the conversion to Document and then reassign it later.
Plus, your subclass is available in your form, so you can (let's imagine) build a select for the type.
your controller looks cleaner: #document = Document.find params[:id] nothing more. Just like a classic resource.
If you want to share this form across different actions(typically edit and new):
<%= form_for #document, url: { controller: 'media_files', action: action }, as: :media_file do |f| %>%>
# edit.html.erb
<%= render 'form', action: 'update' %>
# new.html.erb
<%= render 'form', action: 'create' %>
Pretty much it is a bug and it should work as you initially expected. The following patch to address the issue looks like it was pulled back in October
https://github.com/lazyatom/rails/commit/73cb0f98289923c8fa0287bf1cc8857664078d43

Resources