I'm building a Rails 4 app with the paperclip gem in which users can upload a video file from a form with several other fields (uploader_first_name, uploader_last_name, uploader_email).
The paperclip installation went smoothly (although I needed to add gem 'protected_attributes'
) and I'm able to save file to the correct and create the corresponding records in the videos table, however all the non-paperclip fields are null and I'm not sure why.
class Video < ActiveRecord::Base
belongs_to :project
attr_accessible :file
has_attached_file :file, :url=>"/tmp/video_uploads", :path=>":rails_root/tmp/video_uploads"
end
class VideosController < ApplicationController
def save
#video = Video.create(params[:video])
if #video.save
redirect_to root_path
else
redirect_to "somewhere_else"
end
end
#I tried with this too...
#private
#def video_params
#params.require(:video).permit( :uploader_first_name, :uploader_last_name, :uploader_email)
#end
end
In the view...
<h2>Upload your video</h2>
<% myclass = {:class=>'form-control'} %>
<%= form_for(:video, :url => {:action=>'save', :controller=>'videos'}, :html=>{:class=>'upload-form-js', :multipart=> true} ) do |f| %>
<div class="form-group">
<%= f.label(:uploader_first_name, "First Name") %>
<%= f.text_field(:uploader_first_name, myclass) %>
</div>
<div class="form-group">
<%= f.label(:uploader_last_name, "Last Name") %>
<%= f.text_field(:uploader_last_name, myclass) %>
</div>
<div class="form-group">
<%= f.label(:uploader_email, "Email") %>
<%= f.text_field(:uploader_email, myclass) %>
</div>
<div class="form-group">
<label>Video File</label>
<input type="file" name="video[file]" id="video_file"/></span>
</div>
<%= f.submit('Upload', {class: 'btn btn-primary btn-block'}) %>
<% end %>
Update 1
I changed to this...
class VideosController < ApplicationController
def save
#video = Video.create( video_params )
if #video.save
redirect_to root_path
else
redirect_to "somewhere_else"
end
end
private
def video_params
params.require(:video).permit(:file, :uploader_first_name, :uploader_last_name, :uploader_email)
end
end
...and now I get this error:
Errno::EISDIR in VideosController#save
Is a directory - /Applications/MAMP/htdocs/clippo2/tmp/video_uploads
Don't I want the url/path to point to a directory?
Update 2
I changed the video model to...
has_attached_file :file, :url=>"/tmp/video_uploads/:basename.:extension", :path=>":rails_root/tmp/video_uploads/:basename.:extension"
...and now no errors, files get saved to the right directory and the corresponding fields added to the new row, but all other fields are still NULL (original problem).
Update 3
I turned on the debugger and here's what I see after I try to upload a file. It looks like a strong parameters error but I'm not sure how to fix it:
Started POST "/videos/save" for 127.0.0.1 at 2013-08-27 09:21:34 -0400
Processing by VideosController#save as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"es4wPqFr9xPBUFsbHQR/gAzofDC+ZwYsiiJ7RAQZUHk=", "video"=>{"uploader_first_name"=>"adsfasdf", "uploader_last_name"=>"asdfasdf", "uploader_email"=>"asdfasdf", "file"=>#<ActionDispatch::Http::UploadedFile:0x007fc4782e31e8 #tempfile=#<Tempfile:/var/folders/f2/jhv7xx0j3hlckhcg_jbv6hr00000gn/T/RackMultipart20130827-89636-188f0hs>, #original_filename="sample_iPod.m4v", #content_type="video/mp4", #headers="Content-Disposition: form-data; name=\"video[file]\"; filename=\"sample_iPod.m4v\"\r\nContent-Type: video/mp4\r\n">, "project_hashed_id"=>"1377539908"}, "commit"=>"Upload"}
{"utf8"=>"✓", "authenticity_token"=>"es4wPqFr9xPBUFsbHQR/gAzofDC+ZwYsiiJ7RAQZUHk=", "video"=>{"uploader_first_name"=>"adsfasdf", "uploader_last_name"=>"asdfasdf", "uploader_email"=>"asdfasdf", "file"=>#<ActionDispatch::Http::UploadedFile:0x007fc4782e31e8 #tempfile=#<Tempfile:/var/folders/f2/jhv7xx0j3hlckhcg_jbv6hr00000gn/T/RackMultipart20130827-89636-188f0hs>, #original_filename="sample_iPod.m4v", #content_type="video/mp4", #headers="Content-Disposition: form-data; name=\"video[file]\"; filename=\"sample_iPod.m4v\"\r\nContent-Type: video/mp4\r\n">, "project_hashed_id"=>"1377539908"}, "commit"=>"Upload", "controller"=>"videos", "action"=>"save"}
Unpermitted parameters: project_hashed_id
WARNING: Can't mass-assign protected attributes for Video: uploader_first_name, uploader_last_name, uploader_email
app/controllers/videos_controller.rb:6:in `save'
[1m[35m (0.2ms)[0m BEGIN
[1m[36mSQL (0.3ms)[0m [1mINSERT INTO `videos` (`created_at`, `file_content_type`, `file_file_name`, `file_file_size`, `file_updated_at`, `updated_at`) VALUES ('2013-08-27 13:21:34', 'video/mp4', 'sample_iPod.m4v', 2236480, '2013-08-27 13:21:34', '2013-08-27 13:21:34')[0m
[1m[35m (9.0ms)[0m COMMIT
Redirected to http://localhost:3000/
Completed 302 Found in 21ms (ActiveRecord: 9.5ms)
Welcome to the rails 4. 'attr_accessible' was replaced by the strong parameters.
http://api.rubyonrails.org/classes/ActionController/StrongParameters.html
Upd. Can you try this?
def create
#video = Video.create(video_params)
end
private
def video_params
params.require(:video).permit(:file, :uploader_first_name, :uploader_last_name, :uploader_email)
end
I figured out what's going on. The strong parameters were getting in the way because I'm using attr_accessible in the model. Instead of fiddling with the require() and permit() I removed them entirely and added the missing fields to the attr_accessible and now it works:
class Video < ActiveRecord::Base
belongs_to :project
attr_accessible :file, :uploader_first_name, :uploader_last_name, :project_hashed_id, :uploader_email, :rating
has_attached_file :file, :url=>"/tmp/video_uploads/:basename.:extension", :path=>":rails_root/tmp/video_uploads/:basename.:extension"
end
class VideosController < ApplicationController
def save
logger.debug( params )
##video = Video.new( video_params )
#video = Video.new( params[:video])
if #video.save
redirect_to root_path
else
redirect_to "somewhere_else"
end
end
end
I realize that attr_accessible was replaced by strong parameters in Rails 4 but I couldn't get paperclip to work without it. If anyone can tell me how, I'd love to know.
Update
I removed attr_accessible entirely and just used the strong parameters...
class VideosController < ApplicationController
def save
logger.debug( params )
#video = Video.new( video_params )
if #video.save
redirect_to root_path
else
redirect_to "somewhere_else"
end
end
private
def video_params
#params.require(:video).permit(:file, :uploader_first_name, :uploader_last_name, :uploader_email, :project_hashed_id)
params.require(:video).permit!
end
end
...and it works BUT you have to remember to remove the protected_attributes gem and restart rails s for it to take effect (this n00b mistake cost me 45 minutes!)
Moral of this story: Don't mix and match attr_accessible with strong params. Do one or the other and paperclip will work with strong parameters.
Related
I am creating a simple workflow where, after signing up, a publisher can create a newsletter. This newsletter needs three pieces of information: title, description, and publisher_id (i.e. the creator). My question is two-fold:
What is the 'correct' way to set the publisher_id, given that newsletters will have posts nested inside them and Rails recommends not nesting resources more than one level deep (i.e. I shouldn't nest newsletter inside publisher)?
If I am generally approaching it the correct way (see below), how am I supposed to pass the publisher_id and what am I doing wrong?
The workflow is as follows:
Create publisher and set session[:id] to #publisher.id.
Redirect to the newsletter new view.
Upon creating a newsletter, set the newsletter's publisher_id to the session[:id].
Upon navigating to to '/newsletters/new', I'm seeing the following error:
Started GET "/newsletters/new" for ::1 at 2020-05-04 15:53:22 -0700
Processing by NewslettersController#new as HTML
"<ActionController::Parameters {\"controller\"=>\"newsletters\", \"action\"=>\"new\"} permitted: false>"
Rendering newsletters/new.html.erb within layouts/application
Rendered newsletters/new.html.erb within layouts/application (Duration: 2.3ms | Allocations: 738)
And upon submitting 'Create Newsletter', I'm seeing the following error:
ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):
app/controllers/newsletters_controller.rb:21:in `create'
Started POST "/newsletters" for ::1 at 2020-05-04 15:58:34 -0700
(0.0ms) SELECT sqlite_version(*)
Processing by NewslettersController#create as JS
Parameters: {"authenticity_token"=>"XXX", "newsletter"=>{"title"=>"Newsletter 1", "description"=>"Description content"}, "commit"=>"Create Newsletter"}
Completed 500 Internal Server Error in 11ms (ActiveRecord: 1.0ms | Allocations: 7085)
publishers_controller.rb
class PublishersController < ApplicationController
def create
#publisher = Publisher.new(publisher_params)
if #publisher.save!
session[:id] = #publisher.id
redirect_to new_newsletter_path
else
render 'new'
end
end
private
def publisher_params
params.require(:publisher).permit(:email, :password)
end
end
newsletters_controller.rb
class NewslettersController < ApplicationController
def new
#newsletter = Newsletter.new
end
def create
#newsletter = Newsletter.new(newsletter_params)
if #newsletter.save!
redirect_to #newsletter
else
render 'new'
end
end
private
def newsletter_params
params.require(:newsletter).permit(:title, :description).merge(publisher_id: session[:id])
end
end
/newsletters/new.html.erb
<%= form_with model: #newsletter, url: newsletters_path do |form| %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :description %><br>
<%= form.text_area :description %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
You have misunderstood what the rails guides meant by "nesting resources more than one level deep" - whats really meant is that this is OK:
/publishers/1/newsletters/new
Which is one level of nesting and the nesting provides very useful contextual information. While these are kind of fishy:
/publishers/1/newsletters/2
/publishers/1/newsletters/3/replies/new
In both cases we have two levels of nesting should be able to reach the nested resource without going though publishers.
/newsletters/2
/newsletters/3/replies/new
Also if you want to add values from the session or somewhere else then the params hash when creating a record use a block or create the record off the association instead:
class NewslettersController < ApplicationController
def create
#newsletter = Newsletter.new(newsletter_params) do |newletter|
newsletter.publisher = current_publisher
end
# or
# #newsletter = current_publisher.newsletters(newsletter_params)
# save! will raise an exception if the record is not valid
# that is NOT what you want here
if #newsletter.save
redirect_to #newsletter
else
render 'new'
end
end
end
This makes it much more apparent what is coming from where.
I am trying to submit a form in rails that is just a pdf uplaod (using paperclip). There is something wrong with either my form, controller or model and i am not sure which.
this is my form:
<%= form_for #yearguide, :html => { :multipart => true } do |form| %>
<%= form.file_field :pdf %>
<%= form.submit "Add Event", class: "btn btn-primary" %>
<% end %>
my controller:
class YearController < ApplicationController
def new
#yearguide = Year.create(year_params)
end
def create
if #yearguide = Year.save
redirect_to '/'
else
render 'new'
end
end
my model:
class YearlyGuide < ActiveRecord::Base
has_attached_file :pdf
validates_attachment :document, content_type: { content_type: "application/pdf" }
end
my routes:
resources :year
I add the file and press upload, but I am being redirected to 'update.html.erb'.
The file doesn;t exist in the db, just an empty record.
When i debug the params on pressing uplaod I get this output
{"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"G7ZrXEiip/gsqhDObcfYhTT9kerYZGk+Zl29kWA5jos=", "year"=>{"pdf"=>#<ActionDispatch::Http::UploadedFile:0x000001029b0340 #tempfile=#<Tempfile:/var/folders/ns/ry6z7jfd6qg6j8xr2q6dw0yc0000gn/T/RackMultipart20140609-21455-1eg1sk3>, #original_filename="Artsmill Hebden Bridge Exhibition Programme 2014.pdf", #content_type="application/pdf", #headers="Content-Disposition: form-data; name=\"year[pdf]\"; filename=\"Artsmill Hebden Bridge Exhibition Programme 2014.pdf\"\r\nContent-Type: application/pdf\r\n">}, "commit"=>"Add Event", "action"=>"update", "controller"=>"year", "id"=>"9"}
=========================
EDIT
OK, so discrepancies with my naming led to the previosu errors, i started again, generating:
rails g model YearlyGuide pdf:attachment start:datetime end:datetime
rails g controller YearlyGuide new index show
now in my routes i have added
resources :yearly_guides
when i visit
/yearly_guides/new
I get this error
uninitialized constant YearlyGuidesController
I am really at a loss as to what I am doing wrong, I have done this before and never had these issues.
#iceman, thanks for your help and patience thus far.
The controller is not doing what it's supposed to do. This is the bare bones basic scheme of creating a new object in Rails.
class YearsController < ApplicationController
def new
#yearguide = Year.new
end
def create
#yearguide = Year.create(year_params)
if #yearguide.save
redirect_to '/' # I would consider redirect_to #yearguide to show the newly created object
else
render 'new'
end
end
end
EDIT:
You have to update your routes.rb to
resources :years
Since you are creating the yearguide object, rails infer that you have to do put/patch request, so request is going to update since rails getting id.
You have two options.
1) Change new method of controller like below
class YearController < ApplicationController
def new
#yearguide = Year.new
end
end
2) Override the method parameter by passing method params as 'post' inside your form tag
I'm building a simple API with Rails 4, but with my "create" method, it all goes horribly wrong.
Here is the relevant part of my routes file:
namespace :api, defaults: { format: 'json' } do
# /api/... Api::
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :users
end
end
Here is the api/v1/users_controller.rb:
class Api::V1::UsersController < ApplicationController
protect_from_forgery except: :create
respond_to :json
def index
respond_to do |format|
format.html {render text: "Your data was sucessfully loaded. Thanks"}
format.json { render text: User.last.to_json }
end
end
def show
respond_with User.find(params[:id])
end
def create
respond_with User.create(user_params)
end
def update
respond_with User.update(params[:id], params[:users])
end
def destroy
respond_with User.destroy(params[:id])
end
private
def user_params
params.require(:user).permit(:name, :age, :location, :genre_ids => [], :instrument_ids => [])
end
end
Whenever I try to add an API with JSON, I get "{"errors":{"name":["can't be blank"]}}"
It works to create a user with my regular controller, but I have a feeling my API controller is getting messed up because of the Strong Parameters.
Any suggestions for how to do this correctly in Rails 4?
Also, I have a few Has-Many-Through relationships through my user model. The API's user controller should be able to see that off the bat, right?
Thanks
EDIT:
I'm now getting this error:
EDIT:
{
"name": "Sally",
"age": "23",
"location": "Blue York",
"genre_ids": [1, 2, 3]
}
EDIT AGAIN
Even with adding the User parameter in my JSON call, it still gives me the same error of the :user param missing. Am I using strong parameters incorrectly? In my "regular" users_controller, I can create a user easily with a form that I have set up, but with this API controller, I can't seem to create one with JSON. Any other suggestions?
EDIT YET AGAIN
Here Is The Log From Start to Error
rails s
=> Booting WEBrick
=> Rails 4.0.1 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2013-12-19 14:03:01] INFO WEBrick 1.3.1
[2013-12-19 14:03:01] INFO ruby 1.9.3 (2013-02-22) [x86_64-darwin10.8.0]
[2013-12-19 14:03:01] INFO WEBrick::HTTPServer#start: pid=53778 port=3000
Started GET "/api/users" for 127.0.0.1 at 2013-12-19 14:03:02 -0500
ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by Api::V1::UsersController#index as JSON
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
Rendered text template (0.0ms)
Completed 200 OK in 142ms (Views: 27.8ms | ActiveRecord: 0.6ms)
[2013-12-19 14:03:03] WARN Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-12-19 14:03:03] WARN Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
Started POST "/api/users" for 127.0.0.1 at 2013-12-19 14:03:37 -0500
Processing by Api::V1::UsersController#create as JSON
Completed 400 Bad Request in 1ms
ActionController::ParameterMissing (param not found: user):
app/controllers/api/v1/users_controller.rb:40:in `user_params'
app/controllers/api/v1/users_controller.rb:20:in `create'
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack- 4.0.1/lib/action_dispatch/middleware/templates/rescues/_source.erb (0.7ms)
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-4.0.1/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms)
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-4.0.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.8ms)
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack- 4.0.1/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (31.6ms)
EDIT #6
Here is my "real" users_controller that lives in my app and not my API. The form creates a user from this controller and NOT the API controller.
class UsersController < ApplicationController
def index
#users = User.all
#genres = Genre.all
#instruments = Instrument.all
render json: #users
end
def new
#user = User.new
end
def show
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
if #user.save
render json: #user, status: :created, location: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:name, :age, :location, :genre_ids => [], :instrument_ids => [])
end
end
ALSO - The User Form
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :age %>
<%= f.text_field :age %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :location %>
<%= f.text_field :location %>
<br>
<% Genre.all.each do |genre| %>
<%= check_box_tag "user[genre_ids][]", genre.id %>
<%= genre.name %><br>
<% end %>
<br>
<% Instrument.all.each do |instrument| %>
<%= check_box_tag "user[instrument_ids][]", instrument.id %>
<%= instrument.name %><br>
<% end %>
<%= f.submit "Create My Account!" %>
<% end %>
</div>
</div>
<%= users_path %>
Here is my user.rb File
class User < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 50 }
has_many :generalizations
has_many :genres, through: :generalizations
has_many :instrumentations
has_many :instruments, through: :instrumentations
end
Here is what I have in my routes file:
namespace :api do
namespace :v1 do
resources :users
end
end
My POST Request
POST /api/v1/users HTTP/1.1
Host: localhost:3000
Cache-Control: no-cache
{ "user": { "name": "Sally", "age": "23", "location": "Blue York", "genre_ids": [1, 2, 3] } }
UPDATE
I changed my strong-params to be this:
def user_params
params.require(:user).permit(:name, :age, :location, :genre_ids => [], :instrument_ids => []) if params[:user]
end
So the "if" statement at the end makes the error go away, but whenever I post to my API, it gives me back "null". So this could be the same problem as before, but shown in a different way. But, at the same time, it could be progress!
Here Is The Log For The Previous Update
Started POST "/api/v1/users" for 127.0.0.1 at 2013-12-21 11:38:03 -0500
Processing by API::V1::UsersController#create as */*
(0.1ms) begin transaction
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
(0.1ms) rollback transaction
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
Rendered text template (0.0ms)
Completed 200 OK in 20ms (Views: 0.3ms | ActiveRecord: 0.6ms)
FINAL UPDATE
I was missing a few things, but the main thing that did it was that I was missing "Content-Type - application/json" as my Header.
I feel so accomplished! Thanks for all your help, everyone!
According to your code parameters in the JSON you are posting should be inside params[:user]. So the JSON should look like:
{
"user": {
"name": "Sally",
"age": "23",
"location": "Blue York",
"genre_ids": [1, 2, 3]
}
}
Rails 4 is a great choice for building APIs. I would go with the rails-api gem. It will perform way better than a full blown Rails stack.
I have built plenty of API's in Rails using the Rails API gem. Usually in combination with RABL (which you can use to create nice templates to render your JSON). I am not a big fan of integrating an API directly into your production Rails app (serving websites and JSON) as you will create a big mess over time when starting to add more versions to your API. There are some very good Railscasts (www.railscasts.com): Search for API.
When accessing your API you would use a global filter in your application_controller.rb file. You could do something like this:
before_filter :authenticate_user, :except => 'users#index'
private
def authenticate_user
#current_user = User.find_by_api_key(params[:token])
unless #current_user
render :status=>403, :json=>{:message=>"Invalid token"}
end
end
def current_user
#current_user
end
end
In this case you would send the token in your request (that's quick and dirty, rather use the header instead) as a request parameter. You need to add the API key or whatever you want to use to your user model. Just create a migration adding api_key or however you want to call it to the user model or create a new table with keys, secrets etc. and a user_id field for your belongs_to (and a User has_many api_keys, or has_one). This way you can allow your users at any time to change their keys etc. (re-generate) without messing with usernames/password or even allow them to have multiple API keys with some tags (testing, production, etc). For your user signup you could add to your model:
before_create :generate_api_key
and then create a simple method like:
def generate_api_key
begin
self.api_key = SecureRandom.hex
end while self.class.exists?(api_key: api_key)
end
Hope it helps!
sorry I am new to rails
Right now I am trying to build a small application just like https://pinboard.in, I am trying to get a summer internship with them.
Here is my Bookmark model
class Bookmark < ActiveRecord::Base
attr_accessible :url, :title, :description, :counter
belongs_to :user
#validates that url has https:// or http://
validates :url, :format => { :with => /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0- 9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)/ix,
:message => "Invalid URL" }
end
Here is my bookmark controller
class BookmarksController < ApplicationController
def add_bookmark
#bookmark = Bookmark.new
respond_to do |format|
format.html
end
end
def draw_recent
#bookmarks = Bookmark.all
end
end
Here is my form
<%= form_for :bookmark do |f| %>
URL: <%= f.text_field :url %><br/>
Title: <%= f.text_field :title %><br/>
Description: <%= f.text_field :description %><br/>
<%= f.submit "Submit" %>
<% end %>
Everything get rendered correctly and when I put in the information and submit add
here is my output
Started POST "/add" for 127.0.0.1 at 2013-05-09 09:55:58 -0400
Processing by BookmarksController#add_bookmark as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ZCxA226pOVyu5AkQAmvvfKz5uPQ4gFggPTwrswtqZYk=", "bookmark"=> {"url"=>"http://litmus.com", "title"=>"Email marketing ", "description"=>"email marketing "}, "commit"=>"Submit"}
Rendered bookmarks/_form.html.erb (1.9ms)
Rendered bookmarks/add_bookmark.html.erb within layouts/application (3.3ms)
Completed 200 OK in 96ms (Views: 95.4ms | ActiveRecord: 0.0ms)
I have two thoughts in my head, well my form is posting it correctly but somehow it is not saving anything to my db, do I need a save method in my controller?
when I try #bookmark instead of :bookmark the app throws me an error saying wrong bookmarks.path
undefined method `bookmarks_path
I understand the former you are actually working with the instance #bookmark in the controller and the latter is wrapping around the model....
Can someone enlighten me? i feel like this is very trivial for you guys... :)
Should be form_for #bookmark do |f|. You are getting the undefined method error because you have not defined your routes in config/routes.rb, add there:
resources :bookmarks
That will add the RESTful resources following the CRUD convention, so you should change the name of your controller methods for this to work out-of-the-box. You will need at least three methods for what you are doing right now:
In your app/controllers/bookmarks_controller.rb:
First one will render your form at /bookmarks/new
def new
#bookmark = Bookmark.new
end
Second one will process the form submission (no action is needed from you apart that following the naming convention)
def create
#bookmark = Bookmark.new(params[:bookmark])
#bookmark.save
end
Third one to show the bookmarks as in your current 'draw_recent'
def index
#bookmark = Bookmark.all
end
Afterwards you can go on validating the data, etc but the basic flow should be like that.
It is better to start with the conventions to go later on to change the method standard names when you have more confidence.
I am relatively new to rails cannot get the following code to work. I am trying to upload a data file (Excel or csv), copy it to a temp folder and create a record in a Datafiles model which holds basic file information, such as filename, type, and date. Then I want to read the file and use the data to create or update records in several other models. If all goes well, move the file to a permanent location and write the new path in the Datafiles record.
Controller:
def new
#datafile = Datafile.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #datafile }
end
end
def create
#datafile = Datafile.new(params[:upload])
#datafile.save!
redirect_to datafile_path(#datafile), :notice => "Successfully imported datafile"
rescue => e
logger.error( 'Upload failed. ' + e.to_s )
flash[:error] = 'Upload failed. Please try again.'
render :action => 'new'
end
View:
<%= form_for #datafile, :html => {:multipart => true} do |f| %>
<p>
<%= f.label(:upload, "Select File:") %>
<%= f.file_field :upload %>
</p>
<p> <%= f.submit "Import" %> </p>
<% end %>
Model:
require 'spreadsheet'
class Datafile < ActiveRecord::Base
attr_accessor :upload
attr_accessible :upload
before_create :upload_file
def upload_file
begin
File.open(Rails.root.join('uploads/temp', upload.original_filename), 'wb') do |file|
file.write(upload.read)
self.filename = upload.original_filename
Spreadsheet.client_encoding = 'UTF-8'
#book = Spreadsheet.open(file.path)
self.import
end
rescue => e
#upload_path = Rails.root.join('uploads/temp', upload.original_filename)
File.delete(#upload_path) if File::exists?(#upload_path)
raise e
end
end
def import
case #book.worksheet(0).row(0)[0]
when "WIP Report - Inception to Date"
self.report_type = 'WIP'
puts 'report_type assigned'
self.import_wip
else
self.report_type = 'Unknown'
end
end
def import_wip
self.end_date = #book.worksheet(0).row(0)[3]
puts 'end_date assigned'
end
def formatted_end_date
end_date.strftime("%d %b, %Y")
end
end
However, it fails and the rails server window says
Started POST "/datafiles" for 127.0.0.1 at 2011-05-24 16:05:25 +0200
Processing by DatafilesController#create as HTML
Parameters: {"utf8"=>"✓", "datafile"=>{"upload"=>#<ActionDispatch::Http::UploadedFile:0xa0282d0 #original_filename="wip.xls", #content_type="application/vnd.ms-excel", #headers="Content-Disposition: form-data; name=\"datafile[upload]\"; filename=\"wip.xls\"\r\nContent-Type: application/vnd.ms-excel\r\n", #tempfile=#<File:/tmp/RackMultipart20110524-14236-1kcu3hm>>}, "commit"=>"Import"}
Upload failed. undefined method `original_filename' for nil:NilClass
Rendered datafiles/new.html.erb within layouts/application (54.5ms)
Completed 200 OK in 131ms (Views: 56.3ms | ActiveRecord: 0.0ms)
I have rspec model tests that pass and controller tests that fail to redirect after saving. I can post them if it would be useful.
I inserted the raise #datafile.to_yaml and got the following in the terminal.
ERROR RuntimeError: --- !ruby/object:Datafile
attributes:
filename:
report_type:
import_successful:
project:
begin_date:
end_date:
created_at:
updated_at:
attributes_cache: {}
changed_attributes: {}
destroyed: false
marked_for_destruction: false
persisted: false
previously_changed: {}
readonly: false
I notice that :upload is not listed. Can I set model instance variables from the form? :upload is an instance variable, not an attribute, because I do not want to keep the uploaded file in the database (just its path to the local directory). If I cannot set instance variables in the view's form, any suggestions? Does it make sense (in terms of MVC) to upload the file to a temp folder in the controller, then create a model record by passing it the temp file's path?
Hello I am pretty new to Rails and was strugling with this as well I found a solution though it probably isn't the best. It does work though.
in you model make a public function called import_upload
def import_upload( upload )
#uploaded_file = upload
end
now in your controller you can explicitly pass it. I don't know why this doesn't happen automatically if you make an attr_accsessor with the same name as the file_field but this was the solution that worked for me.
def new
foo = Foo.new( params[:foo] )
foo.import_upload( params[:foo][:uploaded_file] ) #This is were the majic happens
#Do your saving stuff and call it a day
end